From 6988cc52f594a9aff105df3c6c679ff58f2ead7f Mon Sep 17 00:00:00 2001 From: Andrew6810 <16847730+andrew6180@users.noreply.github.com> Date: Fri, 21 Oct 2022 06:52:48 -0700 Subject: [PATCH] init --- Decursive/Bindings.xml | 56 + .../Changelog-Decursive-2.5.1-6-gd3885c5.txt | 9 + Decursive/DCR_init.lua | 1401 +++++++++ Decursive/Dcr_DIAG.lua | 430 +++ Decursive/Dcr_DIAG.xml | 138 + Decursive/Dcr_DebuffsFrame.lua | 1654 ++++++++++ Decursive/Dcr_DebuffsFrame.xml | 186 ++ Decursive/Dcr_Events.lua | 834 +++++ Decursive/Dcr_LDB.lua | 202 ++ Decursive/Dcr_LiveList.lua | 575 ++++ Decursive/Dcr_LiveList.xml | 211 ++ Decursive/Dcr_Raid.lua | 783 +++++ Decursive/Dcr_lists.lua | 601 ++++ Decursive/Dcr_lists.xml | 913 ++++++ Decursive/Dcr_opt.lua | 2709 +++++++++++++++++ Decursive/Dcr_utils.lua | 525 ++++ Decursive/Decursive.lua | 889 ++++++ Decursive/Decursive.toc | 73 + Decursive/Decursive.xml | 352 +++ Decursive/LICENSE.txt | 23 + Decursive/LibStub/LibStub.lua | 30 + Decursive/Libs/AceAddon-3.0/AceAddon-3.0.lua | 642 ++++ Decursive/Libs/AceAddon-3.0/AceAddon-3.0.xml | 4 + Decursive/Libs/AceComm-3.0/AceComm-3.0.lua | 309 ++ Decursive/Libs/AceComm-3.0/AceComm-3.0.xml | 5 + .../Libs/AceComm-3.0/ChatThrottleLib.lua | 503 +++ .../Libs/AceConfig-3.0/AceConfig-3.0.lua | 57 + .../Libs/AceConfig-3.0/AceConfig-3.0.xml | 8 + .../AceConfigCmd-3.0/AceConfigCmd-3.0.lua | 787 +++++ .../AceConfigCmd-3.0/AceConfigCmd-3.0.xml | 4 + .../AceConfigDialog-3.0.lua | 1901 ++++++++++++ .../AceConfigDialog-3.0.xml | 4 + .../AceConfigRegistry-3.0.lua | 346 +++ .../AceConfigRegistry-3.0.xml | 4 + .../Libs/AceConsole-3.0/AceConsole-3.0.lua | 250 ++ .../Libs/AceConsole-3.0/AceConsole-3.0.xml | 4 + Decursive/Libs/AceDB-3.0/AceDB-3.0.lua | 728 +++++ Decursive/Libs/AceDB-3.0/AceDB-3.0.xml | 4 + .../AceDBOptions-3.0/AceDBOptions-3.0.lua | 420 +++ .../AceDBOptions-3.0/AceDBOptions-3.0.xml | 4 + Decursive/Libs/AceEvent-3.0/AceEvent-3.0.lua | 126 + Decursive/Libs/AceEvent-3.0/AceEvent-3.0.xml | 4 + Decursive/Libs/AceGUI-3.0/AceGUI-3.0.lua | 805 +++++ Decursive/Libs/AceGUI-3.0/AceGUI-3.0.xml | 28 + .../AceGUIContainer-BlizOptionsGroup.lua | 133 + .../widgets/AceGUIContainer-DropDownGroup.lua | 157 + .../widgets/AceGUIContainer-Frame.lua | 298 ++ .../widgets/AceGUIContainer-InlineGroup.lua | 102 + .../widgets/AceGUIContainer-ScrollFrame.lua | 204 ++ .../widgets/AceGUIContainer-SimpleGroup.lua | 69 + .../widgets/AceGUIContainer-TabGroup.lua | 348 +++ .../widgets/AceGUIContainer-TreeGroup.lua | 670 ++++ .../widgets/AceGUIContainer-Window.lua | 331 ++ .../widgets/AceGUIWidget-Button.lua | 92 + .../widgets/AceGUIWidget-CheckBox.lua | 289 ++ .../widgets/AceGUIWidget-ColorPicker.lua | 186 ++ .../widgets/AceGUIWidget-DropDown-Items.lua | 465 +++ .../widgets/AceGUIWidget-DropDown.lua | 707 +++++ .../widgets/AceGUIWidget-EditBox.lua | 235 ++ .../widgets/AceGUIWidget-Heading.lua | 78 + .../AceGUI-3.0/widgets/AceGUIWidget-Icon.lua | 144 + .../widgets/AceGUIWidget-InteractiveLabel.lua | 101 + .../widgets/AceGUIWidget-Keybinding.lua | 230 ++ .../AceGUI-3.0/widgets/AceGUIWidget-Label.lua | 162 + .../widgets/AceGUIWidget-MultiLineEditBox.lua | 311 ++ .../widgets/AceGUIWidget-Slider.lua | 281 ++ Decursive/Libs/AceHook-3.0/AceHook-3.0.lua | 514 ++++ Decursive/Libs/AceHook-3.0/AceHook-3.0.xml | 4 + .../Libs/AceLocale-3.0/AceLocale-3.0.lua | 136 + .../Libs/AceLocale-3.0/AceLocale-3.0.xml | 4 + Decursive/Libs/AceTimer-3.0/AceTimer-3.0.lua | 473 +++ Decursive/Libs/AceTimer-3.0/AceTimer-3.0.xml | 4 + .../CallbackHandler-1.0.lua | 240 ++ .../CallbackHandler-1.0.xml | 4 + .../Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua | 264 ++ .../LibDataBroker-1.1/LibDataBroker-1.1.lua | 90 + .../Libs/LibDataBroker-1.1/README.textile | 13 + Decursive/Libs/LibQTip-1.0/LICENSE.txt | 29 + Decursive/Libs/LibQTip-1.0/LibQTip-1.0.lua | 1278 ++++++++ Decursive/Libs/LibQTip-1.0/LibQTip-1.0.toc | 17 + Decursive/Libs/LibQTip-1.0/lib.xml | 4 + Decursive/Localization/deDE.lua | 390 +++ Decursive/Localization/enUS.lua | 395 +++ Decursive/Localization/esES.lua | 103 + Decursive/Localization/esMX.lua | 78 + Decursive/Localization/frFR.lua | 392 +++ Decursive/Localization/koKR.lua | 385 +++ Decursive/Localization/load.xml | 14 + Decursive/Localization/ruRU.lua | 388 +++ Decursive/Localization/zhCN.lua | 384 +++ Decursive/Localization/zhTW.lua | 382 +++ Decursive/OldChangeLog.txt | 836 +++++ Decursive/Readme.txt | 23 + Decursive/Sounds/AfflictionAlert.wav | Bin 0 -> 132494 bytes Decursive/Sounds/FailedSpell.wav | Bin 0 -> 44140 bytes Decursive/Textures/BackDrop.tga | Bin 0 -> 204 bytes Decursive/Textures/GoldBorder.tga | Bin 0 -> 1048594 bytes Decursive/Todo.txt | 27 + Decursive/WhatsNew.txt | 256 ++ Decursive/doc/Description.txt | 85 + Decursive/doc/MUFs.txt | 85 + Decursive/doc/commands.txt | 86 + Decursive/doc/faq.txt | 79 + Decursive/doc/macro.txt | 31 + Decursive/doc/user-actions.txt | 123 + Decursive/embeds.xml | 29 + Decursive/iconOFF.tga | Bin 0 -> 2590 bytes Decursive/iconON.tga | Bin 0 -> 2647 bytes Decursive/loc_lists.txt | 140 + README.md | 4 +- 110 files changed, 32921 insertions(+), 2 deletions(-) create mode 100644 Decursive/Bindings.xml create mode 100644 Decursive/Changelog-Decursive-2.5.1-6-gd3885c5.txt create mode 100644 Decursive/DCR_init.lua create mode 100644 Decursive/Dcr_DIAG.lua create mode 100644 Decursive/Dcr_DIAG.xml create mode 100644 Decursive/Dcr_DebuffsFrame.lua create mode 100644 Decursive/Dcr_DebuffsFrame.xml create mode 100644 Decursive/Dcr_Events.lua create mode 100644 Decursive/Dcr_LDB.lua create mode 100644 Decursive/Dcr_LiveList.lua create mode 100644 Decursive/Dcr_LiveList.xml create mode 100644 Decursive/Dcr_Raid.lua create mode 100644 Decursive/Dcr_lists.lua create mode 100644 Decursive/Dcr_lists.xml create mode 100644 Decursive/Dcr_opt.lua create mode 100644 Decursive/Dcr_utils.lua create mode 100644 Decursive/Decursive.lua create mode 100644 Decursive/Decursive.toc create mode 100644 Decursive/Decursive.xml create mode 100644 Decursive/LICENSE.txt create mode 100644 Decursive/LibStub/LibStub.lua create mode 100644 Decursive/Libs/AceAddon-3.0/AceAddon-3.0.lua create mode 100644 Decursive/Libs/AceAddon-3.0/AceAddon-3.0.xml create mode 100644 Decursive/Libs/AceComm-3.0/AceComm-3.0.lua create mode 100644 Decursive/Libs/AceComm-3.0/AceComm-3.0.xml create mode 100644 Decursive/Libs/AceComm-3.0/ChatThrottleLib.lua create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfig-3.0.lua create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfig-3.0.xml create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.xml create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.xml create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua create mode 100644 Decursive/Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.xml create mode 100644 Decursive/Libs/AceConsole-3.0/AceConsole-3.0.lua create mode 100644 Decursive/Libs/AceConsole-3.0/AceConsole-3.0.xml create mode 100644 Decursive/Libs/AceDB-3.0/AceDB-3.0.lua create mode 100644 Decursive/Libs/AceDB-3.0/AceDB-3.0.xml create mode 100644 Decursive/Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua create mode 100644 Decursive/Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml create mode 100644 Decursive/Libs/AceEvent-3.0/AceEvent-3.0.lua create mode 100644 Decursive/Libs/AceEvent-3.0/AceEvent-3.0.xml create mode 100644 Decursive/Libs/AceGUI-3.0/AceGUI-3.0.lua create mode 100644 Decursive/Libs/AceGUI-3.0/AceGUI-3.0.xml create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-BlizOptionsGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-DropDownGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-Frame.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-InlineGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-ScrollFrame.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-SimpleGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-TabGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-TreeGroup.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIContainer-Window.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Button.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-CheckBox.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-ColorPicker.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown-Items.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-DropDown.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-EditBox.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Heading.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Icon.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-InteractiveLabel.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Keybinding.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Label.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-MultiLineEditBox.lua create mode 100644 Decursive/Libs/AceGUI-3.0/widgets/AceGUIWidget-Slider.lua create mode 100644 Decursive/Libs/AceHook-3.0/AceHook-3.0.lua create mode 100644 Decursive/Libs/AceHook-3.0/AceHook-3.0.xml create mode 100644 Decursive/Libs/AceLocale-3.0/AceLocale-3.0.lua create mode 100644 Decursive/Libs/AceLocale-3.0/AceLocale-3.0.xml create mode 100644 Decursive/Libs/AceTimer-3.0/AceTimer-3.0.lua create mode 100644 Decursive/Libs/AceTimer-3.0/AceTimer-3.0.xml create mode 100644 Decursive/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua create mode 100644 Decursive/Libs/CallbackHandler-1.0/CallbackHandler-1.0.xml create mode 100644 Decursive/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua create mode 100644 Decursive/Libs/LibDataBroker-1.1/LibDataBroker-1.1.lua create mode 100644 Decursive/Libs/LibDataBroker-1.1/README.textile create mode 100644 Decursive/Libs/LibQTip-1.0/LICENSE.txt create mode 100644 Decursive/Libs/LibQTip-1.0/LibQTip-1.0.lua create mode 100644 Decursive/Libs/LibQTip-1.0/LibQTip-1.0.toc create mode 100644 Decursive/Libs/LibQTip-1.0/lib.xml create mode 100644 Decursive/Localization/deDE.lua create mode 100644 Decursive/Localization/enUS.lua create mode 100644 Decursive/Localization/esES.lua create mode 100644 Decursive/Localization/esMX.lua create mode 100644 Decursive/Localization/frFR.lua create mode 100644 Decursive/Localization/koKR.lua create mode 100644 Decursive/Localization/load.xml create mode 100644 Decursive/Localization/ruRU.lua create mode 100644 Decursive/Localization/zhCN.lua create mode 100644 Decursive/Localization/zhTW.lua create mode 100644 Decursive/OldChangeLog.txt create mode 100644 Decursive/Readme.txt create mode 100644 Decursive/Sounds/AfflictionAlert.wav create mode 100644 Decursive/Sounds/FailedSpell.wav create mode 100644 Decursive/Textures/BackDrop.tga create mode 100644 Decursive/Textures/GoldBorder.tga create mode 100644 Decursive/Todo.txt create mode 100644 Decursive/WhatsNew.txt create mode 100644 Decursive/doc/Description.txt create mode 100644 Decursive/doc/MUFs.txt create mode 100644 Decursive/doc/commands.txt create mode 100644 Decursive/doc/faq.txt create mode 100644 Decursive/doc/macro.txt create mode 100644 Decursive/doc/user-actions.txt create mode 100644 Decursive/embeds.xml create mode 100644 Decursive/iconOFF.tga create mode 100644 Decursive/iconON.tga create mode 100644 Decursive/loc_lists.txt diff --git a/Decursive/Bindings.xml b/Decursive/Bindings.xml new file mode 100644 index 0000000..5c0c8fc --- /dev/null +++ b/Decursive/Bindings.xml @@ -0,0 +1,56 @@ + + + + + + Dcr:HideBar(false); + + + Dcr:ShowHideDebuffsFrame (); + + + Dcr.Waterfall:Open("Decursive"); + + + Dcr:ShowHidePriorityListUI(); + + + Dcr:AddTargetToPriorityList(); + + + Dcr:ClearPriorityList(); + + + + Dcr:ShowHideSkipListUI(); + + + Dcr:AddTargetToSkipList(); + + + Dcr:ClearSkipList(); + + + diff --git a/Decursive/Changelog-Decursive-2.5.1-6-gd3885c5.txt b/Decursive/Changelog-Decursive-2.5.1-6-gd3885c5.txt new file mode 100644 index 0000000..282ff02 --- /dev/null +++ b/Decursive/Changelog-Decursive-2.5.1-6-gd3885c5.txt @@ -0,0 +1,9 @@ +Archarodim: + - - Added X-Max-Interface and X-Min-Interface to toc + - - fix "Dcr_lists.lua:140: attempt to index local 'frame' (a nil value)" introduced in b03f6b93f98 + - - Added Fear (rank 1) for Warlocks to use on charmed units + - WoW 4.0: Added support for 'Singe Magic' imp spell for Warlocks and changed 'Devour Magic' to 'enemy magic' only. + - WoW 4.0: Removed rank for Polymorph + - - fixed changelog in .pkgmeta + - - Fix missing spell reporting in WoW 4.0 + - WoW 4.0 basic compatibility (this --> self, etc...) diff --git a/Decursive/DCR_init.lua b/Decursive/DCR_init.lua new file mode 100644 index 0000000..ea3b705 --- /dev/null +++ b/Decursive/DCR_init.lua @@ -0,0 +1,1401 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then + -- the beautiful error popup : {{{ - + StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} + T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["enUS.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (enUS.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +T.Dcr = LibStub("AceAddon-3.0"):NewAddon("Decursive", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceHook-3.0"); +Dcr = T.Dcr; -- needed until we get rid of the xml based UI. + +local D = T.Dcr; + +D.name = "Decursive"; +D.version = "2.5.1-6-gd3885c5"; +D.author = "Archarodim"; + +D.L = LibStub("AceLocale-3.0"):GetLocale("Decursive", true); + +D.LC = _G.LOCALIZED_CLASS_NAMES_MALE; + +if not D.LC then + T._AddDebugText("DCR_init.lua: Couldn't get LOCALIZED_CLASS_NAMES_MALE!"); + D.LC = {}; +end + +D.DcrFullyInitialized = false; + +local L = D.L; +local LC = D.LC; + +local BOOKTYPE_PET = BOOKTYPE_PET; +local BOOKTYPE_SPELL = BOOKTYPE_SPELL; + + + + +local select = _G.select; +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local InCombatLockdown = _G.InCombatLockdown; + + + +------------------------------------------------------------------------------- +-- variables {{{ +------------------------------------------------------------------------------- +D.Groups_datas_are_invalid = true; +------------------------------------------------------------------------------- +-- Internal HARD settings for decursive +D.CONF = {}; +D.CONF.TEXT_LIFETIME = 4.0; +D.CONF.MAX_LIVE_SLOTS = 10; +D.CONF.MACRONAME = "Decursive"; +D.CONF.MACROCOMMAND = string.format("MACRO %s", D.CONF.MACRONAME); + +BINDING_HEADER_DECURSIVE = "Decursive"; + +D.CONF.MACRO_DIAG = "/dcrdiag"; +D.CONF.MACRO_COMMAND = "/decursive"; +D.CONF.MACRO_SHOW = "/dcrshow"; +D.CONF.MACRO_HIDE = "/dcrhide"; +D.CONF.MACRO_OPTION = "/dcroptions"; +D.CONF.MACRO_RESET = "/dcrreset"; + +D.CONF.MACRO_PRADD = "/dcrpradd"; +D.CONF.MACRO_PRCLEAR = "/dcrprclear"; +D.CONF.MACRO_PRLIST = "/dcrprlist"; +D.CONF.MACRO_PRSHOW = "/dcrprshow"; + +D.CONF.MACRO_SKADD = "/dcrskadd"; +D.CONF.MACRO_SKCLEAR = "/dcrskclear"; +D.CONF.MACRO_SKLIST = "/dcrsklist"; +D.CONF.MACRO_SKSHOW = "/dcrskshow"; +D.CONF.MACRO_DEBUG = "/dcrdebug"; +D.CONF.MACRO_SHOW_ORDER = "/dcrshoworder"; + +-- CONSTANTS + +local DC = DcrC; + +DC.DS = {}; + +local DS = DC.DS; + +DC.AfflictionSound = "Interface\\AddOns\\Decursive\\Sounds\\AfflictionAlert.wav"; +--DC.AfflictionSound = "Sound\\Doodad\\BellTollTribal.wav" +DC.FailedSound = "Interface\\AddOns\\Decursive\\Sounds\\FailedSpell.wav"; + +DC.IconON = "Interface\\AddOns\\Decursive\\iconON.tga"; +DC.IconOFF = "Interface\\AddOns\\Decursive\\iconOFF.tga"; + +DC.CLASS_DRUID = 'DRUID'; +DC.CLASS_HUNTER = 'HUNTER'; +DC.CLASS_MAGE = 'MAGE'; +DC.CLASS_PALADIN = 'PALADIN'; +DC.CLASS_PRIEST = 'PRIEST'; +DC.CLASS_ROGUE = 'ROGUE'; +DC.CLASS_SHAMAN = 'SHAMAN'; +DC.CLASS_WARLOCK = 'WARLOCK'; +DC.CLASS_WARRIOR = 'WARRIOR'; +DC.CLASS_DEATHKNIGHT = 'DEATHKNIGHT'; + +DC.MyClass = "NOCLASS"; +DC.MyName = "NONAME"; +DC.MyGUID = ""; + +DC.MAGIC = 1; +DC.ENEMYMAGIC = 2; +DC.CURSE = 4; +DC.POISON = 8; +DC.DISEASE = 16; +DC.CHARMED = 32; +DC.NOTYPE = 64; + + +DC.NORMAL = 8; +DC.ABSENT = 16; +DC.FAR = 32; +DC.STEALTHED = 64; +DC.BLACKLISTED = 128; +DC.AFFLICTED = 256; +DC.AFFLICTED_NIR = 512; +DC.CHARMED_STATUS = 1024; +DC.AFFLICTED_AND_CHARMED = bit.bor(DC.AFFLICTED, DC.CHARMED_STATUS); + +DC.MFSIZE = 20; + +-- This value is returned by UnitName when the name of a unit is not available yet +DC.UNKNOWN = UNKNOWNOBJECT; + +-- Get the translation for "pet" +DC.PET = SPELL_TARGET_TYPE8_DESC; + +-- Holder for 'Rank #' translation +DC.RANKNUMTRANS = false; + +DC.DebuffHistoryLength = 40; -- we use a rather high value to avoid garbage creation + +DC.DevVersionExpired = false; + +DC.RAID_ICON_LIST = _G.ICON_LIST; +if not DC.RAID_ICON_LIST then + T._AddDebugText("DCR_init.lua: Couldn't get Raid Target Icon List!"); + DC.RAID_ICON_LIST = {}; +end + +DC.RAID_ICON_TEXTURE_LIST = {}; + +for i,v in ipairs(DC.RAID_ICON_LIST) do + DC.RAID_ICON_TEXTURE_LIST[i] = "Interface\\TargetingFrame\\UI-RaidTargetingIcon_" .. i; +end + + + +D.DebuffHistory = {}; + +D.MFContainer = false; +D.LLContainer = false; + +D.profile = {}; +D.classprofile = {}; + +D.Status = {}; + +D.Status.CuringSpells = {}; +D.Status.CuringSpellsPrio = {}; +D.Status.DelayedFunctionCalls = {}; +D.Status.DelayedFunctionCallsCount = 0; + +D.Status.Blacklisted_Array = {}; +D.Status.UnitNum = 0; + +D.Status.PrioChanged = true; + +D.Status.last_focus_GUID = false; +D.Status.UpdateCooldown = 0; + +D.Status.GroupUpdatedOn = 0; +D.Status.GroupUpdateEvent = 0; + +D.Status.TestLayout = false; +D.Status.TestLayoutUNum = 25; + +-- An acces the debuff table +D.ManagedDebuffUnitCache = {}; +-- A table UnitID=>IsDebuffed (boolean) +D.UnitDebuffed = {}; + +-- // }}} +------------------------------------------------------------------------------- + + +-- D.Initialized = false; +------------------------------------------------------------------------------- + +-- add support for FuBar +D.independentProfile = true; -- for Fubar +D.hasIcon = DC.IconOFF; +D.hasNoColor = true; +D.overrideMenu = true; +D.defaultMinimapPosition = 250; +D.hideWithoutStandby = true; +D.defaultPosition = "LEFT"; +D.hideMenuTitle = true; + +function D:AddDebugText(a1, ...) + T._AddDebugText(a1, ...); +end + +function D:BetaWarning() + + local alpha = false; + --@alpha@ + alpha = true; + --@end-alpha@ + + if (("2.5.1-6-gd3885c5"):lower()):find("beta") or ("2.5.1-6-gd3885c5"):find("RC") or ("2.5.1-6-gd3885c5"):find("Candidate") or alpha then + + -- check for expiration of this dev version + if D.VersionTimeStamp ~= 0 then + + local VersionLifeTime = 3600 * 24 * 30; -- 30 days + + if time() > D.VersionTimeStamp + VersionLifeTime then + DC.DevVersionExpired = true; + -- Display the expiration notice only once evry 48 hours + if time() - self.db.global.LastExpirationAlert > 48 * 3600 then + StaticPopup_Show ("Decursive_Notice_Frame", "|cff00ff00Decursive version: 2.5.1-6-gd3885c5|r\n\n" .. "|cFFFFAA66" .. L["DEV_VERSION_EXPIRED"] .. "|r"); + + self.db.global.LastExpirationAlert = time(); + end + + return; + end + + end + + if self.db.global.NonRealease ~= "2.5.1-6-gd3885c5" then + self.db.global.NonRealease = "2.5.1-6-gd3885c5"; + StaticPopup_Show ("Decursive_Notice_Frame", "|cff00ff00Decursive version: 2.5.1-6-gd3885c5|r\n\n" .. "|cFFFFAA66" .. L["DEV_VERSION_ALERT"] .. "|r"); + end + end + + +end + +function D:OnInitialize() -- Called on ADDON_LOADED -- {{{ + + if T._SelfDiagnostic() == 2 then + return false; + end + + T._HookErrorHandler(); + T._CatchAllErrors = true; -- During init we catch all the errors else, if a library fails we won't know it. + + D.defaults = D:GetDefaultsSettings(); + + self.db = LibStub("AceDB-3.0"):New("DecursiveDB", D.defaults, true); + + self.db.RegisterCallback(self, "OnProfileChanged", "SetConfiguration") + self.db.RegisterCallback(self, "OnProfileCopied", "SetConfiguration") + self.db.RegisterCallback(self, "OnProfileReset", "SetConfiguration") + + D:ExportOptions (); + + -- Create some useful cache tables + D:CreateClassColorTables(); + + + D.MFContainer = DcrMUFsContainer; + D.MFContainerHandle = DcrMUFsContainerDragButton; + D.MicroUnitF.Frame = D.MFContainer; + + + D.LLContainer = DcrLiveList; + D.LiveList.Frame = DcrLiveList; + + + DC.TypeNames = { + [DC.MAGIC] = "Magic"; + [DC.ENEMYMAGIC] = "Magic"; + [DC.CURSE] = "Curse"; + [DC.POISON] = "Poison"; + [DC.DISEASE] = "Disease"; + [DC.CHARMED] = "Charm"; + } + + DC.NameToTypes = D:tReverse(DC.TypeNames); + DC.NameToTypes["Magic"] = DC.MAGIC; -- make sure 'Magic' is set to DC.MAGIC and not to DC.ENEMYMAGIC + + DC.TypeColors = { + [DC.MAGIC] = "2222DD"; + [DC.ENEMYMAGIC] = "2222FF"; + [DC.CURSE] = "DD22DD"; + [DC.POISON] = "22DD22"; + [DC.DISEASE] = "995533"; + [DC.CHARMED] = "FF0000"; + [DC.NOTYPE] = "AAAAAA"; + } + + -- /script DcrC.SpellsToUse[DcrC.DS["Dampen Magic"]] = {Types = {DcrC.MAGIC, DcrC.DISEASE, DcrC.POISON},IsBest = false}; Dcr:Configure(); + -- /script DcrC.SpellsToUse[DcrC.DS["SPELL_POLYMORPH"]] = { Types = {DcrC.CHARMED}, IsBest = false, Pet = false, Rank = "1 : Pig"}; Dcr:Configure(); + + -- SPELL TABLE -- must be parsed after localisation is loaded {{{ + DC.SpellsToUse = { + + --[[ + -- used for testing only + [DS["Dampen Magic"] ] = { + Types = {DC.MAGIC},--, DC.DISEASE, DC.POISON}, + IsBest = 0, + Pet = false, + }, --]] + --[[ + -- used for testing only + [DS["Amplify Magic"] ] = { + Types = {DC.DISEASE, DC.POISON}, + IsBest = 0, + Pet = false, + }, --]] + + -- Priests + [DS["SPELL_CURE_DISEASE"]] = { + Types = {DC.DISEASE}, + IsBest = 0, + Pet = false, + }, + -- Priests + [DS["SPELL_ABOLISH_DISEASE"]] = { + Types = {DC.DISEASE}, + IsBest = 1, + Pet = false, + + EnhancedBy = DS["TALENT_BODY_AND_SOUL"], + EnhancedByCheck = function () + return (select(5, GetTalentInfo(2,20))) > 0; + end, + Enhancements = { + Types = {DC.DISEASE, DC.POISON}, + OnPlayerOnly = { + [DC.DISEASE] = false, + [DC.POISON] = true, + }, + } + }, + -- Priests + [DS["SPELL_DISPELL_MAGIC"]] = { + Types = {DC.MAGIC, DC.ENEMYMAGIC}, + IsBest = 1, + Pet = false, + }, + -- Paladins + [DS["SPELL_PURIFY"]] = { + Types = {DC.DISEASE, DC.POISON}, + IsBest = 1, + Pet = false, + }, + -- Paladins + [DS["SPELL_CLEANSE"]] = { + Types = {DC.MAGIC, DC.DISEASE, DC.POISON}, + IsBest = 2, + Pet = false, + }, + -- Druids + [DS["SPELL_CURE_POISON"]] = { + Types = {DC.POISON}, + IsBest = 0, + Pet = false, + }, + -- Druids + [DS["SPELL_ABOLISH_POISON"]] = { + Types = {DC.POISON}, + IsBest = 1, + Pet = false, + }, + -- Druids + [DS["SPELL_CYCLONE"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + }, + -- Mages and Druids + [DS["SPELL_REMOVE_CURSE"]] = { + Types = {DC.CURSE}, + IsBest = 0, + Pet = false, + }, + [DS["SPELL_REMOVE_LESSER_CURSE"]] = { + Types = {DC.CURSE}, + IsBest = 0, + Pet = false, + }, + -- Mages + [DS["SPELL_POLYMORPH"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + Rank = 1, + }, + -- Shamans + [DS["SPELL_CURE_TOXINS"]] = { + Types = {DC.POISON, DC.DISEASE}, + IsBest = 1, + Pet = false, + }, + -- Shaman resto + [DS["CLEANSE_SPIRIT"]] = { + Types = {DC.CURSE, DC.DISEASE, DC.POISON}, + IsBest = 3, + Pet = false, + }, + -- Shamans http://www.wowhead.com/?spell=51514 + [DS["SPELL_HEX"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + }, + --[=[ -- disabled because of Korean locals... see below + -- Shamans + [DS["SPELL_PURGE"]] = { + Types = {DC.ENEMYMAGIC}, + IsBest = 0, + Pet = false, + }, --]=] + -- Hunters http://www.wowhead.com/?spell=19801 + [DS["SPELL_TRANQUILIZING_SHOT"]] = { + Types = {DC.ENEMYMAGIC}, + IsBest = 0, + Pet = false, + }, + -- Warlock + [DS["SPELL_FEAR"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + Rank = 1, + }, + -- Warlock + [DS["PET_FEL_CAST"]] = { + Types = {DC.MAGIC, DC.ENEMYMAGIC}, + IsBest = 1, + Pet = true, + }, + -- Warlock + [DS["PET_DOOM_CAST"]] = { + Types = {DC.MAGIC, DC.ENEMYMAGIC}, + IsBest = 1, + Pet = true, + }, + }; + + -- WoW 4.0 changes {{{ + + if T._tocversion == 40000 then + DC.SpellsToUse[DS["PET_FEL_CAST"]] = { + Types = {DC.ENEMYMAGIC}, + IsBest = 0, + Pet = true, + }; + -- Warlocks + DC.SpellsToUse[DS["SPELL_SINGE_MAGIC"]] = { + Types = {DC.MAGIC}, + IsBest = 0, + Pet = true, + }; + -- Warlock + DC.SpellsToUse[DS["SPELL_FEAR"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + }; + -- Mages + DC.SpellsToUse[DS["SPELL_POLYMORPH"]] = { + Types = {DC.CHARMED}, + IsBest = 0, + Pet = false, + }; + end + + -- }}} + + + -- Thanks to Korean localization team of WoW we have to make an exception.... + -- They found the way to call two different spells the same (Shaman PURGE and Paladin CLEANSE... (both are called "정화") ) + if ((select(2, UnitClass("player"))) == "SHAMAN") then + -- Shamans + DC.SpellsToUse[DS["SPELL_PURGE"]] = { + Types = {DC.ENEMYMAGIC}, + IsBest = 0, + Pet = false, + }; + end + + --[=[ this exception is no longer required since Consume magic no longer exists: http://www.wowwiki.com/Consume_Magic + -- Thanks to Chinese localization team of WoW we have to make anOTHER exception.... :///// + -- They found the way to call two different spells the same (Devour Magic and Consume Magic... (both are called "吞噬魔法" ) + if ((select(2, UnitClass("player"))) == "PRIEST") then + DC.SpellsToUse[DS["PET_FEL_CAST"]] = nil; -- so we remove PET_FEL_CAST. + end + --]=] + + -- // }}} + + + -- New Comm Part used for version checking + -- only if AceComm is here + if LibStub:GetLibrary("AceComm-3.0", true) then + DC.COMMAVAILABLE = true; + LibStub("AceComm-3.0"):RegisterComm("DecursiveVersion", D.OnCommReceived); + end + + T._CatchAllErrors = false; + +end -- // }}} + +local FirstEnable = true; +function D:OnEnable() -- called after PLAYER_LOGIN -- {{{ + + if T._SelfDiagnostic() == 2 then + return false; + end + T._CatchAllErrors = true; -- During init we catch all the errors else, if a library fails we won't know it. + + + -- Register slashes command {{{ + if (FirstEnable) then + SLASH_DECURSIVEDIAG1 = D.CONF.MACRO_DIAG; + SlashCmdList["DECURSIVEDIAG"] = function(msg) + T._SelfDiagnostic(true, true); + end + + SLASH_DECURSIVEPRADD1 = D.CONF.MACRO_PRADD; + SlashCmdList["DECURSIVEPRADD"] = function(msg) + D:AddTargetToPriorityList(); + end + SLASH_DECURSIVEPRCLEAR1 = D.CONF.MACRO_PRCLEAR; + SlashCmdList["DECURSIVEPRCLEAR"] = function(msg) + D:ClearPriorityList(); + end + + SLASH_DECURSIVEPRSHOW1 = D.CONF.MACRO_PRSHOW; + SlashCmdList["DECURSIVEPRSHOW"] = function(msg) + D:ShowHidePriorityListUI(); + end + + SLASH_DECURSIVESKADD1 = D.CONF.MACRO_SKADD; + SlashCmdList["DECURSIVESKADD"] = function(msg) + D:AddTargetToSkipList(); + end + SLASH_DECURSIVESKCLEAR1 = D.CONF.MACRO_SKCLEAR; + SlashCmdList["DECURSIVESKCLEAR"] = function(msg) + D:ClearSkipList(); + end + + SLASH_DECURSIVESKSHOW1 = D.CONF.MACRO_SKSHOW; + SlashCmdList["DECURSIVESKSHOW"] = function(msg) + D:ShowHideSkipListUI(); + end + + SLASH_DECURSIVESHOW1 = D.CONF.MACRO_SHOW; + SlashCmdList["DECURSIVESHOW"] = function(msg) + D:HideBar(0); + end + + SLASH_DECURSIVERESET1 = D.CONF.MACRO_RESET; + SlashCmdList["DECURSIVERESET"] = function(msg) + D:ResetWindow(); + end + + SLASH_DECURSIVEHIDE1 = D.CONF.MACRO_HIDE; + SlashCmdList["DECURSIVEHIDE"] = function(msg) + D:HideBar(1); + end + + SLASH_DECURSIVEOPTION1 = D.CONF.MACRO_OPTION; + SlashCmdList["DECURSIVEOPTION"] = function(msg) + LibStub("AceConfigDialog-3.0"):Open(D.name); + end + + SLASH_DECURSIVESHOWORDER1 = D.CONF.MACRO_SHOW_ORDER; + SlashCmdList["DECURSIVESHOWORDER"] = function(msg) + D:Show_Cure_Order(); + end + end -- }}} + + D:LocalizeBindings (); + + if (FirstEnable) then + -- configure the message frame for Decursive + DecursiveTextFrame:SetFading(true); + DecursiveTextFrame:SetFadeDuration(D.CONF.TEXT_LIFETIME / 3); + DecursiveTextFrame:SetTimeVisible(D.CONF.TEXT_LIFETIME); + + + -- hook the load macro thing {{{ + -- So Decursive will re-update its macro when the macro UI is closed + D:SecureHook("ShowMacroFrame", function () + if not D:IsHooked(MacroPopupFrame, "Hide") then + D:Debug("Hooking MacroPopupFrame:Hide()"); + D:SecureHook(MacroPopupFrame, "Hide", function () D:UpdateMacro(); end); + end + end); -- }}} + + + + end + + -- these events are automatically stopped when the addon is disabled by Ace + + -- Spell changes events + self:RegisterEvent("LEARNED_SPELL_IN_TAB"); + self:RegisterEvent("SPELLS_CHANGED"); + self:RegisterEvent("PLAYER_TALENT_UPDATE"); + self:RegisterEvent("PLAYER_ALIVE"); + + -- Combat detection events + self:RegisterEvent("PLAYER_REGEN_DISABLED","EnterCombat"); + self:RegisterEvent("PLAYER_REGEN_ENABLED","LeaveCombat"); + + -- Raid/Group changes events + self:RegisterEvent("PARTY_MEMBERS_CHANGED", D.GroupChanged, D); + self:RegisterEvent("PARTY_LEADER_CHANGED", D.GroupChanged, D); + self:RegisterEvent("RAID_ROSTER_UPDATE", D.GroupChanged, D); + self:RegisterEvent("PLAYER_FOCUS_CHANGED"); + + -- Player pet detection event (used to find pet spells) + self:RegisterEvent("UNIT_PET"); + + self:RegisterEvent("UNIT_AURA"); + + self:RegisterEvent("PLAYER_TARGET_CHANGED"); + + self:RegisterEvent("UPDATE_MOUSEOVER_UNIT"); + + -- used for Debugging purpose + --self:RegisterEvent("ADDON_ACTION_FORBIDDEN","ADDON_ACTION_FORBIDDEN"); + --self:RegisterEvent("ADDON_ACTION_BLOCKED","ADDON_ACTION_BLOCKED"); + + self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED"); + self:RegisterEvent("SPELL_UPDATE_COOLDOWN"); + + self:ScheduleRepeatingTimer("ScheduledTasks", 0.2); + + -- Configure specific profile dependent data + D:SetConfiguration(); + + if FirstEnable and not D.db.global.NoStartMessages then + D:ColorPrint(0.3, 0.5, 1, L["IS_HERE_MSG"]); + D:ColorPrint(0.3, 0.5, 1, L["SHOW_MSG"]); + + -- schedule a reconfigure in 5 seconds + --self:ScheduleDelayedCall("Dcr_FirstLogConfUpdate", self.ReConfigure, 5, self); + end + + FirstEnable = false; + + D:CheckPlayer(); + T._CatchAllErrors = false; + +end -- // }}} + +function D:SetConfiguration() + + if T._SelfDiagnostic() == 2 then + return false; + end + T._CatchAllErrors = true; -- During init we catch all the errors else, if a library fails we won't know it. + + + D.DcrFullyInitialized = false; + D:CancelDelayedCall("Dcr_LLupdate"); + D:CancelDelayedCall("Dcr_MUFupdate"); + + D.Groups_datas_are_invalid = true; + D.Status = {}; + D.Status.FoundSpells = {}; + D.Status.PlayerOnlyTypes = {}; + D.Status.CuringSpells = {}; + D.Status.CuringSpellsPrio = {}; + D.Status.Blacklisted_Array = {}; + D.Status.UnitNum = 0; + D.Status.DelayedFunctionCalls = {}; + D.Status.DelayedFunctionCallsCount = 0; + D.Status.MaxConcurentUpdateDebuff = 0; + D.Status.PrioChanged = true; + D.Status.last_focus_GUID = false; + D.Status.GroupUpdatedOn = 0; + D.Status.GroupUpdateEvent = 0; + D.Status.UpdateCooldown = 0; + D.Status.MouseOveringMUF = false; + D.Status.TestLayout = false; + D.Status.TestLayoutUNum = 25; + + + -- if we log in and we are already fighting... + if InCombatLockdown() then + D.Status.Combat = true; + end + + D.profile = D.db.profile; -- shortcut + D.classprofile = D.db.class; -- shortcut + + if type (D.profile.OutputWindow) == "string" then + D.Status.OutputWindow = _G[D.profile.OutputWindow]; + end + + D.debugging = D.db.global.debugging; + D.debugFrame = D.Status.OutputWindow; + D.printFrame = D.Status.OutputWindow; + + D:Debug("Loading profile datas..."); + + -- some useful constants + DC.MyClass = (select(2, UnitClass("player"))); + DC.MyName = (self:UnitName("player")); + DC.MyGUID = (UnitGUID("player")); + + if not DC.MyGUID then + DC.MyGUID = "NONE"; + end + + if D.profile.DisableAbolish then + DC.SpellsToUse[DS["SPELL_CURE_DISEASE"]].IsBest = 10; + DC.SpellsToUse[DS["SPELL_CURE_POISON"]].IsBest = 10; + end + + D:Init(); -- initialize Dcr core (set frames display, scans available cleansing spells) + + + D.MicroUnitF.MaxUnit = D.profile.DebuffsFrameMaxCount; + + + D.Groups_datas_are_invalid = true; + D:CreateDropDownFiltersMenu(); -- create per class filters menus + D:CreateModifierOptionMenu(); + + + if D.profile.MF_colors['Chronometers'] then + D.profile.MF_colors[ "COLORCHRONOS"] = D.profile.MF_colors['Chronometers']; + D.profile.MF_colors['Chronometers'] = nil; + end + + D:CreateDropDownMUFcolorsMenu(); -- create MUF color configuration menus + D.MicroUnitF:RegisterMUFcolors(D.profile.MF_colors); -- set the colors as set in the profile + + + + D.Status.Enabled = true; + + + -- set Icon + if not D.Status.HasSpell or D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame then + D:SetIcon(DC.IconOFF); + else + D:SetIcon(DC.IconON); + end + + -- put the updater events at the end of the init so there is no chance they could be called before everything is ready (even if LUA is not multi-threaded... just to stay logical ) + if not D.profile.Hide_LiveList then + self:ScheduleRepeatedCall("Dcr_LLupdate", D.LiveList.Update_Display, D.profile.ScanTime, D.LiveList); + end + + if D.profile.ShowDebuffsFrame then + self:ScheduleRepeatedCall("Dcr_MUFupdate", self.DebuffsFrame_Update, self.profile.DebuffsFrameRefreshRate, self); + end + + D.DcrFullyInitialized = true; -- everything should be OK + D:ShowHideButtons(true); + D:AutoHideShowMUFs(); + + + D.MicroUnitF:Delayed_MFsDisplay_Update(); -- schedule an update of the MUFs display (number of MUF) + D.MicroUnitF:Delayed_Force_FullUpdate(); -- schedule all attributes of exixting MUF to update + + D:SetMinimapIcon(); + + -- code for backward compatibility + if ((not next(D.profile.PrioGUIDtoNAME)) and #D.profile.PriorityList ~= 0) + or ((not next(D.profile.SkipGUIDtoNAME)) and #D.profile.SkipList ~= 0) then + D:ClearPriorityList(); + D:ClearSkipList(); + end + + D:GetUnitArray(); -- get the unit array + D.MicroUnitF:ResetAllPositions (); -- reset all anchors + + T._CatchAllErrors = false; -- During init we catch all the errors else, if a library fails we won't know it. + --D:BetaWarning(); + +end + +function D:OnDisable() -- When the addon is disabled by Ace + D.Status.Enabled = false; + D.DcrFullyInitialized = false; + + D:SetIcon("Interface\\AddOns\\Decursive\\iconOFF.tga"); + + if ( D.profile.ShowDebuffsFrame) then + D.MFContainer:Hide(); + end + + D:CancelAllTimedCalls(); + + -- the disable warning popup : {{{ - + StaticPopupDialogs["Decursive_OnDisableWarning"] = { + text = L["DISABLEWARNING"], + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = false, + showAlert = 1, + }; -- }}} + + LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name); + StaticPopup_Show("Decursive_OnDisableWarning"); +end + +-- A list of some people I personally have problems with. Decursive will not function for them. +-- I don't want this kind of people benefiting from my hard work. +-- Those [Insert appropriate word here] are players you really don't want to meet. Ignorance is just not enough for them... +-- This list will only be used to disable Decursive for them, nothing else will ever happen. +local BADPLAYERS = { + {"|A|r|a|d|o|s", "|C|o|n|s|e|i|l| |d|e|s| |O|m|b|r|e|s|", "|P|A|L|A|D|I|N|"}, -- This one gave me the most horrible experience I ever had in a pickup-group (At the Oculus). He is a terrible leader ; the kind of incompetent person who will accuse you of his own failures. All of this in a perverse and insidious way so he can turn others against you. + + + --{"|A|r|c|h|a|r|o|d|i|m|", "|L|e|s| |S|e|n|t|i|n|e|l|l|e|s|", "|M|A|G|E|"}, -- so I can test if it works. +}; +local BADPLAYERS_READABLE = false; +local GetRealmName = _G.GetRealmName; +function D:CheckPlayer() + + if not BADPLAYERS_READABLE then + BADPLAYERS_READABLE = {}; + D:tcopycallback(BADPLAYERS_READABLE, BADPLAYERS, function (data) return (data:gsub("|", "")) end); + BADPLAYERS = nil; + end + + for i=1, #BADPLAYERS_READABLE do + --D:Debug("TEST 1"); + if BADPLAYERS_READABLE[i][1] == (self:UnitName("player")) then + --D:Debug("TEST 2 name "); + if BADPLAYERS_READABLE[i][2] == GetRealmName() then + --D:Debug("TEST 3 realmname"); + if BADPLAYERS_READABLE[i][3] == (select(2, UnitClass("player"))) then + --D:Debug("TEST 4 unitclass"); + D:Disable(); + break; + end + end + end + end +end + +------------------------------------------------------------------------------- +-- init functions and configuration functions {{{ +------------------------------------------------------------------------------- +function D:Init() --{{{ + + if (D.profile.OutputWindow == nil or not D.profile.OutputWindow) then + D.Status.OutputWindow = DEFAULT_CHAT_FRAME; + D.profile.OutputWindow = "DEFAULT_CHAT_FRAME"; + end + + if not D.db.global.NoStartMessages then + D:Println("%s %s by %s", D.name, D.version, D.author); + end + + D:Debug( "Decursive Initialization started!"); + + + -- SET MF FRAME AS WRITTEN IN THE CURRENT PROFILE {{{ + -- Set the scale and place the MF container correctly + D.MFContainer:Show(); + D.MFContainer:SetScale(D.profile.DebuffsFrameElemScale); + D.MicroUnitF:Place(); + + if (D.profile.ShowDebuffsFrame) then + D.MFContainer:Show(); + else + D.MFContainer:Hide(); + end + D.MFContainerHandle:EnableMouse(not D.profile.HideMUFsHandle); + -- }}} + + -- SET THE LIVE_LIST FRAME AS WRITTEN IN THE CURRENT PROFILE {{{ + + -- Set poristion and scale + DecursiveMainBar:Show(); + DecursiveMainBar:SetScale(D.profile.LiveListScale); + DcrLiveList:Show(); + DcrLiveList:SetScale(D.profile.LiveListScale); + D:PlaceLL(); + + if (D.profile.Hidden) then + DecursiveMainBar:Hide(); + else + DecursiveMainBar:Show(); + end + + -- displays frame according to the current profile + if (D.profile.Hide_LiveList) then + DcrLiveList:Hide(); + else + DcrLiveList:ClearAllPoints(); + DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT"); + DcrLiveList:Show(); + end + + -- set Alpha + DecursiveMainBar:SetAlpha(D.profile.LiveListAlpha); + -- }}} + + if (D.db.global.MacroBind == "NONE") then + D.db.global.MacroBind = false; + end + + + D:ChangeTextFrameDirection(D.profile.CustomeFrameInsertBottom); + + + -- Configure spells + D:Configure(); + +end --}}} + +function D:ReConfigure() --{{{ + + if not D.Status.HasSpell then + return; + end + + D:Debug("|cFFFF0000D:ReConfigure was called!|r"); + + local Spell, spellName; + local GetSpellInfo = _G.GetSpellInfo; + + local Reconfigure = false; + for spellName, Spell in pairs(DC.SpellsToUse) do + -- Do we have that spell? + if GetSpellInfo(spellName) then -- yes + -- is it new? + if not D.Status.FoundSpells[spellName] then -- yes + Reconfigure = true; + break; + elseif DC.SpellsToUse[spellName].EnhancedBy then -- it's not new but there is an enhancement available... + + if DC.SpellsToUse[spellName].EnhancedByCheck() then -- we have it now + if not D.Status.FoundSpells[spellName][3] then -- but not then :) + Reconfigure = true; + break; + end + else -- we do no not + if D.Status.FoundSpells[spellName][3] then -- but we used to :'( + Reconfigure = true; + break; + end + end + end + + elseif D.Status.FoundSpells[spellName] then -- we don't have it anymore... + Reconfigure = true; + break; + end + end + + if Reconfigure == true then + D:Debug("D:ReConfigure RECONFIGURATION!"); + D:Configure(); + return; + end + D:Debug("D:ReConfigure No reconfiguration required!"); + +end --}}} + +function D:Configure() --{{{ + + -- first empty out the old "spellbook" + self.Status.HasSpell = false; + + + local CuringSpells = self.Status.CuringSpells; + + CuringSpells[DC.MAGIC] = false; + CuringSpells[DC.ENEMYMAGIC] = false; + CuringSpells[DC.CURSE] = false; + CuringSpells[DC.POISON] = false; + CuringSpells[DC.DISEASE] = false; + CuringSpells[DC.CHARMED] = false; + + local Spell, spellName, Type, _; + local GetSpellInfo = _G.GetSpellInfo; + local Types = {}; + local OnPlayerOnly = false; + local IsEnhanced = false; + + self:Debug("Configuring Decursive..."); + + for spellName, Spell in pairs(DC.SpellsToUse) do + -- Do we have that spell? + if GetSpellInfo(spellName) then -- yes + Types = DC.SpellsToUse[spellName].Types; + OnPlayerOnly = false; + IsEnhanced = false; + + -- Could it be enhanced by something (a talent for example)? + if DC.SpellsToUse[spellName].EnhancedBy then + --@alpha@ + self:Debug("Enhancement for ", spellName); + --@end-alpha@ + + + if DC.SpellsToUse[spellName].EnhancedByCheck() then -- we have the enhancement + IsEnhanced = true; + + Types = DC.SpellsToUse[spellName].Enhancements.Types; -- set the type to scan to the new ones + + if DC.SpellsToUse[spellName].Enhancements.OnPlayerOnly then -- On the 'player' unit only? + --@alpha@ + self:Debug("Enhancement for %s is for player only", spellName); + --@end-alpha@ + OnPlayerOnly = DC.SpellsToUse[spellName].Enhancements.OnPlayerOnly; + end + end + end + + -- register it + for _, Type in pairs (Types) do + + if not CuringSpells[Type] or DC.SpellsToUse[spellName].IsBest > DC.SpellsToUse[ CuringSpells[Type] ].IsBest then -- we did not already registered this spell or it's not the best spell for this type + + self.Status.FoundSpells[spellName] = {DC.SpellsToUse[spellName].Pet, (select(2, GetSpellInfo(spellName))), IsEnhanced}; + CuringSpells[Type] = spellName; + + if OnPlayerOnly and OnPlayerOnly[Type] then + --@alpha@ + self:Debug("Enhancement for player only for type added",Type); + --@end-alpha@ + self.Status.PlayerOnlyTypes[Type] = true; + else + self.Status.PlayerOnlyTypes[Type] = false; + end + + self:Debug("Spell \"%s\" (%s) registered for type %d ( %s ), PetSpell: ", spellName, D.Status.FoundSpells[spellName][2], Type, DC.TypeNames[Type], D.Status.FoundSpells[spellName][1]); + self.Status.HasSpell = true; + end + end + + end + end + + -- Verify the cure order list (if it was damaged) + self:CheckCureOrder (); + -- Set the appropriate priorities according to debuffs types + self:SetCureOrder (); + + LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name); + + if (not self.Status.HasSpell) then + return; + end + +end --}}} + +function D:GetSpellsTranslations(FromDIAG) + local GetSpellInfo = _G.GetSpellInfo; + + local Spells = {}; + + + Spells = { + ["SPELL_POLYMORPH"] = { 118, }, + ["SPELL_CYCLONE"] = { 33786, }, + ["SPELL_CURE_DISEASE"] = { 528, }, + ["SPELL_ABOLISH_DISEASE"] = { 552, }, + ["SPELL_PURIFY"] = { 1152, }, -- paladins + ["SPELL_CLEANSE"] = { 4987, }, + ["SPELL_DISPELL_MAGIC"] = { 527, 988, }, + ["SPELL_CURE_TOXINS"] = { 526, }, -- shamans + ["SPELL_CURE_POISON"] = { 8946, }, + ["SPELL_ABOLISH_POISON"] = { 2893, }, + ["SPELL_REMOVE_LESSER_CURSE"] = { 475, }, -- Mages + ["SPELL_REMOVE_CURSE"] = { 2782, }, -- Druids + ['SPELL_TRANQUILIZING_SHOT'] = { 19801, }, + ['SPELL_HEX'] = { 51514, }, -- shamans + ["CLEANSE_SPIRIT"] = { 51886, }, + ["SPELL_PURGE"] = { 370, 8012, }, + ["PET_FEL_CAST"] = { 19505, 19731, 19734, 19736, 27276, 27277,}, + ["SPELL_FEAR"] = { 5782 }, + ["PET_DOOM_CAST"] = { 527, 988, }, + ["CURSEOFTONGUES"] = { 1714, 11719, }, + ["DCR_LOC_SILENCE"] = { 15487, }, + ["DCR_LOC_MINDVISION"] = { 2096, 10909, }, + ["DREAMLESSSLEEP"] = { 15822, }, + ["GDREAMLESSSLEEP"] = { 24360, }, + ["MDREAMLESSSLEEP"] = { 28504, }, + ["ANCIENTHYSTERIA"] = { 19372, }, + ["IGNITE"] = { 19659, }, + ["TAINTEDMIND"] = { 16567, }, + ["MAGMASHAKLES"] = { 19496, }, + ["CRIPLES"] = { 33787, }, + ["DUSTCLOUD"] = { 26072, }, + ["WIDOWSEMBRACE"] = { 28732, }, + ["SONICBURST"] = { 39052, }, + ["DELUSIONOFJINDO"] = { 24306, }, + ["MUTATINGINJECTION"] = { 28169, }, + ['Phase Shift'] = { 4511, }, + ['Banish'] = { 710, 18647, }, + ['Frost Trap Aura'] = { 13810, }, + ['Arcane Blast'] = { 30451, }, + ['Prowl'] = { 5215, 6783, 9913, 24450, }, + ['Stealth'] = { 1784, 1785, 1786, 1787, }, + ['Shadowmeld'] = { 58984, }, + ['Invisibility'] = { 66, }, + ['Lesser Invisibility'] = { 7870, }, + ['Ice Armor'] = { 7302, 7320, 10219, 10220, 27124, }, + ['Unstable Affliction'] = { 30108, 30404, 30405, }, + ['Dampen Magic'] = { 604, }, + ['Amplify Magic'] = { 1008, }, + ['TALENT_BODY_AND_SOUL'] = { 64129, 65081, }, + ['TALENT_ARCANE_POWER'] = { 12042, }, --temp to test + ['DARK_MATTER'] = { 59868, }, --temp to test + --['YOGGG_DOMINATE_MIND'] = { 63042, }, --temp to test + --['STALVAN_CURSE'] = { 3105, }, --temp to test + }; + + DC.ttest = Spells; + + + + local alpha = false; + --@alpha@ + alpha = true; + --@end-alpha@ + local Sname, Sids, Sid, _, ok; + ok = true; + for Sname, Sids in pairs(Spells) do + for _, Sid in ipairs(Sids) do + + if _ == 1 then + DS[Sname] = (GetSpellInfo(Sid)); + if not DS[Sname] then + if random (1, 9000) == 1 or FromDIAG then + D:AddDebugText("SpellID:", Sid, "no longer exists. This was supposed to represent the spell", Sname); + D:errln("SpellID:", Sid, "no longer exists. This was supposed to represent the spell", Sname); + end + DS[Sname] = "_LOST SPELL_"; + end + elseif FromDIAG then + if (GetSpellInfo(Sid)) and DS[Sname] ~= (GetSpellInfo(Sid)) then + + D:AddDebugText("Spell IDs", Sids[1] , "and", Sid, "have different translations:", DS[Sname], "and", (GetSpellInfo(Sid)) ); + + D:errln("Spell IDs", Sids[1] , "and", Sid, "have different translations:", DS[Sname], "and", (GetSpellInfo(Sid)) ); + + D:errln("Please report this to ARCHARODIM+DcrReport@teaser.fr"); + + ok = false; + elseif not DS[Sname] then + + D:AddDebugText("SpellID:", Sid, "no longer exist. This was supposed to represent the spell", Sname); + + D:errln("SpellID:", Sid, "no longer exists. This was supposed to represent the spell", Sname); + end + end + + end + end + + -- get a 'Rank #' string exemple (workaround due to the way the + -- polymorph spell variants are handled in WoW 2.3) + + DC.RANKNUMTRANS = (select(2, GetSpellInfo(118))); + + return ok; + +end + + +-- Create the macro for Decursive +-- This macro will cast the first spell (priority) + +-- NEW SetBindingMacro("KEY", "macroname"|macroid) +-- UPDATED name,texture,body,isLocal = GetMacroInfo(id|"name") - Now takes ID or name +-- UPDATED DeleteMacro() -- as above +-- UPDATED EditMacro() -- as above +-- UPDATED PickupMacro() -- as above +-- CreateMacro("name", icon, "body", local) + + +function D:UpdateMacro () + + + if D.profile.DisableMacroCreation then + return false; + end + + if InCombatLockdown() then + D:AddDelayedFunctionCall ( + "UpdateMacro", self.UpdateMacro, + self); + return false; + end + D:Debug("UpdateMacro called"); + + + local CuringSpellsPrio = D.Status.CuringSpellsPrio; + local ReversedCureOrder = D.Status.ReversedCureOrder; + local CuringSpells = D.Status.CuringSpells; + + + -- Get an ordered spell table + local Spells = {}; + for Spell, Prio in pairs(D.Status.CuringSpellsPrio) do + Spells[Prio] = Spell; + end + + if (next (Spells)) then + for i=1,4 do + if (not Spells[i]) then + table.insert (Spells, CuringSpells[ReversedCureOrder[1] ]); + end + end + end + + local MacroParameters = { + D.CONF.MACRONAME, + 1, -- icon index + next(Spells) and string.format("/stopcasting\n/cast [target=mouseover,nomod,exists] %s; [target=mouseover,exists,mod:ctrl] %s; [target=mouseover,exists,mod:shift] %s", unpack(Spells)) or "/script Dcr:Println('"..L["NOSPELL"].."')", + 0, -- per account + }; + + --D:PrintLiteral(GetMacroIndexByName(D.CONF.MACRONAME)); + if GetMacroIndexByName(D.CONF.MACRONAME) ~= 0 then + if not D.profile.AllowMacroEdit then + EditMacro(D.CONF.MACRONAME, unpack(MacroParameters)); + D:Debug("Macro updated"); + else + D:Debug("Macro not updated due to AllowMacroEdit"); + end + elseif (GetNumMacros()) < 36 then + CreateMacro(unpack(MacroParameters)); + else + D:errln("Too many macros exist, Decursive cannot create its macro"); + return false; + end + + + D:SetMacroKey(D.db.global.MacroBind); + + return true; + +end + + + +-- }}} + +function D:SetDateAndRevision (Date, Revision) + if not D.TextVersion then + D.TextVersion = GetAddOnMetadata("Decursive", "Version"); + D.Revision = 0; + end + + local Rev = tonumber((string.gsub(Revision, "%$Revision: (%d+) %$", "%1"))); + + if Rev and D.Revision < Rev then + D.Revision = Rev; + D.date = Date:gsub("%$Date: (.-) %$", "%1"); + D.version = string.format("%s (|cFF11CCAARevision: %d|r)", D.TextVersion, Rev); + end +end + +--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $"); + +function D:LocalizeBindings () + + BINDING_NAME_DCRSHOW = L["BINDING_NAME_DCRSHOW"]; + BINDING_NAME_DCRMUFSHOWHIDE = L["BINDING_NAME_DCRMUFSHOWHIDE"]; + BINDING_NAME_DCRPRADD = L["BINDING_NAME_DCRPRADD"]; + BINDING_NAME_DCRPRCLEAR = L["BINDING_NAME_DCRPRCLEAR"]; + BINDING_NAME_DCRPRLIST = L["BINDING_NAME_DCRPRLIST"]; + BINDING_NAME_DCRPRSHOW = L["BINDING_NAME_DCRPRSHOW"]; + BINDING_NAME_DCRSKADD = L["BINDING_NAME_DCRSKADD"]; + BINDING_NAME_DCRSKCLEAR = L["BINDING_NAME_DCRSKCLEAR"]; + BINDING_NAME_DCRSKLIST = L["BINDING_NAME_DCRSKLIST"]; + BINDING_NAME_DCRSKSHOW = L["BINDING_NAME_DCRSKSHOW"]; + BINDING_NAME_DCRSHOWOPTION = L["BINDING_NAME_DCRSHOWOPTION"]; + +end + +D.Revision = "d3885c5"; +D.date = "2010-09-06T22:38:15Z"; +D.version = "2.5.1-6-gd3885c5"; +do + + if D.date ~= "@project".."-date-iso@" then + + -- get a fucking table of D.date so we can get a fucking timestamp with time() because @project-timestamp@ doesn't fucking work :/ FUCK!! + + --local example = "2008-05-01T12:34:56Z"; + + local year, month, day, hour, min, sec = string.match( D.date, "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)"); + local projectDate = {["year"] = year, ["month"] = month, ["day"] = day, ["hour"] = hour, ["min"] = min, ["sec"] = sec}; + + D.VersionTimeStamp = time(projectDate); + else + D.VersionTimeStamp = 0; + end + +end + +T._LoadedFiles["DCR_init.lua"] = "2.5.1-6-gd3885c5"; + +------------------------------------------------------------------------------- + +--[======[ +TEST to see what keyword substitutions are actually working.... DAMN!!!! + +Simple replacements + +@file-revision@ + Turns into the current revision of the file in integer form. e.g. 1234 + Note: does not work for git +@project-revision@ + Turns into the highest revision of the entire project in integer form. e.g. 1234 + Note: does not work for git +77a9e9a9e8ed6c70acc4f0c7e3ac7452af3aee90 + Turns into the hash of the file in hex form. e.g. 106c634df4b3dd4691bf24e148a23e9af35165ea + Note: does not work for svn +d3885c5af25c7d93228b124e59ee618a419fe96f + Turns into the hash of the entire project in hex form. e.g. 106c634df4b3dd4691bf24e148a23e9af35165ea + Note: does not work for svn +77a9e9a + Turns into the abbreviated hash of the file in hex form. e.g. 106c63 Note: does not work for svn +d3885c5 + Turns into the abbreviated hash of the entire project in hex form. e.g. 106c63 + Note: does not work for svn +Archarodim + Turns into the last author of the file. e.g. ckknight +Archarodim + Turns into the last author of the entire project. e.g. ckknight +2010-09-05T23:12:38Z + Turns into the last changed date (by UTC) of the file in ISO 8601. e.g. 2008-05-01T12:34:56Z +2010-09-06T22:38:15Z + Turns into the last changed date (by UTC) of the entire project in ISO 8601. e.g. 2008-05-01T12:34:56Z +20100905231238 + Turns into the last changed date (by UTC) of the file in a readable integer fashion. e.g. 20080501123456 +20100906223815 + Turns into the last changed date (by UTC) of the entire project in a readable integer fashion. e.g. 2008050123456 +@file-timestamp@ + Turns into the last changed date (by UTC) of the file in POSIX timestamp. e.g. 1209663296 +@project-timestamp@ + Turns into the last changed date (by UTC) of the entire project in POSIX timestamp. e.g. 1209663296 +2.5.1-6-gd3885c5 + Turns into an approximate version of the project. The tag name if on a tag, otherwise it's up to the repo. + :SVN returns something like "r1234" + :Git returns something like "v0.1-873fc1" + :Mercurial returns something like "r1234". + +--]======] diff --git a/Decursive/Dcr_DIAG.lua b/Decursive/Dcr_DIAG.lua new file mode 100644 index 0000000..ff63954 --- /dev/null +++ b/Decursive/Dcr_DIAG.lua @@ -0,0 +1,430 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + + +local addonName, T = ...; +DecursiveRootTable = T; + +DecursiveInstallCorrupted = false; + +DcrC = {}; -- needed until we get rid of the xml based UI. +T._C = DcrC; +local DC = DcrC; + +DC.StartTime = GetTime(); +DC.MyClass = "unknown"; + +T._LoadedFiles = { + ["Dcr_DIAG.xml"] = false, + ["Dcr_DIAG.lua"] = false, + ["DCR_init.lua"] = false, + ["Dcr_LDB.lua"] = false, + ["Dcr_utils.lua"] = false, + + ["enUS.lua"] = false, + ["frFR.lua"] = false, + ["deDE.lua"] = false, + ["zhTW.lua"] = false, + ["esES.lua"] = false, + ["koKR.lua"] = false, + ["zhCN.lua"] = false, + ["ruRU.lua"] = false, + + ["Dcr_opt.lua"] = false, + ["Dcr_Events.lua"] = false, + ["Dcr_Raid.lua"] = false, + ["Decursive.lua"] = false, + ["Dcr_lists.lua"] = false, + ["Dcr_DebuffsFrame.lua"] = false, + ["Dcr_LiveList.lua"] = false, + + ["Dcr_DebuffsFrame.xml"] = false, + ["Dcr_lists.xml"] = false, + ["Dcr_LiveList.xml"] = false, + ["Decursive.xml"] = false, + +}; + +-- This self diagnostic functionality is here to give clear instructions to the +-- user when something goes wrong with the Ace shared libraries or when a +-- Decursive file could not be loaded. + +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = false, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end + +-- Decursive LUA error manager and debug reporting functions {{{ + +local function NiceTime() + return tonumber(("%.4f"):format(GetTime() - DC.StartTime)); +end + +local function print(t) + if DEFAULT_CHAT_FRAME then + DEFAULT_CHAT_FRAME:AddMessage(t); + end +end + +-- taken from AceConsole-2.0 +local function tostring_args(a1, ...) + if select('#', ...) < 1 then + return tostring(a1) + end + return tostring(a1), tostring_args(...) +end + +T._DebugText = ""; +-- inspired from BugSack +function T._DebugFrameOnTextChanged(frame) + if frame:GetText() ~= T._DebugText then + frame:SetText(T._DebugText) + end + frame:GetParent():UpdateScrollChildRect() + local _, m = DecursiveDebuggingFrameScrollScrollBar:GetMinMaxValues() + if m > 0 and frame.max ~= m then + frame.max = m + DecursiveDebuggingFrameScrollScrollBar:SetValue(0) + end +end + +T._DebugTextTable = {}; +local DebugTextTable = T._DebugTextTable; +local Reported = {}; +local GetFramerate = _G.GetFramerate; +local GetNetStats = _G.GetNetStats; +function T._AddDebugText(a1, ...) + + if T.Dcr.Debug then + T.Dcr:Debug("Error processed"); + end + local text = ""; + + if select('#', ...) > 0 then + text = strjoin(", ", tostring_args(a1, ...)) + else + text = tostring(a1); + end + + if not Reported[text] then + table.insert (DebugTextTable, ("\n------\n%.4f (%d-%d): %s -|count: "):format(NiceTime(), select(3, GetNetStats()), GetFramerate(), text) ); + table.insert (DebugTextTable, 1); + Reported[text] = #DebugTextTable; + else + DebugTextTable[Reported[text]] = DebugTextTable[Reported[text]] + 1; + end +end + +local AddDebugText = T._AddDebugText; + +-- The error handler +local ProperErrorHandler = false; +local IsReporting = false; + +local version, build, date, tocversion = GetBuildInfo(); + +T._CatchAllErrors = false; +T._tocversion = tocversion; + +function T._DecursiveErrorHandler(err, ...) + + -- second blizzard bug HotFix + ---[=[ + if ScriptErrorsFrameScrollFrameText then + if not ScriptErrorsFrameScrollFrameText.cursorOffset then + ScriptErrorsFrameScrollFrameText.cursorOffset = 0; + if ( GetCVarBool("scriptErrors") ) then + print("Decursive |cFF00FF00HotFix to Blizzard_DebugTools:|r |cFFFF0000ScriptErrorsFrameScrollFrameText.cursorOffset was nil (check for Lua errors using BugGrabber and BugSack)|r"); + end + end + end + --]=] + + err = tostring(err); + + --Add a check to see if the error is happening inside the Blizzard debug tool himself... + if (err:lower()):find("blizzard_debugtools") then + if ( GetCVarBool("scriptErrors") ) then + print (("|cFFFF0000%s|r"):format(err)); + end + return; + end + + if not IsReporting and (T._CatchAllErrors or (err:lower()):find("decursive") and not (err:lower()):find("\\libs\\")) then + T._CatchAllErrors = false; -- Errors are unacceptable so one is enough, no need to get all subsequent errors. + IsReporting = true; + AddDebugText(err, debugstack(2), ...); + if T.Dcr then + T.Dcr:Debug("Error recorded"); + end + IsReporting = false; + end + + if ProperErrorHandler then + return ProperErrorHandler( err, ... ); -- returning this way prevents this function from appearing in the stack + end +end + +function T._HookErrorHandler() + if not ProperErrorHandler then + + ---[=[ + -- seems to be required even in 3.3 because debuglocals, unlike debugstack is sensitive to intermediates so we need to add 1 to its level for each intermediate + if GetCVarBool("scriptErrors") and not BugGrabber then + -- this whole block is a bad idea, it could create cascading tainting issues if an error occur in a Blizz secured code... + -- it is enabled only if the user turned Lua error reporting on otherwise no one cares about debuglocals being useless. + T._original_debuglocals = _G.debuglocals; + _G.debuglocals = function (level) + local ADDLEVEL = 2; -- 2 is for this function and _DecursiveErrorHandler + + -- test for other add-on that hooks the error handler and increment ADDLEVEL + if QuestHelper_Errors then + ADDLEVEL = ADDLEVEL + 1; + end + + if Swatter and Swatter.OnError then + ADDLEVEL = ADDLEVEL + 1; + end + + + return T._original_debuglocals(level + ADDLEVEL) or "Sometimes debuglocals() returns nothing, it's one of those times... (FYI: This last sentence (only) is a HotFix from Decursive to prevent a C stack overflow in the new Blizzard error handler and thus giving you the opportunity to send this debug report to the author of the problematic add-on so he/she can fix it)"; + end; + end + --]=] + + + ProperErrorHandler = geterrorhandler(); + seterrorhandler(T._DecursiveErrorHandler); + end +end + +--}}} + + +-- Dev version usage warning {{{ +-- the beautiful beta notice popup : {{{ - +StaticPopupDialogs["Decursive_Notice_Frame"] = { + text = "|cFFFF0000Decursive Notice:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = false, + showAlert = 1, +}; -- }}} + + + +-- }}} + +do + T._DiagStatus = false; + + local PrintMessage = function (message, ...) if T._DiagStatus ~= 2 then T.Dcr:Print("|cFFFFAA55Self diagnostic:|r ", format(message, ...)); end end; + + -- {{{ + function T._SelfDiagnostic (force, FromCommand) + + -- will not executes several times unless forced + if not force and T._DiagStatus then + return T._DiagStatus; + end + + T._DiagStatus = 0; -- will be set to 1 if the diagnostic fails + + -- Table with all the required libraries with their current revision at Decursive release time. + + --LibStub:GetLibrary + local UseLibStub = { + ["AceAddon-3.0"] = 5, + ["AceConsole-3.0"] = 7, + ["AceEvent-3.0"] = 3, + ["AceTimer-3.0"] = 5, + ["AceHook-3.0"] = 5, + ["AceDB-3.0"] = 21, + ["AceDBOptions-3.0"] = 12, + ["AceLocale-3.0"] = 2, + ["AceComm-3.0"] = 6, + + ["AceGUI-3.0"] = 33, + ["AceConfig-3.0"] = 2, + ["AceConfigRegistry-3.0"] = 12, + ["AceConfigCmd-3.0"] = 12, + ["AceConfigDialog-3.0"] = 49, + + ["LibDataBroker-1.1"] = 4, + ["LibDBIcon-1.0"] = 14, + ["LibQTip-1.0"] = 34, + ["CallbackHandler-1.0"] = 5, + }; + + local GenericErrorMessage1 = "Decursive could not initialize properly because one or several of the required shared libraries (at least |cFF00FF00AceLibrary or LibStub|r) could not be found.\n"; + local GenericErrorMessage2 = "Try to re-install Decursive from its original archive or use the |cFF00FF00Curse client|r (Curse.com) to update |cFFFF0000ALL|r your add-ons properly."; + + local ErrorFound = false; + local Errors = {}; + local FatalOccured = false; + + -- Check each version of the required libraries that use LibStub + if LibStub then + for k,v in pairs(UseLibStub) do + if LibStub:GetLibrary(k, true) then + if (select(2, LibStub:GetLibrary(k))) < v then + table.insert(Errors, ("The shared library |cFF00FF00%s|r is out-dated, revision |cFF0077FF%s|r at least is required.\n"):format(k, tostring(v))); + end + else + table.insert(Errors, ("The shared library |cFF00FF00%s|r could not be found!!!\n"):format(k)); + FatalOccured = true; + end + end + else + table.insert(Errors, GenericErrorMessage1); + FatalOccured = true; + end + + + -- check if all Decursive files are loaded + local mixedFileVersionsdetection = {}; + local MixedVersionsCount = 0; + if not FatalOccured then + for k,v in pairs (T._LoadedFiles) do + if v and v ~= "@pro" .. "ject-version@" and not mixedFileVersionsdetection[v] then + mixedFileVersionsdetection[v] = k; + MixedVersionsCount = MixedVersionsCount + 1; + end + + if not v then + table.insert(Errors, ("The Decursive file |cFF00FF00%s|r could not be loaded!\n"):format(k)); + FatalOccured = true; + end + end + end + + if MixedVersionsCount > 1 then + -- some mixed files were detected + local MixedDetails = "|cFFFF5599The versions of these files differ|r:\n\n"; + for k,v in pairs (mixedFileVersionsdetection) do + MixedDetails = ("%s%s --> %s\n"):format(MixedDetails, v, k); + end + + table.insert(Errors, ("Decursive installation is corrupted, mixed versions detected!\n\n%s\n"):format(MixedDetails)); + FatalOccured = true; + end + + if #Errors > 0 then + local ErrorString = ("|cFFFF0000%d error(s)|r found while loading Decursive:\n\n"):format(#Errors); + + for k, v in pairs (Errors) do + ErrorString = ErrorString .. v; + end + + ErrorString = ErrorString .. "\n\n" .. GenericErrorMessage2; + + T._FatalError(ErrorString); + T._DiagStatus = FatalOccured and 2 or 1; + end + + -- if the diagnostic was requested by the user, we also test AceEvent functionalities {{{ - + if force and FromCommand and T._DiagStatus == 0 then + + PrintMessage("|cFF00FF00No problem found in shared libraries or Decursive files!|r"); + + PrintMessage("Now checking spell translations..."); + if T.Dcr:GetSpellsTranslations(true) then + PrintMessage("|cFF00FF00No error found in spell translations!|r"); + end + + AddDebugText("Now checking the event management library..."); + PrintMessage("Now checking the event management library..."); + PrintMessage("If, in about 2 seconds, the message \"|cFF00FF00Event library functionning properly|r\" does not appear then there is a problem"); + + local OneTimeEvent = "not set"; local ReapeatingEventRate = 1; local ReapeatingEventCount = 0; local CustomEvent = "DCR_TEST_DIAG_EVENT"; local CustomEventCaught = "not set"; + local ConfirmOneTimeEventMessage = "That was a good time!"; + local ConfirmCustomEventMessage = "I was really caught!"; + + -- Register a curstom event + T.Dcr:RegisterMessage(CustomEvent, function(message, DiagTestArg1) CustomEventCaught = DiagTestArg1; T.Dcr:Debug("CustomEvent callback executed"); end); + + -- Schedule a function call in 0.5s + T.Dcr:ScheduleDelayedCall("DcrDiagOneTimeEvent", function(DiagTestArg2) OneTimeEvent = DiagTestArg2; T.Dcr:Debug("OneTimeEvent callback executed"); end, ReapeatingEventRate / 2, ConfirmOneTimeEventMessage); + + -- Set a repeating function call that will check for other test event completion + T.Dcr:ScheduleRepeatedCall("DcrDiagRepeat", + function (argTest) + local argtestdone = false; + if not argtestdone and argTest ~= "test" then + AddDebugText("Event lib management error: argument could not be read!"); + PrintMessage("|cFFFF0000Event lib management error: argument could not be read!|r"); + argtestdone = true; + end + + if OneTimeEvent == ConfirmOneTimeEventMessage and CustomEventCaught == ConfirmCustomEventMessage then + T.Dcr:CancelDelayedCall("DcrDiagRepeat"); + T.Dcr:UnregisterMessage(CustomEvent); + PrintMessage("|cFF00FF00Event library functionning properly!|r"); + PrintMessage("|cFF00FF00Everything seems to be OK.|r"); + AddDebugText("Event library functionning properly, Everything seems to be OK"); + return; + else + T.Dcr:Debug(OneTimeEvent, "is not", ConfirmOneTimeEventMessage, "and", CustomEventCaught, "is not", ConfirmCustomEventMessage); + end + + -- cast the custom event + T.Dcr:SendMessage(CustomEvent, ConfirmCustomEventMessage); + + if ReapeatingEventCount == 4 then + AddDebugText("A problem occured, OneTimeEvent:", OneTimeEvent, "CustomEventCaught:", CustomEventCaught); + PrintMessage("|cFFFF0000A problem occured, OneTimeEvent='%q', CustomEventCaught='%q'|r", OneTimeEvent, CustomEventCaught); + T.Dcr:CancelDelayedCall("DcrDiagRepeat"); + T.Dcr:UnregisterMessage(CustomEvent); + return; + end + + ReapeatingEventCount = ReapeatingEventCount + 1; + + end, + ReapeatingEventRate, "test"); + + end -- }}} + + if T._DiagStatus == 0 then + DecursiveInstallCorrupted = nil; + end + + return T._DiagStatus; + + + end -- }}} +end + + +T._LoadedFiles["Dcr_DIAG.lua"] = "2.5.1-6-gd3885c5"; diff --git a/Decursive/Dcr_DIAG.xml b/Decursive/Dcr_DIAG.xml new file mode 100644 index 0000000..90d0301 --- /dev/null +++ b/Decursive/Dcr_DIAG.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self:SetAutoFocus(false) + + + self:SetFocus() + + + DecursiveRootTable._DebugFrameOnTextChanged(self) + + + self:ClearFocus() + + + + + + + + + + + + diff --git a/Decursive/Dcr_DebuffsFrame.lua b/Decursive/Dcr_DebuffsFrame.lua new file mode 100644 index 0000000..f293213 --- /dev/null +++ b/Decursive/Dcr_DebuffsFrame.lua @@ -0,0 +1,1654 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; + +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} + +if not T._LoadedFiles or not T._LoadedFiles["Dcr_lists.xml"] or not T._LoadedFiles["Dcr_lists.lua"] then -- XML are loaded even if LUA syntax errors exixts + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_lists.xml or Dcr_lists.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = T.Dcr; + + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + + +-- NS def +D.MicroUnitF = {}; +-- create a shortcut +local MicroUnitF = D.MicroUnitF; +MicroUnitF.prototype = {}; +MicroUnitF.metatable ={ __index = MicroUnitF.prototype }; + +function MicroUnitF:new(...) + local instance = setmetatable({}, self.metatable); + instance:init(...); + return instance; +end + + + +-- since there are tens of thousands of globals defined at all times, lets use some locals! +local BOOKTYPE_PET = BOOKTYPE_PET; +local BOOKTYPE_SPELL = BOOKTYPE_SPELL; + +-- Init object factory defaults +--MicroUnitF.ExistingPerID = {}; +MicroUnitF.ExistingPerUNIT = {}; +MicroUnitF.ExistingPerNum = {}; +MicroUnitF.UnitToMUF = {}; +MicroUnitF.Number = 0; +MicroUnitF.UnitShown = 0; +MicroUnitF.UnitsDebuffedInRange = 0; +MicroUnitF.DraggingHandle = false; +D.ForLLDebuffedUnitsNum = 0; + + +-- using power 2 values just to OR them but only CHARMED_STATUS is ORed (it's a C style bitfield) +local NORMAL = DC.NORMAL; +local ABSENT = DC.ABSENT; +local FAR = DC.FAR; +local STEALTHED = DC.STEALTHED; +local BLACKLISTED = DC.BLACKLISTED; +local AFFLICTED = DC.AFFLICTED; +local AFFLICTED_NIR = DC.AFFLICTED_NIR; +local CHARMED_STATUS = DC.CHARMED_STATUS; +local AFFLICTED_AND_CHARMED = DC.AFFLICTED_AND_CHARMED; + + +-- Those are the different colors used for the MUFs main texture +local MF_colors = { }; + +local unpack = _G.unpack; +local select = _G.select; +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local GetTime = _G.GetTime; +local PlaySoundFile = _G.PlaySoundFile; +local IsControlKeyDown = _G.IsControlKeyDown; +local floor = _G.math.floor; +local table = _G.table; +local t_insert = _G.table.insert; +local str_format = _G.string.format; +local str_sub = _G.string.gsub; +local table = _G.table; +local string = _G.string; +local UnitExists = _G.UnitExists; +local UnitClass = _G.UnitClass; +local fmod = _G.math.fmod; +local UnitIsUnit = _G.UnitIsUnit; +local str_upper = _G.string.upper; +local InCombatLockdown = _G.InCombatLockdown; +local UnitAura = _G.UnitAura; +local GetRaidTargetIndex= _G.GetRaidTargetIndex; + + +DC.AvailableButtonsReadable = { -- {{{ + ["%s1"] = L["HLP_LEFTCLICK"], -- left mouse button + ["%s2"] = L["HLP_RIGHTCLICK"], -- right mouse button + ["%s3"] = L["HLP_MIDDLECLICK"], -- middle mouse button + ["ctrl-%s1"] = L["CTRL"] .. "-" .. L["HLP_LEFTCLICK"], + ["ctrl-%s2"] = L["CTRL"] .. "-" .. L["HLP_RIGHTCLICK"], + ["ctrl-%s3"] = L["CTRL"] .. "-" .. L["HLP_MIDDLECLICK"], + ["shift-%s1"] = L["SHIFT"] .. "-" .. L["HLP_LEFTCLICK"], + ["shift-%s2"] = L["SHIFT"] .. "-" .. L["HLP_RIGHTCLICK"], + ["shift-%s3"] = L["SHIFT"] .. "-" .. L["HLP_MIDDLECLICK"], + ["alt-%s1"] = L["ALT"] .. "-" .. L["HLP_LEFTCLICK"], + ["alt-%s2"] = L["ALT"] .. "-" .. L["HLP_RIGHTCLICK"], + ["alt-%s3"] = L["ALT"] .. "-" .. L["HLP_MIDDLECLICK"], + -- 3, -- middle mouse button || RESERVED FOR TARGETTING +}; -- }}} + +-- modifier for the macro +local AvailableModifier = { -- {{{ + "shift","ctrl","alt", +} -- }}} + +-- MicroUnitF STATIC methods {{{ + +-- Updates the color table +function MicroUnitF:RegisterMUFcolors () + -- MF_colors = D.profile.MF_colors; -- this should be enough but is not because D.profile can change at unexpected times.... + D:tcopy(MF_colors, D.profile.MF_colors); +end + +-- defines what is printed when the object is read as a string +function MicroUnitF:ToString() -- {{{ + return "Decursive Micro Unit Frame Object"; +end -- }}} + +-- The Factory for MicroUnitF objects +function MicroUnitF:Create(Unit, ID) -- {{{ + + if InCombatLockdown() then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ( + "Create"..Unit, self.Create, + Unit, ID); + return false; + end + + -- if we attempt to create a MUF that already exists, update it instead + if (self.ExistingPerUNIT[Unit]) then + return self.ExistingPerUNIT[Unit]; + end + + self.Number = self.Number + 1; + + -- create a new MUF object + self.ExistingPerUNIT[Unit] = self:new(D.MFContainer, Unit, self.Number, ID); + + self.ExistingPerNum[self.Number] = self.ExistingPerUNIT[Unit]; + + return self.ExistingPerUNIT[Unit]; +end -- }}} + +-- return a MUF object if it exists, nil otherwise +--[=[ +function MicroUnitF:Exists(IdOrNum) -- {{{ + return self.ExistingPerID[IdOrNum] or self.ExistingPerNum[IdOrNum]; +end +--]=] +-- }}} + +-- return the number MUFs we can use +function MicroUnitF:MFUsableNumber () -- {{{ + return ((self.MaxUnit > D.Status.UnitNum) and D.Status.UnitNum or self.MaxUnit); +end -- }}} + +-- this is used when a setting influencing MUF's position is changed +function MicroUnitF:ResetAllPositions () -- {{{ + + if InCombatLockdown() then + D:AddDelayedFunctionCall ( + "ResetAllPositions", self.ResetAllPositions, + self); + return false; + end + + local MF, i; + + D:Debug("Resetting all MF position"); + + self:Delayed_MFsDisplay_Update (); + + local Unit_Array = D.Status.Unit_Array; + + for i=1, #Unit_Array do + MF = self.ExistingPerUNIT[ Unit_Array[i] ] + + if MF then + MF.Frame:SetPoint(unpack(self:GiveMFAnchor(i))); + end + end +end -- }}} + +-- return the anchor of a given MUF depending on its creation ID +do +local Anchor = { "TOPLEFT", x, y, "TOPLEFT" }; +function MicroUnitF:GiveMFAnchor (ID) -- {{{ + local LineNum = floor( (ID - 1) / D.profile.DebuffsFramePerline); + local NumOnLine = fmod( (ID - 1), D.profile.DebuffsFramePerline); + + local x = (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0) + NumOnLine * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing); + local y = (D.profile.DebuffsFrameGrowToTop and -1 or 1) * LineNum * ((-1 * D.profile.DebuffsFrameYSpacing) - DC.MFSIZE); + + Anchor[2] = x; Anchor[3] = y; + + return Anchor; +end +end-- }}} + + +function MicroUnitF:Delayed_MFsDisplay_Update () + if D.profile.ShowDebuffsFrame then + D:ScheduleDelayedCall("Dcr_UpdateMUFsNUM", self.MFsDisplay_Update, 1.5, self); + end +end + +-- This update the MUFs display, show and hide MUFs as necessary +function MicroUnitF:MFsDisplay_Update () -- {{{ + + if (not D.profile.ShowDebuffsFrame) then + return false; -- XXX we will have to call this function again when we show the MUFs + end + + -- This function cannot do anything if we are fighting + if InCombatLockdown() then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ( + "UpdateMicroUnitFrameDisplay", self.MFsDisplay_Update, + self); + return false; + end + + -- Get an up to date unit array if necessary + D:GetUnitArray(); -- this is the only place where GetUnitArray() is called directly + + -- ======= + -- Begin + -- ======= + + -- get the number of MUFs we should display + local NumToShow = self:MFUsableNumber(); + + + -- if we don't have all the MUFs needed then return, we are not ready + if (self.Number < NumToShow) then + self:Delayed_MFsDisplay_Update (); + return false; + end + + + local MF = false; + local i = 1; + local Old_UnitShown = self.UnitShown; + + + D:Debug("Update required: NumToShow = ", NumToShow); + + local Unit_Array_UnitToGUID = D.Status.Unit_Array_UnitToGUID; + local Unit_Array = D.Status.Unit_Array; + + + -- Scan unit array in display order and show the maximum until NumToShow is reached + -- The ID is set for all MUFs present in our unit array + local Updated = 0; + for i, Unit in ipairs(Unit_Array) do + + MF = self.ExistingPerUNIT[Unit]; + if MF then + MF.ID = i; + + if not MF.Shown and i <= NumToShow then -- we got this unit in our group but it's hidden + + MF.Shown = true; + self.UnitShown = self.UnitShown + 1; + MF.ToPlace = true; + Updated = Updated + 1; + + D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.UpdateWithCS, D.profile.DebuffsFrameRefreshRate * Updated, MF); + --D:Debug("|cFF88AA00Show schedule for MUF", Unit, "UnitShown:", self.UnitShown); + end + else + --D:errln("showhide: no muf for", Unit); -- call delay display up + self:Delayed_MFsDisplay_Update (); + end + + end + + -- hide remaining units + if self.UnitShown > NumToShow then + + for Unit, MF in pairs(self.ExistingPerUNIT) do -- see all the MUF we ever created and show or hide them if there corresponding unit exists + + -- show/hide + if MF.Shown and (not Unit_Array_UnitToGUID[Unit] or MF.ID > NumToShow ) then -- we don't have this unit but its MUF is shown + + -- clear debuff before hiding to avoid leaving 'ghosts' behind... + if D.UnitDebuffed[MF.CurrUnit] then + D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum - 1; + end + + MF.Debuffs = false; + MF.IsDebuffed = false; + MF.Debuff1Prio = false; + MF.PrevDebuff1Prio = false; + D.UnitDebuffed[MF.CurrUnit] = false; -- used by the live-list only + D.Stealthed_Units[MF.CurrUnit] = false; + + + MF.Shown = false; + self.UnitShown = self.UnitShown - 1; + --D:Debug("|cFF88AA00Hiding %d (%s), scheduling update in %f|r", i, MF.CurrUnit, D.profile.DebuffsFrameRefreshRate * i); + Updated = Updated + 1; + D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.Update, D.profile.DebuffsFrameRefreshRate * Updated, MF); + MF.Frame:Hide(); + end + + end + end + + -- manage to get what we show in the screen + if self.UnitShown > 0 and Old_UnitShown ~= self.UnitShown then + MicroUnitF:Place(); + end + + return true; +end -- }}} + + +function MicroUnitF:Delayed_Force_FullUpdate () + if (D.profile.ShowDebuffsFrame) then + D:ScheduleDelayedCall("Dcr_Force_FullUpdate", self.Force_FullUpdate, 0.3, self); + end +end + +function MicroUnitF:Force_FullUpdate () -- {{{ + if (not D.profile.ShowDebuffsFrame) then + return false; + end + + -- This function cannot do anything if we are fighting + if InCombatLockdown() then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ( + "Force_FullUpdate", self.Force_FullUpdate, + self); + return false; + end + + local MF;--, MF_f; + + D.Status.SpellsChanged = GetTime(); -- will force an update of all MUFs attributes + + local i = 1; + for Unit, MF in pairs(self.ExistingPerUNIT) do + + if not MF.IsDebuffed then + MF.UnitStatus = 0; -- reset status to force SetColor to update + end + + MF.ChronoFontString:SetTextColor(unpack(MF_colors["COLORCHRONOS"])); + + D:ScheduleDelayedCall("Dcr_Update"..MF.CurrUnit, MF.UpdateWithCS, D.profile.DebuffsFrameRefreshRate * i, MF); + i = i + 1; + end +end -- }}} + + +-- Those set the scalling of the MUF container +-- SACALING FUNCTIONS (MicroUnitF Children) {{{ +-- Place the MUFs container according to its scale +function MicroUnitF:Place () -- {{{ + + if self.UnitShown == 0 or self.DraggingHandle then return end + + if InCombatLockdown() then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ( + "MicroUnitFPlace", self.Place, + self); + return; + end + + + local UIScale = UIParent:GetEffectiveScale() + local FrameScale = self.Frame:GetEffectiveScale(); + local x, y = D.profile.DebuffsFrame_x, D.profile.DebuffsFrame_y; + + -- If executed for the very first time, then put it in the top right corner of the screen + if (not x or not y) then + x = (UIParent:GetWidth() * UIScale) - (UIParent:GetWidth() * UIScale) / 4; + y = - (UIParent:GetHeight() * UIScale) / 5; + + D.profile.DebuffsFrame_x = x; + D.profile.DebuffsFrame_y = y; + end + + + local FirstLineNum = 0; + local Handle_x_offset = 0; + local Handle_y_offset = 0; + + -- get the number of max unit per line + if self.UnitShown >= D.profile.DebuffsFramePerline then FirstLineNum = D.profile.DebuffsFramePerline; else FirstLineNum = self.UnitShown; end + + -- get the offset of the handle we need to apply in order to align the MUFs on the right + if D.profile.DebuffsFrameStickToRight then + Handle_x_offset = -1 * FrameScale * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing ); + end + + Handle_x_offset = Handle_x_offset - FrameScale * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0); + Handle_y_offset = Handle_y_offset + FrameScale * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0); + + -- check the right edge so it can't be out of the screen + local RightEdge = x + FrameScale * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing + (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0)) + Handle_x_offset ; + + if (RightEdge > UIParent:GetWidth() * UIScale) then + Handle_x_offset = Handle_x_offset - (RightEdge - UIParent:GetWidth() * UIScale); + D:Debug("put the MUFs on the screen!!! (Right edge out)"); + end + + -- check the left edge so it can't be out of the screen + local LeftEdge = x + Handle_x_offset; + if (LeftEdge < 0) then + Handle_x_offset = Handle_x_offset - LeftEdge; + D:Debug("put the MUFs on the screen!!! (Left edge out)"); + end + + -- check the bottom edge + local NumberOfLines = floor( (self.UnitShown - 1) / D.profile.DebuffsFramePerline) + 1; + --D:Debug(NumberOfLines); + + local BottomEdge = y - FrameScale * ((D.profile.DebuffsFrameGrowToTop and 1 or NumberOfLines) * (DC.MFSIZE + D.profile.DebuffsFrameYSpacing) - D.profile.DebuffsFrameYSpacing) + Handle_y_offset; + + if -BottomEdge > UIParent:GetHeight() * UIScale then + Handle_y_offset = Handle_y_offset - (BottomEdge + UIParent:GetHeight() * UIScale); + D:Debug("put the MUFs on the screen!!! (Bottom edge out)"); + end + + -- check the top edge + local TopEdge = y + FrameScale * (((D.profile.DebuffsFrameGrowToTop and NumberOfLines > 1) and NumberOfLines - 1 or 1) * (DC.MFSIZE + D.profile.DebuffsFrameYSpacing) - ((not D.profile.DebuffsFrameGrowToTop) and 1 or 0) * D.profile.DebuffsFrameYSpacing) + Handle_y_offset; + if (TopEdge > 0) then + Handle_y_offset = Handle_y_offset - TopEdge; + D:Debug("put the MUFs on the screen!!! (Top edge out)"); + end + + x = x + Handle_x_offset; + y = y + Handle_y_offset; + + + + -- set to the scaled position + self.Frame:ClearAllPoints(); + self.Frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x/FrameScale , y/FrameScale); + D:Debug("MUF Window position set"); +end -- }}} + +-- Save the position of the frame without its scale +function MicroUnitF:SavePos () -- {{{ + + if self.UnitShown == 0 then return end + + local FirstLineNum; + + if self.Frame:IsVisible() then + -- We save the unscalled position (no problem if the sacale is changed behind our back) + D.profile.DebuffsFrame_x = self.Frame:GetEffectiveScale() * self.Frame:GetLeft(); + D.profile.DebuffsFrame_y = self.Frame:GetEffectiveScale() * self.Frame:GetTop() - UIParent:GetHeight() * UIParent:GetEffectiveScale(); + + + -- if we choosed to align the MUF to the right then we have to add the + -- width of the first line to get the original position of the handle + + if D.profile.DebuffsFrameStickToRight then + + if self.UnitShown >= D.profile.DebuffsFramePerline then + FirstLineNum = D.profile.DebuffsFramePerline; + else + FirstLineNum = self.UnitShown; + end + + D.profile.DebuffsFrame_x = D.profile.DebuffsFrame_x + self.Frame:GetEffectiveScale() * (FirstLineNum * (DC.MFSIZE + D.profile.DebuffsFrameXSpacing) - D.profile.DebuffsFrameXSpacing); + end + + D.profile.DebuffsFrame_x = D.profile.DebuffsFrame_x + self.Frame:GetEffectiveScale() * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0); + D.profile.DebuffsFrame_y = D.profile.DebuffsFrame_y - self.Frame:GetEffectiveScale() * (D.profile.DebuffsFrameGrowToTop and DC.MFSIZE or 0); + + -- D:Debug("Frame position saved"); + end + +end -- }}} + +-- set the scaling of the MUFs container according to the user settings +function MicroUnitF:SetScale (NewScale) -- {{{ + + -- save the current position without any scaling +-- self:SavePos (); + -- Setting the new scale + self.Frame:SetScale(NewScale); + -- Place the frame adapting its position to the news cale + self:Place (); + +end -- }}} +-- }}} + +-- Update the MUF of a given unitid +function MicroUnitF:UpdateMUFUnit(Unitid, CheckStealth) + if not D.profile.ShowDebuffsFrame then + return; + end + + local unit = false; + + if (D.Status.Unit_Array_UnitToGUID[Unitid]) then + unit = Unitid; + else + D:Debug("Unit", Unitid, "not in raid or party!" ); + return; + end + + -- get the MUF object + local MF = self.UnitToMUF[unit]; + + if (MF and MF.Shown) then + -- The MUF will be updated only every DebuffsFrameRefreshRate seconds at most + -- but we don't miss any event XXX note this can be the cause of slowdown if 25 or 40 players got debuffed at the same instant, DebuffUpdateRequest is here to prevent that since 2008-02-17 + if (not D:DelayedCallExixts("Dcr_Update"..unit)) then + D.DebuffUpdateRequest = D.DebuffUpdateRequest + 1; + D:ScheduleDelayedCall("Dcr_Update"..unit, CheckStealth and MF.UpdateWithCS or MF.Update, D.profile.DebuffsFrameRefreshRate * (1 + floor(D.DebuffUpdateRequest / (D.profile.DebuffsFramePerUPdate / 2))), MF); + D:Debug("Update scheduled for, ", unit, MF.ID); + + return true; -- return value used to aknowledge that the function actually did something + end + else + D:Debug("No MUF found for ", unit, Unitid); + end +end + +-- Event management functions +-- MUF EVENTS (MicroUnitF children) (OnEnter, OnLeave, OnLoad, OnPreClick) {{{ +-- It's outside the function to avoid creating and discarding this table at each call +local DefaultTTAnchor = {"ANCHOR_TOPLEFT", 0, 6}; +local UnitGUID = _G.UnitGUID; +local TooltipButtonsInfo = {}; -- help tooltip text table +local TooltipUpdate = 0; -- help tooltip change update check +-- This function is responsible for showing the tooltip when the mouse pointer is over a MUF +-- it also handles Unstable Affliction detection and warning. +function MicroUnitF:OnEnter(frame) -- {{{ + D.Status.MouseOveringMUF = true; + + local MF = frame.Object; + local Status; + + local Unit = MF.CurrUnit; -- shortcut + local TooltipText = ""; + + + local GUIDwasFixed = false; + local unitguid = UnitGUID(Unit); + + if unitguid ~= D.Status.Unit_Array_UnitToGUID[Unit] or Unit ~= D.Status.Unit_Array_GUIDToUnit[unitguid] then + + if unitguid then + D.Status.Unit_Array_UnitToGUID[Unit] = unitguid; + D.Status.Unit_Array_GUIDToUnit[unitguid] = Unit; + GUIDwasFixed = true; + end + + end + + MF:Update(false, false, true); -- will reset the color early and set the current status of the MUF + MF:SetClassBorder(); -- set the border if it wasn't possible at the time the unit was discovered + + if not Unit then + return; -- If the user overs the MUF befor it's completely initialized + end + + --Test for unstable affliction + if MF.Debuffs then + for i, Debuff in ipairs(MF.Debuffs) do + if Debuff.Type then + -- Create a warning if the Unstable Affliction is detected + if Debuff.Name == DS["Unstable Affliction"] then + --if Debuff.Name == "Malédiction de Stalvan" then -- to test easily + D:Println("|cFFFF0000 ==> %s !!|r (%s)", DS["Unstable Affliction"], D:MakePlayerName((D:PetUnitName( Unit, true )))); + PlaySoundFile("Sound\\Doodad\\G_NecropolisWound.wav"); + end + end + end + end + + if D.profile.AfflictionTooltips then + + -- removes the CHARMED_STATUS bit from Status, we don't need it + Status = bit.band(MF.UnitStatus, bit.bnot(CHARMED_STATUS)); + + -- First, write the name of the unit in its class color + if UnitExists(MF.CurrUnit) then + TooltipText = + ((DC.RAID_ICON_LIST[GetRaidTargetIndex(Unit)]) and (DC.RAID_ICON_LIST[GetRaidTargetIndex(Unit)] .. "0:0:0:0|t ") or "" ) .. + -- Colored unit name + D:ColorText( (D:PetUnitName( Unit, true )) + , "FF" .. ((UnitClass(Unit)) and DC.HexClassColor[ (select(2, UnitClass(Unit))) ] or "AAAAAA")) .. " |cFF3F3F3F(".. Unit .. ")|r"; + else + TooltipText = MF.CurrUnit; + end + + + -- set UnitStatus text + local StatusText = ""; + + -- set the status text, just translate the bitfield to readable text + if Status == NORMAL then + StatusText = L["NORMAL"]; + + elseif Status == ABSENT then + StatusText = str_format(L["ABSENT"], Unit); + + elseif Status == FAR then + StatusText = L["TOOFAR"]; + + elseif Status == STEALTHED then + StatusText = L["STEALTHED"]; + + elseif Status == BLACKLISTED then + StatusText = L["BLACKLISTED"]; + + elseif MF.Debuffs and (Status == AFFLICTED or Status == AFFLICTED_NIR) then + local DebuffType = MF.Debuffs[1].Type; + StatusText = str_format(L["AFFLICTEDBY"], D:ColorText( L[str_upper(DC.TypeNames[DebuffType])], "FF" .. DC.TypeColors[DebuffType]) ); + end + + -- Unit Status + --TooltipText = TooltipText .. "\n" .. L["UNITSTATUS"] .. StatusText .. "\n"; + TooltipText = TooltipText .. "\n" .. StatusText; + + -- list the debuff(s) names + if MF.Debuffs then + for i, Debuff in ipairs(MF.Debuffs) do + if Debuff.Type then + local DebuffApps = Debuff.Applications; + TooltipText = TooltipText .. "\n" .. str_format("%s", D:ColorText(Debuff.Name, "FF" .. DC.TypeColors[Debuff.Type])) .. (DebuffApps>0 and str_format(" (%d)", DebuffApps) or ""); + end + end + end + + local VerticalMUF = floor((self.UnitShown - 1) / D.profile.DebuffsFramePerline ) * D.profile.DebuffsFramePerline + 1; + + -- The tooltip is anchored above the top first MUF + if not D.profile.DebuffsFrameGrowToTop then + local FirstMUFAnchor = self:GiveMFAnchor(1); + DefaultTTAnchor[2] = FirstMUFAnchor[2]; + DefaultTTAnchor[3] = FirstMUFAnchor[3]; + else + local TopMUFAnchor = self:GiveMFAnchor(VerticalMUF); + DefaultTTAnchor[2] = TopMUFAnchor[2]; + DefaultTTAnchor[3] = TopMUFAnchor[3]; + end + + -- Display the tooltip + D:DisplayTooltip( TooltipText , self.Frame, DefaultTTAnchor); + + -- if the tooltip is at the top of the screen it means it's overlaping the MUF, let's move the tooltip somewhere else. + if floor(DcrDisplay_Tooltip:GetTop()) == floor(UIParent:GetTop()) then + local RefMUF = 1; + + if not D.profile.DebuffsFrameGrowToTop then + RefMUF = VerticalMUF; + end + + DcrDisplay_Tooltip:ClearAllPoints(); + DcrDisplay_Tooltip:SetPoint("TOPLEFT", self.ExistingPerNum[RefMUF].Frame, "BOTTOMLEFT"); + end + end + + -- show a help text in the Game default tooltip + if D.profile.DebuffsFrameShowHelp then + -- if necessary we will update the help tooltip text + if (D.Status.SpellsChanged ~= TooltipUpdate) then + TooltipButtonsInfo = {}; + local AvailableButtons = D.db.global.AvailableButtons; + + for Spell, Prio in pairs(D.Status.CuringSpellsPrio) do + TooltipButtonsInfo[Prio] = + str_format("%s: %s", D:ColorText(DC.AvailableButtonsReadable[AvailableButtons[Prio]], D:NumToHexColor(MF_colors[Prio])), Spell); + end + + t_insert(TooltipButtonsInfo, str_format("%s: %s", DC.AvailableButtonsReadable[AvailableButtons[#AvailableButtons - 1]], L["TARGETUNIT"])); + t_insert(TooltipButtonsInfo, str_format("%s: %s", DC.AvailableButtonsReadable[AvailableButtons[#AvailableButtons ]], L["FOCUSUNIT"])); + TooltipButtonsInfo = table.concat(TooltipButtonsInfo, "\n"); + TooltipUpdate = D.Status.SpellsChanged; + end + + GameTooltip_SetDefaultAnchor(GameTooltip, frame); + GameTooltip:SetText(TooltipButtonsInfo); + GameTooltip:Show(); + + end + +end -- }}} + +-- No longer used +--[===[ +function MicroUnitF:LateAnalysis(From, Debuffs, MF, Status, GUIDwasFixed) + + local Unit = MF.CurrUnit; -- shortcut + local unitguid = UnitGUID(Unit); + local RegisteredUnitguid = MF.UnitGUID; + -- search for detection by combat event manager + local foundcblevents = {}; local highestever = 0; local _; local i=1; local dname; local DebuffApplyTime = false; + local debuffname = Debuffs and Debuffs[1].Name or "No debuff, charmed?"; + + -- find the debuff on the unit and find out when it was applyed + while 1 do + dname = UnitAura(Unit, i, "HARMFUL"); + + if not dname then break end; + + if dname == debuffname then + local Dduration, DexpireTime; + dname, _, _, _, _, Dduration, DexpireTime = UnitAura(Unit, i, "HARMFUL"); + DebuffApplyTime = DexpireTime - Dduration; + break; + end + i = i + 1; + end + + + if not DebuffApplyTime or DebuffApplyTime == 0 then + D:AddDebugText("DebuffApplyTime could not be found for ", dname, "on", Unit, "DebuffApplyTime set to now - 3s"); + DebuffApplyTime = GetTime() - 3; + end + + local DetectHistoryIndex = 1; + local halfrange = 5; local RangeMatch = false; local latestever = "none"; local latesttime = 0; + local tconcat = _G.table.concat + D:Debug("Looking for events on", Unit, "between", DebuffApplyTime - halfrange, "and", DebuffApplyTime + halfrange); + -- look in the history of the combat log event handler + while D.DetectHistory[DetectHistoryIndex] do + -- take all the events related to this unit or debuffname around the time the missed debuff was applyed + if D.DetectHistory[DetectHistoryIndex][8] == unitguid or D.DetectHistory[DetectHistoryIndex][12] == debuffname then + + if D.DetectHistory[DetectHistoryIndex][1] > DebuffApplyTime - halfrange and D.DetectHistory[DetectHistoryIndex][1] < DebuffApplyTime + halfrange then + t_insert(foundcblevents, tconcat(D.DetectHistory[DetectHistoryIndex],", ")); + t_insert(foundcblevents, "\n"); + RangeMatch = true; + elseif D.DetectHistory[DetectHistoryIndex][1] > latesttime then-- find the latest event concerning this unit + latesttime = D.DetectHistory[DetectHistoryIndex][1]; + latestever = DetectHistoryIndex; + end + + end + + DetectHistoryIndex = DetectHistoryIndex + 1; + end + + if latestever ~= "none" then + latestever = tconcat(D.DetectHistory[latestever],", "); + end + + D:AddDebugText("Debuff late detection:", From, debuffname, "Type:", Debuffs[1].TypeName, "on unit:", Unit, unitguid, "_AppT_:", DebuffApplyTime, "DFRR:", D.profile.DebuffsFrameRefreshRate, "Status:", Status, "DT:", GetTime(), "LGU:", D.Status.GroupUpdatedOn, "LGuEr", D.Status.GroupUpdateEvent, "JFGUID:", GUIDwasFixed, "DbUreq:", D.DebuffUpdateRequest, "MFGuid~:", RegisteredUnitguid ~= unitguid, "Z:", GetZoneText(), "DTI:", DetectHistoryIndex); + + -- trigger a dcr diag if DetectHistoryIndex is 1 :/ + if DetectHistoryIndex == 1 then + T._SelfDiagnostic(true, true); + end + + if #foundcblevents == 0 then + D:AddDebugText("No event in range at all for ", Unit, "or debuff:", debuffname, "latest found:", latestever); + else + D:AddDebugText(#foundcblevents / 2, "events for ", Unit, "or debuff:", debuffname, "Status:", Status, "Events:\n", unpack(foundcblevents)); + end +end +--]===] + +function MicroUnitF:OnLeave() -- {{{ + D.Status.MouseOveringMUF = false; + --D:Debug("Micro unit Hidden"); + DcrDisplay_Tooltip:Hide(); + + if (D.profile.DebuffsFrameShowHelp) then + GameTooltip:Hide(); + end +end -- }}} + + +function D.MicroUnitF:OnCornerEnter(frame) + if (D.profile.DebuffsFrameShowHelp) then + D:DisplayGameTooltip(frame, + str_format( + "|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n".. + --"|cFF11FF11%s|r: %s\n".. + "|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n".. + "|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n".. + "|cFF11FF11%s|r-|cFF11FF11%s|r: %s\n\n".. + "|cFF11FF11%s|r-|cFF11FF11%s|r: %s", + + D.L["ALT"], D.L["HLP_LEFTCLICK"], D.L["HANDLEHELP"], + + --D.L["HLP_RIGHTCLICK"], D.L["STR_OPTIONS"], + D.L["ALT"], D.L["HLP_RIGHTCLICK"], D.L["BINDING_NAME_DCRSHOWOPTION"], + + D.L["CTRL"], D.L["HLP_LEFTCLICK"], D.L["BINDING_NAME_DCRPRSHOW"], + D.L["SHIFT"], D.L["HLP_LEFTCLICK"], D.L["BINDING_NAME_DCRSKSHOW"], + + D.L["SHIFT"], D.L["HLP_RIGHTCLICK"], D.L["BINDING_NAME_DCRSHOW"] + )); + end; +end + + +function MicroUnitF:OnLoad(frame) -- {{{ + frame:SetScript("PreClick", self.OnPreClick); + frame:SetScript("PostClick", self.OnPostClick); +end +-- }}} + +function MicroUnitF.OnPreClick(frame, Button) -- {{{ + D:Debug("Micro unit Preclicked: ", Button); + + local Unit = frame.Object.CurrUnit; -- shortcut + + if (frame.Object.UnitStatus == NORMAL and (Button == "LeftButton" or Button == "RightButton")) then + + D:Println(L["HLP_NOTHINGTOCURE"]); + + elseif (frame.Object.UnitStatus == AFFLICTED) then + local NeededPrio = D:GiveSpellPrioNum(frame.Object.Debuffs[1].Type); + local RequestedPrio = false; + local ButtonsString = ""; + + if IsControlKeyDown() then + ButtonsString = "ctrl-"; + elseif IsAltKeyDown() then + ButtonsString = "alt-"; + elseif IsShiftKeyDown() then + ButtonsString = "shift-"; + end + + if Button == "LeftButton" then + ButtonsString = ButtonsString .. "%s1"; + elseif Button == "RightButton" then + ButtonsString = ButtonsString .. "%s2"; + end + + RequestedPrio = D:tGiveValueIndex(D.db.global.AvailableButtons, ButtonsString); + + if RequestedPrio and NeededPrio ~= RequestedPrio then + D:errln(L["HLP_WRONGMBUTTON"]); + if NeededPrio and MF_colors[NeededPrio] then + D:Println(L["HLP_USEXBUTTONTOCURE"], D:ColorText(DC.AvailableButtonsReadable[ D.db.global.AvailableButtons[NeededPrio] ], D:NumToHexColor(MF_colors[NeededPrio]))); + --[===[@debug@ + else + D:AddDebugText("Button wrong click info bug: NeededPrio:", NeededPrio, "Unit:", Unit, "RequestedPrio:", RequestedPrio, "Button clicked:", Button, "MF_colors:", unpack(MF_colors), "Debuff Type:", frame.Object.Debuffs[1].Type); + --@end-debug@]===] + end + + + elseif RequestedPrio and D.Status.HasSpell then +-- D:Print("XXX ClickedMF SET"); + D.Status.ClickedMF = frame.Object; -- used to update the MUF on cast success and failure to know which unit is being cured + D.Status.ClickedMF.SPELL_CAST_SUCCESS = false; + D:Debuff_History_Add(frame.Object.Debuffs[1].Name, frame.Object.Debuffs[1].TypeName); + end + end +end -- }}} + +-- }}} + +-- }}} + + + +-- MicroUnitF NON STATIC METHODS {{{ +-- init a new micro frame (Call internally by :new() only) +function MicroUnitF.prototype:init(Container, Unit, FrameNum, ID) -- {{{ + + D:Debug("Initializing MicroUnit object", Unit, "with FrameNum=", FrameNum, " and ID", ID); + + + -- set object default variables + self.Parent = Container; + self.ID = ID; -- is set by te roaming updater + self.FrameNum = FrameNum; + self.ToPlace = true; + self.Debuffs = false; + self.Debuff1Prio = false; + self.PrevDebuff1Prio = false; + self.IsDebuffed = false; + self.CurrUnit = false; + self.UnitName = false; + self.UnitGUID = false; + self.UnitClass = false; + self.UnitStatus = 0; + self.FirstDebuffType = 0; + self.NormalAlpha = false; + self.BorderAlpha = false; + self.Color = {}; + self.IsCharmed = false; + self.UpdateCountDown = 3; + self.LastAttribUpdate = 0; + self.LitTime = false; + self.Chrono = false; + self.PrevChrono = false; + self.Shown = false; -- Setting this to true will broke the stick to right option + self.UpdateCD = 0; + self.RaidTargetIcon = false; + self.PrevRaidTargetIndex= false; + + -- create the frame + self.Frame = CreateFrame ("Button", "DcrMicroUnit"..Unit, self.Parent, "DcrMicroUnitTemplateSecure"); + self.CooldownFrame = CreateFrame ("Cooldown", "DcrMicroUnitCD"..Unit, self.Frame, "DcrMicroUnitCDTemplate"); + + -- outer texture (the class border) + -- Bottom side + self.OuterTexture1 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Bottom", "BORDER"); + self.OuterTexture1:SetPoint("BOTTOMLEFT", self.Frame, "BOTTOMLEFT", 0, 0); + self.OuterTexture1:SetPoint("TOPRIGHT", self.Frame, "BOTTOMRIGHT", 0, 2); + + -- left side + self.OuterTexture2 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Left", "BORDER"); + self.OuterTexture2:SetPoint("TOPLEFT", self.Frame, "TOPLEFT", 0, -2); + self.OuterTexture2:SetPoint("BOTTOMRIGHT", self.Frame, "BOTTOMLEFT", 2, 2); + + -- top side + self.OuterTexture3 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Top", "BORDER"); + self.OuterTexture3:SetPoint("TOPLEFT", self.Frame, "TOPLEFT", 0, 0); + self.OuterTexture3:SetPoint("BOTTOMRIGHT", self.Frame, "TOPRIGHT", 0, -2); + + -- right side + self.OuterTexture4 = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Right", "BORDER"); + self.OuterTexture4:SetPoint("TOPRIGHT", self.Frame, "TOPRIGHT", 0, -2); + self.OuterTexture4:SetPoint("BOTTOMLEFT", self.Frame, "BOTTOMRIGHT", -2, 2); + + + -- global texture + self.Texture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Back", "ARTWORK"); + self.Texture:SetPoint("CENTER",self.Frame ,"CENTER",0,0) + self.Texture:SetHeight(16); + self.Texture:SetWidth(16); + + -- inner Texture (Charmed special texture) + self.InnerTexture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."Charmed", "OVERLAY"); + self.InnerTexture:SetPoint("CENTER",self.Frame ,"CENTER",0,0) + self.InnerTexture:SetHeight(7); + self.InnerTexture:SetWidth(7); + self.InnerTexture:SetTexture(unpack(MF_colors[CHARMED_STATUS])); + + -- Chrono Font string + self.ChronoFontString = self.Frame:CreateFontString("DcrMicroUnit"..Unit.."Chrono", "ARTWORK", "DcrMicroUnitChronoFont"); + self.ChronoFontString:SetTextColor(unpack(MF_colors["COLORCHRONOS"])); + + -- raid target icon + self.RaidIconTexture = self.Frame:CreateTexture("DcrMicroUnit"..Unit.."RaidTargetIcon", "OVERLAY"); + self.RaidIconTexture:SetPoint("CENTER",self.Frame ,"CENTER",0,8) + self.RaidIconTexture:SetHeight(13); + self.RaidIconTexture:SetWidth(13); + + + -- a reference to this object + self.Frame.Object = self; + + -- register events + self.Frame:RegisterForClicks("AnyUp"); + self.Frame:SetFrameStrata("MEDIUM"); + + -- set the frame attributes + self:UpdateAttributes(Unit); + + -- once the MF frame is set up, schedule an event to show it + MicroUnitF:Delayed_MFsDisplay_Update(); +end -- }}} + + +function MicroUnitF.prototype:Update(SkipSetColor, SkipDebuffs, CheckStealth) + + local MF = self; + local ActionsDone = 0; + + local Unit = MF.CurrUnit; + + -- The unit is the same but the name isn't... (check for class change) + if MF.CurrUnit == Unit and D.Status.Unit_Array_UnitToGUID[self.CurrUnit] ~= self.UnitGUID then + if MF:SetClassBorder() then + ActionsDone = ActionsDone + 1; -- count expensive things done + end + -- if the guid changed we really need to rescan the unit! + SkipSetColor = false; SkipDebuffs = false; CheckStealth = true; + --[===[@debug@ + D:Debug("|cFF00CC00MUF:Update(): Guid change rescanning", Unit, "|r"); + --@end-debug@]===] + end + + -- Update the frame attributes if necessary (Spells priority or unit id changes) + if (D.Status.SpellsChanged ~= MF.LastAttribUpdate ) then + --D:Debug("Attributes update required: ", MF.ID); + if (MF:UpdateAttributes(Unit, true)) then + ActionsDone = ActionsDone + 1; -- count expensive things done + SkipSetColor = false; SkipDebuffs = false; -- if some attributes were updated then update the rest + end + end + + + if (not SkipSetColor) then + if (not SkipDebuffs) then + -- get the manageable debuffs of this unit + MF:SetDebuffs(); + D:Debug("Debuff set for ", MF.ID); + if CheckStealth then + D.Stealthed_Units[MF.CurrUnit] = D:CheckUnitStealth(MF.CurrUnit); -- update stealth status +-- D:Debug("MF:Update(): Stealth status checked as requested."); + end + end + + if (MF:SetColor()) then + ActionsDone = ActionsDone + 1; -- count expensive things done + end + end + + return ActionsDone; +end + + +function MicroUnitF.prototype:UpdateWithCS() + self:Update(false, false, true); +end + +function MicroUnitF.prototype:UpdateSkippingSetBuf() + self:Update(false, true); +end + +-- UPDATE attributes (Spells and Unit) {{{ + + + +do + -- used to tell if we changed something to improve performances. + -- Each attribute change trigger an event... + local ReturnValue = false; + -- this updates the sttributes of a MUF's frame object + function MicroUnitF.prototype:UpdateAttributes(Unit, DoNotDelay) + + -- Delay the call if we are fighting + if InCombatLockdown() then + if not DoNotDelay then + D:AddDelayedFunctionCall ( + "MicroUnit_" .. Unit, -- UID + self.UpdateAttributes, self, Unit); -- function call + end + return false; + end + + ReturnValue = false; + + -- if the unit is not set + if not self.CurrUnit then + self.Frame:SetAttribute("unit", Unit); + + -- UnitToMUF[] can only be set when out of fight so it remains + -- coherent with what is displayed when groups are changed during a + -- fight + + MicroUnitF.UnitToMUF[Unit] = self; + self.CurrUnit = Unit; + + self:SetClassBorder(); + + -- set the return value because we did something expensive + ReturnValue = self; + end + + if (D.Status.SpellsChanged == self.LastAttribUpdate) then + return ReturnValue; -- nothing changed + end + + -- D:Debug("UpdateAttributes() executed"); + + if self.LastAttribUpdate == 0 then -- only once + -- set the mouse left-button actions on all modifiers + self.Frame:SetAttribute("type1", "macro"); + self.Frame:SetAttribute("ctrl-type1", "macro"); + self.Frame:SetAttribute("alt-type1", "macro"); + self.Frame:SetAttribute("shift-type1", "macro"); + + -- set the mouse right-button actions on all modifiers + self.Frame:SetAttribute("type2", "macro"); + self.Frame:SetAttribute("ctrl-type2", "macro"); + self.Frame:SetAttribute("alt-type2", "macro"); + self.Frame:SetAttribute("shift-type2", "macro"); + + -- set the mouse middle-button actions on all modifiers + self.Frame:SetAttribute("type3", "macro"); + self.Frame:SetAttribute("ctrl-type3", "macro"); + self.Frame:SetAttribute("alt-type3", "macro"); + self.Frame:SetAttribute("shift-type3", "macro"); + end + + local AvailableButtons = D.db.global.AvailableButtons; + + self.Frame:SetAttribute(str_format(AvailableButtons[#AvailableButtons - 1], "macrotext"), str_format("/target %s", Unit)); + self.Frame:SetAttribute(str_format(AvailableButtons[#AvailableButtons ], "macrotext"), str_format("/focus %s", Unit)); + + -- set the spells attributes using the lookup tables above + for Spell, Prio in pairs(D.Status.CuringSpellsPrio) do + + --the [target=%s, help][target=%s, harm] prevents the 'please select a unit' cursor problem (Blizzard should fix this...) + self.Frame:SetAttribute(str_format(AvailableButtons[Prio], "macrotext"), str_format("%s/cast [target=%s, help][target=%s, harm] %s%s", + ((not D.Status.FoundSpells[Spell][1]) and "/stopcasting\n" or ""), + Unit,Unit, + Spell, + (DC.SpellsToUse[Spell].Rank and "(" .. (str_sub(DC.RANKNUMTRANS, '%d+', DC.SpellsToUse[Spell].Rank)) .. ")" or "") )); + + + --[[ + D:Debug("XX-> macro: ",str_format(AvailableButtons[Prio], "macrotext"), str_format("%s/cast [target=%s, help][target=%s, harm] %s%s", + ((not D.Status.FoundSpells[Spell][1]) and "/stopcasting\n" or ""), + Unit,Unit, + Spell, + (DC.SpellsToUse[Spell].Rank and "(" .. (str_sub(DC.RANKNUMTRANS, '%d+', DC.SpellsToUse[Spell].Rank)) .. ")" or "") )); + --]] + + end + + self.Debuff1Prio = false; + + -- the update timestamp + self.LastAttribUpdate = D.Status.SpellsChanged; + return self; + end +end -- }}} + +function MicroUnitF.prototype:SetDebuffs() -- {{{ + self.Debuffs, self.IsCharmed = D:UnitCurableDebuffs(self.CurrUnit); + + if D.UnitDebuffed[self.CurrUnit] then + D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum - 1; + end + + if (self.Debuffs and self.Debuffs[1] and self.Debuffs[1].Type) then + --D:Debug("A debuff was found"); -- XXX + self.IsDebuffed = true; + self.Debuff1Prio = D:GiveSpellPrioNum( self.Debuffs[1].Type ); + + D.UnitDebuffed[self.CurrUnit] = true; + D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum + 1; + + else + --D:Debug("No debuff found"); -- XXX + self.IsDebuffed = false; + self.Debuff1Prio = false; + self.PrevDebuff1Prio = false; + D.UnitDebuffed[self.CurrUnit] = false; -- used by the live-list only + end +end -- }}} + +do + --[=[ + -- This function is responsible for setting all the textures of a MUF object: + -- - The main color + -- - Showing/Hiding the charmed alert square + -- - The Alpha of the center and borders + -- This function also set the Status of the MUF that will be used in the tooltip + --]=] + local DebuffType, Unit, PreviousStatus, BorderAlpha, Class, ClassColor, ReturnValue, RangeStatus, Alpha, PrioChanged, PrevChrono, Time, Status; + local profile = {}; + + -- global access optimization + local IsSpellInRange = _G.IsSpellInRange; + local UnitClass = _G.UnitClass; + local UnitExists = _G.UnitExists; + local UnitIsVisible = _G.UnitIsVisible; + local UnitLevel = _G.UnitLevel; + local unpack = _G.unpack; + local select = _G.select; + local GetTime = _G.GetTime; + local floor = _G.math.floor; + local fmod = _G.math.fmod; + local CooldownFrame_SetTimer = _G.CooldownFrame_SetTimer; + local GetSpellCooldown = _G.GetSpellCooldown; + local GetRaidTargetIndex= _G.GetRaidTargetIndex; + local bor = _G.bit.bor; + + function MicroUnitF.prototype:SetColor() -- {{{ + + profile = D.profile; + Status = D.Status; + + -- register default alpha of the border + BorderAlpha = profile.DebuffsFrameElemBorderAlpha; + + -- register local variables + DebuffType = false; + ReturnValue = false; + Unit = self.CurrUnit; + PreviousStatus = self.UnitStatus; + + + + -- if unit not available, if a unit cease to exist (this happen often for pets) + if not UnitExists(Unit) then + if PreviousStatus ~= ABSENT then + self.Color = MF_colors[ABSENT]; + self.UnitStatus = ABSENT; + if self.LitTime then + self.LitTime = false; + self.ChronoFontString:SetText(" "); + end + end + + -- UnitIsVisible() behavior is not 100% reliable so we also use UnitLevel() that will return -1 when the Unit is too far... + elseif not UnitIsVisible(Unit) or UnitLevel(Unit) < 1 then + if PreviousStatus ~= FAR then + self.Color = MF_colors[FAR]; + self.UnitStatus = FAR; + if self.LitTime then + self.LitTime = false; + self.ChronoFontString:SetText(" "); + + end + end + + else + -- If the Unit is invisible + if profile.Show_Stealthed_Status and D.Stealthed_Units[Unit] then + if PreviousStatus ~= STEALTHED then + self.Color = MF_colors[STEALTHED]; + self.UnitStatus = STEALTHED; + if self.LitTime then + self.LitTime = false; + self.ChronoFontString:SetText(" "); + + end + end + + -- if unit is blacklisted + elseif Status.Blacklisted_Array[Unit] then + if PreviousStatus ~= BLACKLISTED then + self.Color = MF_colors[BLACKLISTED]; + self.UnitStatus = BLACKLISTED; + if self.LitTime then + self.LitTime = false; + self.ChronoFontString:SetText(" "); + + end + end + + -- if the unit has some debuffs we can handle + elseif (self.IsDebuffed) then + DebuffType = self.Debuffs[1].Type; + + if self.PrevDebuff1Prio ~= self.Debuff1Prio then + self.Color = MF_colors[self.Debuff1Prio]; + self.PrevDebuff1Prio = self.Debuff1Prio; + PrioChanged = true; + end + + -- Test if the spell we are going to use is in range + -- Some time can elaps between the instant the debuff is detected and the instant it is shown. + -- Between those instants, a reconfiguration can happen (pet dies or some spells become unavailable) + -- So we test before calling this api that we can still cure this debuff type + if Status.CuringSpells[DebuffType] then + RangeStatus = IsSpellInRange(Status.CuringSpells[DebuffType], Unit); + else + RangeStatus = false; + end + + Time = GetTime(); + + if RangeStatus and self.UpdateCD < Status.UpdateCooldown then + CooldownFrame_SetTimer (self.CooldownFrame, GetSpellCooldown(Status.CuringSpells[DebuffType]) ); + self.UpdateCD = Time; + end + + -- update the chrono + if profile.DebuffsFrameChrono then + if self.LitTime then + PrevChrono = self.Chrono; + + if not profile.DebuffsFrameTimeLeft then + self.Chrono = floor(Time - self.LitTime); + + if self.Chrono ~= PrevChrono then + self.ChronoFontString:SetText( ((self.Chrono < 60) and self.Chrono or (floor(self.Chrono / 60) .. "\'") )); + end + else + self.Chrono = floor(self.Debuffs[1].expirationTime - Time); + + if self.Chrono ~= PrevChrono then + self.ChronoFontString:SetText( ((self.Chrono < 60) and (self.Chrono + 1) or (floor(self.Chrono / 60 + 1) .. "\'") )); + end + end + else + self.LitTime = Time; + end + end + + self.RaidTargetIcon = GetRaidTargetIndex(Unit); + if self.PrevRaidTargetIndex ~= self.RaidTargetIcon then + self.RaidIconTexture:SetTexture(self.RaidTargetIcon and DC.RAID_ICON_TEXTURE_LIST[self.RaidTargetIcon] or nil); + self.PrevRaidTargetIndex = self.RaidTargetIcon; + end + + + -- set the status according to RangeStatus + if (not RangeStatus or RangeStatus == 0) then + Alpha = 0.40; + self.UnitStatus = AFFLICTED_NIR; + else + Alpha = 1; + self.UnitStatus = AFFLICTED; + BorderAlpha = 1; + + MicroUnitF.UnitsDebuffedInRange = MicroUnitF.UnitsDebuffedInRange + 1; + + if (not Status.SoundPlayed) then + D:PlaySound (self.CurrUnit, "SetColor()" ); + end + end + elseif PreviousStatus ~= NORMAL then + -- the unit has nothing special, set the status to normal + self.Color = MF_colors[NORMAL]; + self.UnitStatus = NORMAL; + if self.LitTime then + self.LitTime = false; + self.ChronoFontString:SetText(" "); + end + + if self.RaidTargetIcon then + self.RaidIconTexture:SetTexture(nil); + self.RaidTargetIcon = false; + self.PrevRaidTargetIndex = false; + end + + -- if the previous status was FAR, trigger a full rescan of the unit (combat log event does not report events for far away units) + if PreviousStatus == FAR then + D.MicroUnitF:UpdateMUFUnit(self.CurrUnit, true); -- this is able to deal when a lot of update queries + end + end + end + + if PreviousStatus == AFFLICTED or PreviousStatus == AFFLICTED_AND_CHARMED then + MicroUnitF.UnitsDebuffedInRange = MicroUnitF.UnitsDebuffedInRange - 1; + + if MicroUnitF.UnitsDebuffedInRange == 0 and profile.Hide_LiveList then + D:Debug("SetColor(): No more unit, sound re-enabled"); + Status.SoundPlayed = false; + end + end + + if not profile.DebuffsFrameElemBorderShow then + BorderAlpha = 0; + end + + + -- set the class border color when needed (the class is unknown and the unit exists or the unit name changed) + --self:SetClassBorder(); + + -- set the alpha of the border if necessary + if self.BorderAlpha ~= BorderAlpha then + self.OuterTexture1:SetAlpha(BorderAlpha); + self.OuterTexture2:SetAlpha(BorderAlpha); + self.OuterTexture3:SetAlpha(BorderAlpha); + self.OuterTexture4:SetAlpha(BorderAlpha); + + self.BorderAlpha = BorderAlpha; + + -- set this to true because we just did something expensive... + ReturnValue = true; + --D:Debug("border alpha set"); + end + + + -- Add the charm status to the bitfield (note that it's treated separatly because it's shown even if the unit is not afflicetd by anything we can cure) + if self.IsCharmed then + self.UnitStatus = bor(self.UnitStatus, CHARMED_STATUS); + end + + -- if the unit is not afflicted or too far set the color to a lower alpha + if not DebuffType then -- if DebuffType was not set, it means that the unit is too far + Alpha = self.Color[4] * profile.DebuffsFrameElemAlpha; + self.PrevDebuff1Prio = false; + end + + + -- Apply the colors and alphas only if necessary + -- The MUF status changed + -- The user changed the defaultAlpha + -- The priority (and thus the color) of the first affliction changed + if (self.UnitStatus ~= PreviousStatus or self.NormalAlpha ~= profile.DebuffsFrameElemAlpha or PrioChanged) then-- or self.FirstDebuffType ~= DebuffType) then + + if PrioChanged then PrioChanged = false; end + + -- Set the main texture + self.Texture:SetTexture(self.Color[1], self.Color[2], self.Color[3], Alpha); + --self.Texture:SetAlpha(Alpha); + + + + -- Show the charmed alert square + if self.IsCharmed then + self.InnerTexture:Show(); + else + self.InnerTexture:Hide(); + end + + --D:Debug("Color Applied, MUF Status:", self.UnitStatus); + + + -- save the current global status + self.NormalAlpha = profile.DebuffsFrameElemAlpha; + self.FirstDebuffType = DebuffType; + + -- set this to true because we just did something expensive... + ReturnValue = true; + end + + return ReturnValue; + + end -- }}} + + function MicroUnitF.prototype:SetClassBorder() + --D:Debug("SetClassBorder called ", D.Status.Unit_Array_UnitToGUID[self.CurrUnit] , self.UnitGUID); + ReturnValue = false; + if (D.profile.DebuffsFrameElemBorderShow and (D.Status.Unit_Array_UnitToGUID[self.CurrUnit] ~= self.UnitGUID or (not self.UnitClass and UnitExists(self.CurrUnit)))) then + + -- Get the GUID of this unit + self.UnitGUID = D.Status.Unit_Array_UnitToGUID[self.CurrUnit]; + + if self.UnitGUID then -- can be nil because of focus... + -- Get its class + Class = (select(2, UnitClass(self.CurrUnit))); + else + Class = false; + end + + -- if the class changed + if (Class and Class ~= self.UnitClass) then + ClassColor = DC.ClassesColors[Class]; + -- update the border color (the four borders) + self.OuterTexture1:SetTexture( unpack(ClassColor) ); + self.OuterTexture2:SetTexture( unpack(ClassColor) ); + self.OuterTexture3:SetTexture( unpack(ClassColor) ); + self.OuterTexture4:SetTexture( unpack(ClassColor) ); + + -- save the class for futur reference + self.UnitClass = Class; + + -- set this to true because we just did something expensive... + ReturnValue = true; + + --D:Debug("Class '%s' set for '%s'", Class, self.CurrUnit); + elseif not Class and self.UnitClass then + -- if the class is not available, set it to false so this test will be done again and again until a class is found + self.UnitClass = false; + BorderAlpha = 0; + self.OuterTexture1:SetAlpha(BorderAlpha); + self.OuterTexture2:SetAlpha(BorderAlpha); + self.OuterTexture3:SetAlpha(BorderAlpha); + self.OuterTexture4:SetAlpha(BorderAlpha); + + self.BorderAlpha = BorderAlpha; + + ReturnValue = true; + --D:Debug("Class of unit %s is Nil", self.CurrUnit); + end + end + return ReturnValue; + + end + +end +-- }}} + +do + local MicroFrameUpdateIndex = 1; -- MUFs are not updated all together + local NumToShow, ActionsDone, Unit, MF, pass, UnitNum; + -- updates the micro frames if needed (called regularly by ACE event, changed in the option menu) + function D:DebuffsFrame_Update() -- {{{ + + local Unit_Array = self.Status.Unit_Array; + local UnitToGUID = self.Status.Unit_Array_UnitToGUID; + + UnitNum = self.Status.UnitNum; -- we need to go through all the units to set MF.ID properly + NumToShow = ((MicroUnitF.MaxUnit > UnitNum) and UnitNum or MicroUnitF.MaxUnit); + + ActionsDone = 0; -- used to limit the maximum number of consecutive UI actions + + + -- we don't check all the MUF at each call, only some of them (changed in the options) + for pass = 1, self.profile.DebuffsFramePerUPdate do + + -- When all frames have been updated, go back to the first + if (MicroFrameUpdateIndex > UnitNum) then + MicroFrameUpdateIndex = 1; + -- self:Debug("last micro frame updated,,:: %d", #self.Status.Unit_Array); + end + + -- get a unit + Unit = Unit_Array[MicroFrameUpdateIndex]; + + -- should never fire unless the player choosed to ignore everything or something is wrong somewhere in the code + if not Unit then + --self:Debug("Unit is nil :/"); + return false; + end + + -- get its MUF + MF = MicroUnitF.ExistingPerUNIT[Unit]; + + -- if no MUF then create it (All MUFs are created here) + if (not MF) then + if not InCombatLockdown() then + MF = MicroUnitF:Create(Unit, MicroFrameUpdateIndex); + ActionsDone = ActionsDone + 1; + end + end + + -- place the MUF ~right where it belongs~ + if MF and MF.ToPlace ~= MicroFrameUpdateIndex and not InCombatLockdown() then + + --sanity check + --[[ + if MicroFrameUpdateIndex ~= MF.ID then -- XXX to remove for release + D:AddDebugText("DebuffsFrame_Update(): MicroFrameUpdateIndex ~= MF.ID", MicroFrameUpdateIndex, MF.ID, Unit, MF.CurrUnit, "ToPlace:", MF.ToPlace); + end + --]] + + MF.ToPlace = MicroFrameUpdateIndex; + + MF.Frame:SetPoint(unpack(MicroUnitF:GiveMFAnchor(MicroFrameUpdateIndex))); + if MF.Shown then + MF.Frame:Show(); + end + + -- test for GUID change and force a debuff update in this case + if UnitToGUID[MF.CurrUnit] ~= MF.UnitGUID then + MF.UpdateCountDown = 0; -- will force MF:Update() to be called + --[===[@debug@ + D:Println("|cFFFFAA55GUID change detected while placing for |r", MicroFrameUpdateIndex, UnitToGUID[MF.CurrUnit], MF.UnitGUID ); + --@end-debug@]===] + end + + ActionsDone = ActionsDone + 1; + end + + -- update the MUF attributes and its colors -- this is done by an event handler now (buff/debuff received...) except when the unit has a debuff and is in range + if MF and MicroFrameUpdateIndex <= NumToShow then + if not (MF.IsDebuffed or MF.IsCharmed) and MF.UpdateCountDown ~= 0 then + MF.UpdateCountDown = MF.UpdateCountDown - 1; + else -- if MF.IsDebuffed or MF.IsCharmed or MF.UpdateCountDown == 0 + ActionsDone = ActionsDone + MF:Update(false, true);--, not ((MF.IsDebuffed or MF.IsCharmed) and MF.UnitStatus ~= AFFLICTED)); -- we rescan debuffs if the unit is not in spell range XXX useless now since we rescan everyone every second + MF.UpdateCountDown = 3; + end + end + + -- we are done for this frame, go to te next + MicroFrameUpdateIndex = MicroFrameUpdateIndex + 1; + + -- don't update more than 5 MUF in a row + -- don't loop when reaching the end, wait for the next call (useful when less MUFs than PerUpdate) + if (ActionsDone > 5 or pass == UnitNum) then + --self:Debug("Max actions count reached"); + break; + end + + end + -- end + end -- }}} +end + +-- This little function returns the priority of the spell corresponding to an affliction type (one spell can be used for several types) +function D:GiveSpellPrioNum (Type) -- {{{ + return D.Status.CuringSpellsPrio[D.Status.CuringSpells[Type]]; +end -- }}} + + + + + + + + +-- old unused variables and functions +-- UNUSED STUFF {{{ +-- Micro Frame Events, useless for now + +function MicroUnitF:OnPostClick(frame, button) +-- D:Debug("Micro unit PostClicked"); +end + +function MicroUnitF:OnAttributeChanged(self, name, value) + D:Debug("Micro unit", name, "AttributeChanged to", value); +end + + +local MUF_Status = { -- unused + [1] = "normal"; + [2] = "absent"; + [3] = "far"; + [4] = "stealthed"; + [5] = "blacklist"; + [6] = "afflicted"; + [7] = "afflicted-far"; + [8] = "afflicted-charmed"; + [9] = "afflicted-charmed-far"; +} + + +local MF_Textures = { -- unused + "Interface/AddOns/Decursive/Textures/BackDrop-red", -- red + "Interface/AddOns/Decursive/Textures/BackDrop-blue", -- blue + "Interface/AddOns/Decursive/Textures/BackDrop-orange", -- orange + ["grey"] = "Interface\\AddOns\\Decursive\\Textures\\BackDrop-grey-medium", + ["black"] = "Interface/AddOns/Decursive/Textures/BackDrop", +}; + + +-- }}} + +T._LoadedFiles["Dcr_DebuffsFrame.lua"] = "2.5.1-6-gd3885c5"; + +-- Heresy diff --git a/Decursive/Dcr_DebuffsFrame.xml b/Decursive/Dcr_DebuffsFrame.xml new file mode 100644 index 0000000..d4127e1 --- /dev/null +++ b/Decursive/Dcr_DebuffsFrame.xml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + self.UpdateYourself = true; + self.MaxUnit = 5; + self.UnitShown = 0; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Decursive/Dcr_Events.lua b/Decursive/Dcr_Events.lua new file mode 100644 index 0000000..f9763d0 --- /dev/null +++ b/Decursive/Dcr_Events.lua @@ -0,0 +1,834 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then + -- the beautiful error popup : {{{ - + StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} + T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} + +if not T._LoadedFiles or not T._LoadedFiles["Dcr_opt.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_opt.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $"); + + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + +D.DebuffUpdateRequest = 0; + +--@alpha@ +D.DetectHistory = {}; +--@end-alpha@ + +local pairs = _G.pairs; +local next = _G.next; +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local unpack = _G.unpack; +local select = _G.select; +local table = _G.table; +local UnitCreatureFamily = _G.UnitCreatureFamily; +local InCombatLockdown = _G.InCombatLockdown; +local PlaySoundFile = _G.PlaySoundFile; +local UnitExists = _G.UnitExists; +local UnitCanAttack = _G.UnitCanAttack; +local UnitName = _G.UnitName; +local UnitGUID = _G.UnitGUID; +local GetTime = _G.GetTime; + + +function D:GroupChanged (reason) -- {{{ + -- this will trigger an update of the unit array + self.Groups_datas_are_invalid = true; + self.Status.GroupUpdateEvent = self:NiceTime(); + + if self.profile.ShowDebuffsFrame then + -- Update the MUFs display in a short moment + self.MicroUnitF:Delayed_MFsDisplay_Update (); + elseif not self.profile.Hide_LiveList then + D:ScheduleDelayedCall("Dcr_GetUnitArray", self.GetUnitArray, 1.5, self); + end + + + -- Test if we have to hide Decursive MUF window + if self.profile.AutoHideDebuffsFrame ~= 0 then + self:ScheduleDelayedCall("Dcr_CheckIfHideShow", self.AutoHideShowMUFs, 2, self); + end + + self:Debug("Groups changed", reason); +end -- }}} + +local OncePetRetry = false; + +function D:UNIT_PET (selfevent, Unit) -- {{{ + + -- when a pet changes somwhere, we update the unit array. + + D:Debug("Pet changed for: ", Unit); + if (D.profile.Scan_Pets and Unit ~= "focus" and self.Status.Unit_Array_UnitToGUID[Unit]) then + D:GroupChanged ("UNIT_PET"); + end + + + -- If the player's pet changed then we should check it for interesting spells + if ( Unit == "player" ) then + self:ScheduleDelayedCall("Dcr_CheckPet", self.UpdatePlayerPet, 2, self); + OncePetRetry = false; + D:Debug ("PLAYER pet detected! Poll in 2 seconds..."); + end +end -- }}} + +local curr_petType = false; +local last_petType = false; + +function D:UpdatePlayerPet () -- {{{ + curr_petType = UnitCreatureFamily("pet"); + D:Debug("|cFF0000FFCurrent Pet type is",curr_petType,"|r"); + + -- if we had a pet and lost it, retry once later... + if (last_petType and not curr_petType and not OncePetRetry) then + OncePetRetry = true; + + D:Debug("|cFF9900FFPet lost, retry in 10 seconds|r"); + D:ScheduleDelayedCall("Dcr_ReCheckPetOnce", D.UpdatePlayerPet, 10, self); + return; + end + + -- if we've changed of pet + if (last_petType ~= curr_petType) then + if (curr_petType) then D:Debug ("|cFF0066FFPet name changed:",curr_petType,"|r"); else D:Debug ("|cFF0066FFNo more pet!|r"); end; -- debug info only + + last_petType = curr_petType; + D:Configure(); + else + D:Debug ("|cFFAA66FFNo change in Pet Type",curr_petType,"|r"); + end +end -- }}} + + + +local FocusPrevious_ElligStatus = false; +function D:PLAYER_FOCUS_CHANGED () -- {{{ + + -- we need to rescan if the focus is not in our group and it's nice or if we already have a focus unit registered + + local FocusCurrent_ElligStatus = ( + not self.Status.Unit_Array_GUIDToUnit[UnitGUID("focus")] -- it's not already in the unit array + ) and ( UnitExists("focus") and (not UnitCanAttack("focus", "player") or UnitIsFriend("focus", "player"))) -- and it is (or used to) be nice + + + if not FocusCurrent_ElligStatus then FocusCurrent_ElligStatus = false; end -- avoid the difference between nil and false... + + if FocusCurrent_ElligStatus~=FocusPrevious_ElligStatus or self.Status.Unit_Array_UnitToGUID["focus"] then + self:GroupChanged ("FOCUS changed"); + self:Debug("Groups set to invalid due to focus update", FocusPrevious_ElligStatus, FocusCurrent_ElligStatus); + + self.MicroUnitF:UpdateMUFUnit("focus", true); -- update the focus unit + + if FocusCurrent_ElligStatus~=FocusPrevious_ElligStatus then -- if the focus is no longer valid, we need to update things + self.MicroUnitF:Delayed_MFsDisplay_Update(); + end + + FocusPrevious_ElligStatus = FocusCurrent_ElligStatus; + + end + + +end -- }}} + +function D:OnDebugEnable () + self.db.global.debugging = true; +end + +function D:OnDebugDisable () + self.db.global.debugging = false; +end + +-- This function update Decursive states : +-- - Clear the black list +-- - Execute things we couldn't do when in combat +local LastScanAllTime = 0; +D.Status.MaxConcurentUpdateDebuff = 0; +function D:ScheduledTasks() -- {{{ + + -- clean up the blacklist + for unit in pairs(self.Status.Blacklisted_Array) do + self.Status.Blacklisted_Array[unit] = self.Status.Blacklisted_Array[unit] - 0.1; + if (self.Status.Blacklisted_Array[unit] < 0) then + self.Status.Blacklisted_Array[unit] = nil; -- remove it from the BL + end + end + + if (self.Status.Combat and not InCombatLockdown()) then -- just in case... + D:LeaveCombat(); + end + + if (not InCombatLockdown() and self.Status.DelayedFunctionCallsCount > 0) then + for Id, FuncAndArgs in pairs (self.Status.DelayedFunctionCalls) do + D:Debug("Running post combat command", Id); + local DidSmth = FuncAndArgs.func(unpack(FuncAndArgs.args)); + self.Status.DelayedFunctionCalls[Id] = nil; -- remove it from the list + self.Status.DelayedFunctionCallsCount = self.Status.DelayedFunctionCallsCount - 1; + if (DidSmth) then + break; + end + end + end + + + if D.DebuffUpdateRequest > D.Status.MaxConcurentUpdateDebuff then + D.Status.MaxConcurentUpdateDebuff = D.DebuffUpdateRequest; + end + + -- Rescan all only if the MUF are used else we don't care at all... + if self.profile.ShowDebuffsFrame and GetTime() - LastScanAllTime > 1 then + self:ScanEveryBody(); + LastScanAllTime = GetTime(); + end + + D.DebuffUpdateRequest = 0; + +end --}}} + +-- the combat functions and events. // {{{ +------------------------------------------------------------------------------- +function D:EnterCombat() -- called on PLAYER_REGEN_DISABLED {{{ + -- this is not reliable for testing unitframe modifications authorization, + -- this event fires after the player enters in combat, only InCombatLockdown() may be used for critical checks + self.Status.Combat = true; +end --}}} + +local LastDebugReportNotification = 0; +function D:LeaveCombat() --{{{ + --D:Debug("Leaving combat"); + self.Status.Combat = false; + + -- test for debug report + if #T._DebugTextTable > 0 and GetTime() - LastDebugReportNotification > 300 then + if LastDebugReportNotification == 0 then + T._FatalError(L["DECURSIVE_DEBUG_REPORT_NOTIFY"]); + end + self:Println(L["DECURSIVE_DEBUG_REPORT_NOTIFY"]); + LastDebugReportNotification = GetTime(); + end +end --}}} +-- }}} + +-- This let us park command we can't execute while in combat to execute them later {{{ + -- the called function must return a non false value when it does something to prevent UI lagging +function D:AddDelayedFunctionCall(CallID,functionLink, ...) + + + if (not self.Status.DelayedFunctionCalls[CallID]) then + self.Status.DelayedFunctionCalls[CallID] = {["func"] = functionLink, ["args"] = {...}}; + self.Status.DelayedFunctionCallsCount = self.Status.DelayedFunctionCallsCount + 1; + elseif select("#",...) > 1 then -- if we had more than the function reference and its object + + local args = self.Status.DelayedFunctionCalls[CallID].args; + + for i=1,select("#",...), 1 do + args[i]=select(i, ...); + end + + end +end -- }}} + + + +function D:UPDATE_MOUSEOVER_UNIT () + if not self.profile.Hide_LiveList and not self.Status.MouseOveringMUF and not UnitCanAttack("mouseover", "player") then + -- D:Debug("will check MouseOver"); + self.LiveList:DelayedGetDebuff("mouseover"); + end +end + + + +function D:PLAYER_TARGET_CHANGED() + + if UnitExists("target") and not UnitCanAttack("player", "target") then + + D.Status.TargetExists = true; + + self.LiveList:DelayedGetDebuff("target"); + + + if self:CheckUnitStealth("target") then + self.Stealthed_Units["target"] = true; + end + + else + D.Status.TargetExists = false; + self.Stealthed_Units["target"] = false; + end +end + +function D:PLAYER_ALIVE() + D:Debug("|cFFFF0000PLAYER_ALIVE|r"); + D:ReConfigure(); + self:UnregisterEvent("PLAYER_ALIVE"); + D:CheckPlayer(); +end + +function D:LEARNED_SPELL_IN_TAB() + D:Debug("|cFFFF0000A new spell was learned, scheduling a reconfiguration|r"); + self:ScheduleDelayedCall("Dcr_NewSpellLearned", self.Configure, 5, self); +end + +function D:SPELLS_CHANGED() + D:Debug("|cFFFF0000Spells were changed, scheduling a reconfiguration check|r"); + self:ScheduleDelayedCall("Dcr_SpellsChanged", self.ReConfigure, 15, self); +end + +function D:PLAYER_TALENT_UPDATE() + D:Debug("|cFFFF0000Talents were changed, scheduling a reconfiguration check|r"); + self:ScheduleDelayedCall("Dcr_TalentUpdate", self.ReConfigure, 4, self); +end + +---[=[ +local SeenUnitEventsUNITAURA = {}; +local SeenUnitEventsCOMBAT = {}; + +do + local FAR = DC.FAR; + local UnitAura = _G.UnitAura; + local UnitGUID = _G.UnitGUID; + local UnitIsCharmed = _G.UnitIsCharmed; + local time = _G.time; + local GetTime = _G.GetTime; + -- This event manager is only here to catch events when the GUID unit array is not reliable. + -- For everything else the combat log event manager does the job since it's a lot more resource friendly. (UNIT_AURA fires way too often and provides no data) + function D:UNIT_AURA(selfevent, UnitID, ...) + + if not self.Status.Unit_Array_UnitToGUID[UnitID] then + -- self:Debug(UnitID, " |cFFFF7711is not in raid|r"); + return; + end + + local unitguid = UnitGUID(UnitID); + + --[===[@debug@ + + + --D:Debug("UNIT_AURA", ..., UnitID, GetTime() + (GetTime() % 1)); + + --@end-debug@]===] + + + -- Here we test if the GUID->Unit array is ok if it isn't we need to scan the unit for debuffs + -- We also scan the unit if it's charmed. The combatLog event manager tends to not detect those properly, the charm effect is a bitch to manage. + if unitguid ~= self.Status.Unit_Array_UnitToGUID[UnitID] or UnitID ~= self.Status.Unit_Array_GUIDToUnit[unitguid] or UnitIsCharmed(UnitID) then + + local unitToguid = self.Status.Unit_Array_UnitToGUID[UnitID]; + + -- if we updated the unit array but we are here then rebuild the unit array. + if self.Status.GroupUpdatedOn >= self.Status.GroupUpdateEvent then + + D:GroupChanged("UNIT_AURA-|cFFFF0000bad group detection|r"); + + --[=[ + self:AddDebugText("AURA event received and Unit_Array_UnitToGUID ~= UnitGUID() and groups up to date, SG:", self.Status.Unit_Array_UnitToGUID[UnitID], + "FG:", unitguid, + "Unit ID:|cFFFF0000", UnitID, + "|rGUIDToUnit[UnitGUID()]:", unitguid and self.Status.Unit_Array_GUIDToUnit[unitguid] or "Xnoguid", + "GUIDToUnit[UnitToGUID[]]:|cFFFF0000", unitToguid and self.Status.Unit_Array_GUIDToUnit[unitToguid] or "Xnone", + "|rScP:", self.profile.Scan_Pets, + "LGU:", self.Status.GroupUpdatedOn, + "LGuEr", self.Status.GroupUpdateEvent, + "foundUnits:", #self.Status.Unit_Array, + "RealRaidNum:", GetNumRaidMembers() + --"Zone:", GetZoneText() + --"FUnitsList:", unpack(D.Status.Unit_Array) + ); + --]=] + end + + --[===[@debug@ + self:Debug("|cFF552255UNIT_AURA triggers a rescan|r because of", UnitID); + --@end-debug@]===] + + if unitguid then + self.Status.Unit_Array_UnitToGUID[UnitID] = unitguid; + self.Status.Unit_Array_GUIDToUnit[unitguid] = UnitID; + end + + if self.profile.ShowDebuffsFrame and self.MicroUnitF.UnitToMUF[UnitID] then + + if self.MicroUnitF.UnitToMUF[UnitID].UnitStatus == FAR then + --self:Debug(UnitID, " |cFFFF7711is too far|r (UNIT_AURA)"); + return + end + + -- get out of here if this is just about a fucking buff, combat log event manager handles those... unless there is no debuff because the last was removed + if not UnitAura(UnitID, 1, "HARMFUL") and not self.MicroUnitF.UnitToMUF[UnitID].IsDebuffed then + --self:Debug(UnitID, " |cFFFF7711has no debuff|r (UNIT_AURA)"); + return; + end + + --self:errln("update schedule for MUF", UnitID); + self.MicroUnitF:UpdateMUFUnit(UnitID, true); + return; + end + + if not self.profile.Hide_LiveList then + self.LiveList:DelayedGetDebuff(UnitID); + end + end + end +end + +--]=] + + +do + local bit = _G.bit; + local band = bit.band; + local bor = bit.bor; + local UnitGUID = _G.UnitGUID; + local GetTime = _G.GetTime; + local GetSpellInfo = _G.GetSpellInfo; + local time = _G.time; + local timev = 0; + + + --@alpha@ + local DetectHistoryIndex = 1; + --@end-alpha@ + + -- AURA bitfields -- now useless {{{ + -- a friendly player character controled directly by the player that is not an outsider + local PLAYER = bit.bor (COMBATLOG_OBJECT_CONTROL_PLAYER , COMBATLOG_OBJECT_TYPE_PLAYER , COMBATLOG_OBJECT_REACTION_FRIENDLY ); -- still used + local PLAYER_MASK = bit.bnot (COMBATLOG_OBJECT_AFFILIATION_OUTSIDER); + + -- a hostile player character contoled as a pet and that is not an outsider + local REACTION_HOSTILE = COMBATLOG_OBJECT_REACTION_HOSTILE; + + -- a pet controled by a friendly player that is not an outsider + local PET = bit.bor (COMBATLOG_OBJECT_CONTROL_PLAYER , COMBATLOG_OBJECT_TYPE_PET , COMBATLOG_OBJECT_REACTION_FRIENDLY ); + local PET_MASK = bit.bnot (COMBATLOG_OBJECT_AFFILIATION_OUTSIDER); + + -- An outsider friendly focused unit + local FOCUSED_FRIEND = bit.bor (COMBATLOG_OBJECT_REACTION_FRIENDLY , COMBATLOG_OBJECT_FOCUS , COMBATLOG_OBJECT_AFFILIATION_OUTSIDER); + -- }}} + + local OUTSIDER = COMBATLOG_OBJECT_AFFILIATION_OUTSIDER; + local HOSTILE_OUTSIDER = bit.bor (COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, COMBATLOG_OBJECT_REACTION_HOSTILE); + local FRIENDLY_TARGET = bit.bor (COMBATLOG_OBJECT_TARGET, COMBATLOG_OBJECT_REACTION_FRIENDLY); + local ME = COMBATLOG_OBJECT_AFFILIATION_MINE; + + + local AuraEvents = { -- check if there are some other rare events... + ["SPELL_AURA_APPLIED"] = 1, + ["SPELL_AURA_APPLIED_DOSE"] = 1, + ["SPELL_AURA_REMOVED"] = 0, + ["SPELL_AURA_APPLIED_DOSE"] = 1, + ["SPELL_AURA_REMOVED_DOSE"] = 0, + ["UNIT_DIED"] = 0, + --["SPELL_AURA_DISPELLED"] = 0, + }; + + local SpellEvents = { + ["SPELL_MISSED"] = true, + ["SPELL_CAST_START"] = true, + ["SPELL_CAST_FAILED"] = true, + ["SPELL_CAST_SUCCESS"] = true, + ["SPELL_DISPEL_FAILED"] = true, + }; + + local UnitID; + + function D:DummyDebuff (UnitID) + --[=[ + if self.profile.ShowDebuffsFrame then + self.MicroUnitF:UpdateMUFUnit(UnitID); + elseif not self.profile.Hide_LiveList then + self.LiveList:DelayedGetDebuff(UnitID); + end + --]=] + D:COMBAT_LOG_EVENT_UNFILTERED("COMBAT_LOG_EVENT_UNFILTERED", 0, "SPELL_AURA_APPLIED", nil, nil, COMBATLOG_OBJECT_NONE, UnitGUID(UnitID), (UnitName(UnitID)), PLAYER, 0, "Test item", 0x32, "DEBUFF"); + end + + local SpecialDebuffs = { + [59868] = "SPELL_DAMAGE", + }; + + local oldest = 0; + + function D:COMBAT_LOG_EVENT_UNFILTERED(selfevent, timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, arg9, arg10, arg11, arg12) + + --@alpha@ + --[=[ + if destGUID or destName or arg10 then + UnitID = self.Status.Unit_Array_GUIDToUnit[destGUID]; -- get the grouped unit associated to the destGUID if there is none then the unit is not in our group or is filtered out + timev = GetTime(); + + if timev - oldest > 120 then DetectHistoryIndex = 1 end + + if DetectHistoryIndex == 1 then + oldest = timev; + end + + if not D.DetectHistory[DetectHistoryIndex] then + D.DetectHistory[DetectHistoryIndex] = {timev, UnitID or "NIL", timestamp or "NIL", event or "NIL", sourceGUID or "NIL", sourceName or "NIL", sourceFlags or "NIL", destGUID or "NIL", destName or "NIL", destFlags or "NIL", arg9 or "NIL", arg10 or "NIL", arg11 or "NIL", arg12 or "NIL"}; + else + local temp = D.DetectHistory[DetectHistoryIndex]; + + temp[1] = timev; + temp[2] = UnitID or "NIL"; + temp[3] = timestamp or "NIL"; + temp[4] = event or "NIL"; + temp[5] = sourceGUID or "NIL"; + temp[6] = sourceName or "NIL"; + temp[7] = sourceFlags or "NIL"; + temp[8] = destGUID or "NIL"; + temp[9] = destName or "NIL"; + temp[10] = destFlags or "NIL"; + temp[11] = arg9 or "NIL"; + temp[12] = arg10 or "NIL"; + temp[13] = arg11 or "NIL"; + temp[14] = arg12 or "NIL"; + end + DetectHistoryIndex = DetectHistoryIndex + 1; + end + --]=] + --@end-alpha@ + + + -- check for exceptions + if SpecialDebuffs[arg9] and event == SpecialDebuffs[arg9] then + + --[=[ + --@alpha@ + if self.Status.CuringSpells[DC.MAGIC] then + UnitID = self.Status.Unit_Array_GUIDToUnit[destGUID]; -- get the grouped unit associated to the destGUID if there is none then the unit is not in our group or is filtered out + --self:AddDebugText("CbEvent with DM:", timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, arg9, arg10, arg11, arg12, "Z:", GetZoneText(), "Unit:", UnitID); + end + --@end-alpha@ + --]=] + + event = "SPELL_AURA_APPLIED"; + end + + if destName and AuraEvents[event] then + + if not self.DcrFullyInitialized then + self:Println("|cFFFF0000Could not process event: init uncomplete!|r"); + return; + end + + UnitID = self.Status.Unit_Array_GUIDToUnit[destGUID]; -- get the grouped unit associated to the destGUID if there is none then the unit is not in our group or is filtered out + + --D:Print("? (source=%s) (dest=|cFF00AA00%s|r -- %X): |cffff0000%s|r", sourceName, destName, destFlags, event); + + + -- we don't use the following test because it's unecessary, if a unit is missing, it'll be scanned on addition anyway... + --if not UnitID and band (destFlags, OUTSIDER) ~= OUTSIDER then -- we don't have a unit but the flags say it's in our group... + --end + + if UnitID then -- this test is enough, if the unit is groupped we definetely need to scan it, whatever is its status... + + --[=[ + if UnitGUID(UnitID) ~= destGUID then -- sometimes UnitGUID("player") may returns nil... but it's not important since the player GUID is registered once and for all at init time + + self.Groups_datas_are_invalid = true; + self:GetUnitArray(); + UnitID = self.Status.Unit_Array_GUIDToUnit[destGUID]; + + if not UnitID then + D:Debug("|cFFFF0000No unit for GUID %s|r, in skip list?", destGUID); + return; + end + end + --]=] + + if arg12 == "BUFF" and self.profile.Show_Stealthed_Status then + + if DC.IsStealthBuff[arg10] then + if AuraEvents[event] == 1 then + self.Stealthed_Units[UnitID] = true; + else + if self.debugging then D:Debug("STEALTH LOST: ", UnitID, arg10); end + self.Stealthed_Units[UnitID] = false; + end + self.MicroUnitF:UpdateMUFUnit(UnitID); + end + else + + --[===[@debug@ + if self.debugging then D:Debug("Debuff, UnitId: ", UnitID, arg10, event, time() + (GetTime() % 1), timestamp); end + --@end-debug@]===] + + if self.profile.ShowDebuffsFrame then + self.MicroUnitF:UpdateMUFUnit(UnitID); + + --@alpha@ + --D.DetectHistory[DetectHistoryIndex - 1][4] = D.DetectHistory[DetectHistoryIndex - 1][4] .. " DETECTED by cem " .. D.DebuffUpdateRequest; + --@end-alpha@ + + elseif not self.profile.Hide_LiveList then + if self.debugging then D:Debug("(LiveList) Registering delayed GetDebuff for ", destName); end + self.LiveList:DelayedGetDebuff(UnitID); + end + + if event == "UNIT_DIED" then + self.Stealthed_Units[UnitID] = false; + end + + end + end + + if self.Status.TargetExists and band (destFlags, FRIENDLY_TARGET) == FRIENDLY_TARGET then -- TARGET + + if self.debugging then D:Debug("A Target got something (source=", sourceName, "sFlags:", D:NumToHexStr(sourceFlags), "(dest=|cFF00AA00", destName, "dFlags:", D:NumToHexStr(destFlags), "|r, |cffff0000", event, "|r, |cFF00AAAA", arg10, "|r", arg12); end + + self.LiveList:DelayedGetDebuff("target"); + + if arg12 == "BUFF" and self.profile.Show_Stealthed_Status then + if DC.IsStealthBuff[arg10] then + if AuraEvents[event] == 1 then + self.Stealthed_Units["target"] = true; + else + if self.debugging then D:Debug("TARGET STEALTH LOST: ", "target", arg10); end + self.Stealthed_Units["target"] = false; + end + end + end + end + + -- SPELL EVENTS {{{ + elseif self.Status.ClickedMF and SpellEvents[event] and self.Status.CuringSpellsPrio[arg10] and band(sourceFlags, ME) ~= 0 then -- SPELL_MISSED SPELL_CAST_START SPELL_CAST_FAILED SPELL_CAST_SUCCESS DISPEL_FAILED + + if event == "SPELL_CAST_START" then -- useless + + self:Print("|cFFFF0000Starting SPELL: ", arg10, "|r"); + self:ScheduleDelayedCall("Dcr_UpdatePC"..self.Status.ClickedMF.CurrUnit, self.Status.ClickedMF.Update, 1 + ((select(7, GetSpellInfo(arg9))) / 1000), self.Status.ClickedMF); + end + + if event == "SPELL_CAST_SUCCESS" then + + if self.debugging then self:Debug(L["SUCCESSCAST"], arg10, (select(2, GetSpellInfo(arg9))), D:MakePlayerName(destName)); end + + --self:Debug("|cFFFF0000XXXXX|r |cFF11FF11Updating color of clicked frame|r"); + self:ScheduleDelayedCall("Dcr_UpdatePC"..self.Status.ClickedMF.CurrUnit, self.Status.ClickedMF.Update, 1, self.Status.ClickedMF); + self:ScheduleDelayedCall("Dcr_clickedMFreset", + function() + if D.Status.ClickedMF then + D.Status.ClickedMF.SPELL_CAST_SUCCESS = false; + D.Status.ClickedMF = false; + if self.debugging then D:Debug("ClickedMF to false (sched)"); end + end + end, 0.1 ); + + self.Status.ClickedMF.SPELL_CAST_SUCCESS = true; + + end + + if event == "SPELL_CAST_FAILED" and not D.Status.ClickedMF.SPELL_CAST_SUCCESS then + destName = self:PetUnitName( self.Status.ClickedMF.CurrUnit, true); + + D:Println(L["FAILEDCAST"], arg10, (select(2, GetSpellInfo(arg9))), D:MakePlayerName(destName), arg12); + + if (arg12 == SPELL_FAILED_LINE_OF_SIGHT or arg12 == SPELL_FAILED_BAD_TARGETS) then + + if not self.profile.DoNot_Blacklist_Prio_List or not self:IsInPriorList(self.Status.Unit_Array_UnitToGUID[self.Status.ClickedMF.CurrUnit]) then + self.Status.Blacklisted_Array[self.Status.ClickedMF.CurrUnit] = self.profile.CureBlacklist; + + self:Debug("|cFFFF0000XXXXX|r |cFF11FF11Updating color of blacklist frame|r"); + self:ScheduleDelayedCall("Dcr_Update"..self.Status.ClickedMF.CurrUnit, self.Status.ClickedMF.UpdateSkippingSetBuf, self.profile.DebuffsFrameRefreshRate, self.Status.ClickedMF); + end + + PlaySoundFile(DC.FailedSound); + --[=[ + elseif arg12 == SPELL_FAILED_BAD_IMPLICIT_TARGETS then + self:AddDebugText("ERR_GENERIC_NO_TARGET", "Unit:", self.Status.ClickedMF.CurrUnit, "UE:", UnitExists(self.Status.ClickedMF.CurrUnit), "UiF:", UnitIsFriend("player",self.Status.ClickedMF.CurrUnit), "CBEs:", timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, arg9, arg10, arg11, arg12); --]=] + end + self.Status.ClickedMF = false; + + elseif event == "SPELL_MISSED" or event == "SPELL_DISPEL_FAILED" then -- XXX to test + destName = self:PetUnitName( self.Status.ClickedMF.CurrUnit, true); + + D:Println(L["FAILEDCAST"], arg10, (select(2, GetSpellInfo(arg9))), D:MakePlayerName(destName), arg12); + PlaySoundFile(DC.FailedSound); + self.Status.ClickedMF = false; + end + + + ---- }}} + --else + -- if self.debugging then D:Debug(sourceName, sourceFlags, destName, destFlags, event, arg10, arg11, arg12, arg13, arg14, arg15, arg16); end + -- }}} + end + + + end + + + +end + +function D:SPELL_UPDATE_COOLDOWN() + D.Status.UpdateCooldown = GetTime(); +end + +T.LastVCheck = 0; +function D:AskVersion() + + if InCombatLockdown() then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ("AskVersion", self.AskVersion); + return false; + end + + if GetTime() - T.LastVCheck < 60 then + D:Debug("AskVersion(): Too early!"); + return false; + end + + T.LastVCheck = GetTime(); + + local Distribution = false; + -- "PARTY", "RAID", "GUILD", "BATTLEGROUND". As of 2.1, "WHISPER" + + if UnitExists("target") and (UnitFactionGroup("target")) == (UnitFactionGroup("player")) and (tonumber((UnitGUID("target")):sub(5,5), 16) % 8) == 0 then -- the unit exists and is a player of our faction + LibStub("AceComm-3.0"):SendCommMessage( "DecursiveVersion", "giveversion", "WHISPER", self:UnitName("target")); + D:Debug("Asking version to ", self:UnitName("target")); + end + + local inInstance, InstanceType = IsInInstance(); + + if InstanceType == "pvp" then + Distribution = "BATTLEGROUND"; + end + + if not Distribution then + if GetNumRaidMembers() ~= 0 then + Distribution = "RAID"; + elseif UnitExists("party1") then + Distribution = "PARTY"; + elseif GetGuildInfo("player") then + Distribution = "GUILD"; + end + end + + if Distribution then + LibStub("AceComm-3.0"):SendCommMessage( "DecursiveVersion", "giveversion", Distribution); + end + D:Debug("Asking version on ", Distribution); + + return true; + +end + +local LastVersionQueryAnswerPerFrom = {}; +local LastVersionQueryAnswerPerDist = {}; +function D:OnCommReceived(message, distribution, from) + local alpha = false; + --@alpha@ + alpha = true; + --@end-alpha@ + + --@alpha@ + D:Debug("OnCommReceived:", message, distribution, from); + --@end-alpha@ + + local time = GetTime(); + + -- answer version queries but no more than once every 60 seconds to the same player and every 5 seconds to the same chanel + -- This avoids a player who would be crafting its own version query messages and sending them repeatidly from causing any damage + -- This avoids race conditions where several players would send a version query at the same time on the same chanel + if message == "giveversion" + and (not LastVersionQueryAnswerPerDist[distribution] or time - LastVersionQueryAnswerPerDist[distribution] > 5 ) + and (not LastVersionQueryAnswerPerFrom[from] or time - LastVersionQueryAnswerPerFrom[from] > 60 ) + then + + LibStub("AceComm-3.0"):SendCommMessage("DecursiveVersion", ("Version: %s,%u,%d,%d"):format(D.version, D.VersionTimeStamp, alpha and 1 or 0, D:IsEnabled() and 1 or 0 ), distribution, from ) + + LastVersionQueryAnswerPerFrom[from] = time; + LastVersionQueryAnswerPerDist[distribution] = time; + + --@alpha@ + if self.debugging then D:Debug("Version info sent to, ", from, "by", distribution, ("Version: %s,%u,%d,%d"):format(D.version, D.VersionTimeStamp, alpha and 1 or 0, D:IsEnabled() and 1 or 0 )); end + --@end-alpha@ + + elseif message:sub(1, 8) == "Version:" then + + local version, date, isAlpha, enabled = message:match ("^Version: ([^,]+),(%d+),(%d),(%d)"); + + --@alpha@ + if self.debugging then D:Debug("Version info received from, ", from, "by", distribution, "version:", version, "date:", date, "islpha:", isAlpha, "enabled:", enabled); end + --@end-alpha@ + + if version then + if not D.versions then + D.versions = {} + end + + D.versions[from] = { version, date, isAlpha, enabled }; + + --delayed call to LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name); plus "spam" prevention system (after receiving version info from someone) + if not D:DelayedCallExixts ("NewversionDatareceived") then + D:ScheduleDelayedCall("NewversionDatareceived", LibStub("AceConfigRegistry-3.0").NotifyChange, 1, LibStub("AceConfigRegistry-3.0"), D.name); + T.LastVCheck = time; + end + else + D:Debug("Malformed version string received: ", message); + end + else + D:Debug("Unhandled comm received (spam?)"); + end +end + +function D:ReturnVersions() + if not D.versions then + return "no data available"; + end + + local formatedversions = {}; + for name, versiondetails in pairs(D.versions) do + formatedversions[#formatedversions + 1] = ("%s: %s %s (%s)"):format(D:ColorText(name, "FF00AA00"), versiondetails[1], versiondetails[4]==0 and D:ColorText("disabled", "FFFF0000") or "", date("%Y-%m-%d", versiondetails[2])); + end + + return table.concat(formatedversions, "\n"); +end + +T._LoadedFiles["Dcr_Events.lua"] = "2.5.1-6-gd3885c5"; + +-- The Great Below diff --git a/Decursive/Dcr_LDB.lua b/Decursive/Dcr_LDB.lua new file mode 100644 index 0000000..e20682f --- /dev/null +++ b/Decursive/Dcr_LDB.lua @@ -0,0 +1,202 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. + +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["DCR_init.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (DCR_init.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local DC = DcrC; +local D = Dcr; +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + + +local icon = LibStub("LibDBIcon-1.0"); + +local LibQTip = LibStub('LibQTip-1.0'); + + +local LDB = LibStub("LibDataBroker-1.1"):NewDataObject("Decursive", { + type = "launcher", + OnClick = function(Frame, button) + D:QuickAccess(Frame, button); + end, + + text = "Decursive", + label = "Decursive", + + icon = DC.IconOFF, +}); + + +local HeadFont; +local function CreateFonts() + + Dcr:Debug("create font called"); + + -- Create the fonts objects we'll use in the tooltip: + -- New font looking like GameTooltipText + local HeadFont = CreateFont("DCR_QT_HeadFont") + HeadFont:SetFont(GameTooltipText:GetFont(), 16) + HeadFont:SetTextColor(0.8,0.8,0.3) + + --[=[ + + -- New font looking like defaultFont + local CommandFont = CreateFont("DCR_QT_CommandFont") + CommandFont:CopyFontObject(defaultFont) + CommandFont:SetTextColor(0,1,0) + + --]=] + + return HeadFont; + + +end + + +local function ShowToolTip (frame) + if not D.DcrFullyInitialized then + return; + end + + --Dcr:Debug("ShowToolTip called"); + + if not HeadFont then + HeadFont = CreateFonts(); + end + + local tooltip = LibQTip:Acquire("DecursiveGenInfo", 2, "LEFT", "RIGHT"); + frame.tooltip = tooltip + + tooltip:SetHeaderFont(HeadFont); + + local x, y; + -- 1 + x, y = tooltip:AddLine(); + tooltip:SetCell(x,y,'Decursive', HeadFont,"CENTER",2); + + -- 2 + --tooltip:AddLine( ("|cFF00FF00%s|r: "):format(D.L["HLP_RIGHTCLICK"]), + -- D.L["STR_OPTIONS"]); + + -- 3 + tooltip:AddLine( ("|cFF00FF00%s-%s|r: "):format(D.L["ALT"], D.L["HLP_RIGHTCLICK"]), + D.L["BINDING_NAME_DCRSHOWOPTION"]); + + -- 4 + tooltip:AddLine( ("|cFF00FF00%s-%s|r: "):format(D.L["CTRL"], D.L["HLP_LEFTCLICK"]), + D.L["BINDING_NAME_DCRPRSHOW"]); + + -- 5 + tooltip:AddLine( ("|cFF00FF00%s-%s|r: "):format(D.L["SHIFT"], D.L["HLP_LEFTCLICK"]), + D.L["BINDING_NAME_DCRSKSHOW"]); + + -- 6 + tooltip:AddLine( ("|cFF00FF00%s-%s|r: " ):format(D.L["SHIFT"], D.L["HLP_RIGHTCLICK"]), + D.L["BINDING_NAME_DCRSHOW"]); + + if (D.db.global.debugging) then + tooltip:AddSeparator(); + + x, y = tooltip:AddLine(); + tooltip:SetCell(x,y,'Debugging', HeadFont,"CENTER",2); + + tooltip:AddLine("Afflicted units count:", D.ForLLDebuffedUnitsNum); + + tooltip:AddLine("Afflicted units count in range:", D.MicroUnitF.UnitsDebuffedInRange); + + tooltip:AddLine("Max Concurrent update events:", D.Status.MaxConcurentUpdateDebuff); + + tooltip:AddSeparator(); + + x, y = tooltip:AddLine(); + tooltip:SetCell(x,y,'Debuff seen history:', HeadFont,"CENTER",2); + + local HistoryIndex = 1; + + while HistoryIndex < 10 do + tooltip:AddLine( "|cFFAAFFAA"..HistoryIndex.."|r", (D:Debuff_History_Get (HistoryIndex, true))); + + HistoryIndex = HistoryIndex + 1; + + end + end + + -- Use smart anchoring code to anchor the tooltip to our frame + tooltip:SmartAnchorTo(frame) + + -- Show it + tooltip:Show() + +end + +LDB.OnEnter = function(frame) + ShowToolTip(frame); +end + +LDB.OnLeave = function(frame) + LibQTip:Release(frame.tooltip) + frame.tooltip = nil + + --Dcr:Debug("Releasing tooltip"); +end + + + +function D:SetIcon (icon) + LDB.icon = icon; +end + +function D:SetMinimapIcon() + if not icon:IsRegistered("Decursive") then + icon:Register("Decursive", LDB, D.profile.MiniMapIcon); + end +end + +function D:HideMiniMapIcon() + icon:Hide(); +end + +T._LoadedFiles["Dcr_LDB.lua"] = "2.5.1-6-gd3885c5"; diff --git a/Decursive/Dcr_LiveList.lua b/Decursive/Dcr_LiveList.lua new file mode 100644 index 0000000..1eef552 --- /dev/null +++ b/Decursive/Dcr_LiveList.lua @@ -0,0 +1,575 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. + +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Dcr_DebuffsFrame.xml"] or not T._LoadedFiles["Dcr_DebuffsFrame.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_DebuffsFrame.xml or Dcr_DebuffsFrame.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $"); + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + +--D.LiveList = OOP.Class(); + +-- http://www.lua.org/pil/13.4.1.html +-- define the namespace +D.LiveList = {}; +local LiveList = D.LiveList; +-- a prototype for LiveList objects, empty, defaults are defined in the :New for better performances +LiveList.prototype = {}; +LiveList.metatable ={ __index = LiveList.prototype }; + +function LiveList:new(...) + local instance = setmetatable({}, self.metatable); + instance:init(...); + return instance; +end + +local MicroUnitF = D.MicroUnitF; + +LiveList.ExistingPerID = {}; +LiveList.Number = 0; +LiveList.NumberShown = 0; +D.ForLLDebuffedUnitsNum = 0; + +-- temporary variables often used in function +local Debuff, Debuffs, IsCharmed, MF, i, Index, RangeStatus; + +-- local shortcuts to often called global functions +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local next = _G.next; +local select = _G.select; +local unpack = _G.unpack; +local table = _G.table; +local UnitExists = _G.UnitExists; +local IsSpellInRange = _G.IsSpellInRange; +local UnitClass = _G.UnitClass; +local UnitIsFriend = _G.UnitIsFriend; +local UnitGUID = _G.UnitGUID; +local floor = _G.math.floor; +local str_upper = _G.string.upper; +local GetRaidTargetIndex= _G.GetRaidTargetIndex; + + +-- defines what is printed when the object is read as a string +function LiveList:ToString() -- {{{ + return "Decursive Live-List object"; +end -- }}} + +-- The Factory for LiveList objects +function LiveList:Create() -- {{{ + + if self.Number + 1 > D.profile.Amount_Of_Afflicted then + return false; + end + + self.Number = self.Number + 1; + + self.ExistingPerID[self.Number] = self:new(DcrLiveList, self.Number); + + + return self.ExistingPerID[self.Number]; + +end -- }}} + +function LiveList:DisplayItem (ID, UnitID, Debuff) -- {{{ + + --D:Debug("(LiveList) Displaying LVItem %d for UnitID %s", ID, UnitID); + local LVItem = false; + + if ID > self.Number + 1 then + return error(("LiveList:DisplayItem: bad argument #1 'ID (= %d)' must be < LiveList.Number + 1 (LiveList.Number = %d)"):format(ID, self.Number),2); + end + + if not self.ExistingPerID[ID] then + LVItem=self:Create(); + else + LVItem = self.ExistingPerID[ID]; + end + + if not LVItem then + return false; + end + + if not Debuff then + Debuff = D.ManagedDebuffUnitCache[UnitID][1]; + end + + LVItem:SetDebuff(UnitID, Debuff, nil); + --D:Debug("XXXX => Updating ll item %d for %s", ID, UnitID); + + if not LVItem.IsShown then + --D:Debug("(LiveList) Showing LVItem %d", ID); + LVItem.Frame:Show(); + self.NumberShown = self.NumberShown + 1; + LVItem.IsShown = true; + end + +end -- }}} + +function LiveList:RestAllPosition () -- {{{ + for _, LVitem in ipairs(self.ExistingPerID) do + LVitem.Frame:ClearAllPoints(); + LVitem.Frame:SetPoint(LVitem:GiveAnchor()); + end +end -- }}} + +function LiveList.prototype:GiveAnchor() -- {{{ + + local ItemHeight = self.Frame:GetHeight(); + + if D.profile.ReverseLiveDisplay then + end + + if self.ID == 1 then + if D.profile.ReverseLiveDisplay then + return "BOTTOMLEFT", DecursiveMainBar, "BOTTOMLEFT", 5, -1 * (ItemHeight + 1) * D.profile.Amount_Of_Afflicted; + else + return "TOPLEFT", DecursiveMainBar, "BOTTOMLEFT", 5, 0; + end + else + if D.profile.ReverseLiveDisplay then + return "BOTTOMLEFT", LiveList.ExistingPerID[self.ID - 1].Frame, "TOPLEFT", 0, 1; + else + return "TOPLEFT", LiveList.ExistingPerID[self.ID - 1].Frame, "BOTTOMLEFT", 0, -1; + end + end + +end -- }}} + + +function LiveList.prototype:init(Container,ID) -- {{{ + + --LiveList.super.prototype.init(self); -- needed + D:Debug("(LiveList) Initializing LiveList object '%s'", ID); + + --ObjectRelated + self.ID = ID; + self.IsShown = false; + self.Parent = Container; + + --Debuff info + self.UnitID = false; + self.UnitName = false; + self.RaidTargetIndex = false; + self.PrevUnitName = false; + self.PrevUnitID = false; + self.PrevRaidTargetIndex= false; + self.UnitClass = false; + + self.Debuff = {}; + + self.PrevDebuffIndex = false; + self.PrevDebuffName = false; + self.PrevDebuffTypeName = false; + self.PrevDebuffApplicaton = false; + self.PrevDebuffTexture = false; + + self.IsCharmed = false; + self.PrevIsCharmed = false; + + self.Alpha = false; + + -- Create the frame + self.Frame = CreateFrame ("Button", "DcrLiveListItem"..ID, self.Parent, "DcrLVItemTemplate"); + + -- Set some basic properties + self.Frame:SetFrameStrata("LOW"); + self.Frame:RegisterForClicks("AnyDown"); + + -- Set the anchor of this item + self.Frame:SetPoint(self:GiveAnchor()); + + -- create the background + self.BackGroundTexture = self.Frame:CreateTexture("DcrLiveListItem"..ID.."BackTexture", "BACKGROUND", "DcrLVBackgroundTemplate"); + + -- Create the Icon Texture + self.IconTexture = self.Frame:CreateTexture("DcrLiveListItem"..ID.."Icon", "ARTWORK", "DcrLVIconTemplate"); + + -- Create the Debuff application count font string + self.DebuffAppsFontString = self.Frame:CreateFontString("DcrLiveListItem"..ID.."Count", "OVERLAY", "DcrLLAfflictionCountFont"); + + -- Create the character name Fontstring + self.UnitNameFontString = self.Frame:CreateFontString("DcrLiveListItem"..ID.."UnitName", "OVERLAY", "DcrLLUnitNameFont"); + + -- Create the unitID Fontstring + self.UnitIDFontString = self.Frame:CreateFontString("DcrLiveListItem"..ID.."UnitID", "OVERLAY", "DcrLLUnitIDFont"); + --self.UnitIDFontString:SetHeight(3); + + -- Create the debuff type fontstring + self.DebuffTypeFontString = self.Frame:CreateFontString("DcrLiveListItem"..ID.."Type", "OVERLAY", "DcrLLDebuffTypeFont"); + + -- Create the Raid Target Icon Texture + self.RaidIconTexture = self.Frame:CreateTexture("DcrLiveListItem"..ID.."RaidIcon", "ARTWORK", "DcrLVRaidIconTemplate"); + + -- Create the debuff name fontstring + self.DebuffNameFontString = self.Frame:CreateFontString("DcrLiveListItem"..ID.."Name", "OVERLAY", "DcrLLDebuffNameFont"); + + + -- a reference to this object + self.Frame.Object = self; + + self.Frame:Show(); + + +end -- }}} + +function LiveList.prototype:SetDebuff(UnitID, Debuff, IsCharmed) -- {{{ + self.UnitID = UnitID; + self.UnitName = D:PetUnitName(UnitID, true); + self.Debuff = Debuff; + self.IsCharmed = IsCharmed; + self.RaidTargetIndex = GetRaidTargetIndex(UnitID); + + if D.profile.LiveListAlpha ~= self.Alpha then + self.Frame:SetAlpha(D.profile.LiveListAlpha); + self.Alpha = D.profile.LiveListAlpha; + end + + -- Set the graphical elements to the right values + -- Icon + if self.PrevDebuffTexture ~= Debuff.Texture then + self.IconTexture:SetTexture(Debuff.Texture); + self.PrevDebuffTexture = Debuff.Texture; + end + + -- Raid Icon + if self.PrevRaidTargetIndex ~= self.RaidTargetIndex then + self.RaidIconTexture:SetTexture(self.RaidTargetIndex and DC.RAID_ICON_TEXTURE_LIST[self.RaidTargetIndex] or nil); + self.PrevRaidTargetIndex = self.RaidTargetIndex; + end + + -- Applications count + if self.PrevDebuffApplicaton ~= Debuff.Applications then + if (Debuff.Applications > 1) then + self.DebuffAppsFontString:SetText(Debuff.Applications); + self.PrevDebuffApplicaton = Debuff.Applications; + else + self.DebuffAppsFontString:SetText(" "); + self.PrevDebuffApplicaton = " "; + end + end + + -- Unit Name + if self.PrevUnitName ~= self.UnitName then + self.UnitClass = (select(2, UnitClass(UnitID))); + self.UnitNameFontString:SetText(self.UnitName); + if self.UnitClass then + self.UnitNameFontString:SetTextColor(unpack(DC.ClassesColors[self.UnitClass])); + end + self.PrevUnitName = self.UnitName; + --D:Debug("(LiveList) Updating %d with %s", self.ID, UnitID); + end + + -- Unit ID + if self.PrevUnitID ~= UnitID then + self.UnitIDFontString:SetText("( "..UnitID.." )"); + self.PrevUnitID = UnitID; + end + + -- Debuff Type Name + if self.PrevDebuffTypeName ~= Debuff.TypeName then + if Debuff.Type then + self.DebuffTypeFontString:SetText(D:ColorText(L[str_upper(Debuff.TypeName)], "FF" .. DC.TypeColors[Debuff.Type] )); + --self.DebuffTypeFontString:SetTextColor(DC.TypeColors[Debuff.Type]); + else + self.DebuffTypeFontString:SetText("Unknown"); + end + self.PrevDebuffTypeName = Debuff.TypeName; + end + + -- Debuff Name + if self.PrevDebuffName ~= Debuff.Name then + self.DebuffNameFontString:SetText(Debuff.Name); + self.PrevDebuffName = Debuff.Name; + end + +end -- }}} + + +function LiveList:GetDebuff(UnitID) -- {{{ + -- (note that this function is only called for the mouseover and target if the MUFs are active) + + --D:Debug("(LiveList) Getting Debuff for ", UnitID); + if (UnitID == "target" or UnitID == "mouseover") and not UnitIsFriend(UnitID, "player") then + if D.ManagedDebuffUnitCache[UnitID] and D.ManagedDebuffUnitCache[UnitID][1] and D.ManagedDebuffUnitCache[UnitID][1].Type then + D.ManagedDebuffUnitCache[UnitID][1].Type = false; -- clear target/mouseover debuff + D.UnitDebuffed[UnitID] = false; -- XXX changed from 'target' to UnitID on 2010-06-08 + end + --D:Debug("(LiveList) GetDebuff() |cFF00DDDDcanceled|r, unit %s is hostile or gone.", UnitID); + return false; + end + + -- decrease the total debuff number if the MUFs system isn't already doing it and if it's not the mouseover or target unit + if not D.profile.ShowDebuffsFrame and D.UnitDebuffed[UnitID] and UnitID ~= "mouseover" and UnitID ~= "target" then + D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum - 1; + end + + -- Get the unit Debuffs + if not D.profile.ShowDebuffsFrame or not MicroUnitF.UnitToMUF[UnitID] or UnitID == "mouseover" or UnitID == "target" then + Debuffs, IsCharmed = D:UnitCurableDebuffs(UnitID); + --Debuffs, IsCharmed = D:UnitCurableDebuffs(UnitID, true); + else -- The MUFs are active and Unit is not mouseover and is not target + MF = MicroUnitF.UnitToMUF[UnitID]; + if MF then + Debuffs = MF.Debuffs; + --[=[ + else -- (ticket #6) + D:AddDebugText("Sanity check failed in LiveList:GetDebuff() no MUF for unit", UnitID, "MUFs are", D.profile.ShowDebuffsFrame, "MUFnum:", MicroUnitF.Number, "MUFshown:", MicroUnitF.UnitShown, "UnitNum:", D.Status.UnitNum, "UnitExists:", UnitExists(UnitID), "Auto MUF show/hide:", D.profile.AutoHideDebuffsFrame, "InCombatLockdown():", InCombatLockdown()); + D:AddDebugText("Stack:\n", debugstack(2)); + --]=] + end + end + + if (Debuffs and Debuffs[1] and Debuffs[1].Type) then -- there is a Debuff + + D.UnitDebuffed[UnitID] = true; -- register that this unit is debuffed + + -- increase the total debuff number + if not D.profile.ShowDebuffsFrame and UnitID ~= "mouseover" and UnitID ~= "target" then + + D.ForLLDebuffedUnitsNum = D.ForLLDebuffedUnitsNum + 1; + + end + else + D.UnitDebuffed[UnitID] = false; -- unregister this unit + end + + return D.UnitDebuffed[UnitID]; +end -- }}} + +function LiveList:DelayedGetDebuff(UnitID) -- {{{ + if not D:DelayedCallExixts("Dcr_GetDebuff"..UnitID) then + D.DebuffUpdateRequest = D.DebuffUpdateRequest + 1; + D:Debug("LiveList: GetDebuff scheduled for, ", UnitID); + D:ScheduleDelayedCall("Dcr_GetDebuff"..UnitID, self.GetDebuff, (D.profile.ScanTime / 2) * (1 + floor(D.DebuffUpdateRequest / 7.5)), self, UnitID); + end +end -- }}} + +local IndexOffset = 0; -- used when target and/or mouseover are found +local DebuffedUnitsNumber = 0; +local _; +function LiveList:Update_Display() -- {{{ + + if not D.DcrFullyInitialized then + return; + end + + -- Update the unit array + --[[ + if (D.Groups_datas_are_invalid) then + D:GetUnitArray(); + end + --]] + + Index = 0; + + if D.profile.ShowDebuffsFrame and D.profile.LV_OnlyInRange then -- The MUFs are here and we test for range + DebuffedUnitsNumber = MicroUnitF.UnitsDebuffedInRange; + else -- the MUFs are not here or we don't test for range + DebuffedUnitsNumber = D.ForLLDebuffedUnitsNum; + end + + -- Check the units in order of importance: + + -- First the Target + if D.Status.TargetExists and not D.Status.Unit_Array_GUIDToUnit[UnitGUID("target")] and self:GetDebuff("target") then -- TargetExists implies that the unit is a friend + Index = Index + 1; + self:DisplayItem(Index, "target"); + --D:Debug("frenetic target update"); + + DebuffedUnitsNumber = DebuffedUnitsNumber + 1; + + if not D.Status.SoundPlayed then + D:PlaySound ("target", "LV target" ); + end + end + + -- Then the MouseOver + if not D.Status.MouseOveringMUF and D.UnitDebuffed["mouseover"] and not D.Status.Unit_Array_GUIDToUnit[UnitGUID("mouseover")] and self:GetDebuff("mouseover") then -- this won't catch new debuff if all debuffs disappeard while overing the unit... + Index = Index + 1; + self:DisplayItem(Index, "mouseover"); + --D:Debug("frenetic mouseover update"); + + DebuffedUnitsNumber = DebuffedUnitsNumber + 1; + + if not D.Status.SoundPlayed then + D:PlaySound ("mouseover", "LV mouseover" ); + end + end + + -- the sound played status is reset here because the live list is able to display target and mouseover units and far away ones... + if DebuffedUnitsNumber == 0 then + D.Status.SoundPlayed = false; + end + + IndexOffset = Index; + + -- Then continue with all the remaining units if at least one of them is debuffed + -- We need this loop because: + -- 1, we have to show an ordered list (always true) + -- 2, we want to test if the unit is in spell range (only if the option is active and the MUFs hidden) + -- There is no event to do the last and a not simple table.sort() would be needed for the first... + if DebuffedUnitsNumber > 0 and Index < D.profile.Amount_Of_Afflicted then + for _, UnitID in ipairs(D.Status.Unit_Array) do + -- if the unit is debuffed and still exists and is not stealthed check this only if the MUFs engine is not there, redudent tests otherwise... + if D.UnitDebuffed[UnitID] and UnitExists(UnitID) then + + -- we don't care about range + if not D.profile.LV_OnlyInRange then + Index = Index + 1; + self:DisplayItem(Index, UnitID); + + -- play the sound if not already done + if not D.Status.SoundPlayed then + D:PlaySound (UnitID, "LV scan NR" ); + end + + else -- we care about range + + if D.profile.ShowDebuffsFrame and MicroUnitF.UnitToMUF[UnitID] then + RangeStatus = MicroUnitF.UnitToMUF[UnitID].UnitStatus; -- MicroUnitF.UnitToMUF[UnitID] is nil sometimes XXX + RangeStatus = (RangeStatus == DC.AFFLICTED or RangeStatus == DC.AFFLICTED_AND_CHARMED) and true or false; + else + if D.Status.CuringSpells[D.ManagedDebuffUnitCache[UnitID][1].Type] then + RangeStatus = IsSpellInRange(D.Status.CuringSpells[D.ManagedDebuffUnitCache[UnitID][1].Type], UnitID); + else + D:AddDebugText( + "LiveList:Update_Display(): couldn't get range, DType:", D.ManagedDebuffUnitCache[UnitID][1].Type, + "DTypeName:", D.ManagedDebuffUnitCache[UnitID][1].TypeName, + "DName:", D.ManagedDebuffUnitCache[UnitID][1].Name, + "MUFs are:", D.profile.ShowDebuffsFrame, + "InCombatLockdown():", InCombatLockdown(), + "UnitID:", UnitID + ); + RangeStatus = 0; + + end + RangeStatus = (RangeStatus and RangeStatus ~= 0) and true or false; + end + + if (RangeStatus) then + Index = Index + 1; + self:DisplayItem(Index, UnitID); + -- play the sound if not already done + if not D.Status.SoundPlayed then + D:PlaySound (UnitID, "LV R" ); + end + end + end + end + + -- don't loop if we reach the max displayed unit num or if all debuffed units have been displayed + if Index == D.profile.Amount_Of_Afflicted or Index == DebuffedUnitsNumber + IndexOffset then + break; + end + end + end + + -- reset the sound if no units were displayed + if not D.profile.ShowDebuffsFrame and Index == 0 and D.Status.SoundPlayed then + Dcr:Debug("LV: No more unit displayed, sound re-enabled"); + D.Status.SoundPlayed = false; -- re-enable the sound if no more debuff + end + + -- Hide unneeded Items + if self.NumberShown > Index then -- if there are more units shown than the actual number of debuffed units + for i = Index + 1, self.NumberShown do + if self.ExistingPerID[i] and self.ExistingPerID[i].IsShown then + --D:Debug("(LiveList) Hidding LVItem %d", i); + self.ExistingPerID[i].Frame:Hide(); + self.ExistingPerID[i].IsShown = false; + self.NumberShown = self.NumberShown - 1; + else + break; + end + end + end + + +end -- }}} + + + +function LiveList:DisplayTestItem() -- {{{ + if not self.TestItemDisplayed and D.Status.Unit_Array[1] then + self.TestItemDisplayed = GetTime(); + D:DummyDebuff(D.Status.Unit_Array[1], "Test item"); + end +end -- }}} + +function LiveList:HideTestItem() -- {{{ + self.TestItemDisplayed = false; + local i = 1; + + for UnitID, Debuffed in pairs(D.UnitDebuffed) do + if Debuffed then + D:ScheduleDelayedCall("Dcr_rmt"..i, D.DummyDebuff, i * (D.profile.ScanTime / 2), D, UnitID); + i = i + 1; + end + end + +end -- }}} + + +-- this displays the tooltips of the live-list +function LiveList:DebuffTemplate_OnEnter(frame) --{{{ + if (D.profile.AfflictionTooltips and frame.Object.UnitID) then + DcrDisplay_Tooltip:SetOwner(frame, "ANCHOR_CURSOR"); + DcrDisplay_Tooltip:ClearLines(); + DcrDisplay_Tooltip:SetUnitDebuff(frame.Object.UnitID,frame.Object.Debuff.index); -- OK + DcrDisplay_Tooltip:Show(); + else + D:Debug(D.profile.AfflictionTooltips, frame.Object.UnitID ); + end +end --}}} + +function LiveList:Onclick() -- {{{ + D:Println(L["HLP_LL_ONCLICK_TEXT"]); +end -- }}} + +T._LoadedFiles["Dcr_LiveList.lua"] = "2.5.1-6-gd3885c5"; diff --git a/Decursive/Dcr_LiveList.xml b/Decursive/Dcr_LiveList.xml new file mode 100644 index 0000000..982cc0a --- /dev/null +++ b/Decursive/Dcr_LiveList.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Decursive/Dcr_Raid.lua b/Decursive/Dcr_Raid.lua new file mode 100644 index 0000000..3cc2fa7 --- /dev/null +++ b/Decursive/Dcr_Raid.lua @@ -0,0 +1,783 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Dcr_Events.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_Events.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $"); + + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + +local RaidRosterCache = { }; +local SortingTable = { }; +D.Status.Unit_Array_GUIDToUnit = { }; +D.Status.Unit_Array_UnitToGUID = { }; + +D.Status.InternalPrioList = { }; +D.Status.InternalSkipList = { }; +D.Status.Unit_Array = { }; + +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local type = _G.type; +local select = _G.select; +local UnitIsFriend = _G.UnitIsFriend; +local UnitCanAttack = _G.UnitCanAttack; +local GetNumRaidMembers = _G.GetNumRaidMembers; +local GetNumPartyMembers = _G.GetNumPartyMembers; +local GetRaidRosterInfo = _G.GetRaidRosterInfo; +local random = _G.random; +local UnitIsUnit = _G.UnitIsUnit; +local UnitClass = _G.UnitClass; +local UnitExists = _G.UnitExists; +local UnitGUID = _G.UnitGUID; +local table = _G.table; +local t_insert = _G.table.insert; +local str_upper = _G.string.upper; +local MAX_RAID_MEMBERS = _G.MAX_RAID_MEMBERS; +local setmetatable = _G.setmetatable; +local rawget = _G.rawget; +local GetTime = _G.GetTime; +------------------------------------------------------------------------------- + +-- GROUP STATUS UPDATE, these functions update the UNIT table to scan {{{ +------------------------------------------------------------------------------- + +--[=[ +local function AddToSort (unit, GUID, index) -- // {{{ + if (D.profile.Random_Order and + (not D.Status.InternalPrioList[GUID]) and not GUID~=MyGUID) then + index = random (1, 3000); + end + SortingTable[unit] = index; + --D:Debug("Adding to sort: ", unit, index); +end --}}} +--]=] + + +-- Raid/Party Name Check Function (a terrible function, need optimising) +-- this returns the UnitID that the Name points to +-- this does not check "target" or "mouseover" +--[=[ +function D:NameToUnit( Name ) --{{{ + + local numRaidMembers = GetNumRaidMembers(); + local FoundUnit = false; + + + if (not Name) then + return false; + end + + if self.Status.Unit_Array_NameToUnit[Name] ~= nil then + return self.Status.Unit_Array_NameToUnit[Name]; + end + + if (numRaidMembers == 0) then + if (Name == (self:UnitName("player"))) then + FoundUnit = "player"; + elseif (Name == (self:UnitName("pet"))) then + FoundUnit = "pet"; + elseif GetNumPartyMembers() > 0 then + if (Name == (self:UnitName("party1"))) then + FoundUnit = "party1"; + elseif (Name == (self:UnitName("party2"))) then + FoundUnit = "party2"; + elseif (Name == (self:UnitName("party3"))) then + FoundUnit = "party3"; + elseif (Name == (self:UnitName("party4"))) then + FoundUnit = "party4"; + elseif (Name == (self:UnitName("partypet1"))) then + FoundUnit = "partypet1"; + elseif (Name == (self:UnitName("partypet2"))) then + FoundUnit = "partypet2"; + elseif (Name == (self:UnitName("partypet3"))) then + FoundUnit = "partypet3"; + elseif (Name == (self:UnitName("partypet4"))) then + FoundUnit = "partypet4"; + end + end + else + -- we are in a raid + local i; + local foundmembers = 0; + local RaidName; + for i=1, MAX_RAID_MEMBERS do + RaidName = (GetRaidRosterInfo(i)); + + if RaidName then + + foundmembers = foundmembers + 1; + + if ( Name == RaidName) then + FoundUnit = "raid"..i; + break; + end + if ( self.profile.Scan_Pets and Name == (self:UnitName("raidpet"..i))) then + FoundUnit = "raidpet"..i; + break; + end + + if foundmembers == numRaidMembers then + break; + end + + end + end + end + + self.Status.Unit_Array_NameToUnit[Name] = FoundUnit; + + return FoundUnit; + +end --}}} +--]=] +-- }}} + + + +DC.ClassNumToLName = { + [11] = LC[DC.CLASS_DRUID], + [12] = LC[DC.CLASS_HUNTER], + [13] = LC[DC.CLASS_MAGE], + [14] = LC[DC.CLASS_PALADIN], + [15] = LC[DC.CLASS_PRIEST], + [16] = LC[DC.CLASS_ROGUE], + [17] = LC[DC.CLASS_SHAMAN], + [18] = LC[DC.CLASS_WARLOCK], + [19] = LC[DC.CLASS_WARRIOR], + [20] = LC[DC.CLASS_DEATHKNIGHT], +} + +DC.ClassLNameToNum = D:tReverse(DC.ClassNumToLName); + +DC.ClassNumToUName = { + [11] = DC.CLASS_DRUID, + [12] = DC.CLASS_HUNTER, + [13] = DC.CLASS_MAGE, + [14] = DC.CLASS_PALADIN, + [15] = DC.CLASS_PRIEST, + [16] = DC.CLASS_ROGUE, + [17] = DC.CLASS_SHAMAN, + [18] = DC.CLASS_WARLOCK, + [19] = DC.CLASS_WARRIOR, + [20] = DC.CLASS_DEATHKNIGHT, +} + +DC.ClassUNameToNum = D:tReverse(DC.ClassNumToUName); + + +-- this gets an array of units for us to check + +do + + local i = 1; + local D = D; + local _ = false; -- a local dummy trash variable + + local MAX_RAID_MEMBERS = _G.MAX_RAID_MEMBERS; + + local UnitToGUID = {}; + local GUIDToUnit = {}; + + local raidnum = 0; + + local UnitToGUID_mt = { __index = function(self, unit) + local GUID = UnitGUID(unit) or false; + + self[unit] = GUID; + GUIDToUnit[GUID] = unit; + + --[=[ + if (D.db.global.debugging) then + if not GUID then + D:errln("UnitToGUID_mt: no GUID for: ", unit); -- this is not an error, it's to see when raid# ids are not contiguous... + end + end + --]=] + + + return self[unit]; + end }; + + + local GUIDToUnit_ScannedAll = false; + local lookforpets = true; + local GUIDToUnit_mt = { __index = function(self, GUID) + -- {{{ + + if GUIDToUnit_ScannedAll then + self[GUID] = false; + D:Debug("GUIDToUnit_mt: %s is not in our group!", GUID); + return self[GUID]; + end + + if (not GUID) then + D:errln("GUIDToUnit_mt: no GUID! "); + return false; + end + + local unit = false; + + + if GUID == DC.MyGUID then + unit = "player"; + elseif GUID == UnitToGUID["pet"] then + unit = "pet"; + elseif (raidnum == 0) then + if GetNumPartyMembers() > 0 then + if GUID == UnitToGUID["party1"] then + unit = "party1"; + elseif GUID == UnitToGUID["party2"] then + unit = "party2"; + elseif GUID == UnitToGUID["party3"] then + unit = "party3"; + elseif GUID == UnitToGUID["party4"] then + unit = "party4"; + elseif D.profile.Scan_Pets then + if GUID == UnitToGUID["partypet1"] then + unit = "partypet1"; + elseif GUID == UnitToGUID["partypet2"] then + unit = "partypet2"; + elseif GUID == UnitToGUID["partypet3"] then + unit = "partypet3"; + elseif GUID == UnitToGUID["partypet4"] then + unit = "partypet4"; + end + end + end + else + -- we are in a raid + local i; + local foundmembers = 0; + local RaidGUID; + for i=1, MAX_RAID_MEMBERS do + RaidGUID = UnitToGUID[ "raid"..i]; + + if RaidGUID then + + foundmembers = foundmembers + 1; + + if GUID == RaidGUID then + unit = "raid"..i; + break; + end + + if lookforpets and D.profile.Scan_Pets and GUID == UnitToGUID["raidpet"..i] then + unit = "raidpet"..i; + break; + end + + if foundmembers == raidnum then + break; + end + end + end + end + + if not unit then + GUIDToUnit_ScannedAll = true; + end + + self[GUID] = unit; + + return self[GUID]; + end }; + --}}} + + + local function IsInSkipList ( GUID, group, classNum ) -- {{{ + if (D.Status.InternalSkipList[GUID] or D.Status.InternalSkipList[group] or D.Status.InternalSkipList[classNum]) then + return true; + end + + return false; + end -- }}} + + local function IsInSkipOrPriorList( GUID, group, classNum ) --{{{ + + if (IsInSkipList ( GUID, group, classNum )) then + return true; + end + + if (D.Status.InternalPrioList[GUID] or D.Status.InternalPrioList[group] or D.Status.InternalPrioList[classNum]) then + return true; + end + + return false; + end --}}} + + + local ClassPrio = { }; + local GroupsPrio = { }; + + local currentGroup = 0; -- the group we are in + + local function GetUnitDefaultPriority (RaidId, UnitGroup) -- {{{ + + if (not UnitGroup) then + return RaidId; + end + + if (UnitGroup >= currentGroup) then + return ( 8 - ( UnitGroup - currentGroup ) ) * 100 + (41 - RaidId); + end + + if (UnitGroup < currentGroup) then + return (currentGroup - UnitGroup) * 100 + (41 - RaidId); + end + end -- }}} + + local function GetUnitPriority(Unit, RaidId, UnitGroup, UNClass, IsPet) -- {{{ + + -- A little explanation of the principle behind this function {{{ + --[=[ **************************************************************************** + levels of priority: + + 0 --> PriorityList + 1 --> Group + 2 --> Class + 3 --> Default (Decursive "natural" order: our group, groups after, groups before) + 4 --> Pets + + - 8 groups with 5 persons maximum per group + - 10 classes with 80 persons max for each class (Pets may be counted) + - 80 persons for default (including possible pets) + + Priority list: 1,000,000 till 100,000,000 + Group indexes: 10,000, 20,000, 30,000, till 80,000 + class indexes: 1,000, 2,000, 3,000, till 10,000 + default indexes: 100 to 800 (player's index will be 900) + pet indexes: Same as above but * -1 + + We make additions, exemple: + - Our current group is the group 7 + - The resulting default groups priorities are: + 7:800 8: 700, 1:600, 2: 500, 3: 400, 4: 300, 5: 200, 6:100 + - Archarodim, Mage from Group 5 (23rd unit of the raid) + - Unit Archarodim priority is 223 + - Class Mage priority is 4000 + - Group 5 priority is 20000 + + --> Archarodim priority is 200 + 23 + 4000 + 20000 = 24223 + **************************************************************************** }}} ]=] + + -- Get Decursive's natural default priority of the unit + local UnitPriority = GetUnitDefaultPriority(RaidId, UnitGroup); + + -- Get the class priority if available + if ( UNClass and ClassPrio[ DC.ClassUNameToNum [UNClass] ] ) then + UnitPriority = UnitPriority + ( 10 + 1 - ClassPrio[DC.ClassUNameToNum [UNClass]]) * 1000; -- XXX 10 (Deathknight) is no good + end + + -- Get the group priority if available + if (UnitGroup and GroupsPrio[UnitGroup]) then + UnitPriority = UnitPriority + (8 + 1 - GroupsPrio[UnitGroup]) * 10000; + end + + -- Get the priority list index if available + if not IsPet then + local Unit_GUID = UnitToGUID[Unit]; + + local PrioListIndex = 100; + + -- get the higher of the three... + if (D.Status.InternalPrioList[Unit_GUID] and D.Status.InternalPrioList[Unit_GUID] < PrioListIndex) then + PrioListIndex = D.Status.InternalPrioList[Unit_GUID]; + end + + if (D.Status.InternalPrioList[UnitGroup] and D.Status.InternalPrioList[UnitGroup] < PrioListIndex) then + PrioListIndex = D.Status.InternalPrioList[UnitGroup]; + end + + if (D.Status.InternalPrioList[ DC.ClassUNameToNum [UNClass] ] and D.Status.InternalPrioList[ DC.ClassUNameToNum [UNClass] ] < PrioListIndex) then + PrioListIndex = D.Status.InternalPrioList[ DC.ClassUNameToNum [UNClass] ]; + end + + + if ( PrioListIndex < 100) then + UnitPriority = UnitPriority + (100 + 1 - PrioListIndex) * 1000000; + end + end + + if IsPet then + UnitPriority = UnitPriority * -1; + end + + return UnitPriority; + end -- }}} + + + local RaidRosterCache = {}; + + local pet; + function D:GetUnitArray() --{{{ + -- if the groups composition did not changed + if not self.Groups_datas_are_invalid or not self.DcrFullyInitialized then + return; + end + self.Groups_datas_are_invalid = false; + + self:Debug ("|cFFFF44FF-->|r Updating Units Array"); + + local pGUID; + raidnum = GetNumRaidMembers(); + + if DC.MyGUID == "NONE" then + + self:Debug("|cFFFF0000DC.MyGUID was nil!!|r"); + + DC.MyGUID = (UnitGUID("player")); + + if not DC.MyGUID then + DC.MyGUID = "NONE"; + self:Debug("|cFFFF0000DC.MyGUID is STILL nil!!|r"); + end + + end + + local MyGUID = DC.MyGUID; + + -- clear all the arrays + local Status = self.Status; + Status.InternalPrioList = {}; -- these lists contains only units currently present + Status.InternalSkipList = {}; + SortingTable = {}; + Status.Unit_Array_GUIDToUnit = {}; + Status.Unit_Array_UnitToGUID = {}; + + UnitToGUID = setmetatable(UnitToGUID, UnitToGUID_mt); -- we could simply erase this one to prevent garbage + GUIDToUnit = setmetatable(GUIDToUnit, GUIDToUnit_mt); -- this one cannot be erased (memory leak due to GUID...) + GUIDToUnit_ScannedAll = false; + + if Status.TestLayout then + D:GetFakeUnit_array (); + return; + end + + + local unit; + + + -- ############### PARSE PRIO AND SKIP LIST ############### + + GroupsPrio, ClassPrio = D:MakeGroupsAndClassPrio(); + + lookforpets = false; + + -- First clean and load the prioritylist (remove missing units) + for i, ListEntry in ipairs(self.profile.PriorityList) do + + -- first add GUIDs present in our raid group + if (type(ListEntry) == "string") then + unit = GUIDToUnit[ListEntry]; + if (unit) then + Status.InternalPrioList[ListEntry] = i; + end + + else -- if ListEntry is not a string, then it's a number + -- representing the groups or the classes + + Status.InternalPrioList[ListEntry] = i; + end + end + + -- Get a cleaned skip list + for i, ListEntry in ipairs(self.profile.SkipList) do + if (type(ListEntry) == "string") then + unit = GUIDToUnit[ListEntry]; + if (unit) then + Status.InternalSkipList[ListEntry] = i; + end + else + Status.InternalSkipList[ListEntry] = i; + end + end + lookforpets = true; + + + -- if we are not in a raid but in a party + if (raidnum == 0) then + currentGroup = 1; -- this is used to compute the default priorities + -- Add the player to the main list if needed + if not IsInSkipOrPriorList(MyGUID, false, DC.ClassUNameToNum[DC.MyClass]) then + -- the player is not in a priority state, add to default prio + SortingTable["player"] = 900; + Status.Unit_Array_GUIDToUnit[MyGUID] = "player"; + + + elseif not IsInSkipList(MyGUID, false, DC.ClassUNameToNum[DC.MyClass]) then + -- The player is contained within a priority rule + SortingTable["player"] = GetUnitPriority ("player", 1, 1, DC.MyClass ); + Status.Unit_Array_GUIDToUnit[MyGUID] = "player"; + + end + + local unit = ""; + + -- add the party members and their pets... if they exist + for i = 1, 4 do + unit = "party"..i; + + if (UnitExists(unit)) then + + pGUID = UnitToGUID[unit]; + + if (not pGUID) then -- at logon sometimes pGUID is nil... + pGUID = unit; + end + + -- check the GUID to see if we skip + if (not IsInSkipList(pGUID, nil, DC.ClassUNameToNum[(select(2, UnitClass(unit)))])) then + + Status.Unit_Array_GUIDToUnit[pGUID] = unit; + + SortingTable[unit] = GetUnitPriority (unit, i + 1, 1, (select(2, UnitClass(unit)) ) ); + + end + + if ( self.profile.Scan_Pets ) then + + pet = "partypet"..i; + + if (UnitExists(pet)) then + pGUID = UnitToGUID[pet]; + + if (not pGUID) then -- at logon sometimes pGUID is nil... + pGUID = pet; + end + + SortingTable[pet] = GetUnitPriority (pet, i + 1, 1, (select(2, UnitClass(pet))), true); + Status.Unit_Array_GUIDToUnit[pGUID] = pet; + end + end + end + end + end + + -- add our own pet + if ( self.profile.Scan_Pets ) then + if (UnitExists("pet")) then + SortingTable["pet"] = -900; + Status.Unit_Array_GUIDToUnit[UnitToGUID["pet"]] = "pet"; + end + end + + if ( raidnum > 0 ) then -- if we are in a raid + currentGroup = 0; + local rName, rGroup, rClass, GUID; + local CaheID = 1; -- make an ordered table + local excluded = 0; + local playerPrio = 900; + + -- Cache the raid roster info eliminating useless info and already listed members + for i = 1, MAX_RAID_MEMBERS do + rName, _, rGroup, _, _, rClass = GetRaidRosterInfo(i); + GUID = UnitToGUID["raid"..i]; + + -- add all except member to skip + if not IsInSkipList(GUID, rGroup, DC.ClassUNameToNum[rClass]) then + + if (rName) then -- (at log-in GetRaidRosterInfo() returns garbage) + + if (not RaidRosterCache[CaheID]) then + RaidRosterCache[CaheID] = {}; + end + + RaidRosterCache[CaheID].rName = rName; + RaidRosterCache[CaheID].rGroup = rGroup; + RaidRosterCache[CaheID].rClass = rClass; + RaidRosterCache[CaheID].rIndex = i; + RaidRosterCache[CaheID].rGUID = GUID; + CaheID = CaheID + 1; + end + else + excluded = excluded + 1; + end + + -- find our group (a whole iteration is required, raid info are not ordered) -- wrong, the player is always the last now but never trust Blizzard... + if currentGroup==0 and GUID == MyGUID then -- anyway they do the same thing in PlayerFrame.lua... + currentGroup = rGroup; + playerPrio = GetUnitPriority ("player", i, rGroup, rClass, false); + end + + if CaheID + excluded > raidnum then -- we found all the units + RaidRosterCache[CaheID] = false; + break; + end + end + + -- Add the player to the main list if needed + if (not IsInSkipOrPriorList(MyGUID, currentGroup, DC.ClassUNameToNum[DC.MyClass])) then + SortingTable["player"] = 900; + Status.Unit_Array_GUIDToUnit[MyGUID] = "player"; + else -- well let's see if people complains that they cannot exclude themself... + SortingTable["player"] = playerPrio; + Status.Unit_Array_GUIDToUnit[MyGUID] = "player"; + end + + -- Now we have a cache without the units we want to skip + local TempID; + for _, raidMember in ipairs(RaidRosterCache) do + + if not raidMember then break; end; + + -- put each raid member with the right priority in our sorting table + if not Status.Unit_Array_GUIDToUnit[raidMember.rGUID] then + + TempID = "raid"..raidMember.rIndex; + + SortingTable[TempID] = GetUnitPriority (TempID, raidMember.rIndex, raidMember.rGroup, raidMember.rClass, false); + Status.Unit_Array_GUIDToUnit[raidMember.rGUID] = TempID; + end + + if ( self.profile.Scan_Pets ) then + local pet = ""; + pet = "raidpet"..raidMember.rIndex; + + if ( UnitExists(pet) ) then + + pGUID = UnitToGUID[pet]; + + if (not pGUID) then -- at logon sometimes pGUID is nil... + pGUID = pet; + end + + -- add it only if not already in (could be the player pet...) + if (not Status.Unit_Array_GUIDToUnit[pGUID]) then + SortingTable[pet] = GetUnitPriority (pet, raidMember.rIndex, raidMember.rGroup, (select(2,UnitClass(pet))), true); + Status.Unit_Array_GUIDToUnit[pGUID] = pet; + end + end + + end + + end + + end -- END if we are in a raid + + -- NEW focus management + -- there is a focus and its not hostile in the first place + if UnitExists("focus") and (not UnitCanAttack("focus", "player") or UnitIsFriend("focus", "player")) then + pGUID = UnitToGUID["focus"] + -- the unit is not registered somewhere else yet + if not Status.Unit_Array_GUIDToUnit[pGUID] then + SortingTable["focus"] = -1; -- add it at the end... + Status.Unit_Array_GUIDToUnit[pGUID] = "focus"; + end + end + + -- we use a hash-key style table for Status.Unit_Array_GUIDToUnit because it allows us + -- to not care if we add a same unit several times (speed optimization) + -- but we cannot use sort unless indexes are integer so: + Status.Unit_Array = {} + local GUID; + for GUID, unit in pairs(Status.Unit_Array_GUIDToUnit) do -- /!\ PAIRS not iPAIRS + t_insert(Status.Unit_Array, unit); + + Status.Unit_Array_UnitToGUID[unit] = GUID; -- just a useful table, not used here :) + end + + table.sort(Status.Unit_Array, function (a,b) + if (not (SortingTable[a] < 0 and SortingTable[b] < 0)) then -- one of the values is > 0 + return SortingTable[b] < SortingTable[a]; + else -- both are < 0 + return SortingTable[a] < SortingTable[b]; + end + end); + + + Status.UnitNum = #Status.Unit_Array; + + UnitToGUID = {}; + GUIDToUnit = {}; + D.Status.GroupUpdatedOn = D:NiceTime(); -- It's used in UNIT_AURA event handler to trigger a rescan if the array is found inacurate + + self:Debug ("|cFFFF44FF-->|r Update complete!", Status.UnitNum); + return; + end + + function D:GetFakeUnit_array () + + if not D.Status.TestLayout then + return; + end + + self:Debug ("|cFFFF22FF-->|r Creating fake Units Array"); + + local Status = self.Status; + Status.Unit_Array_GUIDToUnit[DC.MyGUID] = "player"; + + Status.Unit_Array = {} + + for i = 1, D.Status.TestLayoutUNum - 1 do -- the player is always in so we remove one here. + Status.Unit_Array_GUIDToUnit["raid" .. i .. "GUID"] = "raid" .. i; + end + + local GUID; + for GUID, unit in pairs(Status.Unit_Array_GUIDToUnit) do -- /!\ PAIRS not iPAIRS + t_insert(Status.Unit_Array, unit); + + Status.Unit_Array_UnitToGUID[unit] = GUID; -- just a useful table, not used here :) + end + + table.sort(Status.Unit_Array); + + Status.UnitNum = #Status.Unit_Array; + D.Status.GroupUpdatedOn = D:NiceTime(); -- It's used in UNIT_AURA event handler to trigger a rescan if the array is found inacurate + + end + +end + +--}}} + +------------------------------------------------------------------------------- +T._LoadedFiles["Dcr_Raid.lua"] = "2.5.1-6-gd3885c5"; + +-- "Your God is dead and no one cares" +-- "If there is a Hell I'll see you there" diff --git a/Decursive/Dcr_lists.lua b/Decursive/Dcr_lists.lua new file mode 100644 index 0000000..fde4fe5 --- /dev/null +++ b/Decursive/Dcr_lists.lua @@ -0,0 +1,601 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Decursive.xml"] or not T._LoadedFiles["Decursive.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Decursive.xml or Decursive.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-08-12 04:50:10 +0200 (mar., 12 août 2008) $", "$Revision: 80230 $"); + + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; +local _; +local _G = _G; + +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local UnitGUID = _G.UnitGUID; +local table = _G.table; +local str_format = _G.string.format; +local str_sub = _G.string.gsub; +local t_insert = _G.table.insert; + +-- Dcr_ListFrameTemplate specific internal functions {{{ +function D.ListFrameTemplate_OnLoad(frame) + frame.ScrollFrame = _G[frame:GetName().."ScrollFrame"]; + frame.ScrollBar = _G[frame.ScrollFrame:GetName().."ScrollBar"]; + frame.ScrollFrame.offset = 0; +end + +function D:ListFrameScrollFrameTemplate_OnMouseWheel(frame, value) + local scrollBar = _G[frame:GetName().."ScrollBar"]; + local min, max = scrollBar:GetMinMaxValues(); + if ( value > 0 ) then + if (IsShiftKeyDown() ) then + scrollBar:SetValue(min); + else + scrollBar:SetValue(scrollBar:GetValue() - scrollBar:GetValueStep()); + end + else + if (IsShiftKeyDown() ) then + scrollBar:SetValue(max); + else + scrollBar:SetValue(scrollBar:GetValue() + scrollBar:GetValueStep()); + end + end +end + +-- }}} + +-- Dcr_ListFrameTemplate specific handlers {{{ + +function D.PrioSkipListFrame_OnUpdate(frame) --{{{ + + + if not D.DcrFullyInitialized then + return; + end + + if (frame.UpdateYourself) then + frame.UpdateYourself = false; + local baseName = frame:GetName(); + local size; + + if (frame.Priority) then + size = table.getn(D.profile.PriorityList); + else + size = table.getn(D.profile.SkipList); + end + -- D:Debug("PrioSkipListFrame_OnUpdate executed", size, this.ScrollFrame.offset); + + local i; + for i = 1, 10 do + local id = ""..i; + if (i < 10) then + id = "0"..i; + end + local btn = _G[baseName.."Index"..id]; + + btn:SetID( i + frame.ScrollFrame.offset); + D:PrioSkipListEntry_Update(btn); + + if (i <= size) then + btn:Show(); + else + btn:Hide(); + end + end + frame.ScrollUpdateFunc(_G[baseName.."ScrollFrame"], true); + end + +end --}}} + +function D:PrioSkipListEntryTemplate_OnClick(frame) --{{{ +-- D:PrintLiteral(arg1); + + local list; + local UnitNum; + if (frame:GetParent().Priority) then + list = D.profile.PriorityList; + UnitNum = getn(D.profile.PriorityList); + else + list = D.profile.SkipList; + UnitNum = getn(D.profile.SkipList); + end + + + local id = frame:GetID(); + if (id) then + if (IsControlKeyDown()) then + if (frame:GetParent().Priority) then + D:RemoveIDFromPriorityList(id); + else + D:RemoveIDFromSkipList(id); + end + elseif (UnitNum > 1) then + local previousUnit_ID, previousUnit, nextUnit_ID, nextUnit, currentUnit; + + --if (id == 0) then + -- previousUnit_ID = UnitNum; else + previousUnit_ID = id - 1; + --end + --if (id == UnitNum - 1) then + --nextUnit_ID = 0; else + nextUnit_ID = id + 1; + --end + + previousUnit = list[previousUnit_ID]; + nextUnit = list[nextUnit_ID ]; + currentUnit = list[id]; + + + if (arg1=="RightButton" and IsShiftKeyDown()) then -- move at the bottom + table.remove(list, id); + table.insert(list, UnitNum, currentUnit); + + elseif (arg1=="LeftButton" and IsShiftKeyDown()) then -- move at the top + table.remove(list, id); + table.insert(list, 1, currentUnit); + elseif (arg1=="LeftButton" and id ~= 1) then -- unit gets higher + D:Debug("upping %s of id %d", list[id], id); + list[previousUnit_ID] = list[id]; + list[id] = previousUnit; + elseif (arg1=="RightButton" and id ~= UnitNum) then -- unit gets lower + D:Debug("downing %s of id %d", list[id], id); + list[nextUnit_ID] = list[id]; + list[id] = nextUnit; + elseif (arg1=="MiddleButton") then + + end + frame:GetParent().UpdateYourself = true; + end + D.Status.PrioChanged = true; + D:GroupChanged ("PrioSkipListEntryTemplate_OnClick"); + else + D:Debug("No ID"); + end + +end --}}} + +function D:PrioSkipListEntry_Update(Entry) --{{{ + local id = Entry:GetID(); + if (id) then + --D:Debug("PrioSkipListEntry_Update executed"); + local name, classname, GUIDorNum; + if (Entry:GetParent().Priority) then + GUIDorNum = D.profile.PriorityList[id]; + classname = D.profile.PriorityListClass[GUIDorNum]; + name = D.profile.PrioGUIDtoNAME[GUIDorNum]; + else + GUIDorNum = D.profile.SkipList[id]; + classname = D.profile.SkipListClass[GUIDorNum]; + name = D.profile.SkipGUIDtoNAME[GUIDorNum]; + end + if not classname then + classname = "WARRIOR"; + end + if (GUIDorNum) then + if (type(GUIDorNum) == "number") then + if (GUIDorNum < 10) then + name = str_format("[ %s %s ]", L["STR_GROUP"], GUIDorNum); + else + name = str_format("[ %s ]", DC.ClassNumToLName[GUIDorNum]); + end + end + Entry:SetText(id.." - "..D:ColorText(name, "FF"..DC.HexClassColor[classname])); + else + Entry:SetText("Error - NO name!"); + end + else + Entry:SetText("Error - No ID!"); + end +end --}}} + +function D.PrioSkipList_ScrollFrame_Update (ScrollFrame) -- {{{ + + if not D.DcrFullyInitialized then + return; + end + + local maxentry; + local UpdateListOnceDone = true; + local DirectCall = false; + + D:Debug("ScrollFrame is a %s", type(ScrollFrame)); + if (not ScrollFrame) then + --ScrollFrame = this; -- Called from the scrollbar frame handler + else + --UpdateListOnceDone = false; -- The function was called from the list update function + DirectCall = true; + end + + if (not ScrollFrame.UpdateYourself) then + ScrollFrame.UpdateYourself = true; + return; + end + + if (ScrollFrame:GetParent().Priority) then + maxentry = table.getn(D.profile.PriorityList); + else + maxentry = table.getn(D.profile.SkipList); + end + + FauxScrollFrame_Update(ScrollFrame,maxentry,10,16); + + + if (UpdateListOnceDone) then + ScrollFrame.UpdateYourself = false; -- prevent this function to re-execute unecessarily + ScrollFrame:GetParent().UpdateYourself = true; + end + D:Debug("PrioSkipList_ScrollFrame_Update executed for %s", ScrollFrame:GetName()); +end -- }}} + + +-- }}} + +-- list specific management functions {{{ +------------------------------------------------------------------------------- +D.Status.GroupsPrio = { }; +D.Status.ClassPrio = { }; +function D:MakeGroupsAndClassPrio () + + if D.Status.PrioChanged then + local GroupsPrio = {}; + local ClassPrio = {}; + + for i, ListEntry in ipairs(self.profile.PriorityList) do + if (type(ListEntry) ~= "string") then + if (ListEntry < 10) then + t_insert(GroupsPrio, ListEntry); + else + t_insert(ClassPrio, ListEntry); + end + end + end + + -- Reverse GroupsPrio and ClassPrio so we can have something useful... + D.Status.GroupsPrio = self:tReverse(GroupsPrio); + D.Status.ClassPrio = self:tReverse(ClassPrio); + D.Status.PrioChanged = false; + end + + return D.Status.GroupsPrio, D.Status.ClassPrio; +end + + +function D:AddTargetToPriorityList() --{{{ + D:Debug( "Adding the target to the priority list"); + return D:AddUnitToPriorityList("target", true); +end --}}} + +function D:AddUnitToPriorityList( unit, check ) --{{{ + + if not D.DcrFullyInitialized then + return false; + end + + if (#D.profile.PriorityList > 99) then + return false; + end + + if (not check or UnitExists(unit)) then + if (type(unit) == "number" or UnitIsPlayer(unit)) then + D:Debug("adding %s", unit); + + --local name; + local GUIDorNum; + + if type(unit) == "number" then + GUIDorNum = unit; + else + --name = (D:UnitName(unit)); + GUIDorNum = UnitGUID(unit); + --if name == DC.UNKNOWN then + if not GUIDorNum then + return false; + end + end + + if D.profile.PrioGUIDtoNAME[GUIDorNum] then + return false; + end + --[[ + for _, pname in pairs(D.profile.PriorityList) do + if (name == pname) then + return false; + end + end + --]] + + table.insert(D.profile.PriorityList,GUIDorNum); + + + + if (type(unit) == "string") then + _, D.profile.PriorityListClass[GUIDorNum] = UnitClass(unit); + D.profile.PrioGUIDtoNAME[GUIDorNum] = (D:UnitName(unit)); + elseif unit > 10 then + D.profile.PriorityListClass[unit] = DC.ClassNumToUName[unit]; + D.profile.PrioGUIDtoNAME[GUIDorNum] = str_format("[ %s ]", DC.ClassNumToLName[GUIDorNum]); + else + D.profile.PrioGUIDtoNAME[GUIDorNum] = str_format("[ %s %s ]", L["STR_GROUP"], GUIDorNum); + end + + DecursivePriorityListFrame.UpdateYourself = true; + D:Debug("Unit %s added to the prio list", GUIDorNum); + D.Status.PrioChanged = true; + D:GroupChanged ("AddUnitToPriorityList"); + return true; + else + D:Debug("Unit is not a player:", unit, check, UnitExists(unit)); + if (not unit) then + error("D:AddUnitToPriorityList: bad argument #1 'unit' must be!",2); + end + end + else + D:Debug("Unit does not exist"); + end + return false; +end --}}} + +function D:RemoveIDFromPriorityList(id) --{{{ + + D.profile.PriorityListClass[ D.profile.PriorityList[id] ] = nil; -- remove it from the table + D.profile.PrioGUIDtoNAME[ D.profile.PriorityList[id]] = nil; + + table.remove( D.profile.PriorityList, id ); + + D.Status.PrioChanged = true; + D:GroupChanged ("RemoveIDFromPriorityList"); + DecursivePriorityListFrame.UpdateYourself = true; +end --}}} + +function D:ClearPriorityList() --{{{ + D.profile.PriorityList = {}; + D.profile.PriorityListClass = {}; + D.profile.PrioGUIDtoNAME = {}; + + D.Status.PrioChanged = true; + D:GroupChanged ("ClearPriorityList"); + DecursivePriorityListFrame.UpdateYourself = true; +end --}}} + +function D:AddTargetToSkipList() --{{{ + D:Debug( "Adding the target to the Skip list"); + return D:AddUnitToSkipList("target"); +end --}}} + +function D:AddUnitToSkipList( unit) --{{{ + + if (#D.profile.SkipList > 99) then + return false; + end + + if (not check or UnitExists(unit)) then + if (type(unit) == "number" or UnitIsPlayer(unit)) then + D:Debug("adding %s", unit); + + --local name; + local GUIDorNum; + + + if type(unit) == "number" then + GUIDorNum = unit; + else + --name = (D:UnitName( unit)); + GUIDorNum = UnitGUID(unit); + --if name == DC.UNKNOWN then + if not GUIDorNum then + return false; + end + end + + if D.profile.SkipGUIDtoNAME[GUIDorNum] then + return false; + end + --[[ + for _, pname in pairs(D.profile.SkipList) do + if (name == pname) then + return false; + end + end + --]] + + table.insert(D.profile.SkipList,GUIDorNum); + + if (type(unit) == "string") then + _, D.profile.SkipListClass[GUIDorNum] = UnitClass(unit); + D.profile.SkipGUIDtoNAME[GUIDorNum] = (D:UnitName(unit)); + elseif unit > 10 then + D.profile.SkipListClass[unit] = DC.ClassNumToUName[unit]; + D.profile.SkipGUIDtoNAME[GUIDorNum] = str_format("[ %s ]", DC.ClassNumToLName[GUIDorNum]); + else + D.profile.SkipGUIDtoNAME[GUIDorNum] = str_format("[ %s %s ]", L["STR_GROUP"], GUIDorNum); + end + + DecursiveSkipListFrame.UpdateYourself = true; + D:Debug("Unit %s added to the skip list", GUIDorNum); + D.Status.PrioChanged = true; + D:GroupChanged ("AddUnitToSkipList"); + return true; + else + D:Debug("Unit is not a player:", unit); + end + else + D:Debug("Unit does not exist"); + end + return false; +end --}}} + +function D:RemoveIDFromSkipList(id) --{{{ + + D.profile.SkipListClass[ D.profile.SkipList[id] ] = nil; -- remove it from the table + D.profile.SkipGUIDtoNAME[ D.profile.SkipList[id]] = nil; + + + table.remove( D.profile.SkipList, id ); + + D.Status.PrioChanged = true; + D:GroupChanged ("RemoveIDFromSkipList"); + DecursiveSkipListFrame.UpdateYourself = true; +end --}}} + +function D:ClearSkipList() --{{{ + local i; + + D.profile.SkipList = {}; + D.profile.SkipListClass = {}; + D.profile.SkipGUIDtoNAME = {}; + + D.Status.PrioChanged = true; + D.Groups_datas_are_invalid = true; + D:GroupChanged ("ClearSkipList"); + DecursiveSkipListFrame.UpdateYourself = true; +end --}}} + + +function D:IsInPriorList (GUID) --{{{ + return self.Status.InternalPrioList[GUID] or false; + +--[=[ + for _, PriorName in pairs(D.profile.PrioGUIDtoNAME) do + if (PriorName == name) then + return true; + end + end + return false; + --]=] +end --}}} + +function D:IsInSkipList (name) --{{{ + return self.Status.InternalSkipList[GUID] or false; +--[=[ + for _, SkipName in pairs(D.profile.SkipGUIDtoNAME) do + if (SkipName == name) then + return true; + end + end + return false + --]=] +end --}}} + + +-- }}} + + + +function D:PopulateButtonPress(frame) --{{{ + local PopulateFrame = frame:GetParent(); + local UppedClass = ""; + + if (IsShiftKeyDown() and frame.ClassType) then + + -- UnitClass returns uppercased class... + UppedClass = string.upper(frame.ClassType); + + D:Debug("Populate called for %s", frame.ClassType); + -- for the class type stuff... we do party + + local _, pclass = UnitClass("player"); + if (pclass == UppedClass) then + PopulateFrame:addFunction("player"); + end + + _, pclass = UnitClass("party1"); + if (pclass == UppedClass) then + PopulateFrame:addFunction("party1"); + end + _, pclass = UnitClass("party2"); + if (pclass == UppedClass) then + PopulateFrame:addFunction("party2"); + end + _, pclass = UnitClass("party3"); + if (pclass == UppedClass) then + PopulateFrame:addFunction("party3"); + end + _, pclass = UnitClass("party4"); + if (pclass == UppedClass) then + PopulateFrame:addFunction("party4"); + end + end + + local i, pgroup, pclass; + + + if (IsShiftKeyDown() and frame.ClassType) then + D:Debug("Finding raid units with a macthing class"); + for index, unit in ipairs(D.Status.Unit_Array) do + _, pclass = UnitClass(unit); + + if (pclass == UppedClass) then + D:Debug("found %s", pclass); + PopulateFrame:addFunction(unit); + end + + end + elseif (frame.ClassType) then + PopulateFrame:addFunction(DC.ClassUNameToNum[string.upper(frame.ClassType)]); + end + + + local max = GetNumRaidMembers(); + + if (IsShiftKeyDown() and frame.GroupNumber and max > 0) then + D:Debug("Finding raid units with a macthing group number"); + for i = 1, max do + _, _, pgroup, _, _, pclass = GetRaidRosterInfo(i); + + if (pgroup == frame.GroupNumber) then + D:Debug("found %s in group %d", pclass, max); + PopulateFrame:addFunction("raid"..i); + end + end + elseif (not IsShiftKeyDown() and frame.GroupNumber) then + PopulateFrame:addFunction(frame.GroupNumber); + end + +end --}}} + +T._LoadedFiles["Dcr_lists.lua"] = "2.5.1-6-gd3885c5"; diff --git a/Decursive/Dcr_lists.xml b/Decursive/Dcr_lists.xml new file mode 100644 index 0000000..47198fa --- /dev/null +++ b/Decursive/Dcr_lists.xml @@ -0,0 +1,913 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dcr:DisplayTooltip(self.ListTitle, self); + + + DcrDisplay_Tooltip:Hide(); + + + if DecursiveInstallCorrupted then return end + Dcr.ListFrameTemplate_OnLoad(self); + + + self.UpdateYourself = true; + + + + self.OnUpdate(self); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FauxScrollFrame_OnVerticalScroll(self, offset, 16, self:GetParent().ScrollUpdateFunc); + + + self.UpdateYourself = true; + self:GetParent().ScrollUpdateFunc(self) + + + Dcr:ListFrameScrollFrameTemplate_OnMouseWheel(self, delta); + + + + + + + + + + + + + + + + + + + + + + + + + + + if DecursiveInstallCorrupted then return end + Dcr.ListFrameTemplate_OnLoad(self); + + self.ScrollUpdateFunc = Dcr.PrioSkipList_ScrollFrame_Update; + + self.Priority = true; + self.ListTitle = Dcr.L["PRIORITY_LIST"]; + _G[self:GetName().."Text"]:SetText(Dcr.L["STR_DCR_PRIO"]); + + self.OnUpdate = Dcr.PrioSkipListFrame_OnUpdate; + + self.ClearButtonText = Dcr.L["CLEAR_PRIO"]; + self.ClearButtonTip = Dcr.L["BINDING_NAME_DCRPRCLEAR"]; + self.ClearFunction = Dcr.ClearPriorityList; + + self.CloseButtonTip = Dcr.L["BINDING_NAME_DCRPRSHOW"]; + + self.PopulateAddFunction = Dcr.AddUnitToPriorityList; + self.PopulateFrameTitle = Dcr.L["PRIORITY_LIST"]; + + self.AddTargetFunc = Dcr.AddTargetToPriorityList; + self.EntryOnclick = Dcr.PrioSkipListEntryTemplate_OnClick; + + + + + + + + + + + + + if DecursiveInstallCorrupted then return end + Dcr.ListFrameTemplate_OnLoad(self); + + self.ScrollUpdateFunc = Dcr.PrioSkipList_ScrollFrame_Update; + + self.Priority = false; + self.ListTitle = Dcr.L["SKIP_LIST_STR"]; + + _G[self:GetName().."Text"]:SetText(Dcr.L["STR_DCR_SKIP"]); + + self.OnUpdate = Dcr.PrioSkipListFrame_OnUpdate; + + self.ClearButtonText = Dcr.L["CLEAR_SKIP"]; + self.ClearButtonTip = Dcr.L["BINDING_NAME_DCRSKCLEAR"]; + self.ClearFunction = Dcr.ClearSkipList; + + self.CloseButtonTip = Dcr.L["BINDING_NAME_DCRSKSHOW"]; + + self.PopulateAddFunction = Dcr.AddUnitToSkipList; + self.PopulateFrameTitle = Dcr.L["SKIP_LIST_STR"]; + + self.AddTargetFunc = Dcr.AddTargetToSkipList; + self.EntryOnclick = Dcr.PrioSkipListEntryTemplate_OnClick; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _G[self:GetName().."Title"]:SetText(Dcr.L["STR_QUICK_POP"]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Decursive/Dcr_opt.lua b/Decursive/Dcr_opt.lua new file mode 100644 index 0000000..6e57e81 --- /dev/null +++ b/Decursive/Dcr_opt.lua @@ -0,0 +1,2709 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Dcr_utils.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_utils.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; +local icon = LibStub("LibDBIcon-1.0", true) + +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local type = _G.type; +local table = _G.table; +local str_format = _G.string.format; +local str_gsub = _G.string.gsub; +local str_sub = _G.string.sub; +local abs = _G.math.abs; +local GetNumRaidMembers = _G.GetNumRaidMembers; +local GetNumPartyMembers = _G.GetNumPartyMembers; +local InCombatLockdown = _G.InCombatLockdown; +local _; +-- Default values for the option + +D:GetSpellsTranslations(false); -- Register spell translations + +function D:GetDefaultsSettings() + return { + -- default settings {{{ + class = { + -- Curring order (1 is the most important, 6 the lesser...) + CureOrder = { + [DC.ENEMYMAGIC] = 1, + [DC.MAGIC] = 2, + [DC.CURSE] = 3, + [DC.POISON] = 4, + [DC.DISEASE] = 5, + [DC.CHARMED] = 6, + }, + }, + + global = { + debugging = false, + NonRealease = false, + LastExpirationAlert = 0, + + -- the key to bind the macro to + MacroBind = false, + NoStartMessages = false, + + AvailableButtons = { + "%s1", -- left mouse button + "%s2", -- right mouse button + "ctrl-%s1", + "ctrl-%s2", + "shift-%s1", + "shift-%s2", + "shift-%s3", + "alt-%s1", + "alt-%s2", + "alt-%s3", + "%s3", -- the last two entries are always target and focus + "ctrl-%s3", + }, + }, + + profile = { + -- this is the priority list of people to cure + PriorityList = { }, + PriorityListClass = { }, + PrioGUIDtoNAME = { }, + + -- this is the people to skip + SkipList = { }, + SkipListClass = { }, + SkipGUIDtoNAME = { }, + + -- The micro units debuffs frame + ShowDebuffsFrame = true, + + -- Setting to hide the MUF handle (render it mouse-non-interactive) + HideMUFsHandle = false, + + AutoHideDebuffsFrame = 0, + + -- The maximum number of MUFs to be displayed + DebuffsFrameMaxCount = 80, + + DebuffsFrameElemScale = 1, + + DebuffsFrameElemAlpha = .35, + + DebuffsFrameElemBorderShow = true, + + DebuffsFrameElemBorderAlpha = .2, + + DebuffsFrameElemTieTransparency = true, + + DebuffsFramePerline = 10, + + DebuffsFrameTieSpacing = true, + + DebuffsFrameXSpacing = 3, + + DebuffsFrameYSpacing = 3, + + DebuffsFrameStickToRight = false, + + -- The time between each MUF update + DebuffsFrameRefreshRate = 0.10, + + -- The number of MUFs updated every DebuffsFrameRefreshRate + DebuffsFramePerUPdate = 10, + + DebuffsFrameShowHelp = true, + + -- position x save + DebuffsFrame_x = false, + + -- position y save + DebuffsFrame_y = false, + + -- reverse MUFs disaplay + DebuffsFrameGrowToTop = false, + + -- display chronometer on MUFs + DebuffsFrameChrono = true, + + DebuffsFrameTimeLeft = false, + + -- this is wether or not to show the live-list + Hide_LiveList = false, + + LiveListAlpha = 0.7, + + LiveListScale = 1.0, + + -- position of the "Decursive" main bar, the live-list is anchored to this bar. + MainBarX = false, + + MainBarY = false, + + -- This will turn on and off the sending of messages to the default chat frame + Print_ChatFrame = true, + + -- this will send the messages to a custom frame that is moveable + Print_CustomFrame = true, + + -- this will disable error messages + Print_Error = true, + + -- check for abolish before curing poison or disease + Check_For_Abolish = false, + + -- "Do not use 'Abolish' spells + DisableAbolish = false, + + -- Will randomize the order of the live-list and of the MUFs + --Random_Order = false, + + -- should we scan pets + Scan_Pets = true, + + -- should we ignore stealthed units? A useless option since a very long time. + Ingore_Stealthed = false, + + Show_Stealthed_Status = true, + + -- how many to show in the livelist + Amount_Of_Afflicted = 3, + + -- The live-list will only display units in range of your curring spell + LV_OnlyInRange = true, + + -- how many seconds to "black list" someone with a failed spell + CureBlacklist = 5.0, + + -- how often to poll for afflictions in seconds (for the live-list only) + ScanTime = 0.3, + + -- Are prio list members protected from blacklisting? + DoNot_Blacklist_Prio_List = false, + + -- Play a sound when there is something to decurse + PlaySound = true, + + -- The sound file to use + SoundFile = DC.AfflictionSound, + + -- Example to change the sound : /run Dcr.profile.SoundFile = "Sound\\interface\\AuctionWindowOpen.wav" + + -- Hide the buttons + HideButtons = false, + + -- Display text above in the custom frame + CustomeFrameInsertBottom = false, + + -- Disable tooltips in affliction list + AfflictionTooltips = true, + + -- Reverse LiveList Display + ReverseLiveDisplay = false, + + -- Hide the "Decursive" bar + Hidden = false, + + -- if true then the live list will show only if the "Decursive" bar is shown + LiveListTied = false, + + -- allow to changes the default output window + OutputWindow = "DEFAULT_CHAT_FRAME", -- ACEDB CRASHES if we set it directly + + + MiniMapIcon = {hide=true}, + + -- Are we using the macro? + UseMacro = true, + + -- Display a warning if no key is mapped. + NoKeyWarn = false, + + -- Disable macro creation + DisableMacroCreation = false, + + -- Allow Decursive's macro editing + AllowMacroEdit = false, + + -- Those are the different colors used for the MUFs main textures + MF_colors = { + [1] = { .8 , 0 , 0 , 1 }, -- red + [2] = { 0 , 0 , 0.8 , 1 }, -- blue + [3] = { 1 , .5 , .25 , 1 }, -- orange + [4] = { 1 , 0 , 1 , 1 }, -- purple + [5] = { 1 , 1 , 1 , 1 }, -- white for undefined + [6] = { 1 , 1 , 1 , 1 }, -- white for undefined + [DC.NORMAL] = { .0 , .3 , .1 , .9 }, -- dark green + [DC.BLACKLISTED] = { 0 , 0 , 0 , 1 }, -- black + [DC.ABSENT] = { .4 , .4 , .4 , .9 }, -- transparent grey + [DC.FAR] = { .4 , .1 , .4 , .85 }, -- transparent purple + [DC.STEALTHED] = { .4 , .6 , .4 , 1 }, -- pale green + [DC.CHARMED_STATUS] = { 0 , 1 , 0 , 1 }, -- full green + ["COLORCHRONOS"] = { 0.6 , 0.1 , 0.2 , 0.7 }, -- medium red + }, + + + -- Debuffs {{{ + -- those debuffs prevent us from curing the unit + DebuffsToIgnore = { + [DS["Phase Shift"]] = true, + [DS["Banish"]] = true, + [DS["Frost Trap Aura"]] = true, + }, + + -- thoses debuffs are in fact buffs... + BuffDebuff = { + [DS["DREAMLESSSLEEP"]] = true, + [DS["GDREAMLESSSLEEP"]] = true, + [DS["MDREAMLESSSLEEP"]] = true, + [DS["DCR_LOC_MINDVISION"]] = true, + [DS["MUTATINGINJECTION"]] = true, + [DS["Arcane Blast"]] = true, + }, + + DebuffAlwaysSkipList = { + }, + + DebuffsSkipList = { + DS["DCR_LOC_SILENCE"], + DS["ANCIENTHYSTERIA"], + DS["IGNITE"], + DS["TAINTEDMIND"], + DS["MAGMASHAKLES"], + DS["CRIPLES"], + DS["DUSTCLOUD"], + DS["WIDOWSEMBRACE"], + DS["CURSEOFTONGUES"], + DS["SONICBURST"], + DS["DELUSIONOFJINDO"] + }, + + skipByClass = { + ["WARRIOR"] = { + [DS["ANCIENTHYSTERIA"]] = true, + [DS["IGNITE"]] = true, + [DS["TAINTEDMIND"]] = true, + [DS["WIDOWSEMBRACE"]] = true, + [DS["CURSEOFTONGUES"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["ROGUE"] = { + [DS["DCR_LOC_SILENCE"]] = true, + [DS["ANCIENTHYSTERIA"]] = true, + [DS["IGNITE"]] = true, + [DS["TAINTEDMIND"]] = true, + [DS["WIDOWSEMBRACE"]] = true, + [DS["CURSEOFTONGUES"]] = true, + [DS["SONICBURST"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["HUNTER"] = { + [DS["MAGMASHAKLES"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["MAGE"] = { + [DS["MAGMASHAKLES"]] = true, + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["WARLOCK"] = { + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["DRUID"] = { + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["PALADIN"] = { + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["PRIEST"] = { + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["SHAMAN"] = { + [DS["CRIPLES"]] = true, + [DS["DUSTCLOUD"]] = true, + [DS["DELUSIONOFJINDO"]]= true, + }, + ["DEATHKNIGHT"] = { + } + } + -- }}} + } + } -- }}} +end + +local function GetOptions() + return { + -- {{{ + type = "group", + --handler = D, + handler = { + ["hidden"] = function () return not D:IsEnabled(); end, + ["disabled"] = function () return not D:IsEnabled(); end, + }, + hidden = false, + args = { + -- enable and disable + enable = { + type = 'toggle', + guiHidden = true, + --hidden = function() return D:IsEnabled(); end, + disabled = function() return D:IsEnabled(); end, + name = 'enable', + set = function() D.Status.Enabled = D:Enable(); return D.Status.Enabled; end, + get = function() return D:IsEnabled(); end, + order = -2, + }, + disable = { + type = 'toggle', + --hidden = function() return not D:IsEnabled(); end, + guiHidden = true, + disabled = function() return not D:IsEnabled(); end, + name = 'disable', + set = function() D.Status.Enabled = not D:Disable(); return not D.Status.Enabled; end, + get = function() return not D:IsEnabled(); end, + order = -3, + }, + HideMUFsHandle = { + type = 'toggle', + name = L["OPT_HIDEMUFSHANDLE"], + desc = L["OPT_HIDEMUFSHANDLE_DESC"], + guiHidden = true, + disabled = function() return not D:IsEnabled() or not D.profile.ShowDebuffsFrame; end, + set = function(info, v) + D.profile[info[#info]] = v; + D.MFContainerHandle:EnableMouse(not v); + D:Print(v and "MUFs handle disabled" or "MUFs handle enabled"); + return v; + end, + get = function(info) return not D.MFContainerHandle:IsMouseEnabled(); end, + confirm = function(info, v) return v; end, + order = -4, + }, + + -- Atticus Ross rules! + + general = { + -- {{{ + type = 'group', + name = L["OPT_GENERAL"], + order = 1, + icon = DC.IconON, + args = { + version = { + type = 'description', + name = D.version, + image = DC.IconON, + order = 0, + }, + enable = { + type = 'execute', + hidden = function() return D:IsEnabled() end, + disabled = function() return D:IsEnabled() end, + name = L["OPT_ENABLEDECURSIVE"], + func = function() D.Status.Enabled = D:Enable(); return D.Status.Enabled; end, + order = 5, + }, + Sound = { + type = "toggle", + hidden = "hidden", + disabled = function() return D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame or not D:IsEnabled(); end, + name = L["PLAY_SOUND"], + desc = L["OPT_PLAYSOUND_DESC"], + get = function() return D.profile.PlaySound end, + set = function(info, v) + D.profile.PlaySound = v; + end, + order = 10, + }, + ToolTips = { + type = "toggle", + hidden = "hidden", + disabled = function() return D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame or not D:IsEnabled(); end, + name = L["SHOW_TOOLTIP"], + desc = L["OPT_SHOWTOOLTIP_DESC"], + get = function() return D.profile.AfflictionTooltips end, + set = function(info, v) + D.profile.AfflictionTooltips = v; + for k,v in ipairs(D.LiveList.ExistingPerID) do + v.Frame:EnableMouse(D.profile.AfflictionTooltips); + end + + end, + order = 20, + }, + minimap = { + type = "toggle", + hidden = "hidden", + disabled = "disabled", + name = L["OPT_SHOWMINIMAPICON"], + desc = L["OPT_SHOWMINIMAPICON_DESC"], + get = function() return not D.profile.MiniMapIcon or not D.profile.MiniMapIcon.hide end, + set = function(info,v) + local hide = not v; + D.profile.MiniMapIcon.hide = hide; + if hide then + icon:Hide("Decursive"); + else + icon:Show("Decursive"); + end + end, + order = 30, + }, + BlacklistedTime = { + type = 'range', + hidden = "hidden", + disabled = "disabled", + name = L["BLACK_LENGTH"], + desc = L["OPT_BLACKLENTGH_DESC"], + get = function() return D.profile.CureBlacklist end, + set = function(info, v) + D.profile.CureBlacklist = v + end, + min = 1, + max = 20, + step = 0.1, + isPercent = false, + order = 40, + }, + SysOps = { + type = 'header', + hidden = "hidden", + name = "", + order = 50 + }, + ShowTestItem = { + type = "toggle", + hidden = "hidden", + name = L["OPT_CREATE_VIRTUAL_DEBUFF"], + desc = L["OPT_CREATE_VIRTUAL_DEBUFF_DESC"], + get = function() return D.LiveList.TestItemDisplayed end, + set = function() + if not D.LiveList.TestItemDisplayed then + D.LiveList:DisplayTestItem(); + else + D.LiveList:HideTestItem(); + end + end, + disabled = function() return D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame or not D.Status.HasSpell or not D.Status.Enabled end, + order = 60 + }, + ---[[ + NoStartMessages = { + type = "toggle", + hidden = "hidden", + name = L["OPT_NOSTARTMESSAGES"], + desc = L["OPT_NOSTARTMESSAGES_DESC"], + get = function() return D.db.global.NoStartMessages end, + set = function(info,v) + D.db.global.NoStartMessages = v; + end, + disabled = function() return not D.Status.Enabled end, + order = 70 + }, + --]] + report = { + type = "execute", + hidden = "hidden", + name = D:ColorText(L["DECURSIVE_DEBUG_REPORT_SHOW"], "FFFF0000"), + desc = L["DECURSIVE_DEBUG_REPORT_SHOW_DESC"], + func = function () + D:ShowDebugReport(); + end, + hidden = function() return #T._DebugTextTable < 1 end, + order = 1000 + }, + debug = { + type = "toggle", + hidden = "hidden", + name = L["OPT_ENABLEDEBUG"], + desc = L["OPT_ENABLEDEBUG_DESC"], + get = function() return D.db.global.debugging end, + set = function(info,v) + D.db.global.debugging = v; + D.debugging = v; + end, + disabled = "disabled", + order = 90, + }, + GlorfindalMemorium = { + type = "execute", + hidden = "hidden", + name = D:ColorText(L["GLOR1"], "FF" .. D:GetClassHexColor( "WARRIOR" )), + desc = L["GLOR2"], + func = function () + + -- {{{ + LibStub("AceConfigDialog-3.0"):Close(D.name); + if not D.MemoriumFrame then + D.MemoriumFrame = CreateFrame("Frame", nil, UIParent); + local f = D.MemoriumFrame; + local w = 512; local h = 390; + + f:SetFrameStrata("TOOLTIP"); + f:EnableKeyboard(true); + f:SetScript("OnKeyUp", function (frame, event, arg1, arg2) D.MemoriumFrame:Hide(); end); + --[[ + f:SetScript("OnShow", + function () + -- I wanted to make the shadow to move over the marble very slowly as clouds + -- I tried to make it rotate but the way I found would only make it rotate around its origin (which is rarely useful) + -- so leaving it staedy for now... if someone got an idea, let me know. + D:ScheduleRepeatingEvent("Dcr_GlorMoveShadow", + function (f) + local cos, sin = math.cos, math.sin; + f.Shadow.Angle = f.Shadow.Angle + 1; + if f.Shadow.Angle == 360 then f.Shadow.Angle = 0; end + local angle = math.rad(f.Shadow.Angle); + D:SetCoords(f.Shadow, cos(angle), sin(angle), 0, -sin(angle), cos(angle), 0); + + end + , 1/50, f); + end); + f:SetScript("OnHide", function() D:CancelDelayedCall("Dcr_GlorMoveShadow"); end) + --]] + + f:SetWidth(w); + f:SetHeight(h); + f.tTL = f:CreateTexture(nil,"BACKGROUND") + f.tTL:SetTexture("Interface\\ItemTextFrame\\ItemText-Marble-TopLeft") + f.tTL:SetWidth(w - w / 5); + f.tTL:SetHeight(h - h / 3); + f.tTL:SetTexCoord(0, 1, 5/256, 1); + f.tTL:SetPoint("TOPLEFT", f, "TOPLEFT", 2, -10); + + f.tTR = f:CreateTexture(nil,"BACKGROUND") + f.tTR:SetTexture("Interface\\ItemTextFrame\\ItemText-Marble-TopRight") + f.tTR:SetWidth(w / 5 - 3); + f.tTR:SetHeight(h - h / 3); + f.tTR:SetTexCoord(0, 1, 5/256, 1); + f.tTR:SetPoint("TOPLEFT", f.tTL, "TOPRIGHT", 0, 0); + + f.tBL = f:CreateTexture(nil,"BACKGROUND") + f.tBL:SetTexture("Interface\\ItemTextFrame\\ItemText-Marble-BotLeft") + f.tBL:SetWidth(w - w / 5); + f.tBL:SetHeight(h / 3 - 20); + f.tBL:SetTexCoord(0,1,0, 408/512); + f.tBL:SetPoint("TOPLEFT", f.tTL, "BOTTOMLEFT", 0, 0); + + f.tBR = f:CreateTexture(nil,"BACKGROUND") + f.tBR:SetTexture("Interface\\ItemTextFrame\\ItemText-Marble-BotRight") + f.tBR:SetWidth(w / 5 - 3); + f.tBR:SetHeight(h / 3 - 20); + f.tBR:SetTexCoord(0,1,0, 408/512); + f.tBR:SetPoint("TOPLEFT", f.tBL, "TOPRIGHT", 0, 0); + + f.Shadow = f:CreateTexture(nil, "ARTWORK"); + f.Shadow:SetTexture("Interface\\TabardFrame\\TabardFrameBackground") + f.Shadow:SetPoint("TOPLEFT", f, "TOPLEFT", 2, -9); + f.Shadow:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -2, 9); + f.Shadow:SetAlpha(0.1); + + ---[[ + f.fB = f:CreateTexture(nil,"OVERLAY") + f.fB:SetTexture("Interface\\AddOns\\Decursive\\Textures\\GoldBorder") + f.fB:SetTexCoord(5/512, 324/512, 6/512, 287/512); + f.fB:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0); + f.fB:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 0, 0); + --]] + + f.FSt = f:CreateFontString(nil,"OVERLAY", "MailTextFontNormal"); + f.FSt:SetFont("Fonts\\MORPHEUS.TTF", 18 ); + f.FSt:SetTextColor(0.18, 0.12, 0.06, 1); + f.FSt:SetPoint("TOPLEFT", f.tTL, "TOPLEFT", 5, -20); + f.FSt:SetPoint("TOPRIGHT", f.tTR, "TOPRIGHT", -5, -20); + f.FSt:SetJustifyH("CENTER"); + f.FSt:SetText(L["GLOR3"]); + f.FSt:SetAlpha(0.80); + + f.FSc = f:CreateFontString(nil,"OVERLAY", "MailTextFontNormal"); + f.FSc:SetFont("Fonts\\MORPHEUS.TTF", 15 ); + f.FSc:SetTextColor(0.18, 0.12, 0.06, 1); + f.FSc:SetHeight(h - 30 - 60); + f.FSc:SetPoint("TOP", f.FSt, "BOTTOM", 0, -28); + f.FSc:SetPoint("LEFT", f.tTL, "LEFT", 30, 0); + f.FSc:SetPoint("RIGHT", f.tTR, "RIGHT", -30, 0); + f.FSc:SetJustifyH("CENTER"); + f.FSc:SetJustifyV("TOP"); + f.FSc:SetSpacing(5); + + f.FSc:SetText(L["GLOR4"]); + + + f.FSc:SetAlpha(0.80); + + f.FSl = f:CreateFontString(nil,"OVERLAY", "MailTextFontNormal"); + f.FSl:SetFont("Fonts\\MORPHEUS.TTF", 15 ); + f.FSl:SetTextColor(0.18, 0.12, 0.06, 1); + f.FSl:SetJustifyH("LEFT"); + f.FSl:SetJustifyV("BOTTOM"); + f.FSl:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 30, 33); + f.FSl:SetAlpha(0.80); + f.FSl:SetText(L["GLOR5"]); + + f:SetPoint("CENTER",0,0); + + end + D.MemoriumFrame:Show(); + + --[[ + + In remembrance of Bertrand Sense + 1969 - 2007 + + + Friendship and affection can take their roots anywhere, those + who met Glorfindal in World of Warcraft knew a man of great + commitment and a charismatic leader. + + He was in life as he was in game, selfless, generous, dedicated + to his friends and most of all, a passionate man. + + He left us at the age of 38 leaving behind him not just + anonymous players in a virtual world, but a group of true + friends who will miss him forever. + + He will always be remembered... + + -- + + En souvenir de Bertrand Sense + 1969 - 2007 + + L'amitié et l'affection peuvent prendre naissance n'importe où, + ceux qui ont rencontré Glorfindal dans World Of Warcraft on + connu un homme engagé et un leader charismatique. + + Il était dans la vie comme dans le jeux, désintéressé, + généreux, dévoué envers les siens et surtout un homme passionné. + + Il nous a quitté à l'âge de 38 ans laissant derrière lui pas + seulement des joueurs anonymes dans un monde virtuel, mais un + groupe de véritables amis à qui il manquera eternellement. + + On ne l'oubliera jamais... + + --]] + -- }}} + end, + order = 100, + }, + } + }, -- }}} + + livelistoptions = { + -- {{{ + type = "group", + handler = { + ["hidden"] = function () return not D:IsEnabled(); end, + ["disabled"] = function () return not D:IsEnabled(); end, + ["subhidden"] = function () return not D:IsEnabled() or D.profile.Hide_LiveList; end, + ["subdisabled"] = function () return not D:IsEnabled() or D.profile.Hide_LiveList; end, + }, + hidden = "hidden", + disabled = "disabled", + name = D:ColorText(L["OPT_LIVELIST"], "FF22EE33"), + desc = L["OPT_LIVELIST_DESC"], + order = 2, + + args = { + description = {name = L["OPT_LIVELIST_DESC"], order = 0, type = "description"}, + show = { + type = "toggle", + name = L["HIDE_LIVELIST"], + desc = L["OPT_HIDELIVELIST_DESC"], + get = function() return D.profile.Hide_LiveList end, + set = function() + D:ShowHideLiveList() + if D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame or not D.Status.HasSpell then + D:SetIcon(DC.IconOFF); + else + D:SetIcon(DC.IconON); + end + end, + order = 100 + }, + OnlyInRange = { + type = "toggle", + hidden = "subhidden", + disabled = "subdisabled", + name = L["OPT_LVONLYINRANGE"], + desc = L["OPT_LVONLYINRANGE_DESC"], + get = function() return D.profile.LV_OnlyInRange end, + set = function(info, v) D.profile.LV_OnlyInRange = v end, + order = 100.5 + }, + livenum = { + type = 'range', + hidden = "subhidden", + disabled = "subdisabled", + name = L["AMOUNT_AFFLIC"], + desc = L["OPT_AMOUNT_AFFLIC_DESC"], + get = function() return D.profile.Amount_Of_Afflicted end, + set = function(info, v) + D:Debug(v); + D.profile.Amount_Of_Afflicted = v; + D.LiveList:RestAllPosition(); + end, + min = 1, + max = D.CONF.MAX_LIVE_SLOTS, + step = 1, + isPercent = false, + order = 104, + }, + ScanFreq = { + type = 'range', + hidden = "subhidden", + disabled = "subdisabled", + name = L["SCAN_LENGTH"], + desc = L["OPT_SCANLENGTH_DESC"], + get = function() return D.profile.ScanTime end, + set = function(info,v) + if (v ~= D.profile.ScanTime) then + D.profile.ScanTime = v; + D:ScheduleRepeatedCall("Dcr_LLupdate", D.LiveList.Update_Display, D.profile.ScanTime, D.LiveList); + D:Debug("LV scan delay changed:", D.profile.ScanTime, v); + end + end, + min = 0.1, + max = 1, + step = 0.1, + isPercent = false, + order = 106, + }, + ReverseLL = { + type = "toggle", + hidden = "subhidden", + disabled = "subdisabled", + name = L["REVERSE_LIVELIST"], + desc = L["OPT_REVERSE_LIVELIST_DESC"], + get = function() return D.profile.ReverseLiveDisplay end, + set = function(info, v) + D.profile.ReverseLiveDisplay = v + D.LiveList:RestAllPosition(); + end, + order = 107 + }, + TieLLVisibility = { + type = "toggle", + disabled = true, -- deprecated old option, let's see how people react + hidden = true, + name = L["TIE_LIVELIST"], + desc = L["OPT_TIE_LIVELIST_DESC"], + get = function() return D.profile.LiveListTied end, + set = function(info, v) + D.profile.LiveListTied = v + end, + order = 108 + }, + FrameScaleLL = { + type = 'range', + hidden = "subhidden", + disabled = function() return D.profile.Hide_LiveList or D.profile.Hidden end, + name = L["OPT_LLSCALE"], + desc = L["OPT_LLSCALE_DESC"], + get = function() return D.profile.LiveListScale end, -- D.profile.DebuffsFrameElemScale end, + set = function(info,v) + if (v ~= D.profile.LiveListScale) then + D.profile.LiveListScale = v; + + D:SetLLScale(D.profile.LiveListScale); + end + end, + min = 0.3, + max = 4, + step = 0.01, + isPercent = true, + order = 1009, + }, + AlphaLL = { + type = 'range', + hidden = "subhidden", + disabled = function() return D.profile.Hide_LiveList or D.profile.Hidden end, + name = L["OPT_LLALPHA"], + desc = L["OPT_LLALPHA_DESC"], + get = function() return 1 - D.profile.LiveListAlpha end, + set = function(info,v) + if (v ~= D.profile.LiveListAlpha) then + D.profile.LiveListAlpha = 1 - v; + DecursiveMainBar:SetAlpha(D.profile.LiveListAlpha); + DcrLiveList:SetAlpha(D.profile.LiveListAlpha); + end + end, + min = 0, + max = 0.8, + step = 0.01, + isPercent = true, + order = 1010, + } + }, + }, -- // }}} + + MessageOptions = { + -- {{{ + type = "group", + hidden = "hidden", + name = D:ColorText(L["OPT_MESSAGES"], "FF229966"), + desc = L["OPT_MESSAGES_DESC"], + order = 3, + disabled = function() return not D.Status.Enabled end, + args = { + description = {name = L["OPT_MESSAGES_DESC"], order = 1, type = "description"}, + PrintToDefaultChat = { + type = "toggle", + width = 'full', + name = L["PRINT_CHATFRAME"], + desc = L["OPT_CHATFRAME_DESC"], + get = function() return D.profile.Print_ChatFrame end, + set = function(info,v) + D.profile.Print_ChatFrame = v; + end, + order = 120 + }, + PrintToCustomChat = { + type = "toggle", + width = 'full', + name = L["PRINT_CUSTOM"], + desc = L["OPT_PRINT_CUSTOM_DESC"], + get = function() return D.profile.Print_CustomFrame end, + set = function(info,v) + D.profile.Print_CustomFrame = v; + end, + order = 121 + }, + PrintErrors = { + type = "toggle", + width = 'full', + name = L["PRINT_ERRORS"], + desc = L["OPT_PRINT_ERRORS_DESC"], + get = function() return D.profile.Print_Error end, + set = function(info,v) + D.profile.Print_Error = v; + end, + order = 122 + }, + ShowCustomFAnchor = { + type = "toggle", + width = 'full', + name = L["ANCHOR"], + desc = L["OPT_ANCHOR_DESC"], + get = function() return DecursiveAnchor:IsVisible() end, + set = function() + D:ShowHideTextAnchor(); + end, + order = 123 + }, + } + }, -- }}} + + MicroFrameOpt = { + -- {{{ + type = "group", + handler = { + ["hidden"] = function () return not D:IsEnabled(); end, + ["disabled"] = function () return not D:IsEnabled(); end, + ["subhidden"] = function () return not D:IsEnabled() or not D.profile.ShowDebuffsFrame; end, + ["subdisabled"] = function () return not D:IsEnabled() or not D.profile.ShowDebuffsFrame; end, + }, + hidden = "hidden", + childGroups = "tab", + name = D:ColorText(L["OPT_MFSETTINGS"], "FFBBCC33"), + desc = L["OPT_MFSETTINGS_DESC"], + order = 4, + disabled = function() return not D.Status.Enabled end, + args = { + displayOpts = { + type = "group", + name = L["OPT_DISPLAYOPTIONS"], + desc = L["OPT_MFSETTINGS_DESC"], + order = 1, + args = { + -- {{{ + Show = { + type = "toggle", + name = L["OPT_SHOWMFS"], + desc = L["OPT_SHOWMFS_DESC"], + get = function() return D.profile.ShowDebuffsFrame end, + set = function() + D:ShowHideDebuffsFrame (); + end, + disabled = function() return D.Status.Combat end, + order = 1200, + }, + + AutoHide = { + type = "select", + style = "dropdown", + name = L["OPT_AUTOHIDEMFS"], + desc = L["OPT_AUTOHIDEMFS_DESC"] .. "\n\n" .. ("%s: %s\n%s: %s\n%s: %s"):format(D:ColorText(L["OPT_HIDEMFS_NEVER"], "FF88CCAA"), L["OPT_HIDEMFS_NEVER_DESC"], D:ColorText(L["OPT_HIDEMFS_SOLO"], "FF88CCAA"), L["OPT_HIDEMFS_SOLO_DESC"], D:ColorText(L["OPT_HIDEMFS_GROUP"], "FF88CCAA"), L["OPT_HIDEMFS_GROUP_DESC"]), + order = 1210, + values = {L["OPT_HIDEMFS_NEVER"], L["OPT_HIDEMFS_SOLO"], L["OPT_HIDEMFS_GROUP"]}, + set = function(info,value) + D:Debug(value); + D.profile.AutoHideDebuffsFrame = value - 1; + D:AutoHideShowMUFs(); + end, + get = function() + return D.profile.AutoHideDebuffsFrame + 1; + end, + }, + GrowToTop = { + type = "toggle", + name = L["OPT_GROWDIRECTION"], + desc = L["OPT_GROWDIRECTION_DESC"], + get = function() return D.profile.DebuffsFrameGrowToTop end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameGrowToTop) then + D.profile.DebuffsFrameGrowToTop = v; + D.MicroUnitF:SavePos(); + D.MicroUnitF:ResetAllPositions (); + D.MicroUnitF:Place (); + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + order = 1300, + }, + StickToRight = { + type = "toggle", + name = L["OPT_STICKTORIGHT"], + desc = L["OPT_STICKTORIGHT_DESC"], + get = function() return D.profile.DebuffsFrameStickToRight end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameStickToRight) then + D.profile.DebuffsFrameStickToRight = v; + D.MicroUnitF:SavePos(); + D.MicroUnitF:Delayed_MFsDisplay_Update(); + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + order = 1310, + }, + ShowBorder = { + type = "toggle", + name = L["OPT_SHOWBORDER"], + desc = L["OPT_SHOWBORDER_DESC"], + get = function() return D.profile.DebuffsFrameElemBorderShow end, + set = function(info,v) + D.profile.DebuffsFrameElemBorderShow = v; + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + order = 1350, + }, + ShowChrono = { + type = "toggle", + disabled = "subdisabled", + name = L["OPT_SHOWCHRONO"], + desc = L["OPT_SHOWCHRONO_DESC"], + get = function() return D.profile.DebuffsFrameChrono end, + set = function(info,v) + D.profile.DebuffsFrameChrono = v; + end, + order = 1360, + }, + ShowChronoTimeLeft = { + type = "toggle", + disabled = function () return not D.profile.DebuffsFrameChrono or not D.profile.ShowDebuffsFrame end, + name = L["OPT_SHOWCHRONOTIMElEFT"], + desc = L["OPT_SHOWCHRONOTIMElEFT_DESC"], + get = function() return D.profile.DebuffsFrameTimeLeft end, + set = function(info,v) + D.profile.DebuffsFrameTimeLeft = v; + end, + order = 1365, + }, + ShowStealthStatus = { + type = "toggle", + disabled = "subdisabled", + name = L["OPT_SHOW_STEALTH_STATUS"], + desc = L["OPT_SHOW_STEALTH_STATUS_DESC"], + get = function() return D.profile.Show_Stealthed_Status end, + set = function(info,v) + D.profile.Show_Stealthed_Status = v; + end, + order = 1370, + }, + ToolTips = { + type = "toggle", + name = L["SHOW_TOOLTIP"], + desc = L["OPT_SHOWTOOLTIP_DESC"], + get = function() return D.profile.AfflictionTooltips end, + set = function(info,v) + D.profile.AfflictionTooltips = v + for k,v in ipairs(D.LiveList.ExistingPerID) do + v.Frame:EnableMouse(D.profile.AfflictionTooltips); + end + end, + disabled = function() return D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame end, + order = 1400, + }, + ShowHelp = { + type = "toggle", + disabled = "subdisabled", + name = L["OPT_SHOWHELP"], + desc = L["OPT_SHOWHELP_DESC"], + get = function() return D.profile.DebuffsFrameShowHelp end, + set = function(info,v) + D.profile.DebuffsFrameShowHelp = v; + end, + order = 1450, + }, + MaxCount = { + type = 'range', + name = L["OPT_MAXMFS"], + desc = L["OPT_MAXMFS_DESC"], + get = function() return D.profile.DebuffsFrameMaxCount end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameMaxCount) then + D.profile.DebuffsFrameMaxCount = v; + D.MicroUnitF.MaxUnit = v; + D.MicroUnitF:Delayed_MFsDisplay_Update(); + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + min = 1, + max = 82, + step = 1, + isPercent = false, + order = 1500, + }, + MFPerline = { + type = 'range', + name = L["OPT_UNITPERLINES"], + desc = L["OPT_UNITPERLINES_DESC"], + get = function() return D.profile.DebuffsFramePerline end, + set = function(info,v) + if (v ~= D.profile.DebuffsFramePerline) then + D.profile.DebuffsFramePerline = v; + D.MicroUnitF:ResetAllPositions (); + D.MicroUnitF:Place (); + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + min = 1, + max = 40, + step = 1, + isPercent = false, + order = 1600, + }, + FrameScale = { + type = 'range', + name = L["OPT_MFSCALE"], + desc = L["OPT_MFSCALE_DESC"], + get = function() return D.profile.DebuffsFrameElemScale end, -- D.profile.DebuffsFrameElemScale end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameElemScale) then + D.profile.DebuffsFrameElemScale = v; + + + D.MicroUnitF:SetScale(D.profile.DebuffsFrameElemScale); + + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + min = 0.3, + max = 4, + step = 0.01, + isPercent = true, + order = 1800, + }, + Alpha = { + type = 'range', + name = L["OPT_MFALPHA"], + desc = L["OPT_MFALPHA_DESC"], + get = function() return 1 - D.profile.DebuffsFrameElemAlpha end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameElemAlpha) then + D.profile.DebuffsFrameElemAlpha = 1 - v; + D.profile.DebuffsFrameElemBorderAlpha = (1 - v) / 2; + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame or not D.profile.DebuffsFrameElemTieTransparency end, + min = 0, + max = 1, + step = 0.01, + isPercent = true, + order = 1900, + }, + TestLayout = { + type = "toggle", + disabled = "subdisabled", + name = L["OPT_TESTLAYOUT"], + desc = L["OPT_TESTLAYOUT_DESC"], + get = function() return D.Status.TestLayout end, + set = function(info,v) + D.Status.TestLayout = v; + D:GroupChanged("Test Layout"); + end, + order = 1950, + }, + TestLayoutUNum = { + type = 'range', + name = L["OPT_TESTLAYOUTUNUM"], + desc = L["OPT_TESTLAYOUTUNUM_DESC"], + get = function() return D.Status.TestLayoutUNum end, + set = function(info,v) + if v ~= D.Status.TestLayoutUNum then + D.Status.TestLayoutUNum = v; + D:GroupChanged("Test Layout num changed"); + --D.MicroUnitF:Delayed_MFsDisplay_Update(); + end + end, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame or not D.Status.TestLayout end, + min = 1, + max = 82, + step = 1, + isPercent = false, + order = 2000, + }, + -- }}} + }, + }, + + AdvDispOptions = { + type = "group", + name = L["OPT_ADVDISP"], + desc = L["OPT_ADVDISP_DESC"], + order = 2, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + args = { + -- {{{ + TransparencyOpts = { + type = 'group', + inline = true, + name = " ", + args = { + TieTransparency = { + type = "toggle", + name = L["OPT_TIECENTERANDBORDER"], + desc = L["OPT_TIECENTERANDBORDER_OPT"], + get = function() return D.profile.DebuffsFrameElemTieTransparency end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameElemTieTransparency) then + D.profile.DebuffsFrameElemTieTransparency = v; + if v then + D.profile.DebuffsFrameElemBorderAlpha = (D.profile.DebuffsFrameElemAlpha / 2); + end + end + end, + disabled = function() return D.Status.Combat end, + order = 100 + }, + BorderAlpha = { + type = 'range', + name = L["OPT_BORDERTRANSP"], + desc = L["OPT_BORDERTRANSP_DESC"], + get = function() return 1 - D.profile.DebuffsFrameElemBorderAlpha end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameElemBorderAlpha) then + D.profile.DebuffsFrameElemBorderAlpha = 1 - v; + end + end, + disabled = function() return D.Status.Combat or D.profile.DebuffsFrameElemTieTransparency end, + min = 0, + max = 1, + step = 0.01, + isPercent = true, + order = 102, + }, + CenterAlpha = { + type = 'range', + name = L["OPT_CENTERTRANSP"], + desc = L["OPT_CENTERTRANSP_DESC"], + get = function() return 1 - D.profile.DebuffsFrameElemAlpha end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameElemAlpha) then + D.profile.DebuffsFrameElemAlpha = 1 - v; + if D.profile.DebuffsFrameElemTieTransparency then + D.profile.DebuffsFrameElemBorderAlpha = (1 - v) / 2; + end + end + end, + disabled = function() return D.Status.Combat end, + min = 0, + max = 1, + step = 0.01, + isPercent = true, + order = 101, + }, + }, + }, + SpacingOpts = { + type = 'group', + inline = true, + name = " ", + args = { + TieXY = { + type = "toggle", + name = L["OPT_TIEXYSPACING"], + desc = L["OPT_TIEXYSPACING_DESC"], + get = function() return D.profile.DebuffsFrameTieSpacing end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameTieSpacing) then + D.profile.DebuffsFrameTieSpacing = v; + if v then + D.profile.DebuffsFrameYSpacing = D.profile.DebuffsFrameXSpacing; + end + D.MicroUnitF:ResetAllPositions (); + end + end, + disabled = function() return D.Status.Combat end, + order = 104 + }, + XSpace = { + type = 'range', + name = L["OPT_XSPACING"], + desc = L["OPT_XSPACING_DESC"], + get = function() return D.profile.DebuffsFrameXSpacing end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameXSpacing) then + D.profile.DebuffsFrameXSpacing = v; + if D.profile.DebuffsFrameTieSpacing then + D.profile.DebuffsFrameYSpacing = v; + end + D.MicroUnitF:ResetAllPositions (); + D.MicroUnitF:Place (); + end + end, + disabled = function() return D.Status.Combat end, + min = 0, + max = 100, + step = 1, + isPercent = false, + order = 105, + }, + YSpace = { + type = 'range', + name = L["OPT_YSPACING"], + desc = L["OPT_YSPACING_DESC"], + get = function() return D.profile.DebuffsFrameYSpacing end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameYSpacing) then + D.profile.DebuffsFrameYSpacing = v; + D.MicroUnitF:ResetAllPositions (); + D.MicroUnitF:Place (); + end + end, + disabled = function() return D.Status.Combat or D.profile.DebuffsFrameTieSpacing end, + min = 0, + max = 100, + step = 1, + isPercent = false, + order = 106, + }, -- }}} + }, + }, + }, + }, + + MUFsColors = { + type = "group", + name = L["OPT_MUFSCOLORS"], + desc = L["OPT_MUFSCOLORS_DESC"], + order = 3, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame or not D:IsEnabled() end, + hidden = function () return not D:IsEnabled(); end, + args = {} + }, + + MUFsMouseButtons = { + type = "group", + name = L["OPT_MUFMOUSEBUTTONS"], + desc = L["OPT_MUFMOUSEBUTTONS_DESC"], + order = 4, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame or not D:IsEnabled() end, + hidden = function () return not D:IsEnabled(); end, + args = { + -- {{{ + ClicksAdssigmentsDesc = { + type = "description", + name = L["OPT_MUFMOUSEBUTTONS_DESC"], + order = 151, + }, + ResetClicksAdssigments = { + type = "execute", + confirm = true, + name = L["OPT_RESETMUFMOUSEBUTTONS"], + desc = L["OPT_RESETMUFMOUSEBUTTONS_DESC"], + func = function () + table.wipe(D.db.global.AvailableButtons); + D:tcopy(D.db.global.AvailableButtons, D.defaults.global.AvailableButtons); + end, + order = -1, + }, + -- }}} + } + }, + + PerfOptions = { + type = "group", + name = L["OPT_MFPERFOPT"], + --desc = L["OPT_ADVDISP_DESC"], + order = 5, + disabled = function() return D.Status.Combat or not D.profile.ShowDebuffsFrame end, + args = { + -- {{{ + UpdateRate = { + type = 'range', + name = L["OPT_MFREFRESHRATE"], + desc = L["OPT_MFREFRESHRATE_DESC"], + get = function() return D.profile.DebuffsFrameRefreshRate end, + set = function(info,v) + if (v ~= D.profile.DebuffsFrameRefreshRate) then + D.profile.DebuffsFrameRefreshRate = v; + + D:ScheduleRepeatedCall("Dcr_MUFupdate", D.DebuffsFrame_Update, D.profile.DebuffsFrameRefreshRate, D); + D:Debug("MUFs refresh rate changed:", D.profile.DebuffsFrameRefreshRate, v); + end + end, + --disabled = function() return not D.profile.ShowDebuffsFrame end, + disabled = "subdisabled", + min = 0.017, + max = 0.2, + step = 0.01, + isPercent = false, + order = 2600, + }, + PerUpdate = { + type = 'range', + name = L["OPT_MFREFRESHSPEED"], + desc = L["OPT_MFREFRESHSPEED_DESC"], + get = function() return D.profile.DebuffsFramePerUPdate end, + set = function(info,v) + if (v ~= D.profile.DebuffsFramePerUPdate) then + D.profile.DebuffsFramePerUPdate = v; + end + end, + --disabled = function() return not D.profile.ShowDebuffsFrame end, + disabled = "subdisabled", + min = 1, + max = 82, + step = 1, + isPercent = false, + order = 2700, + }, + }, + }, -- }}} + }, + }, -- }}} + + CureOptions = { + -- {{{ + type = "group", + hidden = "hidden", + name = D:ColorText(L["OPT_CURINGOPTIONS"], "FFFF5533"), + desc = L["OPT_CURINGOPTIONS_DESC"], + order = 5, + disabled = function() return not D.Status.Enabled end, + args = { + description = {name = L["OPT_CURINGOPTIONS_DESC"], order = 1, type = "description"}, + AbolishCheck = { + type = "toggle", + width = 'full', + name = L["ABOLISH_CHECK"], + desc = L["OPT_ABOLISHCHECK_DESC"], + get = function() return D.profile.Check_For_Abolish end, + set = function(info, v) + D.profile.Check_For_Abolish = v; + end, + order = 120 + }, + DisableAbolish = { + type = "toggle", + width = 'full', + name = L["OPT_DISABLEABOLISH"], + desc = L["OPT_DISABLEABOLISH_DESC"], + get = function() return D.profile.DisableAbolish end, + set = function(info, v) + D.profile.DisableAbolish = v; + if v then + DC.SpellsToUse[DS["SPELL_CURE_DISEASE"]].IsBest = 10; + DC.SpellsToUse[DS["SPELL_CURE_POISON"]].IsBest = 10; + else + DC.SpellsToUse[DS["SPELL_CURE_DISEASE"]].IsBest = 0; + DC.SpellsToUse[DS["SPELL_CURE_POISON"]].IsBest = 0; + end + D:Configure(); + end, + order = 130 + }, + DoNotBlPrios = { + type = "toggle", + width = 'full', + name = L["DONOT_BL_PRIO"], + desc = L["OPT_DONOTBLPRIO_DESC"], + get = function() return D.profile.DoNot_Blacklist_Prio_List end, + set = function(info, v) + D.profile.DoNot_Blacklist_Prio_List = v; + end, + order = 131 + }, + CurePets = { + type = "toggle", + width = 'full', + name = L["CURE_PETS"], + desc = L["OPT_CUREPETS_DESC"], + get = function() return D.profile.Scan_Pets end, + set = function(info, v) + D.profile.Scan_Pets = v; + D:GroupChanged ("opt CURE_PETS"); + end, + order = 133 + }, + + Title2 = { + type="header", + name = L["OPT_CURINGORDEROPTIONS"], + order = 139, + }, + description = { + type = "description", + name = L["OPT_CURINGOPTIONS_EXPLANATION"], + order = 140, + }, + CureMagic = { + type = "toggle", + name = " "..L["MAGIC"], + desc = L["OPT_MAGICCHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.MAGIC) end, + set = function() + D:SetCureOrder (DC.MAGIC); + end, + disabled = function() return not D.Status.CuringSpells[DC.MAGIC] end, + order = 141 + }, + CureEnemyMagic = { + type = "toggle", + name = " "..L["MAGICCHARMED"], + desc = L["OPT_MAGICCHARMEDCHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.ENEMYMAGIC) end, + set = function() + D:SetCureOrder (DC.ENEMYMAGIC); + end, + disabled = function() return not D.Status.CuringSpells[DC.ENEMYMAGIC] end, + order = 142 + }, + CurePoison = { + type = "toggle", + name = " "..L["POISON"], + desc = L["OPT_POISONCHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.POISON) end, + set = function() + D:SetCureOrder (DC.POISON); + end, + disabled = function() return not D.Status.CuringSpells[DC.POISON] end, + order = 143 + }, + CureDisease = { + type = "toggle", + name = " "..L["DISEASE"], + desc = L["OPT_DISEASECHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.DISEASE) end, + set = function() + D:SetCureOrder (DC.DISEASE); + end, + disabled = function() return not D.Status.CuringSpells[DC.DISEASE] end, + order = 144 + }, + CureCurse = { + type = "toggle", + name = " "..L["CURSE"], + desc = L["OPT_CURSECHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.CURSE) end, + set = function() + D:SetCureOrder (DC.CURSE); + end, + disabled = function() return not D.Status.CuringSpells[DC.CURSE] end, + order = 145 + }, + CureCharmed = { + type = "toggle", + name = " "..L["CHARM"], + desc = L["OPT_CHARMEDCHECK_DESC"], + get = function() return D:GetCureCheckBoxStatus(DC.CHARMED) end, + set = function() + D:SetCureOrder (DC.CHARMED); + end, + disabled = function() return not D.Status.CuringSpells[DC.CHARMED] end, + order = 146 + }, + } + }, -- }}} + + DebuffSkip = { + -- {{{ + type = "group", + hidden = function () return not D:IsEnabled(); end, + disabled = function () return not D:IsEnabled(); end, + name = D:ColorText(L["OPT_DEBUFFFILTER"], "FF99CCAA"), + desc = L["OPT_DEBUFFFILTER_DESC"], + order = 6, + childGroups= "select", + disabled = function() return not D.Status.Enabled end, + args = {} + }, -- }}} + + Macro = { + -- {{{ + type = "group", + hidden = "hidden", + name = D:ColorText(L["OPT_MACROOPTIONS"], "FFCC99BB"), + desc = L["OPT_MACROOPTIONS_DESC"], + order = 7, + disabled = function() return not D.Status.Enabled or D.Status.Combat end, + args = { + description = {name = L["OPT_MACROOPTIONS_DESC"], order = 1, type = "description"}, + SetKey = { + type = "keybinding", + name = L["OPT_MACROBIND"], + desc = L["OPT_MACROBIND_DESC"], + get = function () + local key = (GetBindingKey(D.CONF.MACROCOMMAND)); + D.db.global.MacroBind = key; + return key; + end, + set = function (info,key) + if key ~= "BUTTON1" and key ~= "BUTTON2" then + D:SetMacroKey ( key ); + end + end, + disabled = function () return D.profile.DisableMacroCreation end, + order = 200, + }, + NoKeyWarn = { + type = "toggle", + name = L["OPT_NOKEYWARN"], + desc = L["OPT_NOKEYWARN_DESC"], + get = function() return D.profile.NoKeyWarn end, + set = function(info,v) + D.profile.NoKeyWarn = v; + end, + disabled = function () return D.profile.DisableMacroCreation end, + order = 300 + }, + AllowMacroEdit = { + type = "toggle", + name = L["OPT_ALLOWMACROEDIT"], + desc = L["OPT_ALLOWMACROEDIT_DESC"], + get = function() return D.profile.AllowMacroEdit end, + set = function(info,v) + D.profile.AllowMacroEdit = v; + end, + disabled = function () return D.profile.DisableMacroCreation end, + order = 350 + }, + DisableMacroCreation = { + type = "toggle", + name = L["OPT_DISABLEMACROCREATION"], + desc = L["OPT_DISABLEMACROCREATION_DESC"], + get = function() return D.profile.DisableMacroCreation end, + set = function(info,v) + if v then + D:SetMacroKey (nil); -- remove the macro key assignment. + D:Debug("SetMacroKey (nil)"); + end + D.profile.DisableMacroCreation = v; + end, + order = 400 + } + } + }, -- }}} + + About = { + type = "group", + name = D:ColorText(L["OPT_ABOUT"], "FFFFFFFF"), + order = -1, + args = { + -- Decursive vxx by x released on XX + Title = { + type = 'description', + name = ( + "\n\n\n\nDecursive |cFFDD0000 v%s |r by |cFFDD0000 %s |r released on |cFFDD0000 %s |r".. + "\n\n |cFF55DDDD %s |r".. + "\n\n|cFFDDDD00 %s|r:\n %s".. + "\n\n|cFFDDDD00 %s|r:\n %s".. + "\n\n|cFFDDDD00 %s|r:\n %s".. + "\n\n|cFFDDDD00 %s|r:\n %s".. + "\n\n|cFFDDDD00 %s|r:\n %s" + ):format( + "2.5.1-6-gd3885c5", "Archarodim", ("2010-09-06T22:38:15Z"):sub(1,10), + L["ABOUT_NOTES"], + L["ABOUT_LICENSE"], GetAddOnMetadata("Decursive", "X-License"), + L["ABOUT_SHAREDLIBS"], GetAddOnMetadata("Decursive", "X-Embeds"), + L["ABOUT_OFFICIALWEBSITE"], GetAddOnMetadata("Decursive", "X-Website"), + L["ABOUT_AUTHOREMAIL"], GetAddOnMetadata("Decursive", "X-eMail"), + L["ABOUT_CREDITS"], GetAddOnMetadata("Decursive", "X-Credits") + ), + order = 0, + }, + Sep1 = { + type = "header", + name = "", + order = 5, + }, + CheckVersions = { + type = "execute", + name = L["OPT_CHECKOTHERPLAYERS"], + desc = L["OPT_CHECKOTHERPLAYERS_DESC"], + hidden = function () return not DC.COMMAVAILABLE; end, + disabled = function () return InCombatLockdown() or GetTime() - T.LastVCheck < 60; end, + func = function () if D:AskVersion() then D.versions = false; end end, + order = 10, + }, + VersionsDisplay = { + type = "description", + name = D.ReturnVersions, + hidden = function () return not D.versions; end, + order = 30, + }, + + } + } + }, + } -- }}} +end + +function D:ExportOptions () + -- Export the option table to Blizz option UI and to Ace3 option UI + D.options = GetOptions(); + local option_args = D.options.args; + option_args.general.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db); + option_args.general.args.profiles.order = -1; + option_args.general.args.profiles.inline = true; + option_args.general.args.profiles.hidden = function() return not D:IsEnabled(); end; + option_args.general.args.profiles.disabled = option_args.general.args.profiles.hidden; + + LibStub("AceConfig-3.0"):RegisterOptionsTable(D.name, D.options, 'dcr'); + LibStub("AceConfigDialog-3.0"):AddToBlizOptions(D.name, D.name, nil, "general"); + + local SubGroups_ToBlizzOptions = { + [D:ColorText(L["OPT_LIVELIST"], "FF22EE33")] = "livelistoptions", + [D:ColorText(L["OPT_MESSAGES"], "FF229966")] = "MessageOptions", + [D:ColorText(L["OPT_MFSETTINGS"], "FFBBCC33")] = "MicroFrameOpt", + [D:ColorText(L["OPT_CURINGOPTIONS"], "FFFF5533")] = "CureOptions", + [D:ColorText(L["OPT_DEBUFFFILTER"], "FF99CCAA")] = "DebuffSkip", + [D:ColorText(L["OPT_MACROOPTIONS"], "FFCC99BB")] = "Macro", + [D:ColorText(L["OPT_ABOUT"], "FFFFFFFF")] = "About", + }; + + for key,value in pairs(SubGroups_ToBlizzOptions) do + LibStub("AceConfigDialog-3.0"):AddToBlizOptions(D.name, key, D.name, value); + end +end + + + +function D:GetCureCheckBoxStatus (Type) + return D.classprofile.CureOrder[Type] and D.classprofile.CureOrder[Type] > 0; +end + +local TypesToUName = { + [DC.ENEMYMAGIC] = "MAGICCHARMED", + [DC.MAGIC] = "MAGIC", + [DC.CURSE] = "CURSE", + [DC.POISON] = "POISON", + [DC.DISEASE] = "DISEASE", + [DC.CHARMED] = "CHARM", +} + +local CureCheckBoxes = false; +function D:SetCureCheckBoxNum (Type) + local CheckBox = CureCheckBoxes[Type]; + + -- add the number in green before the name if we have a spell available and if we checked the box + if (D:GetCureCheckBoxStatus(Type)) then + CheckBox.name = D:ColorText(D.classprofile.CureOrder[Type], "FF00FF00") .. " " .. L[TypesToUName[Type]]; + else + CheckBox.name = " " .. L[TypesToUName[Type]]; + end + +end + +function D:CheckCureOrder () + + D:Debug("Verifying CureOrder..."); + + local TempTable = {}; + local AuthorizedKeys = { + [DC.ENEMYMAGIC] = 1, + [DC.MAGIC] = 2, + [DC.CURSE] = 3, + [DC.POISON] = 4, + [DC.DISEASE] = 5, + [DC.CHARMED] = 6, + }; + local AuthorizedValues = { + [false] = true; -- LOL Yes, it's TRUE tnat FALSE is an authorized value xD + -- Other <0 values are used when there used to be a spell... + [1] = DC.ENEMYMAGIC, + [-11] = DC.ENEMYMAGIC, + [2] = DC.MAGIC, + [-12] = DC.MAGIC, + [3] = DC.CURSE, + [-13] = DC.CURSE, + [4] = DC.POISON, + [-14] = DC.POISON, + [5] = DC.DISEASE, + [-15] = DC.DISEASE, + [6] = DC.CHARMED, + [-16] = DC.CHARMED, + }; + local GivenValues = {}; + + + -- add missing entries... + for key, value in pairs(AuthorizedKeys) do + if not D.classprofile.CureOrder[key] then + D.classprofile.CureOrder[key] = false; + end + end + + -- Validate existing entries + local WrongValue = 0; + for key, value in pairs(D.classprofile.CureOrder) do + + if (AuthorizedKeys[key]) then -- is this a correct type ? + if (AuthorizedValues[value] and not GivenValues[value]) then -- is this value authorized and not already given? + GivenValues[value] = true; + + elseif (value) then -- FALSE is the only value that can be given several times + D:Debug("Incoherent value for (key, value, Duplicate?)", key, value, GivenValues[value]); + + D.classprofile.CureOrder[key] = -20 - WrongValue; -- if the value was wrong or already given to another type + WrongValue = WrongValue + 1; + end + else + D.classprofile.CureOrder[key] = nil; -- remove it from the table + end + end + +end + +function D:SetCureOrder (ToChange) + + if not CureCheckBoxes then + CureCheckBoxes = { + [DC.ENEMYMAGIC] = D.options.args.CureOptions.args.CureEnemyMagic, + [DC.MAGIC] = D.options.args.CureOptions.args.CureMagic, + [DC.CURSE] = D.options.args.CureOptions.args.CureCurse, + [DC.POISON] = D.options.args.CureOptions.args.CurePoison, + [DC.DISEASE] = D.options.args.CureOptions.args.CureDisease, + [DC.CHARMED] = D.options.args.CureOptions.args.CureCharmed, + } + end + + local CureOrder = D.classprofile.CureOrder; + local tmpTable = {}; + D:Debug("SetCureOrder called for prio ", CureOrder[ToChange]); + + if (ToChange) then + -- if there is a positive value, it means we want to disable this type, set it to false (see GetCureCheckBoxStatus()) + if (D:GetCureCheckBoxStatus(ToChange)) then + CureOrder[ToChange] = false; + D:Debug("SetCureOrder(): set to false"); + else -- else if there was no value (or a negative one), add this type at the end (see GetCureCheckBoxStatus()) + CureOrder[ToChange] = 20; -- this will cause the spell to be added at the end + D:Debug("SetCureOrder(): set to 20"); + end + end + + local LostSpells = {}; -- an orphanage for the lost spells :'( + local FoundSpell = 0; -- we wouldn't need that if #table was always returning something meaningful... + + -- re-compute the position of each spell type + for Type, Num in pairs (CureOrder) do + + -- if we have a spell or if we did not unchecked the checkbox (note the difference between "checked" and "not unchecked") + if (D.Status.CuringSpells[Type] and CureOrder[Type]) then + tmpTable[abs(CureOrder[Type])] = Type; -- CureOrder[Type] can have a <0 value if the spell was lost + FoundSpell = FoundSpell + 1; + elseif (CureOrder[Type]) then -- if we don't have a spell for this type + LostSpells[abs(CureOrder[Type])] = Type; -- save the position + end + end + + -- take care of the lost spells here + -- Sort the lost spells so that they can be readded in the correct order + LostSpells = D:tSortUsingKeys(LostSpells); + + -- Place the lost spells after the found ones but with <0 values so they + -- can be readded later using their former priorities + local AvailableSpot = (FoundSpell + 10 + 1) * -1; -- we add 10 so that they'll be re-added after any not-lost spell... + + -- D:PrintLiteral(LostSpells); + for FormerPrio, Type in ipairs(LostSpells) do + CureOrder[Type] = AvailableSpot + AvailableSpot = AvailableSpot - 1; + end + + -- we sort the tables + tmpTable = D:tSortUsingKeys(tmpTable); + + -- apply the new priority to the types we can handle, leave their negative value to the others + for Num, Type in ipairs (tmpTable) do + CureOrder[Type] = Num; + end + + -- create / update the ReversedCureOrder table (prio => type, ..., ) + D.Status.ReversedCureOrder = D:tReverse(CureOrder); + + for Type, CheckBox in pairs(CureCheckBoxes) do + D:SetCureCheckBoxNum(Type); + end + + + -- Create spell priority table + D.Status.CuringSpellsPrio = {}; + + -- some shortcuts + local CuringSpellsPrio = D.Status.CuringSpellsPrio; + local ReversedCureOrder = D.Status.ReversedCureOrder; + local CuringSpells = D.Status.CuringSpells; + + local DebuffType; + -- set the priority for each spell, Micro frames will use this to determine which button to map + local affected = 1; + for i=1,6 do + DebuffType = ReversedCureOrder[i]; -- there is no gap between indexes + if (DebuffType and not CuringSpellsPrio[ CuringSpells[DebuffType] ] ) then + CuringSpellsPrio[ CuringSpells[DebuffType] ] = affected; + affected = affected + 1; + end + end + + -- Set the spells shortcut (former decurse key) + D:UpdateMacro(); + D:Debug("Spell changed"); + D.Status.SpellsChanged = GetTime(); + + -- If no spell is selected or none is available set Decursive icon to off + if FoundSpell ~= 0 then + D:Debug("icon changed to ON"); + D:SetIcon(DC.IconON); + else + D:Debug("icon changed to OFF"); + D:SetIcon(DC.IconOFF); + end + + + +end + +function D:ShowHideDebuffsFrame () + + if InCombatLockdown() or not D.DcrFullyInitialized then + return + end + + D.profile.ShowDebuffsFrame = not D.profile.ShowDebuffsFrame; + + if (D.MFContainer:IsVisible()) then + D.MFContainer:Hide(); + D.profile.ShowDebuffsFrame = false; + else + D.MFContainer:Show(); + D.MFContainer:SetScale(D.profile.DebuffsFrameElemScale); + D.MicroUnitF:Place (); + D.profile.ShowDebuffsFrame = true; + D.MicroUnitF:Delayed_MFsDisplay_Update (); + + --[=[ + local i = 0; + for Unit, MF in pairs(D.MicroUnitF.ExistingPerUNIT) do -- XXX what the fuck is this ?!? + if MF.IsDebuffed and MF.Shown then + D:ScheduleDelayedCall("Dcr_updMUF"..i, D.DummyDebuff, i * (D.profile.ScanTime / 2), D, MF.CurrUnit, "Test item"); + i = i + 1; + end + end + --]=] + end + + if (not D.profile.ShowDebuffsFrame) then + D:CancelDelayedCall("Dcr_MUFupdate"); + else + D:ScheduleRepeatedCall("Dcr_MUFupdate", D.DebuffsFrame_Update, D.profile.DebuffsFrameRefreshRate, D); + end + + -- set Icon + if not D.Status.HasSpell or D.profile.Hide_LiveList and not D.profile.ShowDebuffsFrame then + D:SetIcon(DC.IconOFF); + else + D:SetIcon(DC.IconON); + end + +end + +function D:ShowHideTextAnchor() --{{{ + if (DecursiveAnchor:IsVisible()) then + DecursiveAnchor:Hide(); + else + DecursiveAnchor:Show(); + end +end --}}} + +function D:ChangeTextFrameDirection(bottom) --{{{ + local button = DecursiveAnchorDirection; + if (bottom) then + DecursiveTextFrame:SetInsertMode("BOTTOM"); + button:SetText("v"); + else + DecursiveTextFrame:SetInsertMode("TOP"); + button:SetText("^"); + end +end --}}} + +do -- All this block predates Ace3, it could be recoded in a much more effecicent and cleaner way now (memory POV) thanks to the "info" table given to all callbacks in Ace3. + -- A good example would be the code creating the MUF color configuration menu or the click assigment settings right after this block. + + local DebuffsSkipList, DefaultDebuffsSkipList, skipByClass, AlwaysSkipList, DefaultSkipByClass; + + local spacer = function(num) return { name="",type="header", order = 100 + num } end; + + local RemoveFunc = function (handler) + D:Debug("Removing '%s'...", handler["Debuff"]); + + + D:tremovebyval(D.profile.DebuffsSkipList, handler["Debuff"]) + + skipByClass = D.profile.skipByClass; + for class, debuffs in pairs (skipByClass) do + skipByClass[class][handler["Debuff"]] = nil; + end + + D.profile.DebuffAlwaysSkipList[handler["Debuff"]] = nil; -- remove it from the table + + D:Debug("%s removed!", handler["Debuff"]); + D:CreateDropDownFiltersMenu(); + + end + + local AddToAlwaysSkippFunc = function (handler, v) + AlwaysSkipList[handler["Debuff"]] = v; + end + + local ResetFunc = function (handler) + local DebuffName = handler["Debuff"]; + + D:Debug("Resetting '%s'...", handler["Debuff"]); + + skipByClass = D.profile.skipByClass; + for Classe, Debuffs in pairs(skipByClass) do + if (DefaultSkipByClass[Classe][DebuffName]) then + skipByClass[Classe][DebuffName] = true; + else + skipByClass[Classe][DebuffName] = nil; -- Removes it + end + end + end + + local function ClassCheckbox (Class, DebuffName, num) + local CheckedByDefault = false; + + if (DefaultSkipByClass[Class][DebuffName]) then + CheckedByDefault = true; + end + + return { + type = "toggle", + name = D:ColorText( LC[Class], "FF"..DC.HexClassColor[Class]) .. + (CheckedByDefault and D:ColorText(" *", "FFFFAA00") or ""), + desc = str_format(L["OPT_AFFLICTEDBYSKIPPED"], LC[Class], DebuffName) .. + (CheckedByDefault and D:ColorText(L["OPT_DEBCHECKEDBYDEF"], "FFFFAA00") or ""); + handler = { + ["Debuff"]=DebuffName, + ["Class"]=Class, + ["get"] = function (handler) + skipByClass = D.profile.skipByClass; + return skipByClass[handler["Class"]][handler["Debuff"]]; + end, + ["set"] = function (handler, info, v) + skipByClass = D.profile.skipByClass; + skipByClass[handler["Class"]][string.trim(handler["Debuff"])] = v; + end + }, + get = "get", + set = "set", + order = 100 + num; + } + end + + local function ClassValues(DebuffName) + local values = {}; + + for i, class in pairs (DC.ClassNumToUName) do + values[i] = D:ColorText( LC[class], "FF"..DC.HexClassColor[class]) .. + (DefaultSkipByClass[class][DebuffName] and D:ColorText(" *", "FFFFAA00") or ""); + end + + --D:Debug(unpack (values)); + return values; + + end + + local function DebuffSubmenu (DebuffName, num) + local classes = {}; + + classes["header"] = { + type = "description", + name = (L["OPT_FILTEROUTCLASSES_FOR_X"]):format(D:ColorText(DebuffName, "FF77CC33")), + order = 0, + } + + skipByClass = D.profile.skipByClass; + classes[DebuffName] = { + type = "multiselect", + name = "", + --desc = "test desc", + values = ClassValues(DebuffName), + order = num, + get = "get", + set = "set", + + handler = { + ["Debuff"]=DebuffName, + ["get"] = function (handler, info, Classnum) + return skipByClass[DC.ClassNumToUName[Classnum]][handler["Debuff"]]; + end, + ["set"] = function (handler, info, Classnum, state) + skipByClass[DC.ClassNumToUName[Classnum]][string.trim(handler["Debuff"])] = state; + end + }; + + }; + + --classes["spacer1"] = spacer(num); + + num = num + 1; + + classes["PermIgnore"] = { + type = "toggle", + name = D:ColorText(L["OPT_ALWAYSIGNORE"], "FFFF9900"), + desc = str_format(L["OPT_ALWAYSIGNORE_DESC"], DebuffName), + handler = { + ["Debuff"] = DebuffName, + ["get"] = function (handler) + return AlwaysSkipList[handler["Debuff"]]; + end, + ["set"] = function (handler,info,v) AddToAlwaysSkippFunc(handler,v) end, + }, + get = "get", + set = "set", + order = 100 + num; + + }; + + num = num + 1; + + --classes["spacer1p5"] = spacer(num); + + num = num + 1; + + classes["remove"] = { + type = "execute", + name = D:ColorText(L["OPT_REMOVETHISDEBUFF"], "FFFF0000"), + desc = str_format(L["OPT_REMOVETHISDEBUFF_DESC"], DebuffName), + handler = { + ["Debuff"] = DebuffName, + ["remove"] = RemoveFunc, + }, + confirm = true, + func = "remove", + order = 100 + num, + + }; + + num = num + 1; + + --classes["spacer2"] = spacer(num); + + num = num + 1; + + local resetDisabled = false; + + if (not D:tcheckforval(DefaultDebuffsSkipList, DebuffName)) then + resetDisabled = true; + end + + classes["reset"] = { + type = "execute", + -- the two statements below are like (()?:) in C + name = not resetDisabled and D:ColorText(L["OPT_RESETDEBUFF"], "FF11FF00") or L["OPT_RESETDEBUFF"], + desc = not resetDisabled and str_format(L["OPT_RESETDTDCRDEFAULT"], DebuffName) or L["OPT_USERDEBUFF"], + handler = { + ["Debuff"] = DebuffName, + ["reset"] = ResetFunc, + }, + func = "reset"; + disabled = resetDisabled, + order = 100 + num; + + }; + + num = num + 1; + + --classes["spacer3"] = spacer(num); + + return classes; + end + + + + --Entry Templates + local function DebuffEntryGroup (DebuffName, num) + local IsADefault = D:tcheckforval(DefaultDebuffsSkipList, DebuffName); + return { + type = "group", + name = IsADefault and D:ColorText(DebuffName, "FFFFFFFF") or D:ColorText(DebuffName, "FF99FFFF"), + desc = L["OPT_DEBUFFENTRY_DESC"], + order = num, + args = DebuffSubmenu(DebuffName, num), + } + end + + local AddFunc = function (NewDebuff) + if (not D:tcheckforval(DebuffsSkipList, NewDebuff)) then + table.insert(DebuffsSkipList, strtrim(NewDebuff)); + D:CreateDropDownFiltersMenu(); + D:Debug("'%s' added to debuff skip list", strtrim(NewDebuff)); + end + end + + + local ReAddDefaultsDebuffs = function () + + for _, Debuff in ipairs(DefaultDebuffsSkipList) do + + if (not D:tcheckforval(DebuffsSkipList, Debuff)) then + + table.insert(DebuffsSkipList, Debuff); + + ResetFunc({["Debuff"] = Debuff}); + + end + end + + D:CreateDropDownFiltersMenu(); + end + + local CheckDefaultsPresence = function () + for _, Debuff in ipairs(DefaultDebuffsSkipList) do + if (not D:tcheckforval(DebuffsSkipList, Debuff)) then + + return false; + end + end + return true; + end + + local DebuffHistTable = {}; + local First = ""; + + local GetHistoryDebuff = function () + local DebuffName, exists, index; + + for index=1, DC.DebuffHistoryLength do + DebuffName, exists = D:Debuff_History_Get (index, true); + + if not exists or index == 1 and DebuffName == First then + break; + end + + if index == 1 then + First = DebuffName; + end + + DebuffHistTable[index] = DebuffName; + index = index + 1; + end + + return DebuffHistTable; + end + + function D:CreateDropDownFiltersMenu() + DebuffsSkipList = D.profile.DebuffsSkipList; + DefaultDebuffsSkipList = D.defaults.profile.DebuffsSkipList; + + skipByClass = D.profile.skipByClass; + AlwaysSkipList = D.profile.DebuffAlwaysSkipList; + DefaultSkipByClass = D.defaults.profile.skipByClass; + + local DebuffsSubMenu = {}; + local num = 1; + + + for _, Debuff in ipairs(DebuffsSkipList) do + DebuffsSubMenu[str_gsub(Debuff, " ", "")] = DebuffEntryGroup(Debuff, num); + num = num + 1; + end + + DebuffsSubMenu["description"] = { + type = "description", + name = L["OPT_DEBUFFFILTER_DESC"], + order = 0, + }; + num = num + 1; + + DebuffsSubMenu["add"] = { + type = "input", + name = D:ColorText(L["OPT_ADDDEBUFF"], "FFFF3300"), + desc = L["OPT_ADDDEBUFF_DESC"], + usage = L["OPT_ADDDEBUFF_USAGE"], + get = false, + set = function(info,value) AddFunc(value) end, + order = 100 + num, + }; + + num = num + 1; + + DebuffsSubMenu["addFromHist"] = { + type = "select", + name = L["OPT_ADDDEBUFFFHIST"], --"Add from Debuff history", + desc = L["OPT_ADDDEBUFFFHIST_DESC"], --"Add a recently seen debuff", + disabled = function () GetHistoryDebuff(); return (#DebuffHistTable == 0) end, + values = GetHistoryDebuff; + get = function() GetHistoryDebuff(); return false; end, + set = function(info,value) + AddFunc(D:RemoveColor(GetHistoryDebuff()[value])); end, + order = 100 + num, + --validate = DebuffHistTable, --GetHistoryDebuff(), + }; + + + local ReaddIsDisabled = CheckDefaultsPresence(); + num = num + 1; + DebuffsSubMenu["ReAddDefaults"] = { + type = "execute", + name = not ReaddIsDisabled and D:ColorText(L["OPT_READDDEFAULTSD"], "FFA75728") or L["OPT_READDDEFAULTSD"], + desc = not ReaddIsDisabled and L["OPT_READDDEFAULTSD_DESC1"] + or L["OPT_READDDEFAULTSD_DESC2"], + func = ReAddDefaultsDebuffs, + disabled = CheckDefaultsPresence; + order = 100 + num, + }; + + table.wipe(D.options.args.DebuffSkip.args); + D.options.args.DebuffSkip.args = DebuffsSubMenu; + + LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name); + end +end + +do + + local tonumber = _G.tonumber; + local L_MF_colors = {}; + + local function GetNameAndDesc (ColorReason) -- {{{ + local name, desc; + + L_MF_colors = D.profile.MF_colors; + + if (type(ColorReason) == "number" and ColorReason <= 6) then + + name = D:ColorText(DC.AvailableButtonsReadable[D.db.global.AvailableButtons[ColorReason] ], D:NumToHexColor(L_MF_colors[ColorReason])); + desc = (L["COLORALERT"]):format(DC.AvailableButtonsReadable[D.db.global.AvailableButtons[ColorReason] ]); + + elseif (type(ColorReason) == "number") then + local Text = ""; + + if (ColorReason == DC.NORMAL) then + Text = L["NORMAL"]; + + elseif (ColorReason == DC.ABSENT) then + Text = L["MISSINGUNIT"]; + + elseif (ColorReason == DC.FAR) then + Text = L["TOOFAR"]; + + elseif (ColorReason == DC.STEALTHED) then + Text = L["STEALTHED"]; + + elseif (ColorReason == DC.BLACKLISTED) then + Text = L["BLACKLISTED"]; + + elseif (ColorReason == DC.CHARMED_STATUS) then + Text = L["CHARM"]; + end + + name = ("%s %s"):format(L["UNITSTATUS"], D:ColorText(Text, D:NumToHexColor(L_MF_colors[ColorReason])) ); + desc = (L["COLORSTATUS"]):format(Text); + + elseif (type(ColorReason) == "string") then + + name = L[ColorReason]; + + if ColorReason == "COLORCHRONOS" then + desc = L["COLORCHRONOS_DESC"]; + else + desc = "This is abnormal!"; + end + end + + return {name, desc}; + end -- }}} + + local retrieveColorReason = function(info) + local ColorReason = str_sub(info[#info], 2); + + if tonumber(ColorReason) then + return tonumber(ColorReason); + else + return ColorReason; + end + end + + local GetName = function (info) + return GetNameAndDesc(retrieveColorReason(info))[1]; + end + + local GetDesc = function (info) + return GetNameAndDesc(retrieveColorReason(info))[2]; + end + + local GetOrder = function (info) + local ColorReason = retrieveColorReason(info); + return 100 + (type(ColorReason) == "number" and ColorReason or 2048); + end + + local function GetColor (info) + return unpack(D.profile.MF_colors[retrieveColorReason(info)]); + end + + local function SetColor (info, r, g, b, a) + + local ColorReason = retrieveColorReason(info); + + D.profile.MF_colors[ColorReason] = {r, g, b, (a and a or 1)}; + D.MicroUnitF:RegisterMUFcolors(); + L_MF_colors = D.profile.MF_colors; + + D.MicroUnitF:Delayed_Force_FullUpdate(); + + D:Debug("MUF color setting %d changed.", ColorReason); + end + + local ColorPicker = { + type = "color", + name = GetName, + desc = GetDesc, + hasAlpha = true, + order = GetOrder, + + get = GetColor, + set = SetColor, + }; + + function D:CreateDropDownMUFcolorsMenu() + L_MF_colors = D.profile.MF_colors; + + local MUFsColorsSubMenu = {}; + + for ColorReason, Color in pairs(L_MF_colors) do + + if not L_MF_colors[ColorReason][4] then + D.profile.MF_colors[ColorReason][4] = 1; + end + + -- add a separator for the different color typs when necessary. + if (type(ColorReason) == "number" and (ColorReason - 2) == 6) or (type(ColorReason) == "string" and ColorReason == "COLORCHRONOS") then + MUFsColorsSubMenu["S" .. ColorReason] = { + type = "header", + name = "", + order = function (info) return GetOrder(info) - 1 end, + } + --D:Debug("Created space ", "Space" .. ColorReason, "at ", MUFsColorsSubMenu["S" .. ColorReason].order); + end + + + MUFsColorsSubMenu["c"..ColorReason] = ColorPicker; + + end + + D.options.args.MicroFrameOpt.args.MUFsColors.args = MUFsColorsSubMenu; + end +end + +-- Modifiers order choosing dynamic menu creation +do + + local orderStart = 152; + local tonumber = _G.tonumber; + + local TempTable = {}; + local i = 1; + + local function retrieveKeyComboNum (info) + return tonumber(str_sub(info[#info], 9)); + -- #"KeyCombo" == 8 + end + + local function GetValues (info) -- {{{ + + if retrieveKeyComboNum (info) == 1 then + table.wipe(TempTable); + + for i=1, #D.db.global.AvailableButtons do + TempTable[i] = D:ColorText(DC.AvailableButtonsReadable[D.db.global.AvailableButtons[i]], + i < 7 and D:NumToHexColor(D.profile.MF_colors[i]) -- defined priorities + or (i >= #D.db.global.AvailableButtons - 1 and "FFFFFFFF" -- target and focus + or "FFBBBBBB") -- other unused buttons + ); + end + end + + return TempTable; + end -- }}} + + local function GetOrder (info) + return orderStart + retrieveKeyComboNum (info); + end + + local OptionPrototype = { + -- {{{ + type = "select", + name = function (info) + if retrieveKeyComboNum (info) < #D.db.global.AvailableButtons - 1 then + return ""; + elseif retrieveKeyComboNum (info) == #D.db.global.AvailableButtons - 1 then + return L["OPT_MUFTARGETBUTTON"]; + else + return L["OPT_MUFFOCUSBUTTON"]; + end + end, + values = GetValues, + order = GetOrder, + get = function (info) + return retrieveKeyComboNum (info); + end, + set = function (info, value) + + local ThisKeyComboNum = retrieveKeyComboNum (info); + + + if value ~= ThisKeyComboNum then -- we would destroy the table + + D:tSwap(D.db.global.AvailableButtons, ThisKeyComboNum, value); + + -- force all MUFs to update their attributes + D.Status.SpellsChanged = GetTime(); + end + end, + style = "dropdown", + -- }}} + }; + + function D:CreateModifierOptionMenu () + for i = 1, 6 do + D.options.args.MicroFrameOpt.args.MUFsMouseButtons.args["KeyCombo" .. i] = OptionPrototype; + end + + -- create choice munu for targeting (it's always the last but one available button) + D.options.args.MicroFrameOpt.args.MUFsMouseButtons.args["KeyCombo" .. #D.db.global.AvailableButtons - 1] = OptionPrototype; + -- create choice munu for focusing (it's always the last available button) + D.options.args.MicroFrameOpt.args.MUFsMouseButtons.args["KeyCombo" .. #D.db.global.AvailableButtons] = OptionPrototype; + end + +end + +-- to test on 2.3 : /script D:PrintLiteral(GetBindingAction(D.db.global.MacroBind)); +-- to test on 2.3 : /script D:PrintLiteral(GetBindingKey(D.CONF.MACROCOMMAND)); + +function D:SetMacroKey ( key ) + + -- if the key is already correctly mapped, return here. + --if (key and key == D.db.global.MacroBind and GetBindingAction(key) == D.CONF.MACROCOMMAND) then + if D.profile.DisableMacroCreation or key and key == D.db.global.MacroBind and D:tcheckforval({GetBindingKey(D.CONF.MACROCOMMAND)}, key) then -- change for 2.3 where GetBindingAction() is no longer working + return; + end + + -- if the current set key is currently mapped to Decursive macro (it means we are changing the key) + --if (D.profile.MacroBind and GetBindingAction(D.profile.MacroBind) == D.CONF.MACROCOMMAND) then + if (D.db.global.MacroBind and D:tcheckforval({GetBindingKey(D.CONF.MACROCOMMAND)}, D.db.global.MacroBind) ) then -- change for 2.3 where GetBindingAction() is no longer working + + -- clearing redudent mapping to Decursive macro. + local MappedKeys = {GetBindingKey(D.CONF.MACROCOMMAND)}; + for _, key in pairs(MappedKeys) do + D:Debug("Unlinking [%s]", key); + SetBinding(key, nil); -- clear the binding + end + + -- Restore previous key state + if (D.profile.PreviousMacroKeyAction) then + D:Debug("Previous key action restored:", D.profile.PreviousMacroKeyAction); + if not SetBinding(D.db.global.MacroBind, D.profile.PreviousMacroKeyAction) then + -- /script SetBinding ("BUTTON1", "CAMERAORSELECTORMOVE"); to communicate to people who accidently set BUUTON1 to our macro. + D:Debug("Restoration failed"); + end + end + end + + + if (key) then + if (GetBindingAction(key) ~= "" and GetBindingAction(key) ~= D.CONF.MACROCOMMAND) then + -- save current key assignement + D.profile.PreviousMacroKeyAction = GetBindingAction(key) + D:Debug("Old key action saved:", D.profile.PreviousMacroKeyAction); + D:errln(L["MACROKEYALREADYMAPPED"], key, D.profile.PreviousMacroKeyAction); + else + D.profile.PreviousMacroKeyAction = false; + D:Debug("Old key action not saved because it was mapped to nothing"); + end + + -- set + if (SetBindingMacro(key, D.CONF.MACRONAME)) then + D.db.global.MacroBind = key; + D:Println(L["MACROKEYMAPPINGSUCCESS"], key); + else + D:errln(L["MACROKEYMAPPINGFAILED"], key); + end + else + D.db.global.MacroBind = false; + if D.profile.NoKeyWarn and not GetBindingKey(D.CONF.MACROCOMMAND) then + D:errln(L["MACROKEYNOTMAPPED"]); + end + end + + -- save the bindings to disk + if GetCurrentBindingSet()==1 or GetCurrentBindingSet()==2 then -- GetCurrentBindingSet() may return strange values when the game is loaded without WTF folder. + SaveBindings(GetCurrentBindingSet()); + end + +end + + +function D:AutoHideShowMUFs () + + -- This function cannot do anything if we are fighting + if (InCombatLockdown()) then + -- if we are fighting, postpone the call + D:AddDelayedFunctionCall ( + "CheckIfHideShow", self.AutoHideShowMUFs, + self); + return false; + end + + if D.profile.AutoHideDebuffsFrame == 0 then + return; + else + -- if we want to hide the MUFs when in solo or not in raid + local InGroup = (GetNumRaidMembers() ~= 0 or (D.profile.AutoHideDebuffsFrame ~= 2 and GetNumPartyMembers() ~= 0) ); + D:Debug("AutoHideShowMUFs, InGroup: ", InGroup); + + -- if we are not in such a group + if not InGroup then + -- if the frame is displayed + if D.profile.ShowDebuffsFrame then + -- hide it + D:ShowHideDebuffsFrame (); + end + else + -- if we are in a group + -- if the frame is not displayed + if not D.profile.ShowDebuffsFrame then + -- show it + D:ShowHideDebuffsFrame (); + end + end + end +end + +function D:QuickAccess (CallingObject, button) -- {{{ + --D:Debug("clicked"); + + if (not CallingObject) then + CallingObject = "noframe"; + end + + if (button == "RightButton" and not IsShiftKeyDown()) then + + if (not IsAltKeyDown()) then + D:Println(L["DEWDROPISGONE"]); + else + LibStub("AceConfigDialog-3.0"):Open(D.name); + end + + elseif (button == "RightButton" and IsShiftKeyDown()) then + D:HideBar(); + elseif (button == "LeftButton" and IsControlKeyDown()) then + D:ShowHidePriorityListUI(); + elseif (button == "LeftButton" and IsShiftKeyDown()) then + D:ShowHideSkipListUI(); + end + +end -- }}} + + +local DebugHeader = false; +function D:ShowDebugReport() + +--[[ if DC.DevVersionExpired then + D:BetaWarning(); + return; + end ]] + + D:Debug(GetLocale()); + + if not DebugHeader then + DebugHeader = ("%s\n2.5.1-6-gd3885c5 %s CT: %0.4f D: %s %s (%s, %s, %s, %s)"):format((Dcr and Dcr.L) and Dcr.L["DEBUG_REPORT_HEADER"] or "X|cFF11FF33Please report the content of this window to Archarodim@teaser.fr|r\n|cFF009999(Use CTRL+A to select all and then CTRL+C to put the text in your clip-board)|r\n", DC.MyClass, D:NiceTime(), date(), GetLocale(), GetBuildInfo()); + end + + T._DebugText = DebugHeader .. table.concat(T._DebugTextTable, ""); + _G.DecursiveDebuggingFrameText:SetText(T._DebugText); + + _G.DecursiveDEBUGtext:SetText(L["DECURSIVE_DEBUG_REPORT"]); + _G.DecursiveDebuggingFrame:Show(); +end + +T._LoadedFiles["Dcr_opt.lua"] = "2.5.1-6-gd3885c5"; + +-- Closer diff --git a/Decursive/Dcr_utils.lua b/Decursive/Dcr_utils.lua new file mode 100644 index 0000000..47ccdf9 --- /dev/null +++ b/Decursive/Dcr_utils.lua @@ -0,0 +1,525 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Dcr_LDB.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_LDB.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-09-16 00:48:59 +0200 (mar., 16 sept. 2008) $", "$Revision: 81756 $"); + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; + +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local type = _G.type; +local unpack = _G.unpack; +local select = _G.select; +local str_sub = _G.string.sub; +local str_upper = _G.string.upper; +local str_lower = _G.string.lower; +local str_format = _G.string.format; +local table = _G.table; +local t_remove = _G.table.remove; +local t_insert = _G.table.insert; +local UnitName = _G.UnitName; +local UnitIsPlayer = _G.UnitIsPlayer; +local string = _G.string; +local tonumber = _G.tonumber; +local UnitGUID = _G.UnitGUID; +local band = _G.bit.band; +local GetTime = _G.GetTime; + + +function D:ColorText (text, color) --{{{ + return "|c".. color .. text .. "|r"; +end --}}} + +function D:RemoveColor (text) + return str_sub(text, 11, -3); +end + +function D:MakePlayerName (name) --{{{ + if not name then name = "NONAME" end + return "|cFFFFAA22|Hplayer:" .. name .. "|h" .. str_upper(name) .. "|h|r"; +end --}}} + +function D:UnitIsPet (Unit) + local GUID = UnitGUID(Unit); + + if not GUID then return end + + if band(tonumber(GUID:sub(0,5), 16), 0x00f)==0x004 then + return true; + end + return false; + +end + +function D:PetUnitName (Unit, Check) -- {{{ + local Name = (self:UnitName(Unit)); + + if not Name or Name == DC.UNKNOWN then + Name = DC.UNKNOWN .. "-" .. Unit; + D:Debug("PetUnitName(): Name of %s is unknown", Unit); + end + + if not Check or (self:UnitIsPet(Unit)) then + Name = ("%s-%s"):format (DC.PET,Name); + end + + return Name; + +end -- }}} + +function D:UnitName(Unit) + local name, server = UnitName(Unit); + if ( server and server ~= "" ) then + return name.."-"..server; + else + return name; + end +end + +local function isFormattedString(string) + return type(string)=='string' and (string:find("%%[cdEefgGiouXxsq]")) or false; +end + +local function UseFormatIfPresent(...) + if not isFormattedString((select(1,...))) then + return ...; + else + return (select(1,...)):format(select(2, ...)); + end +end + +function D:NumToHexStr(number) + if type(number) == 'number' then + return ("%X"):format(number); + else + return tostring(number); + end +end + +Dcr.UseFormatIfPresent = UseFormatIfPresent; + +local function debugStyle(...) + return "|cFF00AAAADebug:|r", ...; +end + +function D:Println( ... ) --{{{ + + if D.profile.Print_ChatFrame then + self:Print(D.Status.OutputWindow, UseFormatIfPresent(...)); + end + if D.profile.Print_CustomFrame then + self:Print(DecursiveTextFrame, UseFormatIfPresent(...)); + end +end --}}} + +function D:ColorPrint (r,g,b, ... ) --XXX + + local datas = {UseFormatIfPresent(...)}; + + local ColorHeader = ("|cff%02x%02x%02x"):format(r * 255, g * 255, b * 255); + + t_insert(datas, 1, ColorHeader); + t_insert(datas, #datas + 1, "|r"); + + if D.profile.Print_ChatFrame then + self:Print(D.Status.OutputWindow, ColorHeader, unpack(datas)); + end + + if D.profile.Print_CustomFrame then + self:Print(DecursiveTextFrame, ColorHeader, unpack(datas)); + end + + if not Dcr.db then + self:Print(ColorHeader, unpack(datas)); + end + +end + +function D:errln( ... ) --{{{ + if not D.db or D.profile.Print_Error then + self:ColorPrint(1,0,0,...); + + end +end --}}} + + +function D:Debug(...) + if self.debugging then + self:Print(debugStyle(UseFormatIfPresent(...))); + end +end + + +function D:tremovebyval(tab, val) -- {{{ + local k; + local v; + for k,v in pairs(tab) do + if(v==val) then + t_remove(tab, k); + return true; + end + end + return false; +end -- }}} + +function D:tcheckforval(tab, val) -- {{{ + local k; + local v; + if tab then + for k,v in pairs(tab) do + if v==val then + return true; + end + end + end + return false; +end -- }}} + +-- tcopy: recursively copy contents of one table to another +function D:tcopy(to, from) -- "to" must be a table (possibly empty) + if (type(from) ~= "table") then + return error(("D:tcopy: bad argument #2 'from' must be a table, got '%s' instead"):format(type(from)),2); + end + + if (type(to) ~= "table") then + return error(("D:tcopy: bad argument #1 'to' must be a table, got '%s' instead"):format(type(to)),2); + end + for k,v in pairs(from) do + if(type(v)=="table") then + to[k] = {}; -- this generate garbage + D:tcopy(to[k], v); + else + to[k] = v; + end + end +end + + +-- tcopycallback: recursively copy contents of one table to another calling a callback before storing the new values +function D:tcopycallback(to, from, CallBack) -- "to" must be a table (possibly empty) + if (type(from) ~= "table") then + return error(("D:tcopycallback: bad argument #2 'from' must be a table, got '%s' instead"):format(type(from)),2); + end + + if (type(to) ~= "table") then + return error(("D:tcopycallback: bad argument #1 'to' must be a table, got '%s' instead"):format(type(to)),2); + end + if (type(CallBack) ~= "function") then + return error(("D:tcopycallback: bad argument #3 'CallBack' must be a function ref, got '%s' instead"):format(type(CallBack)),2); + end + for k,v in pairs(from) do + if(type(v)=="table") then + to[k] = {}; -- this generate garbage + D:tcopycallback(to[k], v, CallBack); + else + to[k] = CallBack(v); + end + end +end + +function D:tGiveValueIndex(tab, val) + for k,v in pairs(tab) do + if v==val then + return k; + end + end + return false; +end + +function D:tSortUsingKeys(tab) + local SortedTable = {}; + local Keys = {}; + + -- store all the keys in a table + for k,v in pairs(tab) do + t_insert(Keys, k); + end + + -- sort the table + table.sort(Keys); + + -- we now have a sorted table containing the keys + for pos, k in pairs(Keys) do + -- insert the values in a new table using the position of each key + t_insert(SortedTable, pos, tab[k]); + end + + -- we return a new sorted table with new keys but with the same values + return SortedTable; +end + +function D:tReverse(tab) + local ReversedTable = {}; + + for k,v in pairs(tab) do + ReversedTable[v] = k; + end + + return ReversedTable; +end + +function D:Pack(...) + local args = {}; + for i=1,select("#",...), 1 do + args[i]=select(i, ...); + end + return args; +end + +function D:tSwap(t, i1, i2) + + if i1 == i2 then + return false; + end + + local i1c= t[i1]; + local i2c= t[i2]; + + if i1 <= i2 then + t_remove(t, i2) -- remove the greater one first + t_remove(t, i1) + t_insert(t, i1, i2c) -- insert the smaller one first + t_insert(t, i2, i1c) + else + t_remove(t, i1) -- remove the greater one first + t_remove(t, i2) + t_insert(t, i2, i1c) -- insert the smaller one first + t_insert(t, i1, i2c) + end + + return true; +end + + +function D:ThisSetText(text) --{{{ + _G[this:GetName().."Text"]:SetText(text); +end --}}} + +function D:ThisSetParentText(frame, text) --{{{ + _G[this:GetParent():GetName().."Text"]:SetText(text); +end --}}} + +do +local DefaultAnchorTab = {"ANCHOR_LEFT"}; + function D:DisplayTooltip(Message, RelativeTo, AnchorTab) --{{{ + if (not AnchorTab) then + AnchorTab = DefaultAnchorTab; + end + DcrDisplay_Tooltip:SetOwner(RelativeTo, unpack(AnchorTab)); + DcrDisplay_Tooltip:ClearLines(); + DcrDisplay_Tooltip:SetText(Message); + DcrDisplay_Tooltip:Show(); + end --}}} +end + +function D:DisplayGameTooltip(frame, Message) --{{{ + GameTooltip_SetDefaultAnchor(GameTooltip, frame); + GameTooltip:SetText(Message); + GameTooltip:Show(); +end --}}} + + + +function D:NumToHexColor(ColorTable) + return str_format("%02x%02x%02x%02x", ColorTable[4] * 255, ColorTable[1] * 255, ColorTable[2] * 255, ColorTable[3] * 255) +end + +-- function taken from http://www.wowwiki.com/SetTexCoord_Transformations +function D:SetCoords(t, A, B, C, D, E, F) + local det = A*E - B*D; + local ULx, ULy, LLx, LLy, URx, URy, LRx, LRy; + + ULx, ULy = ( B*F - C*E ) / det, ( -(A*F) + C*D ) / det; + LLx, LLy = ( -B + B*F - C*E ) / det, ( A - A*F + C*D ) / det; + URx, URy = ( E + B*F - C*E ) / det, ( -D - A*F + C*D ) / det; + LRx, LRy = ( E - B + B*F - C*E ) / det, ( -D + A -(A*F) + C*D ) / det; + + t:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy); +end + +do + + DC.ClassesColors = { }; + + function D:GetClassColor (EnglishClass) + if not DC.ClassesColors[EnglishClass] then + if RAID_CLASS_COLORS and RAID_CLASS_COLORS[EnglishClass] then + DC.ClassesColors[EnglishClass] = { RAID_CLASS_COLORS[EnglishClass].r, RAID_CLASS_COLORS[EnglishClass].g, RAID_CLASS_COLORS[EnglishClass].b }; + else + DC.ClassesColors[EnglishClass] = { 0.63, 0.63, 0.63 }; + end + DC.ClassesColors[LC[EnglishClass]] = DC.ClassesColors[EnglishClass]; + end + return unpack(DC.ClassesColors[EnglishClass]); + end + + DC.HexClassColor = { }; + + function D:GetClassHexColor(EnglishClass) + if not DC.HexClassColor[EnglishClass] then + local r, g, b = self:GetClassColor(EnglishClass) + DC.HexClassColor[EnglishClass] = str_format("%02x%02x%02x", r * 255, g * 255, b * 255); + DC.HexClassColor[LC[EnglishClass]] = DC.HexClassColor[EnglishClass]; + end + + return DC.HexClassColor[EnglishClass]; + end + + + function D:CreateClassColorTables () + if RAID_CLASS_COLORS then + local class, colors; + for class in pairs(RAID_CLASS_COLORS) do + if LC[class] then -- Some badly coded add-ons are modifying RAID_CLASS_COLORS causing multiple problems... + D:GetClassHexColor(class); + D:GetClassColor(class); + else + RAID_CLASS_COLORS[class] = nil; -- Eat that! + --@alpha@ + D:AddDebugText("Strange class found in RAID_CLASS_COLORS:", class); + --@end-alpha@ + print("Decursive: |cFFFF0000Stupid value found in _G.RAID_CLASS_COLORS table|r\nThis will cause many issues (tainting), Decursive will display this message until the culprit add-on is fixed or removed, the Stupid value is: '", class, "'"); + end + end + else + D:AddDebugText("global RAID_CLASS_COLORS does not exist..."); + T._FatalError("global RAID_CLASS_COLORS does not exist..."); + end + end + +end + +function D:MakeError(something) + local testlocal = "test local"; + + testErrorCapturing(testlocal); +end + +function D:NiceTime() + return tonumber(("%.4f"):format(GetTime() - DC.StartTime)); +end + +local DcrTimers = {}; +function D:TimerExixts(RefName) + return DcrTimers[RefName] and DcrTimers[RefName][1] or false; +end + +function D:DelayedCallExixts(RefName) + return DcrTimers[RefName] and DcrTimers[RefName][1] or false; +end + +local ObjectWithArgs = {["obj"]=false, ["arg"]=false,}; +function D:ScheduleDelayedCall(RefName, FunctionRef, Delay, arg1, ...) + + if DcrTimers[RefName] and DcrTimers[RefName][1] then + self:CancelTimer(DcrTimers[RefName][1]); + end + + if not DcrTimers[RefName] then + DcrTimers[RefName] = {}; + end + + if select('#', ...) > 0 then + -- arg table + DcrTimers[RefName][2] = {arg1}; + + local i; + for i = 1, select('#', ...) do + DcrTimers[RefName][2][i + 1] = (select(i, ...)); + end + + DcrTimers[RefName][1] = self:ScheduleTimer ( + function(arg) + FunctionRef(unpack(arg)); + DcrTimers[RefName][1] = false; + end + , Delay, DcrTimers[RefName][2] + ); + else + DcrTimers[RefName][1] = self:ScheduleTimer ( + function(arg) + FunctionRef(arg); + DcrTimers[RefName][1] = false; + end + , Delay, arg1 + ); + end + + return DcrTimers[RefName][1]; +end + +function D:ScheduleRepeatedCall(RefName, FunctionRef, Delay, arg) + if DcrTimers[RefName] and DcrTimers[RefName][1] then + self:CancelTimer(DcrTimers[RefName][1]); + end + + if not DcrTimers[RefName] then + DcrTimers[RefName] = {}; + end + + DcrTimers[RefName][1] = self:ScheduleRepeatingTimer(FunctionRef, Delay, arg); + + return DcrTimers[RefName][1]; +end + +function D:CancelDelayedCall(RefName) + if DcrTimers[RefName] and DcrTimers[RefName][1] then + local cancelHandle = DcrTimers[RefName][1]; + DcrTimers[RefName][1] = false; + return self:CancelTimer(cancelHandle); + end +end + +function D:CancelAllTimedCalls() + for RefName in pairs(DcrTimers) do + self:CancelDelayedCall(RefName); + end +end + +T._LoadedFiles["Dcr_utils.lua"] = "2.5.1-6-gd3885c5"; diff --git a/Decursive/Decursive.lua b/Decursive/Decursive.lua new file mode 100644 index 0000000..98969f9 --- /dev/null +++ b/Decursive/Decursive.lua @@ -0,0 +1,889 @@ +--[[ + This file is part of Decursive. + + Decursive (v 2.5.1-6-gd3885c5) add-on for World of Warcraft UI + Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php ) + + Starting from 2009-10-31 and until said otherwise by its author, Decursive + is no longer free software, all rights are reserved to its author (John Wellesz). + + The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com. + To distribute Decursive through other means a special authorization is required. + + + Decursive is inspired from the original "Decursive v1.9.4" by Quu. + The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + + Decursive is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY. +--]] +------------------------------------------------------------------------------- + +local addonName, T = ...; +-- big ugly scary fatal error message display function {{{ +if not T._FatalError then +-- the beautiful error popup : {{{ - +StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = { + text = "|cFFFF0000Decursive Error:|r\n%s", + button1 = "OK", + OnAccept = function() + return false; + end, + timeout = 0, + whileDead = 1, + hideOnEscape = 1, + showAlert = 1, + }; -- }}} +T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end +end +-- }}} +if not T._LoadedFiles or not T._LoadedFiles["Dcr_Raid.lua"] then + if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_Raid.lua not loaded)"); end; + DecursiveInstallCorrupted = true; + return; +end + +local D = Dcr; +--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $"); + +local L = D.L; +local LC = D.LC; +local DC = DcrC; +local DS = DC.DS; +------------------------------------------------------------------------------- + +local pairs = _G.pairs; +local ipairs = _G.ipairs; +local type = _G.type; +local table = _G.table; +local t_sort = _G.table.sort; +local PlaySoundFile = _G.PlaySoundFile; +local UnitName = _G.UnitName; +local UnitDebuff = _G.UnitDebuff; +local UnitBuff = _G.UnitBuff; +local UnitIsCharmed = _G.UnitIsCharmed; +local UnitCanAttack = _G.UnitCanAttack; +local UnitClass = _G.UnitClass; +local UnitExists = _G.UnitExists; +local GetNetStats = _G.GetNetStats; +local _; + +------------------------------------------------------------------------------- +-- The UI functions {{{ +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- The printing functions {{{ +------------------------------------------------------------------------------- + +function D:Show_Cure_Order() --{{{ + self:Println("printing cure order:"); + for index, unit in ipairs(self.Status.Unit_Array) do + self:Println( unit, " - ", self:MakePlayerName((self:UnitName(unit))) , " Index: ", index); + end +end --}}} + +-- }}} +------------------------------------------------------------------------------- + +-- Show Hide FUNCTIONS -- {{{ + +function D:ShowHideLiveList(hide) --{{{ + + if not D.DcrFullyInitialized then + return; + end + + -- if hide is requested or if hide is not set and the live-list is shown + if (hide==1 or (not hide and DcrLiveList:IsVisible())) then + D.profile.Hide_LiveList = true; + DcrLiveList:Hide(); + D:CancelDelayedCall("Dcr_LLupdate"); + else + D.profile.Hide_LiveList = false; + DcrLiveList:ClearAllPoints(); + DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT"); + DcrLiveList:Show(); + + D:ScheduleRepeatedCall("Dcr_LLupdate", D.LiveList.Update_Display, D.profile.ScanTime, D.LiveList); + end + +end --}}} + +-- This functions hides or shows the "Decursive" bar depending on its current +-- state, it's also able hide/show the live-list if the "tie live-list" option is active +function D:HideBar(hide) --{{{ + + if not D.DcrFullyInitialized then + return; + end + + if (hide==1 or (not hide and DecursiveMainBar:IsVisible())) then + if (D.profile.LiveListTied) then + D:ShowHideLiveList(1); + end + D.profile.Hidden = true; + DecursiveMainBar:Hide(); + else + if (D.profile.LiveListTied) then + D:ShowHideLiveList(0); + end + D.profile.Hidden = false; + DecursiveMainBar:Show(); + end + + if DecursiveMainBar:IsVisible() and DcrLiveList:IsVisible() then + DcrLiveList:ClearAllPoints(); + DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT"); + else + D:ColorPrint(0.3, 0.5, 1, L["SHOW_MSG"]); + end + + LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name); +end --}}} + +function D:ShowHidePriorityListUI() --{{{ + + if not D.DcrFullyInitialized then + return; + end + + if (DecursivePriorityListFrame:IsVisible()) then + DecursivePriorityListFrame:Hide(); + else + DecursivePriorityListFrame:Show(); + end +end --}}} + +function D:ShowHideSkipListUI() --{{{ + + if not D.DcrFullyInitialized then + return; + end + + if (DecursiveSkipListFrame:IsVisible()) then + DecursiveSkipListFrame:Hide(); + else + DecursiveSkipListFrame:Show(); + end +end --}}} + +-- This shows/hides the buttons near the "Decursive" bar +function D:ShowHideButtons(UseCurrentValue) --{{{ + + if not D.DcrFullyInitialized then + return; + end + + if not D.profile then + return; + end + + + local DcrFrame = "DecursiveMainBar"; + local buttons = { + DcrFrame .. "Priority", + DcrFrame .. "Skip", + DcrFrame .. "Hide", + } + + local DCRframeObject = _G[DcrFrame]; + + if (not UseCurrentValue) then + D.profile.HideButtons = (not D.profile.HideButtons); + end + + for _, ButtonName in pairs(buttons) do + local Button = _G[ButtonName]; + + if (D.profile.HideButtons) then + Button:Hide(); + DCRframeObject.isLocked = 1; + else + Button:Show(); + DCRframeObject.isLocked = 0; + end + + end + +end --}}} + +-- }}} + + +-- this resets the location of the windows +function D:ResetWindow() --{{{ + + DecursiveMainBar:ClearAllPoints(); + DecursiveMainBar:SetPoint("CENTER", UIParent); + DecursiveMainBar:Show(); + + DcrLiveList:ClearAllPoints(); + DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT"); + DcrLiveList:Show(); + + DecursivePriorityListFrame:ClearAllPoints(); + DecursivePriorityListFrame:SetPoint("CENTER", UIParent); + + DecursiveSkipListFrame:ClearAllPoints(); + DecursiveSkipListFrame:SetPoint("CENTER", UIParent); + + DecursivePopulateListFrame:ClearAllPoints(); + DecursivePopulateListFrame:SetPoint("CENTER", UIParent); + + D.MFContainer:ClearAllPoints(); + D.MFContainer:SetPoint("CENTER", UIParent, "CENTER", 0, 0); + + DecursiveAnchor:ClearAllPoints(); + DecursiveAnchor:SetPoint("TOP", UIErrorsFrame, "BOTTOM", 0, 0); + +end --}}} + + +function D:PlaySound (UnitID, Caller) --{{{ + if self.profile.PlaySound and not self.Status.SoundPlayed then + local Debuffs = self:UnitCurableDebuffs(UnitID, true); + if Debuffs and Debuffs[1] and Debuffs[1].Type then + + -- good sounds: Sound\\Doodad\\BellTollTribal.wav + -- Sound\\interface\\AuctionWindowOpen.wav + -- Sound\\interface\\AlarmClockWarning3.wav + PlaySoundFile(self.profile.SoundFile); + self:Debug("Sound Played! by %s", Caller); + self.Status.SoundPlayed = true; + else + self.UnitDebuffed[UnitID] = false; + end + end +end --}}} + +-- LIVE-LIST DISPLAY functions {{{ + + + +-- Those set the scalling of the LIVELIST container +-- SACALING FUNCTIONS {{{ +-- Place the LIVELIST container according to its scale +function D:PlaceLL () -- {{{ + local UIScale = UIParent:GetEffectiveScale() + local FrameScale = DecursiveMainBar:GetEffectiveScale(); + local x, y = D.profile.MainBarX, D.profile.MainBarY; + + -- check if the coordinates are correct + if x and y and (x + 10 > UIParent:GetWidth() * UIScale or x < 0 or (-1 * y + 10) > UIParent:GetHeight() * UIScale or y > 0) then + x = false; -- reset to default position + T._FatalError("Decursive's bar position reset to default"); + end + + -- Executed for the very first time, then put it in the top right corner of the screen + if (not x or not y) then + x = (UIParent:GetWidth() * UIScale) / 2; + y = - (UIParent:GetHeight() * UIScale) / 8; + + D.profile.MainBarX = x; + D.profile.MainBarY = y; + end + + -- set to the scaled position + DecursiveMainBar:ClearAllPoints(); + DecursiveMainBar:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x/FrameScale , y/FrameScale); + DcrLiveList:ClearAllPoints(); + DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT"); +end -- }}} + +-- Save the position of the frame without its scale +function D:SaveLLPos () -- {{{ + if self.profile and DecursiveMainBar:IsVisible() then + -- We save the unscalled position (no problem if the sacale is changed behind our back) + self.profile.MainBarX = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetLeft(); + self.profile.MainBarY = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetTop() - UIParent:GetHeight() * UIParent:GetEffectiveScale(); + + + if self.profile.MainBarX < 0 then + self.profile.MainBarX = 0; + end + + if self.profile.MainBarY > 0 then + self.profile.MainBarY = 0; + end + + end +end -- }}} + +-- set the scaling of the LIVELIST container according to the user settings +function D:SetLLScale (NewScale) -- {{{ + + -- save the current position without any scaling + D:SaveLLPos (); + -- Set the new scale + DecursiveMainBar:SetScale(NewScale); + DcrLiveList:SetScale(NewScale); + -- Place the frame adapting its position to the news cale + D:PlaceLL (); + +end -- }}} +-- }}} + + +-- }}} + +-- // }}} +------------------------------------------------------------------------------- + +do + local iterator = 1; + local DebuffHistHashTable = {}; + + function D:Debuff_History_Add( DebuffName, DebuffType ) + + if not DebuffHistHashTable[DebuffName] then + + -- reset iterator if out of boundaries + if iterator > DC.DebuffHistoryLength then + iterator = 1; + end + + -- clean hastable if necessary before adding a new entry + if DebuffHistHashTable[D.DebuffHistory[iterator]] then + DebuffHistHashTable[D.DebuffHistory[iterator]] = nil; + end + + -- Register the name in the HashTable using the debuff type + DebuffHistHashTable[DebuffName] = (DebuffType and DC.NameToTypes[DebuffType] or DC.NOTYPE); + --D:Debug(DebuffName, DebuffHistHashTable[DebuffName]); + + -- Put this debuff in our history + D.DebuffHistory[iterator] = DebuffName; + + -- This is a useless comment + iterator = iterator + 1; + end + + end + + function D:Debuff_History_Get (Index, Colored) + + local HumanIndex = iterator - Index; + + if HumanIndex < 1 then + HumanIndex = HumanIndex + DC.DebuffHistoryLength; + end + + if not D.DebuffHistory[HumanIndex] then + return "|cFF777777Empty|r", false; + end + + if Colored then + --D:Debug(D.DebuffHistory[HumanIndex], DebuffHistHashTable[D.DebuffHistory[HumanIndex]]); + return D:ColorText(D.DebuffHistory[HumanIndex], "FF" .. DC.TypeColors[DebuffHistHashTable[D.DebuffHistory[HumanIndex]]]), true; + else + return D.DebuffHistory[HumanIndex], true; + end + end + +end + +-- Scanning functionalities {{{ +------------------------------------------------------------------------------- + +do + + local Name, rank, Texture, Applications, TypeName, Duration, expirationTime; + local D = _G.Dcr; + local UnitAura = _G.UnitAura; + + -- This function only returns interesting values of UnitDebuff() + local function GetUnitDebuff (Unit, i) --{{{ + + if D.LiveList.TestItemDisplayed and i == 1 and Unit ~= "target" and Unit ~= "mouseover" and UnitExists(Unit) then + D:Debug("|cFFFF0000Setting test debuff for %s (debuff %d)|r", Unit, i); + return "Test item", DC.TypeNames[D.Status.ReversedCureOrder[1]], 2, "Interface\\AddOns\\Decursive\\iconON.tga", D.LiveList.TestItemDisplayed + 70; + end + + --D:Debug("|cFFFF0000Getting debuffs for %s , id = %d|r", Unit, i); + + + -- Name, rank, Texture, Applications, TypeName, duration, expirationTime, unitCaster, isStealable = UnitAura("unit", index or ["name", "rank"][, "filter"]) + + local Name, rank, Texture, Applications, TypeName, Duration, expirationTime = UnitAura(Unit, i, "HARMFUL"); + + --local Name, rank, Texture, Applications, TypeName, Duration = UnitDebuff(Unit, i); + + if Name then + return Name, TypeName, Applications, Texture, expirationTime; + else + return false, false, false, false, false; + end + end --}}} + + -- there is a known maximum number of unit and a known maximum debuffs per unit so lets allocate the memory needed only once. Memory will be allocated when needed and re-used... + local DebuffUnitCache = {}; + + -- Variables are declared outside so that Lua doesn't initialize them at each call + local Name, Type, i, StoredDebuffIndex, CharmFound, IsCharmed; + + local DcrC = DcrC; -- for faster access + + local UnitIsCharmed = _G.UnitIsCharmed; + local UnitCanAttack = _G.UnitCanAttack; + local GetTime = _G.GetTime; + local IsSpellInRange = _G.IsSpellInRange; + + -- This is the core debuff scanning function of Decursive + -- This function does more than just reporting Debuffs. it also detects charmed units + + function D:GetUnitDebuffAll (Unit) --{{{ + + -- create a Debuff table for this unit if there is not already one + if (not DebuffUnitCache[Unit]) then + DebuffUnitCache[Unit] = {}; + end + + -- This is just a shortcut for easier readability + local ThisUnitDebuffs = DebuffUnitCache[Unit]; + + i = 1; -- => to index all debuffs + StoredDebuffIndex = 1; -- => this index only debuffs with a type + CharmFound = false; -- => avoid to find that the unit is charmed again and again... + + + -- test if the unit is mind controlled once + if (UnitIsCharmed(Unit) and (UnitCanAttack("player", Unit) or UnitIsCharmed("player"))) then + IsCharmed = true; + else + IsCharmed = false; + end + + -- iterate all available debuffs + while (true) do + Name, TypeName, Applications, Texture, expirationTime = GetUnitDebuff(Unit, i); + + if not Name then + break; + end + + + -- test for a type (Magic Curse Disease or Poison) + if (TypeName and TypeName ~= "") then + Type = DC.NameToTypes[TypeName]; + else + Type = false; + end + + -- implement the test for DominateMind I HATE stupid exceptions like this one... so many hours lost because of this :/ + --[=[ + if Name == DS["YOGGG_DOMINATE_MIND"] and Type == DC.MAGIC then + + if DC.MyClass == "PALADIN" then + IsCharmed = false; + CharmFound = false; + else + IsCharmed = true; + end + + if DC.MyClass == "PALADIN" or DC.MyClass == "SHAMAN" then + + YoggReport = true; + + D:AddDebugText("|cFFFF9955Decursive Yoggy Debug (try 6):|r", Unit, TypeName, Applications, + "PC:",DC.MyClass,"IsOvering:", self.Status.MouseOveringMUF, "DN",i, "SDi",StoredDebuffIndex, + "UICp:",UnitIsCharmed( "player"), "UICu:",UnitIsCharmed(Unit), + "UCApu:",UnitCanAttack("player", Unit), "IsCharmed:", IsCharmed + -- "UCAup:",UnitCanAttack(Unit, "player"), "UCApu:",UnitCanAttack("player", Unit), "UIFpu:",UnitIsFriend( "player", Unit), "UIEpu:",UnitIsEnemy( "player", Unit) + ); + end + + end + --]=] + + -- if the unit is charmed and we didn't took care of this information yet + if IsCharmed and (not CharmFound or Type == DC.MAGIC) then + -- If the unit has a magical debuff and we can cure it + -- (note that the target is not friendly in that case) + if (Type == DC.MAGIC and self.Status.CuringSpells[DC.ENEMYMAGIC]) then + Type = DC.ENEMYMAGIC; + + -- NOTE: if a unit is charmed and has another magical debuff + -- this block will be executed... + else -- the unit doesn't have a magical debuff or we can't remove magical debuffs + Type = DC.CHARMED; -- The player can't remove it anyway so just say the unit is afflicted by a charming effect + TypeName = DC.TypeNames[DC.CHARMED]; + end + CharmFound = true; + end + + --[=[ + if YoggReport then + local IsInRange; + if self.Status.CuringSpells[Type] then + IsInRange = IsSpellInRange(self.Status.CuringSpells[Type], Unit) + end + D:AddDebugText("CharmFound:", CharmFound, "TN:", DC.TypeNames[Type], "ISIR", IsInRange); + end + --]=] + + + -- If we found a type, register the Debuff + if (Type) then + -- Create a Debuff index entry if necessary + if (not ThisUnitDebuffs[StoredDebuffIndex]) then + ThisUnitDebuffs[StoredDebuffIndex] = {}; + end + + ThisUnitDebuffs[StoredDebuffIndex].expirationTime = expirationTime; + ThisUnitDebuffs[StoredDebuffIndex].Texture = Texture; + ThisUnitDebuffs[StoredDebuffIndex].Applications = Applications; + ThisUnitDebuffs[StoredDebuffIndex].TypeName = TypeName; + ThisUnitDebuffs[StoredDebuffIndex].Type = Type; + ThisUnitDebuffs[StoredDebuffIndex].Name = Name; + ThisUnitDebuffs[StoredDebuffIndex].index = i; + + -- we can't use i, else we wouldn't have contiguous indexes in the table + StoredDebuffIndex = StoredDebuffIndex + 1; + end + + i = i + 1; + end + + -- erase remaining unused entries without freeing the memory (less garbage) + while (ThisUnitDebuffs[StoredDebuffIndex]) do + ThisUnitDebuffs[StoredDebuffIndex].Type = false; + StoredDebuffIndex = StoredDebuffIndex + 1; + end + + -- if no debuff on the unit then it can't be charmed... FUCKING LAG!! + if i == 1 then + IsCharmed = false; + end + + return ThisUnitDebuffs, IsCharmed; + end --}}} +end + + +do + -- see the comment about DebuffUnitCache + local ManagedDebuffUnitCache = D.ManagedDebuffUnitCache; + + + local D = D; + local _ = false; + local CureOrder; + local sorting = function (a, b) + + CureOrder = D.classprofile.CureOrder; -- LUA is too simple, lets do the access optimization... + + local cura = (a.Type and CureOrder[a.Type] and CureOrder[a.Type] > 0) and CureOrder[a.Type] or 1024; + local curb = (b.Type and CureOrder[b.Type] and CureOrder[b.Type] > 0) and CureOrder[b.Type] or 1024; + + return cura < curb; + end + + -- This function will return a table containing only the Debuffs we can cure excepts the one we have to ignore + -- in different conditions. + function D:UnitCurableDebuffs (Unit, JustOne) -- {{{ + + if not Unit then + self:Debug("No unit supplied to UnitCurableDebuffs()"); + return false; + end + + if (not ManagedDebuffUnitCache[Unit]) then + ManagedDebuffUnitCache[Unit] = {}; + end + + local AllUnitDebuffs, IsCharmed = self:GetUnitDebuffAll(Unit); -- always return a table, may be empty though + + if not (AllUnitDebuffs[1] and AllUnitDebuffs[1].Type ) then -- if there is no curable debuff (a debuff with a type) + return false, IsCharmed; + end + + local Spells = self.Status.CuringSpells; -- shortcut to available spells by debuff type + + + local ManagedDebuffs = ManagedDebuffUnitCache[Unit]; -- shortcut for readability + + --self:Debug("Debuffs were found"); + + local DebuffNum = 1; -- number of found debuff (used for indexing) + + local continue_ = true; -- if we have to ignore a debuff, this will become false + + + + for _, Debuff in ipairs(AllUnitDebuffs) do + + if (not Debuff.Type) then -- useless test, only debuffs with a type are returned... + break; + end + continue_ = true; + + -- test if we have to ignore this debuf {{{ -- + + if self.Status.PlayerOnlyTypes[Debuff.Type] and Unit ~= "player" then -- if this type is curable on the player only + continue_ = false; + end + + if (self.profile.DebuffsToIgnore[Debuff.Name]) then + -- these are the BAD ones... the ones that make the target immune... abort this unit + --D:Debug("UnitCurableDebuffs(): %s is ignored", Debuff.Name); + break; -- exit here + end + + if (self.profile.BuffDebuff[Debuff.Name]) then + -- these are just ones you don't care about (sleepless deam etc...) + continue_ = false; + --D:Debug("UnitCurableDebuffs(): %s is not a real debuff", Debuff.Name); + end + + if (self.Status.Combat or self.profile.DebuffAlwaysSkipList[Debuff.Name]) then + local _, EnUClass = UnitClass(Unit); + if (self.profile.skipByClass[EnUClass]) then + if (self.profile.skipByClass[EnUClass][Debuff.Name]) then + -- these are just ones you don't care about by class while in combat + + -- This lead to a problem because once the fight is finished there are no event to trigger + -- a rescan of this unit, so the debuff does not appear... + + -- solution to the above problem: + + if not self.profile.DebuffAlwaysSkipList[Debuff.Name] then + self:AddDelayedFunctionCall("ReScan"..Unit, D.MicroUnitF.UpdateMUFUnit, D.MicroUnitF, Unit); + end + + D:Debug("UnitCurableDebuffs(): %s is configured to be skipped", Debuff.Name); + continue_ = false; + end + end + end + + -- }}} + + + if continue_ then + -- self:Debug("Debuffs matters"); + -- If we are still here it means that this Debuff is something not to be ignored... + + + -- We have a match for this type and we decided (checked) to + -- cure it NOTE: self.classprofile.CureOrder[DEBUFF_TYPE] is set + -- to FALSE when the type is unchecked and to < 0 when there is + -- no spell available for the type or when the spell is gone + -- (it happens for warlocks or when using the same profile with + -- several characters) + --if (self.classprofile.CureOrder[Debuff.Type] and self.classprofile.CureOrder[Debuff.Type] > 0) then + if (self:GetCureCheckBoxStatus(Debuff.Type)) then + + + -- self:Debug("we can cure it"); + + -- if we do have a spell to cure + if (Spells[Debuff.Type]) then + -- The user doesn't want to cure a unit afllicted by poison or disease if the unit + -- is beeing cured by an abolish spell + + if (self.profile.Check_For_Abolish and (Debuff.Type == DC.POISON and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_POISON"]) or Debuff.Type == DC.DISEASE and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_DISEASE"]))) then + self:Debug("Abolish buff found, skipping"); + else + -- self:Debug("It's managed"); + + -- create an entry for this debuff index if necessary + if (not ManagedDebuffs[DebuffNum]) then + ManagedDebuffs[DebuffNum] = {}; + end + + -- copy the debuff information to this table. + self:tcopy(ManagedDebuffs[DebuffNum], Debuff); + + DebuffNum = DebuffNum + 1; + + -- the live-list only reports the first debuf found and set JustOne to true + if (JustOne) then + break; + end + end + end + end + end + end -- for END + + -- erase unused entries without freeing the memory (less garbage) + if (not JustOne or DebuffNum == 1) then -- if JustOne is set don't clear anything except if we found nothing + while (ManagedDebuffs[DebuffNum]) do + ManagedDebuffs[DebuffNum].Type = false; + -- ManagedDebuffs[DebuffNum].TimeStamp = false; + DebuffNum = DebuffNum + 1; + end + end + + -- sort the table only if it's not 'empty' and only if there is at least two debuffs + if (ManagedDebuffs[1] and ManagedDebuffs[1].Type) then + + -- order Debuffs according to type priority order + if (not JustOne and ManagedDebuffs[2] and ManagedDebuffs[2].Type) then + t_sort(ManagedDebuffs, sorting); -- uses memory.. + end + return ManagedDebuffs, IsCharmed; + else + return false, IsCharmed; + end + + end -- // }}} + + local GetTime = _G.GetTime; + local Debuffs = {}; local IsCharmed = false; local Unit; local MUF; local IsDebuffed= false; local CheckStealth = false; + local NoScanStatuses = false; + function D:ScanEveryBody() + + if not NoScanStatuses then + NoScanStatuses = {[DC.ABSENT] = true, [DC.FAR] = true, [DC.BLACKLISTED] = true}; + end + + local UnitArray = self.Status.Unit_Array; local i = 1; + local CheckStealth = self.profile.Show_Stealthed_Status; + + --[===[@debug@ + local start = GetTime(); + --@end-debug@]===] + + while UnitArray[i] do + Unit = UnitArray[i]; + MUF = self.MicroUnitF.UnitToMUF[Unit]; + + if MUF and not NoScanStatuses[MUF.UnitStatus] then + Debuffs, IsCharmed = self:UnitCurableDebuffs(Unit, true); + + if CheckStealth then + self.Stealthed_Units[Unit] = self:CheckUnitStealth(Unit); -- update stealth status + end + + IsDebuffed = (Debuffs and true) or IsCharmed; + -- If MUF disagrees + if IsDebuffed ~= MUF.IsDebuffed and not D:DelayedCallExixts("Dcr_Update" .. Unit) then + --[===[@debug@ + if IsDebuffed then + self:AddDebugText("delayed debuff found by scaneveryone, scheduling analysis in 1s"); + --D:ScheduleDelayedCall("Dcr_lateanalysis" .. Unit, self.MicroUnitF.LateAnalysis, 1, self.MicroUnitF, "ScanEveryone", Debuffs, MUF, MUF.UnitStatus); + else + self:AddDebugText("delayed UNdebuff found by scaneveryone on", Unit); + end + --@end-debug@]===] + + self.MicroUnitF:UpdateMUFUnit(Unit, true); + + --[===[@debug@ + D:Println("HAAAAAAA!!!!!"); + --@end-debug@]===] + end + end + + i = i + 1; + end + --[===[@debug@ + --D:Debug("|cFF777777Scanning everybody...", i - 1, "units scanned in ", GetTime() - start, "seconds|r"); + --@end-debug@]===] + end + + + -- a little test... the ".." way wins (6x faster than the format solution) when both sides are strings + function D:tests() + + local test = "test1"; + local start = GetTime(); + local strings = {"string1", "string2", "strring3"}; + local teststring = "unitraid5" + for i =1, 1000000 do + teststring = strings[i%3 + 1]; + test = "test_"..teststring; + end + D:Debug("pass (\"\".. completed in:", GetTime() - start, test); + + start = GetTime(); + for i =1, 1000000 do + local t = strings[i%3 + 1]; + test = ("test_%s"):format(teststring); + end + D:Debug("pass format completed in:", GetTime() - start, test); + + end + +end + +local UnitBuffsCache = {}; + +-- this function returns true if one of the debuff(s) passed to it is found on the specified unit +function D:CheckUnitForBuffs(Unit, BuffNamesToCheck) --{{{ + + -- --[=[ + if (not UnitBuffsCache[Unit]) then + UnitBuffsCache[Unit] = {}; + end + + local UnitBuffs = UnitBuffsCache[Unit]; + local i = 1; + local buff_name = ""; + + -- Get all the unit's buffs + while true do + + buff_name = UnitBuff(Unit, i) + + if not buff_name then + break; + end + + UnitBuffs[i] = buff_name; + i = i + 1; + end + + while UnitBuffs[i] do -- clean the rest of the cache + UnitBuffs[i] = false; + i = i + 1; + end + --]=] + + if type(BuffNamesToCheck) ~= "table" then + + if self:tcheckforval(UnitBuffs, BuffNamesToCheck) then + return true; + else + return false; + end + else + local Buff; + for _, Buff in pairs(BuffNamesToCheck) do + + if self:tcheckforval(UnitBuffs, Buff) then + return true; + end + + end + end + + return false; + +end --}}} + + +D.Stealthed_Units = {}; + +do + local Stealthed = {DS["Prowl"], DS["Stealth"], DS["Shadowmeld"], DS["Invisibility"], DS["Lesser Invisibility"]}; --, DS["Ice Armor"],}; + + DC.IsStealthBuff = D:tReverse(Stealthed); + + function D:CheckUnitStealth(Unit) + if self:CheckUnitForBuffs(Unit, Stealthed) then + -- self:Debug("Sealth found !"); + return true; + end + return false; + end +end +-- }}} + + + +T._LoadedFiles["Decursive.lua"] = "2.5.1-6-gd3885c5"; + +-- Sin diff --git a/Decursive/Decursive.toc b/Decursive/Decursive.toc new file mode 100644 index 0000000..a9c9648 --- /dev/null +++ b/Decursive/Decursive.toc @@ -0,0 +1,73 @@ +## Interface: 30300 +## X-Curse-Packaged-Version: 2.5.1-6-gd3885c5 +## X-Curse-Project-Name: Decursive +## X-Curse-Project-ID: decursive +## X-Curse-Repository-ID: wow/decursive/mainline + +## Title: Decursive |cffff00ff -Ace3-|r +## Notes: Afflictions display and cleaning for solo, group and raids with advanced filtering and priority system. +## Notes-frFR: Affichage et guérison des afflictions avec un système évolué de filtrage et de priorité. +## Notes-ruRU: Отображение и лечение недугов с расширенной фильтрацией и системой приоритетов. +## Notes-koKR: 쏠로, 파티, 공격대를 위한 고급화된 필터링과 시스템 우선권으로 고통들의 표시와 제거를 합니다. +## Notes-zhTW: 當單獨、小隊和團隊時清除有害狀態,並可使用高級過濾和優先等級系統。 +## Notes-zhCN: 当单独、小队和团队时清除有害状态,并可使用高级过滤和优先等级系统。 + +## X-Compatible-With: 40000 +## X-Max-Interface: 40000 +## X-Min-Interface: 30300 + +## SavedVariables: DecursiveDB + +## Version: 2.5.1-6-gd3885c5 +## Author: Archarodim + +## X-License: All Rights Reserved + +## OptionalDeps: Ace3, CallbackHandler-1.0, LibQTip-1.0, LibDataBroker-1.1, LibDBIcon-1.0 + +## LoadManagers: AddonLoader +## X-LoadOn-Class: Mage, Priest, Paladin, Druid, Hunter, Warlock, Shaman + + +## X-Embeds: Ace3, LibStub, CallbackHandler-1.0, LibQTip-1.0, LibDataBroker-1.1, LibDBIcon-1.0 + +## X-ReleaseDate: "2010-09-06T22:38:15Z" +## X-Category: Combat +## X-Website: http://www.2072productions.com/to/decursive.php +## X-Credits: Sylvin (Full french translation), Peter Sun, Ananhaid (ZhTW and zhCN translation), Fenlis, Chkid (koKR translation), Hemathio, Swix, Athariel, StingerSoft (Russian Translation), Freydis88, Floyddotnet, Vilogity, Dessa, Farook (German translation) + +## X-AceForum: 4195 + +## X-eMail: archarodim@teaser.fr + +LibStub\LibStub.lua + +embeds.xml + +Dcr_DIAG.lua +Dcr_DIAG.xml + +Localization\load.xml + +DCR_init.lua +Dcr_LDB.lua +Dcr_utils.lua + + +Dcr_opt.lua +Dcr_Events.lua + +Dcr_Raid.lua + +Decursive.lua +Decursive.xml + +Dcr_lists.lua +Dcr_lists.xml + +Dcr_DebuffsFrame.lua +Dcr_DebuffsFrame.xml + +Dcr_LiveList.lua +Dcr_LiveList.xml + diff --git a/Decursive/Decursive.xml b/Decursive/Decursive.xml new file mode 100644 index 0000000..a0e0f17 --- /dev/null +++ b/Decursive/Decursive.xml @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + local T = DecursiveRootTable or {}; + + if T._SelfDiagnostic() ~= 2 then + + Dcr:DisplayTooltip( + string.format( + "|cFF0055AA%s|r %s by %s|r\n" .. + --"|cFF11FF11%s|r: %s\n" .. + "|cFF11FF11%s|r / |cFF11FF11%s|r-|cFF11FF11%s|r: %s" + , Dcr.name, Dcr.version, Dcr.author, + --Dcr.L["HLP_RIGHTCLICK"], Dcr.L["STR_OPTIONS"], + Dcr.L["HLP_MIDDLECLICK"], Dcr.L["SHIFT"], Dcr.L["HLP_LEFTCLICK"], + Dcr.L["HIDESHOW_BUTTONS"] + ) + , self); + end + + + DcrDisplay_Tooltip:Hide(); + + + + if ( self.isMoving ) then + self:StopMovingOrSizing(); + self.isMoving = false; + end + Dcr:SaveLLPos(); + + + if ( ( ( not self.isLocked ) or ( self.isLocked == 0 ) or IsAltKeyDown()) and ( button == "LeftButton" ) ) then + self:StartMoving(); + self.isMoving = true; + end + if (button == "MiddleButton" or button == "LeftButton" and IsShiftKeyDown()) then + Dcr:ShowHideButtons(); + end + + + if ( self.isMoving ) then + self:StopMovingOrSizing(); + self.isMoving = false; + end + + + self:RegisterEvent("ADDON_LOADED"); + + + if event == "ADDON_LOADED" and (...) == "Decursive" then + local T = DecursiveRootTable or {}; + + T._SelfDiagnostic(); + self:UnregisterEvent("ADDON_LOADED"); + end + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Decursive/LICENSE.txt b/Decursive/LICENSE.txt new file mode 100644 index 0000000..79e5b48 --- /dev/null +++ b/Decursive/LICENSE.txt @@ -0,0 +1,23 @@ + +Decursive add-on for World of Warcraft UI +Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr +Official Website: http://www.2072productions.com/to/decursive.php + +Starting from 2009-10-31 and until said otherwise by its author, +Decursive +is no longer free software, all rights are reserved to its author (John +Wellesz). + +The only official and allowed distribution means are +www.2072productions.com, www.wowace.com and curse.com. +To distribute Decursive through other means a special authorization is +required. + +Decursive is provided free of charge and is free to use, if you paid for +it you should ask for a refund. + +Decursive is inspired from the original "Decursive v1.9.4" by Quu. +The original "Decursive 1.9.4" is in public domain ( www.quutar.com ) + +Decursive is distributed in the hope that it will be useful, +but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. \ No newline at end of file diff --git a/Decursive/LibStub/LibStub.lua b/Decursive/LibStub/LibStub.lua new file mode 100644 index 0000000..0a41ac0 --- /dev/null +++ b/Decursive/LibStub/LibStub.lua @@ -0,0 +1,30 @@ +-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info +-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke +local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! +local LibStub = _G[LIBSTUB_MAJOR] + +if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(("Cannot find a library instance of %q."):format(tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) +end diff --git a/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.lua new file mode 100644 index 0000000..6c89654 --- /dev/null +++ b/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.lua @@ -0,0 +1,642 @@ +--- **AceAddon-3.0** provides a template for creating addon objects. +-- It'll provide you with a set of callback functions that allow you to simplify the loading +-- process of your addon.\\ +-- Callbacks provided are:\\ +-- * **OnInitialize**, which is called directly after the addon is fully loaded. +-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present. +-- * **OnDisable**, which is only called when your addon is manually being disabled. +-- @usage +-- -- A small (but complete) addon, that doesn't do anything, +-- -- but shows usage of the callbacks. +-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- +-- function MyAddon:OnInitialize() +-- -- do init tasks here, like loading the Saved Variables, +-- -- or setting up slash commands. +-- end +-- +-- function MyAddon:OnEnable() +-- -- Do more initialization here, that really enables the use of your addon. +-- -- Register Events, Hook functions, Create Frames, Get information from +-- -- the game that wasn't available in OnInitialize +-- end +-- +-- function MyAddon:OnDisable() +-- -- Unhook, Unregister Events, Hide frames that you created. +-- -- You would probably only use an OnDisable if you want to +-- -- build a "standby" mode, or be able to toggle modules on/off. +-- end +-- @class file +-- @name AceAddon-3.0.lua +-- @release $Id: AceAddon-3.0.lua 895 2009-12-06 16:28:55Z nevcairiel $ + +local MAJOR, MINOR = "AceAddon-3.0", 5 +local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR) + +if not AceAddon then return end -- No Upgrade needed. + +AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame +AceAddon.addons = AceAddon.addons or {} -- addons in general +AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon. +AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized +AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled +AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon + +-- Lua APIs +local tinsert, tconcat, tremove = table.insert, table.concat, table.remove +local fmt, tostring = string.format, tostring +local select, pairs, next, type, unpack = select, pairs, next, type, unpack +local loadstring, assert, error = loadstring, assert, error +local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget + +-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded +-- List them here for Mikk's FindGlobals script +-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler + +--[[ + xpcall safecall implementation +]] +local xpcall = xpcall + +local function errorhandler(err) + return geterrorhandler()(err) +end + +local function CreateDispatcher(argCount) + local code = [[ + local xpcall, eh = ... + local method, ARGS + local function call() return method(ARGS) end + + local function dispatch(func, ...) + method = func + if not method then return end + ARGS = ... + return xpcall(call, eh) + end + + return dispatch + ]] + + local ARGS = {} + for i = 1, argCount do ARGS[i] = "arg"..i end + code = code:gsub("ARGS", tconcat(ARGS, ", ")) + return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) +end + +local Dispatchers = setmetatable({}, {__index=function(self, argCount) + local dispatcher = CreateDispatcher(argCount) + rawset(self, argCount, dispatcher) + return dispatcher +end}) +Dispatchers[0] = function(func) + return xpcall(func, errorhandler) +end + +local function safecall(func, ...) + -- we check to see if the func is passed is actually a function here and don't error when it isn't + -- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not + -- present execution should continue without hinderance + if type(func) == "function" then + return Dispatchers[select('#', ...)](func, ...) + end +end + +-- local functions that will be implemented further down +local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype + +-- used in the addon metatable +local function addontostring( self ) return self.name end + +--- Create a new AceAddon-3.0 addon. +-- Any libraries you specified will be embeded, and the addon will be scheduled for +-- its OnInitialize and OnEnable callbacks. +-- The final addon object, with all libraries embeded, will be returned. +-- @paramsig [object ,]name[, lib, ...] +-- @param object Table to use as a base for the addon (optional) +-- @param name Name of the addon object to create +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create a simple addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0") +-- +-- -- Create a Addon object based on the table of a frame +-- local MyFrame = CreateFrame("Frame") +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0") +function AceAddon:NewAddon(objectorname, ...) + local object,name + local i=1 + if type(objectorname)=="table" then + object=objectorname + name=... + i=2 + else + name=objectorname + end + if type(name)~="string" then + error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) + end + if self.addons[name] then + error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2) + end + + object = object or {} + object.name = name + + local addonmeta = {} + local oldmeta = getmetatable(object) + if oldmeta then + for k, v in pairs(oldmeta) do addonmeta[k] = v end + end + addonmeta.__tostring = addontostring + + setmetatable( object, addonmeta ) + self.addons[name] = object + object.modules = {} + object.defaultModuleLibraries = {} + Embed( object ) -- embed NewModule, GetModule methods + self:EmbedLibraries(object, select(i,...)) + + -- add to queue of addons to be initialized upon ADDON_LOADED + tinsert(self.initializequeue, object) + return object +end + + +--- Get the addon object by its name from the internal AceAddon registry. +-- Throws an error if the addon object cannot be found (except if silent is set). +-- @param name unique name of the addon object +-- @param silent if true, the addon is optional, silently return nil if its not found +-- @usage +-- -- Get the Addon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +function AceAddon:GetAddon(name, silent) + if not silent and not self.addons[name] then + error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2) + end + return self.addons[name] +end + +-- - Embed a list of libraries into the specified addon. +-- This function will try to embed all of the listed libraries into the addon +-- and error if a single one fails. +-- +-- **Note:** This function is for internal use by :NewAddon/:NewModule +-- @paramsig addon, [lib, ...] +-- @param addon addon object to embed the libs in +-- @param lib List of libraries to embed into the addon +function AceAddon:EmbedLibraries(addon, ...) + for i=1,select("#", ... ) do + local libname = select(i, ...) + self:EmbedLibrary(addon, libname, false, 4) + end +end + +-- - Embed a library into the addon object. +-- This function will check if the specified library is registered with LibStub +-- and if it has a :Embed function to call. It'll error if any of those conditions +-- fails. +-- +-- **Note:** This function is for internal use by :EmbedLibraries +-- @paramsig addon, libname[, silent[, offset]] +-- @param addon addon object to embed the library in +-- @param libname name of the library to embed +-- @param silent marks an embed to fail silently if the library doesn't exist (optional) +-- @param offset will push the error messages back to said offset, defaults to 2 (optional) +function AceAddon:EmbedLibrary(addon, libname, silent, offset) + local lib = LibStub:GetLibrary(libname, true) + if not lib and not silent then + error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2) + elseif lib and type(lib.Embed) == "function" then + lib:Embed(addon) + tinsert(self.embeds[addon], libname) + return true + elseif lib then + error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2) + end +end + +--- Return the specified module from an addon object. +-- Throws an error if the addon object cannot be found (except if silent is set) +-- @name //addon//:GetModule +-- @paramsig name[, silent] +-- @param name unique name of the module +-- @param silent if true, the module is optional, silently return nil if its not found (optional) +-- @usage +-- -- Get the Addon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- -- Get the Module +-- MyModule = MyAddon:GetModule("MyModule") +function GetModule(self, name, silent) + if not self.modules[name] and not silent then + error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2) + end + return self.modules[name] +end + +local function IsModuleTrue(self) return true end + +--- Create a new module for the addon. +-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\ +-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as +-- an addon object. +-- @name //addon//:NewModule +-- @paramsig name[, prototype|lib[, lib, ...]] +-- @param name unique name of the module +-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional) +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create a module with some embeded libraries +-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0") +-- +-- -- Create a module with a prototype +-- local prototype = { OnEnable = function(self) print("OnEnable called!") end } +-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0") +function NewModule(self, name, prototype, ...) + if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end + if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end + + if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end + + -- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well. + -- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is. + local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name)) + + module.IsModule = IsModuleTrue + module:SetEnabledState(self.defaultModuleState) + module.moduleName = name + + if type(prototype) == "string" then + AceAddon:EmbedLibraries(module, prototype, ...) + else + AceAddon:EmbedLibraries(module, ...) + end + AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries)) + + if not prototype or type(prototype) == "string" then + prototype = self.defaultModulePrototype or nil + end + + if type(prototype) == "table" then + local mt = getmetatable(module) + mt.__index = prototype + setmetatable(module, mt) -- More of a Base class type feel. + end + + safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy. + self.modules[name] = module + + return module +end + +--- Returns the real name of the addon or module, without any prefix. +-- @name //addon//:GetName +-- @paramsig +-- @usage +-- print(MyAddon:GetName()) +-- -- prints "MyAddon" +function GetName(self) + return self.moduleName or self.name +end + +--- Enables the Addon, if possible, return true or false depending on success. +-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback +-- and enabling all modules of the addon (unless explicitly disabled).\\ +-- :Enable() also sets the internal `enableState` variable to true +-- @name //addon//:Enable +-- @paramsig +-- @usage +-- -- Enable MyModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Enable() +function Enable(self) + self:SetEnabledState(true) + return AceAddon:EnableAddon(self) +end + +--- Disables the Addon, if possible, return true or false depending on success. +-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback +-- and disabling all modules of the addon.\\ +-- :Disable() also sets the internal `enableState` variable to false +-- @name //addon//:Disable +-- @paramsig +-- @usage +-- -- Disable MyAddon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:Disable() +function Disable(self) + self:SetEnabledState(false) + return AceAddon:DisableAddon(self) +end + +--- Enables the Module, if possible, return true or false depending on success. +-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object. +-- @name //addon//:EnableModule +-- @paramsig name +-- @usage +-- -- Enable MyModule using :GetModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Enable() +-- +-- -- Enable MyModule using the short-hand +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:EnableModule("MyModule") +function EnableModule(self, name) + local module = self:GetModule( name ) + return module:Enable() +end + +--- Disables the Module, if possible, return true or false depending on success. +-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object. +-- @name //addon//:DisableModule +-- @paramsig name +-- @usage +-- -- Disable MyModule using :GetModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Disable() +-- +-- -- Disable MyModule using the short-hand +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:DisableModule("MyModule") +function DisableModule(self, name) + local module = self:GetModule( name ) + return module:Disable() +end + +--- Set the default libraries to be mixed into all modules created by this object. +-- Note that you can only change the default module libraries before any module is created. +-- @name //addon//:SetDefaultModuleLibraries +-- @paramsig lib[, lib, ...] +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create the addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- -- Configure default libraries for modules (all modules need AceEvent-3.0) +-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0") +-- -- Create a module +-- MyModule = MyAddon:NewModule("MyModule") +function SetDefaultModuleLibraries(self, ...) + if next(self.modules) then + error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2) + end + self.defaultModuleLibraries = {...} +end + +--- Set the default state in which new modules are being created. +-- Note that you can only change the default state before any module is created. +-- @name //addon//:SetDefaultModuleState +-- @paramsig state +-- @param state Default state for new modules, true for enabled, false for disabled +-- @usage +-- -- Create the addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- -- Set the default state to "disabled" +-- MyAddon:SetDefaultModuleState(false) +-- -- Create a module and explicilty enable it +-- MyModule = MyAddon:NewModule("MyModule") +-- MyModule:Enable() +function SetDefaultModuleState(self, state) + if next(self.modules) then + error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2) + end + self.defaultModuleState = state +end + +--- Set the default prototype to use for new modules on creation. +-- Note that you can only change the default prototype before any module is created. +-- @name //addon//:SetDefaultModulePrototype +-- @paramsig prototype +-- @param prototype Default prototype for the new modules (table) +-- @usage +-- -- Define a prototype +-- local prototype = { OnEnable = function(self) print("OnEnable called!") end } +-- -- Set the default prototype +-- MyAddon:SetDefaultModulePrototype(prototype) +-- -- Create a module and explicitly Enable it +-- MyModule = MyAddon:NewModule("MyModule") +-- MyModule:Enable() +-- -- should print "OnEnable called!" now +-- @see NewModule +function SetDefaultModulePrototype(self, prototype) + if next(self.modules) then + error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2) + end + if type(prototype) ~= "table" then + error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2) + end + self.defaultModulePrototype = prototype +end + +--- Set the state of an addon or module +-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize. +-- @name //addon//:SetEnabledState +-- @paramsig state +-- @param state the state of an addon or module (enabled=true, disabled=false) +function SetEnabledState(self, state) + self.enabledState = state +end + + +--- Return an iterator of all modules associated to the addon. +-- @name //addon//:IterateModules +-- @paramsig +-- @usage +-- -- Enable all modules +-- for name, module in MyAddon:IterateModules() do +-- module:Enable() +-- end +local function IterateModules(self) return pairs(self.modules) end + +-- Returns an iterator of all embeds in the addon +-- @name //addon//:IterateEmbeds +-- @paramsig +local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end + +--- Query the enabledState of an addon. +-- @name //addon//:IsEnabled +-- @paramsig +-- @usage +-- if MyAddon:IsEnabled() then +-- MyAddon:Disable() +-- end +local function IsEnabled(self) return self.enabledState end +local mixins = { + NewModule = NewModule, + GetModule = GetModule, + Enable = Enable, + Disable = Disable, + EnableModule = EnableModule, + DisableModule = DisableModule, + IsEnabled = IsEnabled, + SetDefaultModuleLibraries = SetDefaultModuleLibraries, + SetDefaultModuleState = SetDefaultModuleState, + SetDefaultModulePrototype = SetDefaultModulePrototype, + SetEnabledState = SetEnabledState, + IterateModules = IterateModules, + IterateEmbeds = IterateEmbeds, + GetName = GetName, +} +local function IsModule(self) return false end +local pmixins = { + defaultModuleState = true, + enabledState = true, + IsModule = IsModule, +} +-- Embed( target ) +-- target (object) - target object to embed aceaddon in +-- +-- this is a local function specifically since it's meant to be only called internally +function Embed(target) + for k, v in pairs(mixins) do + target[k] = v + end + for k, v in pairs(pmixins) do + target[k] = target[k] or v + end +end + + +-- - Initialize the addon after creation. +-- This function is only used internally during the ADDON_LOADED event +-- It will call the **OnInitialize** function on the addon object (if present), +-- and the **OnEmbedInitialize** function on all embeded libraries. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- @param addon addon object to intialize +function AceAddon:InitializeAddon(addon) + safecall(addon.OnInitialize, addon) + + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedInitialize, lib, addon) end + end + + -- we don't call InitializeAddon on modules specifically, this is handled + -- from the event handler and only done _once_ +end + +-- - Enable the addon after creation. +-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED, +-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons. +-- It will call the **OnEnable** function on the addon object (if present), +-- and the **OnEmbedEnable** function on all embeded libraries.\\ +-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- Use :Enable on the addon itself instead. +-- @param addon addon object to enable +function AceAddon:EnableAddon(addon) + if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end + if self.statuses[addon.name] or not addon.enabledState then return false end + + -- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable. + self.statuses[addon.name] = true + + safecall(addon.OnEnable, addon) + + -- make sure we're still enabled before continueing + if self.statuses[addon.name] then + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedEnable, lib, addon) end + end + + -- enable possible modules. + for name, module in pairs(addon.modules) do + self:EnableAddon(module) + end + end + return self.statuses[addon.name] -- return true if we're disabled +end + +-- - Disable the addon +-- Note: This function is only used internally. +-- It will call the **OnDisable** function on the addon object (if present), +-- and the **OnEmbedDisable** function on all embeded libraries.\\ +-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- Use :Disable on the addon itself instead. +-- @param addon addon object to enable +function AceAddon:DisableAddon(addon) + if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end + if not self.statuses[addon.name] then return false end + + -- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable. + self.statuses[addon.name] = false + + safecall( addon.OnDisable, addon ) + + -- make sure we're still disabling... + if not self.statuses[addon.name] then + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedDisable, lib, addon) end + end + -- disable possible modules. + for name, module in pairs(addon.modules) do + self:DisableAddon(module) + end + end + + return not self.statuses[addon.name] -- return true if we're disabled +end + +--- Get an iterator over all registered addons. +-- @usage +-- -- Print a list of all installed AceAddon's +-- for name, addon in AceAddon:IterateAddons() do +-- print("Addon: " .. name) +-- end +function AceAddon:IterateAddons() return pairs(self.addons) end + +--- Get an iterator over the internal status registry. +-- @usage +-- -- Print a list of all enabled addons +-- for name, status in AceAddon:IterateAddonStatus() do +-- if status then +-- print("EnabledAddon: " .. name) +-- end +-- end +function AceAddon:IterateAddonStatus() return pairs(self.statuses) end + +-- Following Iterators are deprecated, and their addon specific versions should be used +-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon) +function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end +function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end + +-- Event Handling +local function onEvent(this, event, arg1) + if event == "ADDON_LOADED" or event == "PLAYER_LOGIN" then + -- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration + while(#AceAddon.initializequeue > 0) do + local addon = tremove(AceAddon.initializequeue, 1) + -- this might be an issue with recursion - TODO: validate + if event == "ADDON_LOADED" then addon.baseName = arg1 end + AceAddon:InitializeAddon(addon) + tinsert(AceAddon.enablequeue, addon) + end + + if IsLoggedIn() then + while(#AceAddon.enablequeue > 0) do + local addon = tremove(AceAddon.enablequeue, 1) + AceAddon:EnableAddon(addon) + end + end + end +end + +AceAddon.frame:RegisterEvent("ADDON_LOADED") +AceAddon.frame:RegisterEvent("PLAYER_LOGIN") +AceAddon.frame:SetScript("OnEvent", onEvent) + +-- upgrade embeded +for name, addon in pairs(AceAddon.addons) do + Embed(addon) +end diff --git a/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.xml b/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.xml new file mode 100644 index 0000000..e6ad639 --- /dev/null +++ b/Decursive/Libs/AceAddon-3.0/AceAddon-3.0.xml @@ -0,0 +1,4 @@ + +