diff --git a/.github/ISSUE_TEMPLATE/Bug Report.yml b/.github/ISSUE_TEMPLATE/Bug Report.yml
new file mode 100755
index 00000000..891c6d7c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Bug Report.yml
@@ -0,0 +1,55 @@
+name: Bug Report
+description: File a bug report
+title: "[Bug]: "
+labels: [bug]
+# assignees:
+# - dandruff
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ description: Please search to see if an issue already exists for the bug you encountered.
+ options:
+ - label: I have searched the existing issues
+ required: true
+ - type: input
+ id: wow-version
+ attributes:
+ label: WoW version
+ description: |
+ What is the version of your World of Warcraft retail?
+ Found either below the "Play" button in the Battle.NET launcher, or bottom left corner of login screen.
+ placeholder: ex. 9.1.0.39291
+ validations:
+ required: true
+ - type: input
+ id: xct-version
+ attributes:
+ label: xCT+ version
+ description: |
+ What is the version of your xCT+?
+ Found at the top of the xCT+ configuratin window (`/xct`).
+ If you can't open the config, look in the `interface/AddOns/xCT+/xCT+.toc`.
+ placeholder: ex. 4.5.5
+ validations:
+ required: true
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: What happened?
+ description: Also tell us, what did you expect to happen?
+ placeholder: Tell us what you see!
+ validations:
+ required: true
+ - type: textarea
+ id: errors
+ attributes:
+ label: Related Errors
+ description: Please paste any errors you're seeing from xCT+ related to this issue
+ placeholder: |
+ Message: Interface\AddOns\xCT+\...
+ render: lua
diff --git a/.github/ISSUE_TEMPLATE/Feature Request.yml b/.github/ISSUE_TEMPLATE/Feature Request.yml
new file mode 100755
index 00000000..ec6b0b86
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/Feature Request.yml
@@ -0,0 +1,26 @@
+name: Feature Request
+description: Request a feature
+title: "[feature]: "
+labels: [feature]
+# assignees:
+# - dandruff
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to request a feature!
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ description: Please search to see if an issue already exists for the feature you're requesting.
+ options:
+ - label: I have searched the existing issues
+ required: true
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: Feature request
+ description: What feature would you like to request
+ placeholder: It would be great if....
+ validations:
+ required: true
diff --git a/.github/README.md b/.github/README.md
new file mode 100644
index 00000000..7dde6dd8
--- /dev/null
+++ b/.github/README.md
@@ -0,0 +1,89 @@
+# xCT+
+
+Created for optimal performance in the toughest fights, a rugged combat text add-on is finally ready to be put to the
+test!
+
+## Overview
+
+xCT+ is immensely based off the add-on xCT by braindump (aka Affli). It strives to be as minimalistic as possible while
+maintaining ease of use. That is why, introduced in version 3, you can now configure all settings while in-game.
+
+Overall, xCT+ is a replacement for Blizzard’s scrolling combat text. It is superior to the default combat text because
+xCT+ organizes incoming healing, incoming damage, and outgoing damage/healing into their own frames, while minimizing
+spam.
+
+## Features
+
+For those that have used Affli’s xCT Add-On, xCT+ expands the functionality of its predecessor. New features include:
+
+* An In-Game Configuration Tool
+* Frames for tracking Criticals, Loot and Money, Spell Procs and Unit Power (e.g. Mana)
+* A grid when positioning the frames
+* Tons of new options and features
+* A redesigned Spell Merger to cut down on the spam
+* Profiles
+* And many more exciting new features!
+
+## The future of xCT+ in Midnight
+
+I recently gained access to the Midnight beta and tested some things:
+- Listening for event `COMBAT_LOG_EVENT_UNFILTERED` is gone. Thats how this addon got almost all of its information.
+- Same for `COMBAT_LOG_EVENT`
+- I can register a handler for `UNIT_COMBAT` event and it gives me some data but its really lackluster:
+ - No source unit of the damage? (e. g. Did I do that or did another player do that? Who damaged me?)
+ - No spell ID (was it a melee attack or a spell?)
+ - Is dependent on your Combat Log filter (see your chat box).
+- `C_CombatText.GetCurrentEventInfo()` is supposed to provide these informations as `(secret value)`, but it just returns `nil` at the moment.
+
+Currently I'm not optimistic that xCT+ will work in any capacity in the WoW Midnight expansion.
+If something changes I'll look at it again. Even a reduced form of xCT+ would be fine for me but that does not
+seem possible right now.
+
+If anybody knows more and could help me bring some functionality to Midnight, please feel free to contact me via Github or ingame.
+
+## The future of xCT+ in TWW and beyond
+
+As of TWW Season 1, [Dandruff](https://www.curseforge.com/members/tdandruff) (Dandruff-Stormreaver US) resigned from addon development and gave [RedAces](https://www.curseforge.com/members/redaces) (Feylynn-Antonidas EU) the
+permission to develop xCT+ further. He has aided development since mid Dragonflight and will continue to fix and
+enhance xCT+.
+
+### Version 4.9.0
+Version 4.9.0 shapes up to be a major rewrite of the whole addon. The following features are currently in it:
+
+* a lot of cleanup (!)
+* a lot of bugfixes (!)
+* use more headers in the options GUI because the descriptions are sometimes only displayed as "..."
+* allow hiding the auto attack icons for outgoing attacks
+* only register xCT as LibSink target if any other addon has loaded LibSink. Remove LibSink from embedded libraries
+* show fully absorbed outgoing damage events as normal damage events (if enabled)
+* add partially absorbed outgoing damage to the amount to show the full damage we did (if enabled)
+* fix the handling of DK runes coming off CD
+* The spell value filter now works AFTER merging!
+ * e. g. if the spell filter is set for >= 100k damage, then 2x 60k damage events will be merged and displayed as 120k (x2)
+ * Before this change, nothing was displayed because each damage event was filtered before merging
+* add notification for profession skill ups (if enabled)
+* replace "OnUpdate" function for delayed loot display with a AceTimer schedule
+* add new frame "outgoing" healing
+ * Spam Merger now wont merge heal + damage if the Spell ID is the same (e. g. DK consumption, Death Strike)
+* Major overhaul of the spam merger
+ * cleanup merges of older expansions and rearrange them
+ * let the player set the merge interval for everything we are currently merging
+ * fix dispell merging
+ * Spam merger now should display more OnUpdate in order to show all waiting messages
+* modernize spell filter options: you can now delete an entry completely via an select (unsorted for now)
+* Split xCT into a main addon and an options addon to reduce the footprint.
+ * The main addon has a RAM footprint of about 800 KB, and the options of 1,8 MB ... wow!
+
+Feel free to try the alpha and beta versions and provide feedback in GitHub!
+
+## Download
+
+* [CurseForge](https://www.curseforge.com/wow/addons/xct-plus)
+* [GitHub](https://github.com/dandruff/xCT)
+
+## Support
+
+Feel free to use the comments below for quick and dirty questions. If you think that you have a question that is more
+involved, you should create a ticket on the [GitHub issue tracker](https://github.com/dandruff/xCT/issues/new).
+
+All contributions (pull requests, testing, bug reports, ...) are welcome!
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..b956fcee
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,38 @@
+# description of this workflow, can be anything you want
+name: Package and release
+
+# we need to let GitHub know _when_ we want to release.
+# this is typically only when we create a new tag.
+# make sure your tags are annotated!
+on:
+ push:
+ tags:
+ - '**'
+
+# a workflow is built up as jobs, and within these jobs are steps
+jobs:
+
+ # "release" is a job, you can name it anything you want
+ release:
+
+ # we can run our steps on pretty much anything, but the "ubuntu-latest" image is a safe bet
+ runs-on: ubuntu-latest
+
+ # specify the environment variables used by the packager, matching the secrets for the project on GitHub
+ env:
+ CF_API_KEY: ${{ secrets.CF_API_KEY }}
+ GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} # this secret is pre-provided for the workflow and does not
+ # need to be added yourself, we just reference it here.
+
+ # "steps" holds a list of all the steps needed to package and release our AddOn
+ steps:
+
+ # we first have to clone the AddOn project, this is a required step
+ - name: Clone project
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # gets entire git history, needed for automatic changelogs
+
+ # once cloned, we just run the GitHub Action
+ - name: Package and release
+ uses: BigWigsMods/packager@v2
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 00000000..9fdb4520
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.DS_Store
+/.idea
+/scripts
diff --git a/.stylua.toml b/.stylua.toml
new file mode 100644
index 00000000..2b65ff2c
--- /dev/null
+++ b/.stylua.toml
@@ -0,0 +1,4 @@
+indent_type = "Spaces"
+indent_width = 4
+column_width = 180
+line_endings = "Unix"
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 00000000..fe0b2240
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,6 @@
+- add icon
+- refactor options (no more #info-1 stuff)
+- completly redo the mapping of events to frames?
+ - allow X frames and let the user decide which event type goes to which frame
+- more languages!
+- Show abbreviated spell name (Blood Boil => BB?)
diff --git a/config.lua b/config.lua
deleted file mode 100644
index 288d7f9e..00000000
--- a/config.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-local addon, ns=...
-ns.config={
----------------------------------------------------------------------------------
--- use ["option"] = true/false, to set options.
--- options
--- blizz damage options.
- ["blizzheadnumbers"] = false, -- use blizzard damage/healing output (above mob/player head)
- ["damagestyle"] = true, -- change default damage/healing font above mobs/player heads. you need to restart WoW to see changes! has no effect if blizzheadnumbers = false
--- xCT outgoing damage/healing options
- ["damage"] = true, -- show outgoing damage in it's own frame
- ["healing"] = true, -- show outgoing healing in it's own frame
- ["showhots"] = true, -- show periodic healing effects in xCT healing frame.
- ["damagecolor"] = true, -- display damage numbers depending on school of magic, see http://www.wowwiki.com/API_COMBAT_LOG_EVENT
- ["critprefix"] = "|cffFF0000*|r", -- symbol that will be added before amount, if you deal critical strike/heal. leave "" for empty. default is red *
- ["critpostfix"] = "|cffFF0000*|r", -- postfix symbol, "" for empty.
- ["icons"] = true, -- show outgoing damage icons
- ["iconsize"] = 28, -- icon size of spells in outgoing damage frame, also has effect on dmg font size if it's set to "auto"
- ["petdamage"] = true, -- show your pet damage.
- ["dotdamage"] = true, -- show damage from your dots. someone asked an option to disable lol.
- ["treshold"] = 1, -- minimum damage to show in outgoing damage frame
- ["healtreshold"] = 1, -- minimum healing to show in incoming/outgoing healing messages.
-
--- appearence
- ["font"] = "Interface\\Addons\\xCT\\HOOGE.TTF", -- "Fonts\\ARIALN.ttf" is default WoW font.
- ["fontsize"] = 12,
- ["fontstyle"] = "OUTLINE", -- valid options are "OUTLINE", "MONOCHROME", "THICKOUTLINE", "OUTLINE,MONOCHROME", "THICKOUTLINE,MONOCHROME"
- ["damagefont"] = "Interface\\Addons\\xCT\\HOOGE.TTF", -- "Fonts\\FRIZQT__.ttf" is default WoW damage font
- ["damagefontsize"] = "auto", -- size of xCT damage font. use "auto" to set it automatically depending on icon size, or use own value, 16 for example. if it's set to number value icons will change size.
- ["timevisible"] = 3, -- time (seconds) a single message will be visible. 3 is a good value.
- ["scrollable"] = false, -- allows you to scroll frame lines with mousewheel.
- ["maxlines"] = 64, -- max lines to keep in scrollable mode. more lines=more memory. nom nom nom.
-
--- justify messages in frames, valid values are "RIGHT" "LEFT" "CENTER"
- ["justify_1"] = "LEFT", -- incoming damage justify
- ["justify_2"] = "RIGHT", -- incoming healing justify
- ["justify_3"] = "CENTER", -- various messages justify (mana, rage, auras, etc)
- ["justify_4"] = "RIGHT", -- outgoing damage/healing justify
-
--- class modules and goodies
- ["stopvespam"] = false, -- automaticly turns off healing spam for priests in shadowform. HIDE THOSE GREEN NUMBERS PLX!
- ["dkrunes"] = true, -- show deatchknight rune recharge
- ["mergeaoespam"] = true, -- merges multiple aoe spam into single message, can be useful for dots too.
- ["mergeaoespamtime"] = 3, -- time in seconds aoe spell will be merged into single message. minimum is 1.
- ["killingblow"] = true, -- tells you about your killingblows (works only with ["damage"] = true,)
- ["dispel"] = true, -- tells you about your dispels (works only with ["damage"] = true,)
- ["interrupt"] = true, -- tells you about your interrupts (works only with ["damage"] = true,)
-}
diff --git a/pkgmeta.yaml b/pkgmeta.yaml
new file mode 100644
index 00000000..ec68ab90
--- /dev/null
+++ b/pkgmeta.yaml
@@ -0,0 +1,28 @@
+package-as: xCT+
+
+ignore:
+ - .github
+ - .gitignore
+ - .stylua.toml
+ - libs/LibStub/tests
+ - pkgmeta.yaml
+
+# We want to have 2 folders: xCT+ and xCT+Options
+move-folders:
+ xCT+/xCT+: xCT+
+ xCT+/xCT+Options: xCT+Options
+
+externals:
+ xCT+/libs/AceAddon-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceAddon-3.0
+ xCT+/libs/AceConfig-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceConfig-3.0
+ xCT+/libs/AceConsole-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceConsole-3.0
+ xCT+/libs/AceDB-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceDB-3.0
+ xCT+/libs/AceDBOptions-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceDBOptions-3.0
+ xCT+/libs/AceEvent-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceEvent-3.0
+ xCT+/libs/AceGUI-3.0-SharedMediaWidgets: https://repos.curseforge.com/wow/ace-gui-3-0-shared-media-widgets/trunk/AceGUI-3.0-SharedMediaWidgets
+ xCT+/libs/AceGUI-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceGUI-3.0
+ xCT+/libs/AceTimer-3.0: https://repos.curseforge.com/wow/ace3/trunk/AceTimer-3.0
+ xCT+/libs/CallbackHandler-1.0: https://repos.curseforge.com/wow/callbackhandler/trunk/CallbackHandler-1.0
+ xCT+/libs/LibSharedMedia-3.0: https://repos.curseforge.com/wow/libsharedmedia-3-0/trunk/LibSharedMedia-3.0
+ xCT+/libs/LibStub: https://repos.curseforge.com/wow/libstub/trunk
+ xCT+/Libs/AceLocale-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceLocale-3.0
diff --git a/xCT+/config/merge_basic_spells.lua b/xCT+/config/merge_basic_spells.lua
new file mode 100644
index 00000000..a6bd8731
--- /dev/null
+++ b/xCT+/config/merge_basic_spells.lua
@@ -0,0 +1,26 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, _, item, header = unpack(addon.merge_helpers)
+
+header(L["Basic spells"])
+do
+ item(143924, 3.5) -- Item Leech
+
+ -- not technically an item but thats not important right now
+ item(6603, 5) -- Auto Attack
+ item(75, 5) -- Auto Shot
+end
diff --git a/xCT+/config/merge_bfa.lua b/xCT+/config/merge_bfa.lua
new file mode 100644
index 00000000..0f6b4af7
--- /dev/null
+++ b/xCT+/config/merge_bfa.lua
@@ -0,0 +1,24 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, _, item, header = unpack(addon.merge_helpers)
+
+header(" 8.x |cff325A93" .. L["Battle for Azeroth"] .. "|r")
+do
+ -- Trinkets
+ item(276199, 1.0) -- Trinket: Darkmoon Deck: Fathoms
+ item(276132, 2.0) -- Trinket: Darkmoon Deck: Squalls
+end
diff --git a/xCT+/config/merge_class.lua b/xCT+/config/merge_class.lua
new file mode 100644
index 00000000..3b5820f4
--- /dev/null
+++ b/xCT+/config/merge_class.lua
@@ -0,0 +1,533 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+local spell, class, spec, alias = unpack(addon.merge_helpers)
+
+class("DEATHKNIGHT")
+do
+ spec(0) -- All Specs
+ spell(52212, 3.0) -- Death and Decay
+ alias(441426, 441424) -- Talent: Exterminate (Deathbringer)
+ alias(436304, 439843) -- Talent: Reaper's Mark (Deathbringer)
+ alias(439594, 439843) -- Talent: Reaper's Mark (Deathbringer)
+
+ spec(250) -- Blood
+ spell(55078, 3.0) -- Blood Plague
+ spell(196528, 1.0) -- Talent: Bonestorm (DMG)
+ spell(196545, 1.0) -- Talent: Bonestorm (Heal)
+ alias(49998, 45470) -- Death Strike (DRW)
+ alias(383312, 383313) -- Talent: Abomination Limb
+
+ spec(251) -- Frost
+ spell(196771, 3.0) -- Remorseless Winter
+ spell(55095, 3.0) -- Frost Fever
+ spell(222024, 1.0) -- Obliterate
+ spell(222026, 1.0) -- Frost Strike
+ spell(155166, 1.0) -- Talent: Breath of Sindragosa
+ spell(190780, 1.5) -- Talent: Sindragosa's Fury
+ alias(66198, 222024) -- [MH/OH Merger] Obliterate
+ alias(66196, 222026) -- [MH/OH Merger] Frost Strike
+
+ spec(252) -- Unholy
+ spell(199373, 1.0) -- Army: Claw
+ spell(191587, 3.0) -- Virulent Plague (DoT)
+ spell(286836, 1.0) -- Dark Transformation
+ spell(47541, 1.0) -- Death Coil
+ spell(156000, 3.0) -- Talent: Defile
+ spell(115994, 1.0) -- Talent: Unholy Blight
+ spell(319230, 1.0) -- Talent: Unholy Pact
+ alias(55090, 70890) -- Scourge Strike: Merge physical + shadow portion
+ alias(319238, 319230) -- [Cleave Merger] Unholy Pact
+ alias(319236, 319230) -- [Cleave Merger] Unholy Pact
+ alias(191685, 215969) -- [DD/DoT Merger] Virulent Plague Eruption
+ alias(212739, 207317) -- Epidemic
+ alias(215969, 207317) -- Epidemic
+end
+
+class("DEMONHUNTER")
+do
+ spec(0)
+ spell(258920, 1.5) -- Immolation Aura
+ alias(258921, 258920) -- Immolation Aura
+ alias(258922, 258920) -- Immolation Aura
+
+ spec(577) -- Havoc
+ spell(342857, 3.5) -- Glaive Tempest
+ spell(222031, 3.0) -- Chaos Strike
+ spell(185123, 1.5) -- Throw Glaive (Havoc)
+ spell(198030, 1.5) -- Eye Beam
+ spell(192611, 1.5) -- Fel Rush
+ spell(199552, 2.0) -- Blade Dance
+ spell(210153, 2.0) -- Death Sweep
+ spell(203796, 2.5) -- Talent: Demon Blades
+ spell(211052, 1.5) -- Talent: Fel Barrage
+ alias(199547, 222031) -- Chaos Strike
+ alias(210155, 210153) -- Death Sweep (Blade Dance in Metamorphosis)
+ alias(258883, 199552) -- Talent: Blade of Ruin => Blade Dance
+ alias(391374, 199552) -- Blade Dance
+ alias(391378, 199552) -- Blade Dance
+ alias(200685, 199552) -- Blade Dance
+ alias(393035, 185123) -- Throw Glaive (Havoc)
+ alias(337819, 185123) -- Throw Glaive (Havoc)
+ alias(201428, 201427) -- Annihilation (Chaos Strike in Metamorphosis)
+ alias(227518, 201427) -- Annihilation (Chaos Strike in Metamorphosis)
+ alias(444979, 185123) -- Hero Talent: Preemptive Strike => Throw Glaive (Havoc)
+ alias(390181, 185123) -- Talent: Soulscar => Throw Glaive (Havoc)
+
+ spec(581) -- Vengeance
+ spell(204157, 1.5) -- Throw Glaive (Vengeance)
+ spell(204598, 2.5) -- Sigil of Flame
+ spell(203794, 1.5) -- Consume Soul
+ spell(207771, 2.5) -- Talent: Burning Alive
+ spell(218677, 1.5) -- Talent: Spirit Bomb (Frailty Heal)
+ spell(213011, 2.5) -- Artifact: Charred Warblades
+ spell(207407, 1.5) -- Artifact: Soul Carver (DoT)
+ spell(212105, 1.5) -- Talent: Fel Devastation
+ alias(212106, 212105) -- Talent: Fel Devastation
+ alias(212084, 212105) -- Talent: Fel Devastation
+ alias(225919, 263642) -- Talent: Fracture
+ alias(225921, 263642) -- Talent: Fracture
+end
+
+class("DRUID")
+do
+ spec(0) -- All Specs
+ spell(164812, 2.5) -- Moonfire
+ spell(164815, 2.5) -- Sunfire
+
+ spec(102) -- Balance
+ spell(191037, 2.0) -- Starfall
+ spell(202347, 2.5) -- Talent: Stellar Flare
+ spell(202497, 2.5) -- Talent: Shooting Stars
+ spell(211545, 2.5) -- Talent: Fury of Elune
+ alias(226104, 191037) -- Artifact: Echoing Stars
+
+ spec(103) -- Feral
+ spell(106830, 2.5) -- Thrash (Cat)
+ spell(155722, 2.5) -- Rake
+ spell(1079, 2.5) -- Rip
+ spell(155625, 2.5) -- Talent: Lunar Inspiration
+ spell(210723, 1.5) -- Artifact: Ashamane's Frenzy
+ alias(1822, 155722) -- [DD/DoT Merger] Rake
+
+ spec(104) -- Guardian
+ spell(227034, 1.5) -- Mastery: Nature's Guardian
+ spell(22842, 1.5) -- Frenzied Regeneration
+ spell(77758, 2.5) -- Thrash (Bear)
+ spell(213709, 2.5) -- Talent: Brambles
+ spell(204069, 2.5) -- Talent: Lunar Beam
+ spell(400360, 1.0) -- Talent: Moonless Night
+ spell(371982, 1.0) -- Talent: After the Wildfire
+ spell(219432, 2.5) -- Artifact: Rage of the Sleeper
+ alias(192090, 77758) -- [DD/DoT Merger] Thrash
+ alias(203958, 213709) -- [Barkskin Merger] Brambles
+
+ spec(105) -- Restoration
+ spell(290754, 1.5) -- Lifebloom (Honor Talent)
+ spell(81269, 1.5) -- Efflorescence
+ spell(33763, 1.5) -- Lifebloom
+ spell(774, 3.5) -- Rejuvenation
+ spell(8936, 2.5) -- Regrowth
+ spell(157982, 2.5) -- Tranquility
+ spell(48438, 2.5) -- Wild Growth (Instant)
+ spell(42231, 2.5) -- Hurricane
+ spell(200389, 3.5) -- Talent: Cultivation
+ spell(364686, 5.0) -- Tier[id=188849]: Renewing Bloom (every 1s)
+ alias(189800, 48438) -- [HoT/Artifact Merger] Nature's Essence
+ alias(155777, 774) -- [HoT/HoT Merger] Talent: Germination
+ alias(207386, 81269) -- [Heal/HoT Merger] Talent: Spring Blossom
+end
+
+class("HUNTER")
+do
+ spec(0) -- All Specs
+ spell(136, 2.5) -- Mend Pet
+ spell(131900, 2.5) -- Talent: A Murder of Crows
+ spell(120361, 1.5) -- Talent: Barrage
+ spell(199483, 5.0) -- Camouflage
+ spell(339400, 8.5) -- Rejuvenating Wind (every 1s for 8s)
+ alias(214303, 136) -- Mend Pet
+
+ spec(253) -- Beast Mastery
+ spell(118459, 2.5) -- Pet: Beast Cleave
+ spell(207097, 1.5) -- Artifact: Titan's Thunder
+ spell(217200, 4.5) -- Barbed Shot (Seems every 2s over 8s)
+ alias(171457, 171454) -- [Cleave Merger] Chimaera Shot
+
+ spec(254) -- Marksmanship
+ spell(214581, 1.5) -- Talent: Sidewinders
+ spell(257045, 2.0) -- Rapid Fire
+ spell(257620, 2.0) -- Multi shot
+ spell(269576, 5.0) -- Master Marksman
+ spell(324149, 4.5) -- Flayed Shot (every 2s for 18s plus, can be extended)
+ alias(191043, 19434) -- Windburst
+
+ spec(255) -- Survival
+ spell(185855, 2.5) -- Lacerate
+ spell(13812, 2.5) -- Explosive Trap
+ spell(164857, 4.5) -- Survivalist (HoT)
+ spell(194279, 2.5) -- Talent: Caltrops
+ spell(203415, 2.5) -- Artifact: Fury of the Eagle (Bugged Icon Fix)
+ spell(204081, 2.5) -- Artifact: On the Trail
+ spell(265157, 2.5) -- Wildfire Bomb
+ spell(259491, 2.5) -- Serpent Sting
+ alias(269747, 265157) -- Wildfire Bomb (DOT)
+ alias(203525, 203563) -- Talen Strike (2x Hits)
+ alias(194858, 194859) -- Talent: Dragonsfire Grenade (DoT)
+ alias(203413, 203415) -- Artifact: Fury of the Eagle (Bugged Icon Fix)
+end
+
+class("MAGE")
+do
+ spec(0) -- All Specs
+ spell(122, 1.5) -- Frost Nova
+
+ spec(62) -- Arcane
+ spell(7268, 2.5) -- Arcane Missiles
+ spell(44425, 1.0) -- Arcane Barrage (Cleave)
+ spell(114923, 2.5) -- Talent: Nether Tempest
+ spell(153640, 2.5) -- Talent: Arcane Orb
+ spell(211088, 2.5) -- Artifact: Mark of Aluneth (DoT)
+ alias(210817, 44425) -- [DD/Splash Merger] Arcane Rebound
+ alias(114954, 114923) -- [DD/DoT Merger] Arcane Rebound
+ alias(211076, 211088) -- [DD/Splash Merger] Arcane Rebound
+
+ spec(63) -- Fire
+ spell(235314, 1.5) -- Blazing Barrier (DMG)
+ spell(2120, 1.5) -- Flamestrike (Longer for talent)
+ spell(12654, 2.5) -- Ignite (DoT)
+ spell(11366, 2.5) -- Pyroblast
+ spell(205345, 2.5) -- Talent: Conflagration
+ spell(88082, 2.0) -- Talent: Mirror Images
+ spell(198928, 1.5) -- Talent: Cinderstorm
+ spell(217694, 3.5) -- Talent: Living Bomb (DoT)
+ spell(155158, 2.5) -- Talent: Meteor (DoT)
+ spell(194522, 2.5) -- Artifact: Blast Furnace
+ spell(215775, 1.5) -- Artifact: Phoenix Reborn
+ alias(257542, 257541) -- [DD/Splash Merger] Phoenix's Flames
+ alias(226757, 205345) -- [DD/Splash Merger] Conflagration
+ alias(205472, 2120) -- [DD/DoT Merger] Talent: Flame Patch
+ alias(244813, 217694) -- [DD/DoT Merger] multi target living bomb
+ alias(277703, 11366) -- [DD/Splash Merger] Pyroblast
+
+ spec(64) -- Frost
+ spell(84721, 1.5) -- Frozen Orb
+ spell(228354, 1.0) -- Flurry
+ spell(148022, 1.5) -- Icicle
+ spell(190357, 2.5) -- Blizzard
+ spell(153596, 2.5) -- Talent: Comet Storm
+ spell(59638, 2.0) -- Talent: Mirror Images
+ spell(228598, 1.0) -- Talent: Spliting Ice
+ spell(113092, 1.0) -- Talent: Frost Bomb
+end
+
+class("MONK")
+do
+ spec(0) -- All Specs
+ spell(130654, 1.0) -- Talent: Chi Burst (Healing)
+ spell(148135, 1.0) -- Talent: Chi Burst (Damage)
+ spell(132467, 1.0) -- Talent: Chi Wave (Damage)
+ spell(132463, 1.0) -- Talent: Chi Wave (Healing)
+ spell(148187, 1.0) -- Talent: Rushing Jade Wind
+ spell(107270, 1.0) -- Spinning Crane Kick
+ spell(100784, 1.0) -- Blackout Kick
+ spell(450620, 2.0) -- Hero Talent BrM / WW: Flurry Strikes (dmg unleashed in rougly 1.5 - 1.6 seconds)
+ alias(451250, 450620) -- Hero Talent BrM / WW: Flurry Strikes
+ alias(450617, 450620) -- Hero Talent BrM / WW: Flurry Strikes
+
+ spec(268) -- Brewmaster
+ spell(124255, 1.0) -- Damage Taken: Stagger
+ spell(216521, 1.0) -- Celestial Fortune
+ spell(387621, 1.0) -- Talent: Dragonfire Brew
+ alias(123725, 115181) -- Talent: Breath of Fire (DoT)
+ alias(388867, 325153) -- Talent: Exploding Keg
+
+ spec(269) -- Windwalker
+ spell(122470, 3.0) -- Touch of Karma
+ spell(117418, 1.0) -- Fists of Fury
+ alias(124280, 122470) -- Touch of Karma
+ alias(205414, 222029) -- Talent: Strike of the Windlord
+
+ spec(270) -- Mistweaver
+ spell(115175, 3.0) -- Soothing Mist
+ spell(124682, 3.0) -- Enveloping Mist
+ spell(191840, 3.0) -- Essence Font
+ spell(119611, 3.0) -- Renewing Mists
+ spell(124081, 1.0) -- Talent: Zen Pulse
+ spell(162530, 3.0) -- Talent: Refreshing Jade Wind
+ spell(198756, 2.5) -- Talent: Invoke Chi'Ji
+ spell(199668, 2.0) -- Artifact: Blessing of Yu'lon
+ spell(199656, 2.0) -- Artifact: Celestial Breath
+ alias(344006, 191840) -- Essence Font (Faeline Stomp)
+ alias(198533, 115175) -- [Statue Merger] Talent: Jade Serpent Statue
+ alias(228649, 100784) -- [Passive Merger] Teachings of the Monastery
+end
+
+class("PALADIN")
+do
+ spec(0) -- All Specs
+ spell(81297, 2.5) -- Consecration
+ spell(183811, 2.5) -- Talent: Judgment of Light
+ spell(403460, 1.0) -- Talent: Lightforged Blessing
+ spell(377129, 2.5) -- Talent: Golden Path
+ alias(384906, 377129) -- Talent: Seal of Mercy (merged into Golden Path)
+ alias(407467, 403460) -- Talent: Lightforged Blessing
+
+ spec(65) -- Holy
+ spell(53652, 1.5) -- Beacon of Light
+ spell(119952, 2.5) -- Talent: Light's Hammer (Heal)
+ spell(114919, 2.5) -- Talent: Light's Hammer (Damage)
+ spell(210291, 2.5) -- Talent: Aura of Mercy
+ spell(200654, 2.5) -- Artifact: Tyr's Deliverance
+
+ spec(66) -- Protection
+ spell(31935, 1.5) -- Avenger's Shield
+ spell(204301, 2.5) -- Blessed Hammer
+ spell(204241, 2.0) -- Talent: Consecrated Ground
+ spell(378286, 1.5) -- Talent: Tyr's Enforcer
+
+ spec(70) -- Retribution
+ spell(203539, 5.5) -- Greater Blessings of Wisdom
+ spell(20271, 1.5) -- Talent: Greater Judgment
+ spell(198137, 2.5) -- Talent: Divine Hammer
+ spell(199435, 1.0) -- Talent (PvP): Luminescence
+ spell(157122, 1.5) -- Talent: Holy Shield
+ spell(405345, 2.0) -- Talent: Wake of Ashes
+ spell(224239, 1.5) -- Artifact: Echo of the Highlord (Divine Storm)
+ spell(224266, 1.25) -- Artifact: Echo of the Highlord (Templar's Verdict)
+ spell(224239, 1.5) -- Artifact: Divine Tempest (Divine Storm)
+ spell(215257, 1.75) -- Artifact: Healing Storm
+ alias(228288, 20271) -- [Bounce Merger] Judgment
+ alias(216527, 20271) -- [Pvp Talent - Bounce Merger]: Lawbringer
+ alias(184575, 404358) -- Talent: Blade of Vengeance into Blade of Justice
+ alias(255937, 405345) -- Talent: Wake of Ashes
+ alias(405350, 405345) -- Talent: Wake of Ashes
+ alias(224239, 53385) -- Divine Storm
+ alias(423593, 53385) -- Divine Storm
+end
+
+class("PRIEST")
+do
+ spec(0) -- All Specs
+ spell(589, 2.5) -- Shadow Word: Pain
+ spell(122128, 2.5) -- Talent: Divine Star (Damage)
+ spell(110745, 2.5) -- Talent: Divine Star (Heal)
+ spell(120696, 2.0) -- Talent: Halo (Damage)
+ spell(120692, 2.0) -- Talent: Halo (Heal)
+
+ spec(256) -- Discipline
+ spell(81751, 2.5) -- Atonement
+ spell(47666, 2.5) -- Penance (Heal)
+ spell(47750, 2.5) -- Talent: Penance (Damage)
+ spell(204213, 2.5) -- Talent: Purge the Wicked (DoT)
+ alias(204197, 204213) -- Talent: Purge the Wicked (Instant)
+
+ spec(257) -- Holy
+ spell(585, 1.0) -- Smite + Artifact: Invoke the Naaru
+ spell(139, 3.0) -- Renew
+ spell(14914, 2.5) -- Holy Fire
+ spell(132157, 1.0) -- Holy Nova
+ spell(64844, 2.5) -- Divine Hymn
+ spell(77489, 3.5) -- Mastery: Echo of Light
+ spell(196810, 1.0) -- Artifact: Invoke the Naaru (Prayer of Healing & Sanctify)
+ spell(196809, 1.0) -- Artifact: Invoke the Naaru (Heal, Flash Heal & Serenity)
+ spell(196813, 1.0) -- Artifact: Invoke the Naaru (Divine Hymn & Prayer of Mending)
+ alias(196816, 139) -- Artifact: Invoke the Naaru (Renew)
+ alias(196811, 585) -- Artifact: Invoke the Naaru (Smite, Holy Fire & Chastise)
+ alias(196812, 132157) -- Artifact: Invoke the Naaru (Holy Nova)
+
+ spec(258) -- Shadow
+ spell(228360, 1.0) -- Void Eruption
+ spell(34914, 2.5) -- Vampiric Touch
+ spell(148859, 2.5) -- Shadowy Apparition
+ spell(15407, 2.0) -- Mind Flay
+ spell(263165, 3.9) -- Talent: Void Torrent
+ spell(204778, 2.5) -- Honor Talent: Void Shield
+ spell(346111, 2.0) -- Mastery: Shadow Weaving (Shadowfiend/Mindbender extra mastery damage)
+ alias(228361, 228360) -- Void Eruption Merger
+ alias(263446, 228360) -- Void Eruption Merger
+end
+
+class("ROGUE")
+do
+ spec(0) -- All Specs
+ spell(185311, 2.5) -- Crimson Vial
+
+ spec(259) -- Assassination
+ spell(2818, 3.5) -- Deadly Poison (DoT)
+ spell(192660, 2.5) -- Poison Bomb
+ spell(121411, 2.5) -- Talent: Crimson Tempest (DoT)
+ alias(192380, 113780) -- Artifact: Poison Knives
+ alias(27576, 5374) -- Mutilate (OH)
+
+ spec(260) -- Outlaw
+ spell(199804, 2.0) -- Between the Eyes
+ spell(86392, 1.0) -- Main Gauche
+ spell(22482, 1.5) -- Blade Flurry
+ spell(57841, 3.5) -- Killing Spree
+ spell(185779, 2.0) -- Talent: Cannonball Barrage
+ spell(202822, 1.0) -- Artifact: Greed
+ alias(202823, 202822) -- [MH/OH Merger] Artifact: Greed
+ alias(197834, 193315) -- [Proc Merger] Saber Slash
+
+ spec(261) -- Sublety
+ spell(121473, 1.5) -- Shadowblades
+ spell(185438, 1.5) -- Shadowstrike
+ spell(195452, 2.5) -- Nightblade (DoT)
+ spell(158188, 2.5) -- Talent: Soothing Darkness
+ spell(220893, 2.5) -- Artifact: Akaari's Soul
+ alias(121474, 121473) -- [MH/OH Merger] Artifact: Greed
+end
+
+class("SHAMAN")
+do
+ spec(262) -- Elemental
+ spell(285452, 1.0) -- Elemental Blast
+ spell(51505, 1.0) -- Lavaburst (Elemental)
+ spell(188196, 1.0) -- Lightning Bolt (Elemental)
+ spell(188443, 1.5) -- Chain Lightning (Elemental)
+ spell(77478, 1.5) -- Earthquake
+ spell(188389, 2.5) -- Flame Shock
+ spell(192231, 2.5) -- Talent: Liquid Magma Totem
+ spell(210714, 1.0) -- Talent: Ice Fury
+ spell(170379, 2.0) -- Talent: Earthn Rage
+ spell(191732, 1.5) -- Artifact: Lightning Elemental (Chain Lightning)
+ spell(205533, 1.5) -- Artifact: Volcanic Inferno
+ alias(285466, 285452) -- [Mastery Merger] Elemental Blast Overload
+ alias(219271, 210714) -- [Mastery Merger] Ice Fury Overload
+ alias(77451, 51505) -- [Mastery Merger] Lavaburst Overload
+ alias(45297, 188443) -- [Mastery Merger] Chain Lightning Overload
+ alias(45284, 188196) -- [Mastery Merger] Lightning Bolt Overload
+
+ spec(263) -- Enhancement
+ spell(195256, 1.5) -- Stormlash (Gets Spammy!)
+ spell(192592, 1.5) -- Stormstrike: Crash Lightning (TODO: Not working?)
+ spell(10444, 1.5) -- Flametongue
+ spell(198483, 1.5) -- Artifact: Doom Wolves
+ spell(199116, 2.0) -- Artifact: Doom Vortex
+ spell(210801, 2.5) -- Talent: Crashing Storm
+ spell(197385, 2.5) -- Talent: Fury of Air
+ spell(197214, 2.5) -- Talent: Sundering
+ alias(32176, 32175) -- [MH/OH Merger] Stormstrike
+ alias(199053, 199054) -- [MH/OH Merger] Artifact: Unleash Weapons
+
+ spec(264) -- Restoration
+ spell(73921, 2.5) -- Healing Rain
+ spell(61295, 3.5) -- Riptide
+ spell(52042, 3.0) -- Healing Stream Totem
+ spell(114942, 2.5) -- Healing Tide Totem
+ spell(197997, 2.5) -- Talent: Wellspring
+ spell(114911, 2.5) -- Talent: Ancestral Guidance
+ spell(114083, 1.5) -- Talent: Ascendance
+ spell(201633, 2.5) -- Talent: Earthen Shield
+ spell(209069, 2.5) -- Artifact: Tidal Pools
+ spell(208899, 3.0) -- Artifact: Queen's Decree
+end
+
+class("WARLOCK")
+do
+ spec(0) -- All Specs
+ spell(217979, 2.0) -- Heath Funnel (You)
+ spell(85692, 2.0) -- Doomguard (Doom Bolt)
+
+ spec(265) -- Affliction
+ spell(198590, 1.5) -- Drain Soul
+ spell(980, 2.5) -- Agony
+ spell(146739, 2.5) -- Corruption
+ spell(233490, 1.5) -- Unstable Affliction
+ spell(20153, 1.5) -- Infernal: Immolation
+ spell(63106, 3.0) -- Talent: Siphon Life (Heal / Damage has same ID)
+ spell(205246, 1.5) -- Talent: Phantom Singularity
+ spell(205260, 2.5) -- Talent: Soul Effigy
+ spell(278350, 1.0) -- Talent Vile Taint
+ alias(231489, 233490) -- Unstable Affliction (Artifact: Compounding Horror)
+
+ spec(266) -- Demonology
+ spell(89753, 2.5) -- Felguard: Felstorm
+ spell(104318, 1.5) -- Wild Imp: Fel Firebolt
+ spell(271971, 1.5) -- Call Dreadstalker
+ spell(193439, 1.5) -- Demonwrath
+ spell(211720, 2.5) -- Artifact: Thal'kiel's Discord
+ spell(108447, 2.5) -- Soul Link Heal
+ spell(108446, 2.5) -- Soul Link dmgs
+ alias(267971, 267215) -- Talent Demonic Consump
+ alias(211727, 211720) -- Artifact: Thal'kiel's Discord
+
+ spec(267) -- Destruction
+ spell(157736, 3.5) -- Immolate
+ spell(42223, 2.5) -- Rain of Fire
+ spell(196448, 1.5) -- Talent: Channel Demonfire
+ spell(187394, 1.5) -- Artifact: Dimensional Rift
+ alias(348, 157736) -- [DD/DoT Merger] Immolate
+end
+
+class("WARRIOR")
+do
+ spec(0) -- All Specs
+ spell(156287, 2.5) -- Ravager
+ spell(5308, 1.0) -- Execute
+ spell(199658, 1.0) -- Whirlwind
+ alias(44949, 199658) -- Whirlwind
+ alias(199667, 199658) -- Whirlwind
+ alias(199850, 199658) -- Whirlwind
+ alias(199851, 199658) -- Whirlwind
+ alias(199852, 199658) -- Whirlwind
+ alias(385233, 199658) -- Whirlwind
+ alias(163558, 5308) -- Execute (OH - Fury)
+ alias(280849, 5308) -- Execute (OH - Fury)
+
+ spec(71) -- Arms
+ spell(772, 3.5) -- Talent: Rend
+ spell(215537, 2.5) -- Talent: Trauma
+ spell(209577, 2.5) -- Talent: Warbreaker
+ spell(262115, 3.0) -- Mastery: Deep Wounds (DoT over 6s)
+
+ spec(72) -- Fury
+ spell(184367, 2.5) -- Rampage
+ spell(96103, 1.0) -- Raging Blow
+ spell(113344, 2.5) -- Talent: Bloodbath
+ spell(50622, 2.5) -- Talent: Bladestorm
+ alias(85384, 96103) -- [MH/OH] Raging Blow
+ alias(335100, 96103) -- Talent: Reckless Abandon (Crushing Blow merged into Raging Blow)
+ alias(335098, 96103) -- Talent: Reckless Abandon (Crushing Blow merged into Raging Blow)
+ alias(95738, 50622) -- [MH/OH] Bladestorm
+ alias(218617, 184367) -- Rampage (1st Hit)
+ alias(184707, 184367) -- Rampage (2nd Hit)
+ alias(184709, 184367) -- Rampage (3rd Hit)
+ alias(201364, 184367) -- Rampage (4th Hit)
+ alias(201363, 184367) -- Rampage (5th Hit)
+ alias(385060, 385059) -- Talent: Odyns Fury
+ alias(385061, 385059) -- Talent: Odyns Fury
+ alias(385062, 385059) -- Talent: Odyns Fury
+
+ spec(73) -- Protection
+ spell(115767, 3.5) -- Deep Wounds
+ spell(222944, 3.0) -- Talent: Inspiring Presence
+ spell(203526, 3.5) -- Artifact: Neltharion's Fury
+end
+
+class("EVOKER")
+do
+ spec(0) -- All Specs
+
+ spec(1467) -- Devastation
+
+ spec(1468) -- Preservation
+ spell(363502, 3.0) -- Talent: Dream Flight
+ alias(367231, 367230) -- Talent: Spiritbloom Cleave via Empowered Spell
+ alias(409895, 367230) -- Talent: Spiritbloom Cleave via Empowered Spell
+
+ spec(1473) -- Augmentation
+end
diff --git a/xCT+/config/merge_dragonflight.lua b/xCT+/config/merge_dragonflight.lua
new file mode 100644
index 00000000..77666a1d
--- /dev/null
+++ b/xCT+/config/merge_dragonflight.lua
@@ -0,0 +1,28 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, alias, item, header = unpack(addon.merge_helpers)
+
+header("10.x |cff33937F" .. L["Dragonflight"] .. "|r")
+do
+ alias(425461, 425701) -- Trinket: Tainted Rageheart (Spell: Shadowflame Lash)
+
+ item(417134, 3.0) -- Legendary 2x Axe: Rage of Fyr'alath
+ alias(424094, 417134) -- Rage of Fyr'alath -> Weapon Fyr'alath
+ alias(413584, 417134) -- Explosive Rage -> Weapon Fyr'alath
+
+ alias(426527, 426535) -- Amirdrassil Head Enchant (Melee) - 2nd Effect
+end
diff --git a/xCT+/config/merge_helpers.lua b/xCT+/config/merge_helpers.lua
new file mode 100644
index 00000000..0459d3ed
--- /dev/null
+++ b/xCT+/config/merge_helpers.lua
@@ -0,0 +1,83 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+
+addon.merges = {}
+addon.replaceSpellId = {}
+
+do
+ local _working_category = ""
+ local _working_category_order = 0
+ local _working_desc = ""
+
+ -- Switches to a class
+ local function class(name)
+ _working_category = name
+ _working_category_order = _working_category_order + 1
+ end
+
+ -- Switches to a spec of a class (set before)
+ local function spec(id)
+ _working_desc = tonumber(id) or id
+ _working_category_order = _working_category_order + 1
+ end
+
+ -- Switches to another header
+ local function header(title)
+ _working_category = title
+ _working_desc = ""
+ _working_category_order = _working_category_order + 1
+ end
+
+ -- Adds a spell to the merge list
+ -- Either call class() & spec() or header() before!
+ local function spell(id, interval)
+ addon.merges[id] = {
+ category = _working_category,
+ categoryOrder = _working_category_order, -- doesnt matter
+ interval = tonumber(interval) or interval,
+ desc = _working_desc, -- its the spec ID
+ }
+ end
+
+ -- Adds a merge for a racial spell
+ -- Call header() before!
+ local function racial_spell(id, interval)
+ addon.merges[id] = {
+ category = _working_category,
+ categoryOrder = _working_category_order,
+ interval = tonumber(interval) or interval,
+ racial_spell = true,
+ }
+ end
+
+ -- Adds an item to the merge list
+ -- Call header() before!
+ local function item(id, interval)
+ addon.merges[id] = {
+ category = _working_category,
+ categoryOrder = _working_category_order,
+ interval = tonumber(interval) or interval,
+ }
+ end
+
+ -- If spell with the "id" is found, use the "replacementId" instead
+ -- e. g. in order to merge multiple spell variants into one message
+ local function alias(id, replacementId)
+ addon.replaceSpellId[id] = replacementId
+ end
+
+ addon.merge_helpers = { spell, class, spec, alias, item, header, racial_spell }
+end
diff --git a/xCT+/config/merge_legion.lua b/xCT+/config/merge_legion.lua
new file mode 100644
index 00000000..2aafc625
--- /dev/null
+++ b/xCT+/config/merge_legion.lua
@@ -0,0 +1,61 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, alias, item, header = unpack(addon.merge_helpers)
+
+header(" 7.x |cff93BE3D" .. L["Legion"] .. "|r")
+do
+ item(188091, 1.5) -- Potion: Potion of Deadly Grace
+ item(188028, 1.5) -- Potion: Potion of the Old War
+
+ item(225623, 1.5) -- Food: Fishbrul Special
+ alias(225624, 225623) -- Pepper Breath
+ alias(201573, 225623) -- Pepper Breath
+ alias(233150, 188028) -- Hack to give the Old War potion an icon
+
+ -- Trinkets
+ item(215047, 3.0) -- Trinket: Terrorbound Nexus
+ item(222168, 5) -- Trinket: Spontaneous Appendages
+ item(214169, 5) -- Trinket: Spiked Counterweight
+ item(215047, 0) -- Trinket: Terrorbound Nexus
+ item(213786, 5) -- Trinket: Corrupted Starlight
+ item(221845, 5) -- Trinket: Twisting Wind
+ item(214350, 5) -- Trinket: Oakheart's Gnarled Root
+ alias(228780, 214169) -- Brutal Haymaker (Spiked Counterweight)
+ alias(213782, 213786) -- Trinket: Corrupted Starlight
+ alias(213833, 213786) -- Trinket: Corrupted Starlight
+ alias(213784, 213786) -- Trinket: Corrupted Starlight
+ alias(213785, 213786) -- Trinket: Corrupted Starlight
+ alias(221865, 221845) -- Trinket: Twisting Wind
+
+ -- Trinkets: Nighthold
+ item(225777, 5) -- Trinket: Draught of Souls
+ item(225731, 0) -- Trinket: Icon of Rot
+ item(225764, 5) -- Trinket: Star Gate
+
+ -- Legendaries
+ item(207694, 0) -- Legendary: Cinidaria, the Symbiote
+ item(210999, 5) -- Legendary: Obsidian Stone Spaulders
+
+ -- Val'sharah
+ item(202917, 5) -- Trinket: Temple Priestess' Charm
+ item(202891, 5) -- Trinket: Lodestone of the Stormbreaker
+
+ -- Raids
+ item(215300, 0) -- Elerethe Renferal: Web of Pain [Tanks]
+ item(215307, 0) -- Elerethe Renferal: Web of Pain [Other]
+ item(223699, 0) -- Dragons of Nightmare: Volatile Infection
+end
diff --git a/xCT+/config/merge_mop.lua b/xCT+/config/merge_mop.lua
new file mode 100644
index 00000000..83752656
--- /dev/null
+++ b/xCT+/config/merge_mop.lua
@@ -0,0 +1,38 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, _, item, header = unpack(addon.merge_helpers)
+
+header(" 5.x |cffF1A864" .. L["Mists of Pandaria"] .. "|r")
+do
+ item(147891, 5) -- Legendary Cloak for Melee
+ item(148008, 5) -- Legendary Cloak for Casters
+ item(148009, 0) -- Legendary Cloak for Healers
+ item(149276, 5) -- Legendary Cloak for Hunters
+
+ -- Trinket: Kardris' Toxic Totem
+ item(146061, 5) -- Trinket: Kardris' Toxic Totem (Melees)
+ item(146063, 5) -- Trinket: Kardris' Toxic Totem (Holy Damage)
+ item(146064, 5) -- Trinket: Kardris' Toxic Totem (Balance Druids)
+ item(146065, 5) -- Trinket: Kardris' Toxic Totem (Priests, Warlocks)
+ item(146067, 5) -- Trinket: Kardris' Toxic Totem (Mages)
+ item(146069, 5) -- Trinket: Kardris' Toxic Totem (Hunters)
+ item(146071, 5) -- Trinket: Kardris' Toxic Totem (Elemental Shamans)
+ item(146070, 5) -- Trinket: Kardris' Toxic Totem (Mages)
+ item(146075, 5) -- Trinket: Kardris' Toxic Totem (Windwalker Monks)
+ item(146177, 5) -- Trinket: Kardris' Toxic Totem (Priest, Paladin)
+ item(146178, 5) -- Trinket: Kardris' Toxic Totem (Druid, Monk)
+end
diff --git a/xCT+/config/merge_race.lua b/xCT+/config/merge_race.lua
new file mode 100644
index 00000000..41a2db40
--- /dev/null
+++ b/xCT+/config/merge_race.lua
@@ -0,0 +1,28 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+
+-- The order here is relevant!
+local _, _, _, _, _, header, racial_spell = unpack(addon.merge_helpers)
+
+header(C_CreatureInfo.GetRaceInfo(30).raceName) -- Lightforged Draenei
+do
+ racial_spell(256893, 1.0) -- Light's Judgment
+end
+
+header(C_CreatureInfo.GetRaceInfo(29).raceName) -- Void Elf
+do
+ racial_spell(259756, 2.5) -- Entropic embrace
+end
diff --git a/xCT+/config/merge_shadowlands.lua b/xCT+/config/merge_shadowlands.lua
new file mode 100644
index 00000000..2994fbe3
--- /dev/null
+++ b/xCT+/config/merge_shadowlands.lua
@@ -0,0 +1,32 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, alias, item, header = unpack(addon.merge_helpers)
+
+header(" 9.x |cffd2d3d8" .. L["Shadowlands"] .. "|r")
+do
+ alias(344540, 180117) -- Trinket: Empyreal Ordnance dot
+
+ item(178769, 0) -- Trinket: Infinitely Divisible Ooze
+ alias(345495, 178769) -- Infinitely Divisible Ooze damage
+
+ alias(345638, 178772) -- Satchel of Misbegotten Minions damage
+
+ item(184021, 0) -- Trinket: Glyph of Assimilation
+ alias(345319, 184021) -- Glyph of Assimilation dot
+
+ item(324184, 5) -- Enchant: Lightless Force
+end
diff --git a/xCT+/config/merge_wod.lua b/xCT+/config/merge_wod.lua
new file mode 100644
index 00000000..1905aca3
--- /dev/null
+++ b/xCT+/config/merge_wod.lua
@@ -0,0 +1,29 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+local _, _, _, _, item, header = unpack(addon.merge_helpers)
+
+header(" 6.x |cffA32C12" .. L["Warlords of Draenor"] .. "|r")
+do
+ -- WoD Trinkets
+ item(184280, 5) -- Trinket: Mirror of the Blademaster
+ item(184256, 5) -- Trinket: Empty Drinking Horn
+ item(185098, 5) -- Trinket: Soothing Breeze
+ item(185321, 5) -- Trinket: Seed of Creation (Guardian)
+
+ -- HFC
+ item(180223, 5) -- HFC Construct #4
+end
diff --git a/xCT+/config/profile.lua b/xCT+/config/profile.lua
new file mode 100644
index 00000000..3196e0d3
--- /dev/null
+++ b/xCT+/config/profile.lua
@@ -0,0 +1,1550 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- This file is a static default profile. After your first profile is created, editing this file will do nothing.
+local ADDON_NAME, addon = ...
+
+-- taken from: https://github.com/Gethe/wow-ui-source/blob/e337b8949ffad2876ea0489d8331db2414342d32/FrameXML/CombatFeedback.lua
+do -- blizzard hiding globals?
+ SCHOOL_MASK_NONE = 0x00
+ SCHOOL_MASK_PHYSICAL = 0x01
+ SCHOOL_MASK_HOLY = 0x02
+ SCHOOL_MASK_FIRE = 0x04
+ SCHOOL_MASK_NATURE = 0x08
+ SCHOOL_MASK_FROST = 0x10
+ SCHOOL_MASK_SHADOW = 0x20
+ SCHOOL_MASK_ARCANE = 0x40
+end
+
+addon.defaults = {
+ profile = {
+ hideConfig = true,
+ bypassCVars = false,
+
+ blizzardFCT = {
+ blizzardHeadNumbers = false, -- enable the head numbers
+ enabled = false, -- enable custom font for head numbers
+ font = "Condensed Bold (xCT+)",
+ fontName = [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\OpenSans-CondBold.ttf]],
+ fontSize = 32, -- unused
+ fontOutline = "2OUTLINE", -- unused
+
+ -- CVars
+ enableFloatingCombatText = false,
+ floatingCombatTextAllSpellMechanics = false,
+ floatingCombatTextAuras = false,
+ floatingCombatTextCombatDamage = false,
+ floatingCombatTextCombatDamageAllAutos = false,
+ floatingCombatTextCombatHealing = false,
+ floatingCombatTextCombatHealingAbsorbSelf = false,
+ floatingCombatTextCombatHealingAbsorbTarget = false,
+ floatingCombatTextCombatLogPeriodicSpells = false,
+ floatingCombatTextCombatState = false,
+ floatingCombatTextComboPoints = false,
+ floatingCombatTextDamageReduction = false,
+ floatingCombatTextDodgeParryMiss = false,
+ floatingCombatTextEnergyGains = false,
+ floatingCombatTextFloatMode = false,
+ floatingCombatTextFriendlyHealers = false,
+ floatingCombatTextHonorGains = false,
+ floatingCombatTextLowManaHealth = false,
+ floatingCombatTextPeriodicEnergyGains = false,
+ floatingCombatTextPetMeleeDamage = false,
+ floatingCombatTextPetSpellDamage = false,
+ floatingCombatTextReactives = false,
+ floatingCombatTextRepChanges = false,
+ floatingCombatTextSpellMechanics = false,
+ floatingCombatTextSpellMechanicsOther = false,
+
+ floatingCombatTextCombatDamageDirectionalOffset = 1,
+ floatingCombatTextCombatDamageDirectionalScale = 1,
+ },
+
+ SpellColors = {
+ -- Vanilla Schools
+ [tostring(SCHOOL_MASK_PHYSICAL)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_PHYSICAL,
+ default = { 1.00, 1.00, 1.00 },
+ },
+ [tostring(SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_HOLY,
+ default = { 1.00, 1.00, 0.30 },
+ },
+ [tostring(SCHOOL_MASK_FIRE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FIRE,
+ default = { 1.00, 0.15, 0.18 },
+ },
+ [tostring(SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_NATURE,
+ default = { 0.40, 1.00, 0.40 },
+ },
+ [tostring(SCHOOL_MASK_FROST)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FROST,
+ default = { 0.30, 0.30, 0.90 },
+ },
+ [tostring(SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOW,
+ default = { 1.00, 0.70, 1.00 },
+ },
+ [tostring(SCHOOL_MASK_ARCANE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_ARCANE,
+ default = { 0.75, 0.75, 0.75 },
+ },
+
+ -- Physical and a Magical
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FIRE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FLAMESTRIKE,
+ default = { 1.00, 0.58, 0.59 },
+ },
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FROST)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FROSTSTRIKE,
+ default = { 0.65, 0.65, 0.95 },
+ },
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_ARCANE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SPELLSTRIKE,
+ default = { 0.87, 0.87, 0.87 },
+ },
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_STORMSTRIKE,
+ default = { 0.70, 1.00, 0.70 },
+ },
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOWSTRIKE,
+ default = { 1.00, 0.85, 1.00 },
+ },
+ [tostring(SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_HOLYSTRIKE,
+ default = { 1.00, 1.00, 0.83 },
+ },
+
+ -- Two Magical Schools
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FROSTFIRE,
+ default = { 0.65, 0.23, 0.54 },
+ },
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_ARCANE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SPELLFIRE,
+ default = { 0.87, 0.45, 0.47 },
+ },
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FIRESTORM,
+ default = { 0.70, 0.58, 0.29 },
+ },
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOWFLAME,
+ default = { 1.00, 0.43, 0.59 },
+ },
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_HOLYFIRE,
+ default = { 1.00, 0.58, 0.24 },
+ },
+ [tostring(SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SPELLFROST,
+ default = { 0.53, 0.53, 0.83 },
+ },
+ [tostring(SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_FROSTSTORM,
+ default = { 0.35, 0.65, 0.65 },
+ },
+ [tostring(SCHOOL_MASK_FROST + SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOWFROST,
+ default = { 0.65, 0.50, 0.95 },
+ },
+ [tostring(SCHOOL_MASK_FROST + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_HOLYFROST,
+ default = { 0.65, 0.65, 0.60 },
+ },
+ [tostring(SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SPELLSTORM,
+ default = { 0.58, 0.87, 0.58 },
+ },
+ [tostring(SCHOOL_MASK_ARCANE + SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SPELLSHADOW,
+ default = { 0.87, 0.73, 0.87 },
+ },
+ [tostring(SCHOOL_MASK_ARCANE + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_DIVINE,
+ default = { 0.87, 0.87, 0.53 },
+ },
+ [tostring(SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOWSTORM,
+ default = { 0.70, 0.85, 0.70 },
+ },
+ [tostring(SCHOOL_MASK_NATURE + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_HOLYSTORM,
+ default = { 0.70, 1.00, 0.35 },
+ },
+ [tostring(SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_SHADOWLIGHT,
+ default = { 1.00, 0.85, 0.65 },
+ },
+
+ -- Three or More Schools
+ [tostring(SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE)] = {
+ enabled = false,
+ desc = STRING_SCHOOL_ELEMENTAL,
+ default = { 0.57, 0.48, 0.49 },
+ },
+
+ [tostring(
+ SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW
+ )] = {
+ enabled = false,
+ desc = STRING_SCHOOL_CHROMATIC,
+ default = { 0.69, 0.58, 0.65 },
+ },
+
+ [tostring(
+ SCHOOL_MASK_FIRE
+ + SCHOOL_MASK_FROST
+ + SCHOOL_MASK_ARCANE
+ + SCHOOL_MASK_NATURE
+ + SCHOOL_MASK_SHADOW
+ + SCHOOL_MASK_HOLY
+ )] = {
+ enabled = false,
+ desc = STRING_SCHOOL_MAGIC,
+ default = { 0.74, 0.65, 0.59 },
+ },
+
+ [tostring(
+ SCHOOL_MASK_PHYSICAL
+ + SCHOOL_MASK_FIRE
+ + SCHOOL_MASK_FROST
+ + SCHOOL_MASK_ARCANE
+ + SCHOOL_MASK_NATURE
+ + SCHOOL_MASK_SHADOW
+ + SCHOOL_MASK_HOLY
+ )] = {
+ enabled = false,
+ desc = STRING_SCHOOL_CHAOS,
+ default = { 0.78, 0.70, 0.65 },
+ },
+ },
+
+ frameSettings = {
+ clearLeavingCombat = false,
+ showGrid = true,
+ showPositions = true,
+ frameStrata = "5HIGH",
+ },
+
+ megaDamage = {
+ thousandSymbol = "|cffFF8000K|r",
+ millionSymbol = "|cffFF0000M|r",
+ billionSymbol = "|cffFF0000G|r",
+ decimalPoint = true,
+ critPrefix = "|cffFF0000*|r",
+ critSuffix = "|cffFF0000*|r"
+ },
+
+ frames = {
+ general = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = 0,
+ Y = 224,
+ Width = 512,
+ Height = 128,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "CENTER",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ showInterrupts = true,
+ showDispells = true,
+ showIncomingDispells = true,
+ showPartyKills = true,
+ showBuffs = true,
+ showDebuffs = true,
+ showLowManaHealth = true,
+ showCombatState = true,
+ showRepChanges = true,
+ showHonorGains = true,
+ showProfessionSkillups = true,
+ },
+
+ outgoing = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = 400,
+ Y = 0,
+ Width = 164,
+ Height = 512,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "RIGHT",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- name formatting
+ names = {
+ -- appearance
+ namePrefix = " |cffFFFFFF<|r",
+ namePostfix = "|cffFFFFFF>|r",
+
+ -- events from a player's character
+ PLAYER = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true,
+ removeRealmName = true,
+ enableCustomNameColor = false,
+ customNameColor = { 1, 1, 1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from a npc
+ NPC = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true, -- Always On (Not in Options)
+ removeRealmName = false, -- Always Off (Not in Options)
+ enableCustomNameColor = true, -- Always On (Not in Options)
+ customNameColor = { 0.3, 0, 0.3 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from the environment
+ ENVIRONMENT = {
+ nameType = 0,
+ },
+ },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ iconsEnabledAutoAttack = true,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ enableAutoAttack_Outgoing = true, -- OLD: enableAutoAttack
+ enablePetAutoAttack_Outgoing = true,
+
+ enableOutDmg = true,
+ enablePetDmg = true,
+ enableVehicleDmg = true,
+ enableDotDmg = true,
+ enableImmunes = true,
+ enableMisses = true,
+ enableAbsorbs = true,
+ enablePartialMisses = false,
+ showHighestPartialMiss = false,
+ enableKillCommand = false,
+ },
+
+ critical = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = 192,
+ Y = 0,
+ Width = 256,
+ Height = 140,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 24,
+ fontOutline = "2OUTLINE",
+ fontJustify = "RIGHT",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- name formatting
+ names = {
+
+ -- appearance
+ namePrefix = " |cffFFFFFF<|r",
+ namePostfix = "|cffFFFFFF>|r",
+
+ -- events from a player's character
+ PLAYER = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true,
+ removeRealmName = true,
+ enableCustomNameColor = false,
+ customNameColor = { 1, 1, 1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from a npc
+ NPC = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true, -- Always On (Not in Options)
+ removeRealmName = false, -- Always Off (Not in Options)
+ enableCustomNameColor = true, -- Always On (Not in Options)
+ customNameColor = { 0.3, 0, 0.3 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from the environment
+ ENVIRONMENT = {
+ nameType = 0, -- NOT SHOWN
+ },
+ },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ enableAutoAttack_Critical = true, -- OLD: showSwing
+ prefixAutoAttack_Critical = true, -- OLD: prefixSwing
+ petCrits = false,
+ },
+
+ outgoing_healing = {
+ enabledFrame = false,
+ secondaryFrame = 2,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = 400,
+ Y = 0,
+ Width = 164,
+ Height = 512,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "RIGHT",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- name formatting
+ names = {
+ -- appearance
+ namePrefix = " |cffFFFFFF<|r",
+ namePostfix = "|cffFFFFFF>|r",
+
+ -- events from a player's character
+ PLAYER = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true,
+ removeRealmName = true,
+ enableCustomNameColor = false,
+ customNameColor = { 1, 1, 1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from a npc
+ NPC = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 0,
+
+ enableNameColor = true, -- Always On (Not in Options)
+ removeRealmName = false, -- Always Off (Not in Options)
+ enableCustomNameColor = true, -- Always On (Not in Options)
+ customNameColor = { 0.3, 0, 0.3 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from the environment
+ ENVIRONMENT = {
+ nameType = 0,
+ },
+ },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ iconsEnabledAutoAttack = true,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ enableAutoAttack_Outgoing = true, -- OLD: enableAutoAttack
+ enablePetAutoAttack_Outgoing = true,
+
+ enableOutHeal = true,
+ enableHots = true,
+ enableImmunes = true,
+ enableMisses = true,
+
+ enableOverhealing = true,
+ enableOverhealingFormat = false,
+ enableOverhealingSubtraction = false,
+ overhealingPrefix = " |cffFFFFFF(O: ",
+ overhealingPostfix = ")|r",
+ hideAbsorbedOutgoingHeals = false,
+ },
+
+ damage = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "top",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = -288,
+ Y = -80,
+ Width = 448,
+ Height = 160,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "LEFT",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- name formatting
+ names = {
+
+ -- appearance
+ namePrefix = " |cffFFFFFF<|r",
+ namePostfix = "|cffFFFFFF>|r",
+
+ -- events from a player's character
+ PLAYER = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 2,
+
+ enableNameColor = true,
+ removeRealmName = true,
+ enableCustomNameColor = false,
+ customNameColor = { 1, 1, 1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from a npc
+ NPC = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 2,
+
+ enableNameColor = true, -- Always On (Not in Options)
+ removeRealmName = false, -- Always Off (Not in Options)
+ enableCustomNameColor = true, -- Always On (Not in Options)
+ customNameColor = { 0.3, 0, 0.3 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from the environment
+ ENVIRONMENT = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Environment
+ -- 2 = Environment Type
+ -- 3 = Both ("Environment - Environment Type")
+ -- 4 = Both ("Environment Type - Environment")
+ nameType = 2,
+
+ enableNameColor = true,
+ enableCustomNameColor = true,
+ removeRealmName = false, -- Always Off (Not in Options)
+ customNameColor = { 0.32, 0.317, 0.1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+ },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 14,
+ iconsEnabledAutoAttack = true,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- Special Tweaks
+ showDodgeParryMiss = true,
+ showDamageReduction = true,
+ },
+
+ healing = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- positioon
+ X = -288,
+ Y = 88,
+ Width = 448,
+ Height = 144,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "LEFT",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- name formatting
+ names = {
+
+ -- appearance
+ namePrefix = " |cffFFFFFF<|r",
+ namePostfix = "|cffFFFFFF>|r",
+
+ -- events from a player's character
+ PLAYER = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 1,
+
+ enableNameColor = true,
+ removeRealmName = true,
+ enableCustomNameColor = false,
+ customNameColor = { 1, 1, 1 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from a npc
+ NPC = {
+ -- Name Types:
+ -- 0 = None
+ -- 1 = Source Name
+ -- 2 = Spell Name
+ -- 3 = Both ("Source Name - Spell Name")
+ -- 4 = Both ("Spell Name - Source Name")
+ nameType = 2,
+
+ enableNameColor = true, -- Always On (Not in Options)
+ removeRealmName = false, -- Always On (Not in Options)
+ enableCustomNameColor = true, -- Always On (Not in Options)
+ customNameColor = { 0.3, 0, 0.3 },
+
+ enableSpellColor = true,
+ enableCustomSpellColor = false,
+ customSpellColor = { 1, 1, 1 },
+ },
+
+ -- events from the environment
+ ENVIRONMENT = {
+ nameType = 0, -- NOT SHOWN
+ },
+ },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ enableOverHeal = true,
+ hideAbsorbedHeals = false,
+ enableSelfAbsorbs = true,
+ showOnlyMyHeals = false,
+ showOnlyPetHeals = false,
+ },
+
+ --[[class = {
+ enabledFrame = true,
+ alpha = 100,
+
+ -- position
+ X = 0,
+ Y = 64,
+ Width = 64,
+ Height = 64,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 64,
+ fontOutline = "2OUTLINE",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+ },]]
+
+ power = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "bottom",
+ alpha = 100,
+ megaDamage = true,
+
+ -- position
+ X = 0,
+ Y = -16,
+ Width = 128,
+ Height = 96,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 17,
+ fontOutline = "2OUTLINE",
+ fontJustify = "CENTER",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ showEnergyGains = true,
+ showEnergyType = true,
+
+ -- Generated from "Blizzard Add-On's/Constants.lua"
+ disableResource_MANA = false,
+ disableResource_RAGE = false,
+ disableResource_FOCUS = false,
+ disableResource_ENERGY = false,
+
+ disableResource_RUNES = true,
+ disableResource_RUNIC_POWER = false,
+ disableResource_SOUL_SHARDS = false,
+ disableResource_LUNAR_POWER = true,
+
+ disableResource_CHI_POWER = true,
+ disableResource_HOLY_POWER = false,
+ disableResource_INSANITY_POWER = false,
+ disableResource_MAELSTROM_POWER = true,
+
+ disableResource_ARCANE_CHARGES = false,
+ disableResource_FURY = false,
+ disableResource_PAIN = false,
+ },
+
+ procs = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "top",
+ alpha = 100,
+
+ -- position
+ X = 0,
+ Y = -256,
+ Width = 294,
+ Height = 64,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 24,
+ fontOutline = "2OUTLINE",
+ fontJustify = "CENTER",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+ },
+
+ loot = {
+ enabledFrame = true,
+ secondaryFrame = 0,
+ insertText = "top",
+ alpha = 100,
+
+ -- position
+ X = 0,
+ Y = -352,
+ Width = 512,
+ Height = 128,
+
+ -- fonts
+ font = "Condensed Bold (xCT+)",
+ fontSize = 18,
+ fontOutline = "2OUTLINE",
+ fontJustify = "CENTER",
+
+ -- font shadow
+ enableFontShadow = true,
+ fontShadowColor = { 0, 0, 0, 0.6 },
+ fontShadowOffsetX = 2,
+ fontShadowOffsetY = -2,
+
+ -- font colors
+ customColor = false,
+ fontColor = { 1.00, 1.00, 1.00 },
+
+ -- icons
+ iconsEnabled = true,
+ iconsSize = 16,
+ spacerIconsEnabled = true,
+
+ -- scrollable
+ enableScrollable = false,
+ scrollableLines = 10,
+ scrollableInCombat = false,
+
+ -- fading text
+ enableCustomFade = true,
+ enableFade = true,
+ fadeTime = 0.3,
+ visibilityTime = 5,
+
+ -- special tweaks
+ showItems = true,
+ showItemTypes = true,
+ showMoney = true,
+ showItemTotal = true,
+ showCrafted = true,
+ showQuest = true,
+ showPurchased = false,
+ colorBlindMoney = false,
+ filterItemQuality = 3,
+ },
+ },
+
+ spells = {
+ enableMerger = true, -- enable/disable spam merger
+ mergeEverythingInterval = 0.5,
+ mergeIncomingHealingInterval = 5,
+ mergeIncomingDamageInterval = 1,
+ mergeIncomingMissesInterval = 3,
+ mergeOutgoingDamageMissesInterval = 3,
+ mergeDispellInterval = 1,
+ mergeReputationInterval = 3,
+ mergePetInterval = 3,
+ mergeVehicle = true,
+ mergePetColor = { 1, 0.5, 0 },
+ mergeVehicleColor = { 0, 0.5, 1 },
+
+ -- Only one of these can be true
+ mergeDontMergeCriticals = true,
+ mergeCriticalsWithOutgoing = false,
+ mergeCriticalsByThemselves = false,
+ mergeHideMergedCriticals = false,
+
+ -- Abbreviate or Groups Settings
+ formatAbbreviate = false,
+ formatGroups = true,
+
+ -- This gets dynamically generated
+ merge = {},
+
+ -- yes this is supposed to be blank :P
+ -- it is dynamically generated in core.lua
+ items = {},
+ },
+
+ spellFilter = {
+ whitelistBuffs = false,
+ whitelistDebuffs = false,
+ whitelistSpells = false,
+ whitelistProcs = false,
+ whitelistItems = false,
+ whitelistDamage = false,
+ whitelistHealing = false,
+ trackSpells = true,
+
+ listSpells = {}, -- Used to filter outgoing spells (Spell ID)
+ listBuffs = {}, -- Used to filter gains/fades of buffs (Spell Name)
+ listDebuffs = {}, -- Used to filter gains/fades of debuffs (Spell Name)
+ listProcs = {}, -- Used to filter spell procs (Proc Name)
+ listItems = {}, -- Used to filter Items (Item ID)
+ listDamage = {}, -- Used to filter incoming damage (Spell ID)
+ listHealing = {}, -- Used to filter incoming healing (Spell ID)
+
+ -- Minimal Spell Amount
+ filterPowerValue = 0,
+
+ filterOutgoingDamageValue = 0,
+ filterOutgoingDamageCritEnabled = false,
+ filterOutgoingDamageCritValue = 0,
+
+ filterOutgoingHealingValue = 0,
+ filterOutgoingHealingCritEnabled = false,
+ filterOutgoingHealingCritValue = 0,
+
+ filterIncomingDamageValue = 0,
+ filterIncomingDamageCritEnabled = false,
+ filterIncomingDamageCritValue = 0,
+
+ filterIncomingHealingValue = 0,
+ filterIncomingHealingCritEnabled = false,
+ filterIncomingHealingCritValue = 0,
+ },
+
+ Colors = {
+ -- General
+ combatEntering = {
+ enabled = false,
+ desc = "Entering Combat",
+ category = "general",
+ default = { 1.00, 0.10, 0.10 },
+ },
+ combatLeaving = {
+ enabled = false,
+ desc = "Leaving Combat",
+ category = "general",
+ default = { 0.10, 1.00, 0.10 },
+ },
+ interrupts = {
+ enabled = false,
+ desc = "Interrupts",
+ category = "general",
+ default = { 1.00, 0.50, 0.00 },
+ },
+ killingBlow = {
+ enabled = false,
+ desc = "Killing Blows",
+ category = "general",
+ default = { 0.20, 1.00, 0.20 },
+ },
+ buffsGained = {
+ enabled = false,
+ desc = "Buffs Gained",
+ category = "general",
+ default = { 1.00, 0.50, 0.50 },
+ },
+ buffsFaded = {
+ enabled = false,
+ desc = "Buffs Faded",
+ category = "general",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ debuffsGained = {
+ enabled = false,
+ desc = "Debuffs Gained",
+ category = "general",
+ default = { 1.00, 0.10, 0.10 },
+ },
+ debuffsFaded = {
+ enabled = false,
+ desc = "Debuffs Faded",
+ category = "general",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ dispellBuffs = {
+ enabled = false,
+ desc = "Buffs",
+ category = "general",
+ default = { 0.00, 1.00, 0.50 },
+ },
+ dispellDebuffs = {
+ enabled = false,
+ desc = "Debuffs",
+ category = "general",
+ default = { 1.00, 0.00, 0.50 },
+ },
+ dispellStolen = {
+ enabled = false,
+ desc = "Spell Stolen",
+ category = "general",
+ default = { 0.31, 0.71, 1.00 },
+ },
+ lowResourcesHealth = {
+ enabled = false,
+ desc = "Low Health",
+ category = "general",
+ default = { 1.00, 0.10, 0.10 },
+ },
+ lowResourcesMana = {
+ enabled = false,
+ desc = "Low Mana",
+ category = "general",
+ default = { 1.00, 0.10, 0.10 },
+ },
+
+ -- Outgoing Damage
+ melee = {
+ enabled = false,
+ desc = "Auto Attack Damage",
+ category = "outgoing_damage",
+ default = { 1.00, 1.00, 1.00 },
+ },
+ misstypesOut = {
+ enabled = false,
+ desc = "Missed Attacks",
+ category = "outgoing_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+
+ -- Outgoing Healing
+ healingOut = {
+ enabled = false,
+ desc = "Healing",
+ category = "outgoing_healing",
+ default = { 0.10, 0.75, 0.10 },
+ },
+ healingOutPeriodic = {
+ enabled = false,
+ desc = "Healing (Periodic)",
+ category = "outgoing_healing",
+ default = { 0.10, 0.50, 0.10 },
+ },
+
+ -- Outgoing (Criticals)
+ meleeCrit = {
+ enabled = false,
+ desc = "Auto Attack Damage (Critical)",
+ category = "outgoing_criticals",
+ default = { 1.00, 1.00, 0.00 },
+ },
+ healingOutCritical = {
+ enabled = false,
+ desc = "Healing (Critical)",
+ category = "outgoing_criticals",
+ default = { 0.10, 1.00, 0.10 },
+ },
+
+ -- Incoming Damage
+ damageTaken = {
+ enabled = false,
+ desc = "Physical Damage",
+ category = "incoming_damage",
+ default = { 0.75, 0.10, 0.10 },
+ },
+ damageTakenCritical = {
+ enabled = false,
+ desc = "Critical Physical Damage",
+ category = "incoming_damage",
+ default = { 1.00, 0.10, 0.10 },
+ },
+ spellDamageTaken = {
+ enabled = false,
+ desc = "Spell Damage",
+ category = "incoming_damage",
+ default = { 0.75, 0.30, 0.85 },
+ },
+ spellDamageTakenCritical = {
+ enabled = false,
+ desc = "Critical Spell Damage",
+ category = "incoming_damage",
+ default = { 0.75, 0.30, 0.85 },
+ },
+ missTypeMiss = {
+ enabled = false,
+ desc = "Missed",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeDodge = {
+ enabled = false,
+ desc = "Dodged",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeParry = {
+ enabled = false,
+ desc = "Parry",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeEvade = {
+ enabled = false,
+ desc = "Evade",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeDeflect = {
+ enabled = false,
+ desc = "Deflect",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeImmune = {
+ enabled = false,
+ desc = "Immune",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeReflect = {
+ enabled = false,
+ desc = "Reflect",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeResist = {
+ enabled = false,
+ desc = "Resisted",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeBlock = {
+ enabled = false,
+ desc = "Blocked",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeAbsorb = {
+ enabled = false,
+ desc = "Absorbed",
+ category = "incoming_damage",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ missTypeResistPartial = {
+ enabled = false,
+ desc = "Resisted |cff798BDD(Partial)|r",
+ category = "incoming_damage",
+ default = { 0.75, 0.50, 0.50 },
+ },
+ missTypeBlockPartial = {
+ enabled = false,
+ desc = "Blocked |cff798BDD(Partial)|r",
+ category = "incoming_damage",
+ default = { 0.75, 0.50, 0.50 },
+ },
+ missTypeAbsorbPartial = {
+ enabled = false,
+ desc = "Absorbed |cff798BDD(Partial)|r",
+ category = "incoming_damage",
+ default = { 0.75, 0.50, 0.50 },
+ },
+
+ -- Incoming Healing
+ healingTaken = {
+ enabled = false,
+ desc = "Healing",
+ category = "incoming_healing",
+ default = { 0.10, 0.75, 0.10 },
+ },
+ healingTakenCritical = {
+ enabled = false,
+ desc = "Critical Healing",
+ category = "incoming_healing",
+ default = { 0.10, 1.00, 0.10 },
+ },
+ healingTakenPeriodic = {
+ enabled = false,
+ desc = "Periodic Healing",
+ category = "incoming_healing",
+ default = { 0.10, 0.50, 0.10 },
+ },
+ healingTakenPeriodicCritical = {
+ enabled = false,
+ desc = "Critical Periodic Healing",
+ category = "incoming_healing",
+ default = { 0.10, 0.50, 0.10 },
+ },
+
+ -- Class Power
+ -- https://github.com/Gethe/wow-ui-source/blob/e337b8949ffad2876ea0489d8331db2414342d32
+ -- /AddOns/Blizzard_CombatLog/Blizzard_CombatLog.lua#L1797
+ color_MANA = {
+ enabled = false,
+ desc = MANA,
+ category = "class_power",
+ default = { 0.00, 0.00, 1.00 },
+ },
+ color_RAGE = {
+ enabled = false,
+ desc = RAGE,
+ category = "class_power",
+ default = { 1.00, 0.00, 0.00 },
+ },
+ color_FURY = {
+ enabled = false,
+ desc = FURY,
+ category = "class_power",
+ default = { 0.788, 0.259, 0.992 },
+ },
+ color_PAIN = {
+ enabled = false,
+ desc = PAIN,
+ category = "class_power",
+ default = { 1.000, 0.612, 0.000 },
+ },
+ color_FOCUS = {
+ enabled = false,
+ desc = FOCUS,
+ category = "class_power",
+ default = { 1.00, 0.50, 0.25 },
+ },
+ color_RUNES = {
+ enabled = false,
+ desc = RUNES,
+ category = "class_power",
+ default = { 0.50, 0.50, 0.50 },
+ },
+ color_ENERGY = {
+ enabled = false,
+ desc = ENERGY,
+ category = "class_power",
+ default = { 1.00, 1.00, 0.00 },
+ },
+ color_CHI_POWER = {
+ enabled = false,
+ desc = CHI_POWER,
+ category = "class_power",
+ default = { 0.71, 1.00, 0.92 },
+ },
+ color_HOLY_POWER = {
+ enabled = false,
+ desc = HOLY_POWER,
+ category = "class_power",
+ default = { 0.95, 0.90, 0.60 },
+ },
+ color_RUNIC_POWER = {
+ enabled = false,
+ desc = RUNIC_POWER,
+ category = "class_power",
+ default = { 0.00, 0.82, 1.00 },
+ },
+ color_SOUL_SHARDS = {
+ enabled = false,
+ desc = SOUL_SHARDS,
+ category = "class_power",
+ default = { 0.50, 0.32, 0.55 },
+ },
+ color_LUNAR_POWER = {
+ enabled = false,
+ desc = LUNAR_POWER,
+ category = "class_power",
+ default = { 0.30, 0.52, 0.90 },
+ },
+ color_INSANITY_POWER = {
+ enabled = false,
+ desc = INSANITY_POWER,
+ category = "class_power",
+ default = { 0.40, 0.00, 0.80 },
+ },
+ color_MAELSTROM_POWER = {
+ enabled = false,
+ desc = MAELSTROM_POWER,
+ category = "class_power",
+ default = { 0.00, 0.50, 1.00 },
+ },
+ color_ALTERNATE_POWER = {
+ enabled = false,
+ desc = ALTERNATE_POWER_TEXT,
+ category = "class_power",
+ default = { 0.10, 0.10, 0.98 },
+ },
+ color_ARCANE_CHARGES_POWER = {
+ enabled = false,
+ desc = ARCANE_CHARGES_POWER,
+ category = "class_power",
+ default = { 0.10, 0.10, 0.98 },
+ },
+ -- TODO Vigor (Skyriding) & Evoker
+
+ -- Procs
+ spellProc = {
+ enabled = false,
+ desc = "Spell Procs",
+ category = "procs",
+ default = { 1.00, 0.82, 0.00 },
+ },
+ spellReactive = {
+ enabled = false,
+ desc = "Spell Reactive",
+ category = "procs",
+ default = { 1.00, 0.82, 0.00 },
+ },
+
+ -- Loot, Currency & Money
+ honorGains = {
+ enabled = false,
+ desc = "Honor Gained",
+ category = "loot",
+ default = { 0.10, 0.10, 1.00 },
+ },
+ reputationGain = {
+ enabled = false,
+ desc = "Reputation Gained",
+ category = "loot",
+ default = { 0.10, 0.10, 1.00 },
+ },
+ reputationLoss = {
+ enabled = false,
+ desc = "Reputation Lost",
+ category = "loot",
+ default = { 1.00, 0.10, 0.10 },
+ },
+ },
+ },
+}
diff --git a/xCT+/init.lua b/xCT+/init.lua
new file mode 100644
index 00000000..8d056b9c
--- /dev/null
+++ b/xCT+/init.lua
@@ -0,0 +1,19 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, addon = ...
+addon.engine = LibStub("AceAddon-3.0"):NewAddon(AddonName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
+
+-- Make the main Addon globally accessible
+xCT_Plus = addon
diff --git a/xCT+/libs/UTF8/Changelog-UTF8-v1.1.txt b/xCT+/libs/UTF8/Changelog-UTF8-v1.1.txt
new file mode 100644
index 00000000..da848887
--- /dev/null
+++ b/xCT+/libs/UTF8/Changelog-UTF8-v1.1.txt
@@ -0,0 +1,19 @@
+------------------------------------------------------------------------
+r6 | phanx | 2014-10-28 02:27:07 +0000 (Tue, 28 Oct 2014) | 1 line
+Changed paths:
+ A /tags/v1.1 (from /trunk:5)
+
+Tagging as v1.1
+------------------------------------------------------------------------
+r5 | Phanx | 2014-10-28 02:26:44 +0000 (Tue, 28 Oct 2014) | 1 line
+Changed paths:
+ M /trunk/UTF8.toc
+
+- Update TOC for a new release
+------------------------------------------------------------------------
+r4 | Phanx | 2012-09-07 10:25:33 +0000 (Fri, 07 Sep 2012) | 1 line
+Changed paths:
+ M /trunk/utf8.lua
+
+- Switched from slower x:sub notation to faster upvalued strsub(x)
+------------------------------------------------------------------------
diff --git a/xCT+/libs/UTF8/UTF8.toc b/xCT+/libs/UTF8/UTF8.toc
new file mode 100644
index 00000000..fb236859
--- /dev/null
+++ b/xCT+/libs/UTF8/UTF8.toc
@@ -0,0 +1,15 @@
+## Interface: 60000
+## Version: v1.1
+## X-Curse-Packaged-Version: v1.1
+## X-Curse-Project-Name: UTF8
+## X-Curse-Project-ID: utf8
+## X-Curse-Repository-ID: wow/utf8/mainline
+
+## Title: Lib: UTF8
+## Notes: A library for manipulating UTF-8 strings
+## Author: Pastamancer
+## X-License: BSD License
+## X-Website: http://www.wowace.com/addons/utf8/
+
+utf8data.lua
+utf8.lua
\ No newline at end of file
diff --git a/xCT+/libs/UTF8/include.xml b/xCT+/libs/UTF8/include.xml
new file mode 100644
index 00000000..d2f3d103
--- /dev/null
+++ b/xCT+/libs/UTF8/include.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/xCT+/libs/UTF8/utf8.lua b/xCT+/libs/UTF8/utf8.lua
new file mode 100644
index 00000000..485582cc
--- /dev/null
+++ b/xCT+/libs/UTF8/utf8.lua
@@ -0,0 +1,317 @@
+-- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $
+--
+-- Provides UTF-8 aware string functions implemented in pure lua:
+-- * string.utf8len(s)
+-- * string.utf8sub(s, i, j)
+-- * string.utf8reverse(s)
+--
+-- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these
+-- additional functions are available:
+-- * string.utf8upper(s)
+-- * string.utf8lower(s)
+--
+-- All functions behave as their non UTF-8 aware counterparts with the exception
+-- that UTF-8 characters are used instead of bytes for all units.
+
+--[[
+Copyright (c) 2006-2007, Kyle Smith
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--]]
+
+-- ABNF from RFC 3629
+--
+-- UTF8-octets = *( UTF8-char )
+-- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
+-- UTF8-1 = %x00-7F
+-- UTF8-2 = %xC2-DF UTF8-tail
+-- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
+-- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
+-- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
+-- %xF4 %x80-8F 2( UTF8-tail )
+-- UTF8-tail = %x80-BF
+--
+
+local strbyte, strlen, strsub, type = string.byte, string.len, string.sub, type
+
+-- returns the number of bytes used by the UTF-8 character at byte i in s
+-- also doubles as a UTF-8 character validator
+local function utf8charbytes(s, i)
+ -- argument defaults
+ i = i or 1
+
+ -- argument checking
+ if type(s) ~= "string" then
+ error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")")
+ end
+ if type(i) ~= "number" then
+ error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")")
+ end
+
+ local c = strbyte(s, i)
+
+ -- determine bytes needed for character, based on RFC 3629
+ -- validate byte 1
+ if c > 0 and c <= 127 then
+ -- UTF8-1
+ return 1
+
+ elseif c >= 194 and c <= 223 then
+ -- UTF8-2
+ local c2 = strbyte(s, i + 1)
+
+ if not c2 then
+ error("UTF-8 string terminated early")
+ end
+
+ -- validate byte 2
+ if c2 < 128 or c2 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ return 2
+
+ elseif c >= 224 and c <= 239 then
+ -- UTF8-3
+ local c2 = strbyte(s, i + 1)
+ local c3 = strbyte(s, i + 2)
+
+ if not c2 or not c3 then
+ error("UTF-8 string terminated early")
+ end
+
+ -- validate byte 2
+ if c == 224 and (c2 < 160 or c2 > 191) then
+ error("Invalid UTF-8 character")
+ elseif c == 237 and (c2 < 128 or c2 > 159) then
+ error("Invalid UTF-8 character")
+ elseif c2 < 128 or c2 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ -- validate byte 3
+ if c3 < 128 or c3 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ return 3
+
+ elseif c >= 240 and c <= 244 then
+ -- UTF8-4
+ local c2 = strbyte(s, i + 1)
+ local c3 = strbyte(s, i + 2)
+ local c4 = strbyte(s, i + 3)
+
+ if not c2 or not c3 or not c4 then
+ error("UTF-8 string terminated early")
+ end
+
+ -- validate byte 2
+ if c == 240 and (c2 < 144 or c2 > 191) then
+ error("Invalid UTF-8 character")
+ elseif c == 244 and (c2 < 128 or c2 > 143) then
+ error("Invalid UTF-8 character")
+ elseif c2 < 128 or c2 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ -- validate byte 3
+ if c3 < 128 or c3 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ -- validate byte 4
+ if c4 < 128 or c4 > 191 then
+ error("Invalid UTF-8 character")
+ end
+
+ return 4
+
+ else
+ error("Invalid UTF-8 character")
+ end
+end
+
+-- returns the number of characters in a UTF-8 string
+local function utf8len(s)
+ -- argument checking
+ if type(s) ~= "string" then
+ error("bad argument #1 to 'utf8len' (string expected, got ".. type(s).. ")")
+ end
+
+ local pos = 1
+ local bytes = strlen(s)
+ local len = 0
+
+ while pos <= bytes do
+ len = len + 1
+ pos = pos + utf8charbytes(s, pos)
+ end
+
+ return len
+end
+
+-- install in the string library
+if not string.utf8len then
+ string.utf8len = utf8len
+end
+
+-- functions identically to string.sub except that i and j are UTF-8 characters
+-- instead of bytes
+local function utf8sub(s, i, j)
+ -- argument defaults
+ j = j or -1
+
+ -- argument checking
+ if type(s) ~= "string" then
+ error("bad argument #1 to 'utf8sub' (string expected, got ".. type(s).. ")")
+ end
+ if type(i) ~= "number" then
+ error("bad argument #2 to 'utf8sub' (number expected, got ".. type(i).. ")")
+ end
+ if type(j) ~= "number" then
+ error("bad argument #3 to 'utf8sub' (number expected, got ".. type(j).. ")")
+ end
+
+ local pos = 1
+ local bytes = strlen(s)
+ local len = 0
+
+ -- only set l if i or j is negative
+ local l = (i >= 0 and j >= 0) or utf8len(s)
+ local startChar = (i >= 0) and i or l + i + 1
+ local endChar = (j >= 0) and j or l + j + 1
+
+ -- can't have start before end!
+ if startChar > endChar then
+ return ""
+ end
+
+ -- byte offsets to pass to string.sub
+ local startByte, endByte = 1, bytes
+
+ while pos <= bytes do
+ len = len + 1
+
+ if len == startChar then
+ startByte = pos
+ end
+
+ pos = pos + utf8charbytes(s, pos)
+
+ if len == endChar then
+ endByte = pos - 1
+ break
+ end
+ end
+
+ return strsub(s, startByte, endByte)
+end
+
+-- install in the string library
+if not string.utf8sub then
+ string.utf8sub = utf8sub
+end
+
+-- replace UTF-8 characters based on a mapping table
+local function utf8replace(s, mapping)
+ -- argument checking
+ if type(s) ~= "string" then
+ error("bad argument #1 to 'utf8replace' (string expected, got ".. type(s).. ")")
+ end
+ if type(mapping) ~= "table" then
+ error("bad argument #2 to 'utf8replace' (table expected, got ".. type(mapping).. ")")
+ end
+
+ local pos = 1
+ local bytes = strlen(s)
+ local charbytes
+ local newstr = ""
+
+ while pos <= bytes do
+ charbytes = utf8charbytes(s, pos)
+ local c = strsub(s, pos, pos + charbytes - 1)
+
+ newstr = newstr .. (mapping[c] or c)
+
+ pos = pos + charbytes
+ end
+
+ return newstr
+end
+
+-- identical to string.upper except it knows about unicode simple case conversions
+local function utf8upper(s)
+ return utf8replace(s, utf8_lc_uc)
+end
+
+-- install in the string library
+if not string.utf8upper and utf8_lc_uc then
+ string.utf8upper = utf8upper
+end
+
+-- identical to string.lower except it knows about unicode simple case conversions
+local function utf8lower(s)
+ return utf8replace(s, utf8_uc_lc)
+end
+
+-- install in the string library
+if not string.utf8lower and utf8_uc_lc then
+ string.utf8lower = utf8lower
+end
+
+-- identical to string.reverse except that it supports UTF-8
+local function utf8reverse(s)
+ -- argument checking
+ if type(s) ~= "string" then
+ error("bad argument #1 to 'utf8reverse' (string expected, got ".. type(s).. ")")
+ end
+
+ local bytes = strlen(s)
+ local pos = bytes
+ local charbytes
+ local newstr = ""
+
+ while pos > 0 do
+ c = strbyte(s, pos)
+ while c >= 128 and c <= 191 do
+ pos = pos - 1
+ c = strbyte(pos)
+ end
+
+ charbytes = utf8charbytes(s, pos)
+
+ newstr = newstr .. strsub(s, pos, pos + charbytes - 1)
+
+ pos = pos - 1
+ end
+
+ return newstr
+end
+
+-- install in the string library
+if not string.utf8reverse then
+ string.utf8reverse = utf8reverse
+end
\ No newline at end of file
diff --git a/xCT+/libs/UTF8/utf8data.lua b/xCT+/libs/UTF8/utf8data.lua
new file mode 100644
index 00000000..655f7191
--- /dev/null
+++ b/xCT+/libs/UTF8/utf8data.lua
@@ -0,0 +1,1860 @@
+utf8_lc_uc = {
+ ["a"] = "A",
+ ["b"] = "B",
+ ["c"] = "C",
+ ["d"] = "D",
+ ["e"] = "E",
+ ["f"] = "F",
+ ["g"] = "G",
+ ["h"] = "H",
+ ["i"] = "I",
+ ["j"] = "J",
+ ["k"] = "K",
+ ["l"] = "L",
+ ["m"] = "M",
+ ["n"] = "N",
+ ["o"] = "O",
+ ["p"] = "P",
+ ["q"] = "Q",
+ ["r"] = "R",
+ ["s"] = "S",
+ ["t"] = "T",
+ ["u"] = "U",
+ ["v"] = "V",
+ ["w"] = "W",
+ ["x"] = "X",
+ ["y"] = "Y",
+ ["z"] = "Z",
+ ["µ"] = "Μ",
+ ["à"] = "À",
+ ["á"] = "Á",
+ ["â"] = "Â",
+ ["ã"] = "Ã",
+ ["ä"] = "Ä",
+ ["å"] = "Å",
+ ["æ"] = "Æ",
+ ["ç"] = "Ç",
+ ["è"] = "È",
+ ["é"] = "É",
+ ["ê"] = "Ê",
+ ["ë"] = "Ë",
+ ["ì"] = "Ì",
+ ["í"] = "Í",
+ ["î"] = "Î",
+ ["ï"] = "Ï",
+ ["ð"] = "Ð",
+ ["ñ"] = "Ñ",
+ ["ò"] = "Ò",
+ ["ó"] = "Ó",
+ ["ô"] = "Ô",
+ ["õ"] = "Õ",
+ ["ö"] = "Ö",
+ ["ø"] = "Ø",
+ ["ù"] = "Ù",
+ ["ú"] = "Ú",
+ ["û"] = "Û",
+ ["ü"] = "Ü",
+ ["ý"] = "Ý",
+ ["þ"] = "Þ",
+ ["ÿ"] = "Ÿ",
+ ["ā"] = "Ā",
+ ["ă"] = "Ă",
+ ["ą"] = "Ą",
+ ["ć"] = "Ć",
+ ["ĉ"] = "Ĉ",
+ ["ċ"] = "Ċ",
+ ["č"] = "Č",
+ ["ď"] = "Ď",
+ ["đ"] = "Đ",
+ ["ē"] = "Ē",
+ ["ĕ"] = "Ĕ",
+ ["ė"] = "Ė",
+ ["ę"] = "Ę",
+ ["ě"] = "Ě",
+ ["ĝ"] = "Ĝ",
+ ["ğ"] = "Ğ",
+ ["ġ"] = "Ġ",
+ ["ģ"] = "Ģ",
+ ["ĥ"] = "Ĥ",
+ ["ħ"] = "Ħ",
+ ["ĩ"] = "Ĩ",
+ ["ī"] = "Ī",
+ ["ĭ"] = "Ĭ",
+ ["į"] = "Į",
+ ["ı"] = "I",
+ ["ij"] = "IJ",
+ ["ĵ"] = "Ĵ",
+ ["ķ"] = "Ķ",
+ ["ĺ"] = "Ĺ",
+ ["ļ"] = "Ļ",
+ ["ľ"] = "Ľ",
+ ["ŀ"] = "Ŀ",
+ ["ł"] = "Ł",
+ ["ń"] = "Ń",
+ ["ņ"] = "Ņ",
+ ["ň"] = "Ň",
+ ["ŋ"] = "Ŋ",
+ ["ō"] = "Ō",
+ ["ŏ"] = "Ŏ",
+ ["ő"] = "Ő",
+ ["œ"] = "Œ",
+ ["ŕ"] = "Ŕ",
+ ["ŗ"] = "Ŗ",
+ ["ř"] = "Ř",
+ ["ś"] = "Ś",
+ ["ŝ"] = "Ŝ",
+ ["ş"] = "Ş",
+ ["š"] = "Š",
+ ["ţ"] = "Ţ",
+ ["ť"] = "Ť",
+ ["ŧ"] = "Ŧ",
+ ["ũ"] = "Ũ",
+ ["ū"] = "Ū",
+ ["ŭ"] = "Ŭ",
+ ["ů"] = "Ů",
+ ["ű"] = "Ű",
+ ["ų"] = "Ų",
+ ["ŵ"] = "Ŵ",
+ ["ŷ"] = "Ŷ",
+ ["ź"] = "Ź",
+ ["ż"] = "Ż",
+ ["ž"] = "Ž",
+ ["ſ"] = "S",
+ ["ƀ"] = "Ƀ",
+ ["ƃ"] = "Ƃ",
+ ["ƅ"] = "Ƅ",
+ ["ƈ"] = "Ƈ",
+ ["ƌ"] = "Ƌ",
+ ["ƒ"] = "Ƒ",
+ ["ƕ"] = "Ƕ",
+ ["ƙ"] = "Ƙ",
+ ["ƚ"] = "Ƚ",
+ ["ƞ"] = "Ƞ",
+ ["ơ"] = "Ơ",
+ ["ƣ"] = "Ƣ",
+ ["ƥ"] = "Ƥ",
+ ["ƨ"] = "Ƨ",
+ ["ƭ"] = "Ƭ",
+ ["ư"] = "Ư",
+ ["ƴ"] = "Ƴ",
+ ["ƶ"] = "Ƶ",
+ ["ƹ"] = "Ƹ",
+ ["ƽ"] = "Ƽ",
+ ["ƿ"] = "Ƿ",
+ ["Dž"] = "DŽ",
+ ["dž"] = "DŽ",
+ ["Lj"] = "LJ",
+ ["lj"] = "LJ",
+ ["Nj"] = "NJ",
+ ["nj"] = "NJ",
+ ["ǎ"] = "Ǎ",
+ ["ǐ"] = "Ǐ",
+ ["ǒ"] = "Ǒ",
+ ["ǔ"] = "Ǔ",
+ ["ǖ"] = "Ǖ",
+ ["ǘ"] = "Ǘ",
+ ["ǚ"] = "Ǚ",
+ ["ǜ"] = "Ǜ",
+ ["ǝ"] = "Ǝ",
+ ["ǟ"] = "Ǟ",
+ ["ǡ"] = "Ǡ",
+ ["ǣ"] = "Ǣ",
+ ["ǥ"] = "Ǥ",
+ ["ǧ"] = "Ǧ",
+ ["ǩ"] = "Ǩ",
+ ["ǫ"] = "Ǫ",
+ ["ǭ"] = "Ǭ",
+ ["ǯ"] = "Ǯ",
+ ["Dz"] = "DZ",
+ ["dz"] = "DZ",
+ ["ǵ"] = "Ǵ",
+ ["ǹ"] = "Ǹ",
+ ["ǻ"] = "Ǻ",
+ ["ǽ"] = "Ǽ",
+ ["ǿ"] = "Ǿ",
+ ["ȁ"] = "Ȁ",
+ ["ȃ"] = "Ȃ",
+ ["ȅ"] = "Ȅ",
+ ["ȇ"] = "Ȇ",
+ ["ȉ"] = "Ȉ",
+ ["ȋ"] = "Ȋ",
+ ["ȍ"] = "Ȍ",
+ ["ȏ"] = "Ȏ",
+ ["ȑ"] = "Ȑ",
+ ["ȓ"] = "Ȓ",
+ ["ȕ"] = "Ȕ",
+ ["ȗ"] = "Ȗ",
+ ["ș"] = "Ș",
+ ["ț"] = "Ț",
+ ["ȝ"] = "Ȝ",
+ ["ȟ"] = "Ȟ",
+ ["ȣ"] = "Ȣ",
+ ["ȥ"] = "Ȥ",
+ ["ȧ"] = "Ȧ",
+ ["ȩ"] = "Ȩ",
+ ["ȫ"] = "Ȫ",
+ ["ȭ"] = "Ȭ",
+ ["ȯ"] = "Ȯ",
+ ["ȱ"] = "Ȱ",
+ ["ȳ"] = "Ȳ",
+ ["ȼ"] = "Ȼ",
+ ["ɂ"] = "Ɂ",
+ ["ɇ"] = "Ɇ",
+ ["ɉ"] = "Ɉ",
+ ["ɋ"] = "Ɋ",
+ ["ɍ"] = "Ɍ",
+ ["ɏ"] = "Ɏ",
+ ["ɓ"] = "Ɓ",
+ ["ɔ"] = "Ɔ",
+ ["ɖ"] = "Ɖ",
+ ["ɗ"] = "Ɗ",
+ ["ə"] = "Ə",
+ ["ɛ"] = "Ɛ",
+ ["ɠ"] = "Ɠ",
+ ["ɣ"] = "Ɣ",
+ ["ɨ"] = "Ɨ",
+ ["ɩ"] = "Ɩ",
+ ["ɫ"] = "Ɫ",
+ ["ɯ"] = "Ɯ",
+ ["ɲ"] = "Ɲ",
+ ["ɵ"] = "Ɵ",
+ ["ɽ"] = "Ɽ",
+ ["ʀ"] = "Ʀ",
+ ["ʃ"] = "Ʃ",
+ ["ʈ"] = "Ʈ",
+ ["ʉ"] = "Ʉ",
+ ["ʊ"] = "Ʊ",
+ ["ʋ"] = "Ʋ",
+ ["ʌ"] = "Ʌ",
+ ["ʒ"] = "Ʒ",
+ ["ͅ"] = "Ι",
+ ["ͻ"] = "Ͻ",
+ ["ͼ"] = "Ͼ",
+ ["ͽ"] = "Ͽ",
+ ["ά"] = "Ά",
+ ["έ"] = "Έ",
+ ["ή"] = "Ή",
+ ["ί"] = "Ί",
+ ["α"] = "Α",
+ ["β"] = "Β",
+ ["γ"] = "Γ",
+ ["δ"] = "Δ",
+ ["ε"] = "Ε",
+ ["ζ"] = "Ζ",
+ ["η"] = "Η",
+ ["θ"] = "Θ",
+ ["ι"] = "Ι",
+ ["κ"] = "Κ",
+ ["λ"] = "Λ",
+ ["μ"] = "Μ",
+ ["ν"] = "Ν",
+ ["ξ"] = "Ξ",
+ ["ο"] = "Ο",
+ ["π"] = "Π",
+ ["ρ"] = "Ρ",
+ ["ς"] = "Σ",
+ ["σ"] = "Σ",
+ ["τ"] = "Τ",
+ ["υ"] = "Υ",
+ ["φ"] = "Φ",
+ ["χ"] = "Χ",
+ ["ψ"] = "Ψ",
+ ["ω"] = "Ω",
+ ["ϊ"] = "Ϊ",
+ ["ϋ"] = "Ϋ",
+ ["ό"] = "Ό",
+ ["ύ"] = "Ύ",
+ ["ώ"] = "Ώ",
+ ["ϐ"] = "Β",
+ ["ϑ"] = "Θ",
+ ["ϕ"] = "Φ",
+ ["ϖ"] = "Π",
+ ["ϙ"] = "Ϙ",
+ ["ϛ"] = "Ϛ",
+ ["ϝ"] = "Ϝ",
+ ["ϟ"] = "Ϟ",
+ ["ϡ"] = "Ϡ",
+ ["ϣ"] = "Ϣ",
+ ["ϥ"] = "Ϥ",
+ ["ϧ"] = "Ϧ",
+ ["ϩ"] = "Ϩ",
+ ["ϫ"] = "Ϫ",
+ ["ϭ"] = "Ϭ",
+ ["ϯ"] = "Ϯ",
+ ["ϰ"] = "Κ",
+ ["ϱ"] = "Ρ",
+ ["ϲ"] = "Ϲ",
+ ["ϵ"] = "Ε",
+ ["ϸ"] = "Ϸ",
+ ["ϻ"] = "Ϻ",
+ ["а"] = "А",
+ ["б"] = "Б",
+ ["в"] = "В",
+ ["г"] = "Г",
+ ["д"] = "Д",
+ ["е"] = "Е",
+ ["ж"] = "Ж",
+ ["з"] = "З",
+ ["и"] = "И",
+ ["й"] = "Й",
+ ["к"] = "К",
+ ["л"] = "Л",
+ ["м"] = "М",
+ ["н"] = "Н",
+ ["о"] = "О",
+ ["п"] = "П",
+ ["р"] = "Р",
+ ["с"] = "С",
+ ["т"] = "Т",
+ ["у"] = "У",
+ ["ф"] = "Ф",
+ ["х"] = "Х",
+ ["ц"] = "Ц",
+ ["ч"] = "Ч",
+ ["ш"] = "Ш",
+ ["щ"] = "Щ",
+ ["ъ"] = "Ъ",
+ ["ы"] = "Ы",
+ ["ь"] = "Ь",
+ ["э"] = "Э",
+ ["ю"] = "Ю",
+ ["я"] = "Я",
+ ["ѐ"] = "Ѐ",
+ ["ё"] = "Ё",
+ ["ђ"] = "Ђ",
+ ["ѓ"] = "Ѓ",
+ ["є"] = "Є",
+ ["ѕ"] = "Ѕ",
+ ["і"] = "І",
+ ["ї"] = "Ї",
+ ["ј"] = "Ј",
+ ["љ"] = "Љ",
+ ["њ"] = "Њ",
+ ["ћ"] = "Ћ",
+ ["ќ"] = "Ќ",
+ ["ѝ"] = "Ѝ",
+ ["ў"] = "Ў",
+ ["џ"] = "Џ",
+ ["ѡ"] = "Ѡ",
+ ["ѣ"] = "Ѣ",
+ ["ѥ"] = "Ѥ",
+ ["ѧ"] = "Ѧ",
+ ["ѩ"] = "Ѩ",
+ ["ѫ"] = "Ѫ",
+ ["ѭ"] = "Ѭ",
+ ["ѯ"] = "Ѯ",
+ ["ѱ"] = "Ѱ",
+ ["ѳ"] = "Ѳ",
+ ["ѵ"] = "Ѵ",
+ ["ѷ"] = "Ѷ",
+ ["ѹ"] = "Ѹ",
+ ["ѻ"] = "Ѻ",
+ ["ѽ"] = "Ѽ",
+ ["ѿ"] = "Ѿ",
+ ["ҁ"] = "Ҁ",
+ ["ҋ"] = "Ҋ",
+ ["ҍ"] = "Ҍ",
+ ["ҏ"] = "Ҏ",
+ ["ґ"] = "Ґ",
+ ["ғ"] = "Ғ",
+ ["ҕ"] = "Ҕ",
+ ["җ"] = "Җ",
+ ["ҙ"] = "Ҙ",
+ ["қ"] = "Қ",
+ ["ҝ"] = "Ҝ",
+ ["ҟ"] = "Ҟ",
+ ["ҡ"] = "Ҡ",
+ ["ң"] = "Ң",
+ ["ҥ"] = "Ҥ",
+ ["ҧ"] = "Ҧ",
+ ["ҩ"] = "Ҩ",
+ ["ҫ"] = "Ҫ",
+ ["ҭ"] = "Ҭ",
+ ["ү"] = "Ү",
+ ["ұ"] = "Ұ",
+ ["ҳ"] = "Ҳ",
+ ["ҵ"] = "Ҵ",
+ ["ҷ"] = "Ҷ",
+ ["ҹ"] = "Ҹ",
+ ["һ"] = "Һ",
+ ["ҽ"] = "Ҽ",
+ ["ҿ"] = "Ҿ",
+ ["ӂ"] = "Ӂ",
+ ["ӄ"] = "Ӄ",
+ ["ӆ"] = "Ӆ",
+ ["ӈ"] = "Ӈ",
+ ["ӊ"] = "Ӊ",
+ ["ӌ"] = "Ӌ",
+ ["ӎ"] = "Ӎ",
+ ["ӏ"] = "Ӏ",
+ ["ӑ"] = "Ӑ",
+ ["ӓ"] = "Ӓ",
+ ["ӕ"] = "Ӕ",
+ ["ӗ"] = "Ӗ",
+ ["ә"] = "Ә",
+ ["ӛ"] = "Ӛ",
+ ["ӝ"] = "Ӝ",
+ ["ӟ"] = "Ӟ",
+ ["ӡ"] = "Ӡ",
+ ["ӣ"] = "Ӣ",
+ ["ӥ"] = "Ӥ",
+ ["ӧ"] = "Ӧ",
+ ["ө"] = "Ө",
+ ["ӫ"] = "Ӫ",
+ ["ӭ"] = "Ӭ",
+ ["ӯ"] = "Ӯ",
+ ["ӱ"] = "Ӱ",
+ ["ӳ"] = "Ӳ",
+ ["ӵ"] = "Ӵ",
+ ["ӷ"] = "Ӷ",
+ ["ӹ"] = "Ӹ",
+ ["ӻ"] = "Ӻ",
+ ["ӽ"] = "Ӽ",
+ ["ӿ"] = "Ӿ",
+ ["ԁ"] = "Ԁ",
+ ["ԃ"] = "Ԃ",
+ ["ԅ"] = "Ԅ",
+ ["ԇ"] = "Ԇ",
+ ["ԉ"] = "Ԉ",
+ ["ԋ"] = "Ԋ",
+ ["ԍ"] = "Ԍ",
+ ["ԏ"] = "Ԏ",
+ ["ԑ"] = "Ԑ",
+ ["ԓ"] = "Ԓ",
+ ["ա"] = "Ա",
+ ["բ"] = "Բ",
+ ["գ"] = "Գ",
+ ["դ"] = "Դ",
+ ["ե"] = "Ե",
+ ["զ"] = "Զ",
+ ["է"] = "Է",
+ ["ը"] = "Ը",
+ ["թ"] = "Թ",
+ ["ժ"] = "Ժ",
+ ["ի"] = "Ի",
+ ["լ"] = "Լ",
+ ["խ"] = "Խ",
+ ["ծ"] = "Ծ",
+ ["կ"] = "Կ",
+ ["հ"] = "Հ",
+ ["ձ"] = "Ձ",
+ ["ղ"] = "Ղ",
+ ["ճ"] = "Ճ",
+ ["մ"] = "Մ",
+ ["յ"] = "Յ",
+ ["ն"] = "Ն",
+ ["շ"] = "Շ",
+ ["ո"] = "Ո",
+ ["չ"] = "Չ",
+ ["պ"] = "Պ",
+ ["ջ"] = "Ջ",
+ ["ռ"] = "Ռ",
+ ["ս"] = "Ս",
+ ["վ"] = "Վ",
+ ["տ"] = "Տ",
+ ["ր"] = "Ր",
+ ["ց"] = "Ց",
+ ["ւ"] = "Ւ",
+ ["փ"] = "Փ",
+ ["ք"] = "Ք",
+ ["օ"] = "Օ",
+ ["ֆ"] = "Ֆ",
+ ["ᵽ"] = "Ᵽ",
+ ["ḁ"] = "Ḁ",
+ ["ḃ"] = "Ḃ",
+ ["ḅ"] = "Ḅ",
+ ["ḇ"] = "Ḇ",
+ ["ḉ"] = "Ḉ",
+ ["ḋ"] = "Ḋ",
+ ["ḍ"] = "Ḍ",
+ ["ḏ"] = "Ḏ",
+ ["ḑ"] = "Ḑ",
+ ["ḓ"] = "Ḓ",
+ ["ḕ"] = "Ḕ",
+ ["ḗ"] = "Ḗ",
+ ["ḙ"] = "Ḙ",
+ ["ḛ"] = "Ḛ",
+ ["ḝ"] = "Ḝ",
+ ["ḟ"] = "Ḟ",
+ ["ḡ"] = "Ḡ",
+ ["ḣ"] = "Ḣ",
+ ["ḥ"] = "Ḥ",
+ ["ḧ"] = "Ḧ",
+ ["ḩ"] = "Ḩ",
+ ["ḫ"] = "Ḫ",
+ ["ḭ"] = "Ḭ",
+ ["ḯ"] = "Ḯ",
+ ["ḱ"] = "Ḱ",
+ ["ḳ"] = "Ḳ",
+ ["ḵ"] = "Ḵ",
+ ["ḷ"] = "Ḷ",
+ ["ḹ"] = "Ḹ",
+ ["ḻ"] = "Ḻ",
+ ["ḽ"] = "Ḽ",
+ ["ḿ"] = "Ḿ",
+ ["ṁ"] = "Ṁ",
+ ["ṃ"] = "Ṃ",
+ ["ṅ"] = "Ṅ",
+ ["ṇ"] = "Ṇ",
+ ["ṉ"] = "Ṉ",
+ ["ṋ"] = "Ṋ",
+ ["ṍ"] = "Ṍ",
+ ["ṏ"] = "Ṏ",
+ ["ṑ"] = "Ṑ",
+ ["ṓ"] = "Ṓ",
+ ["ṕ"] = "Ṕ",
+ ["ṗ"] = "Ṗ",
+ ["ṙ"] = "Ṙ",
+ ["ṛ"] = "Ṛ",
+ ["ṝ"] = "Ṝ",
+ ["ṟ"] = "Ṟ",
+ ["ṡ"] = "Ṡ",
+ ["ṣ"] = "Ṣ",
+ ["ṥ"] = "Ṥ",
+ ["ṧ"] = "Ṧ",
+ ["ṩ"] = "Ṩ",
+ ["ṫ"] = "Ṫ",
+ ["ṭ"] = "Ṭ",
+ ["ṯ"] = "Ṯ",
+ ["ṱ"] = "Ṱ",
+ ["ṳ"] = "Ṳ",
+ ["ṵ"] = "Ṵ",
+ ["ṷ"] = "Ṷ",
+ ["ṹ"] = "Ṹ",
+ ["ṻ"] = "Ṻ",
+ ["ṽ"] = "Ṽ",
+ ["ṿ"] = "Ṿ",
+ ["ẁ"] = "Ẁ",
+ ["ẃ"] = "Ẃ",
+ ["ẅ"] = "Ẅ",
+ ["ẇ"] = "Ẇ",
+ ["ẉ"] = "Ẉ",
+ ["ẋ"] = "Ẋ",
+ ["ẍ"] = "Ẍ",
+ ["ẏ"] = "Ẏ",
+ ["ẑ"] = "Ẑ",
+ ["ẓ"] = "Ẓ",
+ ["ẕ"] = "Ẕ",
+ ["ẛ"] = "Ṡ",
+ ["ạ"] = "Ạ",
+ ["ả"] = "Ả",
+ ["ấ"] = "Ấ",
+ ["ầ"] = "Ầ",
+ ["ẩ"] = "Ẩ",
+ ["ẫ"] = "Ẫ",
+ ["ậ"] = "Ậ",
+ ["ắ"] = "Ắ",
+ ["ằ"] = "Ằ",
+ ["ẳ"] = "Ẳ",
+ ["ẵ"] = "Ẵ",
+ ["ặ"] = "Ặ",
+ ["ẹ"] = "Ẹ",
+ ["ẻ"] = "Ẻ",
+ ["ẽ"] = "Ẽ",
+ ["ế"] = "Ế",
+ ["ề"] = "Ề",
+ ["ể"] = "Ể",
+ ["ễ"] = "Ễ",
+ ["ệ"] = "Ệ",
+ ["ỉ"] = "Ỉ",
+ ["ị"] = "Ị",
+ ["ọ"] = "Ọ",
+ ["ỏ"] = "Ỏ",
+ ["ố"] = "Ố",
+ ["ồ"] = "Ồ",
+ ["ổ"] = "Ổ",
+ ["ỗ"] = "Ỗ",
+ ["ộ"] = "Ộ",
+ ["ớ"] = "Ớ",
+ ["ờ"] = "Ờ",
+ ["ở"] = "Ở",
+ ["ỡ"] = "Ỡ",
+ ["ợ"] = "Ợ",
+ ["ụ"] = "Ụ",
+ ["ủ"] = "Ủ",
+ ["ứ"] = "Ứ",
+ ["ừ"] = "Ừ",
+ ["ử"] = "Ử",
+ ["ữ"] = "Ữ",
+ ["ự"] = "Ự",
+ ["ỳ"] = "Ỳ",
+ ["ỵ"] = "Ỵ",
+ ["ỷ"] = "Ỷ",
+ ["ỹ"] = "Ỹ",
+ ["ἀ"] = "Ἀ",
+ ["ἁ"] = "Ἁ",
+ ["ἂ"] = "Ἂ",
+ ["ἃ"] = "Ἃ",
+ ["ἄ"] = "Ἄ",
+ ["ἅ"] = "Ἅ",
+ ["ἆ"] = "Ἆ",
+ ["ἇ"] = "Ἇ",
+ ["ἐ"] = "Ἐ",
+ ["ἑ"] = "Ἑ",
+ ["ἒ"] = "Ἒ",
+ ["ἓ"] = "Ἓ",
+ ["ἔ"] = "Ἔ",
+ ["ἕ"] = "Ἕ",
+ ["ἠ"] = "Ἠ",
+ ["ἡ"] = "Ἡ",
+ ["ἢ"] = "Ἢ",
+ ["ἣ"] = "Ἣ",
+ ["ἤ"] = "Ἤ",
+ ["ἥ"] = "Ἥ",
+ ["ἦ"] = "Ἦ",
+ ["ἧ"] = "Ἧ",
+ ["ἰ"] = "Ἰ",
+ ["ἱ"] = "Ἱ",
+ ["ἲ"] = "Ἲ",
+ ["ἳ"] = "Ἳ",
+ ["ἴ"] = "Ἴ",
+ ["ἵ"] = "Ἵ",
+ ["ἶ"] = "Ἶ",
+ ["ἷ"] = "Ἷ",
+ ["ὀ"] = "Ὀ",
+ ["ὁ"] = "Ὁ",
+ ["ὂ"] = "Ὂ",
+ ["ὃ"] = "Ὃ",
+ ["ὄ"] = "Ὄ",
+ ["ὅ"] = "Ὅ",
+ ["ὑ"] = "Ὑ",
+ ["ὓ"] = "Ὓ",
+ ["ὕ"] = "Ὕ",
+ ["ὗ"] = "Ὗ",
+ ["ὠ"] = "Ὠ",
+ ["ὡ"] = "Ὡ",
+ ["ὢ"] = "Ὢ",
+ ["ὣ"] = "Ὣ",
+ ["ὤ"] = "Ὤ",
+ ["ὥ"] = "Ὥ",
+ ["ὦ"] = "Ὦ",
+ ["ὧ"] = "Ὧ",
+ ["ὰ"] = "Ὰ",
+ ["ά"] = "Ά",
+ ["ὲ"] = "Ὲ",
+ ["έ"] = "Έ",
+ ["ὴ"] = "Ὴ",
+ ["ή"] = "Ή",
+ ["ὶ"] = "Ὶ",
+ ["ί"] = "Ί",
+ ["ὸ"] = "Ὸ",
+ ["ό"] = "Ό",
+ ["ὺ"] = "Ὺ",
+ ["ύ"] = "Ύ",
+ ["ὼ"] = "Ὼ",
+ ["ώ"] = "Ώ",
+ ["ᾀ"] = "ᾈ",
+ ["ᾁ"] = "ᾉ",
+ ["ᾂ"] = "ᾊ",
+ ["ᾃ"] = "ᾋ",
+ ["ᾄ"] = "ᾌ",
+ ["ᾅ"] = "ᾍ",
+ ["ᾆ"] = "ᾎ",
+ ["ᾇ"] = "ᾏ",
+ ["ᾐ"] = "ᾘ",
+ ["ᾑ"] = "ᾙ",
+ ["ᾒ"] = "ᾚ",
+ ["ᾓ"] = "ᾛ",
+ ["ᾔ"] = "ᾜ",
+ ["ᾕ"] = "ᾝ",
+ ["ᾖ"] = "ᾞ",
+ ["ᾗ"] = "ᾟ",
+ ["ᾠ"] = "ᾨ",
+ ["ᾡ"] = "ᾩ",
+ ["ᾢ"] = "ᾪ",
+ ["ᾣ"] = "ᾫ",
+ ["ᾤ"] = "ᾬ",
+ ["ᾥ"] = "ᾭ",
+ ["ᾦ"] = "ᾮ",
+ ["ᾧ"] = "ᾯ",
+ ["ᾰ"] = "Ᾰ",
+ ["ᾱ"] = "Ᾱ",
+ ["ᾳ"] = "ᾼ",
+ ["ι"] = "Ι",
+ ["ῃ"] = "ῌ",
+ ["ῐ"] = "Ῐ",
+ ["ῑ"] = "Ῑ",
+ ["ῠ"] = "Ῠ",
+ ["ῡ"] = "Ῡ",
+ ["ῥ"] = "Ῥ",
+ ["ῳ"] = "ῼ",
+ ["ⅎ"] = "Ⅎ",
+ ["ⅰ"] = "Ⅰ",
+ ["ⅱ"] = "Ⅱ",
+ ["ⅲ"] = "Ⅲ",
+ ["ⅳ"] = "Ⅳ",
+ ["ⅴ"] = "Ⅴ",
+ ["ⅵ"] = "Ⅵ",
+ ["ⅶ"] = "Ⅶ",
+ ["ⅷ"] = "Ⅷ",
+ ["ⅸ"] = "Ⅸ",
+ ["ⅹ"] = "Ⅹ",
+ ["ⅺ"] = "Ⅺ",
+ ["ⅻ"] = "Ⅻ",
+ ["ⅼ"] = "Ⅼ",
+ ["ⅽ"] = "Ⅽ",
+ ["ⅾ"] = "Ⅾ",
+ ["ⅿ"] = "Ⅿ",
+ ["ↄ"] = "Ↄ",
+ ["ⓐ"] = "Ⓐ",
+ ["ⓑ"] = "Ⓑ",
+ ["ⓒ"] = "Ⓒ",
+ ["ⓓ"] = "Ⓓ",
+ ["ⓔ"] = "Ⓔ",
+ ["ⓕ"] = "Ⓕ",
+ ["ⓖ"] = "Ⓖ",
+ ["ⓗ"] = "Ⓗ",
+ ["ⓘ"] = "Ⓘ",
+ ["ⓙ"] = "Ⓙ",
+ ["ⓚ"] = "Ⓚ",
+ ["ⓛ"] = "Ⓛ",
+ ["ⓜ"] = "Ⓜ",
+ ["ⓝ"] = "Ⓝ",
+ ["ⓞ"] = "Ⓞ",
+ ["ⓟ"] = "Ⓟ",
+ ["ⓠ"] = "Ⓠ",
+ ["ⓡ"] = "Ⓡ",
+ ["ⓢ"] = "Ⓢ",
+ ["ⓣ"] = "Ⓣ",
+ ["ⓤ"] = "Ⓤ",
+ ["ⓥ"] = "Ⓥ",
+ ["ⓦ"] = "Ⓦ",
+ ["ⓧ"] = "Ⓧ",
+ ["ⓨ"] = "Ⓨ",
+ ["ⓩ"] = "Ⓩ",
+ ["ⰰ"] = "Ⰰ",
+ ["ⰱ"] = "Ⰱ",
+ ["ⰲ"] = "Ⰲ",
+ ["ⰳ"] = "Ⰳ",
+ ["ⰴ"] = "Ⰴ",
+ ["ⰵ"] = "Ⰵ",
+ ["ⰶ"] = "Ⰶ",
+ ["ⰷ"] = "Ⰷ",
+ ["ⰸ"] = "Ⰸ",
+ ["ⰹ"] = "Ⰹ",
+ ["ⰺ"] = "Ⰺ",
+ ["ⰻ"] = "Ⰻ",
+ ["ⰼ"] = "Ⰼ",
+ ["ⰽ"] = "Ⰽ",
+ ["ⰾ"] = "Ⰾ",
+ ["ⰿ"] = "Ⰿ",
+ ["ⱀ"] = "Ⱀ",
+ ["ⱁ"] = "Ⱁ",
+ ["ⱂ"] = "Ⱂ",
+ ["ⱃ"] = "Ⱃ",
+ ["ⱄ"] = "Ⱄ",
+ ["ⱅ"] = "Ⱅ",
+ ["ⱆ"] = "Ⱆ",
+ ["ⱇ"] = "Ⱇ",
+ ["ⱈ"] = "Ⱈ",
+ ["ⱉ"] = "Ⱉ",
+ ["ⱊ"] = "Ⱊ",
+ ["ⱋ"] = "Ⱋ",
+ ["ⱌ"] = "Ⱌ",
+ ["ⱍ"] = "Ⱍ",
+ ["ⱎ"] = "Ⱎ",
+ ["ⱏ"] = "Ⱏ",
+ ["ⱐ"] = "Ⱐ",
+ ["ⱑ"] = "Ⱑ",
+ ["ⱒ"] = "Ⱒ",
+ ["ⱓ"] = "Ⱓ",
+ ["ⱔ"] = "Ⱔ",
+ ["ⱕ"] = "Ⱕ",
+ ["ⱖ"] = "Ⱖ",
+ ["ⱗ"] = "Ⱗ",
+ ["ⱘ"] = "Ⱘ",
+ ["ⱙ"] = "Ⱙ",
+ ["ⱚ"] = "Ⱚ",
+ ["ⱛ"] = "Ⱛ",
+ ["ⱜ"] = "Ⱜ",
+ ["ⱝ"] = "Ⱝ",
+ ["ⱞ"] = "Ⱞ",
+ ["ⱡ"] = "Ⱡ",
+ ["ⱥ"] = "Ⱥ",
+ ["ⱦ"] = "Ⱦ",
+ ["ⱨ"] = "Ⱨ",
+ ["ⱪ"] = "Ⱪ",
+ ["ⱬ"] = "Ⱬ",
+ ["ⱶ"] = "Ⱶ",
+ ["ⲁ"] = "Ⲁ",
+ ["ⲃ"] = "Ⲃ",
+ ["ⲅ"] = "Ⲅ",
+ ["ⲇ"] = "Ⲇ",
+ ["ⲉ"] = "Ⲉ",
+ ["ⲋ"] = "Ⲋ",
+ ["ⲍ"] = "Ⲍ",
+ ["ⲏ"] = "Ⲏ",
+ ["ⲑ"] = "Ⲑ",
+ ["ⲓ"] = "Ⲓ",
+ ["ⲕ"] = "Ⲕ",
+ ["ⲗ"] = "Ⲗ",
+ ["ⲙ"] = "Ⲙ",
+ ["ⲛ"] = "Ⲛ",
+ ["ⲝ"] = "Ⲝ",
+ ["ⲟ"] = "Ⲟ",
+ ["ⲡ"] = "Ⲡ",
+ ["ⲣ"] = "Ⲣ",
+ ["ⲥ"] = "Ⲥ",
+ ["ⲧ"] = "Ⲧ",
+ ["ⲩ"] = "Ⲩ",
+ ["ⲫ"] = "Ⲫ",
+ ["ⲭ"] = "Ⲭ",
+ ["ⲯ"] = "Ⲯ",
+ ["ⲱ"] = "Ⲱ",
+ ["ⲳ"] = "Ⲳ",
+ ["ⲵ"] = "Ⲵ",
+ ["ⲷ"] = "Ⲷ",
+ ["ⲹ"] = "Ⲹ",
+ ["ⲻ"] = "Ⲻ",
+ ["ⲽ"] = "Ⲽ",
+ ["ⲿ"] = "Ⲿ",
+ ["ⳁ"] = "Ⳁ",
+ ["ⳃ"] = "Ⳃ",
+ ["ⳅ"] = "Ⳅ",
+ ["ⳇ"] = "Ⳇ",
+ ["ⳉ"] = "Ⳉ",
+ ["ⳋ"] = "Ⳋ",
+ ["ⳍ"] = "Ⳍ",
+ ["ⳏ"] = "Ⳏ",
+ ["ⳑ"] = "Ⳑ",
+ ["ⳓ"] = "Ⳓ",
+ ["ⳕ"] = "Ⳕ",
+ ["ⳗ"] = "Ⳗ",
+ ["ⳙ"] = "Ⳙ",
+ ["ⳛ"] = "Ⳛ",
+ ["ⳝ"] = "Ⳝ",
+ ["ⳟ"] = "Ⳟ",
+ ["ⳡ"] = "Ⳡ",
+ ["ⳣ"] = "Ⳣ",
+ ["ⴀ"] = "Ⴀ",
+ ["ⴁ"] = "Ⴁ",
+ ["ⴂ"] = "Ⴂ",
+ ["ⴃ"] = "Ⴃ",
+ ["ⴄ"] = "Ⴄ",
+ ["ⴅ"] = "Ⴅ",
+ ["ⴆ"] = "Ⴆ",
+ ["ⴇ"] = "Ⴇ",
+ ["ⴈ"] = "Ⴈ",
+ ["ⴉ"] = "Ⴉ",
+ ["ⴊ"] = "Ⴊ",
+ ["ⴋ"] = "Ⴋ",
+ ["ⴌ"] = "Ⴌ",
+ ["ⴍ"] = "Ⴍ",
+ ["ⴎ"] = "Ⴎ",
+ ["ⴏ"] = "Ⴏ",
+ ["ⴐ"] = "Ⴐ",
+ ["ⴑ"] = "Ⴑ",
+ ["ⴒ"] = "Ⴒ",
+ ["ⴓ"] = "Ⴓ",
+ ["ⴔ"] = "Ⴔ",
+ ["ⴕ"] = "Ⴕ",
+ ["ⴖ"] = "Ⴖ",
+ ["ⴗ"] = "Ⴗ",
+ ["ⴘ"] = "Ⴘ",
+ ["ⴙ"] = "Ⴙ",
+ ["ⴚ"] = "Ⴚ",
+ ["ⴛ"] = "Ⴛ",
+ ["ⴜ"] = "Ⴜ",
+ ["ⴝ"] = "Ⴝ",
+ ["ⴞ"] = "Ⴞ",
+ ["ⴟ"] = "Ⴟ",
+ ["ⴠ"] = "Ⴠ",
+ ["ⴡ"] = "Ⴡ",
+ ["ⴢ"] = "Ⴢ",
+ ["ⴣ"] = "Ⴣ",
+ ["ⴤ"] = "Ⴤ",
+ ["ⴥ"] = "Ⴥ",
+ ["a"] = "A",
+ ["b"] = "B",
+ ["c"] = "C",
+ ["d"] = "D",
+ ["e"] = "E",
+ ["f"] = "F",
+ ["g"] = "G",
+ ["h"] = "H",
+ ["i"] = "I",
+ ["j"] = "J",
+ ["k"] = "K",
+ ["l"] = "L",
+ ["m"] = "M",
+ ["n"] = "N",
+ ["o"] = "O",
+ ["p"] = "P",
+ ["q"] = "Q",
+ ["r"] = "R",
+ ["s"] = "S",
+ ["t"] = "T",
+ ["u"] = "U",
+ ["v"] = "V",
+ ["w"] = "W",
+ ["x"] = "X",
+ ["y"] = "Y",
+ ["z"] = "Z",
+ ["𐐨"] = "𐐀",
+ ["𐐩"] = "𐐁",
+ ["𐐪"] = "𐐂",
+ ["𐐫"] = "𐐃",
+ ["𐐬"] = "𐐄",
+ ["𐐭"] = "𐐅",
+ ["𐐮"] = "𐐆",
+ ["𐐯"] = "𐐇",
+ ["𐐰"] = "𐐈",
+ ["𐐱"] = "𐐉",
+ ["𐐲"] = "𐐊",
+ ["𐐳"] = "𐐋",
+ ["𐐴"] = "𐐌",
+ ["𐐵"] = "𐐍",
+ ["𐐶"] = "𐐎",
+ ["𐐷"] = "𐐏",
+ ["𐐸"] = "𐐐",
+ ["𐐹"] = "𐐑",
+ ["𐐺"] = "𐐒",
+ ["𐐻"] = "𐐓",
+ ["𐐼"] = "𐐔",
+ ["𐐽"] = "𐐕",
+ ["𐐾"] = "𐐖",
+ ["𐐿"] = "𐐗",
+ ["𐑀"] = "𐐘",
+ ["𐑁"] = "𐐙",
+ ["𐑂"] = "𐐚",
+ ["𐑃"] = "𐐛",
+ ["𐑄"] = "𐐜",
+ ["𐑅"] = "𐐝",
+ ["𐑆"] = "𐐞",
+ ["𐑇"] = "𐐟",
+ ["𐑈"] = "𐐠",
+ ["𐑉"] = "𐐡",
+ ["𐑊"] = "𐐢",
+ ["𐑋"] = "𐐣",
+ ["𐑌"] = "𐐤",
+ ["𐑍"] = "𐐥",
+ ["𐑎"] = "𐐦",
+ ["𐑏"] = "𐐧",
+}
+
+
+utf8_uc_lc = {
+ ["A"] = "a",
+ ["B"] = "b",
+ ["C"] = "c",
+ ["D"] = "d",
+ ["E"] = "e",
+ ["F"] = "f",
+ ["G"] = "g",
+ ["H"] = "h",
+ ["I"] = "i",
+ ["J"] = "j",
+ ["K"] = "k",
+ ["L"] = "l",
+ ["M"] = "m",
+ ["N"] = "n",
+ ["O"] = "o",
+ ["P"] = "p",
+ ["Q"] = "q",
+ ["R"] = "r",
+ ["S"] = "s",
+ ["T"] = "t",
+ ["U"] = "u",
+ ["V"] = "v",
+ ["W"] = "w",
+ ["X"] = "x",
+ ["Y"] = "y",
+ ["Z"] = "z",
+ ["À"] = "à",
+ ["Á"] = "á",
+ ["Â"] = "â",
+ ["Ã"] = "ã",
+ ["Ä"] = "ä",
+ ["Å"] = "å",
+ ["Æ"] = "æ",
+ ["Ç"] = "ç",
+ ["È"] = "è",
+ ["É"] = "é",
+ ["Ê"] = "ê",
+ ["Ë"] = "ë",
+ ["Ì"] = "ì",
+ ["Í"] = "í",
+ ["Î"] = "î",
+ ["Ï"] = "ï",
+ ["Ð"] = "ð",
+ ["Ñ"] = "ñ",
+ ["Ò"] = "ò",
+ ["Ó"] = "ó",
+ ["Ô"] = "ô",
+ ["Õ"] = "õ",
+ ["Ö"] = "ö",
+ ["Ø"] = "ø",
+ ["Ù"] = "ù",
+ ["Ú"] = "ú",
+ ["Û"] = "û",
+ ["Ü"] = "ü",
+ ["Ý"] = "ý",
+ ["Þ"] = "þ",
+ ["Ā"] = "ā",
+ ["Ă"] = "ă",
+ ["Ą"] = "ą",
+ ["Ć"] = "ć",
+ ["Ĉ"] = "ĉ",
+ ["Ċ"] = "ċ",
+ ["Č"] = "č",
+ ["Ď"] = "ď",
+ ["Đ"] = "đ",
+ ["Ē"] = "ē",
+ ["Ĕ"] = "ĕ",
+ ["Ė"] = "ė",
+ ["Ę"] = "ę",
+ ["Ě"] = "ě",
+ ["Ĝ"] = "ĝ",
+ ["Ğ"] = "ğ",
+ ["Ġ"] = "ġ",
+ ["Ģ"] = "ģ",
+ ["Ĥ"] = "ĥ",
+ ["Ħ"] = "ħ",
+ ["Ĩ"] = "ĩ",
+ ["Ī"] = "ī",
+ ["Ĭ"] = "ĭ",
+ ["Į"] = "į",
+ ["İ"] = "i",
+ ["IJ"] = "ij",
+ ["Ĵ"] = "ĵ",
+ ["Ķ"] = "ķ",
+ ["Ĺ"] = "ĺ",
+ ["Ļ"] = "ļ",
+ ["Ľ"] = "ľ",
+ ["Ŀ"] = "ŀ",
+ ["Ł"] = "ł",
+ ["Ń"] = "ń",
+ ["Ņ"] = "ņ",
+ ["Ň"] = "ň",
+ ["Ŋ"] = "ŋ",
+ ["Ō"] = "ō",
+ ["Ŏ"] = "ŏ",
+ ["Ő"] = "ő",
+ ["Œ"] = "œ",
+ ["Ŕ"] = "ŕ",
+ ["Ŗ"] = "ŗ",
+ ["Ř"] = "ř",
+ ["Ś"] = "ś",
+ ["Ŝ"] = "ŝ",
+ ["Ş"] = "ş",
+ ["Š"] = "š",
+ ["Ţ"] = "ţ",
+ ["Ť"] = "ť",
+ ["Ŧ"] = "ŧ",
+ ["Ũ"] = "ũ",
+ ["Ū"] = "ū",
+ ["Ŭ"] = "ŭ",
+ ["Ů"] = "ů",
+ ["Ű"] = "ű",
+ ["Ų"] = "ų",
+ ["Ŵ"] = "ŵ",
+ ["Ŷ"] = "ŷ",
+ ["Ÿ"] = "ÿ",
+ ["Ź"] = "ź",
+ ["Ż"] = "ż",
+ ["Ž"] = "ž",
+ ["Ɓ"] = "ɓ",
+ ["Ƃ"] = "ƃ",
+ ["Ƅ"] = "ƅ",
+ ["Ɔ"] = "ɔ",
+ ["Ƈ"] = "ƈ",
+ ["Ɖ"] = "ɖ",
+ ["Ɗ"] = "ɗ",
+ ["Ƌ"] = "ƌ",
+ ["Ǝ"] = "ǝ",
+ ["Ə"] = "ə",
+ ["Ɛ"] = "ɛ",
+ ["Ƒ"] = "ƒ",
+ ["Ɠ"] = "ɠ",
+ ["Ɣ"] = "ɣ",
+ ["Ɩ"] = "ɩ",
+ ["Ɨ"] = "ɨ",
+ ["Ƙ"] = "ƙ",
+ ["Ɯ"] = "ɯ",
+ ["Ɲ"] = "ɲ",
+ ["Ɵ"] = "ɵ",
+ ["Ơ"] = "ơ",
+ ["Ƣ"] = "ƣ",
+ ["Ƥ"] = "ƥ",
+ ["Ʀ"] = "ʀ",
+ ["Ƨ"] = "ƨ",
+ ["Ʃ"] = "ʃ",
+ ["Ƭ"] = "ƭ",
+ ["Ʈ"] = "ʈ",
+ ["Ư"] = "ư",
+ ["Ʊ"] = "ʊ",
+ ["Ʋ"] = "ʋ",
+ ["Ƴ"] = "ƴ",
+ ["Ƶ"] = "ƶ",
+ ["Ʒ"] = "ʒ",
+ ["Ƹ"] = "ƹ",
+ ["Ƽ"] = "ƽ",
+ ["DŽ"] = "dž",
+ ["Dž"] = "dž",
+ ["LJ"] = "lj",
+ ["Lj"] = "lj",
+ ["NJ"] = "nj",
+ ["Nj"] = "nj",
+ ["Ǎ"] = "ǎ",
+ ["Ǐ"] = "ǐ",
+ ["Ǒ"] = "ǒ",
+ ["Ǔ"] = "ǔ",
+ ["Ǖ"] = "ǖ",
+ ["Ǘ"] = "ǘ",
+ ["Ǚ"] = "ǚ",
+ ["Ǜ"] = "ǜ",
+ ["Ǟ"] = "ǟ",
+ ["Ǡ"] = "ǡ",
+ ["Ǣ"] = "ǣ",
+ ["Ǥ"] = "ǥ",
+ ["Ǧ"] = "ǧ",
+ ["Ǩ"] = "ǩ",
+ ["Ǫ"] = "ǫ",
+ ["Ǭ"] = "ǭ",
+ ["Ǯ"] = "ǯ",
+ ["DZ"] = "dz",
+ ["Dz"] = "dz",
+ ["Ǵ"] = "ǵ",
+ ["Ƕ"] = "ƕ",
+ ["Ƿ"] = "ƿ",
+ ["Ǹ"] = "ǹ",
+ ["Ǻ"] = "ǻ",
+ ["Ǽ"] = "ǽ",
+ ["Ǿ"] = "ǿ",
+ ["Ȁ"] = "ȁ",
+ ["Ȃ"] = "ȃ",
+ ["Ȅ"] = "ȅ",
+ ["Ȇ"] = "ȇ",
+ ["Ȉ"] = "ȉ",
+ ["Ȋ"] = "ȋ",
+ ["Ȍ"] = "ȍ",
+ ["Ȏ"] = "ȏ",
+ ["Ȑ"] = "ȑ",
+ ["Ȓ"] = "ȓ",
+ ["Ȕ"] = "ȕ",
+ ["Ȗ"] = "ȗ",
+ ["Ș"] = "ș",
+ ["Ț"] = "ț",
+ ["Ȝ"] = "ȝ",
+ ["Ȟ"] = "ȟ",
+ ["Ƞ"] = "ƞ",
+ ["Ȣ"] = "ȣ",
+ ["Ȥ"] = "ȥ",
+ ["Ȧ"] = "ȧ",
+ ["Ȩ"] = "ȩ",
+ ["Ȫ"] = "ȫ",
+ ["Ȭ"] = "ȭ",
+ ["Ȯ"] = "ȯ",
+ ["Ȱ"] = "ȱ",
+ ["Ȳ"] = "ȳ",
+ ["Ⱥ"] = "ⱥ",
+ ["Ȼ"] = "ȼ",
+ ["Ƚ"] = "ƚ",
+ ["Ⱦ"] = "ⱦ",
+ ["Ɂ"] = "ɂ",
+ ["Ƀ"] = "ƀ",
+ ["Ʉ"] = "ʉ",
+ ["Ʌ"] = "ʌ",
+ ["Ɇ"] = "ɇ",
+ ["Ɉ"] = "ɉ",
+ ["Ɋ"] = "ɋ",
+ ["Ɍ"] = "ɍ",
+ ["Ɏ"] = "ɏ",
+ ["Ά"] = "ά",
+ ["Έ"] = "έ",
+ ["Ή"] = "ή",
+ ["Ί"] = "ί",
+ ["Ό"] = "ό",
+ ["Ύ"] = "ύ",
+ ["Ώ"] = "ώ",
+ ["Α"] = "α",
+ ["Β"] = "β",
+ ["Γ"] = "γ",
+ ["Δ"] = "δ",
+ ["Ε"] = "ε",
+ ["Ζ"] = "ζ",
+ ["Η"] = "η",
+ ["Θ"] = "θ",
+ ["Ι"] = "ι",
+ ["Κ"] = "κ",
+ ["Λ"] = "λ",
+ ["Μ"] = "μ",
+ ["Ν"] = "ν",
+ ["Ξ"] = "ξ",
+ ["Ο"] = "ο",
+ ["Π"] = "π",
+ ["Ρ"] = "ρ",
+ ["Σ"] = "σ",
+ ["Τ"] = "τ",
+ ["Υ"] = "υ",
+ ["Φ"] = "φ",
+ ["Χ"] = "χ",
+ ["Ψ"] = "ψ",
+ ["Ω"] = "ω",
+ ["Ϊ"] = "ϊ",
+ ["Ϋ"] = "ϋ",
+ ["Ϙ"] = "ϙ",
+ ["Ϛ"] = "ϛ",
+ ["Ϝ"] = "ϝ",
+ ["Ϟ"] = "ϟ",
+ ["Ϡ"] = "ϡ",
+ ["Ϣ"] = "ϣ",
+ ["Ϥ"] = "ϥ",
+ ["Ϧ"] = "ϧ",
+ ["Ϩ"] = "ϩ",
+ ["Ϫ"] = "ϫ",
+ ["Ϭ"] = "ϭ",
+ ["Ϯ"] = "ϯ",
+ ["ϴ"] = "θ",
+ ["Ϸ"] = "ϸ",
+ ["Ϲ"] = "ϲ",
+ ["Ϻ"] = "ϻ",
+ ["Ͻ"] = "ͻ",
+ ["Ͼ"] = "ͼ",
+ ["Ͽ"] = "ͽ",
+ ["Ѐ"] = "ѐ",
+ ["Ё"] = "ё",
+ ["Ђ"] = "ђ",
+ ["Ѓ"] = "ѓ",
+ ["Є"] = "є",
+ ["Ѕ"] = "ѕ",
+ ["І"] = "і",
+ ["Ї"] = "ї",
+ ["Ј"] = "ј",
+ ["Љ"] = "љ",
+ ["Њ"] = "њ",
+ ["Ћ"] = "ћ",
+ ["Ќ"] = "ќ",
+ ["Ѝ"] = "ѝ",
+ ["Ў"] = "ў",
+ ["Џ"] = "џ",
+ ["А"] = "а",
+ ["Б"] = "б",
+ ["В"] = "в",
+ ["Г"] = "г",
+ ["Д"] = "д",
+ ["Е"] = "е",
+ ["Ж"] = "ж",
+ ["З"] = "з",
+ ["И"] = "и",
+ ["Й"] = "й",
+ ["К"] = "к",
+ ["Л"] = "л",
+ ["М"] = "м",
+ ["Н"] = "н",
+ ["О"] = "о",
+ ["П"] = "п",
+ ["Р"] = "р",
+ ["С"] = "с",
+ ["Т"] = "т",
+ ["У"] = "у",
+ ["Ф"] = "ф",
+ ["Х"] = "х",
+ ["Ц"] = "ц",
+ ["Ч"] = "ч",
+ ["Ш"] = "ш",
+ ["Щ"] = "щ",
+ ["Ъ"] = "ъ",
+ ["Ы"] = "ы",
+ ["Ь"] = "ь",
+ ["Э"] = "э",
+ ["Ю"] = "ю",
+ ["Я"] = "я",
+ ["Ѡ"] = "ѡ",
+ ["Ѣ"] = "ѣ",
+ ["Ѥ"] = "ѥ",
+ ["Ѧ"] = "ѧ",
+ ["Ѩ"] = "ѩ",
+ ["Ѫ"] = "ѫ",
+ ["Ѭ"] = "ѭ",
+ ["Ѯ"] = "ѯ",
+ ["Ѱ"] = "ѱ",
+ ["Ѳ"] = "ѳ",
+ ["Ѵ"] = "ѵ",
+ ["Ѷ"] = "ѷ",
+ ["Ѹ"] = "ѹ",
+ ["Ѻ"] = "ѻ",
+ ["Ѽ"] = "ѽ",
+ ["Ѿ"] = "ѿ",
+ ["Ҁ"] = "ҁ",
+ ["Ҋ"] = "ҋ",
+ ["Ҍ"] = "ҍ",
+ ["Ҏ"] = "ҏ",
+ ["Ґ"] = "ґ",
+ ["Ғ"] = "ғ",
+ ["Ҕ"] = "ҕ",
+ ["Җ"] = "җ",
+ ["Ҙ"] = "ҙ",
+ ["Қ"] = "қ",
+ ["Ҝ"] = "ҝ",
+ ["Ҟ"] = "ҟ",
+ ["Ҡ"] = "ҡ",
+ ["Ң"] = "ң",
+ ["Ҥ"] = "ҥ",
+ ["Ҧ"] = "ҧ",
+ ["Ҩ"] = "ҩ",
+ ["Ҫ"] = "ҫ",
+ ["Ҭ"] = "ҭ",
+ ["Ү"] = "ү",
+ ["Ұ"] = "ұ",
+ ["Ҳ"] = "ҳ",
+ ["Ҵ"] = "ҵ",
+ ["Ҷ"] = "ҷ",
+ ["Ҹ"] = "ҹ",
+ ["Һ"] = "һ",
+ ["Ҽ"] = "ҽ",
+ ["Ҿ"] = "ҿ",
+ ["Ӏ"] = "ӏ",
+ ["Ӂ"] = "ӂ",
+ ["Ӄ"] = "ӄ",
+ ["Ӆ"] = "ӆ",
+ ["Ӈ"] = "ӈ",
+ ["Ӊ"] = "ӊ",
+ ["Ӌ"] = "ӌ",
+ ["Ӎ"] = "ӎ",
+ ["Ӑ"] = "ӑ",
+ ["Ӓ"] = "ӓ",
+ ["Ӕ"] = "ӕ",
+ ["Ӗ"] = "ӗ",
+ ["Ә"] = "ә",
+ ["Ӛ"] = "ӛ",
+ ["Ӝ"] = "ӝ",
+ ["Ӟ"] = "ӟ",
+ ["Ӡ"] = "ӡ",
+ ["Ӣ"] = "ӣ",
+ ["Ӥ"] = "ӥ",
+ ["Ӧ"] = "ӧ",
+ ["Ө"] = "ө",
+ ["Ӫ"] = "ӫ",
+ ["Ӭ"] = "ӭ",
+ ["Ӯ"] = "ӯ",
+ ["Ӱ"] = "ӱ",
+ ["Ӳ"] = "ӳ",
+ ["Ӵ"] = "ӵ",
+ ["Ӷ"] = "ӷ",
+ ["Ӹ"] = "ӹ",
+ ["Ӻ"] = "ӻ",
+ ["Ӽ"] = "ӽ",
+ ["Ӿ"] = "ӿ",
+ ["Ԁ"] = "ԁ",
+ ["Ԃ"] = "ԃ",
+ ["Ԅ"] = "ԅ",
+ ["Ԇ"] = "ԇ",
+ ["Ԉ"] = "ԉ",
+ ["Ԋ"] = "ԋ",
+ ["Ԍ"] = "ԍ",
+ ["Ԏ"] = "ԏ",
+ ["Ԑ"] = "ԑ",
+ ["Ԓ"] = "ԓ",
+ ["Ա"] = "ա",
+ ["Բ"] = "բ",
+ ["Գ"] = "գ",
+ ["Դ"] = "դ",
+ ["Ե"] = "ե",
+ ["Զ"] = "զ",
+ ["Է"] = "է",
+ ["Ը"] = "ը",
+ ["Թ"] = "թ",
+ ["Ժ"] = "ժ",
+ ["Ի"] = "ի",
+ ["Լ"] = "լ",
+ ["Խ"] = "խ",
+ ["Ծ"] = "ծ",
+ ["Կ"] = "կ",
+ ["Հ"] = "հ",
+ ["Ձ"] = "ձ",
+ ["Ղ"] = "ղ",
+ ["Ճ"] = "ճ",
+ ["Մ"] = "մ",
+ ["Յ"] = "յ",
+ ["Ն"] = "ն",
+ ["Շ"] = "շ",
+ ["Ո"] = "ո",
+ ["Չ"] = "չ",
+ ["Պ"] = "պ",
+ ["Ջ"] = "ջ",
+ ["Ռ"] = "ռ",
+ ["Ս"] = "ս",
+ ["Վ"] = "վ",
+ ["Տ"] = "տ",
+ ["Ր"] = "ր",
+ ["Ց"] = "ց",
+ ["Ւ"] = "ւ",
+ ["Փ"] = "փ",
+ ["Ք"] = "ք",
+ ["Օ"] = "օ",
+ ["Ֆ"] = "ֆ",
+ ["Ⴀ"] = "ⴀ",
+ ["Ⴁ"] = "ⴁ",
+ ["Ⴂ"] = "ⴂ",
+ ["Ⴃ"] = "ⴃ",
+ ["Ⴄ"] = "ⴄ",
+ ["Ⴅ"] = "ⴅ",
+ ["Ⴆ"] = "ⴆ",
+ ["Ⴇ"] = "ⴇ",
+ ["Ⴈ"] = "ⴈ",
+ ["Ⴉ"] = "ⴉ",
+ ["Ⴊ"] = "ⴊ",
+ ["Ⴋ"] = "ⴋ",
+ ["Ⴌ"] = "ⴌ",
+ ["Ⴍ"] = "ⴍ",
+ ["Ⴎ"] = "ⴎ",
+ ["Ⴏ"] = "ⴏ",
+ ["Ⴐ"] = "ⴐ",
+ ["Ⴑ"] = "ⴑ",
+ ["Ⴒ"] = "ⴒ",
+ ["Ⴓ"] = "ⴓ",
+ ["Ⴔ"] = "ⴔ",
+ ["Ⴕ"] = "ⴕ",
+ ["Ⴖ"] = "ⴖ",
+ ["Ⴗ"] = "ⴗ",
+ ["Ⴘ"] = "ⴘ",
+ ["Ⴙ"] = "ⴙ",
+ ["Ⴚ"] = "ⴚ",
+ ["Ⴛ"] = "ⴛ",
+ ["Ⴜ"] = "ⴜ",
+ ["Ⴝ"] = "ⴝ",
+ ["Ⴞ"] = "ⴞ",
+ ["Ⴟ"] = "ⴟ",
+ ["Ⴠ"] = "ⴠ",
+ ["Ⴡ"] = "ⴡ",
+ ["Ⴢ"] = "ⴢ",
+ ["Ⴣ"] = "ⴣ",
+ ["Ⴤ"] = "ⴤ",
+ ["Ⴥ"] = "ⴥ",
+ ["Ḁ"] = "ḁ",
+ ["Ḃ"] = "ḃ",
+ ["Ḅ"] = "ḅ",
+ ["Ḇ"] = "ḇ",
+ ["Ḉ"] = "ḉ",
+ ["Ḋ"] = "ḋ",
+ ["Ḍ"] = "ḍ",
+ ["Ḏ"] = "ḏ",
+ ["Ḑ"] = "ḑ",
+ ["Ḓ"] = "ḓ",
+ ["Ḕ"] = "ḕ",
+ ["Ḗ"] = "ḗ",
+ ["Ḙ"] = "ḙ",
+ ["Ḛ"] = "ḛ",
+ ["Ḝ"] = "ḝ",
+ ["Ḟ"] = "ḟ",
+ ["Ḡ"] = "ḡ",
+ ["Ḣ"] = "ḣ",
+ ["Ḥ"] = "ḥ",
+ ["Ḧ"] = "ḧ",
+ ["Ḩ"] = "ḩ",
+ ["Ḫ"] = "ḫ",
+ ["Ḭ"] = "ḭ",
+ ["Ḯ"] = "ḯ",
+ ["Ḱ"] = "ḱ",
+ ["Ḳ"] = "ḳ",
+ ["Ḵ"] = "ḵ",
+ ["Ḷ"] = "ḷ",
+ ["Ḹ"] = "ḹ",
+ ["Ḻ"] = "ḻ",
+ ["Ḽ"] = "ḽ",
+ ["Ḿ"] = "ḿ",
+ ["Ṁ"] = "ṁ",
+ ["Ṃ"] = "ṃ",
+ ["Ṅ"] = "ṅ",
+ ["Ṇ"] = "ṇ",
+ ["Ṉ"] = "ṉ",
+ ["Ṋ"] = "ṋ",
+ ["Ṍ"] = "ṍ",
+ ["Ṏ"] = "ṏ",
+ ["Ṑ"] = "ṑ",
+ ["Ṓ"] = "ṓ",
+ ["Ṕ"] = "ṕ",
+ ["Ṗ"] = "ṗ",
+ ["Ṙ"] = "ṙ",
+ ["Ṛ"] = "ṛ",
+ ["Ṝ"] = "ṝ",
+ ["Ṟ"] = "ṟ",
+ ["Ṡ"] = "ṡ",
+ ["Ṣ"] = "ṣ",
+ ["Ṥ"] = "ṥ",
+ ["Ṧ"] = "ṧ",
+ ["Ṩ"] = "ṩ",
+ ["Ṫ"] = "ṫ",
+ ["Ṭ"] = "ṭ",
+ ["Ṯ"] = "ṯ",
+ ["Ṱ"] = "ṱ",
+ ["Ṳ"] = "ṳ",
+ ["Ṵ"] = "ṵ",
+ ["Ṷ"] = "ṷ",
+ ["Ṹ"] = "ṹ",
+ ["Ṻ"] = "ṻ",
+ ["Ṽ"] = "ṽ",
+ ["Ṿ"] = "ṿ",
+ ["Ẁ"] = "ẁ",
+ ["Ẃ"] = "ẃ",
+ ["Ẅ"] = "ẅ",
+ ["Ẇ"] = "ẇ",
+ ["Ẉ"] = "ẉ",
+ ["Ẋ"] = "ẋ",
+ ["Ẍ"] = "ẍ",
+ ["Ẏ"] = "ẏ",
+ ["Ẑ"] = "ẑ",
+ ["Ẓ"] = "ẓ",
+ ["Ẕ"] = "ẕ",
+ ["Ạ"] = "ạ",
+ ["Ả"] = "ả",
+ ["Ấ"] = "ấ",
+ ["Ầ"] = "ầ",
+ ["Ẩ"] = "ẩ",
+ ["Ẫ"] = "ẫ",
+ ["Ậ"] = "ậ",
+ ["Ắ"] = "ắ",
+ ["Ằ"] = "ằ",
+ ["Ẳ"] = "ẳ",
+ ["Ẵ"] = "ẵ",
+ ["Ặ"] = "ặ",
+ ["Ẹ"] = "ẹ",
+ ["Ẻ"] = "ẻ",
+ ["Ẽ"] = "ẽ",
+ ["Ế"] = "ế",
+ ["Ề"] = "ề",
+ ["Ể"] = "ể",
+ ["Ễ"] = "ễ",
+ ["Ệ"] = "ệ",
+ ["Ỉ"] = "ỉ",
+ ["Ị"] = "ị",
+ ["Ọ"] = "ọ",
+ ["Ỏ"] = "ỏ",
+ ["Ố"] = "ố",
+ ["Ồ"] = "ồ",
+ ["Ổ"] = "ổ",
+ ["Ỗ"] = "ỗ",
+ ["Ộ"] = "ộ",
+ ["Ớ"] = "ớ",
+ ["Ờ"] = "ờ",
+ ["Ở"] = "ở",
+ ["Ỡ"] = "ỡ",
+ ["Ợ"] = "ợ",
+ ["Ụ"] = "ụ",
+ ["Ủ"] = "ủ",
+ ["Ứ"] = "ứ",
+ ["Ừ"] = "ừ",
+ ["Ử"] = "ử",
+ ["Ữ"] = "ữ",
+ ["Ự"] = "ự",
+ ["Ỳ"] = "ỳ",
+ ["Ỵ"] = "ỵ",
+ ["Ỷ"] = "ỷ",
+ ["Ỹ"] = "ỹ",
+ ["Ἀ"] = "ἀ",
+ ["Ἁ"] = "ἁ",
+ ["Ἂ"] = "ἂ",
+ ["Ἃ"] = "ἃ",
+ ["Ἄ"] = "ἄ",
+ ["Ἅ"] = "ἅ",
+ ["Ἆ"] = "ἆ",
+ ["Ἇ"] = "ἇ",
+ ["Ἐ"] = "ἐ",
+ ["Ἑ"] = "ἑ",
+ ["Ἒ"] = "ἒ",
+ ["Ἓ"] = "ἓ",
+ ["Ἔ"] = "ἔ",
+ ["Ἕ"] = "ἕ",
+ ["Ἠ"] = "ἠ",
+ ["Ἡ"] = "ἡ",
+ ["Ἢ"] = "ἢ",
+ ["Ἣ"] = "ἣ",
+ ["Ἤ"] = "ἤ",
+ ["Ἥ"] = "ἥ",
+ ["Ἦ"] = "ἦ",
+ ["Ἧ"] = "ἧ",
+ ["Ἰ"] = "ἰ",
+ ["Ἱ"] = "ἱ",
+ ["Ἲ"] = "ἲ",
+ ["Ἳ"] = "ἳ",
+ ["Ἴ"] = "ἴ",
+ ["Ἵ"] = "ἵ",
+ ["Ἶ"] = "ἶ",
+ ["Ἷ"] = "ἷ",
+ ["Ὀ"] = "ὀ",
+ ["Ὁ"] = "ὁ",
+ ["Ὂ"] = "ὂ",
+ ["Ὃ"] = "ὃ",
+ ["Ὄ"] = "ὄ",
+ ["Ὅ"] = "ὅ",
+ ["Ὑ"] = "ὑ",
+ ["Ὓ"] = "ὓ",
+ ["Ὕ"] = "ὕ",
+ ["Ὗ"] = "ὗ",
+ ["Ὠ"] = "ὠ",
+ ["Ὡ"] = "ὡ",
+ ["Ὢ"] = "ὢ",
+ ["Ὣ"] = "ὣ",
+ ["Ὤ"] = "ὤ",
+ ["Ὥ"] = "ὥ",
+ ["Ὦ"] = "ὦ",
+ ["Ὧ"] = "ὧ",
+ ["ᾈ"] = "ᾀ",
+ ["ᾉ"] = "ᾁ",
+ ["ᾊ"] = "ᾂ",
+ ["ᾋ"] = "ᾃ",
+ ["ᾌ"] = "ᾄ",
+ ["ᾍ"] = "ᾅ",
+ ["ᾎ"] = "ᾆ",
+ ["ᾏ"] = "ᾇ",
+ ["ᾘ"] = "ᾐ",
+ ["ᾙ"] = "ᾑ",
+ ["ᾚ"] = "ᾒ",
+ ["ᾛ"] = "ᾓ",
+ ["ᾜ"] = "ᾔ",
+ ["ᾝ"] = "ᾕ",
+ ["ᾞ"] = "ᾖ",
+ ["ᾟ"] = "ᾗ",
+ ["ᾨ"] = "ᾠ",
+ ["ᾩ"] = "ᾡ",
+ ["ᾪ"] = "ᾢ",
+ ["ᾫ"] = "ᾣ",
+ ["ᾬ"] = "ᾤ",
+ ["ᾭ"] = "ᾥ",
+ ["ᾮ"] = "ᾦ",
+ ["ᾯ"] = "ᾧ",
+ ["Ᾰ"] = "ᾰ",
+ ["Ᾱ"] = "ᾱ",
+ ["Ὰ"] = "ὰ",
+ ["Ά"] = "ά",
+ ["ᾼ"] = "ᾳ",
+ ["Ὲ"] = "ὲ",
+ ["Έ"] = "έ",
+ ["Ὴ"] = "ὴ",
+ ["Ή"] = "ή",
+ ["ῌ"] = "ῃ",
+ ["Ῐ"] = "ῐ",
+ ["Ῑ"] = "ῑ",
+ ["Ὶ"] = "ὶ",
+ ["Ί"] = "ί",
+ ["Ῠ"] = "ῠ",
+ ["Ῡ"] = "ῡ",
+ ["Ὺ"] = "ὺ",
+ ["Ύ"] = "ύ",
+ ["Ῥ"] = "ῥ",
+ ["Ὸ"] = "ὸ",
+ ["Ό"] = "ό",
+ ["Ὼ"] = "ὼ",
+ ["Ώ"] = "ώ",
+ ["ῼ"] = "ῳ",
+ ["Ω"] = "ω",
+ ["K"] = "k",
+ ["Å"] = "å",
+ ["Ⅎ"] = "ⅎ",
+ ["Ⅰ"] = "ⅰ",
+ ["Ⅱ"] = "ⅱ",
+ ["Ⅲ"] = "ⅲ",
+ ["Ⅳ"] = "ⅳ",
+ ["Ⅴ"] = "ⅴ",
+ ["Ⅵ"] = "ⅵ",
+ ["Ⅶ"] = "ⅶ",
+ ["Ⅷ"] = "ⅷ",
+ ["Ⅸ"] = "ⅸ",
+ ["Ⅹ"] = "ⅹ",
+ ["Ⅺ"] = "ⅺ",
+ ["Ⅻ"] = "ⅻ",
+ ["Ⅼ"] = "ⅼ",
+ ["Ⅽ"] = "ⅽ",
+ ["Ⅾ"] = "ⅾ",
+ ["Ⅿ"] = "ⅿ",
+ ["Ↄ"] = "ↄ",
+ ["Ⓐ"] = "ⓐ",
+ ["Ⓑ"] = "ⓑ",
+ ["Ⓒ"] = "ⓒ",
+ ["Ⓓ"] = "ⓓ",
+ ["Ⓔ"] = "ⓔ",
+ ["Ⓕ"] = "ⓕ",
+ ["Ⓖ"] = "ⓖ",
+ ["Ⓗ"] = "ⓗ",
+ ["Ⓘ"] = "ⓘ",
+ ["Ⓙ"] = "ⓙ",
+ ["Ⓚ"] = "ⓚ",
+ ["Ⓛ"] = "ⓛ",
+ ["Ⓜ"] = "ⓜ",
+ ["Ⓝ"] = "ⓝ",
+ ["Ⓞ"] = "ⓞ",
+ ["Ⓟ"] = "ⓟ",
+ ["Ⓠ"] = "ⓠ",
+ ["Ⓡ"] = "ⓡ",
+ ["Ⓢ"] = "ⓢ",
+ ["Ⓣ"] = "ⓣ",
+ ["Ⓤ"] = "ⓤ",
+ ["Ⓥ"] = "ⓥ",
+ ["Ⓦ"] = "ⓦ",
+ ["Ⓧ"] = "ⓧ",
+ ["Ⓨ"] = "ⓨ",
+ ["Ⓩ"] = "ⓩ",
+ ["Ⰰ"] = "ⰰ",
+ ["Ⰱ"] = "ⰱ",
+ ["Ⰲ"] = "ⰲ",
+ ["Ⰳ"] = "ⰳ",
+ ["Ⰴ"] = "ⰴ",
+ ["Ⰵ"] = "ⰵ",
+ ["Ⰶ"] = "ⰶ",
+ ["Ⰷ"] = "ⰷ",
+ ["Ⰸ"] = "ⰸ",
+ ["Ⰹ"] = "ⰹ",
+ ["Ⰺ"] = "ⰺ",
+ ["Ⰻ"] = "ⰻ",
+ ["Ⰼ"] = "ⰼ",
+ ["Ⰽ"] = "ⰽ",
+ ["Ⰾ"] = "ⰾ",
+ ["Ⰿ"] = "ⰿ",
+ ["Ⱀ"] = "ⱀ",
+ ["Ⱁ"] = "ⱁ",
+ ["Ⱂ"] = "ⱂ",
+ ["Ⱃ"] = "ⱃ",
+ ["Ⱄ"] = "ⱄ",
+ ["Ⱅ"] = "ⱅ",
+ ["Ⱆ"] = "ⱆ",
+ ["Ⱇ"] = "ⱇ",
+ ["Ⱈ"] = "ⱈ",
+ ["Ⱉ"] = "ⱉ",
+ ["Ⱊ"] = "ⱊ",
+ ["Ⱋ"] = "ⱋ",
+ ["Ⱌ"] = "ⱌ",
+ ["Ⱍ"] = "ⱍ",
+ ["Ⱎ"] = "ⱎ",
+ ["Ⱏ"] = "ⱏ",
+ ["Ⱐ"] = "ⱐ",
+ ["Ⱑ"] = "ⱑ",
+ ["Ⱒ"] = "ⱒ",
+ ["Ⱓ"] = "ⱓ",
+ ["Ⱔ"] = "ⱔ",
+ ["Ⱕ"] = "ⱕ",
+ ["Ⱖ"] = "ⱖ",
+ ["Ⱗ"] = "ⱗ",
+ ["Ⱘ"] = "ⱘ",
+ ["Ⱙ"] = "ⱙ",
+ ["Ⱚ"] = "ⱚ",
+ ["Ⱛ"] = "ⱛ",
+ ["Ⱜ"] = "ⱜ",
+ ["Ⱝ"] = "ⱝ",
+ ["Ⱞ"] = "ⱞ",
+ ["Ⱡ"] = "ⱡ",
+ ["Ɫ"] = "ɫ",
+ ["Ᵽ"] = "ᵽ",
+ ["Ɽ"] = "ɽ",
+ ["Ⱨ"] = "ⱨ",
+ ["Ⱪ"] = "ⱪ",
+ ["Ⱬ"] = "ⱬ",
+ ["Ⱶ"] = "ⱶ",
+ ["Ⲁ"] = "ⲁ",
+ ["Ⲃ"] = "ⲃ",
+ ["Ⲅ"] = "ⲅ",
+ ["Ⲇ"] = "ⲇ",
+ ["Ⲉ"] = "ⲉ",
+ ["Ⲋ"] = "ⲋ",
+ ["Ⲍ"] = "ⲍ",
+ ["Ⲏ"] = "ⲏ",
+ ["Ⲑ"] = "ⲑ",
+ ["Ⲓ"] = "ⲓ",
+ ["Ⲕ"] = "ⲕ",
+ ["Ⲗ"] = "ⲗ",
+ ["Ⲙ"] = "ⲙ",
+ ["Ⲛ"] = "ⲛ",
+ ["Ⲝ"] = "ⲝ",
+ ["Ⲟ"] = "ⲟ",
+ ["Ⲡ"] = "ⲡ",
+ ["Ⲣ"] = "ⲣ",
+ ["Ⲥ"] = "ⲥ",
+ ["Ⲧ"] = "ⲧ",
+ ["Ⲩ"] = "ⲩ",
+ ["Ⲫ"] = "ⲫ",
+ ["Ⲭ"] = "ⲭ",
+ ["Ⲯ"] = "ⲯ",
+ ["Ⲱ"] = "ⲱ",
+ ["Ⲳ"] = "ⲳ",
+ ["Ⲵ"] = "ⲵ",
+ ["Ⲷ"] = "ⲷ",
+ ["Ⲹ"] = "ⲹ",
+ ["Ⲻ"] = "ⲻ",
+ ["Ⲽ"] = "ⲽ",
+ ["Ⲿ"] = "ⲿ",
+ ["Ⳁ"] = "ⳁ",
+ ["Ⳃ"] = "ⳃ",
+ ["Ⳅ"] = "ⳅ",
+ ["Ⳇ"] = "ⳇ",
+ ["Ⳉ"] = "ⳉ",
+ ["Ⳋ"] = "ⳋ",
+ ["Ⳍ"] = "ⳍ",
+ ["Ⳏ"] = "ⳏ",
+ ["Ⳑ"] = "ⳑ",
+ ["Ⳓ"] = "ⳓ",
+ ["Ⳕ"] = "ⳕ",
+ ["Ⳗ"] = "ⳗ",
+ ["Ⳙ"] = "ⳙ",
+ ["Ⳛ"] = "ⳛ",
+ ["Ⳝ"] = "ⳝ",
+ ["Ⳟ"] = "ⳟ",
+ ["Ⳡ"] = "ⳡ",
+ ["Ⳣ"] = "ⳣ",
+ ["A"] = "a",
+ ["B"] = "b",
+ ["C"] = "c",
+ ["D"] = "d",
+ ["E"] = "e",
+ ["F"] = "f",
+ ["G"] = "g",
+ ["H"] = "h",
+ ["I"] = "i",
+ ["J"] = "j",
+ ["K"] = "k",
+ ["L"] = "l",
+ ["M"] = "m",
+ ["N"] = "n",
+ ["O"] = "o",
+ ["P"] = "p",
+ ["Q"] = "q",
+ ["R"] = "r",
+ ["S"] = "s",
+ ["T"] = "t",
+ ["U"] = "u",
+ ["V"] = "v",
+ ["W"] = "w",
+ ["X"] = "x",
+ ["Y"] = "y",
+ ["Z"] = "z",
+ ["𐐀"] = "𐐨",
+ ["𐐁"] = "𐐩",
+ ["𐐂"] = "𐐪",
+ ["𐐃"] = "𐐫",
+ ["𐐄"] = "𐐬",
+ ["𐐅"] = "𐐭",
+ ["𐐆"] = "𐐮",
+ ["𐐇"] = "𐐯",
+ ["𐐈"] = "𐐰",
+ ["𐐉"] = "𐐱",
+ ["𐐊"] = "𐐲",
+ ["𐐋"] = "𐐳",
+ ["𐐌"] = "𐐴",
+ ["𐐍"] = "𐐵",
+ ["𐐎"] = "𐐶",
+ ["𐐏"] = "𐐷",
+ ["𐐐"] = "𐐸",
+ ["𐐑"] = "𐐹",
+ ["𐐒"] = "𐐺",
+ ["𐐓"] = "𐐻",
+ ["𐐔"] = "𐐼",
+ ["𐐕"] = "𐐽",
+ ["𐐖"] = "𐐾",
+ ["𐐗"] = "𐐿",
+ ["𐐘"] = "𐑀",
+ ["𐐙"] = "𐑁",
+ ["𐐚"] = "𐑂",
+ ["𐐛"] = "𐑃",
+ ["𐐜"] = "𐑄",
+ ["𐐝"] = "𐑅",
+ ["𐐞"] = "𐑆",
+ ["𐐟"] = "𐑇",
+ ["𐐠"] = "𐑈",
+ ["𐐡"] = "𐑉",
+ ["𐐢"] = "𐑊",
+ ["𐐣"] = "𐑋",
+ ["𐐤"] = "𐑌",
+ ["𐐥"] = "𐑍",
+ ["𐐦"] = "𐑎",
+ ["𐐧"] = "𐑏",
+}
+
diff --git a/xCT+/libs/xCombatParser-1.0/eventargs.md b/xCT+/libs/xCombatParser-1.0/eventargs.md
new file mode 100644
index 00000000..fa6be624
--- /dev/null
+++ b/xCT+/libs/xCombatParser-1.0/eventargs.md
@@ -0,0 +1,142 @@
+# Event Args Methods
+In the callback that you passed to `RegisterCombat`, you will be given a single parameter `args`. This parameter contains all the parsed information from the event in names similar to Wowpedia's page on [Combat Log Event](http://wow.gamepedia.com/COMBAT_LOG_EVENT). The documentation below outlines all the functions inside the `args` table.
+
+### Table of Contents
+* [Event Args Methods](#event-args-methods)
+ * [__Memory__ Methods](#memory-methods)
+ * [__Combat Object__ Methods](#combat-object-methods)
+ * [__Raid Target__ Methods](#raid-target-methods)
+ * [__Special Object__ Methods](#special-object-methods)
+
+### Memory Methods
+ * `args:pin()`— Pins the table in memory so that you can use it at a later time.
+
+ * `args:free()`— Unpins the table in memory. If this was the only reference, the table will be recycled.
+
+### Combat Object Methods
+
+* `args:GetDestinationType()`— Gets the [Object's Type](http://wow.gamepedia.com/UnitFlag#Constants) from the destination flags.
+ * __Returns:__ `string objectType`
+
+> `'GUARDIAN' | 'PET' | 'PLAYER' | 'NPC' | 'OBJECT' | 'UNKNOWN'`
+
+* `args:GetDestinationController()`— Gets the [Object's Controller](http://wow.gamepedia.com/UnitFlag#Constants) from the destination flags.
+ * __Returns:__ `string objectController`
+
+> `'PLAYER' | 'NPC' | 'UNKNOWN'`
+
+* `args:GetDestinationReaction()`— Gets the [Object's Reaction](http://wow.gamepedia.com/UnitFlag#Constants) from the destination flags.
+ * __Returns:__ `string objectReaction`
+
+> `'FRIENDLY' | 'NEUTRAL' | 'HOSTILE' | 'UNKNWON'`
+
+* `args:GetDestinationAffiliation()`— Gets the [Object's Affiliation](http://wow.gamepedia.com/UnitFlag#Constants) from the destination flags.
+ * __Returns:__ `string objectAffiliation`
+
+> `'OUTSIDER' | 'PARTY' | 'RAID' | 'MINE' | 'UNKNWON'`
+
+* `args:GetSourceType()`— Gets the [Object's Type](http://wow.gamepedia.com/UnitFlag#Constants) from the source flags. If this is an environmental event, the type is the environment.
+ * __Returns:__ `string objectType`
+
+> `'ENVIRONMENT' | 'GUARDIAN' | 'PET' | 'PLAYER' | 'NPC' | 'OBJECT' | 'UNKNOWN'`
+
+* `args:GetSourceController()`— Gets the [Object's Controller](http://wow.gamepedia.com/UnitFlag#Constants) from the source flags. If this is an environmental event, the controller is the environment.
+ * __Returns:__ `string objectController`
+
+> `'ENVIRONMENT' | 'PLAYER' | 'NPC' | 'UNKNOWN'`
+
+* `args:GetSourceReaction()`— Gets the [Object's Reaction](http://wow.gamepedia.com/UnitFlag#Constants) from the source flags. If this is an environmental event, the reaction is neutral.
+ * __Returns:__ `string objectReaction`
+
+> `'FRIENDLY' | 'NEUTRAL' | 'HOSTILE' | 'UNKNWON'`
+
+* `args:GetSourceAffiliation()`— Gets the [Object's Affiliation](http://wow.gamepedia.com/UnitFlag#Constants) from the source flags. If this is an environmental event, the affiliation is an outsider.
+ * __Returns:__ `string objectAffiliation`
+
+> `'OUTSIDER' | 'PARTY' | 'RAID' | 'MINE' | 'UNKNWON'`
+
+### Raid Target Methods
+
+> Raid Target Lookup Table
+>
+> | Index | Description | Lua Name (`_G[name]`)
+> |:---:| --- | --- |
+> | 0 | Nothing | `NONE` |
+> | 1 | Yellow Star | `RAID_TARGET_1` |
+> | 2 | Orange Circle | `RAID_TARGET_2` |
+> | 3 | Purple Diamond | `RAID_TARGET_3` |
+> | 4 | Green Triangle | `RAID_TARGET_4` |
+> | 5 | Pale Blue Moon | `RAID_TARGET_5` |
+> | 6 | Blue Square | `RAID_TARGET_6` |
+> | 7 | Red Cross | `RAID_TARGET_7` |
+> | 8 | White Skull | `RAID_TARGET_8` |
+> [Source](http://wow.gamepedia.com/Target_Marker#Arguments)
+
+* `args:GetSourceRaidTargetIndex()`— Gets [Target Marker](http://wow.gamepedia.com/Target_Marker#Arguments) index of the source object.
+ * __Returns:__ `number raidTargetIndex`
+
+* `args:GetDestinationRaidTargetIndex()`— Gets [Target Marker](http://wow.gamepedia.com/Target_Marker#Arguments) index of the destination object.
+ * __Returns:__ `number raidTargetIndex`
+
+* `args:GetSourceRaidTargetName()`— Gets the localized name of the [Target Marker](http://wow.gamepedia.com/Target_Marker#Arguments) that the source object has.
+ * __Returns:__ `string raidTarget`
+
+> `enUS Example: 'None' | 'Star' | 'Circle' | 'Skull' | etc.`
+
+* `args:GetDestinationRaidTargetName()`— Gets the localized name of the [Target Marker](http://wow.gamepedia.com/Target_Marker#Arguments) that the destination object has.
+ * __Returns:__ `string raidTarget`
+
+> `enUS Example: 'None' | 'Star' | 'Circle' | 'Skull' | etc.`
+
+### Special Object Methods
+
+* `args:IsSourceMainAssist()`— Checks to see if the source [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is a main assist (e.g. Assists, Officers, Healers, etc.).
+ * __Returns:__ `boolean isAssist`
+
+* `args:IsDestinationMainAssist()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is a main assist (e.g. Assists, Officers, Healers, etc.).
+ * __Returns:__ `boolean isAssist`
+
+* `args:IsSourceMainTank()`— Checks to see if the source [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is a main tank (e.g. Tank Frames).
+ * __Returns:__ `boolean isTank`
+
+* `args:IsDestinationMainTank()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is a main tank (e.g. Tank Frames).
+ * __Returns:__ `boolean isTank`
+
+* `args:IsSourceFocus()`— Checks to see if the source [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current focus. Using this is faster than comparing GUID's.
+ * __Returns:__ `boolean isFocus`
+
+* `args:IsDestinationFocus()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current focus. Using this is faster than comparing GUID's.
+ * __Returns:__ `boolean isFocus`
+
+* `args:IsSourceTarget()`— Checks to see if the source [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current target. Using this is faster than comparing GUID's.
+ * __Returns:__ `boolean isTarget`
+
+* `args:IsDestinationTarget()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current target. Using this is faster than comparing GUID's.
+ * __Returns:__ `boolean isTarget`
+
+* `args:IsSourceMyPet()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current pet. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isMyPet`
+
+* `args:IsDestinationMyPet()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current pet. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isMyPet`
+
+* `args:IsSourceMyVehicle()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current vehicle. This information is impossible to get from any other method.
+ * __Returns:__ `boolean isMyVehicle`
+
+* `args:IsDestinationMyVehicle()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is the player's current vehicle. This information is impossible to get from any other method.
+ * __Returns:__ `boolean isMyVehicle`
+
+* `args:IsSourceRaidMember()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is in the player's current raid group. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isRaidMember`
+
+* `args:IsDestinationRaidMember()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is in the player's current raid group. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isRaidMember`
+
+* `args:IsSourcePartyMember()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is in the player's current party. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isPartyMember`
+
+* `args:IsDestinationPartyMember()`— Checks to see if the destination [Combat Object](http://wow.gamepedia.com/UnitFlag#Constants) is in the player's current party. Using this is more reliable than trying to update and compare GUID's.
+ * __Returns:__ `boolean isPartyMember`
+
+
+> Written with [StackEdit](https://stackedit.io/).
diff --git a/xCT+/libs/xCombatParser-1.0/eventparameters.md b/xCT+/libs/xCombatParser-1.0/eventparameters.md
new file mode 100644
index 00000000..fd66c668
--- /dev/null
+++ b/xCT+/libs/xCombatParser-1.0/eventparameters.md
@@ -0,0 +1,194 @@
+
+# Parsed Event Args --- Parameters
+
+### Base Parameters
+| `args.`*index* | Description |
+| ---:|:--- |
+| `timestamp` | Uses the function [time()](http://wow.gamepedia.com/API_time) to reference the time of the event when the client received it from the server. |
+| `event` | The full name of the event. This is only useful when looking for a specific event or determining if a special event was triggered. |
+| `hideCaster` | This parameter lets you know when there is no adequate source name for the event. The parameter `sourceName` will generally be a nil value.
__NOTE:__ This parser will recolor Environmental events with relevant names and spell IDs. So if this is set, you **_still_** may want to show the `sourceName`. I am considering turning this off for environmental events. |
+| `sourceGUID` | The [GUID](http://wow.gamepedia.com/GUID) of the source.
+| `sourceName` | The localized name of the source. If `hideCaster` is set, this value may be `nil`, unless the event is environmental. |
+| `sourceFlags` | The [unit flags](http://wow.gamepedia.com/UnitFlag) of the source. See [Combat Object Methods](#NEED-LINK) for a set of parsing functions. May be `nil` if you try parsing it yourself. |
+| `sourceRaidFlags` | The [raid flags](http://wow.gamepedia.com/RaidFlag) of the source. See [Raid Target Methods](#NEED-LINK) for a set of parsing functions. |
+| `destGUID` | The [GUID](http://wow.gamepedia.com/GUID) of the destination.
+| `destName` | The localized name of the destination. |
+| `destFlags` | The [unit flags](http://wow.gamepedia.com/UnitFlag) of the destination. See [Combat Object Methods](#NEED-LINK) for a set of parsing functions. |
+| `destRaidFlags` | The [raid flags](http://wow.gamepedia.com/RaidFlag) of the destination. See [Raid Target Methods](#NEED-LINK) for a set of parsing functions. |
+
+
+### Event Prefixes
+
+Events with the following prefixes will also have some additional parameters
+
+| `args.prefix` | Parameters | | |
+|:--- |:---:|:---:|:---:|
+| `"SWING"` | *none* | | |
+| `"RANGE"` | `spellId` | `spellName` | [`spellSchool`](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Spell_School) |
+| `"SPELL"` | `spellId` | `spellName` | [`spellSchool`](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Spell_School) |
+| `"SPELL_PERIODIC"` | `spellId` | `spellName` | [`spellSchool`](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Spell_School) |
+| `"SPELL_BUILDING"` | `spellId` | `spellName` | [`spellSchool`](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Spell_School) |
+| `"ENVIRONMENTAL"` | [`environmentalType`](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Environmental_Type) | | |
+
+> __NOTE:__ The use of `spellSchool` above is specific to the spell that was cast. For example, if you cast a _Holy_ spell that did Arcane + Holy (_Divine_) damage, then `spellSchool` would be _Holy_ and `school` would be _Divine_.
+
+
+### Event Suffixes
+Use `args.suffix == "_????"` to view which additional parameters each suffix has.
+
+
+#### `_DAMAGE`--- Additional Parameters
+
+This is arguably one of the most complicated suffixes and will have its own section dealing with it.
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `amount` | The _final_ amount of damage that the destination took from the source in the event. |
+| `overkill` | If the destination was killed by the source, this will specify the negative health of the destination. This will be `nil` most of the time. |
+| `school` | Specifies the [spell school](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Spell_School) that was used to damage the destination. It is most useful for coloring the damage amount. |
+| `resisted` | Shows how much damage was subtracted from `amount` that was resisted by the destination. You can use `_G["RESIST"]` for a localized string. |
+| `blocked` | Shows how much damage was subtracted from `amount` that was blocked by the destination. You can use `_G["BLOCK"]` for a localized string. |
+| `absorbed` | Shows how much damage was subtracted from `amount` that was absorbed by the destination. You can use `_G["ABSORB"]` for a localized string. |
+| `critical` | A true/false flag that informs if the event was a critical strike. |
+| `glancing` | A true/false flag that informs if the event was a glancing blow. |
+| `crushing` | A true/false flag that informs if the event was a crushing blow. |
+| `isOffHand` | A true/false flag that informs if the event was from the source's off-hand. |
+
+#### `_ABSORBED`--- Additional Parameters
+
+Another complicated suffix and relatively new. If it has `"SPELL"` as its prefix, it will bring `spellId`, `spellName`, and `spellSchool` arguments.
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `casterGUID` | The [GUID](http://wow.gamepedia.com/GUID) of the source that casted the absorb-aura. |
+| `casterName` | The localized name of the source that casted the absorb-aura. |
+| `casterFlags` | The [unit flags](http://wow.gamepedia.com/UnitFlag) of the source that casted the absorb-aura. See [Combat Object Methods](#NEED-LINK) for a set of parsing functions. May be `nil` if you try parsing it yourself. |
+| `casterRaidFlags` | The [raid flags](http://wow.gamepedia.com/RaidFlag) of the source that casted the absorb-aura. See [Raid Target Methods](#NEED-LINK) for a set of parsing functions. |
+| `absorbSpellId` | The spell ID of the absorb-aura.
__NOTE:__ If this was a `SPELL_ABSORB`, you can use `args.spellId`, `args.spellName`, and `args.spellSchool` to see relevant information about the spell that did the interrupt. `SWING_ABSORB` does not have this information. |
+| `absorbSpellName` | The localized name of the absorb-aura. |
+| `absorbSpellSchool` | The school of the absorb-aura. |
+| `amount` | The _final_ amount of damage that absorb-aura absorbed from the source in the event. |
+| `critical` | A true/false flag that informs if the event was a critical strike. |
+
+#### `_MISSED`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `missType` | The [type of miss](http://wow.gamepedia.com/COMBAT_LOG_EVENT#Miss_type) that occured. |
+| `isOffHand` | A true/false flag that informs if the event was from the source's off-hand. |
+| `amountMissed` | The potential damage that no one will ever see or care about. |
+
+
+#### `_HEAL`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `amount` | The amount of healing that the destination unit from the source. |
+| `overhealing` | The amount of healing that was over the maximum health of the destination unit. |
+| `absorbed` | The amount of healing that was absorbed by the unit (e.g. by a debuff). |
+| `critical` | A true/false flag that informs if the event was a critical strike. |
+
+
+#### `_ENERGIZE`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `amount` | The amount of energy that the destination unit gained. |
+| `powerType` | The [type of power](#power-type-lookup) that the destination unit gained. This used to be an index, but now is just a string with the type. See the table below for the possible values.
__NOTE:__ If you need a localized string of the power type, you can do:
`local name = _G[args.powerType];`
If you need the color of the power type, Blizzard provides that in their `PowerBarColor` global:
`local color = PowerBarColor[args.powerType]`
`local red, green, blue = color.r, color.g, color.b;`|
+
+> #### Power Type Lookup
+> | Index (Old) | Power Type | Color (RGB) | Color (Hex) |
+> | :---: | :--- | :--- | :--- |
+> | 0 | `"MANA"` | rgb(0, 0, 1) | 0000FF |
+> | 1 | `"RAGE"` | rgb(1, 0, 0) | FF0000 |
+> | 2 | `"FOCUS"` | rgb(1, 0.5, 0.25) | FF8040 |
+> | 3 | `"ENERGY"` | rgb(1, 1, 0) | FFFF00 |
+> | 4 | `"CHI"` | rgb(0.71, 1, 0.92) | B5FFEB |
+> | 5 | `"RUNES"` | rgb(0.5, 0.5, 0.5) | 808080 |
+> | 6 | `"RUNIC_POWER"` | rgb(0, 0.82, 1) | 00D1FF |
+> | 7 | `"SOUL_SHARDS"` | rgb(0.5, 0.32, 0.55) | 80528C |
+> | 8 | `"LUNAR_POWER"` | rgb(0.3, 0.52, 0.9) | 4D85E6 |
+> | 9 | `"HOLY_POWER"` | rgb(0.95, 0.9, 0.6) | F2E699 |
+> | 11 | `"MAELSTROM"` | rgb(0, 0.5, 1) | 0080FF |
+> | 13 | `"INSANITY"` | rgb(0.4, 0, 0.8) | 6600CC|
+> | 17 | `"FURY"` | rgb(0.788, 0.259, 0.992) | C942FD |
+> | 18 | `"PAIN"` | rgb(1, 0.612, 0) | FF9C00 |
+> | n/a | `"AMMOSLOT"` | rgb(0.8, 0.6, 0) | CC9900 |
+> | n/a | `"ARCANE_CHARGES"` | rgb(0.1, 0.1, 0.98) | 1A1AFA |
+> | n/a | `"COMBO_POINTS"` | rgb(1, 0.96, 0.41) | FFF569 |
+> | n/a | `"FUEL"` | rgb(0, 0.55, 0.5) | 008C80 |
+> | n/a | `"STAGGER"` | [1] = rgb(0.52, 1, 0.52)
[2] = rgb(1, 0.98, 0.72)
[3] = rgb(1, 0.42, 0.42) | 85FF85 _-- Light_
FFFAB8 _-- Moderate_
FF6B6B _-- Heavy_ |
+
+
+#### `_DRAIN` and `_LEECH`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `amount` | The amount that was drained/leeched from the destination to the source. |
+| `powerType` | The [type of power](#power-type-lookup) that was drained/leeched.
__NOTE:__ I believe if this is -2, health was drained/leeched. Also, the new minor stat **Leech** does not seem to use this event. Rather you will find it with `SPELL_HEAL` and `args.spellId == 143924`. |
+| `extraAmount` | The amount that was over the power type maximum of the source unit. |
+
+
+#### `_INTERRUPT`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `extraSpellId` | The spell ID of the spell that was interrupted.
__NOTE:__ You can use `args.spellId`, `args.spellName`, and `args.spellSchool` to see relevant information about the spell that did the interrupt. |
+| `extraSpellName` | The localized name of the spell that was interrupted. |
+| `extraSchool` | The school of the spell that was interrupted. |
+
+
+#### `_DISPEL` and `_STOLEN`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `extraSpellId` | The spell ID of the aura that was dispelled/stolen.
__NOTE:__ You can use `args.spellId`, `args.spellName`, and `args.spellSchool` to see relevant information about the spell that did the dispelling/stealing. |
+| `extraSpellName` | The localized name of the aura that was dispelled/stolen. |
+| `extraSchool` | The school of the aura that was dispelled/stolen. |
+| `auraType` | The type of aura that was dispelled/stolen: `"BUFF"` or `"DEBUFF"`. |
+
+
+#### `_DISPEL_FAILED`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `extraSpellId` | The spell ID of the aura that wasn't dispelled.
__NOTE:__ You can use `args.spellId`, `args.spellName`, and `args.spellSchool` to see relevant information about the spell that tried to dispel. |
+| `extraSpellName` | The localized name of the aura that wasn't dispelled. |
+| `extraSchool` | The school of the aura that wasn't dispelled. |
+
+#### `_EXTRA_ATTACKS`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `amount` | _Unknown value. More research or documentation is needed._ |
+
+
+#### `_AURA_APPLIED`, `_AURA_REMOVED`, `_AURA_APPLIED_DOSE `, `_AURA_REMOVED_DOSE`, and `_AURA_REFRESH`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `auraType` | The type of the aura being referenced in the event (`"BUFF"` or `"DEBUFF"`). |
+| `amount` | The number of stacks of the aura. |
+
+
+#### `_AURA_BROKEN`--- Additional Parameters
+| `args.`*index* | Description |
+| ---:|:--- |
+| `auraType` | The type of the aura being broken in the event (`"BUFF"` or `"DEBUFF"`). |
+
+
+#### `_AURA_BROKEN_SPELL`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `extraSpellId` | The spell ID of the aura that was broken.
__NOTE:__ You can use `args.spellId`, `args.spellName`, and `args.spellSchool` to see relevant information about the spell that did the breaking. |
+| `extraSpellName` | The localized name of the aura that was broken. |
+| `extraSchool` | The school of the aura that was broken. |
+| `auraType` | The type of aura that was broken: `"BUFF"` or `"DEBUFF"`. |
+
+
+#### `_CAST_FAILED`--- Additional Parameters
+
+| `args.`*index* | Description |
+| ---:|:--- |
+| `failedType` | A localized string that gives the reason the spell failed.
__Examples:__
`"Can only use outside"`
`"Item is not ready yet"`
`"No target"`
...
_etc._|
diff --git a/xCT+/libs/xCombatParser-1.0/example.lua b/xCT+/libs/xCombatParser-1.0/example.lua
new file mode 100644
index 00000000..1d9f1e78
--- /dev/null
+++ b/xCT+/libs/xCombatParser-1.0/example.lua
@@ -0,0 +1,77 @@
+-- Reuben DeLeon: All Rights Reserved. 2018.
+
+-- This example is supposed to help you use xCombatParser-1.0.
+-- You need to already have LibStub loaded before xCombatParser.
+
+-- Description:
+-- Record all the interrupts that your party/raid did during a combat phase.
+-- At the end of combat it will print the top three people that interrupted.
+
+-- Load the xCombatParser Library with LibStub
+local xCP = LibStub and LibStub("xCombatParser-1.0", true)
+
+-- Cache all the interrupts that happened during combat
+local interruptCache = {}
+
+-- Our xCombatParser Handler
+local function xCombatParser_Handler(args)
+ -- We only care about people in our raid that interrupted
+ if args.suffix == "_INTERRUPT" and (args:IsSourceRaidMember() or args:IsSourcePartyMember()) then
+ -- We need to keep the args around until we are finished with them
+ args:pin()
+
+ -- Faster than table.insert
+ interruptCache[#interruptCache + 1] = args
+ end
+end
+
+-- Create a frame to listen for WoW Combat Enter/Leaving Events
+local events = CreateFrame("Frame")
+events:RegisterEvent("PLAYER_REGEN_DISABLED")
+events:RegisterEvent("PLAYER_REGEN_ENABLED")
+
+events:SetScript("OnEvent", function(self, event)
+ -- Start Logging
+ if event == "PLAYER_REGEN_DISABLED" then
+ xCP:RegisterCombat(xCombatParser_Handler)
+
+ -- Stop Logging
+ elseif event == "PLAYER_REGEN_ENABLED" then
+ xCP:UnregisterCombat(xCombatParser_Handler)
+
+ -- List of Names, amounts that interrupt
+ local interrupts = {}
+
+ -- Total up all the interrupts
+ for _, args in pairs(interruptCache) do
+ -- If nil, it will create a new entry, otherwise add one to the count
+ interrupts[args.sourceName] = (interrupts[args.sourceName] or 0) + 1
+
+ -- ALWAYS free anything you pin
+ args:free()
+ end
+
+ -- Sort names by count
+ local temp = {}
+ for name, count in pairs(interrupts) do
+ temp[#temp + 1] = { name = name, count = count }
+ end
+ table.sort(temp, function(a, b)
+ return a.count < b.count
+ end)
+
+ print("Interrupt Recount:")
+
+ if #temp == 0 then
+ print(" Fight Had No Interrupts!")
+ else
+ for index = 1, 3 do
+ local name, count = temp[index].name, temp[index].count
+ print(string.format(" %d: %s interrupted %d time%s.", index, name, count, count == 1 and "" or "s"))
+ end
+ end
+
+ -- Clear out the old cache
+ interrupts = {}
+ end
+end)
diff --git a/xCT+/libs/xCombatParser-1.0/xCombatParser-1.0.lua b/xCT+/libs/xCombatParser-1.0/xCombatParser-1.0.lua
new file mode 100644
index 00000000..5879aeff
--- /dev/null
+++ b/xCT+/libs/xCombatParser-1.0/xCombatParser-1.0.lua
@@ -0,0 +1,691 @@
+-- Create out library
+local Lib, oldLib = LibStub:NewLibrary("xCombatParser-1.0", 2)
+if not Lib then
+ return
+end
+
+-- Our own personal space
+local private = {}
+Lib.private = private
+
+-- This library's functions
+local mt = { __index = {} }
+
+do
+ -- Upvalues
+ local next = next
+
+ -- Registered Event Handles
+ -- Hook into the old handles incase someone registered with them before we could load.
+ private.handles = oldLib and oldLib.private.handles or {}
+
+ -- ------------------------------------------------------------------------------
+ -- Combat Log Helper
+ -- ------------------------------------------------------------------------------
+ -- A concise and easy combat log helper that helps parse events.
+ -- ------------------------------------------------------------------------------
+ -- function Lib:RegisterCombat ( func )
+ -- Parameters:
+ -- func [callback - function ( args )]:
+ -- Callback for every combat event.
+ -- Parameters:
+ -- args [table]: A table that contains the combat event args.
+ -- Please do NOT change any of the values as it is
+ -- going to be passed to everyone. If you need to
+ -- hold it for a while, use "args:pin()" and
+ -- "args:free()" when you are finished with it.
+ -- ------------------------------------------------------------------------------
+ function Lib:RegisterCombat(func)
+ private.handles[func] = true
+ private.frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function Lib:UnregisterCombat ( func )
+ -- Parameters:
+ -- func [callback - function ( args )]:
+ -- The callback that you want to unregister from getting combat
+ -- events. See "Lib:RegisterCombat" for more details.
+ -- ------------------------------------------------------------------------------
+ function Lib:UnregisterCombat(func)
+ private.handles[func] = nil
+ if not next(private.handles) then
+ private.frame:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
+ end
+ end
+end
+
+-- ------------------------------------------------------------------------------
+-- Recyclable Table Pool
+-- ------------------------------------------------------------------------------
+-- An extremely simple table recycle program. "Save the Earth"™
+--
+-- Look at documentation below for details on:
+-- + function private.create
+-- + function private.destroy
+-- + function private.wipe
+-- + function private.pin
+-- + function private.free
+-- ------------------------------------------------------------------------------
+do
+ -- Upvalues
+ local pairs, error, next = pairs, error, next
+
+ -- Pool of tables we've destroyed (they are kept as weak)
+ private.mt_weakKeys = oldLib and oldLib.private.mt_weakKeys or setmetatable({}, { __mode = "k" })
+
+ -- Use our older table pool
+ private.tables = oldLib and oldLib.private.tables or setmetatable({}, private.mt_weakKeys)
+
+ -- ------------------------------------------------------------------------------
+ -- function private.create ( )
+ -- Returns:
+ -- t [table]:
+ -- A clean table ready to go. If a table was recently recycled, you
+ -- got that one, otherwise this is a brand new table.
+ -- ------------------------------------------------------------------------------
+ private.create = function()
+ local t = next(private.tables)
+ if t then
+ private.tables[t] = nil
+ end
+ return t or {}
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function private.destroy ( t )
+ -- Parameters:
+ -- t [table]:
+ -- A table you want recycled. All its values will be removed and it
+ -- will be placed in cold storage waiting for private.create()
+ --
+ -- TODO: Recored where it is pinned created incase they dont destroy
+ -- ------------------------------------------------------------------------------
+ private.destroy = function(t)
+ if t.__pinned and t.__pinned > 0 then
+ error("this table is being used somewhere")
+ end
+ private.tables[private.wipe(t)] = true
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function private.wipe ( t )
+ -- Parameters:
+ -- t [table]:
+ -- A table that contains values you want to wipe.
+ --
+ -- Returns:
+ -- t [table]:
+ -- The return "t" and the parameter "t" are the same table.
+ -- ------------------------------------------------------------------------------
+ private.wipe = function(t)
+ for i in pairs(t) do
+ t[i] = nil
+ end
+ return t
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function private.pin ( t )
+ -- Parameters:
+ -- t [table]:
+ -- A table that you want to keep for an indeterminate amount of time.
+ -- Once "pinned" a table cannot be destroyed until it has been "freed"
+ -- the same amount of times as it has been pinned. When there are no
+ -- more references, it will be destroyed automatically.
+ -- ------------------------------------------------------------------------------
+ private.pin = function(t)
+ if not t.__pinned then
+ t.__pinned = 1
+ else
+ t.__pinned = t.__pinned + 1
+ end
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function private.free ( t )
+ -- Parameters:
+ -- t [table]:
+ -- A table that has been pinned. If there are no more references to
+ -- this table, the table will be destroyed automatically.
+ -- ------------------------------------------------------------------------------
+ private.free = function(t)
+ if not t.__pinned then
+ error("table has not been pinned")
+ end
+ t.__pinned = t.__pinned - 1
+ if t.__pinned == 0 then
+ private.destroy(t)
+ end
+ end
+
+ -- ------------------------------------------------------------------------------
+ -- function private.populate ( ... )
+ -- Parameters:
+ -- ... [dynamic]:
+ -- A list of parameters to populate the table with
+ -- ------------------------------------------------------------------------------
+ private.populate = function(...)
+ local t, n = private.create(), select("#", ...)
+ for i = 1, n do
+ t[i] = select(i, ...)
+ end
+ t.n = n
+ return t
+ end
+end
+
+-- Handle the Events
+do
+ -- Upvalues (Only values that will be used more than once)
+ local sub, playerGUID = string.sub
+
+ -- Recycle or create out event frame
+ private.frame = oldLib and oldLib.private.frame or CreateFrame("frame")
+ private.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
+
+ -- Localize Auto Attack
+ local ENVIRONMENT_SUBHEADER, AUTO_ATTACK = ENVIRONMENT_SUBHEADER, C_Spell.GetSpellName(6603)
+
+ -- Localize Environmental Damage
+ local ENVIRONMENTAL_TYPES = {
+ ["Drowning"] = ACTION_ENVIRONMENTAL_DAMAGE_DROWNING,
+ ["Falling"] = ACTION_ENVIRONMENTAL_DAMAGE_FALLING,
+ ["Fatigue"] = ACTION_ENVIRONMENTAL_DAMAGE_FATIGUE,
+ ["Fire"] = ACTION_ENVIRONMENTAL_DAMAGE_FIRE,
+ ["Lava"] = ACTION_ENVIRONMENTAL_DAMAGE_LAVA,
+ ["Slime"] = ACTION_ENVIRONMENTAL_DAMAGE_SLIME,
+ }
+
+ -- Used for Spell Icons ONLY
+ local ENVIRONMENTAL_FAKE_IDS = {
+ ["Drowning"] = 89662, -- Drowning (Quest in Hillsbrad (Lvl ~22), for Horde only)
+ ["Falling"] = 150875, -- Falling (Not sure where it is used)
+ ["Fatigue"] = 125024, -- Fatigue (Not sure where it is used)
+ ["Fire"] = 186242, -- Fire (Not sure where it is used)
+ ["Lava"] = 192519, -- Lava (New spell mechanic for something in Legion)
+ ["Slime"] = 179021, -- Slime (New spell mechanic for something in Legion)
+ }
+
+ local ENVIRONMENTAL_FAKE_SPELLSCHOOL = {
+ ["Drowning"] = 1, -- Physical
+ ["Falling"] = 1, -- Physical
+ ["Fatigue"] = 1, -- Physical
+ ["Fire"] = 4, -- Fire
+ ["Lava"] = 4, -- Fire
+ ["Slime"] = 8, -- Nature
+ }
+
+ -- Handle all the combat events. This needs to be extremely efficient
+ private.frame:SetScript("OnEvent", function(self, frameEvent)
+ if frameEvent == "COMBAT_LOG_EVENT_UNFILTERED" then
+ local n = 12
+ local i, args = 0, private.create()
+
+ -- Create the table so that we can resuse it later
+ local t = private.populate(CombatLogGetCurrentEventInfo())
+
+ -- fast event
+ local event = t[2]
+
+ -- 11 Parameters of the COMBAT_LOG_EVENT --- Index
+ args.timestamp = t[1]
+ args.event = t[2]
+ args.hideCaster = t[3]
+ args.sourceGUID = t[4]
+ args.sourceName = t[5]
+ args.sourceFlags = t[6]
+ args.sourceRaidFlags = t[7]
+ args.destGUID = t[8]
+ args.destName = t[9]
+ args.destFlags = t[10]
+ args.destRaidFlags = t[11]
+
+ local prefix, suffix
+ if event == "DAMAGE_SHIELD" or event == "DAMAGE_SPLIT" then
+ prefix = "SPELL"
+ suffix = "_DAMAGE"
+ elseif event == "SPELL_ABSORBED" then
+ if t.n <= 21 then
+ prefix = "SWING"
+ suffix = "_ABSORBED"
+ else
+ prefix = "SPELL"
+ suffix = "_ABSORBED"
+ end
+ elseif event == "DAMAGE_SHIELD_MISSED" then
+ prefix = "SPELL"
+ suffix = "_MISSED"
+ elseif event == "ENCHANT_APPLIED" or event == "ENCHANT_REMOVED" then
+ args.spellName, args.itemId, args.itemName = t[n], t[n + 1], t[n + 2]
+ --args.spellName, args.itemId, args.itemName = select(1, ...)
+ elseif event == "UNIT_DIED" or event == "UNIT_DESTROYED" or event == "UNIT_DISSIPATES" then
+ args.recapID, args.unconsciousOnDeath = t[n], t[n + 1]
+ elseif event == "PARTY_KILL" then -- do nothing
+ else
+ prefix = sub(event, 1, 14)
+ if prefix == "SPELL_PERIODIC" or prefix == "SPELL_BUILDING" then
+ suffix = sub(event, 15)
+ else
+ prefix = sub(event, 1, 5)
+ if prefix == "SPELL" or prefix == "SWING" or prefix == "RANGE" then
+ suffix = sub(event, 6)
+ else
+ prefix = sub(event, 1, 13)
+ if prefix == "ENVIRONMENTAL" then
+ suffix = sub(event, 14)
+ else
+ error("Unhandled Combat Log Event: " .. tostring(event))
+ end
+ end
+ end
+ end
+
+ args.prefix = prefix
+ args.suffix = suffix
+
+ if prefix == "SPELL" or prefix == "SPELL_PERIODIC" or prefix == "RANGE" or prefix == "SPELL_BUILDING" then
+ args.spellId, args.spellName, args.spellSchool = t[n], t[n + 1], t[n + 2]
+ i = 3
+ elseif prefix == "ENVIRONMENTAL" then
+ local environmentalType = t[n]
+
+ args.environmentalType = environmentalType
+ args.sourceName = ENVIRONMENT_SUBHEADER
+
+ -- Fake out some spell things for icons and names
+ args.spellId, args.spellName, args.spellSchool =
+ ENVIRONMENTAL_FAKE_IDS[environmentalType],
+ ENVIRONMENTAL_TYPES[environmentalType],
+ ENVIRONMENTAL_FAKE_SPELLSCHOOL[environmentalType]
+ i = 1
+ elseif prefix == "SWING" then
+ -- SpellId for Auto Attack, "Auto Attack" (Localized), Physical Damage (Spell School)
+ args.spellId, args.spellName, args.spellSchool = 6603, AUTO_ATTACK, 1
+ end
+
+ if suffix == "_DAMAGE" then
+ args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing, args.isOffHand =
+ t[n + i],
+ t[n + i + 1],
+ t[n + i + 2],
+ t[n + i + 3],
+ t[n + i + 4],
+ t[n + i + 5],
+ t[n + i + 6],
+ t[n + i + 7],
+ t[n + i + 8],
+ t[n + i + 9]
+ elseif suffix == "_ABSORBED" then
+ args.casterGUID, args.casterName, args.casterFlags, args.casterRaidFlags, args.absorbSpellId, args.absorbSpellName, args.absorbSpellSchool, args.amount, args.critical =
+ t[n + i],
+ t[n + i + 1],
+ t[n + i + 2],
+ t[n + i + 3],
+ t[n + i + 4],
+ t[n + i + 5],
+ t[n + i + 6],
+ t[n + i + 7],
+ t[n + i + 8]
+ elseif suffix == "_MISSED" then
+ args.missType, args.isOffHand, args.amountMissed = t[n + i], t[n + i + 1], t[n + i + 2] or 0
+ elseif suffix == "_HEAL" then
+ args.amount, args.overhealing, args.absorbed, args.critical =
+ t[n + i], t[n + i + 1], t[n + i + 2], t[n + i + 3]
+ elseif suffix == "_ENERGIZE" then
+ args.amount, args.overEnergize, args.powerType, args.alternatePowerType =
+ t[n + i], t[n + i + 1], t[n + i + 2], t[n + i + 3]
+ elseif suffix == "_DRAIN" or suffix == "_LEECH" then
+ args.amount, args.powerType, args.extraAmount = t[n + i], t[n + i + 1], t[n + i + 2]
+ elseif suffix == "_INTERRUPT" or suffix == "_DISPEL_FAILED" then
+ args.extraSpellId, args.extraSpellName, args.extraSchool = t[n + i], t[n + i + 1], t[n + i + 2]
+ elseif suffix == "_DISPEL" or suffix == "_STOLEN" then
+ args.extraSpellId, args.extraSpellName, args.extraSchool, args.auraType =
+ t[n + i], t[n + i + 1], t[n + i + 2], t[n + i + 3]
+ elseif suffix == "_EXTRA_ATTACKS" then
+ args.amount = t[n + i]
+ elseif
+ suffix == "_AURA_APPLIED"
+ or suffix == "_AURA_REMOVED"
+ or suffix == "_AURA_APPLIED_DOSE"
+ or suffix == "_AURA_REMOVED_DOSE"
+ or suffix == "_AURA_REFRESH"
+ then
+ -- auraType: BUFF, DEBUFF
+ args.auraType, args.amount = t[n + i], t[n + i + 1]
+ elseif suffix == "_AURA_BROKEN" then
+ args.auraType = t[n + i]
+ elseif suffix == "_AURA_BROKEN_SPELL" then
+ args.extraSpellId, args.extraSpellName, args.extraSchool, args.auraType =
+ t[n + i], t[n + i + 1], t[n + i + 2]
+ elseif suffix == "_CAST_FAILED" then
+ args.failedType = t[n + i]
+ end
+
+ -- Destroy the old
+ private.destroy(t)
+
+ -- IsPlayer helpers
+ args.isPlayer = playerGUID == args.sourceGUID
+ args.atPlayer = playerGUID == args.destGUID
+
+ -- Add Our API to the Combat Event Args
+ setmetatable(args, mt)
+
+ -- Call all the registered handlers
+ args:pin()
+ for func in pairs(private.handles) do
+ func(args)
+ end
+ args:free() -- If no one else pinned this table, it should be cleaned up now
+ else
+ self:UnregisterEvent("PLAYER_ENTERING_WORLD")
+ playerGUID = UnitGUID("player")
+ end
+ end)
+end
+
+-- Functions that don't require hasFlag
+do
+ local band = bit.band
+ do
+ -- Up-values
+ local COMBATLOG_OBJECT_TYPE_MASK, COMBATLOG_OBJECT_CONTROL_MASK, COMBATLOG_OBJECT_REACTION_MASK, COMBATLOG_OBJECT_AFFILIATION_MASK =
+ COMBATLOG_OBJECT_TYPE_MASK,
+ COMBATLOG_OBJECT_CONTROL_MASK,
+ COMBATLOG_OBJECT_REACTION_MASK,
+ COMBATLOG_OBJECT_AFFILIATION_MASK
+
+ local COMBATLOG_OBJECTS = {
+ [0] = "UNKNOWN",
+
+ -- Types
+ [COMBATLOG_OBJECT_TYPE_GUARDIAN] = "GUARDIAN",
+ [COMBATLOG_OBJECT_TYPE_OBJECT] = "OBJECT",
+ [COMBATLOG_OBJECT_TYPE_PLAYER] = "PLAYER",
+ [COMBATLOG_OBJECT_TYPE_PET] = "PET",
+ [COMBATLOG_OBJECT_TYPE_NPC] = "NPC",
+
+ -- Controllers
+ [COMBATLOG_OBJECT_CONTROL_PLAYER] = "PLAYER",
+ [COMBATLOG_OBJECT_CONTROL_NPC] = "NPC",
+
+ -- Reactions
+ [COMBATLOG_OBJECT_REACTION_FRIENDLY] = "FRIENDLY",
+ [COMBATLOG_OBJECT_REACTION_HOSTILE] = "HOSTILE",
+ [COMBATLOG_OBJECT_REACTION_NEUTRAL] = "NEUTRAL",
+
+ -- Affiliations
+ [COMBATLOG_OBJECT_AFFILIATION_OUTSIDER] = "OUTSIDER",
+ [COMBATLOG_OBJECT_AFFILIATION_PARTY] = "PARTY",
+ [COMBATLOG_OBJECT_AFFILIATION_RAID] = "RAID",
+ [COMBATLOG_OBJECT_AFFILIATION_MINE] = "MINE",
+ }
+
+ function private.GetSourceType(args)
+ if args.prefix == "ENVIRONMENTAL" then
+ return "ENVIRONMENT"
+ end
+ return COMBATLOG_OBJECTS[band(args.sourceFlags or 0, COMBATLOG_OBJECT_TYPE_MASK)]
+ end
+
+ function private.GetDestinationType(args)
+ return COMBATLOG_OBJECTS[band(args.destFlags or 0, COMBATLOG_OBJECT_TYPE_MASK)]
+ end
+
+ function private.GetSourceController(args)
+ if args.prefix == "ENVIRONMENTAL" then
+ return "ENVIRONMENT"
+ end
+ return COMBATLOG_OBJECTS[band(args.sourceFlags or 0, COMBATLOG_OBJECT_CONTROL_MASK)]
+ end
+
+ function private.GetDestinationController(args)
+ return COMBATLOG_OBJECTS[band(args.destFlags or 0, COMBATLOG_OBJECT_CONTROL_MASK)]
+ end
+
+ function private.GetSourceReaction(args)
+ if args.prefix == "ENVIRONMENTAL" then
+ return "NEUTRAL"
+ end
+ return COMBATLOG_OBJECTS[band(args.sourceFlags or 0, COMBATLOG_OBJECT_REACTION_MASK)]
+ end
+
+ function private.GetDestinationReaction(args)
+ return COMBATLOG_OBJECTS[band(args.destFlags or 0, COMBATLOG_OBJECT_REACTION_MASK)]
+ end
+
+ function private.GetSourceAffiliation(args)
+ if args.prefix == "ENVIRONMENTAL" then
+ return "OUTSIDER"
+ end
+ return COMBATLOG_OBJECTS[band(args.sourceFlags or 0, COMBATLOG_OBJECT_AFFILIATION_MASK)]
+ end
+
+ function private.GetDestinationAffiliation(args)
+ return COMBATLOG_OBJECTS[band(args.destFlags or 0, COMBATLOG_OBJECT_AFFILIATION_MASK)]
+ end
+ end
+
+ -- GetSourceRaidTargetIndex, GetDestinationRaidTargetIndex, GetSourceRaidTargetName, and GetDestinationRaidTargetName
+ do
+ local RAIDTARGET_INDEXES, RAIDTARGET_NAMES, COMBATLOG_OBJECT_RAIDTARGET_MASK =
+ {
+ [0] = 0,
+ [COMBATLOG_OBJECT_RAIDTARGET1] = 1,
+ [COMBATLOG_OBJECT_RAIDTARGET2] = 2,
+ [COMBATLOG_OBJECT_RAIDTARGET3] = 3,
+ [COMBATLOG_OBJECT_RAIDTARGET4] = 4,
+ [COMBATLOG_OBJECT_RAIDTARGET5] = 5,
+ [COMBATLOG_OBJECT_RAIDTARGET6] = 6,
+ [COMBATLOG_OBJECT_RAIDTARGET7] = 7,
+ [COMBATLOG_OBJECT_RAIDTARGET8] = 8,
+ }, {
+ RAID_TARGET_1,
+ RAID_TARGET_2,
+ RAID_TARGET_3,
+ RAID_TARGET_4,
+ RAID_TARGET_5,
+ RAID_TARGET_6,
+ RAID_TARGET_7,
+ RAID_TARGET_8,
+ [0] = NONE,
+ }, COMBATLOG_OBJECT_RAIDTARGET_MASK
+
+ function private.GetSourceRaidTargetIndex(args)
+ return RAIDTARGET_INDEXES[band(args.sourceRaidFlags or 0, COMBATLOG_OBJECT_RAIDTARGET_MASK)]
+ end
+
+ function private.GetDestinationRaidTargetIndex(args)
+ return RAIDTARGET_INDEXES[band(args.destRaidFlags or 0, COMBATLOG_OBJECT_RAIDTARGET_MASK)]
+ end
+
+ function private.GetSourceRaidTargetName(args)
+ return RAIDTARGET_NAMES[private.GetSourceRaidTargetIndex(args)]
+ end
+
+ function private.GetDestinationRaidTargetName(args)
+ return RAIDTARGET_NAMES[private.GetDestinationRaidTargetIndex(args)]
+ end
+ end
+end
+
+local hasFlag
+do
+ local band = bit.band
+ function hasFlag(flags, flag)
+ return band(flags or 0, flag) == flag
+ end
+end
+
+do
+ local COMBATLOG_OBJECT_NONE = COMBATLOG_OBJECT_NONE
+
+ function private.IsSourceNotSpecial(args)
+ return hasFlag(args.sourceFlags, COMBATLOG_OBJECT_NONE)
+ end
+
+ function private.IsDestinationNotSpecial(args)
+ return hasFlag(args.destFlags, COMBATLOG_OBJECT_NONE)
+ end
+end
+
+do
+ local COMBATLOG_OBJECT_MAINASSIST = COMBATLOG_OBJECT_MAINASSIST
+
+ function private.IsSourceMainAssist(args)
+ return hasFlag(args.sourceFlags, COMBATLOG_OBJECT_MAINASSIST)
+ end
+
+ function private.IsDestinationMainAssist(args)
+ return hasFlag(args.destFlags, COMBATLOG_OBJECT_MAINASSIST)
+ end
+end
+
+do
+ local COMBATLOG_OBJECT_MAINTANK = COMBATLOG_OBJECT_MAINTANK
+
+ function private.IsSourceMainTank(args)
+ return hasFlag(args.sourceFlags, COMBATLOG_OBJECT_MAINTANK)
+ end
+
+ function private.IsDestinationMainTank(args)
+ return hasFlag(args.destFlags, COMBATLOG_OBJECT_MAINTANK)
+ end
+end
+
+do
+ local COMBATLOG_OBJECT_FOCUS = COMBATLOG_OBJECT_FOCUS
+
+ function private.IsSourceFocus(args)
+ return hasFlag(args.sourceFlags, COMBATLOG_OBJECT_FOCUS)
+ end
+
+ function private.IsDestinationFocus(args)
+ return hasFlag(args.destFlags, COMBATLOG_OBJECT_FOCUS)
+ end
+end
+
+do
+ local COMBATLOG_OBJECT_TARGET = COMBATLOG_OBJECT_TARGET
+
+ function private.IsSourceTarget(args)
+ return hasFlag(args.sourceFlags, COMBATLOG_OBJECT_TARGET)
+ end
+
+ function private.IsDestinationTarget(args)
+ return hasFlag(args.destFlags, COMBATLOG_OBJECT_TARGET)
+ end
+end
+
+do
+ local MY_PET_FLAGS = bit.bor(
+ COMBATLOG_OBJECT_AFFILIATION_MINE,
+ COMBATLOG_OBJECT_REACTION_FRIENDLY,
+ COMBATLOG_OBJECT_CONTROL_PLAYER,
+ COMBATLOG_OBJECT_TYPE_PET
+ )
+
+ function private.IsSourceMyPet(args)
+ return hasFlag(args.sourceFlags, MY_PET_FLAGS)
+ end
+
+ function private.IsDestinationMyPet(args)
+ return hasFlag(args.destFlags, MY_PET_FLAGS)
+ end
+end
+
+do
+ local MY_VEHICLE_FLAGS = bit.bor(
+ COMBATLOG_OBJECT_AFFILIATION_MINE,
+ COMBATLOG_OBJECT_REACTION_FRIENDLY,
+ COMBATLOG_OBJECT_CONTROL_PLAYER,
+ COMBATLOG_OBJECT_TYPE_GUARDIAN
+ )
+
+ function private.IsSourceMyVehicle(args)
+ return hasFlag(args.sourceFlags, MY_VEHICLE_FLAGS)
+ end
+
+ function private.IsDestinationMyVehicle(args)
+ return hasFlag(args.destFlags, MY_VEHICLE_FLAGS)
+ end
+end
+
+do
+ local MY_RAID_MEMBER = bit.bor(
+ COMBATLOG_OBJECT_AFFILIATION_RAID,
+ COMBATLOG_OBJECT_REACTION_FRIENDLY,
+ COMBATLOG_OBJECT_CONTROL_PLAYER,
+ COMBATLOG_OBJECT_TYPE_PLAYER
+ )
+
+ function private.IsSourceRaidMember(args)
+ return hasFlag(args.sourceFlags, MY_RAID_MEMBER)
+ end
+
+ function private.IsDestinationRaidMember(args)
+ return hasFlag(args.destFlags, MY_RAID_MEMBER)
+ end
+end
+
+do
+ local MY_PARTY_MEMBER = bit.bor(
+ COMBATLOG_OBJECT_AFFILIATION_PARTY,
+ COMBATLOG_OBJECT_REACTION_FRIENDLY,
+ COMBATLOG_OBJECT_CONTROL_PLAYER,
+ COMBATLOG_OBJECT_TYPE_PLAYER
+ )
+
+ function private.IsSourcePartyMember(args)
+ return hasFlag(args.sourceFlags, MY_PARTY_MEMBER)
+ end
+
+ function private.IsDestinationPartyMember(args)
+ return hasFlag(args.destFlags, MY_PARTY_MEMBER)
+ end
+end
+
+-- Memory Helpers
+mt.__index.pin = private.pin
+mt.__index.free = private.free
+
+-- Unit Flag Helpers
+mt.__index.GetSourceType = private.GetSourceType
+mt.__index.GetDestinationType = private.GetDestinationType
+mt.__index.GetSourceController = private.GetSourceController
+mt.__index.GetDestinationController = private.GetDestinationController
+mt.__index.GetSourceReaction = private.GetSourceReaction
+mt.__index.GetDestinationReaction = private.GetDestinationReaction
+mt.__index.GetSourceAffiliation = private.GetSourceAffiliation
+mt.__index.GetDestinationAffiliation = private.GetDestinationAffiliation
+
+-- Raid Target Flag Helpers
+mt.__index.GetSourceRaidTargetIndex = private.GetSourceRaidTargetIndex
+mt.__index.GetDestinationRaidTargetIndex = private.GetDestinationRaidTargetIndex
+mt.__index.GetSourceRaidTargetName = private.GetSourceRaidTargetName
+mt.__index.GetDestinationRaidTargetName = private.GetDestinationRaidTargetName
+
+-- Special Unit Flag Helpers
+mt.__index.IsSourceNotSpecial = private.IsSourceNotSpecial
+mt.__index.IsDestinationNotSpecial = private.IsDestinationNotSpecial
+mt.__index.IsSourceMainAssist = private.IsSourceMainAssist
+mt.__index.IsDestinationMainAssist = private.IsDestinationMainAssist
+mt.__index.IsSourceMainTank = private.IsSourceMainTank
+mt.__index.IsDestinationMainTank = private.IsDestinationMainTank
+mt.__index.IsSourceFocus = private.IsSourceFocus
+mt.__index.IsDestinationFocus = private.IsDestinationFocus
+mt.__index.IsSourceTarget = private.IsSourceTarget
+mt.__index.IsDestinationTarget = private.IsDestinationTarget
+
+-- Crafted Unit Flag Helpers
+mt.__index.IsSourceMyPet = private.IsSourceMyPet
+mt.__index.IsDestinationMyPet = private.IsDestinationMyPet
+mt.__index.IsSourceMyVehicle = private.IsSourceMyVehicle
+mt.__index.IsDestinationMyVehicle = private.IsDestinationMyVehicle
+
+-- Raid/Party Member Checks
+mt.__index.IsSourceRaidMember = private.IsSourceRaidMember
+mt.__index.IsDestinationRaidMember = private.IsDestinationRaidMember
+mt.__index.IsSourcePartyMember = private.IsSourcePartyMember
+mt.__index.IsDestinationPartyMember = private.IsDestinationPartyMember
diff --git a/xCT+/locales/enUS.lua b/xCT+/locales/enUS.lua
new file mode 100644
index 00000000..7ae0b135
--- /dev/null
+++ b/xCT+/locales/enUS.lua
@@ -0,0 +1,28 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "enUS", true, true)
+if not L then return end
+
+L["Basic spells"] = true
+L["Battle for Azeroth"] = true
+L["Dragonflight"] = true
+L["Legion"] = true
+L["Mists of Pandaria"] = true
+L["Shadowlands"] = true
+L["Warlords of Draenor"] = true
diff --git a/xCT+/locales/ruRU.lua b/xCT+/locales/ruRU.lua
new file mode 100644
index 00000000..33cfa15f
--- /dev/null
+++ b/xCT+/locales/ruRU.lua
@@ -0,0 +1,30 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- Translators: ZamestoTV (https://github.com/Hubbotu)
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "ruRU", false, false)
+if not L then return end
+
+L["Basic spells"] = "Базовые заклинания"
+L["Battle for Azeroth"] = "Битва за Азерот"
+L["Dragonflight"] = "Полет Дракона"
+L["Legion"] = "Легион"
+L["Mists of Pandaria"] = "Туманы Пандарии"
+L["Shadowlands"] = "Темные Земли"
+L["Warlords of Draenor"] = "Вожди Дренора"
diff --git a/xCT+/locales/zhCN.lua b/xCT+/locales/zhCN.lua
new file mode 100644
index 00000000..3179dd9d
--- /dev/null
+++ b/xCT+/locales/zhCN.lua
@@ -0,0 +1,31 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- Translators:
+-- * https://github.com/fredako
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "zhCN", false, false)
+if not L then return end
+
+L["Basic spells"] = "基础施法"
+L["Battle for Azeroth"] = "争霸艾泽拉斯"
+L["Dragonflight"] = "巨龙时代"
+L["Legion"] = "军团再临"
+L["Mists of Pandaria"] = "熊猫人之谜"
+L["Shadowlands"] = "暗影国度"
+L["Warlords of Draenor"] = "德拉诺之王"
diff --git a/xCT+/media/Champagne & Limousines Bold.ttf b/xCT+/media/Champagne & Limousines Bold.ttf
new file mode 100644
index 00000000..25f9ea3a
Binary files /dev/null and b/xCT+/media/Champagne & Limousines Bold.ttf differ
diff --git a/HOOGE.TTF b/xCT+/media/HOOGE.TTF
similarity index 100%
rename from HOOGE.TTF
rename to xCT+/media/HOOGE.TTF
diff --git a/xCT+/media/OpenSans-CondBold.ttf b/xCT+/media/OpenSans-CondBold.ttf
new file mode 100644
index 00000000..83966f21
Binary files /dev/null and b/xCT+/media/OpenSans-CondBold.ttf differ
diff --git a/xCT+/media/OpenSans-CondLight.ttf b/xCT+/media/OpenSans-CondLight.ttf
new file mode 100644
index 00000000..97c355b9
Binary files /dev/null and b/xCT+/media/OpenSans-CondLight.ttf differ
diff --git a/xCT+/media/OpenSans-CondLightItalic.ttf b/xCT+/media/OpenSans-CondLightItalic.ttf
new file mode 100644
index 00000000..0b45898d
Binary files /dev/null and b/xCT+/media/OpenSans-CondLightItalic.ttf differ
diff --git a/xCT+/media/VintageOne.ttf b/xCT+/media/VintageOne.ttf
new file mode 100644
index 00000000..b5f10737
Binary files /dev/null and b/xCT+/media/VintageOne.ttf differ
diff --git a/blank.tga b/xCT+/media/blank.tga
similarity index 100%
rename from blank.tga
rename to xCT+/media/blank.tga
diff --git a/xCT+/media/homespun.ttf b/xCT+/media/homespun.ttf
new file mode 100644
index 00000000..cc6e6969
Binary files /dev/null and b/xCT+/media/homespun.ttf differ
diff --git a/xCT+/media/media.lua b/xCT+/media/media.lua
new file mode 100644
index 00000000..1df632bf
--- /dev/null
+++ b/xCT+/media/media.lua
@@ -0,0 +1,65 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local ADDON_NAME, addon = ...
+
+-- Textures
+local x = addon.engine
+x.BLANK_ICON = "Interface\\AddOns\\" .. ADDON_NAME .. "\\media\\blank"
+
+-- Fonts
+local LSM = LibStub("LibSharedMedia-3.0")
+
+LSM:Register(
+ "font",
+ "Champagne (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\Champagne & Limousines Bold.ttf]],
+ LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "Homespun (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\homespun.ttf]],
+ LSM.LOCALE_BIT_ruRU + LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "HOOGE (xCT)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\HOOGE.TTF]],
+ LSM.LOCALE_BIT_ruRU + LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "Condensed Bold (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\OpenSans-CondBold.ttf]],
+ LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "Condensed Light (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\OpenSans-CondLight.ttf]],
+ LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "Condensed Light Italics (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\OpenSans-CondLightItalic.ttf]],
+ LSM.LOCALE_BIT_western
+)
+LSM:Register(
+ "font",
+ "Vintage (xCT+)",
+ [[Interface\AddOns\]] .. ADDON_NAME .. [[\media\VintageOne.ttf]],
+ LSM.LOCALE_BIT_western
+)
diff --git a/xCT+/media/pet_icons.lua b/xCT+/media/pet_icons.lua
new file mode 100644
index 00000000..5c3b2d6a
--- /dev/null
+++ b/xCT+/media/pet_icons.lua
@@ -0,0 +1,92 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+
+-- Shorten my handle
+local x = addon.engine
+
+addon.DEFAULT_PET_ICON = "ability_hunter_pet_goto"
+
+addon.PET_ICONS = {
+
+ -- Hunter Pets (from WoWHead) Updated for 10.0.2
+ [236165] = "ability_druid_primalprecision", -- Spirit Beast
+ [236191] = "ability_hunter_pet_corehound", -- Core Hound
+ [1044794] = "inv_clefthoofdraenormount_blue", -- Clefthoof
+ [132203] = "ability_hunter_pet_wolf", -- Wolf
+ [132194] = "ability_hunter_pet_ravager", -- Ravager
+ [236192] = "ability_hunter_pet_devilsaur", -- Devilsaur
+ [132193] = "ability_hunter_pet_raptor", -- Raptor
+ [236195] = "ability_hunter_pet_silithid", -- Aqiri
+ [646378] = "inv_mushanbeastmount", -- Scalehide
+ [132185] = "ability_hunter_pet_cat", -- Cat
+ [625905] = "achievement_moguraid_01", -- Stone Hound
+ [236190] = "ability_hunter_pet_chimera", -- Chimaera
+ [132254] = "ability_mount_ridingelekk", -- Mammoth
+ [2143073] = "inv_horse3_pale", -- Courser
+ [132189] = "ability_hunter_pet_gorilla", -- Gorilla
+ [877478] = "inv_pet_-shalespider", -- Shale Beast
+ [132202] = "ability_hunter_pet_windserpent", -- Wind Serpent
+ [463493] = "trade_archaeology_whitehydrafigurine", -- Hydra
+ [132183] = "ability_hunter_pet_bear", -- Bear
+ [132190] = "ability_hunter_pet_hyena", -- Hyena
+ [132247] = "ability_mount_mechastrider", -- Mechanical
+ [136040] = "spell_nature_guardianward", -- Serpent
+ [644001] = "inv_pet_porcupine", -- Rodent
+ [877481] = "inv_pet_mastiff", -- Hound
+ [132192] = "ability_hunter_pet_owl", -- Bird of Prey
+ [132196] = "ability_hunter_pet_spider", -- Spider
+ [236197] = "ability_hunter_pet_worm", -- Worm
+ [877480] = "inv_pet_direhorn", -- Direhorn
+ [929300] = "inv_misc_elitehippogryph", -- Feathermane
+ [236196] = "ability_hunter_pet_wasp", -- Wasp
+ [132184] = "ability_hunter_pet_boar", -- Boar
+ [2011146] = "achievement_dungeon_thesandqueen", -- Carapid
+ [132201] = "ability_hunter_pet_warpstalker", -- Warp Stalker
+ [1687702] = "inv_bloodtrollbeast_mount", -- Blood Beast
+ [458223] = "ability_hunter_aspectofthefox", -- Fox
+ [132199] = "ability_hunter_pet_turtle", -- Turtle
+ [132195] = "ability_hunter_pet_scorpid", -- Scorpid
+ [132200] = "ability_hunter_pet_vulture", -- Carrion Bird
+ [1044490] = "inv_hippo_green", -- Riverbeast
+ [132182] = "ability_hunter_pet_bat", -- Bat
+ [132186] = "ability_hunter_pet_crab", -- Crab
+ [1624590] = "inv_pterrordax2mount_yellow", -- Pterrordax
+ [132188] = "ability_hunter_pet_dragonhawk", -- Dragonhawk
+ [643423] = "inv_pet_waterstrider", -- Water Strider
+ [2027936] = "inv_komododragon_green", -- Lizard
+ [132197] = "ability_hunter_pet_sporebat", -- Sporebat
+ [132191] = "ability_hunter_pet_netherray", -- Ray
+ [454771] = "ability_mount_camel_brown", -- Camel
+ [804969] = "inv_pet_toad_green", -- Toad
+ [236193] = "ability_hunter_pet_moth", -- Moth
+ [877476] = "inv_pet_-basilisk", -- Basilisk
+ [616693] = "ability_mount_yakmount", -- Oxen
+ [132187] = "ability_hunter_pet_crocolisk", -- Crocolisk
+ [877477] = "inv_pet_-goat", -- Gruffhorn
+ [133570] = "inv_misc_ahnqirajtrinket_01", -- Beetle
+ [877482] = "inv_pet_monkey", -- Monkey
+ [877479] = "inv_pet_crane", -- Crane
+ [1044501] = "inv_talbukdraenor_white", -- Stag
+ [132198] = "ability_hunter_pet_tallstrider", -- Tallstrider
+ [797547] = "inv_misc_head_dragon_nexus", -- Lesser Dragonkin
+
+ -- TODO: Add other class pets here :(
+}
+
+-- GetPetIcon returns a textureID (a number), when it used to return a texture path
+function x:GetPetTexture()
+ return "Interface\\Icons\\" .. (addon.PET_ICONS[GetPetIcon()] or addon.DEFAULT_PET_ICON)
+end
diff --git a/xCT+/modules/blizzard.lua b/xCT+/modules/blizzard.lua
new file mode 100644
index 00000000..686d93df
--- /dev/null
+++ b/xCT+/modules/blizzard.lua
@@ -0,0 +1,27 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- TODO: Fix this up
+
+local _, addon = ...
+local x = addon.engine
+
+-- Intercept Messages Sent by other Add-Ons that use CombatText_AddMessage
+hooksecurefunc("CombatText_AddMessage", function(message, scrollFunction, r, g, b, displayType, isStaggered)
+ if not x.db.profile.blizzardFCT.enableFloatingCombatText then
+ local lastEntry = COMBAT_TEXT_TO_ANIMATE[#COMBAT_TEXT_TO_ANIMATE]
+ CombatText_RemoveMessage(lastEntry)
+ x:AddMessage("general", message, { r, g, b })
+ end
+end)
diff --git a/xCT+/modules/combattext.lua b/xCT+/modules/combattext.lua
new file mode 100644
index 00000000..683dbc99
--- /dev/null
+++ b/xCT+/modules/combattext.lua
@@ -0,0 +1,1976 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+
+-- Shorten my handle
+local x = addon.engine
+
+-- returns a string with the first character in upper case
+local function utf8_fc_upper(source)
+ return string.utf8upper(string.utf8sub(source, 1, 1)) .. string.utf8sub(source, 2)
+end
+
+local function table_clone(source)
+ local clone = {}
+ for k,v in pairs(source) do
+ clone[k] = v
+ end
+ return setmetatable(clone, getmetatable(source))
+end
+
+x.locale = GetLocale()
+
+local L_AUTOATTACK = C_Spell.GetSpellName(6603)
+local L_KILLCOMMAND = C_Spell.GetSpellName(34026)
+
+--[=====================================================[
+ Power Type Definitions, see https://wowpedia.fandom.com/wiki/Enum.PowerType
+--]=====================================================]
+x.POWER_LOOKUP = {
+ [0] = "MANA",
+ [1] = "RAGE",
+ [2] = "FOCUS",
+ [3] = "ENERGY",
+ [4] = "COMBO_POINTS",
+ [5] = "RUNES",
+ [6] = "RUNIC_POWER",
+ [7] = "SOUL_SHARDS",
+ [8] = "LUNAR_POWER",
+ [9] = "HOLY_POWER",
+ [10] = "ALTERNATE_POWER_INDEX",
+ [11] = "MAELSTROM",
+ [12] = "CHI",
+ [13] = "INSANITY",
+ [14] = "BURNING_EMBERS",
+ [15] = "DEMONIC_FURY",
+ [16] = "ARCANE_CHARGES",
+ [17] = "FURY",
+ [18] = "PAIN",
+ [25] = "VIGOR",
+}
+
+--[=====================================================[
+ String Formatters
+--]=====================================================]
+-- TODO: Remove old loot pattern
+--local format_loot = "([^|]*)|cff(%x*)|H([^:]*):(%d+):%d+:(%d+):[-?%d+:]+|h%[?([^%]]*)%]|h|r?%s?x?(%d*)%.?"
+-- "You create: |cffa335ee|Hitem:124515::::::::100:254:4:3::530:::|h[Talisman of the Master Tracker]|h|r"
+--local msg = "|cff1eff00|Hitem:108840:0:0:0:0:0:0:443688319:90:0:0:0|h[Warlords Intro Zone PH Mail Helm]|h|r"
+--local format_loot = "([^|]*)|cff(%x*)|H([^:]*):(%d+):[-?%d+:]+|h%[?([^%]]*)%]|h|r?%s?x?(%d*)%.?"
+
+local format_honor = string.gsub(COMBAT_TEXT_HONOR_GAINED, "%%s", "+%%s")
+
+local format_crafted = (LOOT_ITEM_CREATED_SELF:gsub("%%.*", "")) -- "You create: "
+if x.locale == "koKR" then
+ format_crafted = (LOOT_ITEM_CREATED_SELF:gsub("%%.+ ", ""))
+end
+local format_looted = (LOOT_ITEM_SELF:gsub("%%.*", "")) -- "You receive loot: "
+local format_pushed = (LOOT_ITEM_PUSHED_SELF:gsub("%%.*", "")) -- "You receive item: "
+local format_currency_single = (CURRENCY_GAINED:gsub("%%s", "(.+)")) -- "You receive currency: (.+)."
+local format_currency_multiple = (CURRENCY_GAINED_MULTIPLE:gsub("%%s", "(.+)"):gsub("%%d", "(%%d+)")) -- "You receive currency: (.+) x(%d+)."
+
+--[=====================================================[
+ AddOn:UpdatePlayer()
+ Updates important information about the player we
+ need in order to correctly show combat text events.
+--]=====================================================]
+function x:UpdatePlayer()
+ -- Set the Player's Current Playing Unit
+ if UnitHasVehicleUI("player") then
+ x.player.unit = "vehicle"
+ else
+ x.player.unit = "player"
+ end
+
+ CombatTextSetActiveUnit(x.player.unit)
+
+ -- Set Player's Information
+ local activeTalentGroup = GetActiveSpecGroup(false, false)
+ x.player.spec = GetSpecialization(false, false, activeTalentGroup)
+end
+
+--[=====================================================[
+ Message Formatters
+ TODO remove or sync them mit dem Spam Merger!
+--]=====================================================]
+local xCTFormat = {}
+
+function xCTFormat:SPELL_HEAL(outputFrame, spellId, amount, overhealing, critical, args, frameSettings)
+ local outputColor, message = "healingOut"
+
+ -- Format Criticals and also abbreviate values
+ if critical then
+ outputColor = "healingOutCritical"
+ message = x:Options_Global_FormatCritical(x:Abbreviate(amount, "critical"))
+ else
+ message = x:Abbreviate(amount, outputFrame)
+ end
+
+ -- Show and Format Overhealing values
+ if overhealing > 0 and x:Options_OutgoingHealing_FormatOverhealing() then
+ overhealing = x:Abbreviate(overhealing, outputFrame)
+ message = message .. x:Options_OutgoingHealing_FormatOverhealingAmount(overhealing)
+ end
+
+ -- Add names
+ message = message .. x:formatName(args, frameSettings.names)
+
+ -- Add Icons
+ message = x:GetSpellTextureFormatted(spellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, outputColor)
+end
+
+function xCTFormat:SPELL_PERIODIC_HEAL(outputFrame, spellId, amount, overhealing, critical, args, frameSettings)
+ local outputColor, message = "healingOutPeriodic"
+
+ -- Format Criticals and also abbreviate values
+ if critical then
+ message = x:Options_Global_FormatCritical(x:Abbreviate(amount, "critical"))
+ else
+ message = x:Abbreviate(amount, outputFrame)
+ end
+
+ -- Show and Format Overhealing values
+ if overhealing > 0 and x:Options_OutgoingHealing_FormatOverhealing() then
+ overhealing = x:Abbreviate(overhealing, outputFrame)
+ message = message .. x:Options_OutgoingHealing_FormatOverhealingAmount(overhealing)
+ end
+
+ -- Add names
+ message = message .. x:formatName(args, frameSettings.names)
+
+ -- Add Icons
+ message = x:GetSpellTextureFormatted(spellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, outputColor)
+end
+
+--[=====================================================[
+ Capitalize Locales
+--]=====================================================]
+local unsupportedLocales = { zhCN = true, koKR = true, zhTW = true }
+
+local XCT_STOLE
+local XCT_KILLED
+local XCT_DISPELLED
+
+if unsupportedLocales[x.locale] then
+ XCT_STOLE = ACTION_SPELL_STOLEN
+ XCT_KILLED = ACTION_PARTY_KILL
+ XCT_DISPELLED = ACTION_SPELL_DISPEL
+else
+ XCT_STOLE = utf8_fc_upper(ACTION_SPELL_STOLEN)
+ XCT_KILLED = utf8_fc_upper(ACTION_PARTY_KILL)
+ XCT_DISPELLED = utf8_fc_upper(ACTION_SPELL_DISPEL)
+end
+
+--[=====================================================[
+ Delayed message for loot, to get the correct "totals" of the item in your bag
+--]=====================================================]
+function x:AddLootMessageDelayed(item)
+ local totalCount = C_Item.GetItemCount(item.id)
+
+ local message = item.message
+ if totalCount > 1 then
+ message = message .. string.format(" |cffFFFF00(%s)|r", totalCount)
+ end
+
+ x:AddMessage("loot", message, { item.r, item.g, item.b })
+end
+
+-- =====================================================
+-- Format Name Things
+-- =====================================================
+
+-- Changes a color table into a hex string
+
+local function hexNameColor(t)
+ if type(t) == "string" then
+ return "ff" .. t
+ elseif not t then
+ return "ffFFFFFF"
+ elseif t.colorStr then -- Support Blizzard's raid colors
+ return t.colorStr
+ end
+ return string.format(
+ "ff%2X%2X%2X",
+ math.floor(t[1] * 255 + 0.5),
+ math.floor(t[2] * 255 + 0.5),
+ math.floor(t[3] * 255 + 0.5)
+ )
+end
+
+-- Checks the options you provide and outputs the correctly formatted name
+local function formatNameHelper(name, color)
+ if color then
+ return "|c" .. hexNameColor(color) .. name .. "|r"
+ end
+
+ return "|cffFFFFFF" .. name .. "|r"
+end
+
+-- Format Handlers for name
+local nameFormatter = {}
+nameFormatter.controllerName = function(args, settings, useSource)
+ -- [1] = Source/Destination Name
+ local name = args.controllerName
+ or useSource and args.sourceName
+ or args.destName
+
+ if not name then
+ return ""
+ end
+
+ if settings.removeRealmName then
+ name = string.match(name, "(.*)-.*") or name
+ end
+
+ local color
+ if settings.enableNameColor then
+ if settings.enableCustomNameColor then
+ color = settings.customNameColor
+ elseif args.prefix == "ENVIRONMENTAL" then
+ -- todo spam merger
+ color = x.spellColors[args.school or args.spellSchool or 1]
+ else
+ -- todo guid in the spam merger
+ local guid = useSource and args.sourceGUID or args.destGUID
+ if guid and string.match(guid, "^Player") then
+ local _, class = GetPlayerInfoByGUID(guid)
+ color = RAID_CLASS_COLORS[class or 0]
+ end
+ end
+ end
+
+ return formatNameHelper(name, color)
+end
+
+nameFormatter.spellName = function(args, settings, useSource)
+ -- [2] = Spell Name
+ if not args.spellName then
+ return ""
+ end
+
+ local color
+ if settings.enableNameColor then
+ if settings.enableCustomNameColor then
+ color = settings.customNameColor
+ else
+ -- NOTE: I don't think we want the spell school of the spell
+ -- being cast. We want the spell school of the damage
+ -- being done. That said, if you want to change it so
+ -- that the spell name matches the type of spell it
+ -- is, and not the type of damage it does, change
+ -- "args.school" to "args.spellSchool".
+ color = x.GetSpellSchoolColor(args.school or args.spellSchool)
+ end
+ end
+
+ return formatNameHelper(args.spellName, color)
+end
+
+nameFormatter.controllerNameSpellName = function(args, settings, useSource)
+ -- [3] = Source/Destination Name - Spell Name
+ if not args.hideCaster then
+ local controllerName = nameFormatter.controllerName(args, settings, useSource)
+ if controllerName and controllerName ~= "" then
+ return controllerName .. " - " .. nameFormatter.spellName(args, settings, useSource)
+ end
+ end
+
+ return nameFormatter.spellName(args, settings, useSource)
+end
+
+nameFormatter.spellNameControllerName = function(args, settings, useSource)
+ -- [4] = Spell Name - Source/Destination Name
+ if not args.hideCaster then
+ local controllerName = nameFormatter.controllerName(args, settings, useSource)
+ if controllerName and controllerName ~= "" then
+ return nameFormatter.spellName(args, settings, useSource) .. " - " .. controllerName
+ end
+ end
+
+ return nameFormatter.spellName(args, settings, useSource)
+end
+
+-- Check to see if the name needs for be formatted, if so, handle all the logistics
+function x:formatName(args, frameNameSettings, useSource)
+ -- "PLAYER", "ENVIRONMENT", "NPC", ...
+ local controller = args.controller
+ or useSource and args:GetSourceController()
+ or args:GetDestinationController()
+
+ local eventType = frameNameSettings[controller]
+
+ -- If we have a valid event type that we can handle
+ if eventType and eventType.nameType > 0 then
+ local message
+
+ if eventType.nameType == 1 then
+ message = nameFormatter.controllerName(args, eventType, useSource)
+ elseif eventType.nameType == 2 then
+ message = nameFormatter.spellName(args, eventType, useSource)
+ elseif eventType.nameType == 3 then
+ message = nameFormatter.controllerNameSpellName(args, eventType, useSource)
+ elseif eventType.nameType == 4 then
+ message = nameFormatter.spellNameControllerName(args, eventType, useSource)
+ else
+ return ""
+ end
+
+ if message and message ~= "" then
+ return frameNameSettings.namePrefix .. message .. frameNameSettings.namePostfix
+ end
+ end
+
+ return ""
+end
+
+-- =====================================================
+-- Quick Partial Name Formatter
+-- =====================================================
+
+local missTypeColorLookup = {
+ ["MISS"] = "missTypeMiss",
+ ["DODGE"] = "missTypeDodge",
+ ["PARRY"] = "missTypeParry",
+ ["EVADE"] = "missTypeEvade",
+ ["IMMUNE"] = "missTypeImmune",
+ ["DEFLECT"] = "missTypeDeflect",
+ ["REFLECT"] = "missTypeReflect",
+}
+
+local PARTIAL_MISS_FORMATTERS = {
+ ["absorbed"] = " |c%s" .. (TEXT_MODE_A_STRING_RESULT_ABSORB:gsub("%%d", "%%s")) .. "|r", -- |c%s(%s Absorbed)|r
+ ["blocked"] = " |c%s" .. (TEXT_MODE_A_STRING_RESULT_BLOCK:gsub("%%d", "%%s")) .. "|r", -- |c%s(%s Blocked)|r
+ ["resisted"] = " |c%s" .. (TEXT_MODE_A_STRING_RESULT_RESIST:gsub("%%d", "%%s")) .. "|r", -- |c%s(%s Resisted)|r
+}
+
+local PARTIAL_MISS_COLORS = {
+ ["absorbed"] = "missTypeAbsorbPartial",
+ ["blocked"] = "missTypeBlockPartial",
+ ["resisted"] = "missTypeResistPartial",
+}
+
+local FULL_MISS_COLORS = {
+ ["absorbed"] = "missTypeAbsorb",
+ ["blocked"] = "missTypeBlock",
+ ["resisted"] = "missTypeResist",
+}
+
+local function GetPartialMiss(args, settings, outgoingFrame)
+ local blocked, absorbed, resisted = args.blocked or 0, args.absorbed or 0, args.resisted or 0
+ if blocked > 0 or absorbed > 0 or resisted > 0 then
+ -- Show only the highest partial miss
+ if settings.showHighestPartialMiss then
+ local maxType, color
+ if blocked > absorbed then
+ if blocked > resisted then
+ maxType = "blocked"
+ else
+ maxType = "resisted"
+ end
+ else
+ if absorbed > resisted then
+ maxType = "absorbed"
+ else
+ maxType = "resisted"
+ end
+ end
+
+ color = hexNameColor(
+ x:LookupColorByName(args.amount > 0 and PARTIAL_MISS_COLORS[maxType] or FULL_MISS_COLORS[maxType])
+ )
+ return true,
+ string.format(PARTIAL_MISS_FORMATTERS[maxType], color, x:Abbreviate(args[maxType], outgoingFrame))
+ end
+
+ -- Show All the partial misses that exsist
+ local message, color = ""
+ if absorbed > 0 then
+ color = hexNameColor(
+ x:LookupColorByName(args.amount > 0 and PARTIAL_MISS_COLORS.absorbed or FULL_MISS_COLORS.absorbed)
+ )
+ message = message
+ .. string.format(PARTIAL_MISS_FORMATTERS.absorbed, color, x:Abbreviate(absorbed, outgoingFrame))
+ end
+
+ if blocked > 0 then
+ color = hexNameColor(
+ x:LookupColorByName(args.amount > 0 and PARTIAL_MISS_COLORS.blocked or FULL_MISS_COLORS.blocked)
+ )
+ message = message
+ .. string.format(PARTIAL_MISS_FORMATTERS.blocked, color, x:Abbreviate(blocked, outgoingFrame))
+ end
+
+ if resisted > 0 then
+ color = hexNameColor(
+ x:LookupColorByName(args.amount > 0 and PARTIAL_MISS_COLORS.resisted or FULL_MISS_COLORS.resisted)
+ )
+ message = message
+ .. string.format(PARTIAL_MISS_FORMATTERS.resisted, color, x:Abbreviate(resisted, outgoingFrame))
+ end
+
+ return true, message
+ else
+ return false, ""
+ end
+end
+
+-- =====================================================
+-- The New Combat Handlers
+-- =====================================================
+local EventHandlers = {}
+
+-- Outgoing healing
+EventHandlers.OutgoingHealing = function(args)
+ local isHoT = args.prefix == "SPELL_PERIODIC"
+
+ -- Keep track of spells that go by
+ if x:Options_Filter_TrackSpells() then
+ x.spellCache.spells[args.spellId] = true
+ end
+
+ -- Check to see if this is a HoT
+ if isHoT and not x:Options_OutgoingHealing_ShowHots() then
+ return
+ end
+
+ -- Filter Outgoing Healing Spell
+ if x:Options_Filter_HideSpell(args.spellId) then
+ return
+ end
+
+ local amount, amountOverhealing = args.amount, args.overhealing
+
+ -- Filter Overhealing
+ if x:Options_IncomingHealing_ShowOverHealing() then
+ if x:Options_OutgoingHealing_SubtractOverhealing() then
+ amount = amount - amountOverhealing
+ end
+ else
+ amount = amount - amountOverhealing
+ amountOverhealing = 0
+ end
+
+ if not x:Options_OutgoingHealing_HideAbsorbedHealing() and args.absorbed and args.absorbed > 0 then
+ amount = amount + args.absorbed
+ end
+
+ if amount <= 0 then
+ return
+ end
+
+ -- Figure out which frame and color to output
+ local outputFrame, outputColor = "outgoing_healing", "healingOut"
+ if args.critical then
+ outputFrame = "critical"
+ outputColor = "healingOutCritical"
+ end
+
+ -- HoTs only have one color
+ if isHoT then
+ outputColor = "healingOutPeriodic"
+ end
+
+ -- Condensed Critical Merge
+ local spamMergerInterval = x:Options_SpamMerger_SpellInterval(args.spellId)
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ if args.critical then
+ if x:Options_SpamMerger_MergeCriticalsByThemselves() then
+ x:AddSpamMessage(
+ outputFrame,
+ args.spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ outputFormat = "+%s",
+ critical = args.critical
+ }
+ )
+ return
+ elseif x:Options_SpamMerger_MergeCriticalsWithOutgoing() then
+ x:AddSpamMessage(
+ "outgoing_healing",
+ args.spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ outputFormat = "+%s",
+ critical = args.critical
+ }
+ )
+ elseif x:Options_SpamMerger_HideMergedCriticals() then
+ x:AddSpamMessage(
+ "outgoing_healing",
+ args.spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ outputFormat = "+%s",
+ critical = args.critical
+ }
+ )
+ return
+ end
+ else
+ x:AddSpamMessage(
+ outputFrame,
+ args.spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ outputFormat = "+%s",
+ critical = args.critical
+ }
+ )
+ return
+ end
+ end
+
+ if x:Options_Filter_OutgoingHealing_HideEvent(amount, args.critical) then
+ return
+ end
+
+ -- Get the settings for the correct output frame
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Format the message correctly
+ if args.event == "SPELL_PERIODIC_HEAL" then
+ xCTFormat:SPELL_PERIODIC_HEAL(
+ outputFrame,
+ args.spellId,
+ amount,
+ amountOverhealing,
+ args.critical,
+ args,
+ frameSettings
+ )
+ elseif args.event == "SPELL_HEAL" then
+ xCTFormat:SPELL_HEAL(outputFrame, args.spellId, amount, amountOverhealing, args.critical, args, frameSettings)
+ else
+ x:Print("Please report: unhandled _HEAL event", args.event)
+ end
+end
+
+-- Outgoing damage
+EventHandlers.OutgoingDamage = function(args)
+ local spellId, isEnvironmental, isSwing, isAutoShot, isDoT =
+ args.spellId,
+ args.prefix == "ENVIRONMENTAL",
+ args.prefix == "SWING",
+ args.spellId == 75,
+ args.prefix == "SPELL_PERIODIC"
+
+ -- Keep track of spells that go by (Don't track Swings or Environmental damage)
+ if not isEnvironmental and not isSwing and x:Options_Filter_TrackSpells() then
+ x.spellCache.spells[spellId] = true
+ end
+
+ if not x:Options_Outgoing_ShowDamage() then
+ return
+ end
+
+ -- Check to see if this is a HoT
+ if isDoT and not x:Options_Outgoing_ShowDots() then
+ return
+ end
+
+ -- Filter Outgoing Damage Spell
+ if x:Options_Filter_HideSpell(spellId) then
+ return
+ end
+
+ if isSwing and not args:IsSourceMyPet() and not args:IsSourceMyVehicle() then
+ if args.critical and not x:Options_Outgoing_ShowAutoAttack() then
+ return
+ end
+
+ if not args.critical and not x:Options_Critical_ShowAutoAttack() then
+ return
+ end
+ end
+
+ local amount = args.amount or 0
+ if x:Options_Outgoing_ShowAbsorbedDamageAsNormalDamage() then
+ -- Its a partial absorb, add it to the amount
+ amount = amount + (args.absorbed or 0)
+ end
+
+ if amount <= 0 then
+ return
+ end
+
+ local outputFrame = "outgoing"
+
+ -- Check to see if my pet is doing things
+ if args:IsSourceMyPet() and (not x:Options_Outgoing_ShowKillCommandAsPlayerDamage() or spellId ~= 34026) then
+ if not x:Options_Outgoing_ShowPetDamage() then
+ return
+ end
+
+ if isSwing and not x:Options_Outgoing_ShowPetAutoAttack() then
+ return
+ end
+
+ local spamMergerInterval = x:Options_SpamMerger_PetAttackInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ local icon = x:GetPetTexture() or ""
+ x:AddSpamMessage(
+ outputFrame,
+ icon, -- use the pet icon as spell ID so that EVERYTHING from it will be merged together
+ amount,
+ x.db.profile.spells.mergePetColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ -- We switch to "auto attack" so that each spell of the pet is merged together and displayed as "auto attack"
+ spellName = spellId == 34026 and L_KILLCOMMAND or L_AUTOATTACK,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+
+ if not x:Options_Critical_ShowPetCrits() then
+ args.critical = nil -- stupid spam fix for hunter pets
+ end
+
+ if isSwing then
+ spellId = 0 -- this will get fixed later
+ end
+ end
+
+ if args:IsSourceMyVehicle() then
+ if not x:Options_Outgoing_ShowVehicleDamage() then
+ return
+ end
+ if isSwing and not x:Options_Outgoing_ShowPetAutoAttack() then
+ return
+ end -- for BM's second pet, Hati
+ if not x:Options_Critical_ShowPetCrits() then
+ args.critical = nil -- stupid spam fix for hunter pets
+ end
+ if isSwing then
+ spellId = 0 -- this will get fixed later
+ end
+ end
+
+ -- Check for Critical Swings
+ if args.critical then
+ if (isSwing or isAutoShot) and x:Options_Critical_ShowAutoAttack() then
+ outputFrame = "critical"
+ elseif not isSwing and not isAutoShot then
+ outputFrame = "critical"
+ end
+ end
+
+ -- Lookup the color
+ local outputColorType
+ if isSwing or isAutoShot then
+ outputColorType = args.critical and "meleeCrit" or "melee"
+ end
+
+ local outputColor = x.GetSpellSchoolColor(args.spellSchool, outputColorType)
+
+ local spamMergerInterval = x:Options_SpamMerger_SpellInterval(spellId)
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ if isSwing or isAutoShot then
+ if outputFrame == "critical" then
+ if x:Options_SpamMerger_MergeCriticalsByThemselves() then
+ x:AddSpamMessage(
+ outputFrame,
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = L_AUTOATTACK,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ elseif x:Options_SpamMerger_MergeCriticalsWithOutgoing() then
+ x:AddSpamMessage(
+ "outgoing",
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = L_AUTOATTACK,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ elseif x:Options_SpamMerger_HideMergedCriticals() then
+ x:AddSpamMessage(
+ "outgoing",
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = L_AUTOATTACK,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+ else
+ x:AddSpamMessage(
+ outputFrame,
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = L_AUTOATTACK,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+ elseif not isSwing and not isAutoShot then
+ if args.critical then
+ if x:Options_SpamMerger_MergeCriticalsByThemselves() then
+ x:AddSpamMessage(
+ outputFrame,
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ elseif x:Options_SpamMerger_MergeCriticalsWithOutgoing() then
+ x:AddSpamMessage(
+ "outgoing",
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ elseif x:Options_SpamMerger_HideMergedCriticals() then
+ x:AddSpamMessage(
+ "outgoing",
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+ else
+ x:AddSpamMessage(
+ outputFrame,
+ spellId,
+ amount,
+ outputColor,
+ spamMergerInterval,
+ {
+ useSource = false,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetDestinationController(),
+ controllerName = args.destName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+ end
+ end
+
+ if x:Options_Filter_OutgoingDamage_HideEvent(amount, args.critical) then
+ -- Amount is not high enough
+ return
+ end
+
+ local message
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ if args.critical and (not (isSwing or isAutoShot) or x:Options_Critical_ShowAutoAttack()) then
+ if not (isSwing or isAutoShot) or x:Options_Critical_PrefixAutoAttack() then
+ message = x:Options_Global_FormatCritical(x:Abbreviate(amount, outputFrame))
+ else
+ message = x:Abbreviate(amount, outputFrame)
+ end
+ else
+ message = x:Abbreviate(amount, outputFrame)
+ end
+
+ -- Add the Partial Miss Types
+ if x:Options_Outgoing_ShowPartialMisses() then
+ local hasPartialMiss, formattedMessage = GetPartialMiss(args, frameSettings, outputFrame)
+
+ if hasPartialMiss then
+ message = message .. formattedMessage
+ end
+ end
+
+ -- Add names
+ message = message .. x:formatName(args, frameSettings.names)
+
+ -- Add Icons (Hide Auto Attack icons)
+ if args.prefix ~= "SWING" or x:ShowAutoAttackIcons(outputFrame) then
+ message = x:GetSpellTextureFormatted(args.spellId, message, frameSettings)
+ else
+ message = x:GetSpellTextureFormatted(nil, message, frameSettings)
+ end
+
+ x:AddMessage(outputFrame, message, outputColor)
+end
+
+-- Incoming damage
+EventHandlers.IncomingDamage = function(args)
+ -- Keep track of spells that go by
+ if args.spellId and x:Options_Filter_TrackSpells() then
+ x.spellCache.damage[args.spellId] = true
+ end
+
+ if args.spellId and x:Options_Filter_HideIncomingDamage(args.spellId) then
+ return
+ end
+
+ local outputFrame = "damage"
+ local amount = args.amount
+
+ local colorOverride
+ if args.spellSchool == 1 then
+ colorOverride = args.critical and "damageTakenCritical" or "damageTaken"
+ else
+ colorOverride = args.critical and "spellDamageTakenCritical" or "spellDamageTaken"
+ end
+
+ local spamMergerInterval = x:Options_SpamMerger_IncomingDamageInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ if amount <= 0 then
+ return
+ end
+
+ x:AddSpamMessage(
+ outputFrame,
+ args.spellId,
+ -amount,
+ colorOverride,
+ spamMergerInterval,
+ {
+ useSource = true,
+ spellName = args.spellName,
+ spellSchool = args.spellSchool,
+ controller = args:GetSourceController(),
+ controllerName = args.sourceName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+
+ local message
+ -- Check for partial resists / absorbs / ...
+ if x:Options_IncomingDamage_ShowReductions() then
+ local resistedAmount = (args.resisted or 0) + (args.blocked or 0) + (args.absorbed or 0)
+ if resistedAmount > 0 then
+ local resistType, color
+
+ if (args.resisted or 0) > (args.blocked or 0) and (args.resisted or 0) > (args.absorbed or 0) then
+ resistType = RESIST
+ color = amount > 0 and "missTypeResist" or "missTypeResistPartial"
+ elseif (args.blocked or 0) > (args.resisted or 0) and (args.blocked or 0) > (args.absorbed or 0) then
+ resistType = BLOCK
+ color = amount > 0 and "missTypeBlock" or "missTypeBlockPartial"
+ elseif (args.absorbed or 0) > (args.resisted or 0) and (args.absorbed or 0) > (args.blocked or 0) then
+ resistType = ABSORB
+ color = amount > 0 and "missTypeAbsorb" or "missTypeAbsorbPartial"
+ end
+
+ amount = amount + resistedAmount
+ color = hexNameColor(x:LookupColorByName(color))
+ message = string.format(
+ "-%s |c%s(%s %s)|r",
+ x:Abbreviate(amount, outputFrame),
+ color,
+ resistType,
+ x:Abbreviate(resistedAmount, outputFrame)
+ )
+ end
+ end
+
+ if x:Options_Filter_IncomingDamage_HideEvent(amount, args.critical) then
+ return
+ end
+
+ -- If this is not a resist, then lets format it as normal
+ if not message then
+ -- Format Criticals and also abbreviate values
+ if args.critical then
+ message = x:Options_Global_FormatCritical(x:Abbreviate(-amount, "critical"))
+ else
+ message = x:Abbreviate(-amount, outputFrame)
+ end
+ end
+
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add names
+ message = message .. x:formatName(args, frameSettings.names, true)
+
+ -- Add Icons (Hide Auto Attack icons)
+ if args.prefix ~= "SWING" or x:ShowAutoAttackIcons(outputFrame) then
+ message = x:GetSpellTextureFormatted(args.spellId, message, frameSettings)
+ else
+ message = x:GetSpellTextureFormatted(nil, message, frameSettings)
+ end
+
+ -- Output message
+ x:AddMessage(outputFrame, message, x.GetSpellSchoolColor(args.spellSchool, colorOverride))
+end
+
+EventHandlers.IncomingHealing = function(args)
+ if x:Options_Filter_TrackSpells() then
+ x.spellCache.healing[args.spellId] = true
+ end
+
+ if x:Options_Filter_HideIncomingHealing(args.spellId) then
+ return
+ end
+
+ local amount, isHoT, outputFrame = args.amount, args.prefix == "SPELL_PERIODIC", "healing"
+ local color = isHoT and "healingTakenPeriodic" or args.critical and "healingTakenCritical" or "healingTaken"
+
+ -- Adjust the amount if the user doesnt want over healing
+ if not x:Options_IncomingHealing_ShowOverHealing() then
+ amount = amount - args.overhealing
+ end
+
+ if not x:Options_IncomingHealing_HideAbsorbedHealing() and args.absorbed and args.absorbed > 0 then
+ amount = amount + args.absorbed
+ end
+
+ if amount <= 0 then
+ return
+ end
+
+ if x:Options_IncomingHealing_ShowOnlyMyHeals() and not args.isPlayer then
+ if x:Options_IncomingHealing_ShowOnlyMyPetsHeals() and args:IsSourceMyPet() then
+ -- If its the pet, then continue
+ else
+ return
+ end
+ end
+
+ local message = string.format("+%s", x:Abbreviate(amount, outputFrame))
+
+ local spamMergerInterval = x:Options_SpamMerger_IncomingHealingInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ x:AddSpamMessage(
+ outputFrame,
+ args.sourceName or "Unknown Source",
+ amount,
+ "healingTaken",
+ spamMergerInterval,
+ {
+ useSource = true,
+ sourceGUID = args.sourceGUID,
+ controller = args:GetSourceController(),
+ controllerName = args.sourceName,
+ critical = args.critical
+ }
+ )
+ return
+ end
+
+ if x:Options_Filter_IncomingHealing_HideEvent(amount, args.critical) then
+ return
+ end
+
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add names
+ message = message .. x:formatName(args, frameSettings.names, true)
+
+ -- Add the icon
+ message = x:GetSpellTextureFormatted(args.spellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, color)
+end
+
+EventHandlers.IncomingAura = function(args)
+ -- Some useful information about the event
+ local isBuff, isGaining =
+ args.auraType == "BUFF", args.suffix == "_AURA_APPLIED" or args.suffix == "_AURA_APPLIED_DOSE"
+
+ -- Track the aura
+ if x:Options_Filter_TrackSpells() then
+ x.spellCache[isBuff and "buffs" or "debuffs"][args.spellName] = true
+ end
+
+ if isBuff then
+ -- Stop if we're not showing buffs _or_ the spell's name is filtered
+ if not x:Options_General_ShowBuffGainsAndFades() or x:Options_Filter_HideBuff(args.spellName) then
+ return
+ end
+ else
+ -- Aura is a debuff
+ -- Stop if we're not showing debuffs _or_ the spell's name is filtered
+ if not x:Options_General_ShowDebuffGainsAndFades() or x:Options_Filter_HideDebuff(args.spellName) then
+ return
+ end
+ end
+
+ -- Begin constructing the event message and color
+ local message, color
+ if isGaining then
+ message = string.format("+%s", args.spellName)
+ color = isBuff and "buffsGained" or "debuffsGained"
+ else
+ message = string.format("-%s", args.spellName)
+ color = isBuff and "buffsFaded" or "debuffsFaded"
+ end
+
+ local outputFrame = "general"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ x:AddMessage(outputFrame, x:GetSpellTextureFormatted(args.spellId, message, frameSettings), color)
+end
+
+EventHandlers.KilledUnit = function(args)
+ if not x:Options_General_ShowPartyKill() then
+ return
+ end
+
+ local color = "killingBlow"
+ if args.destGUID then
+ local class = select(2, GetPlayerInfoByGUID(args.destGUID))
+ if RAID_CLASS_COLORS[class] then
+ color = RAID_CLASS_COLORS[class]
+ end
+ end
+
+ x:AddMessage("general", string.format("%s: %s", XCT_KILLED, args.destName), color)
+end
+
+EventHandlers.InterruptedUnit = function(args)
+ if not x:Options_General_ShowInterrupts() then
+ return
+ end
+
+ -- Create and format the message
+ local message = string.format("%s: %s", INTERRUPTED, args.extraSpellName)
+
+ local outputFrame = "general"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add the icon
+ message = x:GetSpellTextureFormatted(args.extraSpellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, "interrupts")
+end
+
+EventHandlers.OutgoingMiss = function(args)
+ local spellId = args.spellId
+
+ -- If this is a melee swing, it could also be our pets
+ if args.prefix == "SWING" then
+ if not x:Options_Outgoing_ShowAutoAttack() then
+ return
+ end
+
+ if args:IsSourceMyPet() then
+ spellId = PET_ATTACK_TEXTURE
+ else
+ spellId = 6603
+ end
+ end
+
+ -- Absorbs are handled in the x.onCombatLogEvent() function
+ -- Check for filtered immunes
+ if args.missType == "IMMUNE" and not x:Options_Outgoing_ShowImmunes() then
+ return
+ end
+
+ if args.missType ~= "IMMUNE" and not x:Options_Outgoing_ShowMisses() then
+ return
+ end
+
+ -- Check if spell is filtered
+ if x:Options_Filter_HideSpell(spellId) then
+ return
+ end
+
+ local outputFrame = "outgoing"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ local message = _G["COMBAT_TEXT_" .. args.missType]
+
+ local spamMergerInterval = x:Options_SpamMerger_OutgoingDamageMissesInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ x:AddSpamMessage(outputFrame, args.missType, message, "misstypesOut", spamMergerInterval)
+ return
+ end
+
+ x:AddMessage(outputFrame, x:GetSpellTextureFormatted(spellId, message, frameSettings), "misstypesOut")
+end
+
+EventHandlers.IncomingMiss = function(args)
+ if not x:Options_IncomingDamage_ShowMissTypes() then
+ return
+ end
+
+ -- Check if incoming spell is filtered
+ if x:Options_Filter_HideIncomingDamage(args.spellId) then
+ return
+ end
+
+ local message = _G["COMBAT_TEXT_" .. args.missType]
+ local color = missTypeColorLookup[args.missType] or "misstypesOut"
+ local outputFrame = "damage"
+
+ local spamMergerInterval = x:Options_SpamMerger_IncomingMissesInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ x:AddSpamMessage(outputFrame, args.missType, message, color, spamMergerInterval)
+ return
+ end
+
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add Icons
+ message = x:GetSpellTextureFormatted(args.spellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, color)
+end
+
+-- Outgoing dispel
+EventHandlers.SpellDispel = function(args)
+ if not x:Options_General_ShowDispells() then
+ return
+ end
+
+ local outputFrame = "general"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ local color = args.auraType == "BUFF" and "dispellBuffs" or "dispellDebuffs"
+ local message = x:GetSpellTextureFormatted(args.extraSpellId, args.extraSpellName, frameSettings)
+ local sourceName = args.sourceName
+ if args.isPlayer then
+ sourceName = "You"
+ end
+
+ message = sourceName .. " dispelled " .. message
+
+ local spamMergerInterval = x:Options_SpamMerger_DispellInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ x:AddSpamMessage(outputFrame, args.extraSpellName, message, color, spamMergerInterval)
+ return
+ end
+
+ local destName = args.destName
+ if args.atPlayer then
+ destName = "you"
+ end
+ x:AddMessage(outputFrame, message .. " on " .. destName, color)
+end
+
+EventHandlers.IncomingSpellDispel = function (args)
+ if not x:Options_General_ShowIncomingDispells() then
+ return
+ end
+
+ if args.isPlayer and args.atPlayer and x:Options_General_ShowDispells() then
+ -- Its a self-dispel and outgoing dispells are enabled too. Skip here so we dont display the same message twice.
+ return
+ end
+
+ local outputFrame = "general"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ local message = x:GetSpellTextureFormatted(args.extraSpellId, args.extraSpellName, frameSettings)
+ local sourceName = args.sourceName
+ if args.isPlayer then
+ sourceName = "You"
+ end
+
+ local destName = args.destName
+ if args.atPlayer then
+ destName = "you"
+ end
+
+ message = sourceName .. " dispelled " .. message .. " on " .. destName
+
+ x:AddMessage(outputFrame, message, "dispellDebuffs")
+end
+
+EventHandlers.SpellStolen = function(args)
+ if not x:Options_General_ShowDispells() then
+ return
+ end
+
+ local message = string.format("%s: %s", XCT_STOLE, args.extraSpellName)
+ local outputFrame = "general"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add Icons
+ message = x:GetSpellTextureFormatted(args.extraSpellId, message, frameSettings)
+
+ x:AddMessage(outputFrame, message, "dispellStolen")
+end
+
+EventHandlers.SpellEnergize = function(args)
+ if not x:Options_Power_ShowGains() then
+ return
+ end
+
+ if math.abs(tonumber(args.amount)) <= tonumber(x:Options_Filter_PlayerPowerMinimumThreshold()) then
+ return
+ end
+
+ local energy_type = x.POWER_LOOKUP[args.powerType]
+ if not energy_type then
+ x:Print("Unknown SPELL_ENERGIZE power type: " .. args.powerType)
+ return
+ end
+
+ if x:Options_Power_ShowResource(energy_type) then
+ return
+ end
+
+ local message = x:Abbreviate(args.amount, "power")
+ local color = x:LookupColorByName("color_" .. energy_type) or { 1, 1, 1 }
+
+ if energy_type == "RUNES" then
+ -- Something procced and a DK rune has gone off cooldown
+ -- Use the corresponding function for it, but we dont know which rune came off CD
+ if x:Options_Power_ShowResource("RUNES") then
+ EventHandlers.RUNE_POWER_UPDATE(nil, 0)
+ end
+ else
+ x:AddMessage(
+ "power",
+ string.format("+%s %s", message, x:Options_Power_ShowEnergyTypes() and _G[energy_type] or ""),
+ color
+ )
+ end
+end
+
+EventHandlers.UNIT_HEALTH = function()
+ if UnitHealth(x.player.unit) / UnitHealthMax(x.player.unit) <= _G.COMBAT_TEXT_LOW_HEALTH_THRESHOLD then
+ if not x.lowHealth then
+ x:AddMessage("general", _G.HEALTH_LOW, "lowResourcesHealth")
+ x.lowHealth = true
+ end
+ else
+ x.lowHealth = false
+ end
+end
+
+EventHandlers.UNIT_POWER_UPDATE = function(_, unit, powerType)
+ if UnitPower(x.player.unit) / UnitPowerMax(x.player.unit) <= _G.COMBAT_TEXT_LOW_MANA_THRESHOLD then
+ if not x.lowMana then
+ x:AddMessage("general", MANA_LOW, "lowResourcesMana")
+ x.lowMana = true
+ end
+ else
+ x.lowMana = false
+ end
+end
+
+EventHandlers.PLAYER_REGEN_DISABLED = function()
+ for framename, settings in pairs(x.db.profile.frames) do
+ if settings.enableScrollable and settings.scrollableInCombat then
+ x:DisableFrameScrolling(framename)
+ end
+ end
+
+ if x:Options_General_ShowCombatState() then
+ x:AddMessage("general", _G.ENTERING_COMBAT, "combatEntering")
+ end
+end
+
+EventHandlers.PLAYER_REGEN_ENABLED = function()
+ for framename, settings in pairs(x.db.profile.frames) do
+ if settings.enableScrollable and settings.scrollableInCombat then
+ x:EnableFrameScrolling(framename)
+ end
+ end
+
+ if x:Options_Global_ClearWhenLeavingCombat() then
+ -- only clear frames with icons
+ x:Clear("general")
+ x:Clear("outgoing")
+ x:Clear("critical")
+ x:Clear("outgoing_healing")
+ x:Clear("damage")
+ x:Clear("healing")
+ x:Clear("power")
+ x:Clear("procs")
+ x:Clear("loot")
+ end
+
+ if x:Options_General_ShowCombatState() then
+ x:AddMessage("general", _G.LEAVING_COMBAT, "combatLeaving")
+ end
+end
+
+EventHandlers.UNIT_ENTERED_VEHICLE = function(_, unit)
+ if unit == "player" then
+ x:UpdatePlayer()
+ end
+end
+
+EventHandlers.UNIT_EXITING_VEHICLE = function(_, unit)
+ if unit == "player" then
+ x:UpdatePlayer()
+ end
+end
+
+EventHandlers.PLAYER_ENTERING_WORLD = function()
+ x:UpdatePlayer()
+ x:Clear()
+
+ -- Lazy Coding (Clear up messy libraries... yuck!)
+ collectgarbage()
+end
+
+EventHandlers.UNIT_PET = function()
+ x:UpdatePlayer()
+end
+
+EventHandlers.CHAT_MSG_SKILL = function(_, msg)
+ -- TODO must be localized!
+ local profession, newSkillLevel = msg:match("Your skill in (.+) has increased to (%d+).")
+ if not profession or not newSkillLevel then
+ return
+ end
+
+ x:AddMessage(
+ "general",
+ string.format("%s increased to %s", profession, newSkillLevel),
+ { 0, 0.44, 0.87 }
+ )
+end
+
+EventHandlers.CHAT_MSG_LOOT = function(_, msg)
+ --x:Print(string.gsub(msg, "\124", "\124\124"))
+
+ local preMessage, itemString, itemName, amount = string.match(
+ msg,
+ "(.+)%|c.+%|H(.+)|h%[(.+)%]%|h%|rx?(.*)"
+ )
+
+ if x.locale == "koKR" and (not preMessage or preMessage == "") then
+ -- TODO amount ?
+ itemString, itemName, preMessage = string.match(msg, "%|c.+%|H(.+)%|h%[(.+)%]%|h%|r.+ (.+)")
+ end
+
+ if not itemString or itemString == "" then
+ return
+ end
+
+ -- Decode item string: (linkQuality for pets only)
+ local linkType, linkID, _, linkQuality = strsplit(":", itemString)
+
+ -- TODO: Clean up this debug scratch stuff
+ --"([^|]*)|cff(%x*)|H([^:]*):(%d+):%d+:(%d+):[-?%d+:]+|h%[?([^%]]*)%]|h|r?%s?x?(%d*)%.?"
+ -- "|cff0070dd|Hbattlepet:1343:1:3:158:10:12:BattlePet-0-000002C398CB|h[Bonkers]|h|r" - C_PetJournal.GetPetInfoBySpeciesID(1343)
+ -- "|cff9d9d9d|Hbattlepet:467:1:0:140:9:9:BattlePet-0-000002C398C4|h[Dung Beetle]|h|r" - C_PetJournal.GetPetInfoBySpeciesID(467)
+ -- ITEM_QUALITY_COLORS[3]
+
+ -- local format_getItemString = "([^|]+)|cff(%x+)|H([^|]+)|h%[([^%]]+)%]|h|r[^%d]*(%d*)"
+ -- "|cffffffff|Hitem:119299::::::::100:252::::::|h[드레노어 기계공학의 비밀]|h|r을 만들었습니다."
+
+ if x:Options_Filter_TrackSpells() then
+ x.spellCache.items[linkID] = true
+ end
+
+ if x:Options_Filter_HideItem(linkID) then
+ return
+ end
+
+ -- Check to see if this is a battle pet
+ if linkType == "battlepet" then
+ -- TODO: Add pet icons!
+ local speciesName, speciesIconTexture, petType = C_PetJournal.GetPetInfoBySpeciesID(linkID)
+
+ -- [Caged]: Pet Name (Pet Family)
+ local message = string.format(
+ "|cff798BDD[%s]:|r %s (%s)",
+ string.gsub(BATTLE_PET_CAGE_ITEM_NAME, "%s?%%s", ""),
+ speciesName,
+ PET_TYPE_SUFFIX[petType] or ""
+ )
+ local itemQualityColor = ITEM_QUALITY_COLORS[tonumber(linkQuality)]
+ x:Print(linkID, linkQuality, itemQualityColor)
+
+ x:AddMessage("loot", message, { itemQualityColor.r, itemQualityColor.g, itemQualityColor.b })
+ return
+ end
+
+ -- Check to see if this is a item
+ if linkType == "item" then
+ local crafted, looted, pushed =
+ (preMessage == format_crafted), (preMessage == format_looted), (preMessage == format_pushed)
+
+ -- Item Quality, See "GetAuctionItemClasses()" For Type and Subtype, Item Icon Texture Location
+ local itemQuality, _, _, itemType, itemSubtype, _, _, itemTexture = select(3, C_Item.GetItemInfo(linkID))
+
+ -- Item White-List Filter
+ local listed = x.db.profile.spells.items[itemType]
+ and (x.db.profile.spells.items[itemType][itemSubtype] == true)
+
+ -- Fix the Amount of a item looted
+ amount = tonumber(amount) or 1
+
+ -- Only let self looted items go through the "Always Show" filter
+ if
+ (listed and looted)
+ or (x:Options_Loot_ShowItems() and looted and itemQuality >= x:Options_Loot_ItemQualityFilter())
+ or (itemType == "Quest" and x:Options_Loot_ShowQuestItems() and looted)
+ or (crafted and x:Options_Loot_ShowCraftedItems())
+ or (pushed and x:Options_Loot_ShowPurchasedItems())
+ then
+ local itemQualityColor = ITEM_QUALITY_COLORS[itemQuality]
+ -- "%s%s: %s [%s]%s %%s"
+
+ local icon = ""
+ if x:Options_Loot_ShowIcons() then
+ icon = x:FormatIcon(itemTexture, x:Options_Loot_IconSize())
+ end
+
+ local itemQualityText = ""
+ if x:Options_Loot_ShowColorBlindMoney() then
+ -- Item Quality (Color Blind)
+ itemQualityText = string.format("(%s)", _G[string.format("ITEM_QUALITY%s_DESC", itemQuality)])
+ end
+
+ local message
+ if amount > 1 then
+ message = string.format(
+ "%s%s: |cff798BDD+%s|r %s [%s]",
+ x:Options_Loot_ShowItemTypes() and itemType or "Item", -- Item Type
+ itemQualityText,
+ amount,
+ icon,
+ itemName
+ )
+ else
+ message = string.format(
+ "%s%s: %s [%s]",
+ x:Options_Loot_ShowItemTypes() and itemType or "Item", -- Item Type
+ itemQualityText,
+ icon,
+ itemName
+ )
+ end
+
+ if x:Options_Loot_ShowItemTotals() then
+ -- We have to delay the message in order to get correct "totals".
+ x:ScheduleTimer("AddLootMessageDelayed", 0.5, {
+ id = linkID,
+ message = message,
+ r = itemQualityColor.r,
+ g = itemQualityColor.g,
+ b = itemQualityColor.b,
+ })
+ else
+ -- Display the message directly
+ x:AddMessage("loot", message, { itemQualityColor.r, itemQualityColor.g, itemQualityColor.b })
+ end
+ end
+ end
+end
+
+EventHandlers.CHAT_MSG_CURRENCY = function(_, msg)
+ -- get currency from chat
+ local currencyLink, amountGained = msg:match(format_currency_multiple)
+ if not currencyLink then
+ amountGained, currencyLink = 1, msg:match(format_currency_single)
+ if not currencyLink then
+ return
+ end
+ end
+
+ local currencyInfo = C_CurrencyInfo.GetCurrencyInfoFromLink(currencyLink)
+
+ local icon = ""
+ if x:Options_Loot_ShowIcons() then
+ icon = x:FormatIcon(currencyInfo.iconFileID, x:Options_Loot_IconSize())
+ end
+
+ local message
+ if tonumber(amountGained) > 1 then
+ message = string.format("%s: |cff798BDD+%s|r %s %s", _G.CURRENCY, amountGained, icon, currencyInfo.name)
+ else
+ message = string.format("%s: %s %s", _G.CURRENCY, icon, currencyInfo.name)
+ end
+
+ if currencyInfo.quantity > 1 then
+ message = message .. string.format(" |cffFFFF00(%s)|r", currencyInfo.quantity)
+ end
+
+ local qualityColor = ITEM_QUALITY_COLORS[currencyInfo.quality]
+
+ x:AddMessage("loot", message, { qualityColor.r, qualityColor.g, qualityColor.b })
+end
+
+EventHandlers.CHAT_MSG_MONEY = function(_, msg)
+ local g, s, c =
+ tonumber(msg:match(GOLD_AMOUNT:gsub("%%d", "(%%d+)"))),
+ tonumber(msg:match(SILVER_AMOUNT:gsub("%%d", "(%%d+)"))),
+ tonumber(msg:match(COPPER_AMOUNT:gsub("%%d", "(%%d+)")))
+ local money, o = (g and g * 10000 or 0) + (s and s * 100 or 0) + (c or 0), MONEY .. ": "
+
+ -- TODO: Add a filter for a minimum amount of money
+
+ if x:Options_Loot_ShowColorBlindMoney() then
+ o = o .. (g and g .. " G " or "") .. (s and s .. " S " or "") .. (c and c .. " C " or "")
+ else
+ o = o .. C_CurrencyInfo.GetCoinTextureString(money) .. " "
+ end
+
+ -- This only works on english clients :\
+ if msg:find("share") then
+ o = o .. "(split)"
+ end
+
+ x:AddMessage("loot", o, { 1, 1, 0 }) -- yellow
+end
+
+EventHandlers.RUNE_POWER_UPDATE = function(_, runeIndex)
+ if not x.DeathKnightRunes then
+ x.DeathKnightRunes = {}
+ end
+
+ if tonumber(runeIndex) and runeIndex >= 1 and runeIndex <= 6 then
+ -- A Rune has gone on cooldown
+ local _, _, runeReady = GetRuneCooldown(runeIndex)
+ if not runeReady then
+ x.DeathKnightRunes[runeIndex] = true
+ end
+ end
+
+ -- A Rune may have come off cooldown!
+ -- IDK why but runeIndex is really really big (> 32k or even negative)
+ local runeCount = 0
+ for otherRuneIndex, wasOnCd in pairs(x.DeathKnightRunes) do
+ if wasOnCd then
+ local _, _, runeReady = GetRuneCooldown(otherRuneIndex)
+ if runeReady then
+ runeCount = runeCount + 1
+ x.DeathKnightRunes[otherRuneIndex] = false
+ end
+ end
+ end
+
+ if runeCount > 0 then
+ x:AddMessage(
+ "power",
+ string.format("+%s %s", runeCount, _G.RUNES),
+ x:LookupColorByName("color_RUNES") or { 1, 1, 1 }
+ )
+ end
+end
+
+EventHandlers.ACTIVE_TALENT_GROUP_CHANGED = function()
+ x:UpdatePlayer()
+end
+
+-- Handlers for COMBAT_TEXT_UPDATE
+local CombatTextUpdateHandlers = {}
+CombatTextUpdateHandlers.SPELL_ACTIVE = function(spellName)
+ if not spellName then
+ return
+ end
+
+ if x:Options_Filter_TrackSpells() then
+ x.spellCache.procs[spellName] = true
+ end
+
+ if x:Options_Filter_HideProc(spellName) then
+ return
+ end
+
+ local message = spellName
+
+ -- Add Stacks
+ local icon, spellStacks = select(3, UnitAura("player", spellName))
+ if spellStacks and tonumber(spellStacks) > 1 then
+ message = spellName .. " |cffFFFFFFx" .. spellStacks .. "|r"
+ end
+
+ local outputFrame = "procs"
+ local frameSettings = x:GetFrameSettings(outputFrame)
+ if not frameSettings then
+ -- Frame is disabled and the secondary frame is disabled too or not chosen
+ return
+ end
+
+ -- Add Icons
+ if icon and frameSettings.iconsEnabled then
+ if frameSettings.fontJustify == "LEFT" then
+ message = x:FormatIcon(icon, frameSettings.iconsSize) .. " " .. message
+ else
+ message = message .. " " .. x:FormatIcon(icon, frameSettings.iconsSize)
+ end
+ end
+
+ x:AddMessage(outputFrame, message, "spellProc")
+end
+
+CombatTextUpdateHandlers.SPELL_CAST = function(spellName)
+ if x:Options_Procs_ShowProcs() then
+ x:AddMessage("procs", spellName, "spellReactive")
+ end
+end
+
+CombatTextUpdateHandlers.HONOR_GAINED = function()
+ -- TODO: Create a merger for honor xp
+ -- UNTESTED
+ if not x:Options_General_ShowHonor() then
+ return
+ end
+
+ local amount = GetCurrentCombatTextEventInfo()
+ local num = math.floor(tonumber(amount) or 0)
+ if num > 0 then
+ x:AddMessage("general", string.format(format_honor, _G.HONOR, x:Abbreviate(amount, "general")), "honorGains")
+ end
+end
+
+CombatTextUpdateHandlers.FACTION = function()
+ if not x:Options_General_ShowReputationChanges() then
+ return
+ end
+
+ local faction, amount = GetCurrentCombatTextEventInfo()
+ amount = math.floor(tonumber(amount) or 0)
+ local outputFrame, color, outputFormat = "general"
+ if amount > 0 then
+ color = "reputationGain"
+ outputFormat = string.format("%s: +%s %s", _G.REPUTATION, "%s", faction)
+ else
+ color = "reputationLoss"
+ outputFormat = string.format("%s: -%s %s", _G.REPUTATION, "%s", faction)
+ end
+
+ local spamMergerInterval = x:Options_SpamMerger_DispellInterval()
+ if x:Options_SpamMerger_EnableSpamMerger() and spamMergerInterval > 0 then
+ x:AddSpamMessage(
+ outputFrame,
+ faction,
+ amount,
+ color,
+ spamMergerInterval,
+ {
+ outputFormat = outputFormat,
+ disableIcon = true
+ }
+ )
+ return
+ end
+
+ x:AddMessage(
+ outputFrame,
+ string.format(outputFormat, x:Abbreviate(amount, "general")),
+ "reputationGain"
+ )
+end
+
+EventHandlers.COMBAT_TEXT_UPDATE = function(_, subevent, ...)
+ if CombatTextUpdateHandlers[subevent] then
+ CombatTextUpdateHandlers[subevent](...)
+ end
+end
+
+function x.onCombatLogEvent(args)
+ -- Is the source someone we care about?
+ if args.isPlayer or args:IsSourceMyVehicle() or x:Options_Outgoing_ShowPetDamage() and args:IsSourceMyPet() then
+ if args.suffix == "_HEAL" then
+ EventHandlers.OutgoingHealing(args)
+ elseif args.suffix == "_DAMAGE" then
+ EventHandlers.OutgoingDamage(args)
+ elseif args.suffix == "_MISSED" then
+ if args.missType == "ABSORB" then
+ if x:Options_Outgoing_ShowAbsorbedDamageAsNormalDamage() then
+ -- This was fully absorbed, but we would like to display it... use the DamageOutgoing EventHandler
+ -- TODO What about fully absorbed heals?
+ args.amount = args.amountMissed
+ EventHandlers.OutgoingDamage(args)
+ end
+ else
+ EventHandlers.OutgoingMiss(args)
+ end
+ elseif args.event == "PARTY_KILL" then
+ EventHandlers.KilledUnit(args)
+ elseif args.event == "SPELL_INTERRUPT" then
+ EventHandlers.InterruptedUnit(args)
+ elseif args.event == "SPELL_DISPEL" then
+ EventHandlers.SpellDispel(args)
+ elseif args.event == "SPELL_STOLEN" then
+ EventHandlers.SpellStolen(args)
+ elseif args.suffix == "_ENERGIZE" then
+ EventHandlers.SpellEnergize(args)
+ end
+ end
+
+ -- Is the destination someone we care about?
+ if args.atPlayer or args:IsDestinationMyVehicle() then
+ if args.suffix == "_HEAL" then
+ EventHandlers.IncomingHealing(args)
+ elseif args.suffix == "_DAMAGE" then
+ EventHandlers.IncomingDamage(args)
+ elseif args.suffix == "_MISSED" then
+ EventHandlers.IncomingMiss(args)
+ elseif args.event == "SPELL_DISPEL" then
+ EventHandlers.IncomingSpellDispel(args)
+ end
+ end
+
+ -- Player Auras
+ if args.atPlayer
+ and (
+ args.suffix == "_AURA_APPLIED"
+ or args.suffix == "_AURA_REMOVED"
+ or args.suffix == "_AURA_APPLIED_DOSE"
+ or args.suffix == "_AURA_REMOVED_DOSE"
+ )
+ then
+ EventHandlers.IncomingAura(args)
+ end
+
+ -- Special case: Warrior's Spell reflect
+ if x.player.class == "WARRIOR" then
+ -- Spell reflect is weird:
+ -- 1. The Buff "Spell Reflect" is gained
+ -- 2. The Buff "Spell Reflect" is lost, this comes directly after a spell is reflected.
+ -- 3. There is a SPELL_DAMAGE event with the source == destination, this will come when the spell LANDS back on the caster.
+ -- 4. There is a SPELL_MISSED event (type REFLECT) with target = player, this usually comes after 3!
+
+ -- We can match the SPELL_MISSED to the SPELL_DAMAGE event via the sourceGUID while the Buff is up
+ -- Now we have to correlate all the 4 events to each other to get the correct SPELL_DAMAGE event to the SPELL_MISSED
+
+ if args.isPlayer and args.atPlayer and args.spellId == 23920
+ then
+ -- The warrior self-buffed (or lost) Spell reflect
+ if args.event == "SPELL_AURA_APPLIED" then
+ x.spellReflectApplied = args.timestamp
+ x.spellReflectRemoved = nil
+ elseif args.event == "SPELL_AURA_REMOVED" then
+ x.spellReflectRemoved = args.timestamp
+ end
+ elseif x.spellReflectApplied
+ and x.spellReflectApplied <= args.timestamp
+ and (x.spellReflectRemoved == nil or x.spellReflectRemoved + 1 >= args.timestamp)
+ then
+ -- The SPELL_DAMAGE event can / will come after the buff is lost, because of spell travel time
+ -- The SPELL_MISSED event came after the SPELL_DAMAGE event, idk why.
+
+ -- This event was triggered while spell reflect is active
+ local runHandler = false
+ if args.atPlayer and args.event == "SPELL_MISSED" and args.missType == "REFLECT" then
+ -- args get reused by the combatparser-lib, we have to clone it
+ x.spellReflectSpellMissed = table_clone(args)
+ runHandler = true
+ elseif args.event == "SPELL_DAMAGE" and args.sourceGUID == args.destGUID then
+ -- args get reused by the combatparser-lib, we have to clone it
+ x.spellReflectReflectedSpell = table_clone(args)
+ runHandler = true
+ end
+
+ if runHandler
+ and x.spellReflectSpellMissed
+ and x.spellReflectReflectedSpell
+ and x.spellReflectSpellMissed.sourceGUID == x.spellReflectReflectedSpell.sourceGUID
+ then
+ EventHandlers.OutgoingDamage(x.spellReflectReflectedSpell)
+ -- We found the reflected spell, nil everything so that it stops!
+ x.spellReflectSpellMissed = nil
+ x.spellReflectReflectedSpell = nil
+ x.spellReflectApplied = nil
+ x.spellReflectRemoved = nil
+ end
+ end
+ end
+end
+
+-- Register for the needed events
+function x:RegisterCombatEvents()
+ -- Unregister all events
+ self:UnregisterAllEvents()
+ LibStub("xCombatParser-1.0"):UnregisterCombat(self.onCombatLogEvent)
+
+ -- Register handlers the events we need
+
+ self:RegisterEvent("COMBAT_TEXT_UPDATE", EventHandlers.COMBAT_TEXT_UPDATE)
+
+ self:RegisterEvent("PLAYER_REGEN_DISABLED", EventHandlers.PLAYER_REGEN_DISABLED)
+ self:RegisterEvent("PLAYER_REGEN_ENABLED", EventHandlers.PLAYER_REGEN_ENABLED)
+ self:RegisterEvent("UNIT_ENTERED_VEHICLE", EventHandlers.UNIT_ENTERED_VEHICLE)
+ self:RegisterEvent("UNIT_EXITING_VEHICLE", EventHandlers.UNIT_EXITING_VEHICLE)
+ self:RegisterEvent("PLAYER_ENTERING_WORLD", EventHandlers.PLAYER_ENTERING_WORLD)
+ self:RegisterEvent("UNIT_PET", EventHandlers.UNIT_PET)
+
+ if self:GetFrameSettings("general") then
+ -- The "general" frame is enabled or its output is rerouted to a secondary frame.
+
+ if x:Options_General_ShowLowManaAndHealth() then
+ self:RegisterEvent("UNIT_HEALTH", EventHandlers.UNIT_HEALTH)
+
+ if select(2, UnitPowerType(x.player.unit)) == "MANA" then
+ self:RegisterEvent("UNIT_POWER_UPDATE", EventHandlers.UNIT_POWER_UPDATE)
+ end
+ end
+
+ if x:Options_General_ShowProfessionSkillups() then
+ self:RegisterEvent("CHAT_MSG_SKILL", EventHandlers.CHAT_MSG_SKILL)
+ end
+ end
+
+ if self:GetFrameSettings("loot") then
+ -- The "loot" frame is enabled or its output is rerouted to a secondary frame.
+
+ self:RegisterEvent("CHAT_MSG_LOOT", EventHandlers.CHAT_MSG_LOOT)
+
+ if x:Options_Loot_ShowCurrency() then
+ self:RegisterEvent("CHAT_MSG_CURRENCY", EventHandlers.CHAT_MSG_CURRENCY)
+ end
+
+ if x:Options_Loot_ShowMoney() then
+ self:RegisterEvent("CHAT_MSG_MONEY", EventHandlers.CHAT_MSG_MONEY)
+ end
+ end
+
+ if self:GetFrameSettings("power") then
+ -- The "power" frame is enabled or its output is rerouted to a secondary frame.
+
+ if x:Options_Power_ShowGains() and x:Options_Power_ShowResource("RUNES") and x.player.class == "DEATHKNIGHT" then
+ self:RegisterEvent("RUNE_POWER_UPDATE", EventHandlers.RUNE_POWER_UPDATE)
+ end
+ end
+
+ self:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED", EventHandlers.ACTIVE_TALENT_GROUP_CHANGED)
+
+ LibStub("xCombatParser-1.0"):RegisterCombat(self.onCombatLogEvent)
+end
diff --git a/xCT+/modules/core.lua b/xCT+/modules/core.lua
new file mode 100644
index 00000000..b1b1f4f1
--- /dev/null
+++ b/xCT+/modules/core.lua
@@ -0,0 +1,773 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- Get Addon's name and Blizzard's Addon Stub
+local AddonName, addon = ...
+
+-- Local Handle to the Engine
+local x = addon.engine
+
+function x:CheckExistingProfile()
+ local key = self.player.name .. " - " .. GetRealmName()
+ return xCTSavedDB
+ and xCTSavedDB.profileKeys
+ and xCTSavedDB.profileKeys[key]
+ and xCTSavedDB.profiles
+ and xCTSavedDB.profiles[xCTSavedDB.profileKeys[key]]
+end
+
+-- Gets called directly after the addon is fully loaded.
+function x:OnInitialize()
+ if xCT or ct and ct.myname and ct.myclass then
+ print("|cffFF0000WARNING:|r xCT+ cannot load. Please disable xCT in order to use xCT+.")
+ return
+ end
+
+ -- Clean Up Colors in the default profile
+ -- TODO is this really necessary?
+ self:LoadDefaultColors()
+
+ -- Load the user configs
+ self.db = LibStub("AceDB-3.0"):New("xCTSavedDB", addon.defaults)
+
+ -- Holds cached spells, buffs, and debuffs
+ x.spellCache = {
+ buffs = {},
+ debuffs = {},
+ spells = {},
+ procs = {},
+ items = {},
+ damage = {},
+ healing = {},
+ }
+
+ x.framesByName = {}
+
+ x.framesById = {
+ [1] = "general",
+ [2] = "outgoing",
+ [3] = "critical",
+ [4] = "damage",
+ [5] = "healing",
+ [6] = "power",
+ [7] = "procs",
+ [8] = "loot",
+ [10] = "outgoing_healing",
+ }
+
+ x.frameTitles = {
+ ["general"] = "General",
+ ["outgoing"] = "Outgoing Damage",
+ ["critical"] = "Outgoing Criticals (Damage & Healing)",
+ ["damage"] = "Incoming Damage",
+ ["healing"] = "Incoming Healing",
+ ["power"] = "Class Power",
+ ["procs"] = "Special Effects (Procs)",
+ ["loot"] = "Loot & Money",
+ ["outgoing_healing"] = "Outgoing Healing",
+ }
+
+ x.spamMergerHeap, x.spamMergerStack = {}, {}
+ for _, frameName in pairs(x.framesById) do
+ x.spamMergerHeap[frameName] = {}
+ x.spamMergerStack[frameName] = {}
+ end
+end
+
+-- Gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+function x:OnEnable()
+ -- A cache of infos about the player
+ self.player = {
+ unit = "player",
+ guid = UnitGUID("player"),
+ class = select(2, UnitClass("player")),
+ name = UnitName("player"),
+ spec = -1,
+ }
+ self:UpdatePlayer()
+
+ -- Clean up the Profile
+ local success = x:CompatibilityLogic(x:CheckExistingProfile())
+ if not success then
+ return
+ end
+
+ -- Delay updating frames until all other addons are loaded!
+ self:RegisterCombatEvents()
+
+ self:UpdateCVar()
+ self:RegisterChatCommand("xct", "OpenxCTCommand")
+ self:CacheColors()
+ self:EnableLibSinkSupport()
+
+ -- Register addon to the new compartment frame see https://wowpedia.fandom.com/wiki/Addon_compartment
+ AddonCompartmentFrame:RegisterAddon({
+ text = AddonName,
+ registerForAnyClick = true,
+ notCheckable = true,
+ func = function()
+ x:ToggleConfigTool()
+ end,
+ })
+
+ x:UpdateFrames()
+ x:UpdateCVar()
+end
+
+-- Gets only called when your addon is manually being disabled.
+function x:OnDisable() end
+
+-- Version Compare Helpers... Yeah!
+local function VersionToTable(version)
+ local major, minor, patch, releaseMsg = string.match(string.lower(version), "(%d+)%.(%d+)%.(%d+)(.*)")
+
+ return {
+ major = tonumber(major) or 0,
+ minor = tonumber(minor) or 0,
+ patch = tonumber(patch) or 0,
+ isAlpha = string.find(releaseMsg or "", "alpha") and true or false,
+ isBeta = string.find(releaseMsg or "", "beta") and true or false,
+ devBuild = tonumber(string.match(releaseMsg or "", "(%d+)")) or 0,
+ }
+end
+
+local function CompareVersions(a, b, debug)
+ if debug then
+ print("First Build:")
+ for i, v in pairs(a) do
+ print(" " .. i .. " = " .. tostring(v))
+ end
+ print("Second Build:")
+ for i, v in pairs(b) do
+ print(" " .. i .. " = " .. tostring(v))
+ end
+ end
+
+ -- Compare Major numbers
+ if a.major > b.major then
+ return 1
+ elseif a.major < b.major then
+ return -1
+ end
+
+ -- Compare Minor numbers
+ if a.minor > b.minor then
+ return 1
+ elseif a.minor < b.minor then
+ return -1
+ end
+
+ -- Compare Patch numbers
+ if a.patch > b.patch then
+ return 1
+ elseif a.patch < b.patch then
+ return -1
+ end
+
+ -- Compare Beta to Release then Alpha
+ if not a.isBeta and b.isBeta then
+ if a.isAlpha then
+ return -1
+ else
+ return 1
+ end
+ elseif a.isBeta and not b.isBeta then
+ if b.isAlpha then
+ return 1
+ else
+ return -1
+ end
+ end
+
+ -- Compare Beta Build Versions
+ if a.isBeta and b.isBeta then
+ if a.devBuild > b.devBuild then
+ return 1
+ elseif a.devBuild < b.devBuild then
+ return -1
+ end
+ return 0
+ end
+
+ -- Compare Alpha to Release
+ if not a.isAlpha and b.isAlpha then
+ return 1
+ elseif a.isAlpha and not b.isAlpha then
+ return -1
+ end
+
+ -- Compare Alpha Build Versions
+ if a.isAlpha and b.isAlpha then
+ if a.devBuild > b.devBuild then
+ return 1
+ elseif a.devBuild < b.devBuild then
+ return -1
+ end
+ return 0
+ end
+
+ return 0
+end
+
+-- This function was created as the central location for crappy code
+function x:CompatibilityLogic(existing)
+ local addonVersionString = C_AddOns.GetAddOnMetadata("xCT+", "Version")
+ if self.db.profile.dbVersion == "4.22" then
+ self.db.profile.dbVersion = "4.9.22"
+ end
+
+ local previousVersion = VersionToTable(self.db.profile.dbVersion or "4.3.0 Beta 2")
+
+ if existing then
+ -- Pre-Legion Requires Complete Reset
+ if CompareVersions(VersionToTable("4.2.9"), previousVersion) > 0 then
+ StaticPopup_Show("XCT_PLUS_DB_CLEANUP_2")
+ return false -- Do not continue loading addon
+ end
+
+ -- 4.3.0 Beta 3 -> Removes Spell School Colors from Outgoing fraame settings
+ if CompareVersions(VersionToTable("4.3.0 Beta 3"), previousVersion) > 0 then
+ if x.db.profile.frames.outgoing.colors and x.db.profile.frames.outgoing.colors.spellSchools then
+ local oldDB = x.db.profile.frames.outgoing.colors.spellSchools.colors
+ local newDB = x.db.profile.SpellColors
+ local keys = {
+ ["SpellSchool_Physical"] = "1",
+ ["SpellSchool_Holy"] = "2",
+ ["SpellSchool_Fire"] = "4",
+ ["SpellSchool_Nature"] = "8",
+ ["SpellSchool_Frost"] = "16",
+ ["SpellSchool_Shadow"] = "32",
+ ["SpellSchool_Arcane"] = "64",
+ }
+ for oldKey, newKey in pairs(keys) do
+ if oldDB[oldKey] then
+ newDB[newKey].enabled = oldDB[oldKey].enabled
+ newDB[newKey].color = oldDB[oldKey].color
+ end
+ end
+ x.db.profile.frames.outgoing.colors.spellSchools = nil
+ end
+ end
+
+ -- 4.3.0 Beta 4 -> Remove redundant Merge Entries from the Config
+ if CompareVersions(VersionToTable("4.3.0 Beta 5"), previousVersion) > 0 then
+ local merge = x.db.profile.spells.merge
+ for id, entry in pairs(merge) do
+ merge[id] = nil
+ if not entry.enabled and addon.merges[id] then
+ merge[id] = { enabled = false }
+ end
+ end
+ end
+
+ -- Clean up colors names in the database
+ if CompareVersions(VersionToTable("4.3.3 Beta 1"), previousVersion) > 0 then
+ for name, settings in pairs(x.db.profile.frames) do
+ if settings.colors then
+ for exists in pairs(settings.colors) do
+ if
+ addon.defaults.profile.frames[name]
+ and not addon.defaults.profile.frames[name].colors[exists]
+ then
+ settings.colors[exists] = nil
+ end
+ end
+ end
+ end
+ end
+
+ -- Clean up class frame from database
+ if CompareVersions(VersionToTable("4.5.1-beta5"), previousVersion) > 0 then
+ self.db.profile.frames.class = nil
+ end
+ end
+
+ if x.db.profile.frames.critical.critPrefix then
+ x.db.profile.megaDamage.critPrefix = x.db.profile.frames.critical.critPrefix
+ x.db.profile.frames.critical.critPrefix = nil
+ end
+
+ if x.db.profile.frames.critical.critPostfix then
+ x.db.profile.megaDamage.critSuffix = x.db.profile.frames.critical.critPostfix
+ x.db.profile.frames.critical.critPostfix = nil
+ end
+
+ self.db.profile.dbVersion = addonVersionString
+
+ return true
+end
+
+function x:CleanUpForLegion()
+ local key = xCTSavedDB.profileKeys[UnitName("player") .. " - " .. GetRealmName()]
+ xCTSavedDB.profiles[key] = {}
+ ReloadUI()
+end
+
+do
+ local cache = {
+ [1] = "1",
+ [2] = "2",
+ [3] = "3",
+ [4] = "4",
+ [5] = "5",
+ [6] = "6",
+ [8] = "8",
+ [9] = "9",
+ [10] = "10",
+ [12] = "12",
+ [16] = "16",
+ [17] = "17",
+ [18] = "18",
+ [20] = "20",
+ [24] = "24",
+ [28] = "28",
+ [32] = "32",
+ [33] = "33",
+ [34] = "34",
+ [36] = "36",
+ [40] = "40",
+ [48] = "48",
+ [64] = "64",
+ [65] = "65",
+ [66] = "66",
+ [68] = "68",
+ [72] = "72",
+ [80] = "80",
+ [96] = "96",
+ [124] = "124",
+ [126] = "126",
+ [127] = "127",
+ }
+
+ function x.GetSpellSchoolColor(spellSchool, override)
+ -- See if the override name is enabled
+ if override then
+ local newColor, enabled = x:LookupColorByName(override)
+ if enabled then
+ return newColor
+ end
+ end
+
+ -- Fast String lookup (faster than tostring)
+ local stringIndex = cache[spellSchool or 1] or "1"
+ local entry = x.db.profile.SpellColors[stringIndex]
+ return entry.enabled and entry.color or entry.default
+ end
+end
+
+-- Possibly add LibSink Support
+function x:EnableLibSinkSupport()
+ local LibSink = LibStub("LibSink-2.0", true)
+ if not LibSink then
+ -- No other addon has loaded LibSink ... so no point for us to register as a Sink.
+ return
+ end
+
+ local frames = {}
+ for name, title in pairs(x.frameTitles) do
+ frames[title] = name
+ end
+
+ -- shortName, name, desc, func, scrollAreaFunc, hasSticky
+ LibSink:RegisterSink(
+ "xCT_Plus",
+ "xCT+",
+ "Created for optimal performance in the toughest fights, this rugged combat text add-on is ready to be put to the test!",
+
+ function(_, text, r, g, b, _, _, _, _, location, icon)
+ local frameSettings = x.db.profile.frames[location or "general"]
+ if frameSettings.iconsEnabled and icon then
+ if frameSettings.fontJustify == "LEFT" then
+ text = x:FormatIcon(icon, frameSettings.iconSize) .. " " .. text
+ else
+ text = text .. " " .. x:FormatIcon(icon, frameSettings.iconSize) .. " " .. text
+ end
+ end
+ x:AddMessage(location or "general", text, { r, g, b })
+ end,
+
+ -- List Active Scrolling Areas
+ function()
+ local tmp = {}
+ for name in pairs(frames) do
+ table.insert(tmp, name)
+ end
+ return tmp
+ end,
+ false
+ )
+end
+
+function x:LoadDefaultColors()
+ -- TODO idk what this does tbh
+ local function cleanColors(colorTable)
+ for _, color in pairs(colorTable) do
+ if color.colors then
+ cleanColors(color.colors)
+ else
+ color.color = { color.default[1], color.default[2], color.default[3] }
+ end
+ end
+ end
+
+ for _, settings in pairs(addon.defaults.profile.frames) do
+ if settings.colors then
+ cleanColors(settings.colors)
+ end
+ end
+
+ cleanColors(addon.defaults.profile.SpellColors)
+end
+
+-- Cache colors for a fast lookup
+function x:CacheColors()
+ self.colorNameDB = {}
+
+ -- pre 4.9.0 format!
+ for frameName, frameSettings in pairs(x.db.profile.frames) do
+ if frameSettings.colors then
+ for colorName, colorSettings in pairs(frameSettings.colors) do
+ if colorSettings.colors then
+ for currentColorName, currentColorSettings in pairs(colorSettings.colors) do
+ -- if there is a valid color here, migrate it to the new version
+ -- only migrate if the target color still exists
+ if x.db.profile.Colors[currentColorName] and currentColorSettings.color and unpack(currentColorSettings.color) then
+ self:Print("Migrating color", currentColorName, "of", frameName, "to new format.")
+ x.db.profile.Colors[currentColorName].enabled = currentColorSettings.enabled
+ x.db.profile.Colors[currentColorName].color = currentColorSettings.color
+ end
+ end
+ else
+ -- if there is a valid color here, migrate it to the new version
+ -- only migrate if the target color still exists
+ if x.db.profile.Colors[colorName] and colorSettings.color and unpack(colorSettings.color) then
+ self:Print("Migrating color", colorName, "of", frameName, "to new format.")
+ x.db.profile.Colors[colorName].enabled = colorSettings.enabled
+ x.db.profile.Colors[colorName].color = colorSettings.color
+ end
+ end
+ end
+
+ -- delete it from the old format
+ x.db.profile.frames[frameName].colors = nil
+ end
+ end
+
+ -- new 4.9.0 format
+ for colorName, colorSettings in pairs(x.db.profile.Colors) do
+ -- Check for nil colors and set them to the default
+ if not colorSettings.color or not unpack(colorSettings.color) then
+ -- This needs to be a new table apparently
+ colorSettings.color = { unpack(colorSettings.default) }
+ end
+
+ self.colorNameDB[colorName] = colorSettings
+ end
+
+ for colorName, colorSettings in pairs(x.db.profile.SpellColors) do
+ colorName = tostring(colorName)
+
+ -- Check for nil colors and set them to the default
+ if not colorSettings.color or not unpack(colorSettings.color) then
+ -- This needs to be a new table apparently
+ colorSettings.color = { unpack(colorSettings.default) }
+ end
+
+ -- Cache this color into a quick lookup
+ self.colorNameDB[colorName] = colorSettings
+ end
+end
+
+function x:UpdateCVar(force)
+ -- Store Localized Strings
+ -- To remove: "Changed Target!"
+ local XCT_CT_DEC_0, XCT_CT_DEC_1, XCT_CT_DEC_2 =
+ COMBAT_THREAT_DECREASE_0, COMBAT_THREAT_DECREASE_1, COMBAT_THREAT_DECREASE_2
+ local XCT_CT_INC_1, XCT_CT_INC_3 = COMBAT_THREAT_INCREASE_1, COMBAT_THREAT_INCREASE_3
+
+ -- Floating Combat Text: Threat Changes
+ if not x.db.profile.blizzardFCT.CombatThreatChanges then
+ COMBAT_THREAT_DECREASE_0, COMBAT_THREAT_DECREASE_1, COMBAT_THREAT_DECREASE_2 = "", "", ""
+ COMBAT_THREAT_INCREASE_1, COMBAT_THREAT_INCREASE_3 = "", ""
+ elseif COMBAT_THREAT_DECREASE_0 == "" then
+ -- only overwrite Blizzard constants if they were previously changed
+ COMBAT_THREAT_DECREASE_0, COMBAT_THREAT_DECREASE_1, COMBAT_THREAT_DECREASE_2 =
+ XCT_CT_DEC_0, XCT_CT_DEC_1, XCT_CT_DEC_2
+ COMBAT_THREAT_INCREASE_1, COMBAT_THREAT_INCREASE_3 = XCT_CT_INC_1, XCT_CT_INC_3
+ end
+
+ if x:CVar_BypassCVars() then
+ if force then
+ StaticPopup_Show("XCT_PLUS_FORCE_CVAR_UPDATE")
+ else
+ return
+ end
+ end
+
+ if x.db.profile.blizzardFCT.enableFloatingCombatText then
+ SetCVar("enableFloatingCombatText", 1)
+ else
+ SetCVar("enableFloatingCombatText", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextAllSpellMechanics then
+ SetCVar("floatingCombatTextAllSpellMechanics", 1)
+ else
+ SetCVar("floatingCombatTextAllSpellMechanics", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextAuras then
+ SetCVar("floatingCombatTextAuras", 1)
+ else
+ SetCVar("floatingCombatTextAuras", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatDamage then
+ SetCVar("floatingCombatTextCombatDamage", 1)
+ else
+ SetCVar("floatingCombatTextCombatDamage", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatDamageAllAutos then
+ SetCVar("floatingCombatTextCombatDamageAllAutos", 1)
+ else
+ SetCVar("floatingCombatTextCombatDamageAllAutos", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatHealing then
+ SetCVar("floatingCombatTextCombatHealing", 1)
+ else
+ SetCVar("floatingCombatTextCombatHealing", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatHealingAbsorbSelf then
+ SetCVar("floatingCombatTextCombatHealingAbsorbSelf", 1)
+ else
+ SetCVar("floatingCombatTextCombatHealingAbsorbSelf", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatHealingAbsorbTarget then
+ SetCVar("floatingCombatTextCombatHealingAbsorbTarget", 1)
+ else
+ SetCVar("floatingCombatTextCombatHealingAbsorbTarget", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatLogPeriodicSpells then
+ SetCVar("floatingCombatTextCombatLogPeriodicSpells", 1)
+ else
+ SetCVar("floatingCombatTextCombatLogPeriodicSpells", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextCombatState then
+ SetCVar("floatingCombatTextCombatState", 1)
+ else
+ SetCVar("floatingCombatTextCombatState", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextComboPoints then
+ SetCVar("floatingCombatTextComboPoints", 1)
+ else
+ SetCVar("floatingCombatTextComboPoints", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextDamageReduction then
+ SetCVar("floatingCombatTextDamageReduction", 1)
+ else
+ SetCVar("floatingCombatTextDamageReduction", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextDodgeParryMiss then
+ SetCVar("floatingCombatTextDodgeParryMiss", 1)
+ else
+ SetCVar("floatingCombatTextDodgeParryMiss", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextEnergyGains then
+ SetCVar("floatingCombatTextEnergyGains", 1)
+ else
+ SetCVar("floatingCombatTextEnergyGains", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextFloatMode then
+ SetCVar("floatingCombatTextFloatMode", 1)
+ else
+ SetCVar("floatingCombatTextFloatMode", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextFriendlyHealers then
+ SetCVar("floatingCombatTextFriendlyHealers", 1)
+ else
+ SetCVar("floatingCombatTextFriendlyHealers", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextHonorGains then
+ SetCVar("floatingCombatTextHonorGains", 1)
+ else
+ SetCVar("floatingCombatTextHonorGains", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextLowManaHealth then
+ SetCVar("floatingCombatTextLowManaHealth", 1)
+ else
+ SetCVar("floatingCombatTextLowManaHealth", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextPeriodicEnergyGains then
+ SetCVar("floatingCombatTextPeriodicEnergyGains", 1)
+ else
+ SetCVar("floatingCombatTextPeriodicEnergyGains", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextPetMeleeDamage then
+ SetCVar("floatingCombatTextPetMeleeDamage", 1)
+ else
+ SetCVar("floatingCombatTextPetMeleeDamage", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextPetSpellDamage then
+ SetCVar("floatingCombatTextPetSpellDamage", 1)
+ else
+ SetCVar("floatingCombatTextPetSpellDamage", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextReactives then
+ SetCVar("floatingCombatTextReactives", 1)
+ else
+ SetCVar("floatingCombatTextReactives", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextRepChanges then
+ SetCVar("floatingCombatTextRepChanges", 1)
+ else
+ SetCVar("floatingCombatTextRepChanges", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextSpellMechanics then
+ SetCVar("floatingCombatTextSpellMechanics", 1)
+ else
+ SetCVar("floatingCombatTextSpellMechanics", 0)
+ end
+
+ if x.db.profile.blizzardFCT.floatingCombatTextSpellMechanicsOther then
+ SetCVar("floatingCombatTextSpellMechanicsOther", 1)
+ else
+ SetCVar("floatingCombatTextSpellMechanicsOther", 0)
+ end
+
+ SetCVar(
+ "floatingCombatTextCombatDamageDirectionalOffset",
+ x.db.profile.blizzardFCT.floatingCombatTextCombatDamageDirectionalOffset
+ )
+ SetCVar(
+ "floatingCombatTextCombatDamageDirectionalScale",
+ x.db.profile.blizzardFCT.floatingCombatTextCombatDamageDirectionalScale
+ )
+end
+
+-- Process the slash command ('input' contains whatever follows the slash command)
+function x:OpenxCTCommand(input)
+ input = string.lower(input)
+
+ if input == "lock" or input == "save" then
+ if not x.configuring and input == "save" then
+ return
+ end
+
+ if x.configuring then
+ x:SaveAllFrames()
+ x:EndConfigMode()
+ x:Print("Frames have been saved. Please fasten your seat belts.")
+ StaticPopup_Hide("XCT_PLUS_CONFIGURING")
+ else
+ x.ToggleConfigMode()
+
+ x:Print("You are now free to move about the cabin.")
+ x:Print("/xct lock - Saves the current frame positions.")
+ x:Print("/xct cancel - Resets the frame positions.")
+ end
+
+ -- return before you can do anything else
+ return
+ end
+
+ if input == "cancel" then
+ if x.configuring then
+ x:UpdateFrames()
+ x:EndConfigMode()
+ x:Print("Reset the frame positions.")
+ end
+
+ return
+ end
+
+ if input == "help" then
+ x:Print("Slash Commands:")
+ x:Print("/xct lock - Locks and unlocks the frame movers.")
+ x:Print("/xct test - Attempts to emulate combat.")
+ return
+ end
+
+ if input == "test" then
+ x.ToggleTestMode(true)
+ return
+ end
+
+ if not x.configuring then
+ x:ToggleConfigTool()
+ end
+end
+
+-- Load xCT+Options on demand
+function x:LoadOptionsAddon()
+ if not x.isOptionsAddonLoaded then
+ -- Funnily enough, this works in combat!
+ local loaded = C_AddOns.LoadAddOn("xCT+Options")
+ if not loaded then
+ self:Print("Options could not be loaded. Did you have the 'xCT+Options' folder in your Addons-folder?")
+ return
+ end
+ x.isOptionsAddonLoaded = true
+ end
+end
+
+function x:ToggleConfigTool()
+ self:LoadOptionsAddon()
+
+ xCT_Plus_Options.engine:ToggleConfigTool()
+end
+
+function x:ShowConfigTool(...)
+ self:LoadOptionsAddon()
+
+ xCT_Plus_Options.engine:ShowConfigTool(...)
+end
+
+function x:HideConfigTool(wait)
+ if x.isOptionsAddonLoaded then
+ xCT_Plus_Options.engine:HideConfigTool(wait)
+ end
+end
+
+-- DB for the colors
+-- Returns the color and if it was enabled
+function x:LookupColorByName(colorName)
+ if self.colorNameDB[colorName] then
+ if self.colorNameDB[colorName].enabled then
+ return self.colorNameDB[colorName].color or self.colorNameDB[colorName].default, true
+ end
+ return self.colorNameDB[colorName].default, false
+ else
+ return
+ end
+end
diff --git a/xCT+/modules/frames.lua b/xCT+/modules/frames.lua
new file mode 100644
index 00000000..fecb9305
--- /dev/null
+++ b/xCT+/modules/frames.lua
@@ -0,0 +1,1510 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- this file handles updating the frame settings and anything that changes the UI frames themselves
+local ADDON_NAME, addon = ...
+
+local LSM = LibStub("LibSharedMedia-3.0")
+
+-- Start the Random Machine!
+math.random(time())
+math.random()
+math.random(time())
+
+-- Shorten my handle
+local x = addon.engine
+
+local now = 0
+
+-- Function to allow users to scroll a frame with mouseover
+local function Frame_OnMouseWheel(self, delta)
+ if delta > 0 then
+ self:ScrollUp()
+ elseif delta < 0 then
+ self:ScrollDown()
+ end
+end
+
+-- =====================================================
+-- AddOn:UpdateFrames(
+-- specificFrame, [string] - (Optional) the framename
+-- )
+-- If you specify a specificFrame then only that
+-- frame will be updated, otherwise all the frames will
+-- be updated.
+-- =====================================================
+function x:UpdateFrames(specificFrame)
+ -- Update the frames
+ for frameName, frameSettings in pairs(x.db.profile.frames) do
+ if specificFrame and specificFrame == frameName or not specificFrame then
+ local f
+
+ -- Create the frame (or retrieve it)
+ if x.framesByName[frameName] then
+ f = x.framesByName[frameName]
+ else
+ f = CreateFrame(
+ "ScrollingMessageFrame",
+ "xCT_Plus" .. frameName .. "Frame",
+ UIParent,
+ "BackdropTemplate"
+ )
+ f:SetSpacing(2)
+ f:ClearAllPoints()
+ f:SetMovable(true)
+ f:SetResizable(true)
+ --f:SetMinResize(64, 32)
+ --f:SetMaxResize(768, 768)
+ f:SetClampedToScreen(true)
+ f:SetShadowColor(0, 0, 0, 0)
+
+ f.sizing = CreateFrame("Frame", "xCT_Plus" .. frameName .. "SizingFrame", f)
+ f.sizing.parent = f
+ f.sizing:SetHeight(16)
+ f.sizing:SetWidth(16)
+ f.sizing:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -1, 1)
+ f.sizing:Hide()
+
+ f.moving = CreateFrame("Frame", "xCT_Plus" .. frameName .. "MovingFrame", f)
+ f.moving.parent = f
+ f.moving:SetPoint("TOPLEFT", f, "TOPLEFT", 1, -1)
+ f.moving:SetPoint("TOPRIGHT", f, "TOPRIGHT", -1, -21)
+ f.moving:SetHeight(20)
+ f.moving:Hide()
+
+ x.framesByName[frameName] = f
+ end
+
+ f.frameName = frameName
+ f.settings = frameSettings
+
+ -- Frame Strata
+ if x.configuring then
+ f:SetFrameStrata("FULLSCREEN_DIALOG")
+ else
+ f:SetFrameStrata(string.sub(x.db.profile.frameSettings.frameStrata, 2))
+ end
+
+ -- Set the position
+ if frameSettings.enabledFrame then
+ f:SetWidth(frameSettings.Width)
+ f:SetHeight(frameSettings.Height)
+
+ -- WoW's default movement from changing the anchor
+ local point, relativeTo, relativePoint, xOfs, yOfs =
+ unpack(f:GetNumPoints() > 0 and { f:GetPoint(0) } or {})
+
+ -- If the point is not center, then something dirty happened... clean it up
+ if point and point ~= "CENTER" then
+ -- Calculate the center of the screen
+ local ResX, ResY = GetScreenWidth(), GetScreenHeight()
+ local midX, midY = ResX / 2, ResY / 2
+
+ -- Calculate the Top/Left of a frame relative to the center
+ local left, top = math.floor(f:GetLeft() - midX + 0.5), math.floor(f:GetTop() - midY + 0.5)
+
+ -- Calculate get the center of the screen from the left/top
+ local pointX = math.floor(left + (f:GetWidth() / 2) + 0.5)
+ local pointY = math.floor(top - (f:GetHeight() / 2) + 0.5)
+
+ f:ClearAllPoints()
+ f:SetPoint("CENTER", pointX, pointY)
+ else
+ f:ClearAllPoints()
+ f:SetPoint("CENTER", frameSettings.X, frameSettings.Y)
+ end
+ end
+
+ -- For keeping the frame on the screen
+ --f:SetClampRectInsets(0, 0, settings.fontSize, 0)
+
+ -- Frame Alpha
+ --f:SetAlpha(settings.alpha / 100)
+
+ -- NOTE: Setting the frame alpha this way still works... but it
+ -- doesn't apply to string texture children. FontString:SetAlpha
+ -- still works normally. Added bonus of not fading the frame
+ -- when we are configuring.
+
+ -- Insert Direction
+ if frameSettings.insertText then
+ f:SetInsertMode(
+ frameSettings.insertText == "top" and SCROLLING_MESSAGE_FRAME_INSERT_MODE_TOP
+ or SCROLLING_MESSAGE_FRAME_INSERT_MODE_BOTTOM
+ )
+ end
+
+ -- Font Template
+ local outline = string.sub(frameSettings.fontOutline, 2)
+
+ if outline == "NONE" then
+ f:SetFont(LSM:Fetch("font", frameSettings.font), frameSettings.fontSize, "")
+ else
+ f:SetFont(LSM:Fetch("font", frameSettings.font), frameSettings.fontSize, outline)
+ end
+
+ if frameSettings.fontJustify then
+ f:SetJustifyH(frameSettings.fontJustify)
+ end
+
+ -- Scrolling
+ if frameSettings.enableScrollable then
+ f:SetMaxLines(frameSettings.scrollableLines)
+ if not frameSettings.scrollableInCombat then
+ if InCombatLockdown() then
+ x:DisableFrameScrolling(frameName)
+ else
+ x:EnableFrameScrolling(frameName)
+ end
+ else
+ x:EnableFrameScrolling(frameName)
+ end
+ else
+ f:SetMaxLines(math.max(1, math.floor(frameSettings.Height / frameSettings.fontSize) - 1)) --- shhhhhhhhhhhhhhhhhhhh
+ x:DisableFrameScrolling(frameName)
+ end
+
+ -- fading
+ if frameSettings.enableCustomFade then
+ f:SetFading(frameSettings.enableFade)
+ f:SetFadeDuration(frameSettings.fadeTime)
+ f:SetTimeVisible(frameSettings.visibilityTime)
+ else
+ f:SetFading(true)
+ f:SetTimeVisible(3)
+ end
+
+ if frameSettings.enableFontShadow then
+ f:SetShadowColor(unpack(frameSettings.fontShadowColor))
+ f:SetShadowOffset(frameSettings.fontShadowOffsetX, frameSettings.fontShadowOffsetY)
+ else
+ f:SetShadowColor(0, 0, 0, 0)
+ end
+
+ if frameSettings.enabledFrame then
+ f:Show()
+ else
+ f:Hide()
+ end
+ end
+ end
+end
+
+function x:EnableFrameScrolling(framename)
+ local f = x.framesByName[framename]
+ f:EnableMouseWheel(true)
+ f:SetScript("OnMouseWheel", Frame_OnMouseWheel)
+end
+
+function x:DisableFrameScrolling(framename)
+ local f = x.framesByName[framename]
+ f:EnableMouseWheel(false)
+ f:SetScript("OnMouseWheel", nil)
+end
+
+-- =====================================================
+-- AddOn:Clear(
+-- specificFrame, [string] - (Optional) the framename
+-- )
+-- If you specify a specificFrame then only that
+-- frame will be cleared of its text, otherwise all
+-- the frames will be cleared.
+-- =====================================================
+function x:Clear(specificFrame)
+ if not specificFrame then
+ for framename in pairs(x.db.profile.frames) do
+ local frame = x:GetFrame(framename)
+ if frame then -- attempt to fix login 'attempt to index nil value frame' error
+ frame:Clear()
+ end
+ end
+ else
+ x:GetFrame(specificFrame):Clear()
+ end
+end
+
+-- =====================================================
+-- AddOn:Abbreviate(
+-- amount, [int] - the amount to abbreviate
+-- frameName*, [string] - (optional) the name of the frame whose settings we need to check.
+-- )
+--
+-- Abbreviates the specified amount. Will also check the current settings profile if a name frame is specified.
+-- =====================================================
+function x:Abbreviate(amount, frameName)
+ local roundNumber = function(value)
+ return math.floor(value + 0.5)
+ end
+
+ local isNegative = amount < 0
+ if isNegative then
+ amount = math.abs(amount)
+ end
+
+ local message = tostring(amount)
+
+ if frameName and self.db.profile.frames[frameName] and self.db.profile.frames[frameName].megaDamage then
+ local suffix = ""
+ if self.db.profile.spells.formatAbbreviate then
+ if x.locale == "koKR" then
+ if amount >= 100000000 then
+ suffix = self.db.profile.megaDamage.billionSymbol
+ amount = amount / 100000000
+ elseif amount >= 10000 then
+ suffix = self.db.profile.megaDamage.millionSymbol
+ amount = amount / 10000
+ elseif amount >= 1000 then
+ suffix = self.db.profile.megaDamage.thousandSymbol
+ amount = amount / 1000
+ end
+ else
+ if amount >= 1000000000 then
+ suffix = self.db.profile.megaDamage.billionSymbol
+ amount = amount / 1000000000
+ elseif amount >= 1000000 then
+ suffix = self.db.profile.megaDamage.millionSymbol
+ amount = amount / 1000000
+ elseif amount >= 1000 then
+ suffix = self.db.profile.megaDamage.thousandSymbol
+ amount = amount / 1000
+ end
+ end
+
+ if self.db.profile.megaDamage.decimalPoint then
+ message = tostring(roundNumber(amount * 10) / 10) .. suffix
+ else
+ message = tostring(roundNumber(amount)) .. suffix
+ end
+ else
+ local k
+ while true do
+ message, k = string.gsub(message, "^(-?%d+)(%d%d%d)", "%1,%2")
+ if k == 0 then
+ break
+ end
+ end
+ end
+ end
+
+ if isNegative then
+ message = "-" .. message
+ end
+
+ return message
+end
+
+-- =====================================================
+-- AddOn:AddMessage(
+-- frameName, [string] - the framename
+-- message, [string] - the pre-formatted message to be sent
+-- colorName, [string or table] - the name of the color OR a
+-- table containing the color
+-- e.g. colorname={1,2,3} --r=1,g=2,b=3
+-- )
+-- Sends a message to the framename specified.
+-- =====================================================
+function x:AddMessage(frameName, message, colorName)
+ local frame = x:GetFrame(frameName, true)
+ local frameSettings = x.db.profile.frames[frameName]
+
+ -- Make sure we have a valid frame
+ if not frameSettings or not frame then
+ x:Print("Frame not found", frameName)
+ return
+ end
+
+ -- Load the color
+ local r, g, b = 1, 1, 1
+ if type(colorName) == "table" then
+ -- unpack({0, 0, 1}) leads to OOM... idk why?!
+ r, g, b = colorName[1], colorName[2], colorName[3]
+ else
+ local color = x:LookupColorByName(colorName)
+ if color then
+ r, g, b = color[1], color[2], color[3]
+ else
+ x:Print("There is no color named", colorName)
+ error("missing color")
+ end
+ end
+
+ if not frameSettings.enabledFrame then
+ if not frameSettings.secondaryFrame or frameSettings.secondaryFrame == 0 then
+ -- Neither the frame nor the secondary frame is enabled
+ return nil
+ end
+
+ -- Overwrite the output frame
+ frameName = x.framesById[frameSettings.secondaryFrame]
+ frameSettings = x.db.profile.frames[frameName]
+
+ if not frameSettings.enabledFrame then
+ return nil
+ end
+
+ frame = x:GetFrame(frameName, true)
+ end
+
+ -- check for forced color
+ if frameSettings.customColor and frameSettings.fontColor then
+ r, g, b = frameSettings.fontColor[1], frameSettings.fontColor[2], frameSettings.fontColor[3]
+ end
+
+ frame:AddMessage(message, r, g, b)
+end
+
+--[=====================================================[
+ AddOn:GetSpellTextureFormatted(
+ spellID, [number] - The spell ID you want the icon for
+ message, [string] - The message that will be used (usually the amount)
+ frameSettings, [table] - The settings of the frame
+ strColor, [string] - the color to be used or defaults white
+ mergeCount [number] - The number of events merged into this message
+ )
+ Returns:
+ message, [string] - the message contains the formatted icon
+
+ Formats an icon quickly for use when outputting to a combat text frame.
+--]=====================================================]
+function x:GetSpellTextureFormatted(spellId, message, frameSettings, iconSize, strColor, mergeCount)
+ iconSize = iconSize or frameSettings.iconsEnabled and frameSettings.iconsSize or -1
+ local showInvisibleIcon = frameSettings.spacerIconsEnabled
+
+ strColor = strColor or "ffffff"
+
+ local icon = x.BLANK_ICON
+ if iconSize >= 1 then
+ -- always show unless we specify enableIcons to be off (overriding iconSize to be -1)
+ showInvisibleIcon = true
+
+ if spellId == 0 then
+ icon = PET_ATTACK_TEXTURE
+ elseif type(spellId) == "string" then
+ icon = spellId
+ else
+ icon = spellId and C_Spell.GetSpellTexture(addon.replaceSpellId[spellId] or spellId) or x.BLANK_ICON
+ end
+ end
+
+ message = message or ""
+
+ if mergeCount and mergeCount > 1 then
+ message = string.format("%s |cff%sx%d|r", message, strColor, mergeCount)
+ end
+
+ if showInvisibleIcon then
+ if frameSettings.fontJustify == "LEFT" then
+ message = x:FormatIcon(icon, iconSize) .. " " .. message
+ else
+ message = message .. " " .. x:FormatIcon(icon, iconSize)
+ end
+ end
+
+ if x.enableMergerDebug then
+ message = message .. " |cffFFFFFF[|cffFF0000ID:|r|cffFFFF00" .. (spellId or "No ID") .. "|r]|r"
+ end
+
+ return message
+end
+
+--[=====================================================[
+ AddOn:FormatIcon(
+ iconTexture, [number] - The ID of the icon.
+ iconSize, [number] - The width and heigth of the icon.
+ )
+ Returns:
+ icon [string] - The formatted icon.
+
+ Creates a string for an icon to use in a message.
+--]=====================================================]
+function x:FormatIcon(iconTexture, iconSize)
+ return string.format("|T%s:%d:%d:0:0:64:64:5:59:5:59|t", iconTexture, iconSize, iconSize)
+end
+
+-- WoW - Battle for Azeroth doesn't support fading textures with SetAlpha?
+-- We have to do it on a font string level
+local ScrollingMessageFrame_OverrideAlpha_Worker = CreateFrame("FRAME")
+ScrollingMessageFrame_OverrideAlpha_Worker:SetScript("OnUpdate", function()
+ local now2, alpha, scale = GetTime()
+ for _, frame in pairs(x.framesByName) do
+ alpha = frame.settings.alpha / 100
+
+ -- Only run on frames that have a custom alpha
+ if alpha ~= 1 then
+ -- Loop through each fontstring
+ for _, visibleLine in ipairs(frame.visibleLines) do
+ if visibleLine.messageInfo then -- Check for valid font strings (not released)
+ -- Keep the default fading, we will use their value to scale the custom alpha
+ scale = frame:CalculateLineAlphaValueFromTimestamp(
+ now2,
+ math.max(visibleLine.messageInfo.timestamp, frame.overrideFadeTimestamp)
+ )
+
+ -- If we are fading the message away and fading is enabled
+ if scale ~= 1 and frame:CanEffectivelyFade() then
+ visibleLine:SetAlpha(alpha * scale) -- Fade the font string, scaled for the custom amount
+
+ -- Only change the font string's alpha if it didn't already change
+ elseif visibleLine:GetAlpha() ~= alpha then
+ visibleLine:SetAlpha(alpha)
+ end
+ end
+ end
+ end
+ end
+end)
+
+-- =====================================================
+-- AddOn:AddSpamMessage(
+-- frameName, [string] - the name of the frame you want to output the message to
+-- mergeId, [number or string] - identity items to merge, if number then it HAS TO BE the valid spell ID
+-- message, [number or string] - the pre-formatted message to be sent, if its not a number, then only the
+-- first 'message' value that is sent this mergeId will be used.
+-- colorName, [string or table] - the name of the color OR a table containing the color
+-- (e.g. {1,2,3} -- r=1, b=2, g=3)
+-- interval, [number] - the merge interval
+-- additionalInfo, [nil or table] - additional infos for this message
+-- )
+-- Sends a message to the frameName specified.
+-- =====================================================
+function x:AddSpamMessage(frameName, mergeId, message, colorName, interval, additionalInfo)
+ if message == 0 or message == "" then
+ self:Print("AddSpamMessage(): empty message for frame", frameName, "mergeId", mergeId, "message", message)
+ return
+ end
+
+ -- Check for a Secondary Spell ID
+ mergeId = addon.replaceSpellId[mergeId] or mergeId
+
+ local db = addon.merges[mergeId]
+
+ -- How many seconds are we delaying the output / merging the events?
+ interval = interval or (db and db.interval) or x.db.profile.spells.mergeEverythingInterval
+
+ local heap, stack = x.spamMergerHeap[frameName], x.spamMergerStack[frameName]
+ if heap[mergeId] then
+ -- There is already something in the heap with this mergeId.
+ -- Merge them!
+ heap[mergeId].color = colorName
+
+ if tonumber(message) then
+ heap[mergeId].mergedAmount = heap[mergeId].mergedAmount + tonumber(message)
+ else
+ heap[mergeId].message = message
+ end
+
+ heap[mergeId].mergedCount = heap[mergeId].mergedCount + 1
+
+ if heap[mergeId].displayTime <= now then
+ heap[mergeId].displayTime = now + interval
+ end
+
+ if additionalInfo then
+ if heap[mergeId].args then
+ -- There are args in the heap... dont overwrite them
+ if heap[mergeId].args.controllerName
+ and additionalInfo.controllerName
+ and heap[mergeId].args.controllerName ~= additionalInfo.controllerName
+ then
+ -- remove the controller name if it differs
+ heap[mergeId].args.controllerName = nil
+ end
+
+ if heap[mergeId].args.critical ~= additionalInfo.critical then
+ -- We want to track if all events are crits in this merge
+ -- So either the existing event(s) are no crits or the new one isnt
+ heap[mergeId].args.critical = false
+ end
+ else
+ -- No args are set in the heap - set them!
+ heap[mergeId].args = additionalInfo
+ end
+ end
+ else
+ -- this mergeId is new! Add it to the heap and stack
+ heap[mergeId] = {
+ -- after this time we display it on the frame
+ displayTime = now + interval,
+
+ -- merged entries
+ mergedAmount = 0,
+ mergedCount = 1,
+
+ color = colorName,
+ }
+
+ if tonumber(message) then
+ heap[mergeId].mergedAmount = heap[mergeId].mergedAmount + tonumber(message)
+ else
+ heap[mergeId].message = message
+ end
+
+ if additionalInfo then
+ heap[mergeId].args = additionalInfo
+ end
+
+ -- Insert into the stack - thats our queue for the display!
+ table.insert(stack, mergeId)
+ end
+end
+
+--[================================================================[
+ _____ _______ ____
+ / ____|__ __| |___ \
+ __ _| | | |_| |_ __ _____ _ __ __) |
+ \ \/ / | | |_ _| \ \ / / _ \ '__| |__ <
+ > <| |____ | | |_| \ V / __/ |_ ___) |
+ /_/\_\\_____| |_| \_/ \___|_(_) |____/
+
+ ___ _ __ __ _ _ __ ___ _ __ ___ ___ _ __ __ _ ___ _ __
+ / __| '_ \ / _` | '_ ` _ \ | '_ ` _ \ / _ \ '__/ _` |/ _ \ '__|
+ \__ \ |_) | (_| | | | | | | | | | | | | __/ | | (_| | __/ |
+ |___/ .__/ \__,_|_| |_| |_| |_| |_| |_|\___|_| \__, |\___|_|
+ | | __/ |
+ |_| |___/
+
+ This is the new spam merger. Here is how it works:
+ On each OnUpdate (at 60 FPS this happens 60 times per second):
+ + Go to the current xCT-frame (one frame at a time)
+ - Go through the whole stack of this xCT-frame
+ + if a spell entry says its time to display, then display it
+ + if not, then skip it
+ + Advance our frame index so that we use the next frame next time.
+ + Wait for next Update
+
+ As you can see, I only update one xCT-Frame per OnUpdate.
+
+ As of 4.9.0 we're displaying all waiting messages per OnUpdate instead of just one message.
+ I am hoping that the spell merger will (still) be mostly invisible.
+
+ ]================================================================]
+
+do
+ -- We want to display messages for one frame on each update
+ local currentFrameId = 1
+
+ function x.OnSpamUpdate(_, elapsed)
+ if not x.db then
+ return
+ end
+
+ -- Update 'now'
+ now = now + elapsed
+
+ -- Check to see if we are out of bounds
+ if currentFrameId > #x.framesById then
+ currentFrameId = 1
+ end
+
+ local frameName = x.framesById[currentFrameId]
+
+ local heap, stack, frameSettings =
+ x.spamMergerHeap[frameName], -- the heap contains merge entries
+ x.spamMergerStack[frameName], -- the stack contains lookup values
+ x:GetFrameSettings(frameName) -- this frame's settings
+
+ -- If the frame is not enabled, then dont even worry about it
+ if not frameSettings then
+ currentFrameId = currentFrameId + 1
+ return
+ end
+
+ for _, mergeId in pairs(stack) do
+ -- This has all the information for the message we want to display
+ local item = heap[mergeId]
+ local critical = item.args and item.args.critical
+
+ if item and item.displayTime <= now and item.mergedCount > 0 then
+ item.displayTime = now
+
+ if item.mergedAmount == 0 and item.message == "" then
+ -- How did this happen?!
+ x:Print("Empty item in the spam merger", mergeId)
+ DevTools_Dump(item)
+
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ elseif frameName == "outgoing" then
+ -- Outgoing damage
+ if not item.message and x:Options_Filter_OutgoingDamage_HideEvent(item.mergedAmount, critical) then
+ -- not enough to display
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ elseif frameName == "outgoing_healing" then
+ -- Outgoing healing
+ if not item.message and x:Options_Filter_OutgoingHealing_HideEvent(item.mergedAmount, critical) then
+ -- not enough to display
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ elseif frameName == "critical" then
+ -- Outgoing damage and healing crits
+ if not item.message and x:Options_Filter_OutgoingDamage_HideEvent(item.mergedAmount, critical) then
+ -- not enough to display
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ elseif frameName == "healing" then
+ -- Incoming healing
+ if not item.message and x:Options_Filter_IncomingHealing_HideEvent(item.mergedAmount, critical) then
+ -- not enough to display
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ elseif frameName == "damage" then
+ -- Incoming damage
+ if not item.message and x:Options_Filter_IncomingDamage_HideEvent(item.mergedAmount, critical) then
+ -- not enough to display
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ end
+
+ if item.mergedCount > 0 then
+ -- total as a string
+ local message
+ if tonumber(item.mergedAmount) and item.mergedAmount ~= 0 then
+ message = x:Abbreviate(tonumber(item.mergedAmount), frameName)
+ else
+ message = item.message
+ end
+
+ local strColor = "ffffff"
+ if frameName == "healing" or frameName == "outgoing_healing" then
+ strColor = "ffff00"
+ end
+
+ -- Add critical Prefix and Postfix
+ if critical then
+ message = x:Options_Global_FormatCritical(message)
+ end
+
+ local disableIcon = false
+ if item.args then
+ if item.args.controller and frameSettings.names[item.args.controller].nameType > 0 then
+ if frameSettings.fontJustify == "RIGHT" then
+ message = x:formatName(item.args, frameSettings.names, item.args.useSource) .. " " .. message
+ else
+ message = message .. x:formatName(item.args, frameSettings.names, item.args.useSource)
+ end
+ end
+
+ if item.args.outputFormat then
+ message = string.format(item.args.outputFormat, message)
+ end
+
+ if item.args.disableIcon then
+ disableIcon = item.args.disableIcon
+ end
+ end
+
+ -- Add Icons
+ if not disableIcon then
+ local iconSize
+ if mergeId == 6603 and not x:ShowAutoAttackIcons(frameName) then
+ -- Disable the auto attack icon for the incoming damage frame
+ iconSize = -1
+ end
+
+ message = x:GetSpellTextureFormatted(
+ mergeId,
+ message,
+ frameSettings,
+ iconSize,
+ strColor,
+ item.mergedCount
+ )
+ end
+
+ x:AddMessage(frameName, message, item.color)
+
+ -- Clear all the old amounts, we dont need them anymore
+ item.mergedCount = 0
+ item.mergedAmount = 0
+ item.message = nil
+ item.args = nil
+ end
+ end
+ end
+
+ -- Advance to the next frame
+ currentFrameId = currentFrameId + 1
+ end
+
+ x.spamMergerFrame = CreateFrame("FRAME")
+ x.spamMergerFrame:SetScript("OnUpdate", x.OnSpamUpdate)
+end
+
+local function Frame_Sizing_OnUpdate(self)
+ local settings = self.parent.settings
+ local width, height = math.floor(self.parent:GetWidth() + 0.5), math.floor(self.parent:GetHeight() + 0.5)
+ self.parent.width:SetText(width)
+ self.parent.height:SetText(height)
+
+ self.parent:SetMaxLines(math.floor(height / settings.fontSize) - 1)
+end
+
+local function Frame_Moving_OnUpdate(self)
+ -- Calculate get the center of the screen from the left/top
+ local posX = math.floor(math.floor(self.parent:GetLeft() - GetScreenWidth() / 2 + 0.5))
+ local posY = math.floor(math.floor(self.parent:GetTop() - GetScreenHeight() / 2 + 0.5))
+
+ -- Set the position of the frame
+ self.parent.position:SetText(string.format("%d, %d", posX, posY))
+end
+
+local function Frame_Sizing_OnMouseDown(self, button)
+ if button == "LeftButton" then
+ self.parent:StartSizing()
+ self:SetScript("OnUpdate", Frame_Sizing_OnUpdate)
+ self.isMoving = true
+ end
+end
+
+local function Frame_Sizing_OnMouseUp(self, button)
+ if button == "LeftButton" and self.isMoving then
+ self.parent:StopMovingOrSizing()
+ self:SetScript("OnUpdate", nil)
+ self.isMoving = false
+ end
+end
+
+local function Frame_Moving_OnMouseDown(self, button)
+ if button == "LeftButton" then
+ self.parent:StartMoving()
+ self:SetScript("OnUpdate", Frame_Moving_OnUpdate)
+ self.isMoving = true
+ end
+end
+
+local function Frame_Moving_OnMouseUp(self, button)
+ if button == "LeftButton" and self.isMoving then
+ self.parent:StopMovingOrSizing()
+ self:SetScript("OnUpdate", nil)
+ self.isMoving = false
+ end
+end
+
+local function Frame_MouseEnter(self)
+ if x.db.profile.frameSettings.showPositions then
+ if self.width then
+ self.width:Show()
+ self.height:Show()
+ self.position:Show()
+ else
+ self.parent.width:Show()
+ self.parent.height:Show()
+ self.parent.position:Show()
+ end
+ end
+end
+
+local function Frame_MouseLeave(self)
+ if self.width then
+ self.width:Hide()
+ self.height:Hide()
+ self.position:Hide()
+ else
+ self.parent.width:Hide()
+ self.parent.height:Hide()
+ self.parent.position:Hide()
+ end
+end
+
+-- Starts the "config mode" so that you can move the frames
+function x.StartConfigMode()
+ x.configuring = true
+
+ for framename, settings in pairs(x.db.profile.frames) do
+ if settings.enabledFrame then
+ local f = x:GetFrame(framename)
+
+ f:SetBackdrop({
+ bgFile = "Interface/Tooltips/UI-Tooltip-Background",
+ edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
+ tile = false,
+ tileSize = 0,
+ edgeSize = 2,
+ insets = { left = 0, right = 0, top = 0, bottom = 0 },
+ })
+
+ f:SetBackdropColor(0.1, 0.1, 0.1, 0.8)
+ f:SetBackdropBorderColor(0.1, 0.1, 0.1, 0.5)
+
+ -- Show the sizing and moving frames
+ f.sizing:Show()
+ f.moving:Show()
+
+ -- Frame Title
+ f.title = f:CreateFontString(nil, "OVERLAY")
+ f.title:SetPoint("BOTTOM", f, "TOP", 0, -18)
+ f.title:SetFont(LSM:Fetch("font", "Condensed Bold (xCT+)"), 15, "OUTLINE")
+ f.title:SetText(x.frameTitles[framename])
+
+ -- Size Text
+ f.width = f:CreateFontString(nil, "OVERLAY")
+ f.width:SetTextColor(0.47, 0.55, 0.87, 1)
+ f.width:SetPoint("TOP", f, "BOTTOM", 0, -2)
+ f.width:SetFont(LSM:Fetch("font", "Condensed Bold (xCT+)"), 18, "OUTLINE")
+ f.width:SetText(math.floor(f:GetWidth() + 0.5))
+ f.width:Hide()
+
+ f.height = f:CreateFontString(nil, "OVERLAY")
+ f.height:SetTextColor(0.47, 0.55, 0.87, 1)
+ f.height:SetPoint("LEFT", f, "RIGHT", 4, 0)
+ f.height:SetFont(LSM:Fetch("font", "Condensed Bold (xCT+)"), 18, "OUTLINE")
+ f.height:SetText(math.floor(f:GetHeight() + 0.5))
+ f.height:Hide()
+
+ -- Calculate get the center of the screen from the left/top
+ local posX = math.floor(math.floor(f:GetLeft() - GetScreenWidth() / 2 + 0.5))
+ local posY = math.floor(math.floor(f:GetTop() - GetScreenHeight() / 2 + 0.5))
+
+ -- Position Text
+ f.position = f:CreateFontString(nil, "OVERLAY")
+ f.position:SetTextColor(1, 1, 0, 1)
+ f.position:SetPoint("BOTTOMLEFT", f, "TOPLEFT", 0, 4)
+ f.position:SetFont(LSM:Fetch("font", "Condensed Bold (xCT+)"), 18, "OUTLINE")
+ f.position:SetText(string.format("%d, %d", posX, posY))
+ f.position:Hide()
+
+ f.moving.d = f:CreateTexture(nil, "OVERLAY")
+ f.moving.d:SetPoint("TOPLEFT", f, "TOPLEFT", 1, -1)
+ f.moving.d:SetPoint("TOPRIGHT", f, "TOPRIGHT", -1, -19)
+ f.moving.d:SetHeight(20)
+ f.moving.d:SetVertexColor(0.3, 0.3, 0.3)
+ f.moving.d:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ f.moving.d:SetAlpha(0.6)
+
+ f.sizing.d = f.sizing:CreateTexture("ARTWORK")
+ f.sizing.d:SetHeight(16)
+ f.sizing.d:SetWidth(16)
+ f.sizing.d:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -1, 1)
+ f.sizing.d:SetVertexColor(0.3, 0.3, 0.3)
+ f.sizing.d:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ f.sizing.d:SetAlpha(0.6)
+
+ -- Frame Settings
+ f:SetScript("OnEnter", Frame_MouseEnter)
+ f:SetScript("OnLeave", Frame_MouseLeave)
+
+ -- Moving Settings
+ f.moving:EnableMouse(true)
+ f.moving:RegisterForDrag("LeftButton")
+ f.moving:SetScript("OnMouseDown", Frame_Moving_OnMouseDown)
+ f.moving:SetScript("OnMouseUp", Frame_Moving_OnMouseUp)
+ f.moving:SetScript("OnEnter", Frame_MouseEnter)
+ f.moving:SetScript("OnLeave", Frame_MouseLeave)
+
+ -- Resizing Settings
+ f.sizing:EnableMouse(true)
+ f.sizing:RegisterForDrag("LeftButton")
+ f.sizing:SetScript("OnMouseDown", Frame_Sizing_OnMouseDown)
+ f.sizing:SetScript("OnMouseUp", Frame_Sizing_OnMouseUp)
+ f.sizing:SetScript("OnEnter", Frame_MouseEnter)
+ f.sizing:SetScript("OnLeave", Frame_MouseLeave)
+
+ -- TODO: Add option to adjust the number of lines for memory purposes
+ -- TODO: Show Alignment Grid
+
+ f:SetFrameStrata("FULLSCREEN_DIALOG")
+ end
+ end
+end
+
+function x:EndConfigMode()
+ x.configuring = false
+ if x.AlignGrid then
+ x.AlignGrid:Hide()
+ end
+
+ for framename, settings in pairs(x.db.profile.frames) do
+ if settings.enabledFrame then
+ local f = x:GetFrame(framename)
+
+ f:SetBackdrop(nil)
+
+ -- Remove Scripts
+ f:SetScript("OnEnter", nil)
+ f:SetScript("OnLeave", nil)
+
+ f.moving:SetScript("OnMouseDown", nil)
+ f.moving:SetScript("OnMouseUp", nil)
+ f.moving:SetScript("OnEnter", nil)
+ f.moving:SetScript("OnLeave", nil)
+
+ f.sizing:SetScript("OnMouseDown", nil)
+ f.sizing:SetScript("OnMouseUp", nil)
+ f.sizing:SetScript("OnEnter", nil)
+ f.sizing:SetScript("OnLeave", nil)
+
+ -- Clean up visual items
+ if f.title then
+ f.title:Hide()
+ f.title = nil
+ end
+
+ if f.moving.d then
+ f.moving.d:Hide()
+ f.moving.d = nil
+ end
+
+ if f.sizing.d then
+ f.sizing.d:Hide()
+ f.sizing.d = nil
+ end
+
+ if f.position then
+ f.position:Hide()
+ f.position = nil
+ end
+
+ if f.width then
+ f.width:Hide()
+ f.width = nil
+ end
+
+ if f.height then
+ f.height:Hide()
+ f.height = nil
+ end
+
+ f:EnableMouse(false)
+
+ -- Hide the sizing frame
+ f.sizing:EnableMouse(false)
+ f.sizing:Hide()
+
+ -- Hide the moving frame
+ f.moving:EnableMouse(false)
+ f.moving:Hide()
+
+ -- Set the Frame Strata
+ f:SetFrameStrata(string.sub(x.db.profile.frameSettings.frameStrata, 2))
+ end
+ end
+
+ collectgarbage()
+end
+
+function x.ToggleConfigMode()
+ if x.configuring then
+ return
+ else
+ -- Close the Options Dialog if it is Open
+ -- Because this could be called fromt the UI, we need to wait
+ x:HideConfigTool(true)
+
+ -- Thanks Elv :)
+ GameTooltip:Hide() -- Just in case you're mouseover'ed something and it closes.
+
+ StaticPopup_Show("XCT_PLUS_CONFIGURING")
+
+ if x.db.profile.frameSettings.showGrid then
+ if not x.AlignGrid then
+ x:LoadAlignmentGrid()
+ end
+ x.AlignGrid:Show()
+ end
+
+ x.StartConfigMode()
+ end
+end
+
+function x:SaveAllFrames()
+ for framename, settings in pairs(x.db.profile.frames) do
+ local frame = x.framesByName[framename]
+ -- If frame is disabled, trying to calculate position will fail
+ if settings.enabledFrame then
+ local width = frame:GetWidth()
+ local height = frame:GetHeight()
+
+ settings.Width = math.floor(width + 0.5)
+ settings.Height = math.floor(height + 0.5)
+
+ -- Calculate the center of the screen
+ local ResX, ResY = GetScreenWidth(), GetScreenHeight()
+ local midX, midY = ResX / 2, ResY / 2
+
+ -- Calculate the Top/Left of a frame relative to the center
+ local left, top = math.floor(frame:GetLeft() - midX + 0.5), math.floor(frame:GetTop() - midY + 0.5)
+
+ -- Calculate get the center of the screen from the left/top
+ settings.X = math.floor(left + (width / 2) + 0.5)
+ settings.Y = math.floor(top - (height / 2) + 0.5)
+ end
+ end
+end
+
+local colors = { "1", "2", "4", "8", "16", "32", "64" }
+local function GetRandomSpellColor()
+ local color = colors[math.random(7)]
+ return x.db.profile.SpellColors[color].color or x.db.profile.SpellColors[color].default
+end
+
+-- Gets a random spell icon that is NOT an engineering cog wheel
+local function GetRandomSpellID()
+ local icon, spellID
+ repeat
+ spellID = math.random(100, 80000)
+ icon = C_Spell.GetSpellTexture(spellID)
+ until icon and icon ~= 136243
+ return spellID
+end
+
+function x.TestMoreUpdate(self, elapsed)
+ if InCombatLockdown() then
+ self:SetScript("OnUpdate", nil)
+ else
+ self.lastUpdate = self.lastUpdate + elapsed
+
+ if not self.nextUpdate then
+ self.nextUpdate = math.random(80, 600) / 1000
+ end
+
+ if self.nextUpdate < self.lastUpdate then
+ self.nextUpdate = nil
+ self.lastUpdate = 0
+
+ if self == x.framesByName.general and math.random(3) % 3 == 0 then
+ local outputFrame, color = "general", { math.random(255) / 255, math.random(255) / 255, math.random(255) / 255 }
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ x:AddMessage(outputFrame, COMBAT_TEXT_LABEL, color)
+ elseif self == x.framesByName.outgoing then
+ local outputFrame, color = "outgoing", GetRandomSpellColor()
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ local message = x:Abbreviate(math.random(60000), outputFrame)
+
+ local mergeCount = 0
+ if x.db.profile.spells.enableMerger and math.random(3) % 3 == 0 then
+ mergeCount = math.random(17) + 1
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+
+ message = x:GetSpellTextureFormatted(
+ x.db.profile.frames.outgoing.iconsEnabled and GetRandomSpellID() or -1,
+ message,
+ x.db.profile.frames.outgoing, -- frame settings
+ nil, -- iconSize
+ nil, -- strColor
+ mergeCount -- entries
+ )
+ x:AddMessage(outputFrame, message, color)
+ elseif self == x.framesByName.outgoing_healing then
+ local outputFrame, color = "outgoing_healing", GetRandomSpellColor()
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ local message = x:Abbreviate(math.random(60000), outputFrame)
+
+ local mergeCount = 0
+ if x.db.profile.spells.enableMerger and math.random(3) % 3 == 0 then
+ mergeCount = math.random(17) + 1
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+
+ message = x:GetSpellTextureFormatted(
+ x.db.profile.frames.outgoing.iconsEnabled and GetRandomSpellID() or -1,
+ message,
+ x.db.profile.frames.outgoing, -- frame settings
+ nil, -- iconSize
+ nil, -- strColor
+ mergeCount -- entries
+ )
+ x:AddMessage(outputFrame, message, color)
+ elseif self == x.framesByName.critical and math.random(2) % 2 == 0 then
+ local outputFrame, color = "critical", GetRandomSpellColor()
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+
+ local message = x:Options_Global_FormatCritical(x:Abbreviate(math.random(60000), outputFrame))
+
+ local mergeCount = 0
+ if
+ x:Options_SpamMerger_EnableSpamMerger()
+ and (math.random(3) % 3 == 0)
+ and (
+ x:Options_SpamMerger_MergeCriticalsWithOutgoing()
+ or x:Options_SpamMerger_MergeCriticalsByThemselves()
+ )
+ then
+ mergeCount = math.random(17) + 1
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ message = x:GetSpellTextureFormatted(
+ x.db.profile.frames.critical.iconsEnabled and GetRandomSpellID() or -1, -- spellID
+ message, -- message
+ x.db.profile.frames.critical, -- frame settings
+ nil, -- iconSize
+ nil, -- strColor
+ mergeCount -- entries
+ )
+ x:AddMessage(outputFrame, message, color)
+ elseif self == x.framesByName.damage and math.random(2) % 2 == 0 then
+ local outputFrame, color = "damage", { 1, math.random(100) / 255, math.random(100) / 255 }
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ x:AddMessage(outputFrame, "-" .. x:Abbreviate(math.random(100000), "damage"), color)
+ elseif self == x.framesByName.healing and math.random(2) % 2 == 0 then
+ local outputFrame, color = "healing", { 0.1, ((math.random(3) + 1) * 63) / 255, 0.1 }
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ if COMBAT_TEXT_SHOW_FRIENDLY_NAMES == "1" then
+ local message = UnitName("player")
+ local realm = ""
+ if x.db.profile.frames.healing.enableRealmNames then
+ realm = "-" .. GetRealmName()
+ end
+ if x.db.profile.frames.healing.enableClassNames then
+ message = string.format(
+ "|c%s%s%s|r",
+ RAID_CLASS_COLORS[select(2, UnitClass("player"))].colorStr,
+ message,
+ realm
+ )
+ end
+ if x.db.profile.spells.mergeHealing and math.random(2) % 2 == 0 then
+ message = string.format("%s |cffFFFF00x%s|r", message, math.random(17) + 1)
+ end
+ x:AddMessage(outputFrame, "+" .. x:Abbreviate(math.random(90000), "healing") .. " " .. message, color)
+ else
+ x:AddMessage(outputFrame, "+" .. x:Abbreviate(math.random(90000), "healing"), color)
+ end
+ elseif self == x.framesByName.power and math.random(4) % 4 == 0 then
+ local outputFrame = "power"
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ local _, powerToken = UnitPowerType("player")
+ local color = { PowerBarColor[powerToken].r, PowerBarColor[powerToken].g, PowerBarColor[powerToken].b }
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ x:AddMessage(outputFrame, "+" .. x:Abbreviate(math.random(5000), "power") .. " " .. _G[powerToken], color)
+ elseif self == x.framesByName.procs and math.random(8) % 8 == 0 then
+ local outputFrame = "procs"
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ local color = { 1, 1, 0 }
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ x:AddMessage(outputFrame, ERR_SPELL_COOLDOWN, color)
+ elseif self == x.framesByName.loot and math.random(8) % 8 == 0 then
+ local outputFrame = "loot"
+ if not x.db.profile.frames[outputFrame].enabledFrame then
+ x:Clear(outputFrame)
+ if x.db.profile.frames[outputFrame].secondaryFrame ~= 0 then
+ outputFrame = x.framesById[x.db.profile.frames[outputFrame].secondaryFrame]
+ else
+ return
+ end
+ end
+ local color = { 1, 1, 0 }
+ if x.db.profile.frames[outputFrame].customColor then
+ color = x.db.profile.frames[outputFrame].fontColor
+ end
+ if x.db.profile.frames[outputFrame].colorBlindMoney then
+ local g, s, c, message =
+ math.random(100) % 10 ~= 0 and math.random(100) or nil,
+ math.random(100) % 10 ~= 0 and math.random(100) or nil,
+ math.random(100) % 10 ~= 0 and math.random(100) or nil,
+ ""
+ if g then
+ message = tostring(g) .. "|cffFFD700g|r"
+ end
+ if s then
+ if g then
+ message = message .. " " .. tostring(s) .. "|cffC0C0C0s|r"
+ else
+ message = message .. tostring(s) .. "|cffC0C0C0s|r"
+ end
+ end
+ if c then
+ if s or g then
+ message = message .. " " .. tostring(c) .. "|cffB87333c|r"
+ else
+ message = message .. tostring(c) .. "|cffB87333c|r"
+ end
+ end
+ if not g and not s and not c then
+ return
+ end
+ x:AddMessage(outputFrame, MONEY .. ": " .. message, color)
+ else
+ x:AddMessage(
+ outputFrame,
+ MONEY .. ": " .. C_CurrencyInfo.GetCoinTextureString(math.random(1000000)),
+ color
+ )
+ end
+ end
+ end
+ end
+end
+
+function x.ToggleTestMode(hidePopup)
+ if x.configuring then
+ return
+ else
+ if x.testing then
+ x:EndTestMode()
+ else
+ x.testing = true
+
+ -- Start the Test more
+ for framename in pairs(x.db.profile.frames) do
+ local frame = x:GetFrame(framename)
+ frame.nextUpdate = nil
+ frame.lastUpdate = 0
+ frame:SetScript("OnUpdate", x.TestMoreUpdate)
+ end
+
+ -- Test more Popup
+ -- Because this could be called fromt the UI, we need to wait
+ x:HideConfigTool(true)
+
+ if type(hidePopup) == "boolean" and hidePopup then
+ return
+ else
+ StaticPopup_Show("XCT_PLUS_TESTMODE")
+ end
+ end
+ end
+end
+
+function x:EndTestMode()
+ x.testing = false
+
+ -- Stop the Test more
+ for framename in pairs(x.db.profile.frames) do
+ local frame = x:GetFrame(framename)
+ frame:SetScript("OnUpdate", nil)
+ frame:Clear()
+ end
+
+ StaticPopup_Hide("XCT_PLUS_TESTMODE")
+end
+
+function x.RestoreAllDefaults()
+ LibStub("AceConfigDialog-3.0"):Close(ADDON_NAME)
+ GameTooltip:Hide()
+ StaticPopup_Show("XCT_PLUS_RESET_SETTINGS")
+end
+
+-- Popups
+StaticPopupDialogs["XCT_PLUS_CONFIGURING"] = {
+ text = "Configuring xCT+\nType: |cffFF0000/xct lock|r to save changes",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = SAVE_CHANGES,
+ button2 = CANCEL,
+ OnAccept = function()
+ x:SaveAllFrames()
+ x:EndConfigMode()
+ x:ShowConfigTool()
+ x:Print("Frames have been saved. Please fasten your seat belts.")
+ end,
+ OnCancel = function()
+ x:UpdateFrames()
+ x:EndConfigMode()
+ x:ShowConfigTool()
+ end,
+ hideOnEscape = false,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_TESTMODE"] = {
+ text = "xCT+ Test Mode",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = "Stop",
+ OnAccept = function()
+ x:EndTestMode()
+ x:ShowConfigTool()
+ end,
+ hideOnEscape = true,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_RESET_SETTINGS"] = {
+ text = "Are your certain you want to erase |cffFF0000ALL|r your xCT+ settings?",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = "|cffFF0000ERASE ALL!!|r",
+ button2 = CANCEL,
+ OnAccept = function()
+ xCTSavedDB = nil
+ ReloadUI()
+ end,
+ OnCancel = function()
+ x:ShowConfigTool()
+ end,
+ hideOnEscape = true,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_HIDE_IN_COMBAT"] = {
+ text = "|cffFFFF00Disable the|r |cff798BDDHide Config in Combat|r|cffFFFF00 feature?|r\n\n\n|cffFF0000WARNING:|r By disabling this protection you risk |cffFF8000tainting|r your UI. In some cases, you will need to type: '|cff798BDD/reload|r' in order to change |cff10FF40glyphs|r or |cff10FF40talents|r and to place |cff10FF40world markers|r.\n",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = CONTINUE,
+ button2 = REVERT,
+ OnCancel = function()
+ x.db.profile.hideConfig = true
+ -- TODO Why are we doing with this?
+ x:RefreshConfig()
+ end,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_DB_CLEANUP_1"] = {
+ text = "|cff798BDDxCT+ Spring Cleaning|r\n\nHello, |cffFFFF00xCT|r|cffFF0000+|r needed to cleanup some |cffFF0000old or removed spell entries|r from the spam merger. |cffFFFF00Those settings needed to be reset|r. The rest of your profile settings |cff22FF44remains the same|r.\n\nSorry for this inconvenience.\n\n",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = OKAY .. "!",
+ button2 = "Don't Show Again",
+ hideOnEscape = true,
+
+ OnCancel = function()
+ x.db.global.dontShowDBCleaning = true
+ end,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_FORCE_CVAR_UPDATE"] = {
+ text = "|cff798BDDxCT+|r performed an action that requires it to update some |cffFFFF00Combat Text|r related |cffFF8000CVars|r. It is |cff20DD40highly recommened|r you reload your UI before changing any more settings.",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = "Later",
+ button2 = "Reload UI Now",
+ OnCancel = ReloadUI,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
+
+StaticPopupDialogs["XCT_PLUS_SUGGEST_MULTISTRIKE_OFF"] = {
+ text = "",
+}
+
+StaticPopupDialogs["XCT_PLUS_DB_CLEANUP_2"] = {
+ text = "|cffD7DF23xCT+ Legion Clean Up|r\n\nHello Again,\n\n I am sorry to inform you that |cffFFFF00xCT|r|cffFF0000+|r needs to\n\n|cffFF0000COMPLETELY RESET YOUR PROFILE|r\n\n back to the original defaults. \n\nI know this may significantly inconvenience many of you, but after much deliberation, the profile reset is the only way to properly prepare your profile for Legion.\n\n|cffFFFF00We will need to |r|cff798BDDReload Your UI|r|cffFFFF00 after we |cff798BDDReset Your Profile|r|cffFFFF00. Press the button below to continue...\n\n|cffaaaaaa(Your saved vars have NOT been reset yet and you may revert to an older version of xCT+ at this time by simply exiting the game, but that is not recommended)|r",
+ timeout = 0,
+ whileDead = 1,
+
+ button1 = "Exit WoW",
+ button2 = "Reset Profile and Reload UI",
+
+ OnAccept = Quit,
+ OnCancel = function()
+ x:Print("Resetting UI")
+ x:CleanUpForLegion()
+ end,
+
+ -- Taint work around
+ preferredIndex = 3,
+}
diff --git a/xCT+/modules/grid.lua b/xCT+/modules/grid.lua
new file mode 100644
index 00000000..a3422fcc
--- /dev/null
+++ b/xCT+/modules/grid.lua
@@ -0,0 +1,147 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+
+local mfloor = math.floor
+
+-- Shorten my handle
+local x = addon.engine
+local AlignGrid
+
+function x:LoadAlignmentGrid()
+ AlignGrid = CreateFrame("Frame", nil, UIParent)
+ AlignGrid:SetAllPoints(UIParent)
+ local boxSize = 32
+
+ -- Get the current screen resolution, Mid-points, and the total number of lines
+ local ResX, ResY = mfloor(UIParent:GetWidth() + 0.5), mfloor(UIParent:GetHeight() + 0.5)
+
+ local midX, midY = ResX / 2, ResY / 2
+ local iLinesLeftRight, iLinesTopBottom = midX / boxSize, midY / boxSize
+
+ -- Vertical Bars
+ for i = 1, iLinesLeftRight do
+ -- Vertical Bars to the Left of the Center
+ local tt1 = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ tt1:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+
+ if i % 4 == 0 then
+ --tt1:SetTexture(.9, .9, .1, .6)
+ tt1:SetVertexColor(0.9, 0.9, 0.1)
+ tt1:SetAlpha(0.6)
+ elseif i % 2 == 0 then
+ --tt1:SetTexture(.4, .4, .4, .6)
+ tt1:SetVertexColor(0.4, 0.4, 0.4)
+ tt1:SetAlpha(0.6)
+ else
+ --tt1:SetTexture(.4, .4, .4, .4)
+ tt1:SetVertexColor(0.4, 0.4, 0.4)
+ tt1:SetAlpha(0.4)
+ end
+
+ tt1:SetPoint("TOP", AlignGrid, "TOP", -i * boxSize + 1, 0)
+ tt1:SetPoint("BOTTOM", AlignGrid, "BOTTOM", -i * boxSize + 1, 0)
+ tt1:SetWidth(1)
+
+ -- Vertical Bars to the Right of the Center
+ local tt2 = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ tt2:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+
+ if i % 4 == 0 then
+ --tt2:SetTexture(.9, .9, .1, .6)
+ tt2:SetVertexColor(0.9, 0.9, 0.1)
+ tt2:SetAlpha(0.6)
+ elseif i % 2 == 0 then
+ --tt2:SetTexture(.4, .4, .4, .6)
+ tt2:SetVertexColor(0.4, 0.4, 0.4)
+ tt2:SetAlpha(0.6)
+ else
+ --tt2:SetTexture(.4, .4, .4, .4)
+ tt2:SetVertexColor(0.4, 0.4, 0.4)
+ tt2:SetAlpha(0.4)
+ end
+
+ tt2:SetPoint("TOP", AlignGrid, "TOP", i * boxSize + 1, 0)
+ tt2:SetPoint("BOTTOM", AlignGrid, "BOTTOM", i * boxSize + 1, 0)
+ tt2:SetWidth(1)
+ end
+
+ -- Horizontal Bars
+ for i = 1, iLinesTopBottom do
+ -- Horizontal Bars to the Below of the Center
+ local tt3 = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ tt3:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ if i % 4 == 0 then
+ --tt3:SetTexture(.9, .9, .1, .6)
+ tt3:SetVertexColor(0.9, 0.9, 0.1)
+ tt3:SetAlpha(0.6)
+ elseif i % 2 == 0 then
+ --tt3:SetTexture(.4, .4, .4, .6)
+ tt3:SetVertexColor(0.4, 0.4, 0.4)
+ tt3:SetAlpha(0.6)
+ else
+ --tt3:SetTexture(.4, .4, .4, .4)
+ tt3:SetVertexColor(0.4, 0.4, 0.4)
+ tt3:SetAlpha(0.4)
+ end
+ tt3:SetPoint("LEFT", AlignGrid, "LEFT", 0, -i * boxSize - 1)
+ tt3:SetPoint("RIGHT", AlignGrid, "RIGHT", 0, -i * boxSize - 1)
+ tt3:SetHeight(1)
+
+ -- Horizontal Bars to the Above of the Center
+ local tt4 = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ tt4:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ if i % 4 == 0 then
+ --tt4:SetTexture(.9, .9, .1, .6)
+ tt4:SetVertexColor(0.9, 0.9, 0.1)
+ tt4:SetAlpha(0.6)
+ elseif i % 2 == 0 then
+ --tt4:SetTexture(.4, .4, .4, .6)
+ tt4:SetVertexColor(0.4, 0.4, 0.4)
+ tt4:SetAlpha(0.6)
+ else
+ --tt4:SetTexture(.4, .4, .4, .4)
+ tt4:SetVertexColor(0.4, 0.4, 0.4)
+ tt4:SetAlpha(0.4)
+ end
+ tt4:SetPoint("LEFT", AlignGrid, "LEFT", 0, i * boxSize - 1)
+ tt4:SetPoint("RIGHT", AlignGrid, "RIGHT", 0, i * boxSize - 1)
+ tt4:SetHeight(1)
+ end
+
+ --Create the Vertical Middle Bar
+ local tta = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ tta:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ --tta:SetTexture(1, 0, 0, .6)
+ tta:SetVertexColor(1, 0, 0)
+ tta:SetAlpha(0.6)
+ tta:SetPoint("TOP", AlignGrid, "TOP", 0, 0)
+ tta:SetPoint("BOTTOM", AlignGrid, "BOTTOM", 0, 0)
+ tta:SetWidth(2)
+
+ --Create the Horizontal Middle Bar
+ local ttb = AlignGrid:CreateTexture(nil, "BACKGROUND")
+ ttb:SetTexture("Interface\\BUTTONS\\WHITE8X8.blp")
+ --ttb:SetTexture(1, 0, 0, .6)
+ ttb:SetVertexColor(1, 0, 0)
+ ttb:SetAlpha(0.6)
+ ttb:SetPoint("LEFT", AlignGrid, "LEFT", 0, 0)
+ ttb:SetPoint("RIGHT", AlignGrid, "RIGHT", 0, 0)
+ ttb:SetHeight(2)
+
+ AlignGrid:Hide()
+
+ x.AlignGrid = AlignGrid
+end
diff --git a/xCT+/modules/options.lua b/xCT+/modules/options.lua
new file mode 100644
index 00000000..57afdaba
--- /dev/null
+++ b/xCT+/modules/options.lua
@@ -0,0 +1,527 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local _, addon = ...
+local x = addon.engine
+
+-- Global Frame Settings
+function x:Options_Global_ClearWhenLeavingCombat()
+ return x.db.profile.frameSettings.clearLeavingCombat
+end
+
+function x:Options_Global_CritPrefix()
+ return x.db.profile.megaDamage.critPrefix
+end
+
+function x:Options_Global_CritSuffix()
+ return x.db.profile.megaDamage.critSuffix
+end
+
+function x:Options_Global_FormatCritical(amount)
+ return x.db.profile.megaDamage.critPrefix .. amount .. x.db.profile.megaDamage.critSuffix
+end
+
+-- Frame "General"
+function x:Options_General_ShowHonor()
+ return x.db.profile.frames.general.showHonorGains
+end
+
+function x:Options_General_ShowReputationChanges()
+ return x.db.profile.frames.general.showRepChanges
+end
+
+function x:Options_General_ShowLowManaAndHealth()
+ return x.db.profile.frames.general.showLowManaHealth
+end
+
+function x:Options_General_ShowCombatState()
+ return x.db.profile.frames.general.showCombatState
+end
+
+function x:Options_General_ShowInterrupts()
+ return x.db.profile.frames.general.showInterrupts
+end
+
+function x:Options_General_ShowDispells()
+ return x.db.profile.frames.general.showDispells
+end
+
+function x:Options_General_ShowIncomingDispells()
+ return x.db.profile.frames.general.showIncomingDispells
+end
+
+function x:Options_General_ShowPartyKill()
+ return x.db.profile.frames.general.showPartyKills
+end
+
+function x:Options_General_ShowBuffGainsAndFades()
+ return x.db.profile.frames.general.showBuffs
+end
+
+function x:Options_General_ShowDebuffGainsAndFades()
+ return x.db.profile.frames.general.showDebuffs
+end
+
+function x:Options_General_ShowProfessionSkillups()
+ return x.db.profile.frames.general.showProfessionSkillups
+end
+
+-- Frame "Outgoing Damage"
+function x:Options_Outgoing_ShowDamage()
+ return x.db.profile.frames.outgoing.enableOutDmg
+end
+
+function x:Options_Outgoing_ShowPetDamage()
+ return x.db.profile.frames.outgoing.enablePetDmg
+end
+
+function x:Options_Outgoing_ShowVehicleDamage()
+ return x.db.profile.frames.outgoing.enableVehicleDmg
+end
+
+function x:Options_Outgoing_ShowKillCommandAsPlayerDamage()
+ return x.db.profile.frames.outgoing.enableKillCommand
+end
+
+function x:Options_Outgoing_ShowAutoAttack()
+ return x.db.profile.frames.outgoing.enableAutoAttack_Outgoing
+end
+
+function x:Options_Outgoing_ShowPetAutoAttack()
+ return x.db.profile.frames.outgoing.enablePetAutoAttack_Outgoing
+end
+
+function x:Options_Outgoing_ShowDots()
+ return x.db.profile.frames.outgoing.enableDotDmg
+end
+
+function x:Options_Outgoing_ShowImmunes()
+ return x.db.profile.frames.outgoing.enableImmunes
+end
+
+function x:Options_Outgoing_ShowMisses()
+ return x.db.profile.frames.outgoing.enableMisses
+end
+
+function x:Options_Outgoing_ShowAbsorbedDamageAsNormalDamage()
+ return x.db.profile.frames.outgoing.enableAbsorbs
+end
+
+function x:Options_Outgoing_ShowPartialMisses()
+ return x.db.profile.frames.outgoing.enablePartialMisses
+end
+
+-- Frame "Outgoing Damage (Criticals)"
+function x:Options_Critical_ShowAutoAttack()
+ return x.db.profile.frames.critical.enableAutoAttack_Critical
+end
+
+function x:Options_Critical_PrefixAutoAttack()
+ return x.db.profile.frames.critical.prefixAutoAttack_Critical
+end
+
+function x:Options_Critical_ShowPetCrits()
+ return x.db.profile.frames.critical.petCrits
+end
+
+-- Frame "Outgoing Healing"
+function x:Options_OutgoingHealing_ShowHots()
+ return x.db.profile.frames.outgoing_healing.enableHots
+end
+
+function x:Options_OutgoingHealing_ShowOverhealing()
+ return x.db.profile.frames.outgoing_healing.enableOverhealing
+end
+
+function x:Options_OutgoingHealing_FormatOverhealing()
+ return x.db.profile.frames.outgoing_healing.enableOverhealingFormat
+end
+
+function x:Options_OutgoingHealing_OverhealingPrefix()
+ return x.db.profile.frames.outgoing_healing.overhealingPrefix
+end
+
+function x:Options_OutgoingHealing_OverhealingPostfix()
+ return x.db.profile.frames.outgoing_healing.overhealingPostfix
+end
+
+function x:Options_OutgoingHealing_FormatOverhealingAmount(amount)
+ return x.db.profile.frames.outgoing_healing.overhealingPrefix .. amount .. x.db.profile.frames.outgoing_healing.overhealingPostfix
+end
+
+function x:Options_OutgoingHealing_SubtractOverhealing()
+ return x.db.profile.frames.outgoing_healing.enableOverhealingSubtraction
+end
+
+function x:Options_OutgoingHealing_HideAbsorbedHealing()
+ return x.db.profile.frames.outgoing_healing.hideAbsorbedOutgoingHeals
+end
+
+-- Frame "Incoming Damage"
+function x:Options_IncomingDamage_ShowMissTypes()
+ return x.db.profile.frames.damage.showDodgeParryMiss
+end
+
+function x:Options_IncomingDamage_ShowReductions()
+ return x.db.profile.frames.damage.showDamageReduction
+end
+
+-- Frame "Incoming Healing"
+function x:Options_IncomingHealing_ShowOnlyMyHeals()
+ return x.db.profile.frames.healing.showOnlyMyHeals
+end
+
+function x:Options_IncomingHealing_ShowOnlyMyPetsHeals()
+ return x.db.profile.frames.healing.showOnlyPetHeals
+end
+
+function x:Options_IncomingHealing_ShowOverHealing()
+ return x.db.profile.frames.healing.enableOverHeal
+end
+
+function x:Options_IncomingHealing_HideAbsorbedHealing()
+ return x.db.profile.frames.healing.hideAbsorbedHeals
+end
+
+-- Frame "Class Power"
+function x:Options_Power_ShowGains()
+ return x.db.profile.frames.power.showEnergyGains
+end
+
+function x:Options_Power_ShowEnergyTypes()
+ return x.db.profile.frames.power.showEnergyType
+end
+
+function x:Options_Power_ShowResource(resource)
+ if x.db.profile.frames.power["disableResource_" .. resource] ~= nil then
+ return x.db.profile.frames.power["disableResource_" .. resource]
+ end
+
+ return true
+end
+
+-- Frame "Special Effects (Procs)"
+function x:Options_Procs_ShowProcs()
+ return x.db.profile.frames.procs.enabledFrame
+end
+
+-- Frame "Loot, Currency & Money"
+function x:Options_Loot_ShowItems()
+ return x.db.profile.frames.loot.showItems
+end
+
+function x:Options_Loot_ShowItemTypes()
+ return x.db.profile.frames.loot.showItemTypes
+end
+
+function x:Options_Loot_ShowMoney()
+ return x.db.profile.frames.loot.showMoney
+end
+
+function x:Options_Loot_ShowCurrency()
+ return x.db.profile.frames.loot.showCurrency
+end
+
+function x:Options_Loot_ShowItemTotals()
+ return x.db.profile.frames.loot.showItemTotal
+end
+
+function x:Options_Loot_ShowCraftedItems()
+ return x.db.profile.frames.loot.showCrafted
+end
+
+function x:Options_Loot_ShowQuestItems()
+ return x.db.profile.frames.loot.showQuest
+end
+
+function x:Options_Loot_ShowPurchasedItems()
+ return x.db.profile.frames.loot.showPurchased
+end
+
+function x:Options_Loot_ShowColorBlindMoney()
+ return x.db.profile.frames.loot.colorBlindMoney
+end
+
+function x:Options_Loot_ItemQualityFilter()
+ return x.db.profile.frames.loot.filterItemQuality
+end
+
+function x:Options_Loot_ShowIcons()
+ return x.db.profile.frames.loot.iconsEnabled
+end
+
+function x:Options_Loot_IconSize()
+ return x.db.profile.frames.loot.iconsSize
+end
+
+function x:Options_Loot_EnableSpacerIcons()
+ return x.db.profile.frames.loot.spacerIconsEnabled
+end
+
+-- Spam Merger
+function x:Options_SpamMerger_EnableSpamMerger()
+ return x.db.profile.spells.enableMerger
+end
+
+function x:Options_SpamMerger_FallbackInterval()
+ return x.db.profile.spells.mergeEverythingInterval
+end
+
+function x:Options_SpamMerger_IncomingHealingInterval()
+ return x.db.profile.spells.mergeIncomingHealingInterval or 0
+end
+
+function x:Options_SpamMerger_IncomingDamageInterval()
+ return x.db.profile.spells.mergeIncomingDamageInterval or 0
+end
+
+function x:Options_SpamMerger_PetAttackInterval()
+ return x.db.profile.spells.mergePetInterval
+end
+
+function x:Options_SpamMerger_MergeCriticalsWithOutgoing()
+ return x.db.profile.spells.mergeCriticalsWithOutgoing
+end
+
+function x:Options_SpamMerger_MergeCriticalsByThemselves()
+ return x.db.profile.spells.mergeCriticalsByThemselves
+end
+
+function x:Options_SpamMerger_DontMergeCriticals()
+ return x.db.profile.spells.mergeDontMergeCriticals
+end
+
+function x:Options_SpamMerger_HideMergedCriticals()
+ return x.db.profile.spells.mergeHideMergedCriticals
+end
+
+function x:Options_SpamMerger_DispellInterval()
+ return x.db.profile.spells.mergeDispellInterval or 0
+end
+
+function x:Options_SpamMerger_ReputationInterval()
+ return x.db.profile.spells.mergeReputationInterval or 0
+end
+
+function x:Options_SpamMerger_IncomingMissesInterval()
+ return x.db.profile.spells.mergeIncomingMissesInterval or 0
+end
+
+function x:Options_SpamMerger_OutgoingDamageMissesInterval()
+ return x.db.profile.spells.mergeOutgoingDamageMissesInterval or 0
+end
+
+function x:Options_SpamMerger_SpellInterval(spellId)
+ if x.db.profile.spells.merge[spellId] ~= nil and x.db.profile.spells.merge[spellId].interval ~= nil then
+ return x.db.profile.spells.merge[spellId].interval
+ end
+
+ return x:Options_SpamMerger_FallbackInterval()
+end
+
+-- Spell Filter
+function x:Options_Filter_PlayerPowerMinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterPowerValue)
+end
+
+function x:Options_Filter_OutgoingDamage_Noncritical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterOutgoingDamageValue)
+end
+
+function x:Options_Filter_OutgoingDamage_Critical_UseOwnThreshold()
+ return x.db.profile.spellFilter.filterOutgoingDamageCritEnabled
+end
+
+function x:Options_Filter_OutgoingDamage_Critical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterOutgoingDamageCritValue)
+end
+
+function x:Options_Filter_OutgoingDamage_HideEvent(value, critical)
+ if critical and x:Options_Filter_OutgoingDamage_Critical_UseOwnThreshold() then
+ return tonumber(x:Options_Filter_OutgoingDamage_Critical_MinimumThreshold()) > math.abs(value)
+ end
+
+ return tonumber(x:Options_Filter_OutgoingDamage_Noncritical_MinimumThreshold()) > math.abs(value)
+end
+
+function x:Options_Filter_OutgoingHealing_Noncritical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterOutgoingHealingValue)
+end
+
+function x:Options_Filter_OutgoingHealing_Critical_UseOwnThreshold()
+ return x.db.profile.spellFilter.filterOutgoingHealingCritEnabled
+end
+
+function x:Options_Filter_OutgoingHealing_Critical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterOutgoingHealingCritValue)
+end
+
+function x:Options_Filter_OutgoingHealing_HideEvent(value, critical)
+ if critical and x:Options_Filter_OutgoingHealing_Critical_UseOwnThreshold() then
+ return tonumber(x:Options_Filter_OutgoingHealing_Critical_MinimumThreshold()) > math.abs(value)
+ end
+
+ return tonumber(x:Options_Filter_OutgoingHealing_Noncritical_MinimumThreshold()) > math.abs(value)
+end
+
+function x:Options_Filter_IncomingDamage_Noncritical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterIncomingDamageValue)
+end
+
+function x:Options_Filter_IncomingDamage_Critical_UseOwnThreshold()
+ return x.db.profile.spellFilter.filterIncomingDamageCritEnabled
+end
+
+function x:Options_Filter_IncomingDamage_Critical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterIncomingDamageCritValue)
+end
+
+function x:Options_Filter_IncomingDamage_HideEvent(value, critical)
+ if critical and x:Options_Filter_IncomingDamage_Critical_UseOwnThreshold() then
+ return tonumber(x:Options_Filter_IncomingDamage_Critical_MinimumThreshold()) > math.abs(value)
+ end
+
+ return tonumber(x:Options_Filter_IncomingDamage_Noncritical_MinimumThreshold()) > math.abs(value)
+end
+
+function x:Options_Filter_IncomingHealing_Noncritical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterIncomingHealingValue)
+end
+
+function x:Options_Filter_IncomingHealing_Critical_UseOwnThreshold()
+ return x.db.profile.spellFilter.filterIncomingHealingCritEnabled
+end
+
+function x:Options_Filter_IncomingHealing_Critical_MinimumThreshold()
+ return tostring(x.db.profile.spellFilter.filterIncomingHealingCritValue)
+end
+
+function x:Options_Filter_IncomingHealing_HideEvent(value, critical)
+ if critical and x:Options_Filter_IncomingHealing_Critical_UseOwnThreshold() then
+ return tonumber(x:Options_Filter_IncomingHealing_Critical_MinimumThreshold()) > math.abs(value)
+ end
+
+ return tonumber(x:Options_Filter_IncomingHealing_Noncritical_MinimumThreshold()) > math.abs(value)
+end
+
+function x:Options_Filter_TrackSpells()
+ return x.db.profile.spellFilter.trackSpells
+end
+
+function x:Options_Filter_BuffWhitelist()
+ return x.db.profile.spellFilter.whitelistBuffs
+end
+
+function x:Options_Filter_HideBuff(name)
+ local hidden = x.db.profile.spellFilter.listBuffs[name]
+ if x:Options_Filter_BuffWhitelist() then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideDebuff(name)
+ local hidden = x.db.profile.spellFilter.listDebuffs[name]
+ if x.db.profile.spellFilter.whitelistDebuffs then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideProc(name)
+ local hidden = x.db.profile.spellFilter.listProcs[name]
+ if x.db.profile.spellFilter.whitelistProcs then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideItem(name)
+ local hidden = x.db.profile.spellFilter.listItems[name]
+ if x.db.profile.spellFilter.whitelistItems then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideSpell(spellId)
+ local hidden = x.db.profile.spellFilter.listSpells[tostring(spellId)]
+ if x.db.profile.spellFilter.whitelistSpells then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideIncomingDamage(spellId)
+ local hidden = x.db.profile.spellFilter.listDamage[tostring(spellId)]
+ if x.db.profile.spellFilter.whitelistDamage then
+ return not hidden
+ end
+ return hidden
+end
+
+function x:Options_Filter_HideIncomingHealing(spellId)
+ local hidden = x.db.profile.spellFilter.listHealing[tostring(spellId)]
+ if x.db.profile.spellFilter.whitelistHealing then
+ return not hidden
+ end
+ return hidden
+end
+
+-- Other options
+function x:CVar_BypassCVars()
+ return x.db.profile.bypassCVars
+end
+
+function x:ShowAutoAttackIcons(frameName)
+ return x.db.profile.frames[frameName] and x.db.profile.frames[frameName].iconsEnabledAutoAttack or false
+end
+
+function x:GetFrame(frameName, bypassUpdate)
+ if not bypassUpdate then
+ x:UpdateFrames(frameName)
+ end
+ return x.framesByName[frameName]
+end
+
+function x:GetFrameSettings(frameName)
+ local frameSettings = x.db.profile.frames[frameName]
+ if not frameSettings then
+ return
+ end
+
+ if frameSettings.enabledFrame then
+ return frameSettings
+ end
+
+ if not frameSettings.secondaryFrame or frameSettings.secondaryFrame == 0 then
+ -- Neither the frame itself is enabled, nor its output is redirected to another frame
+ return nil
+ end
+
+ local secondaryFrameName = x.framesById[frameSettings.secondaryFrame]
+ if not secondaryFrameName then
+ self:Print("Invalid secondary frame name", secondaryFrameName, "for frame", frameName)
+ return nil
+ end
+
+ frameSettings = x.db.profile.frames[secondaryFrameName]
+ if frameSettings.enabledFrame then
+ -- Secondary frame is enabled and ready to go!
+ return frameSettings
+ end
+
+ -- The user chose a secondary frame, but its disabled!
+ return nil
+end
diff --git a/xCT+/xCT+.toc b/xCT+/xCT+.toc
new file mode 100644
index 00000000..d66a1bea
--- /dev/null
+++ b/xCT+/xCT+.toc
@@ -0,0 +1,78 @@
+## Interface: 110205
+## Title: xCT+
+## Notes: An Add-On that can out-perform or enhance the default Floating Combat Text!
+## Notes-ruRU: Аддон, который может превзойти или улучшить стандартный всплывающий текст боя!
+## Notes-zhCN: 可以超越或增強預設浮動戰鬥文字的附加元件!
+## Author: Dandruff-Stormreaver US, Feylynn-Antonidas EU
+## Version: @project-version@
+## RequiredDeps: Blizzard_CombatText
+## OptionalDeps: LibSink-2.0
+## SavedVariables: xCTSavedDB
+## X-Curse-Project-ID: 35541
+## IconTexture: 982414
+## Category-enUS: Combat
+## Category-deDE: Kampf
+## Category-esES: Combate
+## Category-esMX: Combate
+## Category-frFR: Combat
+## Category-itIT: Combattimento
+## Category-koKR: 전투
+## Category-ptBR: Combate
+## Category-ruRU: Бой
+## Category-zhCN: 战斗
+## Category-zhTW: 戰鬥
+
+#@no-lib-strip@
+libs\LibStub\LibStub.lua
+libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
+
+libs\AceAddon-3.0\AceAddon-3.0.xml
+libs\AceConsole-3.0\AceConsole-3.0.xml
+Libs\AceLocale-3.0\AceLocale-3.0.xml
+libs\AceDB-3.0\AceDB-3.0.xml
+libs\AceDBOptions-3.0\AceDBOptions-3.0.xml
+libs\AceTimer-3.0\AceTimer-3.0.xml
+libs\AceEvent-3.0\AceEvent-3.0.xml
+
+libs\LibSharedMedia-3.0\lib.xml
+libs\AceGUI-3.0\AceGUI-3.0.xml
+libs\AceGUI-3.0-SharedMediaWidgets\widget.xml
+
+libs\AceConfig-3.0\AceConfig-3.0.xml
+#@end-no-lib-strip@
+
+# Locales first
+# enUS first (because its the default!)
+locales\enUS.lua
+locales\zhCN.lua
+locales\ruRU.lua
+
+init.lua
+
+config\merge_helpers.lua
+config\merge_class.lua
+
+# The order here is relevant!
+config\merge_basic_spells.lua
+config\merge_dragonflight.lua
+config\merge_shadowlands.lua
+config\merge_bfa.lua
+config\merge_legion.lua
+config\merge_wod.lua
+config\merge_mop.lua
+
+config\merge_race.lua
+config\profile.lua
+
+libs\UTF8\include.xml
+libs\xCombatParser-1.0\xCombatParser-1.0.lua
+
+media\media.lua
+media\pet_icons.lua
+
+modules\blizzard.lua
+modules\frames.lua
+modules\combattext.lua
+modules\options.lua
+modules\grid.lua
+modules\core.lua
diff --git a/xCT+Options/init.lua b/xCT+Options/init.lua
new file mode 100644
index 00000000..ffb0de4b
--- /dev/null
+++ b/xCT+Options/init.lua
@@ -0,0 +1,257 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, optionsAddon = ...
+optionsAddon.engine = LibStub("AceAddon-3.0"):NewAddon(AddonName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
+
+local xo = optionsAddon.engine
+
+-- Make the main Addon globally accessible
+xCT_Plus_Options = optionsAddon
+
+-- This allows us to create our config dialog
+local AceGUI = LibStub("AceGUI-3.0")
+local AC = LibStub("AceConfig-3.0")
+local ACD = LibStub("AceConfigDialog-3.0")
+
+-- Gets called directly after the addon is fully loaded.
+function xo:OnInitialize()
+ if not xCT_Plus then
+ self:Print("xCT not found. Please load it first.")
+ end
+
+ self.CLASS_NAMES = {
+ ["DEATHKNIGHT"] = {
+ [0] = 0, -- All Specs
+ [250] = 1, -- Blood
+ [251] = 2, -- Frost
+ [252] = 3, -- Unholy
+ },
+ ["DEMONHUNTER"] = {
+ [0] = 0, -- All Specs
+ [577] = 1, -- Havoc
+ [581] = 2, -- Vengeance
+ },
+ ["DRUID"] = {
+ [0] = 0, -- All Specs
+ [102] = 1, -- Balance
+ [103] = 2, -- Feral
+ [104] = 3, -- Guardian
+ [105] = 4, -- Restoration
+ },
+ ["EVOKER"] = {
+ [0] = 0, -- All Specs
+ [1467] = 1, -- Devastation
+ [1468] = 2, -- Preservation
+ [1473] = 3, -- Augmentation
+ },
+ ["HUNTER"] = {
+ [0] = 0, -- All Specs
+ [253] = 1, -- Beast Mastery
+ [254] = 2, -- Marksmanship
+ [255] = 3, -- Survival
+ },
+ ["MAGE"] = {
+ [0] = 0, -- All Specs
+ [62] = 1, -- Arcane
+ [63] = 2, -- Fire
+ [64] = 3, -- Frost
+ },
+ ["MONK"] = {
+ [0] = 0, -- All Specs
+ [268] = 1, -- Brewmaster
+ [269] = 2, -- Windwalker
+ [270] = 3, -- Mistweaver
+ },
+ ["PALADIN"] = {
+ [0] = 0, -- All Specs
+ [65] = 1, -- Holy
+ [66] = 2, -- Protection
+ [70] = 3, -- Retribution
+ },
+ ["PRIEST"] = {
+ [0] = 0, -- All Specs
+ [256] = 1, -- Discipline
+ [257] = 2, -- Holy
+ [258] = 3, -- Shadow
+ },
+ ["ROGUE"] = {
+ [0] = 0, -- All Specs
+ [259] = 1, -- Assassination
+ [260] = 2, -- Combat
+ [261] = 3, -- Subtlety
+ },
+ ["SHAMAN"] = {
+ [0] = 0, -- All Specs
+ [262] = 1, -- Elemental
+ [263] = 2, -- Enhancement
+ [264] = 3, -- Restoration
+ },
+ ["WARLOCK"] = {
+ [0] = 0, -- All Specs
+ [265] = 1, -- Affliction
+ [266] = 2, -- Demonology
+ [267] = 3, -- Destruction
+ },
+ ["WARRIOR"] = {
+ [0] = 0, -- All Specs
+ [71] = 1, -- Arms
+ [72] = 2, -- Fury
+ [73] = 3, -- Protection
+ },
+ }
+
+ -- Initialize the options
+ xCT_Plus.engine:InitOptionsTable()
+
+ -- Add the profile options to my dialog config
+ optionsAddon.optionsTable.args.Profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(xCT_Plus.engine.db)
+
+ -- Register the Options
+ ACD:SetDefaultSize(AddonName, 803, 560)
+ AC:RegisterOptionsTable(AddonName, optionsAddon.optionsTable)
+end
+
+-- Profile Updated, need to refresh important stuff
+local function RefreshConfig()
+ -- Clean up the Profile
+ xCT_Plus.engine:CompatibilityLogic(true)
+ xCT_Plus.engine:CacheColors()
+ xCT_Plus.engine:UpdateFrames()
+
+ -- Will this fix the profile issue?
+ xo:GenerateSpellSchoolColors()
+ xo:GenerateColorOptions()
+
+ -- Update combat text engine CVars
+ xCT_Plus.engine:UpdateCVar(true)
+
+ collectgarbage()
+end
+
+local function ProfileReset()
+ -- Clean up the Profile
+ xCT_Plus.engine:CompatibilityLogic(false)
+ xCT_Plus.engine:CacheColors()
+ xCT_Plus.engine:UpdateFrames()
+
+ collectgarbage()
+end
+
+-- Gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present.
+function xo:OnEnable()
+ -- Had to pass the explicit method into here, not sure why
+ xCT_Plus.engine.db.RegisterCallback(self, "OnProfileChanged", RefreshConfig)
+ xCT_Plus.engine.db.RegisterCallback(self, "OnProfileCopied", RefreshConfig)
+ xCT_Plus.engine.db.RegisterCallback(self, "OnProfileReset", ProfileReset)
+
+ self:RegisterEvent("PLAYER_REGEN_DISABLED", "onEnteringCombat")
+ self:RegisterEvent("PLAYER_REGEN_ENABLED", "onLeavingCombat")
+
+ self.openConfigAfterCombat = false
+end
+
+function xo:ToggleConfigTool()
+ if self.isConfigToolOpen then
+ xo:HideConfigTool()
+ else
+ xo:ShowConfigTool()
+ end
+end
+
+local function myContainer_OnRelease()
+ AceGUI:Release(self.optionsFrame)
+ self.optionsFrame = nil
+
+ self.isConfigToolOpen = false
+end
+
+function xo:ShowConfigTool(...)
+ if self.isConfigToolOpen then
+ -- Already open
+ return
+ end
+
+ if InCombatLockdown() and xCT_Plus.engine.db.profile.hideConfig then
+ self:Print("Will open the |cff798BDDConfiguration Tool|r after combat.")
+ self.openConfigAfterCombat = true
+ return
+ end
+
+ self.isConfigToolOpen = true
+
+ if self.optionsFrame then
+ self.optionsFrame:Hide()
+ end
+
+ -- Register my AddOn for Escape keypresses
+ self.optionsFrame = AceGUI:Create("Frame")
+ self.optionsFrame.frame:SetScript("OnHide", function()
+ xo:HideConfigTool()
+ end)
+
+ _G.xCT_PlusConfigFrame = self.optionsFrame.frame
+ table.insert(UISpecialFrames, "xCT_PlusConfigFrame")
+
+ -- Properly dispose of this frame
+ self.optionsFrame:SetCallback("OnClose", myContainer_OnRelease)
+
+ -- Go through and select all the groups that are relevant to the player
+ if not xo.selectDefaultGroups then
+ xo.selectDefaultGroups = true
+
+ -- Select the player's class, then go back to home
+ ACD:SelectGroup(AddonName, "spells", "classList", xCT_Plus.engine.player.class)
+ ACD:SelectGroup(AddonName, "spells", "mergeOptions")
+ ACD:SelectGroup(AddonName, "Frames")
+ end
+
+ -- If we get a specific path we need to be at
+ if select("#", ...) > 0 then
+ ACD:SelectGroup(AddonName, ...)
+ end
+
+ ACD:Open(AddonName, self.optionsFrame)
+end
+
+function xo:HideConfigTool(wait)
+ -- If the caller says we need to wait a bit, we'll wait!
+ if wait then
+ self:ScheduleTimer("HideConfigTool", 0.1)
+ return
+ end
+
+ self.isConfigToolOpen = false
+
+ if self.optionsFrame then
+ self.optionsFrame:Hide()
+ end
+
+ GameTooltip:Hide()
+end
+
+function xo:onEnteringCombat()
+ if xCT_Plus.engine.db.profile.hideConfig and self.isConfigToolOpen then
+ self.openConfigAfterCombat = true
+ xo:HideConfigTool()
+ end
+end
+
+function xo:onLeavingCombat()
+ if self.openConfigAfterCombat then
+ xo:ShowConfigTool()
+ end
+
+ self.openConfigAfterCombat = false
+end
diff --git a/xCT+Options/locales/enUS.lua b/xCT+Options/locales/enUS.lua
new file mode 100644
index 00000000..77664977
--- /dev/null
+++ b/xCT+Options/locales/enUS.lua
@@ -0,0 +1,521 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "enUS", true, true)
+if not L then return end
+
+L[" Version: %s "] = true
+L["A frame to forward messages to when this frame is disabled."] = true
+L["A list of |cff1AFF1ABuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["A list of |cff1AFF1AProcs|r that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["A list of |cff71d5ffOutgoing Spell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["A list of |cff798BDDItem|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["A list of |cff798BDDSpell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["A list of |cffFF0000Debuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = true
+L["Abbreviate Numbers"] = true
+L["Absorbed Healing"] = true
+L["Add new Buff to filter"] = true
+L["Add new Debuff to filter"] = true
+L["Add new Item to filter"] = true
+L["Add new Proc to filter"] = true
+L["Add new Spell to filter"] = true
+L["Add these character(s) after the amount of a critical hit."] = true
+L["Add these character(s) before the amount of a critical hit."] = true
+L["Add these character(s) to the beginning of the message."] = true
+L["Add these character(s) to the end of the message."] = true
+L["Add via History"] = true
+L["Add via ID"] = true
+L["Add via Name"] = true
+L["Additional Settings"] = true
+L["Adds the spell ID to each message for this session only."] = true
+L["Advanced"] = true
+L["All Text One Color (Override Color Settings)"] = true
+L["Allow Pet Crits"] = true
+L["Allows you to adjust the position of all the xCT+ frames on your screen.\n\nYou can also type: '|cffFF0000/xct lock|r'"] = true
+L["Allows you to bypass xCT+'s CVar engine. This option might help if you have FCT enabled, but it disappears after awhile. Once you set your FCT options, enable this.\n\n|cffFF0000Changing this requires a UI Reload!|r"] = true
+L["Allows you to customize the fade time of each frame."] = true
+L["Allows you to preview xCT+ in order to tweak settings outside of combat.\n\nYou can also type: '|cffFF0000/xct test|r'"] = true
+L["Appearance"] = true
+L["Background |cffFF0000(Lowest)|r"] = true
+L["Beta Testers - Version 3.0.0"] = true
+L["Beta Testers - Version 4.0.0 (Curse)"] = true
+L["Beta Testers - Version 4.0.0 (Tukui)"] = true
+L["Beta Testers - Version 4.3.0+ (Legion)"] = true
+L["Beta Testers - Version 4.4.0+ (Battle for Azeroth)"] = true
+L["Billion Symbol"] = true
+L["Buff Gains/Fades"] = true
+L["Bypass CVar Updates (requires |cffFF0000/reload|r)"] = true
+L["Center"] = true
+L["Change all the text in this frame to a specific color."] = true
+L["Change the color for |cff798BDD%s|r."] = true
+L["Change the source of |cff798BDDKill Command|r to be the |cffFF8000Player|r. This is helpful when you to turn off |cffFF8000Pet|r damage."] = true
+L["Changes the direction that the text travels in the frame."] = true
+L["Check for whitelist, uncheck for blacklist."] = true
+L["Check the resources that you do not wish to be displayed for your character:"] = true
+L["Class Power"] = true
+L["Class Spells"] = true
+L["Clear Frames when leaving combat"] = true
+L["Color Blind Mode"] = true
+L["Color Environment"] = true
+L["Color Player Name"] = true
+L["Color Spell Name"] = true
+L["Color Type"] = true
+L["Color"] = true
+L["Colors of the events"] = true
+L["Colors"] = true
+L["Crafted Items"] = true
+L["Create a comment on Curseforge:"] = true
+L["Create an issue at GitHub:"] = true
+L["Credits"] = true
+L["Critical Hits"] = true
+L["Critical Prefix"] = true
+L["Critical Suffix"] = true
+L["Criticals that have been merged with the Outgoing frame will not be shown in the Critical frame"] = true
+L["Crits will be merged and the total merged amount in the outgoing frame |cffFF0000DOES NOT|r include crits."] = true
+L["Crits will be merged, but the total merged amount in the outgoing frame includes crits."] = true
+L["Crits will not get merged in the critical frame, but they will be included in the outgoing total. |cffFFFF00(Default)|r"] = true
+L["Custom Colors"] = true
+L["Custom"] = true
+L["Customize Spell School Colors"] = true
+L["Damage Type"] = true
+L["Damage"] = true
+L["Debuff Gains/Fades"] = true
+L["Decimal Marks"] = true
+L["Dialog"] = true
+L["Direction Offset"] = true
+L["Direction Scale"] = true
+L["Disable in Combat"] = true
+L["Disable"] = true
+L["Display 'Immune' when your target cannot take damage."] = true
+L["Display 'Miss', 'Dodge', 'Parry' when you miss your target."] = true
+L["Display Environment Name"] = true
+L["Display NPC Name"] = true
+L["Display Player Name"] = true
+L["Display partially or fully absorbed damage as regular damage."] = true
+L["Display the names of harmful auras |cffFF0000(Debuffs)|r that you gain and lose."] = true
+L["Display the names of helpful auras |cff00FF00(Buffs)|r that you gain and lose."] = true
+L["Display the spell you successfully interrupted."] = true
+L["Display unit that was killed by your final blow."] = true
+L["Displays 'Low Health/Mana' when your health/mana reaches the low threshold."] = true
+L["Displays Dodge, Parry, or Miss when you miss incoming damage."] = true
+L["Displays currency that you gain."] = true
+L["Displays how many items you have in your bag."] = true
+L["Displays items that pertain to a quest."] = true
+L["Displays items that were purchased from a vendor."] = true
+L["Displays items that you crafted."] = true
+L["Displays items that you pick up."] = true
+L["Displays money that you pick up."] = true
+L["Displays money using letters G, S, and C instead of icons."] = true
+L["Displays overhealing."] = true
+L["Displays when the player is leaving or entering combat."] = true
+L["Displays your player's honor gains."] = true
+L["Displays your player's reputation gains and losses."] = true
+L["Displays your skill ups in professions."] = true
+L["Don't Merge Critical Hits Together"] = true
+L["Down"] = true
+L["Each frame has a |cffFFFF00Misc|r section; select a frame and select the drop-down box to find it."] = true
+L["Enable Debugging"] = true
+L["Enable Font Shadow"] = true
+L["Enable Icons"] = true
+L["Enable Scrolling Combat Text (Self)"] = true
+L["Enable Spam Merger"] = true
+L["Enable a custom color for |cff798BDD%s|r."] = true
+L["Enable a different threshold for incoming damage criticals."] = true
+L["Enable a different threshold for incoming healing criticals."] = true
+L["Enable a different threshold for outgoing damage criticals."] = true
+L["Enable a different threshold for outgoing healing criticals."] = true
+L["Enable this option if you have problems with 'floating' icons."] = true
+L["Enable this option if you want to see all auto-attacks."] = true
+L["Enable this option if you want to see threat changes."] = true
+L["Enable this to see when your pet's abilities critical strike, otherwise disable for less combat text spam."] = true
+L["Enable"] = true
+L["Enabled"] = true
+L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "] = true
+L["Environment Format"] = true
+L["Environment"] = true
+L["Events from a NPC"] = true
+L["Events from a Player"] = true
+L["Events from the Environment"] = true
+L["Events to a NPC"] = true
+L["Events to a Player"] = true
+L["Fade Out Duration"] = true
+L["Fading Text Settings"] = true
+L["Filter Item Quality"] = true
+L["Filter Resources"] = true
+L["Filtered Buffs |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Debuffs |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Incoming Damage |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Incoming Healing |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Items |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Procs |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered Spells |cff798BDD(Uncheck to disable)|r"] = true
+L["Filtered auras gains and fades that are |cff1AFF1ABuffs|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filtered auras gains and fades that are |cffFF1A1ADebuffs|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filtered |cff71d5ffIncoming Damage Spells|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filtered |cff71d5ffIncoming Healing Spells|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filtered |cff71d5ffOutgoing Spells|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filtered |cff798BDDItems|r will be on a whitelist (opposed to a blacklist)."] = true
+L["Filters"] = true
+L["Floating Combat Text"] = true
+L["Font Outline"] = true
+L["Font Settings"] = true
+L["Font Shadow Color"] = true
+L["Font Shadow Settings"] = true
+L["Font Size"] = true
+L["Font"] = true
+L["Format Overhealing"] = true
+L["Formats incoming damage to show how much was absorbed. The spam merger hides these reduction and effectively disables this option though."] = true
+L["Formats the looted message to also include the type of item (e.g. Trade Goods, Armor, Junk, etc.)."] = true
+L["Frame Alpha"] = true
+L["Frame Settings"] = true
+L["Frame Strata"] = true
+L["Frame"] = true
+L["Frames"] = true
+L["Fullscreen Dialog"] = true
+L["Fullscreen"] = true
+L["Gained Currency"] = true
+L["General"] = true
+L["GitHub Contributors"] = true
+L["Global Frame Settings |cffFFFFFF(Experimental)|r"] = true
+L["Global Spells / Items"] = true
+L["Groups decimals and separates them by commas; this allows for better responsiveness when reading numbers.\n\n|cffFF0000EXAMPLE|r |cff798BDD12,890|r"] = true
+L["Healing and Absorbs"] = true
+L["Hide Absorbed Heals"] = true
+L["Hide Config in Combat"] = true
+L["Hide Merged Criticals"] = true
+L["High |cffFFFF00(Default)|r"] = true
+L["HoTs"] = true
+L["Honor Gains"] = true
+L["Horizontal Offset"] = true
+L["How to contact me"] = true
+L["ID"] = true
+L["Icon Settings"] = true
+L["Icons"] = true
+L["If enabled, subtract any healing that was absorbed by a |cffFF0000debuff|r from the total."] = true
+L["If the player has a realm name attached to her name, it will be removed."] = true
+L["If the player's class is known (e.g. is a raid member), it will be colored."] = true
+L["If there is a certain |cff798BDDSpell|r, |cff798BDDBuff|r, or |cff798BDDDebuff|r that you don't want to see, consider adding it to a |cff798BDDFilter|r."] = true
+L["If you want to |cff798BDDCombine Frame Outputs|r, disable one of the frames and use the |cffFFFF00Secondary Frame|r option on that frame."] = true
+L["Incoming Damage / Healing"] = true
+L["Incoming Damage"] = true
+L["Incoming Dispells / Spell Steals"] = true
+L["Incoming Healing"] = true
+L["Incoming Player Power Threshold (Mana, Rage, Energy, etc.)"] = true
+L["Interrupts"] = true
+L["Justification"] = true
+L["Justifies the output to a side."] = true
+L["Leave/Enter Combat"] = true
+L["Left"] = true
+L["Loot, Currency & Money"] = true
+L["Looted Items"] = true
+L["Looted Money"] = true
+L["Low Mana/Health"] = true
+L["Low"] = true
+L["Make Auto Attack and Swing criticals more visible by adding the prefix and postfix."] = true
+L["Medium"] = true
+L["Merge Critical Hits by Themselves"] = true
+L["Merge Critical Hits with Outgoing"] = true
+L["Merge Options"] = true
+L["Merge Vehicle Abilities"] = true
+L["Merge-Interval Incoming Damage"] = true
+L["Merge-Interval Incoming Healing"] = true
+L["Merge-Interval Incoming Misses"] = true
+L["Merge-Interval Outgoing Misses"] = true
+L["Merge-Interval for ALL Pet Abilities"] = true
+L["Merge-Interval for Dispells"] = true
+L["Merge-Interval for Reputation"] = true
+L["Merge-Interval for other spells"] = true
+L["Merges all of your vehicle abilities together."] = true
+L["Million Symbol"] = true
+L["Minimal Value Thresholds"] = true
+L["Minimum Threshold for Crits"] = true
+L["Minimum Threshold"] = true
+L["Misc"] = true
+L["Miscellaneous Settings"] = true
+L["Miss Type Settings"] = true
+L["Monochrome Outline"] = true
+L["Monochrome"] = true
+L["Move"] = true
+L["NPC Name Color"] = true
+L["NPC Name Format"] = true
+L["NPC Name"] = true
+L["Name Prefix"] = true
+L["Name Suffix"] = true
+L["Names to display"] = true
+L["Names"] = true
+L["No Description"] = true
+L["No buffs have been added to this list yet."] = true
+L["No debuffs have been added to this list yet."] = true
+L["No items have been added to this list yet."] = true
+L["No procs have been added to this list yet."] = true
+L["No spells have been added to this list yet."] = true
+L["None"] = true
+L["Normally all damage / heal events of a spell will result in one message each.\nSo AoE spells like Rain of Fire or Spinning Crane Kick will spam a lot of messages into the xCT frames.\nIf the spam merger is enabled, then the damage events in a configured interval of X seconds of each spell will be merged into one message.\n|cffFF0000Drawback|r: the (merged) message will be delayed by the configured interval!!\nUse an interval of 0 to disable the specific merge."] = true
+L["Number Format Settings"] = true
+L["Number Formatting"] = true
+L["Number of Lines"] = true
+L["On the left list, under the |cffFFFF00Startup Message|r checkbox, you can click on the |cff798BDD+ Buttons|r (plus) to show more options."] = true
+L["Only show the highest partial miss, instead of all the misses. (Rare, but less spammy)\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = true
+L["Only the |cffFFFF00General|r, |cffFF8000Outgoing|r, |cffFFFF00Outgoing (Crits)|r, |cffFF8000Incoming Damage|r and |cffFFFF00Healing|r, and |cffFF8000Class Power|r frames can be abbreviated."] = true
+L["Other"] = true
+L["Outgoing Damage (Criticals)"] = true
+L["Outgoing Damage / Healing"] = true
+L["Outgoing Damage"] = true
+L["Outgoing Healing"] = true
+L["Outline"] = true
+L["Overhealing Postfix"] = true
+L["Overhealing Prefix"] = true
+L["Overhealing"] = true
+L["Parent |cffFF0000(Lowest)|r"] = true
+L["Pet Attacks"] = true
+L["Pet Auto Attacks"] = true
+L["Pet Color"] = true
+L["Pet and Vehicle Damage Settings"] = true
+L["Player Damage Settings"] = true
+L["Player Gains"] = true
+L["Player Name Format"] = true
+L["Player Name"] = true
+L["Player Status"] = true
+L["Please choose one:"] = true
+L["Preempt an automatic color with a custom one."] = true
+L["Prefix this value to the beginning when displaying a critical amount."] = true
+L["Prefix this value to the beginning when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = true
+L["Prefix this value to the ending when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = true
+L["Profession skillup"] = true
+L["Purchased Items"] = true
+L["Quest Items"] = true
+L["Racial Spells"] = true
+L["Remove Buff from filter"] = true
+L["Remove Debuff from filter"] = true
+L["Remove Item from filter"] = true
+L["Remove Proc from filter"] = true
+L["Remove Realm Name"] = true
+L["Remove Spell from filter"] = true
+L["Remove filtered Buff"] = true
+L["Remove filtered Debuff"] = true
+L["Remove filtered Item"] = true
+L["Remove filtered proc"] = true
+L["Remove filtered spell"] = true
+L["Remove the Buff from the config all together."] = true
+L["Remove the Debuff from the config all together."] = true
+L["Remove the Item from the config all together."] = true
+L["Remove the proc from the config all together."] = true
+L["Remove the spell ID from the config all together."] = true
+L["Reputation Gains/Losses"] = true
+L["Reset the prefix and the suffix of criticals to their default setting."] = true
+L["Reset"] = true
+L["Resets |cff798BDD%s|r back to the default color."] = true
+L["Restore Defaults"] = true
+L["Right"] = true
+L["Scrollable Frame Settings"] = true
+L["Secondary Frame"] = true
+L["Secondary ID(s)"] = true
+L["Set All"] = true
+L["Set the font of the frame."] = true
+L["Set the font outline."] = true
+L["Set the font size of the frame."] = true
+L["Set the icon size. (Recommended value: 16)"] = true
+L["Sets the alpha of the frame."] = true
+L["Show Absorbs (Self)"] = true
+L["Show Absorbs (Target)"] = true
+L["Show Absorbs as damage"] = true
+L["Show Absorbs"] = true
+L["Show Align Grid"] = true
+L["Show Auras"] = true
+L["Show Auto Attack Icon"] = true
+L["Show Auto Attack"] = true
+L["Show Auto Attacks (Pre)Postfix"] = true
+L["Show Auto Attacks"] = true
+L["Show Combat State"] = true
+L["Show Combo Points"] = true
+L["Show Damage Reduction"] = true
+L["Show Damage"] = true
+L["Show DoTs"] = true
+L["Show Effects (All)"] = true
+L["Show Effects (Group)"] = true
+L["Show Effects (Mine)"] = true
+L["Show Energy (Periodic)"] = true
+L["Show Energy Type"] = true
+L["Show Energy"] = true
+L["Show Friendly Healers"] = true
+L["Show Gains"] = true
+L["Show Healing"] = true
+L["Show HoTs"] = true
+L["Show Honor"] = true
+L["Show Immunes"] = true
+L["Show Invisible Icons"] = true
+L["Show Item Types"] = true
+L["Show Kill Command"] = true
+L["Show Low HP/Mana"] = true
+L["Show Miss Types"] = true
+L["Show Misses, Dodges, Parries"] = true
+L["Show My Heals Only"] = true
+L["Show Outgoing Damage"] = true
+L["Show Overhealing"] = true
+L["Show Overheals"] = true
+L["Show Pet Damage"] = true
+L["Show Pet Heals Too"] = true
+L["Show Pet Melee"] = true
+L["Show Pet Spells"] = true
+L["Show Positions"] = true
+L["Show Reactives"] = true
+L["Show Reductions"] = true
+L["Show Rep Changes"] = true
+L["Show Threat Changes"] = true
+L["Show Vehicle Damage"] = true
+L["Show criticals from Auto Attack and Swings. If disabled, they will be displayed as non-critical auto attacks. They will be merged into the Outgoing frame."] = true
+L["Show damage that your vehicle does. This can be anything from a vehicle you are controlling to Hati, the beast mastery pet."] = true
+L["Show damage you do."] = true
+L["Show icons from Auto Attacks."] = true
+L["Show icons next to your damage."] = true
+L["Show icons."] = true
+L["Show instant gains of class resources (e. g. energy, runic power, ...)."] = true
+L["Show partial Misses, Dodges, Parries"] = true
+L["Show the Highest Partial Miss"] = true
+L["Show the overhealing you receive from other players."] = true
+L["Show the spell that somebody else dispelled on you or stole a buff/debuff from you."] = true
+L["Show the spell that you dispelled or stole."] = true
+L["Show the type of energy that you are gaining."] = true
+L["Show when your target takes only a percentage of your damage because it was partially absorbed, resisted, or blocked.\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = true
+L["Show your Damage-Over-Time (DOT) damage. (|cffFF0000Requires:|r Outgoing Damage)"] = true
+L["Show your Heal-Over-Time (HOT) healing."] = true
+L["Show your non-critical, auto attack damage."] = true
+L["Show your pet's damage. Beast Mastery hunters should also look at vehicle damage."] = true
+L["Show your pet's non-critical, auto attacks."] = true
+L["Shows a grid after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = true
+L["Shows a shadow behind the combat text fonts."] = true
+L["Shows a single digit of precision when abbreviating the value (e.g. will show |cff798BDD5.9K|r instead of |cff798BDD6K|r)."] = true
+L["Shows absorbs you gain from other players."] = true
+L["Shows incoming damage and healing done to you. It is also required for a lot of the other events to work (as noted in their descriptions).\n\n|cffFF0000Changing this requires a UI Reload!|r"] = true
+L["Shows only the player's healing done to himself or herself."] = true
+L["Shows the locations and sizes of your frames after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = true
+L["Single Decimal Precision"] = true
+L["Size"] = true
+L["Spam Merger"] = true
+L["Special Effects (Procs)"] = true
+L["Special Thanks"] = true
+L["Specialization"] = true
+L["Spell History"] = true
+L["Spell Name Format"] = true
+L["Spell Name"] = true
+L["Spell School Colors"] = true
+L["Splits overhealing into its own section. Example: +43,000 (O: 12,000)"] = true
+L["Status Effects"] = true
+L["Subtract Overhealing"] = true
+L["Subtract the overhealed amount from the Total Amount"] = true
+L["Symbol for: |cffFF0000Billions|r |cff798BDD(10e+9)|r"] = true
+L["Symbol for: |cffFF0000Millions|r |cff798BDD(10e+6)|r"] = true
+L["Symbol for: |cffFF0000Thousands|r |cff798BDD(10e+3)|r"] = true
+L["Test"] = true
+L["Text Direction"] = true
+L["The ID of the |cff798BDDItem|r you want to filter."] = true
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = true
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = true
+L["The Z-Layer to place the |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames onto. If you find that another addon is in front of |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames, try increasing the Frame Strata."] = true
+L["The amount to offset the vertical origin of the directional damage numbers when they appear. (e.g. move them up and down)\n\n0 = Default"] = true
+L["The amount to scale the distance that directional damage numbers will move as they appear. Damage numbers will just scroll up if this is disabled.\n\n0 = Disabled\n1 = Default\n3.6 = Recommended"] = true
+L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = true
+L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = true
+L["The following settings allow you to tweak Blizzard's Floating Combat Text."] = true
+L["The following settings are marked as experimental. They should all work, but they might not be very useful. Expect changes or updates to these in the near future.\n\nClick |cffFFFF00Set All|r to apply setting to all |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames.\n"] = true
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Power Word: Fortitude')."] = true
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Shadow Word: Pain')."] = true
+L["The full, case-sensitive name of the |cff1AFF1AProc|r you want to filter (e.g. 'Power Word: Fortitude')."] = true
+L["The interval (seconds) in which ALL pet damage will be merged. It will use your pet's icon instead of an spell icon. Use 0 to disable."] = true
+L["The interval (seconds) in which all other spells will be merged. Certain spells have other intervals, see the tabs for them. Use 0 to disable."] = true
+L["The interval (seconds) in which dispells are merged together. Only dispells for the same aura (by name) will be merged. Use 0 to disable."] = true
+L["The interval (seconds) in which incoming damage will be merged. Different messages will still be displayed for different spells. Use 0 to disable."] = true
+L["The interval (seconds) in which incoming full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = true
+L["The interval (seconds) in which incoming healing will be merged. All healing done by the same person will be merged together! Use 0 to disable."] = true
+L["The interval (seconds) in which outgoing full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = true
+L["The interval (seconds) in which reputation gains / losses are merged together. Use 0 to disable."] = true
+L["The merge interval for a lot of spells can be set via the 'Class Spells', 'Global Spells/Items' and 'Racial Spells' tabs."] = true
+L["The minimal amount of damage required for a critical in order for it to be displayed."] = true
+L["The minimal amount of damage required in order for it to be displayed."] = true
+L["The minimal amount of healing required for a critical in order for it to be displayed."] = true
+L["The minimal amount of healing required in order for it to be displayed."] = true
+L["The minimal amount of player's power required in order for it to be displayed."] = true
+L["The name will be colored according to it's environmental type."] = true
+L["The spell ID of the |cff71d5ffOutgoing Spell|r you want to filter."] = true
+L["The spell name will be colored according to it's spell school."] = true
+L["The type will be colored according to it's environmental type."] = true
+L["The |cffFFFF00Hide Config in Combat|r option was added to prevent |cffFFFF00xCT+|r from tainting your UI. It is highly recommended left enabled."] = true
+L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."] = true
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Damage|r to your character."] = true
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Healing|r to your character."] = true
+L["These options allow you to filter out spell |cffFFFF00Procs|r that your player triggers."] = true
+L["These options allow you to filter out |cff1AFF1ABuff|r auras that your player gains or loses."] = true
+L["These options allow you to filter out |cff8020FFItems|r that your player collects."] = true
+L["These options allow you to filter out |cffFF1A1ADebuff|r auras that your player gains or loses."] = true
+L["These options allow you to filter |cff71d5ffOutgoing Spells|r that your player does."] = true
+L["Thick Outline"] = true
+L["This option helps prevent UI taints by closing the config when you enter combat.\n\n|cffFF8000Highly Recommended Enabled|r"] = true
+L["Thousand Symbol"] = true
+L["ToolTip |cffAAFF80(Highest)|r"] = true
+L["Total Items"] = true
+L["Track all Spells"] = true
+L["Track all the spells that you've seen. This will make filtering them out easier."] = true
+L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = true
+L["Unit Killed"] = true
+L["Unknown Item"] = true
+L["Unknown"] = true
+L["Up"] = true
+L["Use Custom Fade"] = true
+L["Use other threshold for Crits"] = true
+L["Vehicle Color"] = true
+L["Vertical Offset"] = true
+L["Visibility Duration"] = true
+L["When icons are disabled, you can still enable invisible icons to line up text."] = true
+L["When moving the Frames"] = true
+L["Which color do you want the merged pet messages to be?"] = true
+L["Whitelist"] = true
+L["Will also attempt to show the player pet's healing."] = true
+L["Will not display any items that are below this quality (does not filter Quest or Crafted items)."] = true
+L["Write me a PM on Curseforge:"] = true
+L["You can change how |cffFFFF00xCT+|r shows you names in the |cffFFFF00Names|r section of most frames."] = true
+L["Your Dispells / Spell Steals"] = true
+L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"] = true
+L["ruRU Translators"] = true
+L["zhCN Translators"] = true
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = true
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = true
+L["|cff798BDDEnvironment|r: Displays 'Environment' as the one who damaged you.\n\n|cff798BDDDamage Types|r: Displays the damage type e.g. "] = true
+L["|cff798BDDMiscellaneous Settings|r:"] = true
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = true
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = true
+L["|cff808080Class Combo Points|r"] = true
+L["|cffFF0000x|rCT|cffFFFF00+|r |cff798BDDConfiguration Tool|r\n"] = true
+L["|cffFFFF00Helpful Tips:|r\n\n"] = true
+L["|cffFFFF00Thank You GitHub Contributors!|r"] = true
+L["|cffFFFF00xCT+|r has several different ways it will merge critical hits. You can check them out in the |cffFFFF00Spam Merger|r section."] = true
+L["|cffFFFFFFClass Power|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDBuffs|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDDebuffs|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Damage|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Healing|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDItems|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDOutgoing Spells|r"] = true
+L["|cffFFFFFFFilter:|r |cff798BDDProcs|r"] = true
+L["|cffFFFFFFGeneral|r"] = true
+L["|cffFFFFFFIncoming Damage|r"] = true
+L["|cffFFFFFFIncoming Healing|r"] = true
+L["|cffFFFFFFLoot, Currency & Money|r"] = true
+L["|cffFFFFFFOutgoing Damage|r"] = true
+L["|cffFFFFFFOutgoing Healing|r"] = true
+L["|cffFFFFFFOutgoing|r |cff798BDD(Criticals)|r"] = true
+L["|cffFFFFFFSpecial Effects|r |cff798BDD(Procs)|r"] = true
diff --git a/xCT+Options/locales/ruRU.lua b/xCT+Options/locales/ruRU.lua
new file mode 100644
index 00000000..b366b530
--- /dev/null
+++ b/xCT+Options/locales/ruRU.lua
@@ -0,0 +1,523 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- Translators: ZamestoTV (https://github.com/Hubbotu)
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "ruRU", false, false)
+if not L then return end
+
+L[" Version: %s "] = " Версия: %s "
+L["A frame to forward messages to when this frame is disabled."] = "Рамка для перенаправления сообщений, когда эта рамка отключена."
+L["A list of |cff1AFF1ABuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список названий |cff1AFF1AБаффов|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["A list of |cff1AFF1AProcs|r that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список |cff1AFF1AПроков|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["A list of |cff71d5ffOutgoing Spell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список ID |cff71d5ffИсходящих заклинаний|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["A list of |cff798BDDItem|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список ID |cff798BDDПредметов|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["A list of |cff798BDDSpell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список ID |cff798BDDЗаклинаний|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["A list of |cffFF0000Debuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "Список названий |cffFF0000Дебаффов|r, которые были замечены. |cffFF0000Требуется:|r |cff798BDDОтслеживание истории заклинаний|r"
+L["Abbreviate Numbers"] = "Сокращать числа"
+L["Absorbed Healing"] = "Поглощенное исцеление"
+L["Add new Buff to filter"] = "Добавить новый бафф в фильтр"
+L["Add new Debuff to filter"] = "Добавить новый дебафф в фильтр"
+L["Add new Item to filter"] = "Добавить новый предмет в фильтр"
+L["Add new Proc to filter"] = "Добавить новый прок в фильтр"
+L["Add new Spell to filter"] = "Добавить новое заклинание в фильтр"
+L["Add these character(s) after the amount of a critical hit."] = "Добавить эти символы после суммы критического удара."
+L["Add these character(s) before the amount of a critical hit."] = "Добавить эти символы перед суммой критического удара."
+L["Add these character(s) to the beginning of the message."] = "Добавить эти символы в начало сообщения."
+L["Add these character(s) to the end of the message."] = "Добавить эти символы в конец сообщения."
+L["Add via History"] = "Добавить через историю"
+L["Add via ID"] = "Добавить по ID"
+L["Add via Name"] = "Добавить по имени"
+L["Additional Settings"] = "Дополнительные настройки"
+L["Adds the spell ID to each message for this session only."] = "Добавляет ID заклинания к каждому сообщению только для этой сессии."
+L["Advanced"] = "Расширенные"
+L["All Text One Color (Override Color Settings)"] = "Весь текст одного цвета (переопределяет настройки цвета)"
+L["Allow Pet Crits"] = "Разрешить критические удары питомца"
+L["Allows you to adjust the position of all the xCT+ frames on your screen.\n\nYou can also type: '|cffFF0000/xct lock|r'"] = "Позволяет настроить положение всех рамок xCT+ на экране.\n\nТакже можно ввести: '|cffFF0000/xct lock|r'"
+L["Allows you to bypass xCT+'s CVar engine. This option might help if you have FCT enabled, but it disappears after awhile. Once you set your FCT options, enable this.\n\n|cffFF0000Changing this requires a UI Reload!|r"] = "Позволяет обойти движок CVar xCT+. Эта опция может помочь, если FCT включен, но через некоторое время исчезает. После настройки параметров FCT включите эту опцию.\n\n|cffFF0000Изменение требует перезагрузки интерфейса!|r"
+L["Allows you to customize the fade time of each frame."] = "Позволяет настроить время затухания для каждой рамки."
+L["Allows you to preview xCT+ in order to tweak settings outside of combat.\n\nYou can also type: '|cffFF0000/xct test|r'"] = "Позволяет предварительно просмотреть xCT+ для настройки параметров вне боя.\n\nТакже можно ввести: '|cffFF0000/xct test|r'"
+L["Appearance"] = "Внешний вид"
+L["Background |cffFF0000(Lowest)|r"] = "Фон |cffFF0000(Самый низкий)|r"
+L["Beta Testers - Version 3.0.0"] = "Бета-тестеры - Версия 3.0.0"
+L["Beta Testers - Version 4.0.0 (Curse)"] = "Бета-тестеры - Версия 4.0.0 (Curse)"
+L["Beta Testers - Version 4.0.0 (Tukui)"] = "Бета-тестеры - Версия 4.0.0 (Tukui)"
+L["Beta Testers - Version 4.3.0+ (Legion)"] = "Бета-тестеры - Версия 4.3.0+ (Легион)"
+L["Beta Testers - Version 4.4.0+ (Battle for Azeroth)"] = "Бета-тестеры - Версия 4.4.0+ (Битва за Азерот)"
+L["Billion Symbol"] = "Символ миллиарда"
+L["Buff Gains/Fades"] = "Получение/Спадание баффов"
+L["Bypass CVar Updates (requires |cffFF0000/reload|r)"] = "Обход обновлений CVar (требуется |cffFF0000/reload|r)"
+L["Center"] = "Центр"
+L["Change all the text in this frame to a specific color."] = "Изменить весь текст в этой рамке на определенный цвет."
+L["Change the color for |cff798BDD%s|r."] = "Изменить цвет для |cff798BDD%s|r."
+L["Change the source of |cff798BDDKill Command|r to be the |cffFF8000Player|r. This is helpful when you to turn off |cffFF8000Pet|r damage."] = "Изменить источник |cff798BDDКоманды Убить|r на |cffFF8000Игрока|r. Это полезно, если вы хотите отключить урон |cffFF8000Питомца|r."
+L["Changes the direction that the text travels in the frame."] = "Изменяет направление движения текста в рамке."
+L["Check for whitelist, uncheck for blacklist."] = "Отметить для белого списка, снять отметку для черного списка."
+L["Check the resources that you do not wish to be displayed for your character:"] = "Отметьте ресурсы, которые вы не хотите отображать для вашего персонажа:"
+L["Class Power"] = "Сила класса"
+L["Class Spells"] = "Заклинания класса"
+L["Clear Frames when leaving combat"] = "Очищать рамки при выходе из боя"
+L["Color Blind Mode"] = "Режим для дальтоников"
+L["Color Environment"] = "Цвет окружения"
+L["Color Player Name"] = "Цвет имени игрока"
+L["Color Spell Name"] = "Цвет имени заклинания"
+L["Color Type"] = "Тип цвета"
+L["Color"] = "Цвет"
+L["Colors of the events"] = "Цвета событий"
+L["Colors"] = "Цвета"
+L["Crafted Items"] = "Созданные предметы"
+L["Create a comment on Curseforge:"] = "Создать комментарий на Curseforge:"
+L["Create an issue at GitHub:"] = "Создать проблему на GitHub:"
+L["Credits"] = "Благодарности"
+L["Critical Hits"] = "Критические удары"
+L["Critical Prefix"] = "Префикс критического удара"
+L["Critical Suffix"] = "Суффикс критического удара"
+L["Criticals that have been merged with the Outgoing frame will not be shown in the Critical frame"] = "Критические удары, объединенные с рамкой исходящего урона, не будут отображаться в рамке критических ударов"
+L["Crits will be merged and the total merged amount in the outgoing frame |cffFF0000DOES NOT|r include crits."] = "Критические удары будут объединены, но общая сумма в рамке исходящего урона |cffFF0000НЕ ВКЛЮЧАЕТ|r критические удары."
+L["Crits will be merged, but the total merged amount in the outgoing frame includes crits."] = "Критические удары будут объединены, но общая сумма в рамке исходящего урона включает критические удары."
+L["Crits will not get merged in the critical frame, but they will be included in the outgoing total. |cffFFFF00(Default)|r"] = "Критические удары не будут объединены в рамке критических ударов, но будут включены в общий итог исходящего урона. |cffFFFF00(По умолчанию)|r"
+L["Custom Colors"] = "Пользовательские цвета"
+L["Custom"] = "Пользовательский"
+L["Customize Spell School Colors"] = "Настройка цветов школ заклинаний"
+L["Damage Type"] = "Тип урона"
+L["Damage"] = "Урон"
+L["Debuff Gains/Fades"] = "Получение/Спадание дебаффов"
+L["Decimal Marks"] = "Десятичные знаки"
+L["Dialog"] = "Диалог"
+L["Direction Offset"] = "Смещение направления"
+L["Direction Scale"] = "Масштаб направления"
+L["Disable in Combat"] = "Отключить в бою"
+L["Disable"] = "Отключить"
+L["Display 'Immune' when your target cannot take damage."] = "Отображать 'Иммунитет', когда цель не может получить урон."
+L["Display 'Miss', 'Dodge', 'Parry' when you miss your target."] = "Отображать 'Промах', 'Уклонение', 'Парирование', когда вы промахиваетесь по цели."
+L["Display Environment Name"] = "Отображать имя окружения"
+L["Display NPC Name"] = "Отображать имя NPC"
+L["Display Player Name"] = "Отображать имя игрока"
+L["Display partially or fully absorbed damage as regular damage."] = "Отображать частично или полностью поглощенный урон как обычный урон."
+L["Display the names of harmful auras |cffFF0000(Debuffs)|r that you gain and lose."] = "Отображать названия вредоносных аур |cffFF0000(Дебаффов)|r, которые вы получаете и теряете."
+L["Display the names of helpful auras |cff00FF00(Buffs)|r that you gain and lose."] = "Отображать названия полезных аур |cff00FF00(Баффов)|r, которые вы получаете и теряете."
+L["Display the spell you successfully interrupted."] = "Отображать заклинание, которое вы успешно прервали."
+L["Display unit that was killed by your final blow."] = "Отображать юнит, убитый вашим завершающим ударом."
+L["Displays 'Low Health/Mana' when your health/mana reaches the low threshold."] = "Отображает 'Низкое здоровье/мана', когда ваше здоровье/мана достигает низкого порога."
+L["Displays Dodge, Parry, or Miss when you miss incoming damage."] = "Отображает 'Уклонение', 'Парирование' или 'Промах', когда вы пропускаете входящий урон."
+L["Displays currency that you gain."] = "Отображает валюту, которую вы получаете."
+L["Displays how many items you have in your bag."] = "Отображает, сколько предметов у вас в сумке."
+L["Displays items that pertain to a quest."] = "Отображает предметы, связанные с квестом."
+L["Displays items that were purchased from a vendor."] = "Отображает предметы, купленные у торговца."
+L["Displays items that you crafted."] = "Отображает предметы, которые вы создали."
+L["Displays items that you pick up."] = "Отображает предметы, которые вы подбираете."
+L["Displays money that you pick up."] = "Отображает деньги, которые вы подбираете."
+L["Displays money using letters G, S, and C instead of icons."] = "Отображает деньги, используя буквы G, S и C вместо иконок."
+L["Displays overhealing."] = "Отображает избыточное исцеление."
+L["Displays when the player is leaving or entering combat."] = "Отображает, когда игрок выходит из боя или вступает в бой."
+L["Displays your player's honor gains."] = "Отображает получение чести вашим игроком."
+L["Displays your player's reputation gains and losses."] = "Отображает получение и потерю репутации вашим игроком."
+L["Displays your skill ups in professions."] = "Отображает повышение навыков в профессиях."
+L["Don't Merge Critical Hits Together"] = "Не объединять критические удары вместе"
+L["Down"] = "Вниз"
+L["Each frame has a |cffFFFF00Misc|r section; select a frame and select the drop-down box to find it."] = "Каждая рамка имеет раздел |cffFFFF00Разное|r; выберите рамку и раскройте выпадающее меню, чтобы найти его."
+L["Enable Debugging"] = "Включить отладку"
+L["Enable Font Shadow"] = "Включить тень шрифта"
+L["Enable Icons"] = "Включить иконки"
+L["Enable Scrolling Combat Text (Self)"] = "Включить прокручивающийся боевой текст (для себя)"
+L["Enable Spam Merger"] = "Включить объединение спама"
+L["Enable a custom color for |cff798BDD%s|r."] = "Включить пользовательский цвет для |cff798BDD%s|r."
+L["Enable a different threshold for incoming damage criticals."] = "Включить другой порог для критических входящих уронов."
+L["Enable a different threshold for incoming healing criticals."] = "Включить другой порог для критических входящих исцелений."
+L["Enable a different threshold for outgoing damage criticals."] = "Включить другой порог для критических исходящих уронов."
+L["Enable a different threshold for outgoing healing criticals."] = "Включить другой порог для критических исходящих исцелений."
+L["Enable this option if you have problems with 'floating' icons."] = "Включите эту опцию, если у вас проблемы с 'всплывающими' иконками."
+L["Enable this option if you want to see all auto-attacks."] = "Включите эту опцию, если хотите видеть все автоатаки."
+L["Enable this option if you want to see threat changes."] = "Включите эту опцию, если хотите видеть изменения угрозы."
+L["Enable this to see when your pet's abilities critical strike, otherwise disable for less combat text spam."] = "Включите, чтобы видеть, когда способности питомца наносят критический удар, или отключите для уменьшения спама боевого текста."
+L["Enable"] = "Включить"
+L["Enabled"] = "Включено"
+L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "] = "Включает форматирование чисел. Эта опция может быть настроена на главной странице опций |cff00FF00Рамки|r как |cff798BDDСокращение|r или |cff798BDDДесятичные знаки|r."
+L["Environment Format"] = "Формат окружения"
+L["Environment"] = "Окружение"
+L["Events from a NPC"] = "События от NPC"
+L["Events from a Player"] = "События от игрока"
+L["Events from the Environment"] = "События от окружения"
+L["Events to a NPC"] = "События для NPC"
+L["Events to a Player"] = "События для игрока"
+L["Fade Out Duration"] = "Длительность затухания"
+L["Fading Text Settings"] = "Настройки затухания текста"
+L["Filter Item Quality"] = "Фильтр качества предметов"
+L["Filter Resources"] = "Фильтр ресурсов"
+L["Filtered Buffs |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованные баффы |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Debuffs |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованные дебаффы |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Incoming Damage |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованный входящий урон |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Incoming Healing |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованное входящее исцеление |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Items |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованные предметы |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Procs |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованные проки |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered Spells |cff798BDD(Uncheck to disable)|r"] = "Отфильтрованные заклинания |cff798BDD(Снимите галочку, чтобы отключить)|r"
+L["Filtered auras gains and fades that are |cff1AFF1ABuffs|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные ауры, получаемые и спадающие, которые являются |cff1AFF1AБаффами|r, будут в белом списке (в отличие от черного списка)."
+L["Filtered auras gains and fades that are |cffFF1A1ADebuffs|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные ауры, получаемые и спадающие, которые являются |cffFF1A1AДебаффами|r, будут в белом списке (в отличие от черного списка)."
+L["Filtered |cff71d5ffIncoming Damage Spells|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные |cff71d5ffЗаклинания входящего урона|r будут в белом списке (в отличие от черного списка)."
+L["Filtered |cff71d5ffIncoming Healing Spells|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные |cff71d5ffЗаклинания входящего исцеления|r будут в белом списке (в отличие от черного списка)."
+L["Filtered |cff71d5ffOutgoing Spells|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные |cff71d5ffИсходящие заклинания|r будут в белом списке (в отличие от черного списка)."
+L["Filtered |cff798BDDItems|r will be on a whitelist (opposed to a blacklist)."] = "Отфильтрованные |cff798BDDПредметы|r будут в белом списке (в отличие от черного списка)."
+L["Filters"] = "Фильтры"
+L["Floating Combat Text"] = "Всплывающий текст боя"
+L["Font Outline"] = "Контур шрифта"
+L["Font Settings"] = "Настройки шрифта"
+L["Font Shadow Color"] = "Цвет тени шрифта"
+L["Font Shadow Settings"] = "Настройки тени шрифта"
+L["Font Size"] = "Размер шрифта"
+L["Font"] = "Шрифт"
+L["Format Overhealing"] = "Форматировать избыточное исцеление"
+L["Formats incoming damage to show how much was absorbed. The spam merger hides these reduction and effectively disables this option though."] = "Форматирует входящий урон, показывая, сколько было поглощено. Однако объединение спама скрывает эти сокращения и фактически отключает эту опцию."
+L["Formats the looted message to also include the type of item (e.g. Trade Goods, Armor, Junk, etc.)."] = "Форматирует сообщение о добыче, чтобы также включать тип предмета (например, торговые товары, броня, хлам и т.д.)."
+L["Frame Alpha"] = "Прозрачность рамки"
+L["Frame Settings"] = "Настройки рамки"
+L["Frame Strata"] = "Слой рамки"
+L["Frame"] = "Рамка"
+L["Frames"] = "Рамки"
+L["Fullscreen Dialog"] = "Полноэкранный диалог"
+L["Fullscreen"] = "Полноэкранный"
+L["Gained Currency"] = "Полученная валюта"
+L["General"] = "Общие"
+L["GitHub Contributors"] = "Контрибьюторы GitHub"
+L["Global Frame Settings |cffFFFFFF(Experimental)|r"] = "Глобальные настройки рамок |cffFFFFFF(Экспериментально)|r"
+L["Global Spells / Items"] = "Глобальные заклинания / предметы"
+L["Groups decimals and separates them by commas; this allows for better responsiveness when reading numbers.\n\n|cffFF0000EXAMPLE|r |cff798BDD12,890|r"] = "Группирует десятичные числа и разделяет их запятыми; это улучшает восприятие чисел.\n\n|cffFF0000ПРИМЕР|r |cff798BDD12,890|r"
+L["Healing and Absorbs"] = "Исцеление и поглощения"
+L["Hide Absorbed Heals"] = "Скрыть поглощенные исцеления"
+L["Hide Config in Combat"] = "Скрыть конфигурацию в бою"
+L["Hide Merged Criticals"] = "Скрыть объединенные критические удары"
+L["High |cffFFFF00(Default)|r"] = "Высокий |cffFFFF00(По умолчанию)|r"
+L["HoTs"] = "Исцеление со временем"
+L["Honor Gains"] = "Получение чести"
+L["Horizontal Offset"] = "Горизонтальное смещение"
+L["How to contact me"] = "Как со мной связаться"
+L["ID"] = "ID"
+L["Icon Settings"] = "Настройки иконок"
+L["Icons"] = "Иконки"
+L["If enabled, subtract any healing that was absorbed by a |cffFF0000debuff|r from the total."] = "Если включено, вычитать любое исцеление, поглощенное |cffFF0000дебаффом|r, из общей суммы."
+L["If the player has a realm name attached to her name, it will be removed."] = "Если к имени игрока прикреплено название сервера, оно будет удалено."
+L["If the player's class is known (e.g. is a raid member), it will be colored."] = "Если класс игрока известен (например, он участник рейда), он будет окрашен."
+L["If there is a certain |cff798BDDSpell|r, |cff798BDDBuff|r, or |cff798BDDDebuff|r that you don't want to see, consider adding it to a |cff798BDDFilter|r."] = "Если есть определенное |cff798BDDЗаклинание|r, |cff798BDDБафф|r или |cff798BDDДебафф|r, которое вы не хотите видеть, рассмотрите возможность добавления его в |cff798BDDFilter|r."
+L["If you want to |cff798BDDCombine Frame Outputs|r, disable one of the frames and use the |cffFFFF00Secondary Frame|r option on that frame."] = "Если вы хотите |cff798BDDОбъединить вывод рамок|r, отключите одну из рамок и используйте опцию |cffFFFF00Вторичная рамка|r для этой рамки."
+L["Incoming Damage / Healing"] = "Входящий урон / исцеление"
+L["Incoming Damage"] = "Входящий урон"
+L["Incoming Dispells / Spell Steals"] = "Входящие рассеивания / кражи заклинаний"
+L["Incoming Healing"] = "Входящее исцеление"
+L["Incoming Player Power Threshold (Mana, Rage, Energy, etc.)"] = "Порог силы игрока для входящих (мана, ярость, энергия и т.д.)"
+L["Interrupts"] = "Прерывания"
+L["Justification"] = "Выравнивание"
+L["Justifies the output to a side."] = "Выравнивает вывод по одной из сторон."
+L["Leave/Enter Combat"] = "Вход/Выход из боя"
+L["Left"] = "Слева"
+L["Loot, Currency & Money"] = "Добыча, валюта и деньги"
+L["Looted Items"] = "Собранные предметы"
+L["Looted Money"] = "Собранные деньги"
+L["Low Mana/Health"] = "Низкая мана/здоровье"
+L["Low"] = "Низкий"
+L["Make Auto Attack and Swing criticals more visible by adding the prefix and postfix."] = "Сделать критические автоатаки и удары более заметными, добавляя префикс и постфикс."
+L["Medium"] = "Средний"
+L["Merge Critical Hits by Themselves"] = "Объединять критические удары отдельно"
+L["Merge Critical Hits with Outgoing"] = "Объединять критические удары с исходящими"
+L["Merge Options"] = "Опции объединения"
+L["Merge Vehicle Abilities"] = "Объединять способности транспорта"
+L["Merge-Interval Incoming Damage"] = "Интервал объединения входящего урона"
+L["Merge-Interval Incoming Healing"] = "Интервал объединения входящего исцеления"
+L["Merge-Interval Incoming Misses"] = "Интервал объединения входящих промахов"
+L["Merge-Interval Outgoing Misses"] = "Интервал объединения исходящих промахов"
+L["Merge-Interval for ALL Pet Abilities"] = "Интервал объединения для всех способностей питомца"
+L["Merge-Interval for Dispells"] = "Интервал объединения для рассеиваний"
+L["Merge-Interval for Reputation"] = "Интервал объединения для репутации"
+L["Merge-Interval for other spells"] = "Интервал объединения для других заклинаний"
+L["Merges all of your vehicle abilities together."] = "Объединяет все способности вашего транспорта вместе."
+L["Million Symbol"] = "Символ миллиона"
+L["Minimal Value Thresholds"] = "Минимальные пороговые значения"
+L["Minimum Threshold for Crits"] = "Минимальный порог для критических ударов"
+L["Minimum Threshold"] = "Минимальный порог"
+L["Misc"] = "Разное"
+L["Miscellaneous Settings"] = "Различные настройки"
+L["Miss Type Settings"] = "Настройки типов промахов"
+L["Monochrome Outline"] = "Монохромный контур"
+L["Monochrome"] = "Монохромный"
+L["Move"] = "Переместить"
+L["NPC Name Color"] = "Цвет имени NPC"
+L["NPC Name Format"] = "Формат имени NPC"
+L["NPC Name"] = "Имя NPC"
+L["Name Prefix"] = "Префикс имени"
+L["Name Suffix"] = "Суффикс имени"
+L["Names to display"] = "Имена для отображения"
+L["Names"] = "Имена"
+L["No Description"] = "Нет описания"
+L["No buffs have been added to this list yet."] = "В этот список пока не добавлены баффы."
+L["No debuffs have been added to this list yet."] = "В этот список пока не добавлены дебаффы."
+L["No items have been added to this list yet."] = "В этот список пока не добавлены предметы."
+L["No procs have been added to this list yet."] = "В этот список пока не добавлены проки."
+L["No spells have been added to this list yet."] = "В этот список пока не добавлены заклинания."
+L["None"] = "Нет"
+L["Normally all damage / heal events of a spell will result in one message each.\nSo AoE spells like Rain of Fire or Spinning Crane Kick will spam a lot of messages into the xCT frames.\nIf the spam merger is enabled, then the damage events in a configured interval of X seconds of each spell will be merged into one message.\n|cffFF0000Drawback|r: the (merged) message will be delayed by the configured interval!!\nUse an interval of 0 to disable the specific merge."] = "Обычно каждое событие урона/исцеления заклинания приводит к одному сообщению.\nТаким образом, AoE-заклинания, такие как Огненный дождь или Вращающийся журавлиный удар, создают много сообщений в рамках xCT.\nЕсли включено объединение спама, события урона в заданном интервале X секунд для каждого заклинания будут объединены в одно сообщение.\n|cffFF0000Недостаток|r: объединенное сообщение будет отображаться с задержкой, равной заданному интервалу!!\nИспользуйте интервал 0, чтобы отключить конкретное объединение."
+L["Number Format Settings"] = "Настройки формата чисел"
+L["Number Formatting"] = "Форматирование чисел"
+L["Number of Lines"] = "Количество строк"
+L["On the left list, under the |cffFFFF00Startup Message|r checkbox, you can click on the |cff798BDD+ Buttons|r (plus) to show more options."] = "В списке слева, под флажком |cffFFFF00Сообщение при запуске|r, вы можете нажать на |cff798BDD+ Кнопки|r (плюс), чтобы показать дополнительные опции."
+L["Only show the highest partial miss, instead of all the misses. (Rare, but less spammy)\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = "Показывать только самый высокий частичный промах вместо всех промахов. (Редко, но меньше спама)\n\n|cffFF0000ОБРАТИТЕ ВНИМАНИЕ:|r Работает только если заклинание не объединено. Отключите объединение заклинаний, чтобы видеть все заклинания."
+L["Only the |cffFFFF00General|r, |cffFF8000Outgoing|r, |cffFFFF00Outgoing (Crits)|r, |cffFF8000Incoming Damage|r and |cffFFFF00Healing|r, and |cffFF8000Class Power|r frames can be abbreviated."] = "Только рамки |cffFFFF00Общие|r, |cffFF8000Исходящие|r, |cffFFFF00Исходящие (Криты)|r, |cffFF8000Входящий урон|r, |cffFFFF00Исцеление|r и |cffFF8000Сила класса|r могут быть сокращены."
+L["Other"] = "Прочее"
+L["Outgoing Damage (Criticals)"] = "Исходящий урон (Критические)"
+L["Outgoing Damage / Healing"] = "Исходящий урон / исцеление"
+L["Outgoing Damage"] = "Исходящий урон"
+L["Outgoing Healing"] = "Исходящее исцеление"
+L["Outline"] = "Контур"
+L["Overhealing Postfix"] = "Постфикс избыточного исцеления"
+L["Overhealing Prefix"] = "Префикс избыточного исцеления"
+L["Overhealing"] = "Избыточное исцеление"
+L["Parent |cffFF0000(Lowest)|r"] = "Родительский |cffFF0000(Самый низкий)|r"
+L["Pet Attacks"] = "Атаки питомца"
+L["Pet Auto Attacks"] = "Автоатаки питомца"
+L["Pet Color"] = "Цвет питомца"
+L["Pet and Vehicle Damage Settings"] = "Настройки урона питомца и транспорта"
+L["Player Damage Settings"] = "Настройки урона игрока"
+L["Player Gains"] = "Получения игрока"
+L["Player Name Format"] = "Формат имени игрока"
+L["Player Name"] = "Имя игрока"
+L["Player Status"] = "Статус игрока"
+L["Please choose one:"] = "Пожалуйста, выберите один:"
+L["Preempt an automatic color with a custom one."] = "Заменить автоматический цвет пользовательским."
+L["Prefix this value to the beginning when displaying a critical amount."] = "Добавлять это значение в начало при отображении критической суммы."
+L["Prefix this value to the beginning when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = "Добавлять это значение в начало при отображении суммы избыточного исцеления.\n\n|cffFF0000Требуется:|r |cff798BDDФорматировать избыточное исцеление|r"
+L["Prefix this value to the ending when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = "Добавлять это значение в конец при отображении суммы избыточного исцеления.\n\n|cffFF0000Требуется:|r |cff798BDDФорматировать избыточное исцеление|r"
+L["Profession skillup"] = "Повышение навыка профессии"
+L["Purchased Items"] = "Купленные предметы"
+L["Quest Items"] = "Квестовые предметы"
+L["Racial Spells"] = "Расовые заклинания"
+L["Remove Buff from filter"] = "Удалить бафф из фильтра"
+L["Remove Debuff from filter"] = "Удалить дебафф из фильтра"
+L["Remove Item from filter"] = "Удалить предмет из фильтра"
+L["Remove Proc from filter"] = "Удалить прок из фильтра"
+L["Remove Realm Name"] = "Удалить название сервера"
+L["Remove Spell from filter"] = "Удалить заклинание из фильтра"
+L["Remove filtered Buff"] = "Удалить отфильтрованный бафф"
+L["Remove filtered Debuff"] = "Удалить отфильтрованный дебафф"
+L["Remove filtered Item"] = "Удалить отфильтрованный предмет"
+L["Remove filtered proc"] = "Удалить отфильтрованный прок"
+L["Remove filtered spell"] = "Удалить отфильтрованное заклинание"
+L["Remove the Buff from the config all together."] = "Полностью удалить бафф из конфигурации."
+L["Remove the Debuff from the config all together."] = "Полностью удалить дебафф из конфигурации."
+L["Remove the Item from the config all together."] = "Полностью удалить предмет из конфигурации."
+L["Remove the proc from the config all together."] = "Полностью удалить прок из конфигурации."
+L["Remove the spell ID from the config all together."] = "Полностью удалить ID заклинания из конфигурации."
+L["Reputation Gains/Losses"] = "Получение/Потеря репутации"
+L["Reset the prefix and the suffix of criticals to their default setting."] = "Сбросить префикс и суффикс критических ударов на настройки по умолчанию."
+L["Reset"] = "Сброс"
+L["Resets |cff798BDD%s|r back to the default color."] = "Сбрасывает |cff798BDD%s|r на цвет по умолчанию."
+L["Restore Defaults"] = "Восстановить значения по умолчанию"
+L["Right"] = "Справа"
+L["Scrollable Frame Settings"] = "Настройки прокручиваемой рамки"
+L["Secondary Frame"] = "Вторичная рамка"
+L["Secondary ID(s)"] = "Вторичные ID"
+L["Set All"] = "Установить все"
+L["Set the font of the frame."] = "Установить шрифт рамки."
+L["Set the font outline."] = "Установить контур шрифта."
+L["Set the font size of the frame."] = "Установить размер шрифта рамки."
+L["Set the icon size. (Recommended value: 16)"] = "Установить размер иконки. (Рекомендуемое значение: 16)"
+L["Sets the alpha of the frame."] = "Устанавливает прозрачность рамки."
+L["Show Absorbs (Self)"] = "Показать поглощения (себя)"
+L["Show Absorbs (Target)"] = "Показать поглощения (цель)"
+L["Show Absorbs as damage"] = "Показать поглощения как урон"
+L["Show Absorbs"] = "Показать поглощения"
+L["Show Align Grid"] = "Показать сетку выравнивания"
+L["Show Auras"] = "Показать ауры"
+L["Show Auto Attack Icon"] = "Показать иконку автоатаки"
+L["Show Auto Attack"] = "Показать автоатаку"
+L["Show Auto Attacks (Pre)Postfix"] = "Показать автоатаки (Пре)Постфикс"
+L["Show Auto Attacks"] = "Показать автоатаки"
+L["Show Combat State"] = "Показать состояние боя"
+L["Show Combo Points"] = "Показать комбо-очки"
+L["Show Damage Reduction"] = "Показать снижение урона"
+L["Show Damage"] = "Показать урон"
+L["Show DoTs"] = "Показать урон со временем"
+L["Show Effects (All)"] = "Показать эффекты (все)"
+L["Show Effects (Group)"] = "Показать эффекты (группа)"
+L["Show Effects (Mine)"] = "Показать эффекты (мои)"
+L["Show Energy (Periodic)"] = "Показать энергию (периодическую)"
+L["Show Energy Type"] = "Показать тип энергии"
+L["Show Energy"] = "Показать энергию"
+L["Show Friendly Healers"] = "Показать дружественных целителей"
+L["Show Gains"] = "Показать получения"
+L["Show Healing"] = "Показать исцеление"
+L["Show HoTs"] = "Показать исцеление со временем"
+L["Show Honor"] = "Показать честь"
+L["Show Immunes"] = "Показать иммунитеты"
+L["Show Invisible Icons"] = "Показать невидимые иконки"
+L["Show Item Types"] = "Показать типы предметов"
+L["Show Kill Command"] = "Показать команду убить"
+L["Show Low HP/Mana"] = "Показать низкое здоровье/ману"
+L["Show Miss Types"] = "Показать типы промахов"
+L["Show Misses, Dodges, Parries"] = "Показать промахи, уклонения, парирования"
+L["Show My Heals Only"] = "Показать только мои исцеления"
+L["Show Outgoing Damage"] = "Показать исходящий урон"
+L["Show Overhealing"] = "Показать избыточное исцеление"
+L["Show Overheals"] = "Показать избыточные исцеления"
+L["Show Pet Damage"] = "Показать урон питомца"
+L["Show Pet Heals Too"] = "Показать также исцеления питомца"
+L["Show Pet Melee"] = "Показать ближний бой питомца"
+L["Show Pet Spells"] = "Показать заклинания питомца"
+L["Show Positions"] = "Показать позиции"
+L["Show Reactives"] = "Показать реактивные способности"
+L["Show Reductions"] = "Показать сокращения"
+L["Show Rep Changes"] = "Показать изменения репутации"
+L["Show Threat Changes"] = "Показать изменения угрозы"
+L["Show Vehicle Damage"] = "Показать урон транспорта"
+L["Show criticals from Auto Attack and Swings. If disabled, they will be displayed as non-critical auto attacks. They will be merged into the Outgoing frame."] = "Показать критические удары от автоатак и взмахов. Если отключено, они будут отображаться как некритические автоатаки. Они будут объединены в рамку исходящего урона."
+L["Show damage that your vehicle does. This can be anything from a vehicle you are controlling to Hati, the beast mastery pet."] = "Показать урон, наносимый вашим транспортом. Это может быть что угодно, от управляемого вами транспорта до Хати, питомца мастера зверей."
+L["Show damage you do."] = "Показать урон, который вы наносите."
+L["Show icons from Auto Attacks."] = "Показать иконки от автоатак."
+L["Show icons next to your damage."] = "Показать иконки рядом с вашим уроном."
+L["Show icons."] = "Показать иконки."
+L["Show instant gains of class resources (e. g. energy, runic power, ...)."] = "Показать мгновенные получения ресурсов класса (например, энергия, руническая сила, ...)."
+L["Show partial Misses, Dodges, Parries"] = "Показать частичные промахи, уклонения, парирования"
+L["Show the Highest Partial Miss"] = "Показать самый высокий частичный промах"
+L["Show the overhealing you receive from other players."] = "Показать избыточное исцеление, получаемое от других игроков."
+L["Show the spell that somebody else dispelled on you or stole a buff/debuff from you."] = "Показать заклинание, которое кто-то другой рассеял на вас или украл бафф/дебафф у вас."
+L["Show the spell that you dispelled or stole."] = "Показать заклинание, которое вы рассеяли или украли."
+L["Show the type of energy that you are gaining."] = "Показать тип энергии, который вы получаете."
+L["Show when your target takes only a percentage of your damage because it was partially absorbed, resisted, or blocked.\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = "Показать, когда ваша цель получает только процент вашего урона из-за частичного поглощения, сопротивления или блокировки.\n\n|cffFF0000ОБРАТИТЕ ВНИМАНИЕ:|r Работает только если заклинание не объединено. Отключите объединение заклинаний, чтобы видеть все заклинания."
+L["Show your Damage-Over-Time (DOT) damage. (|cffFF0000Requires:|r Outgoing Damage)"] = "Показать ваш урон со временем (DOT). (|cffFF0000Требуется:|r Исходящий урон)"
+L["Show your Heal-Over-Time (HOT) healing."] = "Показать ваше исцеление со временем (HOT)."
+L["Show your non-critical, auto attack damage."] = "Показать ваш некритический урон от автоатак."
+L["Show your pet's damage. Beast Mastery hunters should also look at vehicle damage."] = "Показать урон вашего питомца. Охотники со специализацией 'Повелитель зверей' также должны обратить внимание на урон транспорта."
+L["Show your pet's non-critical, auto attacks."] = "Показать некритические автоатаки вашего питомца."
+L["Shows a grid after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = "Показывает сетку после активации |cffFFFF00Переключения рамок|r, чтобы помочь лучше выровнять рамки |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r."
+L["Shows a shadow behind the combat text fonts."] = "Показывает тень за шрифтами боевого текста."
+L["Shows a single digit of precision when abbreviating the value (e.g. will show |cff798BDD5.9K|r instead of |cff798BDD6K|r)."] = "Показывает одну цифру точности при сокращении значения (например, будет показано |cff798BDD5.9K|r вместо |cff798BDD6K|r)."
+L["Shows absorbs you gain from other players."] = "Показывает поглощения, которые вы получаете от других игроков."
+L["Shows incoming damage and healing done to you. It is also required for a lot of the other events to work (as noted in their descriptions).\n\n|cffFF0000Changing this requires a UI Reload!|r"] = "Показывает входящий урон и исцеление, нанесенные вам. Также требуется для работы многих других событий (как указано в их описаниях).\n\n|cffFF0000Изменение требует перезагрузки интерфейса!|r"
+L["Shows only the player's healing done to himself or herself."] = "Показывает только исцеление, которое игрок применяет к себе."
+L["Shows the locations and sizes of your frames after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = "Показывает местоположение и размеры ваших рамок после активации |cffFFFF00Переключения рамок|r, чтобы помочь лучше выровнять рамки |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r."
+L["Single Decimal Precision"] = "Одна десятичная точность"
+L["Size"] = "Размер"
+L["Spam Merger"] = "Объединение спама"
+L["Special Effects (Procs)"] = "Специальные эффекты (Проки)"
+L["Special Thanks"] = "Особая благодарность"
+L["Specialization"] = "Специализация"
+L["Spell History"] = "История заклинаний"
+L["Spell Name Format"] = "Формат имени заклинания"
+L["Spell Name"] = "Имя заклинания"
+L["Spell School Colors"] = "Цвета школ заклинаний"
+L["Splits overhealing into its own section. Example: +43,000 (O: 12,000)"] = "Разделяет избыточное исцеление на отдельную секцию. Пример: +43,000 (O: 12,000)"
+L["Status Effects"] = "Эффекты статуса"
+L["Subtract Overhealing"] = "Вычитать избыточное исцеление"
+L["Subtract the overhealed amount from the Total Amount"] = "Вычитать сумму избыточного исцеления из общей суммы"
+L["Symbol for: |cffFF0000Billions|r |cff798BDD(10e+9)|r"] = "Символ для: |cffFF0000Миллиардов|r |cff798BDD(10e+9)|r"
+L["Symbol for: |cffFF0000Millions|r |cff798BDD(10e+6)|r"] = "Символ для: |cffFF0000Миллионов|r |cff798BDD(10e+6)|r"
+L["Symbol for: |cffFF0000Thousands|r |cff798BDD(10e+3)|r"] = "Символ для: |cffFF0000Тысяч|r |cff798BDD(10e+3)|r"
+L["Test"] = "Тест"
+L["Text Direction"] = "Направление текста"
+L["The ID of the |cff798BDDItem|r you want to filter."] = "ID |cff798BDDПредмета|r, который вы хотите отфильтровать."
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = "ID |cff798BDDЗаклинания|r, который вы хотите отфильтровать."
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = "ID |cff798BDDЗаклинания|r, который вы хотите отфильтровать."
+L["The Z-Layer to place the |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames onto. If you find that another addon is in front of |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames, try increasing the Frame Strata."] = "Z-слой для размещения рамок |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r. Если вы обнаружите, что другой аддон находится перед рамками |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r, попробуйте увеличить слой рамки."
+L["The amount to offset the vertical origin of the directional damage numbers when they appear. (e.g. move them up and down)\n\n0 = Default"] = "Величина смещения вертикального начала чисел направленного урона при их появлении. (например, перемещение вверх и вниз)\n\n0 = По умолчанию"
+L["The amount to scale the distance that directional damage numbers will move as they appear. Damage numbers will just scroll up if this is disabled.\n\n0 = Disabled\n1 = Default\n3.6 = Recommended"] = "Величина масштабирования расстояния, на которое числа направленного урона будут перемещаться при появлении. Числа урона будут просто прокручиваться вверх, если это отключено.\n\n0 = Отключено\n1 = По умолчанию\n3.6 = Рекомендуется"
+L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "Длительность анимации затухания. |cffFFFF00(По умолчанию: |cff798BDD0.3|r)|r\n\n|cffFF0000Требуется:|r |cffFFFF00Использовать пользовательское затухание|r"
+L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "Длительность отображения текста в рамке. |cffFFFF00(По умолчанию: |cff798BDD5|r)|r\n\n|cffFF0000Требуется:|r |cffFFFF00Использовать пользовательское затухание|r"
+L["The following settings allow you to tweak Blizzard's Floating Combat Text."] = "Следующие настройки позволяют настроить всплывающий текст боя Blizzard."
+L["The following settings are marked as experimental. They should all work, but they might not be very useful. Expect changes or updates to these in the near future.\n\nClick |cffFFFF00Set All|r to apply setting to all |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames.\n"] = "Следующие настройки помечены как экспериментальные. Они должны работать, но могут быть не очень полезны. Ожидайте изменений или обновлений в ближайшем будущем.\n\nНажмите |cffFFFF00Установить все|r, чтобы применить настройки ко всем рамкам |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r.\n"
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Power Word: Fortitude')."] = "Полное, чувствительное к регистру название |cff1AFF1AБаффа|r, которое вы хотите отфильтровать (например, 'Слово силы: Стойкость')."
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Shadow Word: Pain')."] = "Полное, чувствительное к регистру название |cff1AFF1AБаффа|r, которое вы хотите отфильтровать (например, 'Слово тьмы: Боль')."
+L["The full, case-sensitive name of the |cff1AFF1AProc|r you want to filter (e.g. 'Power Word: Fortitude')."] = "Полное, чувствительное к регистру название |cff1AFF1AПрока|r, которое вы хотите отфильтровать (например, 'Слово силы: Стойкость')."
+L["The interval (seconds) in which ALL pet damage will be merged. It will use your pet's icon instead of an spell icon. Use 0 to disable."] = "Интервал (в секундах), в течение которого весь урон питомца будет объединен. Будет использоваться иконка питомца вместо иконки заклинания. Используйте 0, чтобы отключить."
+L["The interval (seconds) in which all other spells will be merged. Certain spells have other intervals, see the tabs for them. Use 0 to disable."] = "Интервал (в секундах), в течение которого все остальные заклинания будут объединены. Некоторые заклинания имеют другие интервалы, см. соответствующие вкладки. Используйте 0, чтобы отключить."
+L["The interval (seconds) in which dispells are merged together. Only dispells for the same aura (by name) will be merged. Use 0 to disable."] = "Интервал (в секундах), в течение которого рассеивания объединяются. Объединяются только рассеивания для одной и той же ауры (по названию). Используйте 0, чтобы отключить."
+L["The interval (seconds) in which incoming damage will be merged. Different messages will still be displayed for different spells. Use 0 to disable."] = "Интервал (в секундах), в течение которого входящий урон будет объединен. Разные сообщения будут отображаться для разных заклинаний. Используйте 0, чтобы отключить."
+L["The interval (seconds) in which incoming full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = "Интервал (в секундах), в течение которого входящие полные промахи, уклонения и парирования будут объединены. Разные сообщения будут отображаться для разных типов промахов. Используйте 0, чтобы отключить."
+L["The interval (seconds) in which incoming healing will be merged. All healing done by the same person will be merged together! Use 0 to disable."] = "Интервал (в секундах), в течение которого входящее исцеление будет объединено. Все исцеления, выполненные одним и тем же человеком, будут объединены вместе! Используйте 0, чтобы отключить."
+L["The interval (seconds) in which outgoing full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = "Интервал (в секундах), в течение которого исходящие полные промахи, уклонения и парирования будут объединены. Разные сообщения будут отображаться для разных типов промахов. Используйте 0, чтобы отключить."
+L["The interval (seconds) in which reputation gains / losses are merged together. Use 0 to disable."] = "Интервал (в секундах), в течение которого получения/потери репутации объединяются. Используйте 0, чтобы отключить."
+L["The merge interval for a lot of spells can be set via the 'Class Spells', 'Global Spells/Items' and 'Racial Spells' tabs."] = "Интервал объединения для многих заклинаний можно установить через вкладки 'Заклинания класса', 'Глобальные заклинания/предметы' и 'Расовые заклинания'."
+L["The minimal amount of damage required for a critical in order for it to be displayed."] = "Минимальная сумма урона, необходимая для отображения критического удара."
+L["The minimal amount of damage required in order for it to be displayed."] = "Минимальная сумма урона, необходимая для отображения."
+L["The minimal amount of healing required for a critical in order for it to be displayed."] = "Минимальная сумма исцеления, необходимая для отображения критического исцеления."
+L["The minimal amount of healing required in order for it to be displayed."] = "Минимальная сумма исцеления, необходимая для отображения."
+L["The minimal amount of player's power required in order for it to be displayed."] = "Минимальная сумма силы игрока, необходимая для отображения."
+L["The name will be colored according to it's environmental type."] = "Имя будет окрашено в соответствии с типом окружения."
+L["The spell ID of the |cff71d5ffOutgoing Spell|r you want to filter."] = "ID заклинания |cff71d5ffИсходящего заклинания|r, которое вы хотите отфильтровать."
+L["The spell name will be colored according to it's spell school."] = "Имя заклинания будет окрашено в соответствии с его школой заклинаний."
+L["The type will be colored according to it's environmental type."] = "Тип будет окрашен в соответствии с типом окружения."
+L["The |cffFFFF00Hide Config in Combat|r option was added to prevent |cffFFFF00xCT+|r from tainting your UI. It is highly recommended left enabled."] = "Опция |cffFFFF00Скрыть конфигурацию в бою|r была добавлена, чтобы предотвратить искажение интерфейса |cffFFFF00xCT+|r. Настоятельно рекомендуется оставить ее включенной."
+L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."] = "Настройки |cffFFFF00Имена|r позволяют добавлять имя игрока / NPC / заклинания к каждому сообщению. Объединение спама скрывает имена игроков / NPC, если были поражены разные игроки / NPC."
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Damage|r to your character."] = "Эти опции позволяют отфильтровать определенные |cffFFFF00ID заклинаний|r из |cff798BDDВходящего урона|r для вашего персонажа."
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Healing|r to your character."] = "Эти опции позволяют отфильтровать определенные |cffFFFF00ID заклинаний|r из |cff798BDDВходящего исцеления|r для вашего персонажа."
+L["These options allow you to filter out spell |cffFFFF00Procs|r that your player triggers."] = "Эти опции позволяют отфильтровать |cffFFFF00Проки|r заклинаний, которые активирует ваш игрок."
+L["These options allow you to filter out |cff1AFF1ABuff|r auras that your player gains or loses."] = "Эти опции позволяют отфильтровать ауры |cff1AFF1AБаффов|r, которые ваш игрок получает или теряет."
+L["These options allow you to filter out |cff8020FFItems|r that your player collects."] = "Эти опции позволяют отфильтровать |cff8020FFПредметы|r, которые собирает ваш игрок."
+L["These options allow you to filter out |cffFF1A1ADebuff|r auras that your player gains or loses."] = "Эти опции позволяют отфильтровать ауры |cffFF1A1AДебаффов|r, которые ваш игрок получает или теряет."
+L["These options allow you to filter |cff71d5ffOutgoing Spells|r that your player does."] = "Эти опции позволяют отфильтровать |cff71d5ffИсходящие заклинания|r, которые использует ваш игрок."
+L["Thick Outline"] = "Толстый контур"
+L["This option helps prevent UI taints by closing the config when you enter combat.\n\n|cffFF8000Highly Recommended Enabled|r"] = "Эта опция помогает предотвратить искажения интерфейса, закрывая конфигурацию при входе в бой.\n\n|cffFF8000Настоятельно рекомендуется включить|r"
+L["Thousand Symbol"] = "Символ тысячи"
+L["ToolTip |cffAAFF80(Highest)|r"] = "Подсказка |cffAAFF80(Самый высокий)|r"
+L["Total Items"] = "Всего предметов"
+L["Track all Spells"] = "Отслеживать все заклинания"
+L["Track all the spells that you've seen. This will make filtering them out easier."] = "Отслеживать все заклинания, которые вы видели. Это упростит их фильтрацию."
+L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "Отключите, чтобы полностью отключить затухание.\n\n|cffFF0000Требуется:|r |cffFFFF00Использовать пользовательское затухание|r"
+L["Unit Killed"] = "Юнит убит"
+L["Unknown Item"] = "Неизвестный предмет"
+L["Unknown"] = "Неизвестно"
+L["Up"] = "Вверх"
+L["Use Custom Fade"] = "Использовать пользовательское затухание"
+L["Use other threshold for Crits"] = "Использовать другой порог для критических ударов"
+L["Vehicle Color"] = "Цвет транспорта"
+L["Vertical Offset"] = "Вертикальное смещение"
+L["Visibility Duration"] = "Длительность видимости"
+L["When icons are disabled, you can still enable invisible icons to line up text."] = "Когда иконки отключены, вы все еще можете включить невидимые иконки для выравнивания текста."
+L["When moving the Frames"] = "При перемещении рамок"
+L["Which color do you want the merged pet messages to be?"] = "Какой цвет вы хотите для объединенных сообщений питомца?"
+L["Whitelist"] = "Белый список"
+L["Will also attempt to show the player pet's healing."] = "Также попытается показать исцеление питомца игрока."
+L["Will not display any items that are below this quality (does not filter Quest or Crafted items)."] = "Не будет отображать предметы ниже этого качества (не фильтрует квестовые или созданные предметы)."
+L["Write me a PM on Curseforge:"] = "Напишите мне личное сообщение на Curseforge:"
+L["You can change how |cffFFFF00xCT+|r shows you names in the |cffFFFF00Names|r section of most frames."] = "Вы можете изменить, как |cffFFFFFleet00xCT+|r показывает вам имена в разделе |cffFFFF00Имена|r большинства рамок."
+L["Your Dispells / Spell Steals"] = "Ваши рассеивания / кражи заклинаний"
+L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"] = "\n\n|cffFF0000Требуется прокручивающийся боевой текст для себя|r"
+L["ruRU Translators"] = "Переводчики ruRU"
+L["zhCN Translators"] = "Переводчики на zhCN"
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = "|CffFF0000Требуется:|r |cff00FF33/reload|r после изменения"
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = "|CffFF0000Требуется:|r |cff00FF33/reload|r после изменения"
+L["|cff798BDDEnvironment|r: Displays 'Environment' as the one who damaged you.\n\n|cff798BDDDamage Types|r: Displays the damage type e.g. "] = "|cff798BDDОкружение|r: Отображает 'Окружение' как того, кто нанес вам урон.\n\n|cff798BDDТипы урона|r: Отображает тип урона, например, "
+L["|cff798BDDMiscellaneous Settings|r:"] = "|cff798BDDРазличные настройки|r:"
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = "|cff798BDDНет|r - Отключено\n\n|cff798BDDИмя NPC|r - Имя NPC, затронутого событием. Пусто при использовании объединения заклинаний и поражении разных целей.\n\n|cff798BDDИмя заклинания|r - Название заклинания."
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = "|cff798BDDНет|r - Отключено\n\n|cff798BDDИмя игрока|r - Имя игрока, затронутого событием. Пусто при использовании объединения заклинаний и поражении разных целей.\n\n|cff798BDDИмя заклинания|r - Название заклинания."
+L["|cff808080Class Combo Points|r"] = "|cff808080Комбо-очки класса|r"
+L["|cffFF0000x|rCT|cffFFFF00+|r |cff798BDDConfiguration Tool|r\n"] = "|cffFF0000x|rCT|cffFFFF00+|r |cff798BDDИнструмент настройки|r\n"
+L["|cffFFFF00Helpful Tips:|r\n\n"] = "|cffFFFF00Полезные советы:|r\n\n"
+L["|cffFFFF00Thank You GitHub Contributors!|r"] = "|cffFFFF00Спасибо контрибьюторам GitHub!|r"
+L["|cffFFFF00xCT+|r has several different ways it will merge critical hits. You can check them out in the |cffFFFF00Spam Merger|r section."] = "|cffFFFF00xCT+|r имеет несколько способов объединения критических ударов. Вы можете ознакомиться с ними в разделе |cffFFFF00Объединение спама|r."
+L["|cffFFFFFFClass Power|r"] = "|cffFFFFFFСила класса|r"
+L["|cffFFFFFFFilter:|r |cff798BDDBuffs|r"] = "|cffFFFFFFФильтр:|r |cff798BDDБаффы|r"
+L["|cffFFFFFFFilter:|r |cff798BDDDebuffs|r"] = "|cffFFFFFFФильтр:|r |cff798BDDДебаффы|r"
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Damage|r"] = "|cffFFFFFFФильтр:|r |cff798BDDВходящий урон|r"
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Healing|r"] = "|cffFFFFFFФильтр:|r |cff798BDDВходящее исцеление|r"
+L["|cffFFFFFFFilter:|r |cff798BDDItems|r"] = "|cffFFFFFFФильтр:|r |cff798BDDПредметы|r"
+L["|cffFFFFFFFilter:|r |cff798BDDOutgoing Spells|r"] = "|cffFFFFFFФильтр:|r |cff798BDDИсходящие заклинания|r"
+L["|cffFFFFFFFilter:|r |cff798BDDProcs|r"] = "|cffFFFFFFФильтр:|r |cff798BDDПроки|r"
+L["|cffFFFFFFGeneral|r"] = "|cffFFFFFFОбщие|r"
+L["|cffFFFFFFIncoming Damage|r"] = "|cffFFFFFFВходящий урон|r"
+L["|cffFFFFFFIncoming Healing|r"] = "|cffFFFFFFВходящее исцеление|r"
+L["|cffFFFFFFLoot, Currency & Money|r"] = "|cffFFFFFFДобыча, валюта и деньги|r"
+L["|cffFFFFFFOutgoing Damage|r"] = "|cffFFFFFFИсходящий урон|r"
+L["|cffFFFFFFOutgoing Healing|r"] = "|cffFFFFFFИсходящее исцеление|r"
+L["|cffFFFFFFOutgoing|r |cff798BDD(Criticals)|r"] = "|cffFFFFFFИсходящие|r |cff798BDD(Критические)|r"
+L["|cffFFFFFFSpecial Effects|r |cff798BDD(Procs)|r"] = "|cffFFFFFFСпециальные эффекты|r |cff798BDD(Проки)|r"
diff --git a/xCT+Options/locales/zhCN.lua b/xCT+Options/locales/zhCN.lua
new file mode 100644
index 00000000..4b19e3d5
--- /dev/null
+++ b/xCT+Options/locales/zhCN.lua
@@ -0,0 +1,525 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Feylynn-Antonidas EU ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+-- Translators:
+-- * https://www.curseforge.com/members/%E8%90%8C%E4%B8%B6%E6%B1%89%E4%B8%B6%E7%BA%B8/projects
+-- * https://github.com/fredako
+
+local AddonName = ...
+
+-- 3rd param: isDefault
+-- 4th param: silent
+local L = LibStub:GetLibrary("AceLocale-3.0"):NewLocale(AddonName, "zhCN", false, false)
+if not L then return end
+
+L[" Version: %s "] = " 版本: %s "
+L["A frame to forward messages to when this frame is disabled."] = "当这个框体被禁用时要转发信息的框体."
+L["A list of |cff1AFF1ABuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cff1AFF1ABuff|r名称列表. |cffFF0000需要:|r |cff798BDD追踪法术历史|r"
+L["A list of |cff1AFF1AProcs|r that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cff1AFF1A触发|r名称列表. |cffFF0000需要:|r |cff798BDD追踪法术历史|r"
+L["A list of |cff71d5ffOutgoing Spell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cff71d5ff输出法术|rID列表. |cffFF0000需要:|r |cff798BDD追踪法术历史|r"
+L["A list of |cff798BDDItem|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cff798BDD物品|rID列表. |cffFF0000需要:|r |cff798BDD追踪法术历史|r"
+L["A list of |cff798BDDSpell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cff798BDD法术|rID列表. |cffFF0000需要:|r |cff798BDD跟踪法术历史|r"
+L["A list of |cffFF0000Debuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"] = "已见过的|cffFF0000Debuff|r名称列表. |cffFF0000需要:|r |cff798BDD追踪法术历史|r"
+L["Abbreviate Numbers"] = "数字缩写"
+L["Absorbed Healing"] = "吸收治疗"
+L["Add new Buff to filter"] = "添加新的Buff以进行过滤"
+L["Add new Debuff to filter"] = "添加新的Debuff以进行过滤"
+L["Add new Item to filter"] = "添加新的物品到过滤"
+L["Add new Proc to filter"] = "添加新的触发到过滤"
+L["Add new Spell to filter"] = "添加新的法术到过滤"
+L["Add these character(s) after the amount of a critical hit."] = "在爆击伤害数值后添加这些字符."
+L["Add these character(s) before the amount of a critical hit."] = "在爆击伤害数值前添加这些字符."
+L["Add these character(s) to the beginning of the message."] = "在消息的开头添加这些字符."
+L["Add these character(s) to the end of the message."] = "在消息的结尾添加这些字符."
+L["Add via History"] = "通过历史添加"
+L["Add via ID"] = "通过ID添加"
+L["Add via Name"] = "通过名字添加"
+L["Additional Settings"] = "额外设置"
+L["Adds the spell ID to each message for this session only."] = "仅在此会话中将法术ID添加到每条消息中."
+L["Advanced"] = "高级"
+L["All Text One Color (Override Color Settings)"] = "所有文字都是一种颜色(覆盖颜色设置)"
+L["Allow Pet Crits"] = "显示宠物爆击"
+L["Allows you to adjust the position of all the xCT+ frames on your screen.\n\nYou can also type: '|cffFF0000/xct lock|r'"] = "允许您调整所有xCT+框体在您屏幕上的位置.\n\n您也可以输入: '|cffFF0000/xct lock|r'"
+L["Allows you to bypass xCT+'s CVar engine. This option might help if you have FCT enabled, but it disappears after awhile. Once you set your FCT options, enable this.\n\n|cffFF0000Changing this requires a UI Reload!|r"] = "许您绕过xCT+的CVar引擎. 如果您启用了FCT这个选项可能会有帮助, 但一段时间后它就会消失. 启用这个一旦您设置了您的FCT选项.\n\n|cffFF0000改变这一点需要重新加载用户界面!|r"
+L["Allows you to customize the fade time of each frame."] = "允许您自定义每个框体的淡出时间."
+L["Allows you to preview xCT+ in order to tweak settings outside of combat.\n\nYou can also type: '|cffFF0000/xct test|r'"] = "允许您预览xCT+以便在战斗之外调整设置.\n\n您也可以输入: '|cffFF0000/xct test|r'"
+L["Appearance"] = "展示"
+L["Background |cffFF0000(Lowest)|r"] = "背景|cffFF0000(最底)|r"
+L["Beta Testers - Version 3.0.0"] = "测试人员 - 版本3.0.0"
+L["Beta Testers - Version 4.0.0 (Curse)"] = "测试人员 - 版本4.0.0 (Curse)"
+L["Beta Testers - Version 4.0.0 (Tukui)"] = "测试人员 - 版本4.0.0 (Tukui)"
+L["Beta Testers - Version 4.3.0+ (Legion)"] = "测试人员 - 版本4.3.0+ (军团再临)"
+L["Beta Testers - Version 4.4.0+ (Battle for Azeroth)"] = "测试人员 - 版本4.3.0+ (争霸艾泽拉斯)"
+L["Billion Symbol"] = "十亿符号"
+L["Buff Gains/Fades"] = "Buff获得/失去"
+L["Bypass CVar Updates (requires |cffFF0000/reload|r)"] = "绕过CVar更新(需要|cffFF0000/reload|r)"
+L["Center"] = "中"
+L["Change all the text in this frame to a specific color."] = "将此框体中的所有文字改为特定颜色."
+L["Change the color for |cff798BDD%s|r."] = "更改|cff798BDD%s|r颜色"
+L["Change the source of |cff798BDDKill Command|r to be the |cffFF8000Player|r. This is helpful when you to turn off |cffFF8000Pet|r damage."] = "将|cff798BDD杀戮命令|r的来源改为|cffFF8000玩家|r. 这在你关闭|cffFF8000宠物|r伤害时很有帮助."
+L["Changes the direction that the text travels in the frame."] = "改变文字在框体中的行进方向."
+L["Check for whitelist, uncheck for blacklist."] = "勾选为白名单, 取消勾选为黑名单."
+L["Check the resources that you do not wish to be displayed for your character:"] = "勾选您不希望在角色上显示的资源:"
+L["Class Power"] = "职业资源"
+L["Class Spells"] = "职业法术"
+L["Clear Frames when leaving combat"] = "离开战斗时清除框体"
+L["Color Blind Mode"] = "色盲模式"
+L["Color Environment"] = "环境颜色"
+L["Color Player Name"] = "玩家名字颜色"
+L["Color Spell Name"] = "法术名字颜色"
+L["Color Type"] = "类型颜色"
+L["Color"] = "颜色"
+L["Colors of the events"] = "事件的颜色"
+L["Colors"] = "颜色"
+L["Crafted Items"] = "制作物品"
+L["Create a comment on Curseforge:"] = "在Curseforge上创建评论:"
+L["Create an issue at GitHub:"] = "在GitHub上创建一个问题:"
+L["Credits"] = "致谢"
+L["Critical Hits"] = "爆击伤害"
+L["Critical Prefix"] = "爆击前缀"
+L["Critical Suffix"] = "爆击后缀"
+L["Criticals that have been merged with the Outgoing frame will not be shown in the Critical frame"] = "已与输出框体合并的爆击将不显示在爆击框体中"
+L["Crits will be merged and the total merged amount in the outgoing frame |cffFF0000DOES NOT|r include crits."] = "爆击将被合并, 并在输出框体中合并总量|cffFF0000不|r包括爆击."
+L["Crits will be merged, but the total merged amount in the outgoing frame includes crits."] = "爆击将被合并, 但在输出框体中合并的总量包括爆击."
+L["Crits will not get merged in the critical frame, but they will be included in the outgoing total. |cffFFFF00(Default)|r"] = "爆击不会被合并到爆击框体中, 但它们将被包括在输出总数中.|cffFFFF00(默认)|r"
+L["Custom Colors"] = "自定义颜色"
+L["Custom"] = "自定义"
+L["Customize Spell School Colors"] = "自定义法术类型颜色"
+L["Damage Type"] = "伤害类型"
+L["Damage"] = "伤害"
+L["Debuff Gains/Fades"] = "Debuff获得/失去"
+L["Decimal Marks"] = "小数点符号"
+L["Dialog"] = "聊天框"
+L["Direction Offset"] = "方向偏移"
+L["Direction Scale"] = "方向比例"
+L["Disable in Combat"] = "战斗中禁用"
+L["Disable"] = "禁用"
+L["Display 'Immune' when your target cannot take damage."] = "当目标无法受到伤害时显示'免疫'."
+L["Display 'Miss', 'Dodge', 'Parry' when you miss your target."] = "当您未击中目标时显示'未命中', '闪避', '格挡'."
+L["Display Environment Name"] = "显示环境名字"
+L["Display NPC Name"] = "显示NPC名字"
+L["Display Player Name"] = "显示玩家名字"
+L["Display partially or fully absorbed damage as regular damage."] = "将部分或完全吸收的伤害显示为常规伤害."
+L["Display the names of harmful auras |cffFF0000(Debuffs)|r that you gain and lose."] = "显示您获得和失去的有害光环的|cffFF0000(Debuff)|r名称."
+L["Display the names of helpful auras |cff00FF00(Buffs)|r that you gain and lose."] = "显示您获得和失去的有益光环的|cff00FF00(Buff)|r名称."
+L["Display the spell you successfully interrupted."] = "显示您成功打断的法术."
+L["Display unit that was killed by your final blow."] = "显示被您最后一击杀死的单位."
+L["Displays 'Low Health/Mana' when your health/mana reaches the low threshold."] = "当您的血量/法力达到低阈值时显示'低血量/法力."
+L["Displays Dodge, Parry, or Miss when you miss incoming damage."] = "当您未能成功抵挡受到伤害时显示闪避, 格挡或未命中."
+L["Displays currency that you gain."] = "显示您获得的货币."
+L["Displays how many items you have in your bag."] = "显示您的包里有多少物品."
+L["Displays items that pertain to a quest."] = "显示与任务有关的物品."
+L["Displays items that were purchased from a vendor."] = "显示从商人处购买的物品."
+L["Displays items that you crafted."] = "显示您制作的物品."
+L["Displays items that you pick up."] = "显示您捡到的物品."
+L["Displays money that you pick up."] = "显示您捡到的金币."
+L["Displays money using letters G, S, and C instead of icons."] = "使用字母G, S和C而不是图标显示货币."
+L["Displays overhealing."] = "显示过量治疗."
+L["Displays when the player is leaving or entering combat."] = "显示玩家离开或进入战斗."
+L["Displays your player's honor gains."] = "显示玩家获得的荣誉."
+L["Displays your player's reputation gains and losses."] = "显示玩家获得和失去声望."
+L["Displays your skill ups in professions."] = "显示您在专业技能上的提升."
+L["Don't Merge Critical Hits Together"] = "不要将爆击伤害合并在一起"
+L["Down"] = "下"
+L["Each frame has a |cffFFFF00Misc|r section; select a frame and select the drop-down box to find it."] = "每个框体都有一个|cffFFFF00其他|r部分; 选择一个框体然后选择下拉框以找到它."
+L["Enable Debugging"] = "启用调试"
+L["Enable Font Shadow"] = "启用字体阴影"
+L["Enable Icons"] = "启用图标"
+L["Enable Scrolling Combat Text (Self)"] = "启用滚动战斗文字(自己)"
+L["Enable Spam Merger"] = "启用垃圾信息合并"
+L["Enable a custom color for |cff798BDD%s|r."] = "为|cff798BDD%s|r启用自定义颜色"
+L["Enable a different threshold for incoming damage criticals."] = "为输出治疗的爆击启用不同的阈值."
+L["Enable a different threshold for incoming healing criticals."] = "为受到治疗的爆击启用不同的阈值."
+L["Enable a different threshold for outgoing damage criticals."] = "为输出伤害的爆击启用不同的阈值."
+L["Enable a different threshold for outgoing healing criticals."] = "为输出治疗的爆击启用不同的阈值."
+L["Enable this option if you have problems with 'floating' icons."] = "如果您遇到'浮动'图标的问题请启用此选项."
+L["Enable this option if you want to see all auto-attacks."] = "如果您想看到所有的自动攻击请启用此选项."
+L["Enable this option if you want to see threat changes."] = "如果您想看到仇恨变化请启用此选项."
+L["Enable this to see when your pet's abilities critical strike, otherwise disable for less combat text spam."] = "启用此功能以查看您的宠物技能的爆击, 否则禁用以减少战斗文字垃圾."
+L["Enable"] = "启用"
+L["Enabled"] = "启用"
+L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "] = "启用数字格式. 这个选项可以在主|cff00FF00框体|r选项页中自定义为|cff798BDD缩写|r或|cff798BDD小数点符号|r."
+L["Environment Format"] = "环境格式"
+L["Environment"] = "环境"
+L["Events from a NPC"] = "来自NPC的事件"
+L["Events from a Player"] = "来自玩家的事件"
+L["Events from the Environment"] = "来自环境的事件"
+L["Events to a NPC"] = "对NPC的事件"
+L["Events to a Player"] = "对玩家的事件"
+L["Fade Out Duration"] = "淡出持续时间"
+L["Fading Text Settings"] = "淡出文本设置"
+L["Filter Item Quality"] = "过滤物品品质"
+L["Filter Resources"] = "过滤资源"
+L["Filtered Buffs |cff798BDD(Uncheck to disable)|r"] = "过滤Buff|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Debuffs |cff798BDD(Uncheck to disable)|r"] = "过滤Debuff|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Incoming Damage |cff798BDD(Uncheck to disable)|r"] = "过滤受到伤害|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Incoming Healing |cff798BDD(Uncheck to disable)|r"] = "过滤受到治疗|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Items |cff798BDD(Uncheck to disable)|r"] = "过滤物品|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Procs |cff798BDD(Uncheck to disable)|r"] = "过滤触发|cff798BDD(取消勾选以禁用)|r"
+L["Filtered Spells |cff798BDD(Uncheck to disable)|r"] = "过滤法术|cff798BDD(取消勾选以禁用)|r"
+L["Filtered auras gains and fades that are |cff1AFF1ABuffs|r will be on a whitelist (opposed to a blacklist)."] = "被过滤获得和失去的|cff1AFF1ABuffs|r的光环将被添加到白名单中(与黑名单相对)."
+L["Filtered auras gains and fades that are |cffFF1A1ADebuffs|r will be on a whitelist (opposed to a blacklist)."] = "被过滤的获得和失去的|cffFF1A1ADebuffs|r的光环将被添加到白名单中(与黑名单相对)."
+L["Filtered |cff71d5ffIncoming Damage Spells|r will be on a whitelist (opposed to a blacklist)."] = "被过滤的|cff71d5ff受到伤害法术|r将被添加到白名单中(与黑名单相对)."
+L["Filtered |cff71d5ffIncoming Healing Spells|r will be on a whitelist (opposed to a blacklist)."] = "被过滤的|cff71d5ff受到治疗法术|r将被添加到白名单中(与黑名单相对)."
+L["Filtered |cff71d5ffOutgoing Spells|r will be on a whitelist (opposed to a blacklist)."] = "被过滤的|cff71d5ff输出法术|r将被添加到白名单中(与黑名单相对)."
+L["Filtered |cff798BDDItems|r will be on a whitelist (opposed to a blacklist)."] = "被过滤的|cff798BDD物品|r将被添加到白名单中(与黑名单相对)."
+L["Filters"] = "过滤"
+L["Floating Combat Text"] = "浮动战斗文字"
+L["Font Outline"] = "字体描边"
+L["Font Settings"] = "字体设置"
+L["Font Shadow Color"] = "字体阴影颜色"
+L["Font Shadow Settings"] = "字体阴影设置"
+L["Font Size"] = "字体大小"
+L["Font"] = "字体"
+L["Format Overhealing"] = "过量治疗格式"
+L["Formats incoming damage to show how much was absorbed. The spam merger hides these reduction and effectively disables this option though."] = "受到伤害格式为显示被吸收的多少. 垃圾信息合并会隐藏这些伤害减少效果并有效禁用此选项."
+L["Formats the looted message to also include the type of item (e.g. Trade Goods, Armor, Junk, etc.)."] = "拾取信息格式也包括物品的类型(例如交易品、装备、垃圾等)."
+L["Frame Alpha"] = "框体透明度"
+L["Frame Settings"] = "框体设置"
+L["Frame Strata"] = "框体层级"
+L["Frame"] = "框体"
+L["Frames"] = "框体"
+L["Fullscreen Dialog"] = "全屏聊天框"
+L["Fullscreen"] = "全屏"
+L["Gained Currency"] = "获得货币"
+L["General"] = "通用"
+L["GitHub Contributors"] = "GitHub贡献者"
+L["Global Frame Settings |cffFFFFFF(Experimental)|r"] = "全局框体设置|cffFFFFFF(试验)|r"
+L["Global Spells / Items"] = "全局法术/物品"
+L["Groups decimals and separates them by commas; this allows for better responsiveness when reading numbers.\n\n|cffFF0000EXAMPLE|r |cff798BDD12,890|r"] = "将小数分组并以逗号分隔; 这使得在读取数字时有更好的反应速度.\n\n|cffFF0000比如|r |cff798BDD12,890|r"
+L["Healing and Absorbs"] = "治疗和吸收"
+L["Hide Absorbed Heals"] = "隐藏吸收的治疗"
+L["Hide Config in Combat"] = "在战斗中隐藏设置"
+L["Hide Merged Criticals"] = "隐藏合并爆击"
+L["High |cffFFFF00(Default)|r"] = "高|cffFFFF00(默认)|r"
+L["HoTs"] = "HoT"
+L["Honor Gains"] = "荣誉获取"
+L["Horizontal Offset"] = "横向偏移"
+L["How to contact me"] = "怎样联系我"
+L["ID"] = "ID"
+L["Icon Settings"] = "图标设置"
+L["Icons"] = "图标"
+L["If enabled, subtract any healing that was absorbed by a |cffFF0000debuff|r from the total."] = "如果启用, 从总治疗量中减去被|cffFF0000debuff|r吸收的任何治疗."
+L["If the player has a realm name attached to her name, it will be removed."] = "如果玩家的名字上有服务器名字, 它将被删除."
+L["If the player's class is known (e.g. is a raid member), it will be colored."] = "如果玩家职业已知(例如是团队成员), 它将被着色."
+L["If there is a certain |cff798BDDSpell|r, |cff798BDDBuff|r, or |cff798BDDDebuff|r that you don't want to see, consider adding it to a |cff798BDDFilter|r."] = "如果有某个|cff798BDD法术|r, |cff798BDDBuff|r或|cff798BDDDebuff|r是您不想看到的, 可以考虑将其添加到|cff798BDD过滤|r."
+L["If you want to |cff798BDDCombine Frame Outputs|r, disable one of the frames and use the |cffFFFF00Secondary Frame|r option on that frame."] = "如果您想要|cff798BDD合并框体输出|r, 请禁用其中一个框体并在该框体上使用|cffFFFF00次级框体|r选项."
+L["Incoming Damage / Healing"] = "受到伤害/治疗"
+L["Incoming Damage"] = "受到伤害"
+L["Incoming Dispells / Spell Steals"] = "受到的驱散/法术偷取"
+L["Incoming Healing"] = "受到治疗"
+L["Incoming Player Power Threshold (Mana, Rage, Energy, etc.)"] = "玩家资源阈值(法力, 怒气, 能量, 等.)"
+L["Interrupts"] = "打断"
+L["Justification"] = "对齐"
+L["Justifies the output to a side."] = "将输出对齐到一侧."
+L["Leave/Enter Combat"] = "离开/进入战斗"
+L["Left"] = "左"
+L["Loot, Currency & Money"] = "拾取, 货币和金币"
+L["Looted Items"] = "拾取物品"
+L["Looted Money"] = "拾取金币"
+L["Low Mana/Health"] = "低法力/血量"
+L["Low"] = "底层"
+L["Make Auto Attack and Swing criticals more visible by adding the prefix and postfix."] = "通过添加前缀和后缀使自动攻击和平砍的爆击更加明显."
+L["Medium"] = "中间"
+L["Merge Critical Hits by Themselves"] = "自行合并爆击伤害"
+L["Merge Critical Hits with Outgoing"] = "将爆击伤害与输出合并"
+L["Merge Options"] = "合并选项"
+L["Merge Vehicle Abilities"] = "载具技能合并"
+L["Merge-Interval Incoming Damage"] = "受到伤害合并间隔"
+L["Merge-Interval Incoming Healing"] = "受到治疗合并间隔"
+L["Merge-Interval Incoming Misses"] = "未命中攻击合并间隔"
+L["Merge-Interval Outgoing Misses"] = "未命中攻击合并间隔"
+L["Merge-Interval for ALL Pet Abilities"] = "所有宠物技能合并间隔"
+L["Merge-Interval for Dispells"] = "驱散合并间隔"
+L["Merge-Interval for Reputation"] = "声望合并间隔"
+L["Merge-Interval for other spells"] = "其他法术合并间隔"
+L["Merges all of your vehicle abilities together."] = "将您所有的载具技能合并在一起"
+L["Million Symbol"] = "百万符号"
+L["Minimal Value Thresholds"] = "最小值阈值"
+L["Minimum Threshold for Crits"] = "爆击的最小阈值"
+L["Minimum Threshold"] = "最小阈值"
+L["Misc"] = "其他"
+L["Miscellaneous Settings"] = "其他设置"
+L["Miss Type Settings"] = "未命中类型设置"
+L["Monochrome Outline"] = "单色描边"
+L["Monochrome"] = "单色"
+L["Move"] = "移动"
+L["NPC Name Color"] = "NPC名字颜色"
+L["NPC Name Format"] = "NPC名字格式"
+L["NPC Name"] = "NPC名字"
+L["Name Prefix"] = "名字前缀"
+L["Name Suffix"] = "名字后缀"
+L["Names to display"] = "要显示的名字"
+L["Names"] = "名字"
+L["No Description"] = "无描述"
+L["No buffs have been added to this list yet."] = "尚未向此列表添加任何buff."
+L["No debuffs have been added to this list yet."] = "尚未向此列表添加任何debuff."
+L["No items have been added to this list yet."] = "尚未向此列表添加任何物品."
+L["No procs have been added to this list yet."] = "尚未向此列表添加任何触发."
+L["No spells have been added to this list yet."] = "尚未向此列表添加任何法术."
+L["None"] = "无"
+L["Normally all damage / heal events of a spell will result in one message each.\nSo AoE spells like Rain of Fire or Spinning Crane Kick will spam a lot of messages into the xCT frames.\nIf the spam merger is enabled, then the damage events in a configured interval of X seconds of each spell will be merged into one message.\n|cffFF0000Drawback|r: the (merged) message will be delayed by the configured interval!!\nUse an interval of 0 to disable the specific merge."] = "通常法术的所有伤害/治疗事件将产生各自的一条消息.\n因此像火焰之雨或神鹤引项踢这样的范围法术会在xCT框架中产生大量消息.\n如果启用了垃圾信息合并, 那么在每个法术的配置时间间隔X秒内的伤害事件将合并为一条消息.\n|cffFF0000缺点|r: 合并后的消息将会延迟配置的时间间隔!!\n使用间隔0来禁用特定的合并."
+L["Number Format Settings"] = "数字格式设置"
+L["Number Formatting"] = "数字格式"
+L["Number of Lines"] = "行数"
+L["On the left list, under the |cffFFFF00Startup Message|r checkbox, you can click on the |cff798BDD+ Buttons|r (plus) to show more options."] = "在左边的列表中, 在|cffFFFF00启动消息|r复选框下, 您可以点击|cff798BDD+按钮|r(加号)来显示更多选项."
+L["Only show the highest partial miss, instead of all the misses. (Rare, but less spammy)\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = "仅显示最高的部分未命中, 而不是所有未命中. (较少见, 但信息更简洁)\n\n|cffFF0000请注意:|r仅在法术未合并时有效. 请关闭法术合并以查看所有法术."
+L["Only the |cffFFFF00General|r, |cffFF8000Outgoing|r, |cffFFFF00Outgoing (Crits)|r, |cffFF8000Incoming Damage|r and |cffFFFF00Healing|r, and |cffFF8000Class Power|r frames can be abbreviated."] = "只有|cffFFFF00通用|r, |cffFF8000输出|r, |cffFFFF00输出(爆击)|r, |cffFF8000受到伤害|r和|cffFFFF00治疗|r, 和|cffFF8000职业能量|r框体可以缩写."
+L["Other"] = "其他"
+L["Outgoing Damage (Criticals)"] = "输出伤害(爆击)"
+L["Outgoing Damage / Healing"] = "输出伤害/治疗"
+L["Outgoing Damage"] = "输出伤害"
+L["Outgoing Healing"] = "输出治疗"
+L["Outline"] = "描边"
+L["Overhealing Postfix"] = "过量治疗后缀"
+L["Overhealing Prefix"] = "过量治疗前缀"
+L["Overhealing"] = "过量治疗"
+L["Parent |cffFF0000(Lowest)|r"] = "父级|cffFF0000(最低)|r"
+L["Pet Attacks"] = "宠物攻击"
+L["Pet Auto Attacks"] = "宠物自动攻击"
+L["Pet Color"] = "宠物颜色"
+L["Pet and Vehicle Damage Settings"] = "宠物和载具伤害设置"
+L["Player Damage Settings"] = "玩家伤害设置"
+L["Player Gains"] = "玩家获得"
+L["Player Name Format"] = "玩家名字格式"
+L["Player Name"] = "玩家名字"
+L["Player Status"] = "玩家状态"
+L["Please choose one:"] = "请选一个:"
+L["Preempt an automatic color with a custom one."] = "用自定义的颜色来取代自动颜色."
+L["Prefix this value to the beginning when displaying a critical amount."] = "在显示爆击时将此值作为开头的前缀."
+L["Prefix this value to the beginning when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = "在显示过量治疗量时将该值作为前缀添加到开头.\n\n|cffFF0000要求:|r |cff798BDD过量治疗格式|r"
+L["Prefix this value to the ending when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"] = "在显示过量治疗量时将该值作为后缀添加到结尾.\n\n|cffFF0000要求:|r |cff798BDD过量治疗格式|r"
+L["Profession skillup"] = "专业技能提升"
+L["Purchased Items"] = "购买的物品"
+L["Quest Items"] = "任务物品"
+L["Racial Spells"] = "种族法术"
+L["Remove Buff from filter"] = "从过滤中移除Buff"
+L["Remove Debuff from filter"] = "从过滤中移除Debuff"
+L["Remove Item from filter"] = "从过滤中移除物品"
+L["Remove Proc from filter"] = "从过滤中移除触发"
+L["Remove Realm Name"] = "移除服务器名字"
+L["Remove Spell from filter"] = "从过滤中移除法术"
+L["Remove filtered Buff"] = "移除过滤Buff"
+L["Remove filtered Debuff"] = "移除过滤Debuff"
+L["Remove filtered Item"] = "移除过滤物品"
+L["Remove filtered proc"] = "移除过滤触发"
+L["Remove filtered spell"] = "移除过滤法术"
+L["Remove the Buff from the config all together."] = "将Buff从配置中彻底移除."
+L["Remove the Debuff from the config all together."] = "将Debuff从配置中彻底移除."
+L["Remove the Item from the config all together."] = "将物品从配置中彻底移除."
+L["Remove the proc from the config all together."] = "将触发从配置中彻底移除."
+L["Remove the spell ID from the config all together."] = "将法术ID从配置中彻底移除."
+L["Reputation Gains/Losses"] = "声望获取/失去"
+L["Reset the prefix and the suffix of criticals to their default setting."] = "将爆击的前缀和后缀重置为默认设置."
+L["Reset"] = "重置"
+L["Resets |cff798BDD%s|r back to the default color."] = "将|cff798BDD%s|r重置为默认颜色"
+L["Restore Defaults"] = "恢复默认设置"
+L["Right"] = "右"
+L["Scrollable Frame Settings"] = "可滚动框体设置"
+L["Secondary Frame"] = "次级框体"
+L["Secondary ID(s)"] = "次级ID"
+L["Set All"] = "设置全部"
+L["Set the font of the frame."] = "设置框体字体."
+L["Set the font outline."] = "设置字体描边."
+L["Set the font size of the frame."] = "设置框体字体大小."
+L["Set the icon size. (Recommended value: 16)"] = "设置图标大小. (推荐数值: 16)"
+L["Sets the alpha of the frame."] = "设置框体透明度."
+L["Show Absorbs (Self)"] = "显示吸收(自己)"
+L["Show Absorbs (Target)"] = "显示吸收(目标)"
+L["Show Absorbs as damage"] = "将吸收显示为伤害"
+L["Show Absorbs"] = "显示吸收"
+L["Show Align Grid"] = "显示对齐网格"
+L["Show Auras"] = "显示光环"
+L["Show Auto Attack Icon"] = "显示自动攻击图标"
+L["Show Auto Attack"] = "显示自动攻击"
+L["Show Auto Attacks (Pre)Postfix"] = "显示自动攻击(前)后缀"
+L["Show Auto Attacks"] = "显示自动攻击"
+L["Show Combat State"] = "显示战斗状态"
+L["Show Combo Points"] = "显示连击点"
+L["Show Damage Reduction"] = "显示伤害减免"
+L["Show Damage"] = "显示伤害"
+L["Show DoTs"] = "显示DoT"
+L["Show Effects (All)"] = "显示效果(所有)"
+L["Show Effects (Group)"] = "显示效果(队伍)"
+L["Show Effects (Mine)"] = "显示效果(我的)"
+L["Show Energy (Periodic)"] = "显示能量(周期)"
+L["Show Energy Type"] = "显示能量类型"
+L["Show Energy"] = "显示能量"
+L["Show Friendly Healers"] = "显示友方治疗"
+L["Show Gains"] = "显示获得"
+L["Show Healing"] = "显示治疗"
+L["Show HoTs"] = "显示HoT"
+L["Show Honor"] = "显示荣誉"
+L["Show Immunes"] = "显示免疫效果"
+L["Show Invisible Icons"] = "显示不可见图标"
+L["Show Item Types"] = "显示物品类型"
+L["Show Kill Command"] = "显示杀戮命令"
+L["Show Low HP/Mana"] = "显示低血量/法力"
+L["Show Miss Types"] = "显示未命中类型"
+L["Show Misses, Dodges, Parries"] = "显示未命中, 闪避, 格挡"
+L["Show My Heals Only"] = "仅显示我的治疗"
+L["Show Outgoing Damage"] = "显示输出伤害"
+L["Show Overhealing"] = "显示过量治疗"
+L["Show Overheals"] = "显示过量治疗"
+L["Show Pet Damage"] = "显示宠物伤害"
+L["Show Pet Heals Too"] = "也显示宠物治疗"
+L["Show Pet Melee"] = "显示宠物近战攻击"
+L["Show Pet Spells"] = "显示宠物法术"
+L["Show Positions"] = "显示位置"
+L["Show Reactives"] = "显示警告"
+L["Show Reductions"] = "显示吸收"
+L["Show Rep Changes"] = "显示声望变化"
+L["Show Threat Changes"] = "显示仇恨变化"
+L["Show Vehicle Damage"] = "显示载具伤害"
+L["Show criticals from Auto Attack and Swings. If disabled, they will be displayed as non-critical auto attacks. They will be merged into the Outgoing frame."] = "显示自动攻击和平砍爆击信息. 如果禁用它们将被显示为非爆击的自动攻击. 它们将被合并到输出框体中."
+L["Show damage that your vehicle does. This can be anything from a vehicle you are controlling to Hati, the beast mastery pet."] = "显示您的载具造成的伤害.这可以是你控制的任何载具,或者是哈提, 野兽控制的宠物."
+L["Show damage you do."] = "显示您的伤害."
+L["Show icons from Auto Attacks."] = "显示自动攻击图标."
+L["Show icons next to your damage."] = "显示您的伤害旁的图标."
+L["Show icons."] = "显示图标."
+L["Show instant gains of class resources (e. g. energy, runic power, ...)."] = "显示职业资源的实时获得(例如能量, 符文能量等)."
+L["Show partial Misses, Dodges, Parries"] = "显示部分未命中, 闪避, 格挡"
+L["Show the Highest Partial Miss"] = "显示最高的部分未命中"
+L["Show the overhealing you receive from other players."] = "显示您从其他玩家那里获得的过量治疗."
+L["Show the spell that somebody else dispelled on you or stole a buff/debuff from you."] = "显示其他人驱散您身上的法术或偷取您身上的buff/debuff."
+L["Show the spell that you dispelled or stole."] = "显示您驱散或偷取的法术."
+L["Show the type of energy that you are gaining."] = "显示您正在获得的能量类型."
+L["Show when your target takes only a percentage of your damage because it was partially absorbed, resisted, or blocked.\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."] = "显示当您的目标只因为部分吸收, 抵抗或格挡而承受您伤害的一定百分比.\n\n|cffFF0000请注意:|r仅在法术未合并时有效. 请关闭法术合并以查看所有法术."
+L["Show your Damage-Over-Time (DOT) damage. (|cffFF0000Requires:|r Outgoing Damage)"] = "显示您的DoT伤害.(|cffFF0000需要:|r 输出伤害)"
+L["Show your Heal-Over-Time (HOT) healing."] = "显示您的HoT治疗."
+L["Show your non-critical, auto attack damage."] = "显示您的非爆击自动攻击伤害."
+L["Show your pet's damage. Beast Mastery hunters should also look at vehicle damage."] = "显示您的宠物伤害. 野兽控制猎人也应该关注载具伤害."
+L["Show your pet's non-critical, auto attacks."] = "显示您宠物的非爆击自动攻击伤害."
+L["Shows a grid after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = "在您|cffFFFF00切换框体|r后显示一个网格帮助您更好地对齐|cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r框体."
+L["Shows a shadow behind the combat text fonts."] = "在战斗文字字体后面显示阴影."
+L["Shows a single digit of precision when abbreviating the value (e.g. will show |cff798BDD5.9K|r instead of |cff798BDD6K|r)."] = "在缩写数值时显示单一小数精度(例如显示|cff798BDD5.9K|r而不是|cff798BDD6K|r)."
+L["Shows absorbs you gain from other players."] = "显示您从其他玩家那里获得的吸收效果."
+L["Shows incoming damage and healing done to you. It is also required for a lot of the other events to work (as noted in their descriptions).\n\n|cffFF0000Changing this requires a UI Reload!|r"] = "显示受到伤害和治疗. 它也是许多其他事件工作的必要条件(正如其描述中所指出的).\n\n|cffFF0000改变这一点需要重新加载用户界面!|r"
+L["Shows only the player's healing done to himself or herself."] = "仅显示该玩家对自己施加的治疗."
+L["Shows the locations and sizes of your frames after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."] = "在您|cffFFFF00切换框体|r后显示框体的位置和大小帮助您更好地对齐|cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r框体."
+L["Single Decimal Precision"] = "单一小数精度"
+L["Size"] = "大小"
+L["Spam Merger"] = "垃圾信息合并"
+L["Special Effects (Procs)"] = "特效(触发)"
+L["Special Thanks"] = "特别感谢"
+L["Specialization"] = "专精:"
+L["Spell History"] = "法术历史"
+L["Spell Name Format"] = "法术名字格式"
+L["Spell Name"] = "法术名字"
+L["Spell School Colors"] = "法术类型颜色"
+L["Splits overhealing into its own section. Example: +43,000 (O: 12,000)"] = "将过量治疗分成自己的部分. 例如: +43,000 (O: 12,000)"
+L["Status Effects"] = "状态效果"
+L["Subtract Overhealing"] = "减去过量治疗"
+L["Subtract the overhealed amount from the Total Amount"] = "从总量中减去过量治疗"
+L["Symbol for: |cffFF0000Billions|r |cff798BDD(10e+9)|r"] = "符号表示: |cffFF0000十亿|r|cff798BDD(10e+9)|r"
+L["Symbol for: |cffFF0000Millions|r |cff798BDD(10e+6)|r"] = "符号表示: |cffFF0000百万|r|cff798BDD(10e+6)|r"
+L["Symbol for: |cffFF0000Thousands|r |cff798BDD(10e+3)|r"] = "符号表示: |cffFF0000千位|r|cff798BDD(10e+3)|r"
+L["Test"] = "测试"
+L["Text Direction"] = "文字方向"
+L["The ID of the |cff798BDDItem|r you want to filter."] = "您想要过滤的|cff798BDD物品|r的物品ID."
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = "您想要过滤的|cff798BDD法术|r的法术ID."
+L["The Spell ID of the |cff798BDDSpell|r you want to filter."] = "您想要过滤的|cff798BDD法术|r的法术ID."
+L["The Z-Layer to place the |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames onto. If you find that another addon is in front of |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames, try increasing the Frame Strata."] = "设置|cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r框体的Z层级. 如果您发现另一个插件挡住了|cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r框体, 可以尝试提高框体层级."
+L["The amount to offset the vertical origin of the directional damage numbers when they appear. (e.g. move them up and down)\n\n0 = Default"] = "当伤害数字出现时其与垂直原点的距离.(例如上下移动它们)\n\n0 = 默认值"
+L["The amount to scale the distance that directional damage numbers will move as they appear. Damage numbers will just scroll up if this is disabled.\n\n0 = Disabled\n1 = Default\n3.6 = Recommended"] = "调整方位伤害数字出现时的移动距离. 如果禁用这个功能伤害数字将只是向上滚动.\n\n0 = 禁用\n1 = 默认值\n3.6 = 推荐"
+L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "淡出动画的持续时间. |cffFFFF00(默认: |cff798BDD0.3|r)|r\n\n|cffFF0000需要:|r |cffFFFF00使用自定义淡出|r"
+L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "文本在框体中显示的持续时间. |cffFFFF00(默认: |cff798BDD5|r)|r\n\n|cffFF0000需要:|r |cffFFFF00使用自定义淡出|r"
+L["The following settings allow you to tweak Blizzard's Floating Combat Text."] = "以下设置允许您调整暴雪的浮动战斗文字."
+L["The following settings are marked as experimental. They should all work, but they might not be very useful. Expect changes or updates to these in the near future.\n\nClick |cffFFFF00Set All|r to apply setting to all |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames.\n"] = "以下设置被标记为实验性的. 他们应该都能工作但他们可能不是很有用. 期待在不久的将来对这些内容进行修改或更新.\n\n点击|cffFFFF00设置全部|r将设置应用于所有|cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r框体.\n"
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Power Word: Fortitude')."] = "您想要过滤的|cff1AFF1ABuff|r的完整(区分大小写)名称(例如'Power Word: Fortitude')."
+L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Shadow Word: Pain')."] = "您想要过滤的|cff1AFF1ABuff|r的完整(区分大小写)名称(例如'Shadow Word: Pain')."
+L["The full, case-sensitive name of the |cff1AFF1AProc|r you want to filter (e.g. 'Power Word: Fortitude')."] = "您想要过滤的|cff1AFF1A触发|r的完整(区分大小写)名称(例如'Power Word: Fortitude')."
+L["The interval (seconds) in which ALL pet damage will be merged. It will use your pet's icon instead of an spell icon. Use 0 to disable."] = "所有宠物伤害的合并时间间隔(秒). 将使用您宠物的图标而不是法术图标. 使用0来禁用."
+L["The interval (seconds) in which all other spells will be merged. Certain spells have other intervals, see the tabs for them. Use 0 to disable."] = "所有其他法术的合并时间间隔(秒). 某些法术有其他间隔, 请查看相应的选项卡. 使用0来禁用."
+L["The interval (seconds) in which dispells are merged together. Only dispells for the same aura (by name) will be merged. Use 0 to disable."] = "驱散合并的时间间隔(秒). 只有相同光环(按名称)的驱散将被合并. 使用0来禁用."
+L["The interval (seconds) in which incoming damage will be merged. Different messages will still be displayed for different spells. Use 0 to disable."] = "受到伤害的合并时间间隔(秒). 不同法术的消息仍将显示为不同的内容. 使用0来禁用."
+L["The interval (seconds) in which incoming full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = "合并受到完全未命中, 躲闪和格挡的时间间隔(秒). 不同类型的未命中仍将显示不同的消息. 使用0来禁用."
+L["The interval (seconds) in which incoming healing will be merged. All healing done by the same person will be merged together! Use 0 to disable."] = "受到治疗的合并时间间隔(秒). 同一个人施加的所有治疗将被合并在一起! 使用0来禁用."
+L["The interval (seconds) in which outgoing full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."] = "合并输出完全未命中, 躲闪和格挡的时间间隔(秒). 不同类型的未命中仍将显示不同的消息. 使用0来禁用."
+L["The interval (seconds) in which reputation gains / losses are merged together. Use 0 to disable."] = "合并声望获得/失去的时间间隔(秒). 使用0来禁用."
+L["The merge interval for a lot of spells can be set via the 'Class Spells', 'Global Spells/Items' and 'Racial Spells' tabs."] = "许多法术的合并间隔可以通过'职业法术', '全局法术/物品'和'种族法术'选项卡进行设置."
+L["The minimal amount of damage required for a critical in order for it to be displayed."] = "显示爆击所需的最小伤害."
+L["The minimal amount of damage required in order for it to be displayed."] = "显示所需的最小伤害."
+L["The minimal amount of healing required for a critical in order for it to be displayed."] = "显示爆击所需的最小治疗."
+L["The minimal amount of healing required in order for it to be displayed."] = "显示所需的最小治疗."
+L["The minimal amount of player's power required in order for it to be displayed."] = "显示所需的最小玩家能量值."
+L["The name will be colored according to it's environmental type."] = "名字将根据它的环境类型而着色."
+L["The spell ID of the |cff71d5ffOutgoing Spell|r you want to filter."] = "您想要过滤的|cff71d5ff输出法术|r的法术ID."
+L["The spell name will be colored according to it's spell school."] = "法术名字将根据它的法术类型而被着色."
+L["The type will be colored according to it's environmental type."] = "该类型将根据它的环境类型而被着色."
+L["The |cffFFFF00Hide Config in Combat|r option was added to prevent |cffFFFF00xCT+|r from tainting your UI. It is highly recommended left enabled."] = "|cffFFFF00在战斗中隐藏配置|r选项的添加是为了防止|cffFFFF00xCT+|r污染您的用户界面. 强烈建议保持启用."
+L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."] = "|cffFFFF00名字设置|r允许您在每条消息中添加玩家/NPC/法术名称. 如果不同的玩家/NPC受到了攻击, 垃圾信息合并将隐藏玩家/NPC名称."
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Damage|r to your character."] = "这些选项允许您通过|cffFFFF00法术ID|r过滤掉角色|cff798BDD受到伤害|r."
+L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Healing|r to your character."] = "这些选项允许您通过|cffFFFF00法术ID|r过滤掉角色|cff798BDD受到治疗|r."
+L["These options allow you to filter out spell |cffFFFF00Procs|r that your player triggers."] = "这些选项允许您过滤掉玩家触发的法术|cffFFFF00触发|r."
+L["These options allow you to filter out |cff1AFF1ABuff|r auras that your player gains or loses."] = "这些选项允许您过滤掉您角色获得或失去的|cff1AFF1ABuff|r光环."
+L["These options allow you to filter out |cff8020FFItems|r that your player collects."] = "这些选项允许您过滤掉玩家收集的|cff8020FF物品|r."
+L["These options allow you to filter out |cffFF1A1ADebuff|r auras that your player gains or loses."] = "这些选项允许您过滤掉您角色获得或失去的|cffFF1A1ADebuff|r光环."
+L["These options allow you to filter |cff71d5ffOutgoing Spells|r that your player does."] = "这些选项允许您过滤玩家施放的|cff71d5ff输出法术|r."
+L["Thick Outline"] = "粗体描边"
+L["This option helps prevent UI taints by closing the config when you enter combat.\n\n|cffFF8000Highly Recommended Enabled|r"] = "这个选项有助于防止用户界面污染, 当您进入战斗时关闭设置.\n\n|cffFF8000强烈推荐启用|r"
+L["Thousand Symbol"] = "千位符号"
+L["ToolTip |cffAAFF80(Highest)|r"] = "提示信息|cffAAFF80(最高)|r"
+L["Total Items"] = "物品总数"
+L["Track all Spells"] = "追踪所有法术"
+L["Track all the spells that you've seen. This will make filtering them out easier."] = "追踪您所见的所有法术. 这将使过滤它们变得更容易."
+L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"] = "关闭以禁用所有淡出功能.\n\n|cffFF0000需要:|r |cffFFFF00使用自定义淡出|r"
+L["Unit Killed"] = "击杀单位"
+L["Unknown Item"] = "未知物品"
+L["Unknown"] = "未知"
+L["Up"] = "上"
+L["Use Custom Fade"] = "使用自定义淡出"
+L["Use other threshold for Crits"] = "对爆击使用其他阈值"
+L["Vehicle Color"] = "载具颜色"
+L["Vertical Offset"] = "纵向偏移"
+L["Visibility Duration"] = "显示持续时间"
+L["When icons are disabled, you can still enable invisible icons to line up text."] = "当图标被禁用时, 你仍然可以启用不可见图标来对齐文本."
+L["When moving the Frames"] = "移动框体时"
+L["Which color do you want the merged pet messages to be?"] = "您希望合并的宠物消息使用哪种颜色?"
+L["Whitelist"] = "白名单"
+L["Will also attempt to show the player pet's healing."] = "还将尝试显示玩家宠物的治疗效果."
+L["Will not display any items that are below this quality (does not filter Quest or Crafted items)."] = "将不显示任何低于此品质的物品(不过滤任务或制造物品)."
+L["Write me a PM on Curseforge:"] = "在Curseforge上给我发送私信:"
+L["You can change how |cffFFFF00xCT+|r shows you names in the |cffFFFF00Names|r section of most frames."] = "您可以在大多数框体的|cffFFFF00名字|r部分更改|cffFFFF00xCT+|r显示名字的方式."
+L["Your Dispells / Spell Steals"] = "你的驱散/法术偷取"
+L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"] = "\n\n|cffFF0000需要自滚动战斗文字|r"
+L["ruRU Translators"] = "ruRU 翻譯"
+L["zhCN Translators"] = "简中翻译者"
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = "|CffFF0000需要:|r 修改后|cff00FF33/reload|r"
+L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"] = "|CffFF0000需要:|r 修改后|cff00FF33/reload|r"
+L["|cff798BDDEnvironment|r: Displays 'Environment' as the one who damaged you.\n\n|cff798BDDDamage Types|r: Displays the damage type e.g. "] = "|cff798BDD环境|r: 显示对您造成伤害的来源为'环境'.\n\n|cff798BDD伤害类型|r: 显示伤害类型, 例如"
+L["|cff798BDDMiscellaneous Settings|r:"] = "|cff798BDD其他设置|r:"
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = "|cff798BDD无|r - 禁用\n\n|cff798BDDNPC名字|r - 受事件影响的目标名字. 使用法术合并并命中不同目标时为空.\n\n|cff798BDD法术名字|r - 法术名字"
+L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."] = "|cff798BDD无|r - 禁用\n\n|cff798BDD玩家名字|r - 受事件影响的玩家的名字. 使用法术合并并命中不同目标时为空.\n\n|cff798BDD法术名字|r - 法术名字"
+L["|cff808080Class Combo Points|r"] = "|cff808080职业连击点|r"
+L["|cffFF0000x|rCT|cffFFFF00+|r |cff798BDDConfiguration Tool|r\n"] = "|cffFF0000x|rCT|cffFFFF00+|r|cff798BDD设置工具|r\n"
+L["|cffFFFF00Helpful Tips:|r\n\n"] = "|cffFFFF00实用小贴士:|r\n\n"
+L["|cffFFFF00Thank You GitHub Contributors!|r"] = "|cffFFFF00感谢GitHub贡献者!|r"
+L["|cffFFFF00xCT+|r has several different ways it will merge critical hits. You can check them out in the |cffFFFF00Spam Merger|r section."] = "|cffFFFF00xCT+|r具有多种合并爆击的方式. 您可以在|cffFFFF00垃圾信息合并|r部分查看它们."
+L["|cffFFFFFFClass Power|r"] = "|cffFFFFFF职业资源|r"
+L["|cffFFFFFFFilter:|r |cff798BDDBuffs|r"] = "|cffFFFFFF过滤:|r |cff798BDDBuff|r"
+L["|cffFFFFFFFilter:|r |cff798BDDDebuffs|r"] = "|cffFFFFFF过滤:|r |cff798BDDDebuff|r"
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Damage|r"] = "|cffFFFFFF过滤:|r |cff798BDD受到伤害|r"
+L["|cffFFFFFFFilter:|r |cff798BDDIncoming Healing|r"] = "|cffFFFFFF过滤:|r |cff798BDD受到治疗|r"
+L["|cffFFFFFFFilter:|r |cff798BDDItems|r"] = "|cffFFFFFF过滤:|r |cff798BDD物品|r"
+L["|cffFFFFFFFilter:|r |cff798BDDOutgoing Spells|r"] = "|cffFFFFFF过滤:|r |cff798BDD输出法术|r"
+L["|cffFFFFFFFilter:|r |cff798BDDProcs|r"] = "|cffFFFFFF过滤:|r |cff798BDD触发|r"
+L["|cffFFFFFFGeneral|r"] = "|cffFFFFFF通用|r"
+L["|cffFFFFFFIncoming Damage|r"] = "|cffFFFFFF受到伤害|r"
+L["|cffFFFFFFIncoming Healing|r"] = "|cffFFFFFF受到治疗|r"
+L["|cffFFFFFFLoot, Currency & Money|r"] = "|cffFFFFFF拾取, 货币和金币|r"
+L["|cffFFFFFFOutgoing Damage|r"] = "|cffFFFFFF输出伤害|r"
+L["|cffFFFFFFOutgoing Healing|r"] = "|cffFFFFFF输出治疗|r"
+L["|cffFFFFFFOutgoing|r |cff798BDD(Criticals)|r"] = "|cffFFFFFF数据|r|cff798BDD(爆击)|r"
+L["|cffFFFFFFSpecial Effects|r |cff798BDD(Procs)|r"] = "|cffFFFFFF特效|r|cff798BDD(触发)|r"
diff --git a/xCT+Options/options_table.lua b/xCT+Options/options_table.lua
new file mode 100644
index 00000000..89f157fa
--- /dev/null
+++ b/xCT+Options/options_table.lua
@@ -0,0 +1,7973 @@
+--[[ ____ ______
+ /\ _`\ /\__ _\ __
+ __ _\ \ \/\_\/_/\ \/ /_\ \___
+/\ \/'\\ \ \/_/_ \ \ \/\___ __\
+\/> \ \ \L\ \ \ \ \/__/\_\_/
+ /\_/\_\ \ \____/ \ \_\ \/_/
+ \//\/_/ \/___/ \/_/
+
+ [=====================================]
+ [ Author: Dandraffbal-Stormreaver US ]
+ [ xCT+ Version 4.x.x ]
+ [ ©2010-2025 All Rights Reserved. ]
+ [====================================]]
+
+local AddonName, optionsAddon = ...
+
+-- Short handles to the xCT engine and the xCT options engine
+local x = xCT_Plus.engine
+local xo = optionsAddon.engine
+local L = LibStub("AceLocale-3.0"):GetLocale(AddonName)
+
+if not x then
+ return
+end
+
+local function getColoredClassName(class)
+ return string.format(
+ "|c%s%s|r",
+ RAID_CLASS_COLORS[class].colorStr,
+ LOCALIZED_CLASS_NAMES_MALE[class]
+ )
+end
+
+function x:InitOptionsTable()
+ -- Create the options table for AceConfig
+ optionsAddon.optionsTable = {
+ -- Add a place for the user to grab
+ name = string.format(
+ L[" Version: %s "],
+ C_AddOns.GetAddOnMetadata("xCT+", "Version") or L["Unknown"]
+ ),
+ handler = x,
+ type = "group",
+ args = {
+ xCT_Title = {
+ order = 0,
+ type = "description",
+ fontSize = "large",
+ name = L["|cffFF0000x|rCT|cffFFFF00+|r |cff798BDDConfiguration Tool|r\n"],
+ width = "double",
+ },
+
+ spacer0 = {
+ order = 1,
+ type = "description",
+ name = L["|cffFFFF00Helpful Tips:|r\n\n"],
+ width = "half",
+ },
+
+ helpfulTip = {
+ order = 2,
+ type = "description",
+ fontSize = "medium",
+ name = L["On the left list, under the |cffFFFF00Startup Message|r checkbox, you can click on the |cff798BDD+ Buttons|r (plus) to show more options."],
+ width = "double",
+ },
+
+ space1 = {
+ order = 10,
+ type = "description",
+ name = "\n",
+ width = "full",
+ },
+
+ hideConfig = {
+ order = 12,
+ type = "toggle",
+ name = L["Hide Config in Combat"],
+ desc = L["This option helps prevent UI taints by closing the config when you enter combat.\n\n|cffFF8000Highly Recommended Enabled|r"],
+ get = function()
+ return x.db.profile.hideConfig
+ end,
+ set = function(_, value)
+ x.db.profile.hideConfig = value
+ if not value then
+ StaticPopup_Show("XCT_PLUS_HIDE_IN_COMBAT")
+ end
+ end,
+ },
+ --[==[RestoreDefaults = {
+ order = 3,
+ type = 'execute',
+ name = L["Restore Defaults"],
+ func = x.RestoreAllDefaults,
+ },]==]
+ space2 = {
+ order = 20,
+ type = "description",
+ name = "",
+ width = "half",
+ },
+ space3 = {
+ order = 30,
+ type = "description",
+ name = "",
+ width = "half",
+ },
+ space4 = {
+ order = 30,
+ type = "description",
+ name = "",
+ width = "half",
+ },
+ ToggleTestMode = {
+ order = 31,
+ type = "execute",
+ name = L["Test"],
+ desc = L["Allows you to preview xCT+ in order to tweak settings outside of combat.\n\nYou can also type: '|cffFF0000/xct test|r'"],
+ width = "half",
+ func = x.ToggleTestMode,
+ },
+ ToggleFrames = {
+ order = 32,
+ type = "execute",
+ name = L["Move"],
+ desc = L["Allows you to adjust the position of all the xCT+ frames on your screen.\n\nYou can also type: '|cffFF0000/xct lock|r'"],
+ width = "half",
+ func = x.ToggleConfigMode,
+ },
+
+ hiddenObjectShhhhhh = {
+ order = 9001,
+ type = "description",
+ name = function()
+ x:OnAddonConfigRefreshed()
+ return ""
+ end,
+ },
+ },
+ }
+
+ -- Generic Get/Set methods
+ local function get0(info)
+ return x.db.profile[info[#info - 1]][info[#info]]
+ end
+ local function set0(info, value)
+ x.db.profile[info[#info - 1]][info[#info]] = value
+ x:UpdateCVar()
+ end
+ local function set0_update(info, value)
+ x.db.profile[info[#info - 1]][info[#info]] = value
+ x:UpdateFrames()
+ x:UpdateCVar()
+ end
+ local function get0_1(info)
+ return x.db.profile[info[#info - 2]][info[#info]]
+ end
+ local function set0_1(info, value)
+ x.db.profile[info[#info - 2]][info[#info]] = value
+ x:UpdateCVar()
+ end
+ local function getColor0_1(info)
+ return unpack(x.db.profile[info[#info - 2]][info[#info]] or {})
+ end
+ local function setColor0_1(info, r, g, b)
+ x.db.profile[info[#info - 2]][info[#info]] = { r, g, b }
+ end
+ local function getTextIn0(info)
+ return string.gsub(x.db.profile[info[#info - 1]][info[#info]], "|", "||")
+ end
+ local function setTextIn0(info, value)
+ x.db.profile[info[#info - 1]][info[#info]] = string.gsub(value, "||", "|")
+ x:UpdateCVar()
+ end
+ local function get2(info)
+ return x.db.profile.frames[info[#info - 2]][info[#info]]
+ end
+ local function set2(info, value)
+ local optionKey = info[#info]
+ x.db.profile.frames[info[#info - 2]][optionKey] = value
+ if optionKey == "showProfessionSkillups"
+ or optionKey == "showLowManaHealth"
+ or optionKey == "showCurrency"
+ or optionKey == "showMoney"
+ or optionKey == "showEnergyGains"
+ then
+ x:RegisterCombatEvents()
+ else
+ x:UpdateCVar()
+ end
+ end
+ local function set2_update(info, value)
+ set2(info, value)
+ x:UpdateFrames(info[#info - 2])
+ x:UpdateCVar()
+ end
+ local function getColor2(info)
+ return unpack(x.db.profile.frames[info[#info - 2]][info[#info]] or {})
+ end
+ local function setColor2(info, r, g, b)
+ x.db.profile.frames[info[#info - 2]][info[#info]] = { r, g, b }
+ end
+ local function setColor2_alpha(info, r, g, b, a)
+ x.db.profile.frames[info[#info - 2]][info[#info]] = { r, g, b, a }
+ end
+ local function setTextIn2(info, value)
+ x.db.profile.frames[info[#info - 2]][info[#info]] = string.gsub(value, "||", "|")
+ end
+ local function setNumber2(info, value)
+ if tonumber(value) then
+ x.db.profile[info[#info - 2]][info[#info]] = tonumber(value)
+ end
+ end
+
+ -- Man this is soooo getting out of hand D:
+ local function getNameFormat(info)
+ return x.db.profile.frames[info[#info - 3]].names[info[#info - 1]][info[#info]]
+ end
+ local function setNameFormat(info, value)
+ x.db.profile.frames[info[#info - 3]].names[info[#info - 1]][info[#info]] = value
+ end
+ local function getNameFormatColor(info)
+ return unpack(x.db.profile.frames[info[#info - 3]].names[info[#info - 1]][info[#info]] or {})
+ end
+ local function setNameFormatColor(info, r, g, b)
+ x.db.profile.frames[info[#info - 3]].names[info[#info - 1]][info[#info]] = { r, g, b }
+ end
+ local function getNameFormatText(info)
+ return string.gsub(x.db.profile.frames[info[#info - 2]].names[info[#info]], "|", "||")
+ end
+ local function setNameFormatText(info, value)
+ x.db.profile.frames[info[#info - 2]].names[info[#info]] = string.gsub(value, "||", "|")
+ end
+
+ local function isFrameItemDisabled(info)
+ return not x.db.profile.frames[info[#info - 2]].enabledFrame
+ end
+ local function isFrameNotScrollable(info)
+ return isFrameItemDisabled(info) or not x.db.profile.frames[info[#info - 2]].enableScrollable
+ end
+ local function isFrameUseCustomFade(info)
+ return not x.db.profile.frames[info[#info - 2]].enableCustomFade or isFrameItemDisabled(info)
+ end
+ local function isFrameFadingDisabled(info)
+ return isFrameUseCustomFade(info) or not x.db.profile.frames[info[#info - 2]].enableFade
+ end
+ local function isFrameIconDisabled(info)
+ return isFrameItemDisabled(info) or not x.db.profile.frames[info[#info - 2]].iconsEnabled
+ end
+ local function isFrameIconSpacerDisabled(info)
+ return x.db.profile.frames[info[#info - 2]].iconsEnabled
+ end
+ local function isFrameFontShadowDisabled(info)
+ return isFrameItemDisabled(info) or not x.db.profile.frames[info[#info - 2]].enableFontShadow
+ end
+ local function isFrameCustomColorDisabled(info)
+ return not x.db.profile.frames[info[#info - 2]].customColor
+ end
+ -- This is TEMP
+ local function isFrameItemEnabled(info)
+ return x.db.profile.frames[info[#info - 2]].enabledFrame
+ end
+
+ local function setSpecialCriticalOptions(info)
+ x.db.profile[info[#info - 2]].mergeCriticalsWithOutgoing = false
+ x.db.profile[info[#info - 2]].mergeCriticalsByThemselves = false
+ x.db.profile[info[#info - 2]].mergeDontMergeCriticals = false
+ x.db.profile[info[#info - 2]].mergeHideMergedCriticals = false
+
+ x.db.profile[info[#info - 2]][info[#info]] = true
+ end
+
+ local function setFormatting(info)
+ x.db.profile.spells.formatAbbreviate = false
+ x.db.profile.spells.formatGroups = false
+
+ x.db.profile.spells[info[#info]] = true
+ end
+
+ local function getDBSpells(info)
+ return x.db.profile.spells[info[#info]]
+ end
+
+ local function IsTrackSpellsDisabled()
+ return not x.db.profile.spellFilter.trackSpells
+ end
+
+ local function GetBuffHistory()
+ local result = {}
+
+ for i in pairs(x.spellCache.buffs) do
+ result[i] = i
+ end
+
+ return result
+ end
+
+ local function GetDebuffHistory()
+ local result = {}
+
+ for i in pairs(x.spellCache.debuffs) do
+ result[i] = i
+ end
+
+ return result
+ end
+
+ local function GetSpellHistory()
+ local result = {}
+
+ for id in pairs(x.spellCache.spells) do
+ result[tostring(id)] = string.format(
+ "%s %s |cff798BDD(%d)|r",
+ x:FormatIcon(C_Spell.GetSpellTexture(id) or 0, 16),
+ C_Spell.GetSpellName(id) or UNKNOWN,
+ id
+ )
+ end
+
+ return result
+ end
+
+ local function GetProcHistory()
+ local result = {}
+
+ for i in pairs(x.spellCache.procs) do
+ result[i] = i
+ end
+
+ return result
+ end
+
+ local function GetItemHistory()
+ local result = {}
+
+ for id in pairs(x.spellCache.items) do
+ result[tostring(id)] = string.format(
+ "%s %s |cff798BDD(%d)|r",
+ x:FormatIcon(C_Item.GetItemIconByID(id) or 0, 16),
+ C_Item.GetItemNameByID(id) or UNKNOWN,
+ id
+ )
+ end
+
+ return result
+ end
+
+ local function GetDamageIncomingHistory()
+ local result = {}
+
+ for id in pairs(x.spellCache.damage) do
+ result[tostring(id)] = string.format(
+ "%s %s |cff798BDD(%d)|r",
+ x:FormatIcon(C_Spell.GetSpellTexture(id) or 0, 16),
+ C_Spell.GetSpellName(id) or UNKNOWN,
+ id
+ )
+ end
+
+ return result
+ end
+
+ local function GetHealingIncomingHistory()
+ local result = {}
+
+ for id in pairs(x.spellCache.healing) do
+ result[tostring(id)] = string.format(
+ "%s %s |cff798BDD(%d)|r",
+ x:FormatIcon(C_Spell.GetSpellTexture(id) or 0, 16),
+ C_Spell.GetSpellName(id) or UNKNOWN,
+ id
+ )
+ end
+
+ return result
+ end
+
+ local function getFilteredSpells(info)
+ local category = info[#info - 1]
+ local result = {}
+
+ for id in pairs(x.db.profile.spellFilter[category]) do
+ local spellID = tonumber(id)
+ if spellID then
+ local spellName = C_Spell.GetSpellName(spellID)
+ if spellName then
+ result[id] = spellName .. " (" .. spellID .. ")"
+ end
+ else
+ result[id] = id
+ end
+ end
+
+ return result
+ end
+
+ local function AddFilteredSpell(info, value)
+ local category = info[#info - 1]
+
+ x.db.profile.spellFilter[category][value] = true
+
+ if category == "listBuffs" then
+ xo:UpdateAuraSpellFilter("buffs")
+ elseif category == "listDebuffs" then
+ xo:UpdateAuraSpellFilter("debuffs")
+ elseif category == "listSpells" then
+ xo:UpdateAuraSpellFilter("spells")
+ elseif category == "listProcs" then
+ xo:UpdateAuraSpellFilter("procs")
+ elseif category == "listItems" then
+ xo:UpdateAuraSpellFilter("items")
+ elseif category == "listDamage" then
+ xo:UpdateAuraSpellFilter("damage")
+ elseif category == "listHealing" then
+ xo:UpdateAuraSpellFilter("healing")
+ else
+ xo:Print("|cffFF0000Error:|r Unknown filter type '" .. category .. "'!")
+ end
+ end
+
+ local function removeFilteredSpell(info, value)
+ local category = info[#info - 1]
+
+ x.db.profile.spellFilter[category][value] = nil
+
+ if category == "listBuffs" then
+ xo:UpdateAuraSpellFilter("buffs")
+ elseif category == "listDebuffs" then
+ xo:UpdateAuraSpellFilter("debuffs")
+ elseif category == "listSpells" then
+ xo:UpdateAuraSpellFilter("spells")
+ elseif category == "listProcs" then
+ xo:UpdateAuraSpellFilter("procs")
+ elseif category == "listItems" then
+ xo:UpdateAuraSpellFilter("items")
+ elseif category == "listDamage" then
+ xo:UpdateAuraSpellFilter("damage")
+ elseif category == "listHealing" then
+ xo:UpdateAuraSpellFilter("healing")
+ else
+ x:Print("|cffFF0000Error:|r Unknown filter type '" .. category .. "'!")
+ end
+ end
+
+ -- Apply to All variables
+ local miscFont, miscFontOutline, miscEnableCustomFade
+
+ optionsAddon.optionsTable.args.Frames = {
+ name = L["Frames"],
+ type = "group",
+ order = 0,
+ args = {
+ frameSettings = {
+ order = 1,
+ name = L["Frame Settings"],
+ type = "group",
+ guiInline = true,
+ args = {
+ frameStrata = {
+ order = 1,
+ type = "select",
+ name = L["Frame Strata"],
+ desc = L["The Z-Layer to place the |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames onto. If you find that another addon is in front of |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames, try increasing the Frame Strata."],
+ values = {
+ --["1PARENT"] = L["Parent |cffFF0000(Lowest)|r"],
+ ["2BACKGROUND"] = L["Background |cffFF0000(Lowest)|r"],
+ ["3LOW"] = L["Low"],
+ ["4MEDIUM"] = L["Medium"],
+ ["5HIGH"] = L["High |cffFFFF00(Default)|r"],
+ ["6DIALOG"] = L["Dialog"],
+ ["7FULLSCREEN"] = L["Fullscreen"],
+ ["8FULLSCREEN_DIALOG"] = L["Fullscreen Dialog"],
+ ["9TOOLTIP"] = L["ToolTip |cffAAFF80(Highest)|r"],
+ },
+ get = get0,
+ set = set0_update,
+ },
+ clearLeavingCombat = {
+ order = 2,
+ type = "toggle",
+ name = L["Clear Frames when leaving combat"],
+ desc = L["Enable this option if you have problems with 'floating' icons."],
+ width = "full",
+ get = get0,
+ set = set0,
+ },
+
+ whenMovingFrames = {
+ order = 10,
+ type = "header",
+ name = L["When moving the Frames"],
+ },
+ showGrid = {
+ order = 11,
+ type = "toggle",
+ name = L["Show Align Grid"],
+ desc = L["Shows a grid after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."],
+ get = get0,
+ set = set0,
+ },
+ showPositions = {
+ order = 12,
+ type = "toggle",
+ name = L["Show Positions"],
+ desc = L["Shows the locations and sizes of your frames after you |cffFFFF00Toggle Frames|r to help you align |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames better."],
+ get = get0,
+ set = set0,
+ },
+ },
+ },
+
+ spacer1 = {
+ order = 2,
+ type = "description",
+ name = "\n",
+ },
+
+ megaDamage = {
+ order = 3,
+ name = L["Number Format Settings"],
+ type = "group",
+ guiInline = true,
+ args = {
+ formatAbbreviate = {
+ order = 1,
+ type = "toggle",
+ name = L["Abbreviate Numbers"],
+ set = setFormatting,
+ get = getDBSpells,
+ },
+ formatGroups = {
+ order = 2,
+ type = "toggle",
+ name = L["Decimal Marks"],
+ desc = L["Groups decimals and separates them by commas; this allows for better responsiveness when reading numbers.\n\n|cffFF0000EXAMPLE|r |cff798BDD12,890|r"],
+ set = setFormatting,
+ get = getDBSpells,
+ },
+ decimalPoint = {
+ order = 3,
+ type = "toggle",
+ name = L["Single Decimal Precision"],
+ desc = L["Shows a single digit of precision when abbreviating the value (e.g. will show |cff798BDD5.9K|r instead of |cff798BDD6K|r)."],
+ get = get0,
+ set = set0,
+ },
+ thousandSymbol = {
+ order = 4,
+ type = "input",
+ name = L["Thousand Symbol"],
+ desc = L["Symbol for: |cffFF0000Thousands|r |cff798BDD(10e+3)|r"],
+ get = getTextIn0,
+ set = setTextIn0,
+ },
+ millionSymbol = {
+ order = 5,
+ type = "input",
+ name = L["Million Symbol"],
+ desc = L["Symbol for: |cffFF0000Millions|r |cff798BDD(10e+6)|r"],
+ get = getTextIn0,
+ set = setTextIn0,
+ },
+ billionSymbol = {
+ order = 6,
+ type = "input",
+ name = L["Billion Symbol"],
+ desc = L["Symbol for: |cffFF0000Billions|r |cff798BDD(10e+9)|r"],
+ get = getTextIn0,
+ set = setTextIn0,
+ },
+
+ critPrefix = {
+ order = 11,
+ type = "input",
+ name = L["Critical Prefix"],
+ desc = L["Add these character(s) before the amount of a critical hit."],
+ get = function()
+ return string.gsub(x.db.profile.megaDamage.critPrefix, "|", "||")
+ end,
+ set = setTextIn0,
+ },
+ critSuffix = {
+ order = 12,
+ type = "input",
+ name = L["Critical Suffix"],
+ desc = L["Add these character(s) after the amount of a critical hit."],
+ get = function()
+ return string.gsub(x.db.profile.megaDamage.critSuffix, "|", "||")
+ end,
+ set = setTextIn0,
+ },
+ critPrefixSuffixReset = {
+ order = 13,
+ type = "execute",
+ name = L["Reset"],
+ desc = L["Reset the prefix and the suffix of criticals to their default setting."],
+ func = function()
+ x.db.profile.megaDamage.critPrefix = "|cffFF0000*|r"
+ x.db.profile.megaDamage.critSuffix = "|cffFF0000*|r"
+ end,
+ width = "half",
+ disabled = function()
+ return x:Options_Global_CritPrefix() == "|cffFF0000*|r"
+ and x:Options_Global_CritSuffix() == "|cffFF0000*|r"
+ end,
+ },
+ },
+ },
+
+ spacer2 = {
+ type = "description",
+ name = "\n",
+ order = 4,
+ },
+
+ miscFonts = {
+ order = 5,
+ type = "group",
+ guiInline = true,
+ name = L["Global Frame Settings |cffFFFFFF(Experimental)|r"],
+ args = {
+ miscDesc = {
+ order = 51,
+ type = "description",
+ name = L["The following settings are marked as experimental. They should all work, but they might not be very useful. Expect changes or updates to these in the near future.\n\nClick |cffFFFF00Set All|r to apply setting to all |cffFF0000x|r|cffFFFF00CT|r|cffFF0000+|r frames.\n"],
+ },
+ font = {
+ order = 52,
+ type = "select",
+ dialogControl = "LSM30_Font",
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = function()
+ return miscFont
+ end,
+ set = function(_, value)
+ miscFont = value
+ end,
+ },
+ applyFont = {
+ order = 53,
+ type = "execute",
+ name = L["Set All"],
+ width = "half",
+ func = function()
+ if miscFont then
+ for _, settings in pairs(x.db.profile.frames) do
+ settings.font = miscFont
+ end
+ x:UpdateFrames()
+ end
+ end,
+ },
+
+ spacer1 = {
+ order = 54,
+ type = "description",
+ name = "",
+ },
+
+ fontOutline = {
+ order = 55,
+ type = "select",
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = function()
+ return miscFontOutline
+ end,
+ set = function(_, value)
+ miscFontOutline = value
+ end,
+ },
+
+ applyFontOutline = {
+ order = 56,
+ type = "execute",
+ name = L["Set All"],
+ width = "half",
+ func = function()
+ if miscFontOutline then
+ for _, settings in pairs(x.db.profile.frames) do
+ settings.fontOutline = miscFontOutline
+ end
+ x:UpdateFrames()
+ end
+ end,
+ },
+
+ spacer2 = {
+ order = 57,
+ type = "description",
+ name = "",
+ },
+
+ customFade = {
+ order = 58,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ desc = L["Allows you to customize the fade time of each frame."],
+ get = function()
+ return miscEnableCustomFade
+ end,
+ set = function(_, value)
+ miscEnableCustomFade = value
+ end,
+ },
+
+ applyCustomFade = {
+ order = 59,
+ type = "execute",
+ name = L["Set All"],
+ width = "half",
+ func = function()
+ if miscEnableCustomFade ~= nil then
+ for _, settings in pairs(x.db.profile.frames) do
+ if settings.enableCustomFade ~= nil then
+ settings.enableCustomFade = miscEnableCustomFade
+ end
+ end
+ x:UpdateFrames()
+ end
+ end,
+ },
+ },
+ },
+
+ --[[ XCT+ The Frames: ]]
+ general = {
+ name = L["|cffFFFFFFGeneral|r"],
+ type = "group",
+ order = 11,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ --[1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 20,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 21,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 22,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 23,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 24,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 50,
+ name = L["Misc"],
+ type = "group",
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ showInterrupts = {
+ order = 1,
+ type = "toggle",
+ name = L["Interrupts"],
+ desc = L["Display the spell you successfully interrupted."],
+ get = "Options_General_ShowInterrupts",
+ set = set2,
+ },
+ showDispells = {
+ order = 2,
+ type = "toggle",
+ name = L["Your Dispells / Spell Steals"],
+ desc = L["Show the spell that you dispelled or stole."],
+ get = "Options_General_ShowDispells",
+ set = set2,
+ },
+ showIncomingDispells = {
+ order = 3,
+ type = "toggle",
+ name = L["Incoming Dispells / Spell Steals"],
+ desc = L["Show the spell that somebody else dispelled on you or stole a buff/debuff from you."],
+ get = "Options_General_ShowIncomingDispells",
+ set = set2,
+ },
+ showPartyKills = {
+ order = 4,
+ type = "toggle",
+ name = L["Unit Killed"],
+ desc = L["Display unit that was killed by your final blow."],
+ get = "Options_General_ShowPartyKill",
+ set = set2,
+ },
+ showBuffs = {
+ order = 5,
+ type = "toggle",
+ name = L["Buff Gains/Fades"],
+ desc = L["Display the names of helpful auras |cff00FF00(Buffs)|r that you gain and lose."],
+ get = "Options_General_ShowBuffGainsAndFades",
+ set = set2,
+ },
+ showDebuffs = {
+ order = 6,
+ type = "toggle",
+ name = L["Debuff Gains/Fades"],
+ desc = L["Display the names of harmful auras |cffFF0000(Debuffs)|r that you gain and lose."],
+ get = "Options_General_ShowDebuffGainsAndFades",
+ set = set2,
+ },
+ showLowManaHealth = {
+ order = 7,
+ type = "toggle",
+ name = L["Low Mana/Health"],
+ desc = L["Displays 'Low Health/Mana' when your health/mana reaches the low threshold."],
+ get = "Options_General_ShowLowManaAndHealth",
+ set = set2,
+ },
+ showCombatState = {
+ order = 8,
+ type = "toggle",
+ name = L["Leave/Enter Combat"],
+ desc = L["Displays when the player is leaving or entering combat."],
+ get = "Options_General_ShowCombatState",
+ set = set2,
+ },
+ showRepChanges = {
+ order = 9,
+ type = "toggle",
+ name = L["Reputation Gains/Losses"],
+ desc = L["Displays your player's reputation gains and losses."],
+ get = "Options_General_ShowReputationChanges",
+ set = set2,
+ },
+ showHonorGains = {
+ order = 10,
+ type = "toggle",
+ name = L["Honor Gains"],
+ desc = L["Displays your player's honor gains."],
+ get = "Options_General_ShowHonor",
+ set = set2,
+ },
+ showProfessionSkillups = {
+ order = 11,
+ type = "toggle",
+ name = L["Profession skillup"],
+ desc = L["Displays your skill ups in professions."],
+ get = "Options_General_ShowProfessionSkillups",
+ set = set2,
+ },
+ },
+ },
+ },
+ },
+
+ outgoing = {
+ name = L["|cffFFFFFFOutgoing Damage|r"],
+ type = "group",
+ order = 12,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ --[2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 30,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 31,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 32,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 33,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 34,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+
+ headerAdditionalSettings = {
+ type = "header",
+ order = 10,
+ name = L["Additional Settings"],
+ },
+ iconsEnabledAutoAttack = {
+ order = 11,
+ type = "toggle",
+ name = L["Show Auto Attack Icon"],
+ desc = L["Show icons from Auto Attacks."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ order = 0,
+ type = "header",
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ names = {
+ order = 50,
+ type = "group",
+ name = L["Names"],
+ childGroups = "select",
+ get = getNameFormat,
+ set = setNameFormat,
+ args = {
+ namesDescription = {
+ type = "description",
+ order = 1,
+ name = L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."],
+ fontSize = "small",
+ },
+
+ namePrefix = {
+ order = 2,
+ type = "input",
+ name = L["Name Prefix"],
+ desc = L["Add these character(s) to the beginning of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ namePostfix = {
+ order = 4,
+ type = "input",
+ name = L["Name Suffix"],
+ desc = L["Add these character(s) to the end of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ PLAYER = {
+ order = 10,
+ type = "group",
+ name = L["Events to a Player"],
+ args = {
+ playerNames = {
+ order = 1,
+ type = "header",
+ name = L["Player Name Format"],
+ },
+
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Player Name"],
+ desc = L["If the player's class is known (e.g. is a raid member), it will be colored."],
+ },
+
+ removeRealmName = {
+ order = 3,
+ type = "toggle",
+ name = L["Remove Realm Name"],
+ desc = L["If the player has a realm name attached to her name, it will be removed."],
+ },
+
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ playerSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+
+ playerNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Player Name"],
+ [2] = L["Spell Name"],
+ [3] = L["Player Name"] .. " - " .. L["Spell Name"],
+ [4] = L["Spell Name"] .. " - " .. L["Player Name"],
+ },
+ },
+ },
+ },
+
+ NPC = {
+ order = 20,
+ type = "group",
+ name = L["Events to a NPC"],
+ args = {
+ npcNames = {
+ order = 1,
+ type = "header",
+ name = L["NPC Name Format"],
+ },
+ customNameColor = {
+ order = 2,
+ type = "color",
+ name = L["NPC Name Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ npcSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+ npcNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["NPC Name"],
+ [2] = L["Spell Name"],
+ [3] = L["NPC Name"] .. ' - ' .. L["Spell Name"],
+ [4] = L["Spell Name"] .. ' - ' .. L["NPC Name"],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 60,
+ type = "group",
+ name = L["Misc"],
+ args = {
+ specialTweaksPlayer = {
+ type = "header",
+ order = 0,
+ name = L["Player Damage Settings"],
+ },
+ enableOutDmg = {
+ order = 10,
+ type = "toggle",
+ name = L["Show Outgoing Damage"],
+ desc = L["Show damage you do."],
+ get = "Options_Outgoing_ShowDamage",
+ set = set2,
+ },
+ enableDotDmg = {
+ order = 11,
+ type = "toggle",
+ name = L["Show DoTs"],
+ desc = L["Show your Damage-Over-Time (DOT) damage. (|cffFF0000Requires:|r Outgoing Damage)"],
+ get = "Options_Outgoing_ShowDots",
+ set = set2,
+ },
+ enableAutoAttack_Outgoing = {
+ order = 12,
+ type = "toggle",
+ name = L["Show Auto Attack"],
+ desc = L["Show your non-critical, auto attack damage."],
+ get = "Options_Outgoing_ShowAutoAttack",
+ set = set2,
+ },
+ enableAbsorbs = {
+ order = 13,
+ type = "toggle",
+ name = L["Show Absorbs as damage"],
+ desc = L["Display partially or fully absorbed damage as regular damage."],
+ get = "Options_Outgoing_ShowAbsorbedDamageAsNormalDamage",
+ set = set2,
+ },
+
+ specialTweaksPet = {
+ type = "header",
+ order = 20,
+ name = L["Pet and Vehicle Damage Settings"],
+ },
+
+ enablePetDmg = {
+ order = 21,
+ type = "toggle",
+ name = L["Show Pet Damage"],
+ desc = L["Show your pet's damage. Beast Mastery hunters should also look at vehicle damage."],
+ get = "Options_Outgoing_ShowPetDamage",
+ set = set2,
+ },
+ enablePetAutoAttack_Outgoing = {
+ order = 22,
+ type = "toggle",
+ name = L["Pet Auto Attacks"],
+ desc = L["Show your pet's non-critical, auto attacks."],
+ get = "Options_Outgoing_ShowPetAutoAttack",
+ set = set2,
+ },
+ enableKillCommand = {
+ order = 23,
+ type = "toggle",
+ name = L["Show Kill Command"],
+ desc = L["Change the source of |cff798BDDKill Command|r to be the |cffFF8000Player|r. This is helpful when you to turn off |cffFF8000Pet|r damage."],
+ get = "Options_Outgoing_ShowKillCommandAsPlayerDamage",
+ set = set2,
+ hidden = function()
+ return x.player.class ~= "HUNTER"
+ end,
+ },
+ enableVehicleDmg = {
+ order = 24,
+ type = "toggle",
+ name = L["Show Vehicle Damage"],
+ desc = L["Show damage that your vehicle does. This can be anything from a vehicle you are controlling to Hati, the beast mastery pet."],
+ get = "Options_Outgoing_ShowVehicleDamage",
+ set = set2,
+ },
+
+ missTypeSettings = {
+ type = "header",
+ order = 50,
+ name = L["Miss Type Settings"],
+ },
+ enableImmunes = {
+ order = 51,
+ type = "toggle",
+ name = L["Show Immunes"],
+ desc = L["Display 'Immune' when your target cannot take damage."],
+ get = "Options_Outgoing_ShowImmunes",
+ set = set2,
+ },
+ enableMisses = {
+ order = 52,
+ type = "toggle",
+ name = L["Show Misses, Dodges, Parries"],
+ desc = L["Display 'Miss', 'Dodge', 'Parry' when you miss your target."],
+ get = "Options_Outgoing_ShowMisses",
+ set = set2,
+ },
+ enablePartialMisses = {
+ order = 54,
+ type = "toggle",
+ name = L["Show partial Misses, Dodges, Parries"],
+ desc = L["Show when your target takes only a percentage of your damage because it was partially absorbed, resisted, or blocked.\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."],
+ get = "Options_Outgoing_ShowPartialMisses",
+ set = set2,
+ },
+ showHighestPartialMiss = {
+ order = 55,
+ type = "toggle",
+ name = L["Show the Highest Partial Miss"],
+ desc = L["Only show the highest partial miss, instead of all the misses. (Rare, but less spammy)\n\n|cffFF0000PLEASE NOTE:|r Only works if the spell is not merged. Turn off the Spell Merger to see all spells."],
+ get = get2,
+ set = set2,
+ },
+ },
+ },
+ },
+ },
+
+ outgoing_healing = {
+ name = L["|cffFFFFFFOutgoing Healing|r"],
+ type = "group",
+ order = 13,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ --[10] = L["Outgoing Healing"]
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 30,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 31,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 32,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 33,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 34,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ names = {
+ order = 50,
+ type = "group",
+ name = L["Names"],
+ childGroups = "select",
+ get = getNameFormat,
+ set = setNameFormat,
+ args = {
+ namesDescription = {
+ type = "description",
+ order = 1,
+ name = L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."],
+ fontSize = "small",
+ },
+
+ namePrefix = {
+ order = 2,
+ type = "input",
+ name = L["Name Prefix"],
+ desc = L["Add these character(s) to the beginning of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ namePostfix = {
+ order = 4,
+ type = "input",
+ name = L["Name Suffix"],
+ desc = L["Add these character(s) to the end of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ PLAYER = {
+ order = 10,
+ type = "group",
+ name = L["Events to a Player"],
+ args = {
+ playerNames = {
+ order = 1,
+ type = "header",
+ name = L["Player Name Format"],
+ },
+
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Player Name"],
+ desc = L["If the player's class is known (e.g. is a raid member), it will be colored."],
+ },
+
+ removeRealmName = {
+ order = 3,
+ type = "toggle",
+ name = L["Remove Realm Name"],
+ desc = L["If the player has a realm name attached to her name, it will be removed."],
+ },
+
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ playerSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+
+ playerNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Player Name"],
+ [2] = L["Spell Name"],
+ [3] = L["Player Name"] .. " - " .. L["Spell Name"],
+ [4] = L["Spell Name"] .. " - " .. L["Player Name"],
+ },
+ },
+ },
+ },
+
+ NPC = {
+ order = 20,
+ type = "group",
+ name = L["Events to a NPC"],
+ args = {
+ npcNames = {
+ order = 1,
+ type = "header",
+ name = L["NPC Name Format"],
+ },
+ customNameColor = {
+ order = 2,
+ type = "color",
+ name = L["NPC Name Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ npcSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+ npcNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["NPC Name"],
+ [2] = L["Spell Name"],
+ [3] = L["NPC Name"] .. ' - ' .. L["Spell Name"],
+ [4] = L["Spell Name"] .. ' - ' .. L["NPC Name"],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 60,
+ type = "group",
+ name = L["Misc"],
+ args = {
+ headerHots = {
+ order = 1,
+ type = "header",
+ name = L["Miscellaneous Settings"],
+ },
+ enableHots = {
+ order = 2,
+ type = "toggle",
+ name = L["Show HoTs"],
+ desc = L["Show your Heal-Over-Time (HOT) healing."],
+ get = "Options_OutgoingHealing_ShowHots",
+ set = set2,
+ },
+ hideAbsorbedOutgoingHeals = {
+ order = 3,
+ type = "toggle",
+ name = L["Hide Absorbed Heals"],
+ desc = L["If enabled, subtract any healing that was absorbed by a |cffFF0000debuff|r from the total."],
+ get = "Options_OutgoingHealing_HideAbsorbedHealing",
+ set = set2,
+ },
+
+ headerOverhealing = {
+ order = 10,
+ type = "header",
+ name = L["Overhealing"],
+ },
+ enableOverhealing = {
+ order = 11,
+ type = "toggle",
+ name = L["Show Overhealing"],
+ desc = L["Displays overhealing."],
+ get = "Options_OutgoingHealing_ShowOverhealing",
+ set = set2,
+ },
+ enableOverhealingSubtraction = {
+ order = 12,
+ type = "toggle",
+ name = L["Subtract Overhealing"],
+ desc = L["Subtract the overhealed amount from the Total Amount"],
+ get = "Options_OutgoingHealing_SubtractOverhealing",
+ set = set2,
+ disabled = function()
+ return not x.db.profile.frames.outgoing_healing.enableOverhealing
+ end,
+ },
+ enableOverhealingFormat = {
+ order = 13,
+ type = "toggle",
+ name = L["Format Overhealing"],
+ desc = L["Splits overhealing into its own section. Example: +43,000 (O: 12,000)"],
+ get = "Options_OutgoingHealing_FormatOverhealing",
+ set = set2,
+ disabled = function()
+ return not x.db.profile.frames.outgoing_healing.enableOverhealing
+ end,
+ },
+ overhealingPrefix = {
+ order = 14,
+ type = "input",
+ name = L["Overhealing Prefix"],
+ desc = L["Prefix this value to the beginning when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"],
+ get = "Options_OutgoingHealing_OverhealingPrefix",
+ set = setTextIn2,
+ disabled = function()
+ return not x.db.profile.frames.outgoing_healing.enableOverhealing
+ or not x.db.profile.frames.outgoing_healing.enableOverhealingFormat
+ end,
+ },
+ overhealingPostfix = {
+ order = 15,
+ type = "input",
+ name = L["Overhealing Postfix"],
+ desc = L["Prefix this value to the ending when displaying an overheal amount.\n\n|cffFF0000Requires:|r |cff798BDDFormat Overhealing|r"],
+ get = "Options_OutgoingHealing_OverhealingPostfix",
+ set = setTextIn2,
+ disabled = function()
+ return not x.db.profile.frames.outgoing_healing.enableOverhealing
+ or not x.db.profile.frames.outgoing_healing.enableOverhealingFormat
+ end,
+ },
+ },
+ },
+ },
+ },
+
+ critical = {
+ name = L["|cffFFFFFFOutgoing|r |cff798BDD(Criticals)|r"],
+ type = "group",
+ order = 14,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ --[3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 30,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 31,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 32,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 33,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 34,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ names = {
+ order = 50,
+ type = "group",
+ name = L["Names"],
+ childGroups = "select",
+ get = getNameFormat,
+ set = setNameFormat,
+ args = {
+ namesDescription = {
+ type = "description",
+ order = 1,
+ name = L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."],
+ fontSize = "small",
+ },
+
+ namePrefix = {
+ order = 2,
+ type = "input",
+ name = L["Name Prefix"],
+ desc = L["Add these character(s) to the beginning of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ namePostfix = {
+ order = 4,
+ type = "input",
+ name = L["Name Suffix"],
+ desc = L["Add these character(s) to the end of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ PLAYER = {
+ order = 10,
+ type = "group",
+ name = L["Events to a Player"],
+ args = {
+ playerNames = {
+ order = 1,
+ type = "header",
+ name = L["Player Name Format"],
+ },
+
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Player Name"],
+ desc = L["If the player's class is known (e.g. is a raid member), it will be colored."],
+ },
+
+ removeRealmName = {
+ order = 3,
+ type = "toggle",
+ name = L["Remove Realm Name"],
+ desc = L["If the player has a realm name attached to her name, it will be removed."],
+ },
+
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ playerSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+
+ playerNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Player Name"],
+ [2] = L["Spell Name"],
+ [3] = L["Player Name"] .. " - " .. L["Spell Name"],
+ [4] = L["Spell Name"] .. " - " .. L["Player Name"],
+ },
+ },
+ },
+ },
+
+ NPC = {
+ order = 20,
+ type = "group",
+ name = L["Events to a NPC"],
+ args = {
+ npcNames = {
+ order = 1,
+ type = "header",
+ name = L["NPC Name Format"],
+ },
+ customNameColor = {
+ order = 2,
+ type = "color",
+ name = L["NPC Name Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ npcSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+ npcNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["NPC Name"],
+ [2] = L["Spell Name"],
+ [3] = L["NPC Name"] .. ' - ' .. L["Spell Name"],
+ [4] = L["Spell Name"] .. ' - ' .. L["NPC Name"],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 60,
+ type = "group",
+ name = L["Misc"],
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ enableAutoAttack_Critical = {
+ order = 1,
+ type = "toggle",
+ name = L["Show Auto Attacks"],
+ desc = L["Show criticals from Auto Attack and Swings. If disabled, they will be displayed as non-critical auto attacks. They will be merged into the Outgoing frame."],
+ get = "Options_Critical_ShowAutoAttack",
+ set = set2,
+ },
+ prefixAutoAttack_Critical = {
+ order = 2,
+ type = "toggle",
+ name = L["Show Auto Attacks (Pre)Postfix"],
+ desc = L["Make Auto Attack and Swing criticals more visible by adding the prefix and postfix."],
+ get = "Options_Critical_PrefixAutoAttack",
+ set = set2,
+ },
+ petCrits = {
+ order = 3,
+ type = "toggle",
+ name = L["Allow Pet Crits"],
+ desc = L["Enable this to see when your pet's abilities critical strike, otherwise disable for less combat text spam."],
+ get = "Options_Critical_ShowPetCrits",
+ set = set2,
+ },
+ },
+ },
+ },
+ },
+
+ damage = {
+ name = L["|cffFFFFFFIncoming Damage|r"],
+ type = "group",
+ order = 15,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ --[4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 20,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 21,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 22,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 23,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 24,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+
+ headerAdditionalSettings = {
+ type = "header",
+ order = 10,
+ name = L["Additional Settings"],
+ },
+ iconsEnabledAutoAttack = {
+ order = 11,
+ type = "toggle",
+ name = L["Show Auto Attack Icon"],
+ desc = L["Show icons from Auto Attacks."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ names = {
+ order = 50,
+ type = "group",
+ name = L["Names"],
+ childGroups = "select",
+ get = getNameFormat,
+ set = setNameFormat,
+ args = {
+ namesDescription = {
+ type = "description",
+ order = 1,
+ name = L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."],
+ fontSize = "small",
+ },
+
+ namePrefix = {
+ order = 2,
+ type = "input",
+ name = L["Name Prefix"],
+ desc = L["Add these character(s) to the beginning of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ namePostfix = {
+ order = 4,
+ type = "input",
+ name = L["Name Suffix"],
+ desc = L["Add these character(s) to the end of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ PLAYER = {
+ order = 10,
+ type = "group",
+ name = L["Events from a Player"],
+ args = {
+ playerNames = {
+ order = 1,
+ type = "header",
+ name = L["Player Name Format"],
+ },
+
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Player Name"],
+ desc = L["If the player's class is known (e.g. is a raid member), it will be colored."],
+ },
+
+ removeRealmName = {
+ order = 3,
+ type = "toggle",
+ name = L["Remove Realm Name"],
+ desc = L["If the player has a realm name attached to her name, it will be removed."],
+ },
+
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ playerSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+
+ playerNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Player Name"],
+ [2] = L["Spell Name"],
+ [3] = L["Player Name"] .. " - " .. L["Spell Name"],
+ [4] = L["Spell Name"] .. " - " .. L["Player Name"],
+ },
+ },
+ },
+ },
+
+ NPC = {
+ order = 20,
+ type = "group",
+ name = L["Events from a NPC"],
+ args = {
+ npcNames = {
+ order = 1,
+ type = "header",
+ name = L["NPC Name Format"],
+ },
+ customNameColor = {
+ order = 2,
+ type = "color",
+ name = L["NPC Name Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ npcSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+ npcNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["NPC Name"],
+ [2] = L["Spell Name"],
+ [3] = L["NPC Name"] .. ' - ' .. L["Spell Name"],
+ [4] = L["Spell Name"] .. ' - ' .. L["NPC Name"],
+ },
+ },
+ },
+ },
+
+ ENVIRONMENT = {
+ order = 30,
+ type = "group",
+ name = L["Events from the Environment"],
+ args = {
+ environmentNames = {
+ order = 1,
+ type = "header",
+ name = L["Environment Format"],
+ },
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Environment"],
+ desc = L["The name will be colored according to it's environmental type."],
+ },
+ environmentNames_Spacer1 = {
+ type = "description",
+ order = 3,
+ name = "",
+ width = "normal",
+ },
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ width = "half",
+ },
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ environmentSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Type"],
+ desc = L["The type will be colored according to it's environmental type."],
+ },
+ environmentNames_Spacer2 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDEnvironment|r: Displays 'Environment' as the one who damaged you.\n\n|cff798BDDDamage Types|r: Displays the damage type e.g. "]
+ .. " |cffFFFF00"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_DROWNING
+ .. "|r, |cffFFFF00"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_FALLING
+ .. "|r, |cffFFFF00"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_FATIGUE
+ .. "|r, |cffFF8000"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_FIRE
+ .. "|r, |cffFF8000"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_LAVA
+ .. "|r, |cff4DFF4D"
+ .. ACTION_ENVIRONMENTAL_DAMAGE_SLIME
+ .. "|r.",
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Environment"],
+ [2] = L["Damage Type"],
+ [3] = L["Environment"] .. " - " .. L["Damage Type"],
+ [4] = L["Damage Type"] .. " - " .. L["Environment"],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 60,
+ name = L["Misc"],
+ type = "group",
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ showDodgeParryMiss = {
+ order = 1,
+ type = "toggle",
+ name = L["Show Misses, Dodges, Parries"],
+ desc = L["Displays Dodge, Parry, or Miss when you miss incoming damage."],
+ get = "Options_IncomingDamage_ShowMissTypes",
+ set = set2,
+ },
+ showDamageReduction = {
+ order = 2,
+ type = "toggle",
+ name = L["Show Reductions"],
+ desc = L["Formats incoming damage to show how much was absorbed. The spam merger hides these reduction and effectively disables this option though."],
+ get = "Options_IncomingDamage_ShowReductions",
+ set = set2,
+ },
+ },
+ },
+ },
+ },
+
+ healing = {
+ name = L["|cffFFFFFFIncoming Healing|r"],
+ type = "group",
+ order = 16,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ --[5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 20,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 21,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 22,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 23,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 24,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ names = {
+ order = 50,
+ type = "group",
+ name = L["Names"],
+ childGroups = "select",
+ get = getNameFormat,
+ set = setNameFormat,
+ args = {
+ namesDescription = {
+ type = "description",
+ order = 1,
+ name = L["The |cffFFFF00Names Settings|r allows you to add the player / npc / spell name to each message. The spam merger will hide player / npc names if different players / npcs were hit."],
+ fontSize = "small",
+ },
+
+ namePrefix = {
+ order = 2,
+ type = "input",
+ name = L["Name Prefix"],
+ desc = L["Add these character(s) to the beginning of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ namePostfix = {
+ order = 4,
+ type = "input",
+ name = L["Name Suffix"],
+ desc = L["Add these character(s) to the end of the message."],
+ get = getNameFormatText,
+ set = setNameFormatText,
+ },
+
+ PLAYER = {
+ order = 10,
+ type = "group",
+ name = L["Events from a Player"],
+ args = {
+ playerNames = {
+ order = 1,
+ type = "header",
+ name = L["Player Name Format"],
+ },
+
+ enableNameColor = {
+ order = 2,
+ type = "toggle",
+ name = L["Color Player Name"],
+ desc = L["If the player's class is known (e.g. is a raid member), it will be colored."],
+ },
+
+ removeRealmName = {
+ order = 3,
+ type = "toggle",
+ name = L["Remove Realm Name"],
+ desc = L["If the player has a realm name attached to her name, it will be removed."],
+ },
+
+ enableCustomNameColor = {
+ order = 4,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customNameColor = {
+ order = 5,
+ type = "color",
+ name = L["Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ playerSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+
+ playerNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ width = "half",
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDPlayer Name|r - The name of the player that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["Player Name"],
+ [2] = L["Spell Name"],
+ [3] = L["Player Name"] .. " - " .. L["Spell Name"],
+ [4] = L["Spell Name"] .. " - " .. L["Player Name"],
+ },
+ },
+ },
+ },
+
+ NPC = {
+ order = 20,
+ type = "group",
+ name = L["Events from a NPC"],
+ args = {
+ npcNames = {
+ order = 1,
+ type = "header",
+ name = L["NPC Name Format"],
+ },
+ customNameColor = {
+ order = 2,
+ type = "color",
+ name = L["NPC Name Color"],
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ npcSpellNames = {
+ order = 10,
+ type = "header",
+ name = L["Spell Name Format"],
+ },
+ enableSpellColor = {
+ order = 11,
+ type = "toggle",
+ name = L["Color Spell Name"],
+ desc = L["The spell name will be colored according to it's spell school."],
+ },
+ npcNames_Spacer1 = {
+ type = "description",
+ order = 12,
+ name = "",
+ width = "normal",
+ },
+ enableCustomSpellColor = {
+ order = 13,
+ type = "toggle",
+ name = L["Custom"],
+ desc = L["Preempt an automatic color with a custom one."],
+ width = "half",
+ },
+ customSpellColor = {
+ order = 14,
+ type = "color",
+ name = L["Color"],
+ width = "half",
+ get = getNameFormatColor,
+ set = setNameFormatColor,
+ },
+
+ nameTypeHeader = {
+ order = 20,
+ type = "header",
+ name = L["Names to display"],
+ },
+ nameType = {
+ type = "select",
+ order = 21,
+ name = L["Names to display"],
+ desc = L["|cff798BDDNone|r - Disabled\n\n|cff798BDDNPC Name|r - The name of the NPC that is affected by the event. Empty when using the spell merger and hitting different targets.\n\n|cff798BDDSpell Name|r - The name of the spell."],
+ width = "double",
+ style = "radio",
+ values = {
+ [0] = L["None"],
+ [1] = L["NPC Name"],
+ [2] = L["Spell Name"],
+ [3] = L["NPC Name"] .. ' - ' .. L["Spell Name"],
+ [4] = L["Spell Name"] .. ' - ' .. L["NPC Name"],
+ },
+ },
+ },
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 60,
+ name = L["Misc"],
+ type = "group",
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ enableOverHeal = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Overheals"],
+ desc = L["Show the overhealing you receive from other players."],
+ get = "Options_IncomingHealing_ShowOverHealing",
+ set = set2,
+ },
+ hideAbsorbedHeals = {
+ order = 5,
+ type = "toggle",
+ name = L["Hide Absorbed Heals"],
+ desc = L["If enabled, subtract any healing that was absorbed by a |cffFF0000debuff|r from the total."],
+ get = "Options_IncomingHealing_HideAbsorbedHealing",
+ set = set2,
+ },
+ enableSelfAbsorbs = {
+ order = 6,
+ type = "toggle",
+ name = L["Show Absorbs"],
+ desc = L["Shows absorbs you gain from other players."],
+ get = get2,
+ set = set2,
+ },
+ showOnlyMyHeals = {
+ order = 7,
+ type = "toggle",
+ name = L["Show My Heals Only"],
+ desc = L["Shows only the player's healing done to himself or herself."],
+ get = "Options_IncomingHealing_ShowOnlyMyHeals",
+ set = set2,
+ },
+ showOnlyPetHeals = {
+ order = 7,
+ type = "toggle",
+ name = L["Show Pet Heals Too"],
+ desc = L["Will also attempt to show the player pet's healing."],
+ get = "Options_IncomingHealing_ShowOnlyMyPetsHeals",
+ set = set2,
+ disabled = function()
+ return not x.db.profile.frames.healing.showOnlyMyHeals
+ end,
+ },
+ },
+ },
+ },
+ },
+
+ power = {
+ name = L["|cffFFFFFFClass Power|r"],
+ type = "group",
+ order = 18,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ --[6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ megaDamage = {
+ order = 5,
+ type = "toggle",
+ name = L["Number Formatting"],
+ desc = L["Enables number formatting. This option can be customized in the main |cff00FF00Frames|r options page to be either |cff798BDDAbbreviation|r or |cff798BDDDecimal Marks|r. "],
+ get = get2,
+ set = set2,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 20,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 21,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 22,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 23,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 24,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 50,
+ name = L["Misc"],
+ type = "group",
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ showEnergyGains = {
+ order = 1,
+ type = "toggle",
+ name = L["Show Gains"],
+ desc = L["Show instant gains of class resources (e. g. energy, runic power, ...)."],
+ get = "Options_Power_ShowGains",
+ set = set2,
+ },
+ showEnergyType = {
+ order = 3,
+ type = "toggle",
+ name = L["Show Energy Type"],
+ desc = L["Show the type of energy that you are gaining."],
+ get = "Options_Power_ShowEnergyTypes",
+ set = set2,
+ },
+
+ title1 = {
+ type = "header",
+ order = 10,
+ name = L["Filter Resources"],
+ },
+ title2 = {
+ type = "description",
+ order = 11,
+ name = L["Check the resources that you do not wish to be displayed for your character:"],
+ fontSize = "small",
+ },
+
+ -- Disable Powers
+ disableResource_MANA = {
+ order = 100,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. MANA .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_RAGE = {
+ order = 101,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. RAGE .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_FOCUS = {
+ order = 102,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. FOCUS .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_ENERGY = {
+ order = 103,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. ENERGY .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+
+ disableResource_RUNES = {
+ order = 104,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. RUNES .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_RUNIC_POWER = {
+ order = 105,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. RUNIC_POWER .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_SOUL_SHARDS = {
+ order = 106,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. SOUL_SHARDS .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_LUNAR_POWER = {
+ order = 107,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. LUNAR_POWER .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+
+ disableResource_CHI = {
+ order = 108,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. CHI .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_HOLY_POWER = {
+ order = 109,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. HOLY_POWER .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_INSANITY_POWER = {
+ order = 110,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. INSANITY .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_MAELSTROM_POWER = {
+ order = 111,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. MAELSTROM_POWER .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+
+ disableResource_ARCANE_CHARGES = {
+ order = 112,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. ARCANE_CHARGES .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_FURY = {
+ order = 113,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. FURY .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ disableResource_PAIN = {
+ order = 114,
+ type = "toggle",
+ name = L["Disable"] .. " |cff798BDD" .. PAIN .. "|r",
+ get = get2,
+ set = set2,
+ width = "normal",
+ },
+ },
+ },
+ },
+ },
+
+ procs = {
+ name = L["|cffFFFFFFSpecial Effects|r |cff798BDD(Procs)|r"],
+ type = "group",
+ order = 19,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = "Options_Procs_ShowProcs",
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ --[7] = L["Special Effects (Procs)"],
+ [8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 20,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 21,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 22,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 23,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 24,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons next to your damage."],
+ get = get2,
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = get2,
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = get2,
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+ },
+ },
+
+ loot = {
+ name = L["|cffFFFFFFLoot, Currency & Money|r"],
+ type = "group",
+ order = 20,
+ childGroups = "tab",
+ args = {
+
+ frameSettings = {
+ order = 10,
+ type = "group",
+ name = L["Frame"],
+ args = {
+ headerFrameSettings = {
+ type = "header",
+ order = 0,
+ name = L["Frame Settings"],
+ },
+ enabledFrame = {
+ order = 1,
+ type = "toggle",
+ name = L["Enable"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ },
+ secondaryFrame = {
+ type = "select",
+ order = 2,
+ name = L["Secondary Frame"],
+ desc = L["A frame to forward messages to when this frame is disabled."],
+ values = {
+ [0] = L["None"],
+ [1] = L["General"],
+ [2] = L["Outgoing Damage"],
+ [3] = L["Outgoing Damage (Criticals)"],
+ [4] = L["Incoming Damage"],
+ [5] = L["Incoming Healing"],
+ [6] = L["Class Power"],
+ [7] = L["Special Effects (Procs)"],
+ --[8] = L["Loot, Currency & Money"],
+ [10] = L["Outgoing Healing"],
+ },
+ get = get2,
+ set = set2,
+ disabled = isFrameItemEnabled,
+ },
+ insertText = {
+ type = "select",
+ order = 3,
+ name = L["Text Direction"],
+ desc = L["Changes the direction that the text travels in the frame."],
+ values = {
+ ["top"] = L["Down"],
+ ["bottom"] = L["Up"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ alpha = {
+ order = 4,
+ name = L["Frame Alpha"],
+ desc = L["Sets the alpha of the frame."],
+ type = "range",
+ min = 0,
+ max = 100,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameScrolling = {
+ type = "header",
+ order = 10,
+ name = L["Scrollable Frame Settings"],
+ },
+ enableScrollable = {
+ order = 11,
+ type = "toggle",
+ name = L["Enabled"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ scrollableLines = {
+ order = 12,
+ name = L["Number of Lines"],
+ type = "range",
+ min = 10,
+ max = 60,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameNotScrollable,
+ },
+ scrollableInCombat = {
+ order = 13,
+ type = "toggle",
+ name = L["Disable in Combat"],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ frameFading = {
+ type = "header",
+ order = 30,
+ name = L["Fading Text Settings"],
+ },
+ enableCustomFade = {
+ order = 31,
+ type = "toggle",
+ name = L["Use Custom Fade"],
+ width = "full",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ enableFade = {
+ order = 32,
+ type = "toggle",
+ name = L["Enable"],
+ desc = L["Turn off to disable fading all together.\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ width = "half",
+ get = get2,
+ set = set2_update,
+ disabled = isFrameUseCustomFade,
+ },
+ fadeTime = {
+ order = 33,
+ name = L["Fade Out Duration"],
+ desc = L["The duration of the fade out animation. |cffFFFF00(Default: |cff798BDD0.3|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 0,
+ max = 2,
+ step = 0.1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ visibilityTime = {
+ order = 34,
+ name = L["Visibility Duration"],
+ desc = L["The duration that the text is shown in the frame. |cffFFFF00(Default: |cff798BDD5|r)|r\n\n|cffFF0000Requires:|r |cffFFFF00Use Custom Fade|r"],
+ type = "range",
+ min = 2,
+ max = 15,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFadingDisabled,
+ },
+ },
+ },
+
+ fonts = {
+ order = 20,
+ type = "group",
+ name = L["Font"],
+ args = {
+ fontSettings = {
+ type = "header",
+ order = 0,
+ name = L["Font Settings"],
+ },
+ font = {
+ type = "select",
+ dialogControl = "LSM30_Font",
+ order = 1,
+ name = L["Font"],
+ desc = L["Set the font of the frame."],
+ values = AceGUIWidgetLSMlists.font,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontSize = {
+ order = 2,
+ name = L["Font Size"],
+ desc = L["Set the font size of the frame."],
+ type = "range",
+ min = 6,
+ max = 64,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontOutline = {
+ type = "select",
+ order = 3,
+ name = L["Font Outline"],
+ desc = L["Set the font outline."],
+ values = {
+ ["1NONE"] = L["None"],
+ ["2OUTLINE"] = L["Outline"],
+ -- BUG: Setting font to monochrome AND above size 16 will crash WoW
+ -- http://us.battle.net/wow/en/forum/topic/6470967362
+ ["3MONOCHROME"] = L["Monochrome"],
+ ["4MONOCHROMEOUTLINE"] = L["Monochrome Outline"],
+ ["5THICKOUTLINE"] = L["Thick Outline"],
+ },
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+ fontJustify = {
+ type = "select",
+ order = 4,
+ name = L["Justification"],
+ desc = L["Justifies the output to a side."],
+ values = {
+ ["RIGHT"] = L["Right"],
+ ["LEFT"] = L["Left"],
+ ["CENTER"] = L["Center"],
+ },
+ get = get2,
+ set = set2_update,
+ },
+
+ fontShadowSettings = {
+ type = "header",
+ order = 10,
+ name = L["Font Shadow Settings"],
+ },
+
+ enableFontShadow = {
+ order = 11,
+ type = "toggle",
+ name = L["Enable Font Shadow"],
+ desc = L["Shows a shadow behind the combat text fonts."],
+ get = get2,
+ set = set2_update,
+ disabled = isFrameItemDisabled,
+ },
+
+ fontShadowColor = {
+ order = 12,
+ type = "color",
+ hasAlpha = true,
+ name = L["Font Shadow Color"],
+ get = getColor2,
+ set = setColor2_alpha,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetX = {
+ order = 13,
+ name = L["Horizontal Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+
+ fontShadowOffsetY = {
+ order = 14,
+ name = L["Vertical Offset"],
+ type = "range",
+ min = -10,
+ max = 10,
+ step = 1,
+ get = get2,
+ set = set2_update,
+ disabled = isFrameFontShadowDisabled,
+ },
+ },
+ },
+
+ icons = {
+ order = 30,
+ type = "group",
+ name = L["Icons"],
+ args = {
+ headerIconSettings = {
+ type = "header",
+ order = 1,
+ name = L["Icon Settings"],
+ },
+ iconsEnabled = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Icons"],
+ desc = L["Show icons."],
+ get = "Options_Loot_ShowIcons",
+ set = set2,
+ disabled = isFrameItemDisabled,
+ },
+ iconsSize = {
+ order = 3,
+ name = L["Size"],
+ desc = L["Set the icon size. (Recommended value: 16)"],
+ type = "range",
+ min = 6,
+ max = 22,
+ step = 1,
+ get = "Options_Loot_IconSize",
+ set = set2,
+ disabled = isFrameIconDisabled,
+ },
+ spacerIconsEnabled = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Invisible Icons"],
+ desc = L["When icons are disabled, you can still enable invisible icons to line up text."],
+ get = "Options_Loot_EnableSpacerIcons",
+ set = set2,
+ disabled = isFrameIconSpacerDisabled,
+ },
+ },
+ },
+
+ fontColors = {
+ order = 40,
+ type = "group",
+ name = L["Colors"],
+ args = {
+ customColors_label = {
+ type = "header",
+ order = 0,
+ name = L["Custom Colors"],
+ },
+
+ customColor = {
+ order = 2,
+ type = "toggle",
+ name = L["All Text One Color (Override Color Settings)"],
+ width = "double",
+ desc = L["Change all the text in this frame to a specific color."],
+ get = get2,
+ set = set2,
+ },
+
+ fontColor = {
+ order = 3,
+ type = "color",
+ name = L["Color"],
+ get = getColor2,
+ set = setColor2,
+ hidden = isFrameCustomColorDisabled,
+ },
+
+ headerEventColor = {
+ type = "header",
+ order = 4,
+ name = L["Colors of the events"],
+ },
+ },
+ },
+
+ specialTweaks = {
+ order = 50,
+ type = "group",
+ name = L["Misc"],
+ args = {
+ specialTweaks = {
+ type = "header",
+ order = 0,
+ name = L["Miscellaneous Settings"],
+ },
+ showMoney = {
+ order = 1,
+ type = "toggle",
+ name = L["Looted Money"],
+ desc = L["Displays money that you pick up."],
+ get = "Options_Loot_ShowMoney",
+ set = set2,
+ },
+ showItems = {
+ order = 2,
+ type = "toggle",
+ name = L["Looted Items"],
+ desc = L["Displays items that you pick up."],
+ get = "Options_Loot_ShowItems",
+ set = set2,
+ },
+ showCurrency = {
+ order = 3,
+ type = "toggle",
+ name = L["Gained Currency"],
+ desc = L["Displays currency that you gain."],
+ get = "Options_Loot_ShowCurrency",
+ set = set2,
+ },
+ showItemTypes = {
+ order = 4,
+ type = "toggle",
+ name = L["Show Item Types"],
+ desc = L["Formats the looted message to also include the type of item (e.g. Trade Goods, Armor, Junk, etc.)."],
+ get = "Options_Loot_ShowItemTypes",
+ set = set2,
+ },
+ showItemTotal = {
+ order = 5,
+ type = "toggle",
+ name = L["Total Items"],
+ desc = L["Displays how many items you have in your bag."],
+ get = "Options_Loot_ShowItemTotals",
+ set = set2,
+ },
+ showCrafted = {
+ order = 6,
+ type = "toggle",
+ name = L["Crafted Items"],
+ desc = L["Displays items that you crafted."],
+ get = "Options_Loot_ShowCraftedItems",
+ set = set2,
+ },
+ showQuest = {
+ order = 7,
+ type = "toggle",
+ name = L["Quest Items"],
+ desc = L["Displays items that pertain to a quest."],
+ get = "Options_Loot_ShowQuestItems",
+ set = set2,
+ },
+ showPurchased = {
+ order = 8,
+ type = "toggle",
+ name = L["Purchased Items"],
+ desc = L["Displays items that were purchased from a vendor."],
+ get = "Options_Loot_ShowPurchasedItems",
+ set = set2,
+ },
+ colorBlindMoney = {
+ order = 9,
+ type = "toggle",
+ name = L["Color Blind Mode"],
+ desc = L["Displays money using letters G, S, and C instead of icons."],
+ get = "Options_Loot_ShowColorBlindMoney",
+ set = set2,
+ },
+ filterItemQuality = {
+ order = 10,
+ type = "select",
+ name = L["Filter Item Quality"],
+ desc = L["Will not display any items that are below this quality (does not filter Quest or Crafted items)."],
+ values = {
+ [0] = "1. |cff9d9d9d" .. ITEM_QUALITY0_DESC .. "|r", -- Poor
+ [1] = "2. |cffffffff" .. ITEM_QUALITY1_DESC .. "|r", -- Common
+ [2] = "3. |cff1eff00" .. ITEM_QUALITY2_DESC .. "|r", -- Uncommon
+ [3] = "4. |cff0070dd" .. ITEM_QUALITY3_DESC .. "|r", -- Rare
+ [4] = "5. |cffa335ee" .. ITEM_QUALITY4_DESC .. "|r", -- Epic
+ [5] = "6. |cffff8000" .. ITEM_QUALITY5_DESC .. "|r", -- Legendary
+ [6] = "7. |cffe6cc80" .. ITEM_QUALITY6_DESC .. "|r", -- Artifact
+ [7] = "8. |cffe6cc80" .. ITEM_QUALITY7_DESC .. "|r", -- Heirloom
+ },
+ get = "Options_Loot_ItemQualityFilter",
+ set = set2,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ optionsAddon.optionsTable.args.FloatingCombatText = {
+ name = L["Floating Combat Text"],
+ type = "group",
+ order = 1,
+ childGroups = "tab",
+ args = {
+ title2 = {
+ order = 0,
+ type = "description",
+ name = L["The following settings allow you to tweak Blizzard's Floating Combat Text."],
+ },
+
+ blizzardFCT = {
+ name = L["General"],
+ type = "group",
+ order = 1,
+ disabled = "CVar_BypassCVars",
+ args = {
+ enableFloatingCombatText = {
+ order = 1,
+ name = L["Enable Scrolling Combat Text (Self)"],
+ type = "toggle",
+ desc = L["Shows incoming damage and healing done to you. It is also required for a lot of the other events to work (as noted in their descriptions).\n\n|cffFF0000Changing this requires a UI Reload!|r"],
+ width = "double",
+ get = get0,
+ set = set0_update,
+ },
+
+ enableFCT_Header = {
+ type = "description",
+ order = 2,
+ name = L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"],
+ fontSize = "small",
+ width = "normal",
+ },
+
+ enableFCT_Spacer = {
+ type = "description",
+ order = 3,
+ name = "\n",
+ fontSize = "small",
+ width = "normal",
+ },
+
+ headerAppearance = {
+ type = "header",
+ order = 4,
+ name = L["Appearance"],
+ },
+
+ floatingCombatTextCombatDamageDirectionalOffset = {
+ order = 5,
+ name = L["Direction Offset"],
+ desc = L["The amount to offset the vertical origin of the directional damage numbers when they appear. (e.g. move them up and down)\n\n0 = Default"],
+ type = "range",
+ min = -20,
+ max = 20,
+ step = 0.1,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextCombatDamageDirectionalScale = {
+ order = 6,
+ name = L["Direction Scale"],
+ desc = L["The amount to scale the distance that directional damage numbers will move as they appear. Damage numbers will just scroll up if this is disabled.\n\n0 = Disabled\n1 = Default\n3.6 = Recommended"],
+ type = "range",
+ min = -5,
+ max = 5,
+ step = 0.1,
+ get = get0,
+ set = set0_update,
+ },
+
+ -- Damage
+ headerDamage = {
+ type = "header",
+ order = 10,
+ name = L["Damage"],
+ },
+
+ floatingCombatTextCombatDamage = {
+ order = 11,
+ name = L["Show Damage"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_DAMAGE,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextCombatLogPeriodicSpells = {
+ order = 12,
+ name = L["Show DoTs"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_LOG_PERIODIC_EFFECTS,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextCombatDamageAllAutos = {
+ order = 13,
+ name = L["Show Auto Attacks"],
+ type = "toggle",
+ desc = L["Enable this option if you want to see all auto-attacks."],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextPetMeleeDamage = {
+ order = 14,
+ name = L["Show Pet Melee"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_PET_MELEE_DAMAGE,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextPetSpellDamage = {
+ order = 15,
+ name = L["Show Pet Spells"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_PET_MELEE_DAMAGE,
+ get = get0,
+ set = set0_update,
+ },
+
+ -- Healing and Absorbs
+ headerHealingAbsorbs = {
+ type = "header",
+ order = 20,
+ name = L["Healing and Absorbs"],
+ },
+
+ floatingCombatTextCombatHealing = {
+ order = 21,
+ name = L["Show Healing"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_COMBAT_HEALING,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextFriendlyHealers = {
+ order = 22,
+ name = L["Show Friendly Healers"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_FRIENDLY_NAMES
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextCombatHealingAbsorbSelf = {
+ order = 23,
+ name = L["Show Absorbs (Self)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_COMBAT_HEALING_ABSORB_SELF
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextCombatHealingAbsorbTarget = {
+ order = 24,
+ name = L["Show Absorbs (Target)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_COMBAT_HEALING_ABSORB_TARGET,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextDamageReduction = {
+ order = 25,
+ name = L["Show Damage Reduction"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_RESISTANCES
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ -- Gains
+ headerGains = {
+ type = "header",
+ order = 30,
+ name = L["Player Gains"],
+ },
+
+ floatingCombatTextEnergyGains = {
+ order = 31,
+ name = L["Show Energy"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_ENERGIZE
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextPeriodicEnergyGains = {
+ order = 31,
+ name = L["Show Energy (Periodic)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_PERIODIC_ENERGIZE
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextComboPoints = {
+ order = 32,
+ name = L["Show Combo Points"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_COMBO_POINTS
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextHonorGains = {
+ order = 33,
+ name = L["Show Honor"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_HONOR_GAINED
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextRepChanges = {
+ order = 34,
+ name = L["Show Rep Changes"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_REPUTATION
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ -- Status Effects
+ headerStatusEffects = {
+ type = "header",
+ order = 40,
+ name = L["Status Effects"],
+ },
+
+ floatingCombatTextDodgeParryMiss = {
+ order = 41,
+ name = L["Show Miss Types"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_DODGE_PARRY_MISS,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextAuras = {
+ order = 42,
+ name = L["Show Auras"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextSpellMechanics = {
+ order = 43,
+ name = L["Show Effects (Mine)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_TARGET_EFFECTS,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextSpellMechanicsOther = {
+ order = 44,
+ name = L["Show Effects (Group)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_OTHER_TARGET_EFFECTS,
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextAllSpellMechanics = {
+ order = 45,
+ name = L["Show Effects (All)"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_SHOW_OTHER_TARGET_EFFECTS,
+ get = get0,
+ set = set0_update,
+ },
+
+ CombatThreatChanges = {
+ order = 46,
+ type = "toggle",
+ name = L["Show Threat Changes"],
+ desc = L["Enable this option if you want to see threat changes."],
+ get = get0,
+ set = set0_update,
+ },
+
+ -- Player's Status
+ headerPlayerStatus = {
+ type = "header",
+ order = 50,
+ name = L["Player Status"],
+ },
+
+ floatingCombatTextCombatState = {
+ order = 52,
+ name = L["Show Combat State"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_COMBAT_STATE
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextLowManaHealth = {
+ order = 53,
+ name = L["Show Low HP/Mana"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_LOW_HEALTH_MANA
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+
+ floatingCombatTextReactives = {
+ order = 54,
+ name = L["Show Reactives"],
+ type = "toggle",
+ desc = OPTION_TOOLTIP_COMBAT_TEXT_SHOW_REACTIVES
+ .. L["\n\n|cffFF0000Requires Self Scrolling Combat Text|r"],
+ get = get0,
+ set = set0_update,
+ },
+ },
+ },
+
+ advancedSettings = {
+ name = L["Advanced"],
+ type = "group",
+ order = 2,
+ args = {
+ bypassCVARUpdates = {
+ order = 4,
+ type = "toggle",
+ name = L["Bypass CVar Updates (requires |cffFF0000/reload|r)"],
+ desc = L["Allows you to bypass xCT+'s CVar engine. This option might help if you have FCT enabled, but it disappears after awhile. Once you set your FCT options, enable this.\n\n|cffFF0000Changing this requires a UI Reload!|r"],
+ width = "double",
+ get = function()
+ return x.db.profile.bypassCVars
+ end,
+ set = function(_, value)
+ x.db.profile.bypassCVars = value
+ end,
+ },
+
+ enableFCT_Header = {
+ type = "description",
+ order = 5,
+ name = L["|CffFF0000Requires:|r |cff00FF33/reload|r after change"],
+ fontSize = "small",
+ width = "normal",
+ },
+ },
+ },
+ },
+ }
+
+ optionsAddon.optionsTable.args.spells = {
+ name = L["Spam Merger"],
+ type = "group",
+ childGroups = "tab",
+ order = 2,
+ args = {
+ explanation = {
+ type = "description",
+ order = 1,
+ name = L["Normally all damage / heal events of a spell will result in one message each.\nSo AoE spells like Rain of Fire or Spinning Crane Kick will spam a lot of messages into the xCT frames.\nIf the spam merger is enabled, then the damage events in a configured interval of X seconds of each spell will be merged into one message.\n|cffFF0000Drawback|r: the (merged) message will be delayed by the configured interval!!\nUse an interval of 0 to disable the specific merge."],
+ },
+
+ mergeOptions = {
+ name = L["Merge Options"],
+ type = "group",
+ order = 11,
+ args = {
+ enableMerger = {
+ order = 2,
+ type = "toggle",
+ name = L["Enable Spam Merger"],
+ get = "Options_SpamMerger_EnableSpamMerger",
+ set = set0_1,
+ },
+ enableMergerDebug = {
+ order = 3,
+ type = "toggle",
+ name = L["Enable Debugging"],
+ desc = L["Adds the spell ID to each message for this session only."],
+ get = function()
+ return x.enableMergerDebug or false
+ end,
+ set = function(_, value)
+ x.enableMergerDebug = value
+ end,
+ },
+
+ outgoingHeader = {
+ type = "header",
+ order = 10,
+ name = L["Outgoing Damage / Healing"],
+ },
+
+ outgoingExplanation = {
+ type = "description",
+ order = 11,
+ name = L["The merge interval for a lot of spells can be set via the 'Class Spells', 'Global Spells/Items' and 'Racial Spells' tabs."],
+ },
+
+ mergeOutgoingDamageMissesInterval = {
+ order = 23,
+ name = L["Merge-Interval Outgoing Misses"],
+ desc = L["The interval (seconds) in which outgoing full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_OutgoingDamageMissesInterval",
+ set = set0_1,
+ },
+
+ mergeEverythingInterval = {
+ order = 12,
+ name = L["Merge-Interval for other spells"],
+ desc = L["The interval (seconds) in which all other spells will be merged. Certain spells have other intervals, see the tabs for them. Use 0 to disable."],
+ type = "range",
+ min = 0.1,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_FallbackInterval",
+ set = set0_1,
+ },
+
+ incomingHeader = {
+ type = "header",
+ order = 20,
+ name = L["Incoming Damage / Healing"],
+ },
+
+ mergeIncomingHealingInterval = {
+ order = 21,
+ name = L["Merge-Interval Incoming Healing"],
+ desc = L["The interval (seconds) in which incoming healing will be merged. All healing done by the same person will be merged together! Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_IncomingHealingInterval",
+ set = set0_1,
+ },
+
+ mergeIncomingDamageInterval = {
+ order = 22,
+ name = L["Merge-Interval Incoming Damage"],
+ desc = L["The interval (seconds) in which incoming damage will be merged. Different messages will still be displayed for different spells. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_IncomingDamageInterval",
+ set = set0_1,
+ },
+
+ mergeIncomingMissesInterval = {
+ order = 23,
+ name = L["Merge-Interval Incoming Misses"],
+ desc = L["The interval (seconds) in which incoming full misses, dodges and parries will be merged. Different messages will still be displayed for different types of miss. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_IncomingMissesInterval",
+ set = set0_1,
+ },
+
+ petAttacksHeader = {
+ type = "header",
+ order = 30,
+ name = L["Pet Attacks"],
+ },
+
+ mergePetInterval = {
+ order = 31,
+ name = L["Merge-Interval for ALL Pet Abilities"],
+ desc = L["The interval (seconds) in which ALL pet damage will be merged. It will use your pet's icon instead of an spell icon. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_PetAttackInterval",
+ set = set0_1,
+ },
+
+ mergePetColor = {
+ order = 32,
+ type = "color",
+ name = L["Pet Color"],
+ desc = L["Which color do you want the merged pet messages to be?"],
+ get = getColor0_1,
+ set = setColor0_1,
+ },
+
+ --[[
+ spacer1 = {
+ type = "description",
+ order = 37,
+ name = "",
+ width = 'full',
+ },
+
+ mergeVehicle = {
+ order = 38,
+ type = 'toggle',
+ name = L["Merge Vehicle Abilities"],
+ desc = L["Merges all of your vehicle abilities together."],
+ get = get0_1,
+ set = set0_1,
+ },
+
+ mergeVehicleColor = {
+ order = 39,
+ type = 'color',
+ name = L["Vehicle Color"],
+ get = getColor0_1,
+ set = setColor0_1,
+ },
+ ]]
+
+ criticalHitsHeader = {
+ type = "header",
+ order = 40,
+ name = L["Critical Hits"],
+ },
+
+ criticalHitsExplanation = {
+ type = "description",
+ order = 41,
+ name = L["Please choose one:"],
+ },
+
+ mergeDontMergeCriticals = {
+ order = 42,
+ type = "toggle",
+ name = L["Don't Merge Critical Hits Together"],
+ desc = L["Crits will not get merged in the critical frame, but they will be included in the outgoing total. |cffFFFF00(Default)|r"],
+ get = "Options_SpamMerger_DontMergeCriticals",
+ set = setSpecialCriticalOptions,
+ width = "full",
+ },
+
+ mergeCriticalsWithOutgoing = {
+ order = 43,
+ type = "toggle",
+ name = L["Merge Critical Hits with Outgoing"],
+ desc = L["Crits will be merged, but the total merged amount in the outgoing frame includes crits."],
+ get = "Options_SpamMerger_MergeCriticalsWithOutgoing",
+ set = setSpecialCriticalOptions,
+ width = "full",
+ },
+
+ mergeCriticalsByThemselves = {
+ order = 44,
+ type = "toggle",
+ name = L["Merge Critical Hits by Themselves"],
+ desc = L["Crits will be merged and the total merged amount in the outgoing frame |cffFF0000DOES NOT|r include crits."],
+ get = "Options_SpamMerger_MergeCriticalsByThemselves",
+ set = setSpecialCriticalOptions,
+ width = "full",
+ },
+
+ mergeHideMergedCriticals = {
+ order = 45,
+ type = "toggle",
+ name = L["Hide Merged Criticals"],
+ desc = L["Criticals that have been merged with the Outgoing frame will not be shown in the Critical frame"],
+ get = "Options_SpamMerger_HideMergedCriticals",
+ set = setSpecialCriticalOptions,
+ width = "full",
+ },
+
+ dispellHeader = {
+ type = "header",
+ order = 50,
+ name = L["Other"],
+ },
+
+ mergeDispellInterval = {
+ order = 51,
+ name = L["Merge-Interval for Dispells"],
+ desc = L["The interval (seconds) in which dispells are merged together. Only dispells for the same aura (by name) will be merged. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = "Options_SpamMerger_DispellInterval",
+ set = set0_1,
+ },
+
+ mergeReputationInterval = {
+ order = 52,
+ name = L["Merge-Interval for Reputation"],
+ desc = L["The interval (seconds) in which reputation gains / losses are merged together. Use 0 to disable."],
+ type = "range",
+ min = 0,
+ max = 15,
+ step = 0.1,
+ get = "Options_SpamMerger_ReputationInterval",
+ set = set0_1,
+ },
+ },
+ },
+
+ classList = {
+ name = L["Class Spells"],
+ type = "group",
+ order = 21,
+ childGroups = "select",
+ args = {
+ ["DEATHKNIGHT"] = { type = "group", order = 1, name = getColoredClassName("DEATHKNIGHT") },
+ ["DEMONHUNTER"] = { type = "group", order = 2, name = getColoredClassName("DEMONHUNTER") },
+ ["DRUID"] = { type = "group", order = 3, name = getColoredClassName("DRUID") },
+ ["EVOKER"] = { type = "group", order = 4, name = getColoredClassName("EVOKER") },
+ ["HUNTER"] = { type = "group", order = 5, name = getColoredClassName("HUNTER") },
+ ["MAGE"] = { type = "group", order = 6, name = getColoredClassName("MAGE") },
+ ["MONK"] = { type = "group", order = 7, name = getColoredClassName("MONK") },
+ ["PALADIN"] = { type = "group", order = 8, name = getColoredClassName("PALADIN") },
+ ["PRIEST"] = { type = "group", order = 9, name = getColoredClassName("PRIEST") },
+ ["ROGUE"] = { type = "group", order = 10, name = getColoredClassName("ROGUE") },
+ ["SHAMAN"] = { type = "group", order = 11, name = getColoredClassName("SHAMAN") },
+ ["WARLOCK"] = { type = "group", order = 12, name = getColoredClassName("WARLOCK") },
+ ["WARRIOR"] = { type = "group", order = 13, name = getColoredClassName("WARRIOR") },
+ },
+ },
+
+ globalList = {
+ name = L["Global Spells / Items"],
+ type = "group",
+ order = 22,
+ args = {},
+ },
+
+ raceList = {
+ name = L["Racial Spells"],
+ type = "group",
+ order = 23,
+ args = {},
+ },
+ },
+ }
+
+ optionsAddon.optionsTable.args.spellFilter = {
+ name = L["Filters"],
+ type = "group",
+ order = 3,
+ args = {
+ filterValues = {
+ name = L["Minimal Value Thresholds"],
+ type = "group",
+ order = 10,
+ guiInline = true,
+ args = {
+ headerPlayerPower = {
+ order = 0,
+ type = "header",
+ name = L["Incoming Player Power Threshold (Mana, Rage, Energy, etc.)"],
+ },
+ filterPowerValue = {
+ order = 1,
+ type = "input",
+ name = L["Minimum Threshold"],
+ desc = L["The minimal amount of player's power required in order for it to be displayed."],
+ get = "Options_Filter_PlayerPowerMinimumThreshold",
+ set = setNumber2,
+ },
+
+ headerOutgoingDamage = {
+ order = 10,
+ type = "header",
+ name = L["Outgoing Damage"],
+ },
+ filterOutgoingDamageValue = {
+ order = 11,
+ type = "input",
+ name = L["Minimum Threshold"],
+ desc = L["The minimal amount of damage required in order for it to be displayed."],
+ get = "Options_Filter_OutgoingDamage_Noncritical_MinimumThreshold",
+ set = setNumber2,
+ },
+ filterOutgoingDamageCritEnabled = {
+ order = 12,
+ type = "toggle",
+ name = L["Use other threshold for Crits"],
+ desc = L["Enable a different threshold for outgoing damage criticals."],
+ get = "Options_Filter_OutgoingDamage_Critical_UseOwnThreshold",
+ set = set0_1,
+ },
+ filterOutgoingDamageCritValue = {
+ order = 13,
+ type = "input",
+ name = L["Minimum Threshold for Crits"],
+ desc = L["The minimal amount of damage required for a critical in order for it to be displayed."],
+ get = "Options_Filter_OutgoingDamage_Critical_MinimumThreshold",
+ set = setNumber2,
+ hidden = function()
+ return not x:Options_Filter_OutgoingDamage_Critical_UseOwnThreshold()
+ end,
+ },
+
+ headerOutgoingHealing = {
+ order = 20,
+ type = "header",
+ name = L[L["Outgoing Healing"]],
+ },
+ filterOutgoingHealingValue = {
+ order = 21,
+ type = "input",
+ name = L["Minimum Threshold"],
+ desc = L["The minimal amount of healing required in order for it to be displayed."],
+ get = "Options_Filter_OutgoingHealing_Noncritical_MinimumThreshold",
+ set = setNumber2,
+ },
+ filterOutgoingHealingCritEnabled = {
+ order = 22,
+ type = "toggle",
+ name = L["Use other threshold for Crits"],
+ desc = L["Enable a different threshold for outgoing healing criticals."],
+ get = "Options_Filter_OutgoingHealing_Critical_UseOwnThreshold",
+ set = set0_1,
+ },
+ filterOutgoingHealingCritValue = {
+ order = 23,
+ type = "input",
+ name = L["Minimum Threshold for Crits"],
+ desc = L["The minimal amount of healing required for a critical in order for it to be displayed."],
+ get = "Options_Filter_OutgoingHealing_Critical_MinimumThreshold",
+ set = setNumber2,
+ hidden = function()
+ return not x:Options_Filter_OutgoingHealing_Critical_UseOwnThreshold()
+ end,
+ },
+
+ headerIncomingDamage = {
+ order = 30,
+ type = "header",
+ name = L["Incoming Damage"],
+ },
+ filterIncomingDamageValue = {
+ order = 31,
+ type = "input",
+ name = L["Minimum Threshold"],
+ desc = L["The minimal amount of damage required in order for it to be displayed."],
+ get = "Options_Filter_IncomingDamage_Noncritical_MinimumThreshold",
+ set = setNumber2,
+ },
+ filterIncomingDamageCritEnabled = {
+ order = 32,
+ type = "toggle",
+ name = L["Use other threshold for Crits"],
+ desc = L["Enable a different threshold for incoming damage criticals."],
+ get = "Options_Filter_IncomingDamage_Critical_UseOwnThreshold",
+ set = set0_1,
+ },
+ filterIncomingDamageCritValue = {
+ order = 33,
+ type = "input",
+ name = L["Minimum Threshold for Crits"],
+ desc = L["The minimal amount of damage required for a critical in order for it to be displayed."],
+ get = "Options_Filter_IncomingDamage_Critical_MinimumThreshold",
+ set = setNumber2,
+ hidden = function()
+ return not x:Options_Filter_IncomingDamage_Critical_UseOwnThreshold()
+ end,
+ },
+
+ headerIncomingHealing = {
+ order = 40,
+ type = "header",
+ name = L["Incoming Healing"],
+ },
+ filterIncomingHealingValue = {
+ order = 41,
+ type = "input",
+ name = L["Minimum Threshold"],
+ desc = L["The minimal amount of healing required in order for it to be displayed."],
+ get = "Options_Filter_IncomingHealing_Noncritical_MinimumThreshold",
+ set = setNumber2,
+ },
+ filterIncomingHealingCritEnabled = {
+ order = 42,
+ type = "toggle",
+ name = L["Use other threshold for Crits"],
+ desc = L["Enable a different threshold for incoming healing criticals."],
+ get = "Options_Filter_IncomingHealing_Critical_UseOwnThreshold",
+ set = set0_1,
+ },
+ filterIncomingHealingCritValue = {
+ order = 43,
+ type = "input",
+ name = L["Minimum Threshold for Crits"],
+ desc = L["The minimal amount of healing required for a critical in order for it to be displayed."],
+ get = "Options_Filter_IncomingHealing_Critical_MinimumThreshold",
+ set = setNumber2,
+ hidden = function()
+ return not x:Options_Filter_IncomingHealing_Critical_UseOwnThreshold()
+ end,
+ },
+
+ headerSpellTracker = {
+ order = 50,
+ type = "header",
+ name = L["Spell History"],
+ },
+ trackSpells = {
+ order = 51,
+ type = "toggle",
+ name = L["Track all Spells"],
+ desc = L["Track all the spells that you've seen. This will make filtering them out easier."],
+ get = "Options_Filter_TrackSpells",
+ set = set0_1,
+ },
+ },
+ },
+
+ listBuffs = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDBuffs|r"],
+ type = "group",
+ order = 20,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out |cff1AFF1ABuff|r auras that your player gains or loses."],
+ },
+ whitelistBuffs = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered auras gains and fades that are |cff1AFF1ABuffs|r will be on a whitelist (opposed to a blacklist)."],
+ get = "Options_Filter_BuffWhitelist",
+ set = set0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Buff to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via Name"],
+ desc = L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Power Word: Fortitude')."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff1AFF1ABuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetBuffHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Buff from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered Buff"],
+ desc = L["Remove the Buff from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listDebuffs = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDDebuffs|r"],
+ type = "group",
+ order = 30,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out |cffFF1A1ADebuff|r auras that your player gains or loses."],
+ },
+ whitelistDebuffs = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered auras gains and fades that are |cffFF1A1ADebuffs|r will be on a whitelist (opposed to a blacklist)."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Debuff to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via Name"],
+ desc = L["The full, case-sensitive name of the |cff1AFF1ABuff|r you want to filter (e.g. 'Shadow Word: Pain')."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cffFF0000Debuff|r names that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetDebuffHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Debuff from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered Debuff"],
+ desc = L["Remove the Debuff from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listProcs = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDProcs|r"],
+ type = "group",
+ order = 40,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out spell |cffFFFF00Procs|r that your player triggers."],
+ },
+ whitelistProcs = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Check for whitelist, uncheck for blacklist."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Proc to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via Name"],
+ desc = L["The full, case-sensitive name of the |cff1AFF1AProc|r you want to filter (e.g. 'Power Word: Fortitude')."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff1AFF1AProcs|r that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetProcHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Proc from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered proc"],
+ desc = L["Remove the proc from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listSpells = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDOutgoing Spells|r"],
+ type = "group",
+ order = 50,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter |cff71d5ffOutgoing Spells|r that your player does."],
+ },
+ whitelistSpells = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered |cff71d5ffOutgoing Spells|r will be on a whitelist (opposed to a blacklist)."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Spell to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via ID"],
+ desc = L["The spell ID of the |cff71d5ffOutgoing Spell|r you want to filter."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff71d5ffOutgoing Spell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetSpellHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Spell from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered spell"],
+ desc = L["Remove the spell ID from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listItems = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDItems|r"],
+ type = "group",
+ order = 60,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out |cff8020FFItems|r that your player collects."],
+ },
+ whitelistItems = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered |cff798BDDItems|r will be on a whitelist (opposed to a blacklist)."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Item to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via ID"],
+ desc = L["The ID of the |cff798BDDItem|r you want to filter."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff798BDDItem|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetItemHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Item from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered Item"],
+ desc = L["Remove the Item from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listDamage = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDIncoming Damage|r"],
+ type = "group",
+ order = 70,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Damage|r to your character."],
+ },
+ whitelistDamage = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered |cff71d5ffIncoming Damage Spells|r will be on a whitelist (opposed to a blacklist)."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Spell to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via ID"],
+ desc = L["The Spell ID of the |cff798BDDSpell|r you want to filter."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff798BDDSpell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetDamageIncomingHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Spell from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered spell"],
+ desc = L["Remove the spell ID from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+
+ listHealing = {
+ name = L["|cffFFFFFFFilter:|r |cff798BDDIncoming Healing|r"],
+ type = "group",
+ order = 80,
+ guiInline = false,
+ args = {
+ description = {
+ order = 0,
+ type = "description",
+ name = L["These options allow you to filter out certain |cffFFFF00Spell ID|rs from |cff798BDDIncoming Healing|r to your character."],
+ },
+ whitelistHealing = {
+ order = 1,
+ type = "toggle",
+ name = L["Whitelist"],
+ desc = L["Filtered |cff71d5ffIncoming Healing Spells|r will be on a whitelist (opposed to a blacklist)."],
+ set = set0_1,
+ get = get0_1,
+ },
+
+ headerAdd = {
+ order = 10,
+ type = "header",
+ name = L["Add new Spell to filter"],
+ },
+ spellName = {
+ order = 11,
+ type = "input",
+ name = L["Add via ID"],
+ desc = L["The Spell ID of the |cff798BDDSpell|r you want to filter."],
+ set = AddFilteredSpell,
+ },
+ selectTracked = {
+ order = 12,
+ type = "select",
+ name = L["Add via History"],
+ desc = L["A list of |cff798BDDSpell|r IDs that have been seen. |cffFF0000Requires:|r |cff798BDDTrack Spell History|r"],
+ disabled = IsTrackSpellsDisabled,
+ values = GetHealingIncomingHistory,
+ set = AddFilteredSpell,
+ },
+
+ headerRemove = {
+ order = 20,
+ type = "header",
+ name = L["Remove Spell from filter"],
+ },
+ removeSpell = {
+ order = 21,
+ type = "select",
+ name = L["Remove filtered spell"],
+ desc = L["Remove the spell ID from the config all together."],
+ values = getFilteredSpells,
+ set = removeFilteredSpell,
+ },
+ },
+ },
+ },
+ }
+
+ optionsAddon.optionsTable.args.SpellColors = {
+ name = L["Spell School Colors"],
+ type = "group",
+ order = 4,
+ args = {
+ title = {
+ type = "header",
+ order = 0,
+ name = L["Customize Spell School Colors"],
+ },
+ },
+ }
+
+ optionsAddon.optionsTable.args.Credits = {
+ name = L["Credits"],
+ type = "group",
+ order = 5,
+ args = {
+ title = {
+ type = "header",
+ order = 0,
+ name = L["Special Thanks"],
+ },
+ specialThanksList = {
+ order = 1,
+ type = "description",
+ fontSize = "medium",
+ name = "|cffAA0000Tukz|r, |cffAA0000Elv|r, |cffFFFF00Affli|r, |cffFF8000BuG|r, |cff8080FFShestak|r, Toludin, Nidra, gnangnan, NitZo, Naughtia, Derap, sortokk, ckaotik, Cecile",
+ },
+
+ testerTitle = {
+ order = 10,
+ type = "header",
+ name = L["Beta Testers - Version 3.0.0"],
+ },
+ userName1 = {
+ type = "description",
+ order = 11,
+ fontSize = "medium",
+ name = "Alex, BuG, Kkthnxbye, Azilroka, Prizma, schmeebs, Pat, hgwells, Jaron, Fitzbattleaxe, Nihan, Jaxo, Schaduw, sylenced, kaleidoscope, Killatones, Trokko, Yperia, Edoc, Cazart, Nevah, Refrakt, Thakah, johnis007, Sgt, NitZo, cptblackgb, pollyzoid",
+ },
+
+ curseTitle = {
+ order = 20,
+ type = "header",
+ name = L["Beta Testers - Version 4.0.0 (Curse)"],
+ },
+ userName2 = {
+ order = 21,
+ type = "description",
+ fontSize = "medium",
+ name = "CadjieBOOM, Mokal, ShadoFall, alloman, chhld, chizzlestick, egreym, nukme, razrwolf, star182, zacheklund",
+ },
+
+ tukuiTitle = {
+ order = 30,
+ type = "header",
+ name = L["Beta Testers - Version 4.0.0 (Tukui)"],
+ },
+ userName3 = {
+ order = 31,
+ type = "description",
+ fontSize = "medium",
+ name = "Affiniti, Badinfluence, BuG, Curdi, Dorkie, Galadeon, HarryDotter, Joebacsi21, Kuron, Mabb22, Narlya, Nihan, Verdell, arzelia, blessed, djouga, fakemessiah, faze, firewall, jatha86, jaydogg10, jlor, lunariongames, stoankold",
+ },
+
+ tukuiTitleLegion = {
+ order = 40,
+ type = "header",
+ name = L["Beta Testers - Version 4.3.0+ (Legion)"],
+ },
+ userName3Legion = {
+ order = 41,
+ type = "description",
+ fontSize = "medium",
+ name = "Azazu, Broni, CursedBunny, Daemios, Dajova, Delerionn, dunger, feetss, gesuntight, Homaxz, karamei, Merathilis, re1jo, sammael666, scathee, Tonyleila, Torch, WetU, Znuff, Zylos",
+ },
+
+ tukuiTitleBfA = {
+ order = 50,
+ type = "header",
+ name = L["Beta Testers - Version 4.4.0+ (Battle for Azeroth)"],
+ },
+ userName3BfA = {
+ order = 51,
+ type = "description",
+ fontSize = "medium",
+ name = "Toludin",
+ },
+
+ githubTitle = {
+ type = "header",
+ order = 70,
+ name = L["GitHub Contributors"],
+ },
+ githubUsers = {
+ order = 71,
+ type = "description",
+ fontSize = "medium",
+ name = "oBusk, BourgeoisM, Witnesscm, Tonyleila, ckaotik, Stanzilla, Torch (behub), vforge, Toludin (BfA Update!), Edarlingen",
+ },
+
+ translatorsTitle = {
+ type = "header",
+ order = 80,
+ name = L["zhCN Translators"],
+ },
+ gitHubUsers = {
+ order = 81,
+ type = "description",
+ fontSize = "medium",
+ name = "萌丶汉丶纸 (fredako)",
+ },
+
+ translatorsTitle = {
+ type = "header",
+ order = 90,
+ name = L["ruRU Translators"],
+ },
+ gitHubUsers = {
+ order = 91,
+ type = "description",
+ fontSize = "medium",
+ name = "ZamestoTV",
+ },
+
+ contactMeTitle = {
+ order = 100,
+ type = "header",
+ name = L["How to contact me"],
+ },
+ contactMeGithub = {
+ order = 101,
+ type = "description",
+ name = L["Create an issue at GitHub:"] .. " https://github.com/dandruff/xCT",
+ },
+ contactMeCurseforgeComment = {
+ order = 102,
+ type = "description",
+ name = L["Create a comment on Curseforge:"] .. " https://www.curseforge.com/wow/addons/xct-plus/comments"
+ },
+ contactMeCurseforgePm = {
+ order = 103,
+ type = "description",
+ name = L["Write me a PM on Curseforge:"] .. " https://www.curseforge.com/members/redaces"
+ }
+ },
+ }
+
+ xo:UpdateOptionsTableSpamMergerSpells()
+ xo:UpdateAuraSpellFilter()
+ xo:GenerateSpellSchoolColors()
+ xo:GenerateColorOptions()
+end
+
+-- Gets spammy spells from the database and creates options
+function xo:UpdateOptionsTableSpamMergerSpells()
+ local function SpamMergerGetSpellInterval(info)
+ local spellId = tonumber(info[#info])
+ if x.db.profile.spells.merge[spellId] ~= nil and x.db.profile.spells.merge[spellId].interval ~= nil then
+ return x.db.profile.spells.merge[spellId].interval
+ end
+ return xCT_Plus.merges[spellId].interval or 0
+ end
+
+ local function SpamMergerSetSpellInterval(info, value)
+ local spellId = tonumber(info[#info])
+ local db = x.db.profile.spells.merge[spellId] or {}
+ db.interval = value
+ x.db.profile.spells.merge[spellId] = db
+ end
+
+ local spells = optionsAddon.optionsTable.args.spells.args.classList.args
+ local global = optionsAddon.optionsTable.args.spells.args.globalList.args
+ local racetab = optionsAddon.optionsTable.args.spells.args.raceList.args
+
+ for class, specs in pairs(xo.CLASS_NAMES) do
+ spells[class].args = {}
+ for spec, index in pairs(specs) do
+ local name, _ = "All Specializations"
+ if index ~= 0 then
+ _, name = GetSpecializationInfoByID(spec)
+ end
+
+ spells[class].args["specHeader" .. index] = {
+ type = "header",
+ order = index * 2,
+ name = name,
+ }
+ end
+ end
+
+ -- Create a list of the categories (to be sorted)
+ local spamMergerGlobalSpellCategories = {}
+ local spamMergerRacialSpellCategories = {}
+ for _, entry in pairs(xCT_Plus.merges) do
+ if not xo.CLASS_NAMES[entry.category] then
+ if entry.racial_spell then
+ table.insert(
+ spamMergerRacialSpellCategories,
+ { category = entry.category, order = entry.categoryOrder }
+ )
+ else
+ table.insert(
+ spamMergerGlobalSpellCategories,
+ { category = entry.category, order = entry.categoryOrder }
+ )
+ end
+ end
+ end
+
+ -- Show Categories in insert order
+ local function sortTableByOrder(a, b)
+ return a.order < b.order
+ end
+ table.sort(spamMergerGlobalSpellCategories, sortTableByOrder)
+ table.sort(spamMergerRacialSpellCategories, sortTableByOrder)
+
+ -- Assume less than 1000 entries per category ;)
+ local spamMergerGlobalSpellOrders = {}
+ for i, category in pairs(spamMergerGlobalSpellCategories) do
+ local currentIndex = i * 1000
+
+ -- TODO localization for category.category?
+
+ -- Create the Category Header
+ global[category.category] = {
+ type = "header",
+ order = currentIndex,
+ name = category.category,
+ }
+ spamMergerGlobalSpellOrders[category.category] = currentIndex + 1
+ end
+
+ local spamMergerRacialSpellOrders = {}
+ for i, rcategory in pairs(spamMergerRacialSpellCategories) do
+ local rcurrentIndex = i * 1000
+
+ -- TODO localization for rcategory.category?
+
+ -- Create the Category Header
+ racetab[rcategory.category] = {
+ type = "header",
+ order = rcurrentIndex,
+ name = rcategory.category,
+ }
+ spamMergerRacialSpellOrders[rcategory.category] = rcurrentIndex + 1
+ end
+
+ -- Update the UI
+ for spellID, entry in pairs(xCT_Plus.merges) do
+ local name = C_Spell.GetSpellName(spellID)
+ if name then
+ -- Create a useful description for the spell
+ local desc = string.format(
+ "%s\n\n|cffFF0000%s|r |cff798BDD%s|r",
+ C_Spell.GetSpellDescription(spellID) or L["No Description"],
+ L["ID"],
+ spellID
+ )
+
+ -- TODO C_Spell.GetSpellDescription() sometimes returns "", what to do I do then ?
+
+ local firstSecondaryIdFound = true
+ for originalSpellId, replaceSpellId in pairs(xCT_Plus.replaceSpellId) do
+ if replaceSpellId == spellID then
+ if firstSecondaryIdFound then
+ desc = desc .. "\n|cffFF0000" .. L["Secondary ID(s)"] .. "|r |cff798BDD" .. originalSpellId
+ firstSecondaryIdFound = false
+ else
+ desc = desc .. ", " .. originalSpellId
+ end
+ end
+ end
+ if not firstSecondaryIdFound then
+ desc = desc .. "|r"
+ end
+ -- TODO replacement spells without explicit merging entries are not displayed here
+
+ -- Add the spell to the UI
+ if xo.CLASS_NAMES[entry.category] then
+ local index = xo.CLASS_NAMES[entry.category][tonumber(entry.desc) or 0]
+ spells[entry.category].args[tostring(spellID)] = {
+ order = index * 2 + 1,
+ name = name,
+ desc = desc,
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = SpamMergerGetSpellInterval,
+ set = SpamMergerSetSpellInterval,
+ }
+ elseif entry.racial_spell then
+ racetab[tostring(spellID)] = {
+ order = spamMergerRacialSpellOrders[entry.category],
+ name = name,
+ desc = desc,
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = SpamMergerGetSpellInterval,
+ set = SpamMergerSetSpellInterval,
+ }
+ spamMergerRacialSpellOrders[entry.category] = spamMergerRacialSpellOrders[entry.category] + 1
+ else
+ global[tostring(spellID)] = {
+ order = spamMergerGlobalSpellOrders[entry.category],
+ name = name,
+ desc = desc,
+ type = "range",
+ min = 0,
+ max = 5,
+ step = 0.1,
+ get = SpamMergerGetSpellInterval,
+ set = SpamMergerSetSpellInterval,
+ }
+ spamMergerGlobalSpellOrders[entry.category] = spamMergerGlobalSpellOrders[entry.category] + 1
+ end
+ end
+ end
+end
+
+-- Get and set methods for the spell filter
+local function isSpellFiltered(info)
+ return x.db.profile.spellFilter[info[#info - 2]][info[#info]]
+end
+
+local function setIsSpellFiltered(info, value)
+ x.db.profile.spellFilter[info[#info - 2]][info[#info]] = value
+end
+
+-- Update the Buff, Debuff and Spell filter list
+function xo:UpdateAuraSpellFilter(specific)
+ if not specific or specific == "buffs" then
+ optionsAddon.optionsTable.args.spellFilter.args.listBuffs.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Buffs |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listBuffs.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local buffs = optionsAddon.optionsTable.args.spellFilter.args.listBuffs.args.list.args
+ local updated = false
+
+ -- Update buffs
+ for name in pairs(x.db.profile.spellFilter.listBuffs) do
+ updated = true
+ buffs[name] = {
+ name = name,
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ end
+
+ if not updated then
+ buffs.noSpells = {
+ name = L["No buffs have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ if not specific or specific == "debuffs" then
+ optionsAddon.optionsTable.args.spellFilter.args.listDebuffs.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Debuffs |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listDebuffs.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local debuffs = optionsAddon.optionsTable.args.spellFilter.args.listDebuffs.args.list.args
+ local updated = false
+
+ for name in pairs(x.db.profile.spellFilter.listDebuffs) do
+ updated = true
+ debuffs[name] = {
+ name = name,
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ end
+
+ if not updated then
+ debuffs.noSpells = {
+ name = L["No debuffs have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ -- Update procs
+ if not specific or specific == "procs" then
+ optionsAddon.optionsTable.args.spellFilter.args.listProcs.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Procs |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listProcs.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local procs = optionsAddon.optionsTable.args.spellFilter.args.listProcs.args.list.args
+ local updated = false
+
+ for name in pairs(x.db.profile.spellFilter.listProcs) do
+ -- TODO localization for name
+ updated = true
+ procs[name] = {
+ name = name,
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ end
+
+ if not updated then
+ procs.noSpells = {
+ name = L["No procs have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ -- Update spells
+ if not specific or specific == "spells" then
+ optionsAddon.optionsTable.args.spellFilter.args.listSpells.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Spells |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listSpells.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local spells = optionsAddon.optionsTable.args.spellFilter.args.listSpells.args.list.args
+ local updated = false
+
+ for id in pairs(x.db.profile.spellFilter.listSpells) do
+ local spellID = tonumber(string.match(id, "%d+"))
+ if spellID then
+ local spellName = C_Spell.GetSpellName(spellID)
+ if spellName then
+ local texture = C_Spell.GetSpellTexture(spellID) or x.BLANK_ICON
+ local spellDesc = C_Spell.GetSpellDescription(spellID)
+ updated = true
+ spells[tostring(spellID)] = {
+ name = x:FormatIcon(texture, 16) .. " " .. spellName,
+ desc = string.format(
+ "%s\n\n|cffFF0000%s|r |cff798BDD%s|r",
+ spellDesc,
+ L["ID"],
+ spellID
+ ),
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ else
+ x:Print("Removing deleted spell", id, "from the outgoing spell filter.")
+ x.db.profile.spellFilter.listSpells[id] = nil
+ end
+ else
+ x:Print("Removing deleted spell", id, "from the outgoing spell filter.")
+ x.db.profile.spellFilter.listSpells[id] = nil
+ end
+ end
+
+ if not updated then
+ spells.noSpells = {
+ name = L["No spells have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ -- Update items
+ if not specific or specific == "items" then
+ optionsAddon.optionsTable.args.spellFilter.args.listItems.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Items |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listItems.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local spells = optionsAddon.optionsTable.args.spellFilter.args.listItems.args.list.args
+ local updated = false
+
+ for id in pairs(x.db.profile.spellFilter.listItems) do
+ local itemID = tonumber(string.match(id, "%d+"))
+ if itemID then
+ local name = C_Item.GetItemNameByID(itemID)
+ if name then
+ local texture = C_Item.GetItemIconByID(itemID) or x.BLANK_ICON
+ updated = true
+ spells[id] = {
+ name = x:FormatIcon(texture, 16) .. " " .. name,
+ desc = string.format(
+ "|cffFF0000%s|r |cff798BDD%s|r",
+ L["ID"],
+ id
+ ),
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ else
+ x:Print("Removing deleted item", id, "from the spell filter.")
+ x.db.profile.spellFilter.listItems[id] = nil
+ end
+ else
+ x:Print("Removing deleted item", id, "from the spell filter.")
+ x.db.profile.spellFilter.listItems[id] = nil
+ end
+ end
+
+ if not updated then
+ spells.noSpells = {
+ name = L["No items have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ if not specific or specific == "damage" then
+ optionsAddon.optionsTable.args.spellFilter.args.listDamage.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Incoming Damage |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listDamage.args.list = {
+ order = 101,
+ name = "",
+ type = "group",
+ guiInline = true,
+ args = {},
+ }
+
+ local spells = optionsAddon.optionsTable.args.spellFilter.args.listDamage.args.list.args
+ local updated = false
+
+ for id in pairs(x.db.profile.spellFilter.listDamage) do
+ local spellID = tonumber(string.match(id, "%d+"))
+ if spellID then
+ local spellName = C_Spell.GetSpellName(spellID)
+ if spellName then
+ local texture = C_Spell.GetSpellTexture(spellID) or x.BLANK_ICON
+ local spellDesc = C_Spell.GetSpellDescription(spellID)
+ updated = true
+ spells[tostring(spellID)] = {
+ name = x:FormatIcon(texture, 16) .. " " .. spellName,
+ desc = string.format(
+ "%s\n\n|cffFF0000%s|r |cff798BDD%s|r",
+ spellDesc,
+ L["ID"],
+ spellID
+ ),
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ else
+ x:Print("Removing deleted spell", id, "from the incoming damage spell filter.")
+ x.db.profile.spellFilter.listDamage[id] = nil
+ end
+ else
+ x:Print("Removing deleted spell", id, "from the incoming damage spell filter.")
+ x.db.profile.spellFilter.listDamage[id] = nil
+ end
+ end
+
+ if not updated then
+ spells.noSpells = {
+ name = L["No spells have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+
+ if not specific or specific == "healing" then
+ optionsAddon.optionsTable.args.spellFilter.args.listHealing.args.headerFilterList = {
+ order = 100,
+ name = L["Filtered Incoming Healing |cff798BDD(Uncheck to disable)|r"],
+ type = "header",
+ }
+ optionsAddon.optionsTable.args.spellFilter.args.listHealing.args.list = {
+ name = "",
+ type = "group",
+ guiInline = true,
+ order = 101,
+ args = {},
+ }
+
+ local spells = optionsAddon.optionsTable.args.spellFilter.args.listHealing.args.list.args
+ local updated = false
+
+ for id in pairs(x.db.profile.spellFilter.listHealing) do
+ local spellID = tonumber(string.match(id, "%d+"))
+ if spellID then
+ local spellName = C_Spell.GetSpellName(spellID)
+ if spellName then
+ local texture = C_Spell.GetSpellTexture(spellID) or x.BLANK_ICON
+ local spellDesc = C_Spell.GetSpellDescription(spellID)
+ updated = true
+ spells[tostring(spellID)] = {
+ name = x:FormatIcon(texture, 16) .. " " .. spellName,
+ desc = string.format(
+ "%s\n\n|cffFF0000%s|r |cff798BDD%s|r",
+ spellDesc,
+ L["ID"],
+ spellID
+ ),
+ type = "toggle",
+ get = isSpellFiltered,
+ set = setIsSpellFiltered,
+ }
+ else
+ x:Print("Removing deleted spell", id, "from the incoming healing spell filter.")
+ x.db.profile.spellFilter.listHealing[id] = nil
+ end
+ else
+ x:Print("Removing deleted spell", id, "from the incoming healing spell filter.")
+ x.db.profile.spellFilter.listHealing[id] = nil
+ end
+ end
+
+ if not updated then
+ spells.noSpells = {
+ name = L["No spells have been added to this list yet."],
+ type = "description",
+ }
+ end
+ end
+end
+
+local isColorOverrideEnabled = function(info)
+ local colorName = string.match(info[#info], "(.*)_enabled")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ return x.db.profile[category][colorName].enabled
+end
+
+local setColorOverrideEnabled = function(info, enabled)
+ local colorName = string.match(info[#info], "(.*)_enabled")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ x.db.profile[category][colorName].enabled = enabled
+end
+
+local getColorOverride = function(info)
+ local colorName = string.match(info[#info], "(.*)_color")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ return unpack(x.db.profile[category][colorName].color or x.db.profile[category][colorName].default)
+end
+
+local setColorOverride = function(info, r, g, b)
+ local colorName = string.match(info[#info], "(.*)_color")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ x.db.profile[category][colorName].color = { r, g, b }
+end
+
+local resetColorOverride = function(info)
+ local colorName = string.match(info[#info], "(.*)_reset")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ x.db.profile[category][colorName].color = nil
+end
+
+local isColorPickerHidden = function(info)
+ local colorName = string.match(info[#info], "(.*)_color") or string.match(info[#info], "(.*)_reset")
+ local category = info[#info - 1]
+ if category ~= "SpellColors" then
+ category = "Colors"
+ end
+ return not x.db.profile[category][colorName].enabled
+end
+
+local function GenerateColorOptionsTable_Entry(colorName, colorSettings, options, index)
+ -- Check for nil colors and set them to the default
+ if not colorSettings.color or not unpack(colorSettings.color) then
+ -- This needs to be a new table apparently
+ colorSettings.color = { unpack(colorSettings.default) }
+ end
+
+ options[colorName .. "_enabled"] = {
+ order = index,
+ type = "toggle",
+ -- TODO localization for colorSettings.desc
+ name = colorSettings.desc,
+ get = isColorOverrideEnabled,
+ set = setColorOverrideEnabled,
+ desc = string.format(
+ L["Enable a custom color for |cff798BDD%s|r."],
+ colorSettings.desc
+ ),
+ }
+ options[colorName .. "_color"] = {
+ order = index + 1,
+ type = "color",
+ name = L["Color"],
+ get = getColorOverride,
+ set = setColorOverride,
+ desc = string.format(
+ L["Change the color for |cff798BDD%s|r."],
+ colorSettings.desc
+ ),
+ hidden = isColorPickerHidden,
+ }
+ options[colorName .. "_reset"] = {
+ order = index + 2,
+ type = "execute",
+ name = L["Reset"],
+ width = "half",
+ func = resetColorOverride,
+ desc = string.format(
+ L["Resets |cff798BDD%s|r back to the default color."],
+ colorSettings.desc
+ ),
+ hidden = isColorPickerHidden,
+ }
+ options["spacer" .. index] = {
+ order = index + 3,
+ type = "description",
+ fontSize = "small",
+ width = "full",
+ name = "",
+ }
+end
+
+-- Generate Colors for each Frame
+function xo:GenerateColorOptions()
+ local orders = {}
+ local parents = {
+ general = "general",
+ outgoing_damage = "outgoing",
+ outgoing_healing = "outgoing_healing",
+ outgoing_criticals = "critical",
+ incoming_damage = "damage",
+ incoming_healing = "healing",
+ class_power = "power",
+ procs = "procs",
+ loot = "loot",
+ }
+
+ for colorName, colorSettings in pairs(x.db.profile.Colors) do
+ if not orders[colorSettings.category] then
+ orders[colorSettings.category] = 10
+ end
+
+ if colorSettings.desc then
+ if parents[colorSettings.category] then
+ GenerateColorOptionsTable_Entry(
+ colorName,
+ colorSettings,
+ optionsAddon.optionsTable.args.Frames.args[parents[colorSettings.category]].args.fontColors.args,
+ orders[colorSettings.category] + 1
+ )
+ orders[colorSettings.category] = orders[colorSettings.category] + 5
+ else
+ self:Print("Unknown color category", colorSettings.category, "for color", colorName)
+ end
+ end
+ end
+end
+
+function xo:GenerateSpellSchoolColors()
+ local options = optionsAddon.optionsTable.args.SpellColors.args
+ local settings = x.db.profile.SpellColors
+ local index = 10
+
+ local sortedList = {}
+ for n in pairs(settings) do
+ table.insert(sortedList, tonumber(n))
+ end
+
+ table.sort(sortedList)
+
+ local colorName
+ local colorSettings
+ for _, mask in ipairs(sortedList) do
+ colorName = tostring(mask)
+ colorSettings = settings[colorName]
+ GenerateColorOptionsTable_Entry(colorName, colorSettings, options, index)
+ index = index + 5
+ end
+end
+
+-- A helpful set of tips
+local tips = {
+ L["On the left list, under the |cffFFFF00Startup Message|r checkbox, you can click on the |cff798BDD+ Buttons|r (plus) to show more options."],
+ L["If you want to |cff798BDDCombine Frame Outputs|r, disable one of the frames and use the |cffFFFF00Secondary Frame|r option on that frame."],
+ L["Only the |cffFFFF00General|r, |cffFF8000Outgoing|r, |cffFFFF00Outgoing (Crits)|r, |cffFF8000Incoming Damage|r and |cffFFFF00Healing|r, and |cffFF8000Class Power|r frames can be abbreviated."],
+ L["The |cffFFFF00Hide Config in Combat|r option was added to prevent |cffFFFF00xCT+|r from tainting your UI. It is highly recommended left enabled."],
+ L["|cffFFFF00xCT+|r has several different ways it will merge critical hits. You can check them out in the |cffFFFF00Spam Merger|r section."],
+ L["Each frame has a |cffFFFF00Misc|r section; select a frame and select the drop-down box to find it."],
+ L["If there is a certain |cff798BDDSpell|r, |cff798BDDBuff|r, or |cff798BDDDebuff|r that you don't want to see, consider adding it to a |cff798BDDFilter|r."],
+ L["You can change how |cffFFFF00xCT+|r shows you names in the |cffFFFF00Names|r section of most frames."],
+}
+
+local helpfulList = {}
+local function GetNextTip()
+ if #helpfulList == 0 then
+ local used = {}
+
+ local num
+ while #used ~= #tips do
+ num = random(1, #tips)
+ if not used[num] then
+ used[num] = true
+ table.insert(helpfulList, tips[num])
+ end
+ end
+ end
+
+ local currentItem = helpfulList[1]
+ table.remove(helpfulList, 1)
+
+ return currentItem
+end
+
+local helpfulLastUpdate = GetTime()
+function x:OnAddonConfigRefreshed()
+ if GetTime() - helpfulLastUpdate > 15 then
+ helpfulLastUpdate = GetTime()
+ optionsAddon.optionsTable.args.helpfulTip.name = GetNextTip()
+ x:RefreshConfig()
+ end
+end
+
+-- Force Config Page to refresh
+function x:RefreshConfig()
+ if LibStub("AceConfigDialog-3.0").OpenFrames[AddonName] then
+ LibStub("AceConfigRegistry-3.0"):NotifyChange(AddonName)
+ end
+end
diff --git a/xCT+Options/xCT+Options.toc b/xCT+Options/xCT+Options.toc
new file mode 100644
index 00000000..448b6637
--- /dev/null
+++ b/xCT+Options/xCT+Options.toc
@@ -0,0 +1,30 @@
+## Interface: 110205
+## Title: xCT+Options
+## Notes: Options for xCT+
+## Notes-ruRU: Варианты для xCT+
+## Notes-zhCN: xCT+ 的選項
+## Author: Dandruff-Stormreaver US, Feylynn-Antonidas EU
+## Version: @project-version@
+## LoadOnDemand: 1
+## Dependencies: xCT+
+## IconTexture: 982414
+## Category-enUS: Combat
+## Category-deDE: Kampf
+## Category-esES: Combate
+## Category-esMX: Combate
+## Category-frFR: Combat
+## Category-itIT: Combattimento
+## Category-koKR: 전투
+## Category-ptBR: Combate
+## Category-ruRU: Бой
+## Category-zhCN: 战斗
+## Category-zhTW: 戰鬥
+
+# Locales first
+# enUS first (because its the default!)
+locales\enUS.lua
+locales\zhCN.lua
+locales\ruRU.lua
+
+init.lua
+options_table.lua
diff --git a/xCT.lua b/xCT.lua
deleted file mode 100644
index c449d26c..00000000
--- a/xCT.lua
+++ /dev/null
@@ -1,1148 +0,0 @@
---[[
-
-xCT by affli @ RU-Howling Fjord
-All rights reserved.
-Thanks ALZA and Shestak for making this mod possible. Thanks Tukz for his wonderful style of coding. Thanks Rostok for some fixes and healing code.
-
-]]--
-
-
---some init
-local addon, ns=...
-ct=ns.config
-ct.myname, _ = UnitName("player")
-ct.myclass=select(2,UnitClass("player"))
----------------------------------------------------------------------------------
--- outgoing healing filter, hide this spammy shit, plx
-if(ct.healing)then
- ct.healfilter={}
- -- See class-specific config for filtered spells.
-end
----------------------------------------------------------------------------------
-if(ct.mergeaoespam)then
- ct.aoespam={}
- -- See class-specific config for merged spells.
-end
----------------------------------------------------------------------------------
--- class config, overrides general
-if ct.myclass=="WARLOCK" then
- if(ct.mergeaoespam)then
- ct.aoespam[27243]=true -- Seed of Corruption (DoT)
- ct.aoespam[27285]=true -- Seed of Corruption (Explosion)
- ct.aoespam[87385]=true -- Seed of Corruption (Explosion Soulburned)
- ct.aoespam[172]=true -- Corruption
- ct.aoespam[87389]=true -- Corruption (Soulburn: Seed of Corruption)
- ct.aoespam[30108]=true -- Unstable Affliction
- ct.aoespam[348]=true -- Immolate
- ct.aoespam[980]=true -- Bane of Agony
- ct.aoespam[85455]=true -- Bane of Havoc
- ct.aoespam[85421]=true -- Burning Embers
- ct.aoespam[42223]=true -- Rain of Fire
- ct.aoespam[5857]=true -- Hellfire Effect
- ct.aoespam[47897]=true -- Shadowflame (shadow direct damage)
- ct.aoespam[47960]=true -- Shadowflame (fire dot)
- ct.aoespam[50590]=true -- Immolation Aura
- ct.aoespam[30213]=true -- Legion Strike (Felguard)
- ct.aoespam[89753]=true -- Felstorm (Felguard)
- ct.aoespam[20153]=true -- Immolation (Infrenal)
- end
- if(ct.healing)then
- ct.healfilter[96379]=true -- Fel Armor
- ct.healfilter[63106]=true -- Siphon Life
- ct.healfilter[54181]=true -- Fel Synergy
- ct.healfilter[89653]=true -- Drain Life
- ct.healfilter[79268]=true -- Soul Harvest
- ct.healfilter[30294]=true -- Soul Leech
- end
-elseif ct.myclass=="DRUID"then
- if(ct.mergeaoespam)then
- -- Healer spells
- ct.aoespam[774]=true -- Rejuvenation (Normal)
- ct.aoespam[64801]=true -- Rejuvenation (First tick)
- ct.aoespam[48438]=true -- Wild Growth
- ct.aoespam[8936]=true -- Regrowth
- ct.aoespam[33763]=true -- Lifebloom
- ct.aoespam[44203]=true -- Tranquility
- ct.aoespam[81269]=true -- Efflorescence
- -- Damager spells
- ct.aoespam[8921]=true -- Moonfire
- ct.aoespam[93402]=true -- Sunfire
- ct.aoespam[5570]=true -- Insect Swarm
- ct.aoespam[42231]=true -- Hurricane
- ct.aoespam[50288]=true -- Starfall
- ct.aoespam[78777]=true -- Wild Mushroom
- ct.aoespam[61391]=true -- Typhoon
- ct.aoespam[1822]=true -- Rake
- ct.aoespam[62078]=true -- Swipe (Cat Form)
- ct.aoespam[779]=true -- Swipe (Bear Form)
- ct.aoespam[33745]=true -- Lacerate
- ct.aoespam[1079]=true -- Rip
-
-
- end
-elseif ct.myclass=="PALADIN"then
- if(ct.mergeaoespam)then
- ct.aoespam[81297]=true -- Consecration
- ct.aoespam[2812]=true -- Holy Wrath
- ct.aoespam[53385]=true -- Divine Storm
- ct.aoespam[31803]=true -- Censure
- ct.aoespam[20424]=true -- Seals of Command
- ct.aoespam[42463]=true -- Seal of Truth
- ct.aoespam[25742]=true -- Seal of Righteousness
- ct.aoespam[20167]=true -- Seal of Insight (Heal Effect)
- ct.aoespam[88263]=true -- Hammer of the Righteous
- ct.aoespam[31935]=true -- Avenger's Shield
- ct.aoespam[94289]=true -- Protector of the Innocent
- ct.aoespam[53652]=true -- Beacon of Light
- ct.aoespam[85222]=true -- Light of Dawn
- end
-elseif ct.myclass=="PRIEST"then
- if(ct.mergeaoespam)then
- -- Healer spells
- ct.aoespam[47750]=true -- Penance (Heal Effect)
- ct.aoespam[139]=true -- Renew
- ct.aoespam[596]=true -- Prayer of Healing
- ct.aoespam[56161]=true -- Glyph of Prayer of Healing
- ct.aoespam[64844]=true -- Divine Hymn
- ct.aoespam[32546]=true -- Binding Heal
- ct.aoespam[77489]=true -- Echo of Light
- ct.aoespam[34861]=true -- Circle of Healing
- ct.aoespam[23455]=true -- Holy Nova (Healing Effect)
- ct.aoespam[33110]=true -- Prayer of Mending
- ct.aoespam[63544]=true -- Divine Touch
- -- Damager spells
- ct.aoespam[47666]=true -- Penance (Damage Effect)
- ct.aoespam[15237]=true -- Holy Nova (Damage Effect)
- ct.aoespam[589]=true -- Shadow Word: Pain
- ct.aoespam[34914]=true -- Vampiric Touch
- ct.aoespam[2944]=true -- Devouring Plague
- ct.aoespam[63675]=true -- Improved Devouring Plague
- ct.aoespam[15407]=true -- Mind Flay
- ct.aoespam[49821]=true -- Mind Seer
- ct.aoespam[87532]=true -- Shadowy Apparition
-
- end
- if(ct.healing)then
- ct.healfilter[2944]=true -- Devouring Plague (Healing)
- ct.healfilter[15290]=true -- Vampiric Embrace
- end
-elseif ct.myclass=="SHAMAN"then
- if(ct.mergeaoespam)then
- ct.aoespam[421]=true -- Chain Lightning
- ct.aoespam[8349]=true -- Fire Nova
- ct.aoespam[77478]=true -- Earhquake
- ct.aoespam[51490]=true -- Thunderstorm
- ct.aoespam[8187]=true -- Magma Totem
- end
- if(ct.healing)then
- ct.aoespam[73921]=true -- Healing Rain
- ct.aoespam[5394]=true -- Healing Stream Totem
- ct.aoespam[1064]=true -- Chain Heal
- end
-elseif ct.myclass=="MAGE"then
- if(ct.mergeaoespam)then
- ct.aoespam[44461]=true -- Living Bomb Explosion
- ct.aoespam[44457]=true -- Living Bomb Dot
- ct.aoespam[2120]=true -- Flamestrike
- ct.aoespam[12654]=true -- Ignite
- ct.aoespam[11366]=true -- Pyroblast
- ct.aoespam[31661]=true -- Dragon's Breath
- ct.aoespam[42208]=true -- Blizzard
- ct.aoespam[122]=true -- Frost Nova
- ct.aoespam[1449]=true -- Arcane Explosion
- end
-elseif ct.myclass=="WARRIOR"then
- if(ct.mergeaoespam)then
- ct.aoespam[845]=true -- Cleave
- ct.aoespam[46968]=true -- Shockwave
- ct.aoespam[6343]=true -- Thunder Clap
- ct.aoespam[1680]=true -- Whirlwind
- ct.aoespam[94009]=true -- Rend
- ct.aoespam[12721]=true -- Deep Wounds
-
- end
- if(ct.healing)then
- ct.healfilter[23880]=true -- Bloodthirst
- ct.healfilter[55694]=true -- Enraged Regeneration
- end
-elseif ct.myclass=="HUNTER"then
- if(ct.mergeaoespam)then
- ct.aoespam[2643]=true -- Multi-Shot
- ct.aoespam[83077]=true -- instant part of Serpent Sting
- ct.aoespam[88466]=true -- Serpent Sting#1
- ct.aoespam[1978]=true -- Serpent Sting#2
- ct.aoespam[13812]=true -- Explosive Trap
-
- end
-elseif ct.myclass=="DEATHKNIGHT"then
- if(ct.mergeaoespam)then
- ct.aoespam[55095]=true -- Frost Fever
- ct.aoespam[55078]=true -- Blood Plague
- ct.aoespam[55536]=true -- Unholy Blight
- ct.aoespam[48721]=true -- Blood Boil
- ct.aoespam[49184]=true -- Howling Blast
- ct.aoespam[52212]=true -- Death and Decay
- end
-elseif ct.myclass=="ROGUE"then
- if(ct.mergeaoespam)then
- ct.aoespam[51723]=true -- Fan of Knives
- ct.aoespam[2818]=true -- Deadly Poison
- ct.aoespam[8680]=true -- Instant Poison
- end
-
-end
----------------------------------------------------------------------------------
--- character config, overrides general and class
-if ct.myname == "Affli" then
- ct["treshold"] = 500
-end
----------------------------------------------------------------------------------
-
---do not edit below unless you know what you are doing
-
-local numf
-if(ct.damage or ct.healing)then
- numf=4
-else
- numf=3
-end
--- detect vechile
-local function SetUnit()
- if(UnitHasVehicleUI("player"))then
- ct.unit="vehicle"
- else
- ct.unit="player"
- end
- CombatTextSetActiveUnit(ct.unit)
-end
-
---limit lines
-local function LimitLines()
- for i=1,#ct.frames do
- f=ct.frames[i]
- f:SetMaxLines(f:GetHeight()/ct.fontsize)
- end
-end
-
--- scrollable frames
-local function SetScroll()
- for i=1,#ct.frames do
- ct.frames[i]:EnableMouseWheel(true)
- ct.frames[i]:SetScript("OnMouseWheel", function(self,delta)
- if delta >0 then
- self:ScrollUp()
- elseif delta <0 then
- self:ScrollDown()
- end
- end)
- end
-end
-
--- msg flow direction
-local function ScrollDirection()
- if (COMBAT_TEXT_FLOAT_MODE=="2") then
- ct.mode="TOP"
- else
- ct.mode="BOTTOM"
- end
- for i=1,#ct.frames do
- ct.frames[i]:Clear()
- ct.frames[i]:SetInsertMode(ct.mode)
- end
-end
--- partial resists styler
-local part="-%s (%s %s)"
-local r,g,b
--- the function, handles everything
-local function OnEvent(self,event,subevent,...)
-if(event=="COMBAT_TEXT_UPDATE")then
- local arg2,arg3 = ...
- if (SHOW_COMBAT_TEXT=="0")then
- return
- else
- if subevent=="DAMAGE"then
- xCT1:AddMessage("-"..arg2,.75,.1,.1)
- elseif subevent=="DAMAGE_CRIT"then
- xCT1:AddMessage(ct.critprefix.."-"..arg2..ct.critpostfix,1,.1,.1)
- elseif subevent=="SPELL_DAMAGE"then
- xCT1:AddMessage("-"..arg2,.75,.3,.85)
- elseif subevent=="SPELL_DAMAGE_CRIT"then
- xCT1:AddMessage(ct.critprefix.."-"..arg2..ct.critpostfix,1,.3,.5)
-
- elseif subevent=="HEAL"then
- if(arg3>=ct.healtreshold)then
- if(arg2)then
- if(COMBAT_TEXT_SHOW_FRIENDLY_NAMES=="1")then
- xCT2:AddMessage(arg2.." +"..arg3,.1,.75,.1)
- else
- xCT2:AddMessage("+"..arg3,.1,.75,.1)
- end
- end
- end
- elseif subevent=="HEAL_CRIT"then
- if(arg3>=ct.healtreshold)then
- if(arg2)then
- if(COMBAT_TEXT_SHOW_FRIENDLY_NAMES=="1")then
- xCT2:AddMessage(arg2.." +"..arg3,.1,1,.1)
- else
- xCT2:AddMessage("+"..arg3,.1,1,.1)
- end
- end
- end
- elseif subevent=="PERIODIC_HEAL"then
- if(arg3>=ct.healtreshold)then
- xCT2:AddMessage("+"..arg3,.1,.5,.1)
- end
-
- elseif subevent=="SPELL_CAST"then
- xCT3:AddMessage(arg2,1,.82,0)
-
-
- elseif subevent=="MISS"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(MISS,.5,.5,.5)
- elseif subevent=="DODGE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(DODGE,.5,.5,.5)
- elseif subevent=="PARRY"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(PARRY,.5,.5,.5)
- elseif subevent=="EVADE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(EVADE,.5,.5,.5)
- elseif subevent=="IMMUNE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(IMMUNE,.5,.5,.5)
- elseif subevent=="DEFLECT"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(DEFLECT,.5,.5,.5)
- elseif subevent=="REFLECT"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(REFLECT,.5,.5,.5)
- elseif subevent=="SPELL_MISS"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(MISS,.5,.5,.5)
- elseif subevent=="SPELL_DODGE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(DODGE,.5,.5,.5)
- elseif subevent=="SPELL_PARRY"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(PARRY,.5,.5,.5)
- elseif subevent=="SPELL_EVADE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(EVADE,.5,.5,.5)
- elseif subevent=="SPELL_IMMUNE"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(IMMUNE,.5,.5,.5)
- elseif subevent=="SPELL_DEFLECT"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(DEFLECT,.5,.5,.5)
- elseif subevent=="SPELL_REFLECT"and(COMBAT_TEXT_SHOW_DODGE_PARRY_MISS=="1")then
- xCT1:AddMessage(REFLECT,.5,.5,.5)
-
- elseif subevent=="RESIST"then
- if(arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1") then
- xCT1:AddMessage(part:format(arg2,RESIST,arg3),.75,.5,.5)
- else
- xCT1:AddMessage(arg2,.75,.1,.1)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(RESIST,.5,.5,.5)
- end
- elseif subevent=="BLOCK"then
- if(arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(part:format(arg2,BLOCK,arg3),.75,.5,.5)
- else
- xCT1:AddMessage(arg2,.75,.1,.1)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(BLOCK,.5,.5,.5)
- end
- elseif subevent=="ABSORB"then
- if(arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(part:format(arg2,ABSORB,arg3),.75,.5,.5)
- else
- xCT1:AddMessage(arg2,.75,.1,.1)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(ABSORB,.5,.5,.5)
- end
- elseif subevent=="SPELL_RESIST"then
- if(arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1") then
- xCT1:AddMessage(part:format(arg2,RESIST,arg3),.5,.3,.5)
- else
- xCT1:AddMessage(arg2,.75,.3,.85)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(RESIST,.5,.5,.5)
- end
- elseif subevent=="SPELL_BLOCK"then
- if (arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(part:format(arg2,BLOCK,arg3),.5,.3,.5)
- else
- xCT1:AddMessage("-"..arg2,.75,.3,.85)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(BLOCK,.5,.5,.5)
- end
- elseif subevent=="SPELL_ABSORB"then
- if(arg3)then
- if(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(part:format(arg2,ABSORB,arg3),.5,.3,.5)
- else
- xCT1:AddMessage(arg2,.75,.3,.85)
- end
- elseif(COMBAT_TEXT_SHOW_RESISTANCES=="1")then
- xCT1:AddMessage(ABSORB,.5,.5,.5)
- end
-
- elseif subevent=="ENERGIZE"and(COMBAT_TEXT_SHOW_ENERGIZE=="1")then
- if tonumber(arg2)>0 then
- if(arg3 and arg3=="MANA" or arg3=="RAGE" or arg3=="FOCUS" or arg3=="ENERGY" or arg3=="RUINIC_POWER" or arg3=="SOUL_SHARDS")then
- xCT3:AddMessage("+"..arg2.." ".._G[arg3],PowerBarColor[arg3].r,PowerBarColor[arg3].g,PowerBarColor[arg3].b)
- end
- end
-
- elseif subevent=="PERIODIC_ENERGIZE"and(COMBAT_TEXT_SHOW_PERIODIC_ENERGIZE=="1")then
- if tonumber(arg2)>0 then
- if(arg3 and arg3=="MANA" or arg3=="RAGE" or arg3=="FOCUS" or arg3=="ENERGY" or arg3=="RUINIC_POWER" or arg3=="SOUL_SHARDS")then
- xCT3:AddMessage("+"..arg2.." ".._G[arg3],PowerBarColor[arg3].r,PowerBarColor[arg3].g,PowerBarColor[arg3].b)
- end
- end
- elseif subevent=="SPELL_AURA_START"and(COMBAT_TEXT_SHOW_AURAS=="1")then
- xCT3:AddMessage("+"..arg2,1,.5,.5)
- elseif subevent=="SPELL_AURA_END"and(COMBAT_TEXT_SHOW_AURAS=="1")then
- xCT3:AddMessage("-"..arg2,.5,.5,.5)
- elseif subevent=="SPELL_AURA_START_HARMFUL"and(COMBAT_TEXT_SHOW_AURAS=="1")then
- xCT3:AddMessage("+"..arg2,1,.1,.1)
- elseif subevent=="SPELL_AURA_END_HARMFUL"and(COMBAT_TEXT_SHOW_AURAS=="1")then
- xCT3:AddMessage("-"..arg2,.1,1,.1)
-
- elseif subevent=="HONOR_GAINED"and(COMBAT_TEXT_SHOW_HONOR_GAINED=="1")then
- arg2=tonumber(arg2)
- if(arg2 and abs(arg2)>1) then
- arg2=floor(arg2)
- if (arg2>0)then
- xCT3:AddMessage(HONOR.." +"..arg2,.1,.1,1)
- end
- end
-
- elseif subevent=="FACTION"and(COMBAT_TEXT_SHOW_REPUTATION=="1")then
- xCT3:AddMessage(arg2.." +"..arg3,.1,.1,1)
-
- elseif subevent=="SPELL_ACTIVE"and(COMBAT_TEXT_SHOW_REACTIVES=="1")then
- xCT3:AddMessage(arg2,1,.82,0)
- end
-end
-
-elseif event=="UNIT_HEALTH"and(COMBAT_TEXT_SHOW_LOW_HEALTH_MANA=="1")then
- if subevent==ct.unit then
- if(UnitHealth(ct.unit)/UnitHealthMax(ct.unit)<=COMBAT_TEXT_LOW_HEALTH_THRESHOLD)then
- if (not lowHealth) then
- xCT3:AddMessage(HEALTH_LOW,1,.1,.1)
- lowHealth=true
- end
- else
- lowHealth=nil
- end
- end
-
-elseif event=="UNIT_MANA"and(COMBAT_TEXT_SHOW_LOW_HEALTH_MANA=="1")then
- if subevent==ct.unit then
- local _,powerToken=UnitPowerType(ct.unit)
- if (powerToken=="MANA"and(UnitPower(ct.unit)/UnitPowerMax(ct.unit))<=COMBAT_TEXT_LOW_MANA_THRESHOLD)then
- if (not lowMana)then
- xCT3:AddMessage(MANA_LOW,1,.1,.1)
- lowMana=true
- end
- else
- lowMana=nil
- end
- end
-
-elseif event=="PLAYER_REGEN_ENABLED"and(COMBAT_TEXT_SHOW_COMBAT_STATE=="1")then
- xCT3:AddMessage("-"..LEAVING_COMBAT,.1,1,.1)
-
-elseif event=="PLAYER_REGEN_DISABLED"and(COMBAT_TEXT_SHOW_COMBAT_STATE=="1")then
- xCT3:AddMessage("+"..ENTERING_COMBAT,1,.1,.1)
-
-elseif event=="UNIT_COMBO_POINTS"and(COMBAT_TEXT_SHOW_COMBO_POINTS=="1")then
- if(subevent==ct.unit)then
- local cp=GetComboPoints(ct.unit,"target")
- if(cp>0)then
- r,g,b=1,.82,.0
- if (cp==MAX_COMBO_POINTS)then
- r,g,b=0,.82,1
- end
- xCT3:AddMessage(format(COMBAT_TEXT_COMBO_POINTS,cp),r,g,b)
- end
- end
-
-elseif event=="RUNE_POWER_UPDATE"then
- local arg1,arg2 = subevent,...
- if(arg2==true)then
- local rune=GetRuneType(arg1);
- local msg=COMBAT_TEXT_RUNE[rune];
- if(rune==1)then
- r=.75
- g=0
- b=0
- elseif(rune==2)then
- r=.75
- g=1
- b=0
- elseif(rune==3)then
- r=0
- g=1
- b=1
- end
- if(rune and rune<4)then
- xCT3:AddMessage("+"..msg,r,g,b)
- end
- end
-
-elseif event=="UNIT_ENTERED_VEHICLE"or event=="UNIT_EXITING_VEHICLE"then
- if(arg1=="player")then
- SetUnit()
- end
-
-elseif event=="PLAYER_ENTERING_WORLD"then
- SetUnit()
-
--- ScrollDirection()
-
-
- if(ct.scrollable)then
- SetScroll()
- else
- LimitLines()
- end
-
- if(ct.damage or ct.healing)then
- ct.pguid=UnitGUID"player"
- end
-end
-end
-
--- change damage font (if desired)
-if(ct.damagestyle)then
- DAMAGE_TEXT_FONT=ct.damagefont
-end
-
--- the frames
-ct.locked=true
-ct.frames={}
-for i=1,numf do
- local f=CreateFrame("ScrollingMessageFrame","xCT"..i,UIParent)
- f:SetFont(ct.font,ct.fontsize,ct.fontstyle)
- f:SetShadowColor(0,0,0,0)
- f:SetFading(true)
- f:SetFadeDuration(0.5)
- f:SetTimeVisible(ct.timevisible)
- f:SetMaxLines(ct.maxlines)
- f:SetSpacing(2)
- f:SetWidth(128)
- f:SetHeight(128)
- f:SetPoint("CENTER",0,0)
- f:SetMovable(true)
- f:SetResizable(true)
- f:SetMinResize(64,64)
- f:SetMaxResize(768,768)
- f:SetClampedToScreen(true)
- f:SetClampRectInsets(0,0,ct.fontsize,0)
- if(i==1)then
- f:SetJustifyH(ct.justify_1)
- f:SetPoint("CENTER",-192,-32)
- elseif(i==2)then
- f:SetJustifyH(ct.justify_2)
- f:SetPoint("CENTER",192,-32)
- elseif(i==3)then
- f:SetJustifyH(ct.justify_3)
- f:SetWidth(256)
- f:SetPoint("CENTER",0,192)
- else
- f:SetJustifyH(ct.justify_4)
- f:SetPoint("CENTER",320,0)
- local a,_,c=f:GetFont()
- if (ct.damagefontsize=="auto")then
- if ct.icons then
- f:SetFont(a,ct.iconsize/2,c)
- end
- elseif (type(ct.damagefontsize)=="number")then
- f:SetFont(a,ct.damagefontsize,c)
- end
-
- end
- ct.frames[i] = f
-end
-
--- register events
-local xCT=CreateFrame"Frame"
-xCT:RegisterEvent"COMBAT_TEXT_UPDATE"
-xCT:RegisterEvent"UNIT_HEALTH"
-xCT:RegisterEvent"UNIT_MANA"
-xCT:RegisterEvent"PLAYER_REGEN_DISABLED"
-xCT:RegisterEvent"PLAYER_REGEN_ENABLED"
-xCT:RegisterEvent"UNIT_COMBO_POINTS"
-if(ct.dkrunes and select(2,UnitClass"player")=="DEATHKNIGHT")then
- xCT:RegisterEvent"RUNE_POWER_UPDATE"
-end
-xCT:RegisterEvent"UNIT_ENTERED_VEHICLE"
-xCT:RegisterEvent"UNIT_EXITING_VEHICLE"
-xCT:RegisterEvent"PLAYER_ENTERING_WORLD"
-xCT:SetScript("OnEvent",OnEvent)
-
--- turn off blizz ct
-CombatText:UnregisterAllEvents()
-CombatText:SetScript("OnLoad",nil)
-CombatText:SetScript("OnEvent",nil)
-CombatText:SetScript("OnUpdate",nil)
-
--- steal external messages sent by other addons using CombatText_AddMessage
-Blizzard_CombatText_AddMessage=CombatText_AddMessage
-function CombatText_AddMessage(message,scrollFunction,r,g,b,displayType,isStaggered)
- xCT3:AddMessage(message,r,g,b)
-end
-
--- force hide blizz damage/healing, if desired
-if not(ct.blizzheadnumbers==true)then
- InterfaceOptionsCombatTextPanelTargetDamage:Hide()
- InterfaceOptionsCombatTextPanelPeriodicDamage:Hide()
- InterfaceOptionsCombatTextPanelPetDamage:Hide()
- InterfaceOptionsCombatTextPanelHealing:Hide()
- SetCVar("CombatLogPeriodicSpells",0)
- SetCVar("PetMeleeDamage",0)
- SetCVar("CombatDamage",0)
- SetCVar("CombatHealing",0)
-end
-
--- hook blizz float mode selector. blizz sucks, because changing cVar combatTextFloatMode doesn't fire CVAR_UPDATE
---hooksecurefunc("InterfaceOptionsCombatTextPanelFCTDropDown_OnClick",ScrollDirection)
---COMBAT_TEXT_SCROLL_ARC="" --may cause unexpected bugs, use with caution!
-InterfaceOptionsCombatTextPanelFCTDropDown:Hide() -- sorry, blizz fucking bug with SCM:SetInsertMode()
-
--- modify blizz ct options title lol
-InterfaceOptionsCombatTextPanelTitle:SetText(COMBAT_TEXT_LABEL.." (powered by |cffFF0000x|rCT)")
-
--- color printer
-local pr = function(msg)
- print("|cffFF0000x|rCT:", tostring(msg))
-end
-
--- awesome configmode and testmode
-local StartConfigmode=function()
- if not InCombatLockdown()then
- for i=1,#ct.frames do
- f=ct.frames[i]
- f:SetBackdrop({
- bgFile="Interface/Tooltips/UI-Tooltip-Background",
- edgeFile="Interface/Tooltips/UI-Tooltip-Border",
- tile=false,tileSize=0,edgeSize=2,
- insets={left=0,right=0,top=0,bottom=0}})
- f:SetBackdropColor(.1,.1,.1,.8)
- f:SetBackdropBorderColor(.1,.1,.1,.5)
-
- f.fs=f:CreateFontString(nil,"OVERLAY")
- f.fs:SetFont(ct.font,ct.fontsize,ct.fontstyle)
- f.fs:SetPoint("BOTTOM",f,"TOP",0,0)
- if(i==1)then
- f.fs:SetText(DAMAGE)
- f.fs:SetTextColor(1,.1,.1,.9)
- elseif(i==2)then
- f.fs:SetText(SHOW_COMBAT_HEALING)
- f.fs:SetTextColor(.1,1,.1,.9)
- elseif(i==3)then
- f.fs:SetText(COMBAT_TEXT_LABEL)
- f.fs:SetTextColor(.1,.1,1,.9)
- else
- f.fs:SetText(SCORE_DAMAGE_DONE.." / "..SCORE_HEALING_DONE)
- f.fs:SetTextColor(1,1,0,.9)
- end
-
- f.t=f:CreateTexture"ARTWORK"
- f.t:SetPoint("TOPLEFT",f,"TOPLEFT",1,-1)
- f.t:SetPoint("TOPRIGHT",f,"TOPRIGHT",-1,-19)
- f.t:SetHeight(20)
- f.t:SetTexture(.5,.5,.5)
- f.t:SetAlpha(.3)
-
- f.d=f:CreateTexture"ARTWORK"
- f.d:SetHeight(16)
- f.d:SetWidth(16)
- f.d:SetPoint("BOTTOMRIGHT",f,"BOTTOMRIGHT",-1,1)
- f.d:SetTexture(.5,.5,.5)
- f.d:SetAlpha(.3)
-
- f.tr=f:CreateTitleRegion()
- f.tr:SetPoint("TOPLEFT",f,"TOPLEFT",0,0)
- f.tr:SetPoint("TOPRIGHT",f,"TOPRIGHT",0,0)
- f.tr:SetHeight(20)
-
- f:EnableMouse(true)
- f:RegisterForDrag"LeftButton"
- f:SetScript("OnDragStart",f.StartSizing)
- if not(ct.scrollable)then
- f:SetScript("OnSizeChanged",function(self)
- self:SetMaxLines(self:GetHeight()/ct.fontsize)
- self:Clear()
- end)
- end
-
- f:SetScript("OnDragStop",f.StopMovingOrSizing)
- ct.locked=false
- end
- pr("unlocked.")
- else
- pr("can't be configured in combat.")
- end
-end
-
-local function EndConfigmode()
- for i=1,#ct.frames do
- f=ct.frames[i]
- f:SetBackdrop(nil)
- f.fs:Hide()
- f.fs=nil
- f.t:Hide()
- f.t=nil
- f.d:Hide()
- f.d=nil
- f.tr=nil
- f:EnableMouse(false)
- f:SetScript("OnDragStart",nil)
- f:SetScript("OnDragStop",nil)
- end
- ct.locked=true
- pr("Window positions unsaved, don't forget to reload UI.")
-end
-
-local function StartTestMode()
---init really random number generator.
- local random=math.random
- random(time());random(); random(time())
-
- local TimeSinceLastUpdate=0
- local UpdateInterval
- if(ct.damagecolor)then
- ct.dmindex={}
- ct.dmindex[1]=1
- ct.dmindex[2]=2
- ct.dmindex[3]=4
- ct.dmindex[4]=8
- ct.dmindex[5]=16
- ct.dmindex[6]=32
- ct.dmindex[7]=64
- end
-
-
- for i=1,#ct.frames do
- ct.frames[i]:SetScript("OnUpdate",function(self,elapsed)
- UpdateInterval=random(65,1000)/250
- TimeSinceLastUpdate=TimeSinceLastUpdate+elapsed
- if(TimeSinceLastUpdate>UpdateInterval)then
- if(i==1)then
- ct.frames[i]:AddMessage("-"..random(100000),1,random(255)/255,random(255)/255)
- elseif(i==2)then
- ct.frames[i]:AddMessage("+"..random(50000),.1,random(128,255)/255,.1)
- elseif(i==3)then
- ct.frames[i]:AddMessage(COMBAT_TEXT_LABEL,random(255)/255,random(255)/255,random(255)/255)
- elseif(i==4)then
- local msg
- local icon
- local color={}
- msg=random(40000)
- if(ct.icons)then
- _,_,icon=GetSpellInfo(msg)
- end
- if(icon)then
- msg=msg.." \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- if(ct.damagecolor)then
- color=ct.dmgcolor[ct.dmindex[random(#ct.dmindex)]]
- else
- color={1,1,0}
- end
- elseif(ct.damagecolor) and not(ct.icons)then
- color=ct.dmgcolor[ct.dmindex[random(#ct.dmindex)]]
- elseif not(ct.damagecolor)then
- color={1,1,random(0,1)}
- end
- ct.frames[i]:AddMessage(msg,unpack(color))
-
- end
- TimeSinceLastUpdate = 0
- end
- end)
- ct.testmode=true
-end
-end
-
-local function EndTestMode()
- for i=1,#ct.frames do
- ct.frames[i]:SetScript("OnUpdate",nil)
- ct.frames[i]:Clear()
- end
- if(ct.damagecolor)then
- ct.dmindex=nil
- end
- ct.testmode=false
- end
-
--- /xct lock popup dialog
-StaticPopupDialogs["XCT_LOCK"]={
- text="To save |cffFF0000x|rCT window positions you need to reload your UI.\n Click "..ACCEPT.." to reload UI.\nClick "..CANCEL.." to do it later.",
- button1=ACCEPT,
- button2=CANCEL,
- OnAccept=function() if not InCombatLockdown() then ReloadUI() else EndConfigmode() end end,
- OnCancel=EndConfigmode,
- timeout=0,
- whileDead=1,
- hideOnEscape=true,
- showAlert=true,
-}
-
--- slash commands
-SLASH_XCT1="/xct"
-SlashCmdList["XCT"]=function(input)
- input=string.lower(input)
- if(input=="unlock")then
- if (ct.locked)then
- StartConfigmode()
- else
- pr("already unlocked.")
- end
- elseif(input=="lock")then
- if (ct.locked) then
- pr("already locked.")
- else
- StaticPopup_Show("XCT_LOCK")
- end
- elseif(input=="test")then
- if (ct.testmode) then
- EndTestMode()
- pr("test mode disabled.")
- else
- StartTestMode()
- pr("test mode enabled.")
- end
- elseif(input=="mypos")then
- xCT1:ClearAllPoints()
- xCT1:SetPoint("CENTER",UIParent, "CENTER", -90, -8)
- xCT1:SetHeight(142)
- xCT1:SetWidth(128)
- xCT2:ClearAllPoints()
- xCT2:SetPoint("CENTER",UIParent, "CENTER", 90, -8)
- xCT2:SetHeight(142)
- xCT2:SetWidth(128)
- xCT3:ClearAllPoints()
- xCT3:SetPoint("TOP",UIParent, "TOP", -2, -34)
- xCT3:SetHeight(264)
- xCT3:SetWidth(216)
- xCT4:ClearAllPoints()
- xCT4:SetPoint("CENTER",UIParent, "CENTER", 444, 152)
- xCT4:SetHeight(172)
- xCT4:SetWidth(136)
- else
- pr("use |cffFF0000/xct unlock|r to move and resize frames.")
- pr("use |cffFF0000/xct lock|r to lock frames.")
- pr("use |cffFF0000/xct test|r to toggle testmode (sample xCT output).")
- end
-end
-
--- awesome shadow priest helper
-if(ct.stopvespam and ct.myclass=="PRIEST")then
- local sp=CreateFrame("Frame")
- sp:SetScript("OnEvent",function(...)
- if(GetShapeshiftForm()==1)then
- if(ct.blizzheadnumbers)then
- SetCVar('CombatHealing',0)
- end
- else
- if(ct.blizzheadnumbers)then
- SetCVar('CombatHealing',1)
- end
- end
- end)
- sp:RegisterEvent("PLAYER_ENTERING_WORLD")
- sp:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
- sp:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
-end
-
--- spam merger
-local SQ
-if(ct.mergeaoespam)then
- if (ct.damage or ct.healing) then
- if (not ct.mergeaoespamtime or ct.mergeaoespamtime<1) then
- ct.mergeaoespamtime=1
- end
- local pairs=pairs
- SQ={}
- for k,v in pairs(ct.aoespam) do
- SQ[k]={queue = 0, msg = "", color={}, count=0, utime=0, locked=false}
- end
- ct.SpamQueue=function(spellId, add)
- local amount
- local spam=SQ[spellId]["queue"]
- if (spam and type(spam=="number"))then
- amount=spam+add
- else
- amount=add
- end
- return amount
- end
- local tslu=0
- local xCTspam=CreateFrame"Frame"
- xCTspam:SetScript("OnUpdate", function(self, elapsed)
- local count
- tslu=tslu+elapsed
- if tslu > 0.5 then
- tslu=0
- local utime=time()
- for k,v in pairs(SQ) do
- if not SQ[k]["locked"] and SQ[k]["queue"]>0 and SQ[k]["utime"]+ct.mergeaoespamtime<=utime then
- if SQ[k]["count"]>1 then
- count=" |cffFFFFFF x "..SQ[k]["count"].."|r"
- else
- count=""
- end
- xCT4:AddMessage(SQ[k]["queue"]..SQ[k]["msg"]..count, unpack(SQ[k]["color"]))
- SQ[k]["queue"]=0
- SQ[k]["count"]=0
- end
- end
- end
- end)
- end
-end
-
--- damage
-if(ct.damage)then
- local unpack,select,time=unpack,select,time
- local gflags=bit.bor( COMBATLOG_OBJECT_AFFILIATION_MINE,
- COMBATLOG_OBJECT_REACTION_FRIENDLY,
- COMBATLOG_OBJECT_CONTROL_PLAYER,
- COMBATLOG_OBJECT_TYPE_GUARDIAN
- )
- local xCTd=CreateFrame"Frame"
- if(ct.damagecolor)then
- ct.dmgcolor={}
- ct.dmgcolor[1]={1,1,0} -- physical
- ct.dmgcolor[2]={1,.9,.5} -- holy
- ct.dmgcolor[4]={1,.5,0} -- fire
- ct.dmgcolor[8]={.3,1,.3} -- nature
- ct.dmgcolor[16]={.5,1,1} -- frost
- ct.dmgcolor[32]={.5,.5,1} -- shadow
- ct.dmgcolor[64]={1,.5,1} -- arcane
- end
-
- if(ct.icons)then
- ct.blank="Interface\\Addons\\xCT\\blank"
- end
-
- local dmg=function(self,event,...)
- local msg,icon
- local timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags = select(1,...)
- if(sourceGUID==ct.pguid and destGUID~=ct.pguid)or(sourceGUID==UnitGUID"pet" and ct.petdamage)or(sourceFlags==gflags)then
- if(eventType=="SWING_DAMAGE")then
- local amount,_,_,_,_,_,critical=select(9,...)
- if(amount>=ct.treshold)then
- msg=amount
- if (critical) then
- msg=ct.critprefix..msg..ct.critpostfix
- end
- if(ct.icons)then
- if(sourceGUID==UnitGUID"pet") or (sourceFlags==gflags)then
- icon=PET_ATTACK_TEXTURE
- else
- -- icon=GetSpellTexture(1, BOOKTYPE_SPELL)
- -- _,_,icon=GetSpellInfo(6603)
- icon=GetSpellTexture(6603)
- end
- msg=msg.." \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- end
-
- xCT4:AddMessage(msg)
- end
- elseif(eventType=="RANGE_DAMAGE")then
- local spellId,_,_,amount,_,_,_,_,_,critical=select(9,...)
- if(amount>=ct.treshold)then
- msg=amount
- if (critical) then
- msg=ct.critprefix..msg..ct.critpostfix
- end
- if(ct.icons)then
- --_,_,icon=GetSpellInfo(spellId)
- icon=GetSpellTexture(spellId)
- msg=msg.." \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- end
-
- xCT4:AddMessage(msg)
- end
-
- elseif(eventType=="SPELL_DAMAGE")or(eventType=="SPELL_PERIODIC_DAMAGE" and ct.dotdamage)then
- local spellId,_,spellSchool,amount,_,_,_,_,_,critical=select(9,...)
- if(amount>=ct.treshold)then
- local color={}
- local rawamount=amount
- if (critical) then
- amount=ct.critprefix..amount..ct.critpostfix
- end
-
- if(ct.icons)then
- -- _,_,icon=GetSpellInfo(spellId)
- icon=GetSpellTexture(spellId)
- end
- if(ct.damagecolor)then
- if(ct.dmgcolor[spellSchool])then
- color=ct.dmgcolor[spellSchool]
- else
- color=ct.dmgcolor[1]
- end
- else
- color={1,1,0}
- end
- if (icon) then
- msg=" \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- elseif(ct.icons)then
- msg=" \124T"..ct.blank..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- else
- msg=""
- end
- if ct.mergeaoespam and ct.aoespam[spellId] then
- SQ[spellId]["locked"]=true
- SQ[spellId]["queue"]=ct.SpamQueue(spellId, rawamount)
- SQ[spellId]["msg"]=msg
- SQ[spellId]["color"]=color
- SQ[spellId]["count"]=SQ[spellId]["count"]+1
- if SQ[spellId]["count"]==1 then
- SQ[spellId]["utime"]=time()
- -- SQ[spellId]["utime"]=timestamp -- cant use now, cause log timestamps differ from time() return value by 2+ seconds (CL is in the future)
- -- print("timestamp: "..timestamp)
- -- print("time():"..time())
- -- print(format(TEXT_MODE_A_STRING_TIMESTAMP, date(TEXT_MODE_A_STRING_TIMESTAMP, timestamp), "finalString"))
- end
- SQ[spellId]["locked"]=false
- return
- end
- xCT4:AddMessage(amount..""..msg,unpack(color))
- end
-
- elseif(eventType=="SWING_MISSED")then
- local missType,_=select(9,...)
- if(ct.icons)then
- if(sourceGUID==UnitGUID"pet") or (sourceFlags==gflags)then
- icon=PET_ATTACK_TEXTURE
- else
- -- icon=GetSpellTexture(1, BOOKTYPE_SPELL)
- -- _,_,icon=GetSpellInfo(6603)
- icon=GetSpellTexture(6603)
- end
- missType=missType.." \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- end
-
- xCT4:AddMessage(missType)
-
- elseif(eventType=="SPELL_MISSED")or(eventType=="RANGE_MISSED")then
- local spellId,_,_,missType,_ = select(9,...)
- if(ct.icons)then
- -- _,_,icon=GetSpellInfo(spellId)
- icon=GetSpellTexture(spellId)
- missType=missType.." \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- end
- xCT4:AddMessage(missType)
-
- elseif(eventType=="SPELL_DISPEL")and ct.dispel then
- local target,_, _, id, effect, _, etype = select(9,...)
- local color
- if(ct.icons)then
- icon=GetSpellTexture(id)
- end
- if (icon) then
- msg=" \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- elseif(ct.icons)then
- msg=" \124T"..ct.blank..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- else
- msg=""
- end
- if etype=="BUFF"then
- color={0,1,.5}
- else
- color={1,0,.5}
- end
- xCT3:AddMessage(ACTION_SPELL_DISPEL..": "..effect..msg,unpack(color))
-
- elseif(eventType=="SPELL_INTERRUPT")and ct.interrupt then
- local target,_, _, id, effect = select(9,...)
- local color={1,.5,0}
- if(ct.icons)then
- icon=GetSpellTexture(id)
- end
- if (icon) then
- msg=" \124T"..icon..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- elseif(ct.icons)then
- msg=" \124T"..ct.blank..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- else
- msg=""
- end
- xCT3:AddMessage(ACTION_SPELL_INTERRUPT..": "..effect..msg,unpack(color))
- elseif(eventType=="PARTY_KILL") and ct.killingblow then
- local tname=select(7,...)
- xCT3:AddMessage(ACTION_PARTY_KILL..": "..tname, .2, 1, .2)
- end
-
- end
- end
- xCTd:RegisterEvent"COMBAT_LOG_EVENT_UNFILTERED"
- xCTd:SetScript("OnEvent",dmg)
-end
-
--- healing
-if(ct.healing)then
- local unpack,select,time=unpack,select,time
- local xCTh=CreateFrame"Frame"
- if(ct.icons)then
- ct.blank="Interface\\Addons\\xCT\\blank"
- end
- local heal=function(self,event,...)
- local msg,icon
- local timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags = select(1,...)
- if(sourceGUID==ct.pguid)or(sourceFlags==gflags)then
- if(eventType=='SPELL_HEAL')or(eventType=='SPELL_PERIODIC_HEAL'and ct.showhots)then
- if(ct.healing)then
- local spellId,spellName,spellSchool,amount,overhealing,absorbed,critical = select(9,...)
- if(ct.healfilter[spellId]) then
- return
- end
- if(amount>=ct.healtreshold)then
- local color={}
- local rawamount=amount
- if (critical) then
- amount=ct.critprefix..amount..ct.critpostfix
- color={.1,1,.1}
- else
-
- color={.1,.65,.1}
- end
- if(ct.icons)then
- -- _,_,icon=GetSpellInfo(spellId)
- icon=GetSpellTexture(spellId)
- else
- msg=""
- end
- if (icon) then
- msg=' \124T'..icon..':'..ct.iconsize..':'..ct.iconsize..':0:0:64:64:5:59:5:59\124t'
- elseif(ct.icons)then
- msg=" \124T"..ct.blank..":"..ct.iconsize..":"..ct.iconsize..":0:0:64:64:5:59:5:59\124t"
- end
- if ct.mergeaoespam and ct.aoespam[spellId] then
- SQ[spellId]["locked"]=true
- SQ[spellId]["queue"]=ct.SpamQueue(spellId, rawamount)
- SQ[spellId]["msg"]=msg
- SQ[spellId]["color"]=color
- SQ[spellId]["count"]=SQ[spellId]["count"]+1
- if SQ[spellId]["count"]==1 then
- SQ[spellId]["utime"]=time()
- end
- SQ[spellId]["locked"]=false
- return
- end
- xCT4:AddMessage(amount..""..msg,unpack(color))
- end
- end
- end
- end
- end
- xCTh:RegisterEvent"COMBAT_LOG_EVENT_UNFILTERED"
- xCTh:SetScript("OnEvent",heal)
-end
diff --git a/xCT.toc b/xCT.toc
deleted file mode 100644
index 27145b85..00000000
--- a/xCT.toc
+++ /dev/null
@@ -1,9 +0,0 @@
-## Interface: 40000
-## Title: |cffFF0000x|rCT
-## Notes: X Combat Text
-## Author: Affli @ RU-Howling Fjord
-## Version: 1.8
-## RequiredDeps: Blizzard_CombatText
-
-config.lua
-xCT.lua