From 782c3cdc67163a963af33019e3cff3f1540e22a8 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Wed, 23 Feb 2011 14:17:36 -0800 Subject: [PATCH 01/23] First Commit --- 0 files changed create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..e69de29 -- 1.7.9.5 From 917c760e352b5a9fe740c62aa5165c4ea6fbdd04 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Wed, 23 Feb 2011 15:06:47 -0800 Subject: [PATCH 02/23] Initial commit of oUF Lanerra to Github! --- AuraFilter.lua | 686 ++++++++++++++++++++ Borders.lua | 113 ++++ LICENSE.txt | 21 + Tags.lua | 130 ++++ media/Border.tga | Bin 0 -> 16428 bytes media/ComboPoint.blp | Bin 0 -> 1854 bytes media/borderBackground.tga | Bin 0 -> 77881 bytes media/debuffGlow.tga | Bin 0 -> 16428 bytes media/font.ttf | Bin 0 -> 119384 bytes media/statusbarTexture.tga | Bin 0 -> 32812 bytes modules/DispellableDebuffs.lua | 118 ++++ modules/Smooth.lua | 45 ++ modules/Threat.lua | 67 ++ oUF_Lanerra.lua | 1392 ++++++++++++++++++++++++++++++++++++++++ oUF_Lanerra.toc | 21 + oUF_Lanerra_Config.lua | 124 ++++ 16 files changed, 2717 insertions(+) create mode 100644 AuraFilter.lua create mode 100644 Borders.lua create mode 100644 LICENSE.txt create mode 100644 Tags.lua create mode 100644 media/Border.tga create mode 100644 media/ComboPoint.blp create mode 100644 media/borderBackground.tga create mode 100644 media/debuffGlow.tga create mode 100644 media/font.ttf create mode 100644 media/statusbarTexture.tga create mode 100644 modules/DispellableDebuffs.lua create mode 100644 modules/Smooth.lua create mode 100644 modules/Threat.lua create mode 100644 oUF_Lanerra.lua create mode 100644 oUF_Lanerra.toc create mode 100644 oUF_Lanerra_Config.lua diff --git a/AuraFilter.lua b/AuraFilter.lua new file mode 100644 index 0000000..6a128df --- /dev/null +++ b/AuraFilter.lua @@ -0,0 +1,686 @@ +--[[-------------------------------------------------------------------- + Credits and thanks to Phanx for this filter configuration. + (Phanx < addons@phanx.net >) +------------------------------------------------------------------------ + Values: + 1 = by anyone on anyone + 2 = by player on anyone + 3 = by anyone on friendly + 4 = by anyone on player +----------------------------------------------------------------------]] + +local playerClass = select(2, UnitClass('player')) +local playerRace = select(2, UnitRace('player')) + +local auras = {} + +local function addAuras(t) + for k, v in pairs(t) do + if not auras[k] then + auras[k] = v + end + end +end + +------------------------------------------------------------------------ +-- Buffed + +addAuras({ + [90355] = 4, -- Ancient Hysteria [hunter core hound] + [2825] = 4, -- Bloodlust + [1022] = 4, -- Hand of Protection + [32182] = 4, -- Heroism + [29166] = 4, -- Innervate + [55503] = 4, -- Lifeblood [herbalism] + [80353] = 4, -- Time Warp + [33206] = 4, -- Pain Suppression + [10060] = 4, -- Power Infusion + [49016] = 4, -- Unholy Frenzy +}) + +------------------------------------------------------------------------ +-- Armor reduced + +if playerClass == 'DRUID' or playerClass == 'WARRIOR' then addAuras({ + [35387] = 1, -- Corrosive Spit [hunter serpent] + [91565] = 1, -- Faerie Fire + [8647] = 1, -- Expose Armor + [7386] = 1, -- Sunder Armor + [50498] = 1, -- Tear Armor [hunter raptor] +}) end + +------------------------------------------------------------------------ +-- Attack speed reduced + +if playerClass == 'WARRIOR' then addAuras({ + [54404] = 1, -- Dust Cloud [hunter tallstrider] + [8042] = 1, -- Earth Shock + [55095] = 1, -- Frost Fever + [58179] = 1, -- Infected Wounds [Rank 1] + [58180] = 1, -- Infected Wounds [Rank 2] + [68055] = 1, -- Judgements of the Just + [14251] = 1, -- Riposte + [90315] = 1, -- Tailspin [hunter fox] + [6343] = 1, -- Thunder Clap +}) end + +------------------------------------------------------------------------ +-- Bleed damage taken increased + +if playerClass == 'DRUID' or playerClass == 'ROGUE' then addAuras({ + [35290] = 1, -- Gore [hunter boar] -- NEEDS CHECK + [16511] = 1, -- Hemorrhage + [33878] = 1, -- Mangle [Bear Form] + [33876] = 1, -- Mangle [Cat Form] + [57386] = 1, -- Stampede [hunter rhino] + [50271] = 1, -- Tendon Rip [hunter hyena] + [46857] = 1, -- Trauma <== Blood Frenzy +}) end + +------------------------------------------------------------------------ +-- Casting speed reduced + +if playerClass == 'MAGE' or playerClass == 'ROGUE' or playerClass == 'WARLOCK' then addAuras({ + [1714] = 1, -- Curse of Tongues + [58604] = 1, -- Lava Breath [hunter core hound] + [5760] = 1, -- Mind-Numbing Poison + [31589] = 1, -- Slow + [50274] = 1, -- Spore Cloud [hunter sporebat] +}) end + +------------------------------------------------------------------------ +-- Healing effects reduced + +if playerClass == 'HUNTER' or playerClass == 'ROGUE' or playerClass == 'WARRIOR' then addAuras({ + [56112] = 1, -- Furious Attacks + [48301] = 1, -- Mind Trauma <== Improved Mind Blast + [30213] = 1, -- Legion Strike [warlock felguard] + [54680] = 1, -- Monstrous Bite [hunter devilsaur] + [12294] = 1, -- Mortal Strike + [82654] = 1, -- Widow Venom + [13218] = 1, -- Wound Poison +}) end + +------------------------------------------------------------------------ +-- Physical damage dealt reduced + +if playerClass == 'DEATHKNIGHT' or playerClass == 'DRUID' or playerClass == 'WARRIOR' then addAuras({ + [702] = 1, -- Curse of Weakness + [99] = 1, -- Demoralizing Roar + [50256] = 1, -- Demoralizing Roar [hunter bear] + [1160] = 1, -- Demoralizing Shout + [81130] = 1, -- Scarlet Fever + [26017] = 1, -- Vindication +}) end + +------------------------------------------------------------------------ +-- Disarmed + +if playerClass == '' then addAuras({ + [50541] = 1, -- Clench (hunter scorpid) + [676] = 1, -- Disarm (warrior) + [51722] = 1, -- Dismantle (rogue) + [64058] = 1, -- Psychic Horror (priest) + [91644] = 1, -- Snatch (hunter bird of prey) +}) end + +------------------------------------------------------------------------ +-- Silenced + +if playerClass == '' then addAuras({ + [25046] = 1, -- Arcane Torrent (blood elf) + [31935] = 1, -- Avenger's Shield (paladin) + [1330] = 1, -- Garrote - Silence (rogue) + [50479] = 1, -- Nether Shock (hunter nether ray) + [15487] = 1, -- Silence (priest) + [18498] = 1, -- Silenced - Gag Order (warrior) + [18469] = 1, -- Silenced - Improved Counterspell (mage) + [18425] = 1, -- Silenced - Improved Kick (rogue) + [34490] = 1, -- Silencing Shot (hunter) + [81261] = 1, -- Solar Beam (druid) + [24259] = 1, -- Spell Lock (warlock felhunter) + [47476] = 1, -- Strangulate (death knight) +}) end + +------------------------------------------------------------------------ +-- Spell-locked + +if playerClass == '' then addAuras({ + [2139] = 1, -- Counterspell (mage) + [1766] = 1, -- Kick (rogue) + [47528] = 1, -- Mind Freeze (death knight) + [6552] = 1, -- Pummel (warrior) + [26090] = 1, -- Pummel (hunter gorilla) + [50318] = 1, -- Serenity Dust (hunter moth) + [72] = 1, -- Shield Bash (warrior) + [80964] = 1, -- Skull Bash (Bear) (druid) + [80965] = 1, -- Skull Bash (Cat) (druid) + [57994] = 1, -- Wind Shear (shaman) +}) end + +------------------------------------------------------------------------ +-- Taunted + +if playerClass == 'DEATHKNIGHT' or playerClass == 'DRUID' or playerClass == 'PALADIN' or playerClass == 'WARRIOR' then addAuras({ + [5209] = 1, -- Challenging Roar + [1161] = 1, -- Challenging Shout + [56222] = 1, -- Dark Command + [57604] = 1, -- Death Grip -- NEEDS CHECK 57603 + [20736] = 1, -- Distracting Shot + [6794] = 1, -- Growl + [62124] = 1, -- Hand of Reckoning + [31790] = 1, -- Righteous Defense + [355] = 1, -- Taunt + [58857] = 1, -- Twin Howl [shaman spirit wolves] +}) end + +------------------------------------------------------------------------ +-- Crowd controlled + +addAuras({ + [710] = 1, -- Banish + [76780] = 1, -- Bind Elemental + [33786] = 1, -- Cyclone + [339] = 1, -- Entangling Roots + [5782] = 1, -- Fear + [3355] = 1, -- Freezing Trap -- NEEDS CHECK 31932 43415 55041 + [51514] = 1, -- Hex + [2637] = 1, -- Hibernate + [118] = 1, -- Polymorph + [61305] = 1, -- Polymorph [Black Cat] + [28272] = 1, -- Polymorph [Pig] + [61721] = 1, -- Polymorph [Rabbit] + [61780] = 1, -- Polymorph [Turkey] + [28271] = 1, -- Polymorph [Turtle] + [20066] = 1, -- Repentance + [6770] = 1, -- Sap + [6358] = 1, -- Seduction + [9484] = 1, -- Shackle Undead + [10326] = 1, -- Turn Evil + [19386] = 1, -- Wyvern Sting +}) + +------------------------------------------------------------------------ +-- Death Knight + +if playerClass == 'DEATHKNIGHT' then addAuras({ + [55078] = 2, -- Blood Plague + [45524] = 1, -- Chains of Ice + [77606] = 2, -- Dark Simulacrum + [43265] = 2, -- Death and Decay + [65142] = 2, -- Ebon Plague + [55095] = 2, -- Frost Fever + [49203] = 1, -- Hungering Cold + [81130] = 2, -- Scarlet Fever + [50536] = 2, -- Unholy Blight -- NEEDS CHECK + + [48707] = 4, -- Anti-Magic Shell + [81141] = 4, -- Blood Swarm <== Crimson Scourge + [49222] = 4, -- Bone Shield + [81256] = 4, -- Dancing Rune Weapon + [59052] = 4, -- Freezing Fog <== Rime + [48792] = 4, -- Icebound Fortitude + [51124] = 4, -- Killing Machine + [49039] = 4, -- Lichborne + [51271] = 4, -- Pillar of Frost + [50421] = 4, -- Scent of Blood + [81340] = 4, -- Sudden Doom + [55233] = 4, -- Vampiric Blood + [81162] = 4, -- Will of the Necropolis -- NEEDS CHECK + + [49016] = 1, -- Unholy Frenzy +}) end + +------------------------------------------------------------------------ +-- Druid + +if playerClass == 'DRUID' then addAuras({ + [5211] = 2, -- Bash + [33786] = 2, -- Cyclone + [339] = 2, -- Entangling Roots + [45334] = 2, -- Feral Charge Effect [Bear Form] + [61138] = 2, -- Feral Charge - Cat -- NEEDS CHECK + [2637] = 2, -- Hibernate + [5570] = 2, -- Insect Swarm + [33745] = 2, -- Lacerate + [22570] = 2, -- Maim + [8921] = 2, -- Moonfire + [9005] = 2, -- Pounce + [9007] = 2, -- Pounce Bleed + [1822] = 2, -- Rake + [1079] = 2, -- Rip + [93402] = 2, -- Sunfire + [77758] = 2, -- Thrash + + [22812] = 4, -- Barkskin + [50334] = 4, -- Berserk + [16870] = 4, -- Clearcasting <== Omen of Clarity + [1850] = 4, -- Dash + [5229] = 4, -- Enrage + [48518] = 4, -- Eclipse (Lunar) + [48517] = 4, -- Eclipse (Solar) + [22842] = 4, -- Frenzied Regeneration + [81093] = 4, -- Fury of Stormrage + [81192] = 4, -- Lunar Shower + [16886] = 4, -- Nature's Grace + [16689] = 4, -- Nature's Grasp + [17116] = 4, -- Nature's Swiftness + [80951] = 4, -- Pulverize + [52610] = 4, -- Savage Roar + [93400] = 4, -- Shooting Stars + [81021] = 4, -- Stampede [Ravage effect] + [81022] = 4, -- Stampede [Ravage effect] + [61336] = 4, -- Survival Instincts + [5217] = 4, -- Tiger's Fury + [33891] = 4, -- Tree of Life + [61391] = 4, -- Typhoon + + [33763] = 2, -- Lifebloom + [94447] = 2, -- Lifebloom [Tree of Life version] + [8936] = 2, -- Regrowth + [774] = 2, -- Rejuvenation + [77764] = 1, -- Stampeding Roar + [467] = 1, -- Thorns + [48438] = 2, -- Wild Growth +}) end + +------------------------------------------------------------------------ +-- Hunter + +if playerClass == 'HUNTER' then addAuras({ + [50433] = 2, -- Ankle Crack [crocolisk] + [3674] = 2, -- Black Arrow + [35101] = 2, -- Concussive Barrage + [5116] = 2, -- Concussive Shot + [19306] = 2, -- Counterattack + [20736] = 2, -- Distracting Shot + [64803] = 2, -- Entrapment + [53301] = 2, -- Explosive Shot + [13812] = 2, -- Explosive Trap -- NEEDS CHECK 43446 + [3355] = 2, -- Freezing Trap -- NEEDS CHECK 31932 43415 55041 + [1130] = 1, -- Hunter's Mark + [13810] = 2, -- Ice Trap + [13797] = 2, -- Immolation Trap -- NEEDS CHECK 51740 + [24394] = 2, -- Intimidation + [88691] = 1, -- Marked for Death + [63468] = 2, -- Piercing Shots + [1513] = 2, -- Scare Beast + [19503] = 2, -- Scatter Shot + [1978] = 2, -- Serpent Sting + [82654] = 1, -- Widow Venom + [2974] = 2, -- Wing Clip + [19386] = 2, -- Wyvern Sting + + [82921] = 4, -- Bombardment + [51755] = 4, -- Camouflage + [15571] = 4, -- Dazed <== Aspect of the Cheetah + [19263] = 4, -- Deterrence + [5384] = 4, -- Feign Death + [82926] = 4, -- Fire! <== Lock and Load + [64418] = 4, -- Sniper Training [Rank 1] + [64419] = 4, -- Sniper Training [Rank 2] + [64420] = 4, -- Sniper Training [Rank 3] + [56453] = 4, -- Lock and Load + [34477] = 4, -- Misdirection + [3045] = 4, -- Rapid Fire + [35099] = 4, -- Rapid Killing +-- [82925] = 4, -- Ready, Set, Aim... + + [19574] = 2, -- Bestial Wrath + [1539] = 2, -- Feed Pet + [136] = 2, -- Mend Pet +}) end + +------------------------------------------------------------------------ +-- Mage + +if playerClass == 'MAGE' then addAuras({ + [11113] = 2, -- Blast Wave + [12486] = 2, -- Chilled <== Blizzard <== Ice Shards -- NEEDS CHECK + [7321] = 2, -- Chilled <== Frost Aura + [83853] = 2, -- Combustion + [120] = 2, -- Cone of Cold + [44572] = 2, -- Deep Freeze + [31661] = 2, -- Dragon's Breath + [122] = 2, -- Frost Nova + [116] = 2, -- Frostbolt + [44614] = 2, -- Frostfire Bolt + [12654] = 2, -- Ignite + [12355] = 2, -- Impact + [83301] = 2, -- Improved Cone of Cold [Rank 1] + [83302] = 2, -- Improved Cone of Cold [Rank 2] + [44457] = 2, -- Living Bomb + [118] = 2, -- Polymorph + [61305] = 2, -- Polymorph [Black Cat] + [28272] = 2, -- Polymorph [Pig] + [61721] = 2, -- Polymorph [Rabbit] + [61780] = 2, -- Polymorph [Turkey] + [28271] = 2, -- Polymorph [Turtle] + [82691] = 2, -- Ring of Frost + [31589] = 2, -- Slow + + [36032] = 4, -- Arcane Blast + [79683] = 4, -- Arcane Missiles! + [12042] = 4, -- Arcane Power + [31643] = 4, -- Blazing Speed + [57761] = 4, -- Brain Freeze + [44544] = 4, -- Fingers of Frost + [48108] = 4, -- Hot Streak + [11426] = 4, -- Ice Barrier + [45438] = 4, -- Ice Block + [12472] = 4, -- Icy Veins + [64343] = 4, -- Impact + [66] = 4, -- Invisibility + [543] = 4, -- Mage Ward + [1436] = 4, -- Mana Shield + [12043] = 4, -- Presence of Mind + + [54646] = 2, -- Focus Magic + [130] = 2, -- Slow Fall +}) end + +------------------------------------------------------------------------ +-- Paladin + +if playerClass == 'PALADIN' then addAuras({ + [31935] = 2, -- Avenger's Shield + [31803] = 2, -- Censure <== Seal of Truth + [25771] = 1, -- Forbearance + [853] = 2, -- Hammer of Justice + [2812] = 2, -- Holy Wrath + [20066] = 2, -- Repentance + [10326] = 2, -- Turn Evil + + [86701] = 4, -- Ancient Crusader <== Guardian of Ancient Kings + [86657] = 4, -- Ancient Guardian <== Guardian of Ancient Kings + [86674] = 4, -- Ancient Healer <== Guardian of Ancient Kings + [31850] = 4, -- Ardent Defender + [31821] = 4, -- Aura Mastery + [31884] = 4, -- Avenging Wrath + [88819] = 4, -- Daybreak + [85509] = 4, -- Denounce + [31842] = 4, -- Divine Favor + [54428] = 4, -- Divine Plea + [498] = 4, -- Divine Protection + [642] = 4, -- Divine Shield + [82327] = 4, -- Holy Radiance + [20925] = 4, -- Holy Shield + [54149] = 4, -- Infusion of Light + [84963] = 4, -- Inquisition + [85433] = 4, -- Sacred Duty + [85497] = 4, -- Speed of Light [haste effect] + [59578] = 4, -- The Art of War + [85696] = 4, -- Zealotry + + [53563] = 2, -- Beacon of Light + [70940] = 1, -- Divine Guardian + [1044] = 1, -- Hand of Freedom + [1022] = 1, -- Hand of Protection + [6940] = 1, -- Hand of Sacrifice + [1038] = 1, -- Hand of Salvation +}) end + +------------------------------------------------------------------------ +-- Priest + +if playerClass == 'PRIEST' then addAuras({ + [2944] = 2, -- Devouring Plague + [88625] = 2, -- Holy Word: Chastise + [605] = 2, -- Mind Control + [453] = 1, -- Mind Soothe + [87178] = 2, -- Mind Spike + [87193] = 2, -- Paralysis [Rank 1] + [87194] = 2, -- Paralysis [Rank 2] + [64044] = 2, -- Psychic Horror + [8122] = 2, -- Psychic Scream + [9484] = 2, -- Shackle Undead + [589] = 2, -- Shadow Word: Pain + [34914] = 2, -- Vampiric Touch + [6788] = 1, -- Weakened Soul + + [81700] = 4, -- Archangel + [14751] = 4, -- Chakra + [81208] = 4, -- Chakra: Heal + [81206] = 4, -- Chakra: Prayer of Healing + [81207] = 4, -- Chakra: Renew + [81209] = 4, -- Chakra: Smite + [87153] = 4, -- Dark Archangel + [87117] = 4, -- Dark Evangelism -- NEEDS CHECK + [87118] = 4, -- Dark Evangelism -- NEEDS CHECK + [47585] = 4, -- Dispersion + [81660] = 4, -- Evangelism -- NEEDS CHECK + [81661] = 4, -- Evangelism -- NEEDS CHECK + [586] = 4, -- Fade + [89485] = 4, -- Inner Focus + [81292] = 4, -- Mind Melt [Rank 1] + [87160] = 4, -- Mind Melt [Rank 2] + [88688] = 4, -- Surge of Light + + [6346] = 1, -- Fear Ward + [77613] = 2, -- Grace + [47788] = 2, -- Guardian Spirit + [88682] = 2, -- Holy Word: Aspire + [33206] = 2, -- Pain Suppression + [10060] = 2, -- Power Infusion + [17] = 1, -- Power Word: Shield + [41635] = 2, -- Prayer of Mending + [139] = 2, -- Renew +}) end + +------------------------------------------------------------------------ +-- Rogue + +if playerClass == 'ROGUE' then addAuras({ + [51585] = 2, -- Blade Twisting + [2094] = 2, -- Blind + [1833] = 2, -- Cheap Shot + [3409] = 2, -- Crippling Poison + [2818] = 2, -- Deadly Poison + [26679] = 2, -- Deadly Throw + [51722] = 2, -- Dismantle + [8647] = 1, -- Expose Armor + [703] = 2, -- Garrote + [1776] = 2, -- Gouge + [89775] = 2, -- Hemorrhage [dot from glyph] + [408] = 2, -- Kidney Shot + [84617] = 2, -- Revealing Strike + [14251] = 1, -- Riposte + [1943] = 2, -- Rupture + [79140] = 2, -- Vendetta + [13218] = 2, -- Wound Poison + + [13750] = 4, -- Adrenaline Rush + [13877] = 4, -- Blade Flurry + [31224] = 4, -- Cloak of Shadows + [14177] = 4, -- Cold Blood + [84590] = 4, -- Deadly Momentum + [5277] = 4, -- Evasion + [73651] = 4, -- Recuperate + [5171] = 4, -- Slice and Dice + [2983] = 4, -- Sprint + [57934] = 4, -- Tricks of the Trade +}) end + +------------------------------------------------------------------------ +-- Shaman + +if playerClass == 'SHAMAN' then addAuras({ + [76780] = 2, -- Bind Elemental + [8042] = 2, -- Earth Shock + [3600] = 1, -- Earthbind + [56425] = 1, -- Earth's Grasp -- NEEDS CHECK + [8050] = 2, -- Flame Shock + [8056] = 2, -- Frost Shock + [8034] = 2, -- Frostbrand Attack -- NEEDS CHECK + [89523] = 1, -- Grounding Totem [reflect] + [8178] = 1, -- Grounding Totem Effect + [51514] = 2, -- Hex + [77661] = 1, -- Searing Flames + [39796] = 1, -- Stoneclaw Stun + [17364] = 2, -- Stormstrike + + [16166] = 4, -- Elemental Mastery [instant cast] + [77800] = 4, -- Focused Insight + [65264] = 4, -- Lava Flows -- NEEDS CHECK + [31616] = 4, -- Nature's Guardian + [16188] = 4, -- Nature's Swiftness + [30823] = 4, -- Shamanistic Rage + [79206] = 4, -- Spiritwalker's Grace + [53390] = 4, -- Tidal Waves + + [974] = 2, -- Earth Shield + [61295] = 2, -- Riptide +}) end + +------------------------------------------------------------------------ +-- Warlock + +if playerClass == 'WARLOCK' then addAuras({ + [93986] = 2, -- Aura of Foreboding [stun effect] -- NEEDS CHECK 93975 + [93987] = 2, -- Aura of Foreboding [root effect] -- NEEDS CHECK 93974 + [980] = 2, -- Bane of Agony + [603] = 2, -- Bane of Doom + [80240] = 2, -- Bane of Havoc + [710] = 2, -- Banish + [172] = 2, -- Corruption + [29539] = 1, -- Curse of Exhaustion + [1490] = 1, -- Curse of the Elements + [1714] = 1, -- Curse of Tongues + [702] = 1, -- Curse of Weakness + [5782] = 2, -- Fear + [48181] = 2, -- Haunt + [5484] = 2, -- Howl of Terror + [348] = 2, -- Immolate + [60947] = 2, -- Nightmare <== Improved Fear -- NEEDS CHECK 60946 + [27243] = 2, -- Seed of Corruption + [47960] = 2, -- Shadowflame -- NEEDS CHECK 47897 + [30283] = 2, -- Shadowfury + [63311] = 2, -- Shadowsnare <== Glyph of Shadowflame + [30108] = 2, -- Unstable Affliction + + [54277] = 4, -- Backdraft + [34936] = 4, -- Backlash + [79462] = 4, -- Demon Soul: Felguard + [79460] = 4, -- Demon Soul: Felhunter + [79459] = 4, -- Demon Soul: Imp + [79463] = 4, -- Demon Soul: Succubus + [79464] = 4, -- Demon Soul: Voidwalker + [88448] = 4, -- Demonic Rebirth + [47283] = 4, -- Empowered Imp + [64371] = 4, -- Eradication + [50589] = 4, -- Immolation Aura + [47241] = 4, -- Metamorphosis + [71165] = 4, -- Molten Core + [54373] = 4, -- Nether Protection (Arcane) + [54371] = 4, -- Nether Protection (Fire) + [54372] = 4, -- Nether Protection (Frost) + [54370] = 4, -- Nether Protection (Holy) + [54375] = 4, -- Nether Protection (Nature) + [54374] = 4, -- Nether Protection (Shadow) + [91711] = 4, -- Nether Ward + [7812] = 4, -- Sacrifice + [17941] = 4, -- Shadow Trance <== Nightfall + [6229] = 4, -- Shadow Ward + [86211] = 4, -- Soul Swap + [74434] = 4, -- Soulburn + + [85767] = 2, -- Dark Intent + [20707] = 1, -- Soulstone Resurrection +}) end + +------------------------------------------------------------------------ +-- Warrior + +if playerClass == 'WARRIOR' then addAuras({ + [86346] = 2, -- Colossus Smash + [12809] = 2, -- Concussion Blow + [1160] = 1, -- Demoralizing Shout + [676] = 1, -- Disarm + [1715] = 2, -- Hamstring + [20511] = 2, -- Intimidating Shout + [12294] = 2, -- Mortal Strike + [12323] = 2, -- Piercing Howl + [94009] = 2, -- Rend + [64382] = 1, -- Shattering Throw + [46968] = 2, -- Shockwave + [58567] = 2, -- Sunder Armor + [85388] = 2, -- Throwdown + [6343] = 2, -- Thunder Clap + + [12964] = 4, -- Battle Trance + [18499] = 4, -- Berserker Rage + [46924] = 4, -- Bladestorm + [46916] = 4, -- Bloodsurge + [23885] = 4, -- Bloodthirst -- NEEDS CHECK + [85730] = 4, -- Deadly Calm + [12292] = 4, -- Death Wish + [55694] = 4, -- Enraged Regeneration + [1134] = 4, -- Inner Rage + [65156] = 4, -- Juggernaut + [12976] = 4, -- Last Stand + [1719] = 4, -- Recklessness + [20230] = 4, -- Retaliation + [2565] = 4, -- Shield Block + [871] = 4, -- Shield Wall + [23920] = 4, -- Spell Reflection + [50227] = 4, -- Sword and Board + [87069] = 4, -- Thunderstruck + [32216] = 4, -- Victory Rush + + [3411] = 2, -- Intervene + [50720] = 2, -- Vigilance +}) end + +------------------------------------------------------------------------ +-- Racials + +if playerRace == 'Draenei' then + auras[59545] = 4 -- Gift of the Naaru (death knight) + auras[59543] = 4 -- Gift of the Naaru (hunter) + auras[59548] = 4 -- Gift of the Naaru (mage) + auras[59542] = 4 -- Gift of the Naaru (paladin) + auras[59544] = 4 -- Gift of the Naaru (priest) + auras[59547] = 4 -- Gift of the Naaru (shaman) + auras[28880] = 4 -- Gift of the Naaru (warrior) +elseif playerRace == 'Dwarf' then + auras[20594] = 4 -- Stoneform +elseif playerRace == 'NightElf' then + auras[58984] = 4 -- Shadowmeld +elseif playerRace == 'Orc' then + auras[20572] = 4 -- Blood Fury (attack power) + auras[33702] = 4 -- Blood Fury (spell power) + auras[33697] = 4 -- Blood Fury (attack power and spell damage) +elseif playerRace == 'Scourge' then + auras[7744] = 4 -- Will of the Forsaken +elseif playerRace == 'Tauren' then + auras[20549] = 1 -- War Stomp +elseif playerRace == 'Troll' then + auras[26297] = 4 -- Berserking +elseif playerRace == 'Worgen' then + auras[68992] = 4 -- Darkflight +end + +------------------------------------------------------------------------ + +local unitIsPlayer = { player = true, pet = true, vehicle = true } + +local filters = { + [1] = function( self, unit, caster ) return true end, + [2] = function( self, unit, caster ) return unitIsPlayer[ caster ] end, + [3] = function( self, unit, caster ) return UnitIsFriend( unit, 'player' ) and UnitPlayerControlled( unit ) end, + [4] = function( self, unit, caster ) return unit == 'player' and not self.__owner.isGroupFrame end, +} + +CustomAuraFilter = function( self, unit, icon, name, rank, texture, count, dtype, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID ) + local v = auras[ spellID ] + + -- print( 'CustomAuraFilter', unit, caster, name, spellID, v ) + + if v and filters[ v ] then + return filters[ v ]( self, unit, caster ) + else + return ( not caster or caster == unit ) and UnitCanAttack( unit, 'player' ) and not UnitPlayerControlled( unit ) + end +end + +AuraList = auras \ No newline at end of file diff --git a/Borders.lua b/Borders.lua new file mode 100644 index 0000000..39971be --- /dev/null +++ b/Borders.lua @@ -0,0 +1,113 @@ +--[[ + Border and Shadow generation, color-alteration, size-changing, and parenting + Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. +]] + +-- Create Border Function +function AddBorder(self, size, offset) + if type(self) ~= 'table' or not self.CreateTexture or self.BorderTextures then return end + + local t = { } + + for i = 1, 8 do + t[i] = self:CreateTexture(nil, 'BORDER') + t[i]:SetTexture('Interface\\AddOns\\oUF_Lanerra\\media\\Border.tga') + end + + t[1].name = 'TOPLEFT' + t[1]:SetTexCoord(0, 1/3, 0, 1/3) + + t[2].name = 'TOPRIGHT' + t[2]:SetTexCoord(2/3, 1, 0, 1/3) + + t[3].name = 'TOP' + t[3]:SetTexCoord(1/3, 2/3, 0, 1/3) + + t[4].name = 'BOTTOMLEFT' + t[4]:SetTexCoord(0, 1/3, 2/3, 1) + + t[5].name = 'BOTTOMRIGHT' + t[5]:SetTexCoord(2/3, 1, 2/3, 1) + + t[6].name = 'BOTTOM' + t[6]:SetTexCoord(1/3, 2/3, 2/3, 1) + + t[7].name = 'LEFT' + t[7]:SetTexCoord(0, 1/3, 1/3, 2/3) + + t[8].name = 'RIGHT' + t[8]:SetTexCoord(2/3, 1, 1/3, 2/3) + + self.BorderTextures = t + + self.SetBorderColor = SetBorderColor + self.SetBorderParent = SetBorderParent + self.SetBorderSize = SetBorderSize + + if self.SetBackdropBorderColor then + self.SetBackdropBorderColor = SetBorderColor + end + + SetBorderColor(self) + SetBorderSize(self, size, offset) +end + +-- Border Color Function +function SetBorderColor(frame, r, g, b, a) + local t = frame.BorderTextures + if not t then return end + + if not r or not g or not b or a == 0 then + r, g, b = unpack(Settings.Media.BorderColor) + end + + for i, tex in ipairs(t) do + tex:SetVertexColor(r, g, b) + end +end + +-- Border Parent Function +function SetBorderParent(self, parent) + local t = self.BorderTextures + if not t then return end + + for i, tex in ipairs(t) do + tex:SetParent(parent or self) + end +end + +-- Border Size Function +function SetBorderSize(self, size, offset) + local t = self.BorderTextures + if not t then return end + + if not size then + size = Settings.Media.BorderSize + end + + local d = offset or (floor(size / 2 + 0.5) - 2) + + for i, tex in ipairs(t) do + tex:SetSize(size, size) + end + + t[1]:SetPoint('TOPLEFT', self, -d, d) + + t[2]:SetPoint('TOPRIGHT', self, d, d) + + t[3]:SetPoint('LEFT', t[1], 'TOPRIGHT') + t[3]:SetPoint('TOPRIGHT', t[2], 'TOPLEFT') + + t[4]:SetPoint('BOTTOMLEFT', self, -d, -d) + + t[5]:SetPoint('BOTTOMRIGHT', self, d, -d) + + t[6]:SetPoint('BOTTOMLEFT', t[4], 'BOTTOMRIGHT') + t[6]:SetPoint('BOTTOMRIGHT', t[5], 'BOTTOMLEFT') + + t[7]:SetPoint('TOPLEFT', t[1], 'BOTTOMLEFT') + t[7]:SetPoint('BOTTOMLEFT', t[4], 'TOPLEFT') + + t[8]:SetPoint('TOPRIGHT', t[2], 'BOTTOMRIGHT') + t[8]:SetPoint('BOTTOMRIGHT', t[5], 'TOPRIGHT') +end diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3b62529 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright © 2010-2011 Lanerra. + +The contents of this addon, excluding third-party resources, are +copyrighted to its author with all rights reserved, under United +States copyright law and various international treaties. + +In particular, please note that you may not distribute this addon in +any form, with or without modifications, including as part of a +compilation, without prior written permission from its author. + +The author of this addon hereby grants you the following rights: + +1. You may make modifications to this addon for private use only. + +2. You may use source code from this addon for any purpose, provided +that the names of this addon and its author are not used to promote +your project, and do not appear in the title, source code, or file +names of your project, outside of an optional credits notation. + +All rights not explicitly addressed in this license are reserved by +the copyright holder. \ No newline at end of file diff --git a/Tags.lua b/Tags.lua new file mode 100644 index 0000000..b271ecb --- /dev/null +++ b/Tags.lua @@ -0,0 +1,130 @@ +-- Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. +-- Define some custom oUF tags +oUF.Tags['LanPvPTime'] = function(unit) + return UnitIsPVP(unit) and not IsPVPTimerRunning() and '*' or IsPVPTimerRunning() and ('%d:%02d'):format((GetPVPTimer() / 1000) / 60, (GetPVPTimer() / 1000) % 60) +end + +oUF.TagEvents['LanThreat'] = 'UNIT_THREAT_LIST_UPDATE' +oUF.Tags['LanThreat'] = function() + local _, _, perc = UnitDetailedThreatSituation('player', 'target') + return perc and ('%s%d%%|r'):format(hex(GetThreatStatusColor(UnitThreatSituation('player', 'target'))), perc) +end + +oUF.Tags['LanLevel'] = function(unit) + local level = UnitLevel(unit) + local colorL = GetQuestDifficultyColor(level) + + if (level < 0) then + r, g, b = 1, 0, 0 + level = '??' + elseif (level == 0) then + r, g, b = colorL.r, colorL.g, colorL.b + level = '?' + else + r, g, b = colorL.r, colorL.g, colorL.b + level = level + end + + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, level) +end + +oUF.TagEvents['LanName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' +oUF.Tags['LanName'] = function(unit) + local colorA + local UnitName, UnitRealm = UnitName(unit) + local _, class = UnitClass(unit) + + if (UnitRealm) and (UnitRealm ~= '') then + UnitName = UnitName + end + + colorA = {1, 1, 1} + + r, g, b = colorA[1], colorA[2], colorA[3] + + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, UnitName) +end + +oUF.TagEvents['LanRaidName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' +oUF.Tags['LanRaidName'] = function(unit) + local Name = string.sub(UnitName(unit), 1, 4) + return Name +end + +oUF.TagEvents['LanPower'] = 'UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER' +oUF.Tags['LanPower'] = function(unit) + local min = UnitPower(unit) + return min +end + +oUF.TagEvents['LanCombat'] = 'PLAYER_REGEN_DISABLED PLAYER_REGEN_ENABLED' +oUF.Tags['LanCombat'] = function(unit) + if unit == 'player' and UnitAffectingCombat('player') then + return [[|TInterface\CharacterFrame\UI-StateIcon:0:0:0:0:64:64:37:58:5:26|t]] + end +end +oUF.UnitlessTagEvents['PLAYER_REGEN_DISABLED'] = true +oUF.UnitlessTagEvents['PLAYER_REGEN_ENABLED'] = true + +oUF.TagEvents['LanLeader'] = 'PARTY_LEADER_CHANGED PARTY_MEMBERS_CHANGED' +oUF.Tags['LanLeader'] = function(unit) + if UnitIsPartyLeader(unit) then + return [[|TInterface\GroupFrame\UI-Group-LeaderIcon:0|t]] + elseif UnitInRaid(unit) and UnitIsRaidOfficer(unit) then + return [[|TInterface\GroupFrame\UI-Group-AssistantIcon:0|t]] + end +end + +oUF.TagEvents['LanMaster'] = 'PARTY_LOOT_METHOD_CHANGED PARTY_MEMBERS_CHANGED' +oUF.Tags['LanMaster'] = function(unit) + local method, pid, rid = GetLootMethod() + if method ~= 'master' then return end + local munit + if pid then + if pid == 0 then + munit = 'player' + else + munit = 'party' .. pid + end + elseif rid then + munit = 'raid' .. rid + end + if munit and UnitIsUnit(munit, unit) then + return [[|TInterface\GroupFrame\UI-Group-MasterLooter:0:0:0:2|t]] + end +end + +oUF.TagEvents['LanResting'] = 'PLAYER_UPDATE_RESTING' +oUF.Tags['LanResting'] = function(unit) + if unit == 'player' and IsResting() then + return [[|TInterface\CharacterFrame\UI-StateIcon:0:0:0:-6:64:64:28:6:6:28|t]] + end +end + + +oUF.Tags['LanHolyPower'] = function(unit) + local hp = UnitPower('player', SPELL_POWER_HOLY_POWER) + + if hp > 0 then + return string.format('|c50f58cba%d|r', hp) + end +end +oUF.TagEvents['LanHolyPower'] = 'UNIT_POWER' + +oUF.Tags['LanShards'] = function(unit) + local hp = UnitPower('player', SPELL_POWER_SOUL_SHARDS) + + if hp > 0 then + return string.format('|c909482c9%d|r', hp) + end +end +oUF.TagEvents['LanShards'] = 'UNIT_POWER' + +oUF.Tags['LanCombo'] = function(unit) + local cp = GetComboPoints('player', 'target') + + if cp > 0 then + return string.format('|cffffff00%d|r', cp) + end +end +oUF.TagEvents['LanCombo'] = 'UNIT_COMBO_POINTS' diff --git a/media/Border.tga b/media/Border.tga new file mode 100644 index 0000000000000000000000000000000000000000..080087ee8263963a4409157fcba8141e0cec7149 GIT binary patch literal 16428 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 literal 0 HcmV?d00001 diff --git a/media/statusbarTexture.tga b/media/statusbarTexture.tga new file mode 100644 index 0000000000000000000000000000000000000000..ddf3e54b1e1485ce1c595df050c004b65ae6a7d2 GIT binary patch literal 32812 zcmeIuTM2+b5CFi{FJoK*ZE?baDEPwuMa~~F#Y}{W=;POwC_}@50RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK rfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjXz@AstKNC!wvNx7OP%5c05puK literal 0 HcmV?d00001 diff --git a/modules/DispellableDebuffs.lua b/modules/DispellableDebuffs.lua new file mode 100644 index 0000000..47ee443 --- /dev/null +++ b/modules/DispellableDebuffs.lua @@ -0,0 +1,118 @@ +--[[-------------------------------------------------------------------- + oUF_DispelHighlight + Highlights oUF frames by dispellable debuff type. + Originally based on Ammo's oUF_DebuffHighlight. + + You may embed this module in your own layout, but please do not + distribute it as a standalone plugin. + + To have your frame's health bar highlighted: + frame.DispelHighlight = true + + To use your own highlighting function: + frame.DispelHighlight = function(frame, event, unit, debuffType, canDispel) + -- debuffType : string or nil : type of the highest priority debuff, nil if no debuffs + -- canDispel : boolean : indicates whether the player can dispel the debuff + end + + To highlight only debuffs you can dispel: + frame.DispelHighlightFilter = true +----------------------------------------------------------------------]] + +if not oUF then return end +if select(4, GetAddOnInfo('oUF_DispelHighlight')) then return end + +local _, playerClass = UnitClass('player') + +local canDispel +if playerClass == 'DRUID' then + canDispel = { Curse = true, Poison = true } +elseif playerClass == 'MAGE' then + canDispel = { Curse = true } +elseif playerClass == 'PALADIN' then + canDispel = { Disease = true, Magic = true, Poison = true } +elseif playerClass == 'PRIEST' then + canDispel = { Disease = true, Magic = true } +elseif playerClass == 'SHAMAN' then + canDispel = { Curse = true, Disease = true, Poison = true } +end + +local DebuffPriority = { } +for type, priority in pairs({ Curse = 2, Disease = 4, Magic = 1, Poison = 3 }) do + table.insert(DebuffPriority, type) + DebuffPriority[type] = ((canDispel and canDispel[type]) and 10 or 5) - priority +end +table.sort(DebuffPriority, function(a, b) return DebuffPriority[a] > DebuffPriority[b] end) + +local DebuffTypeColor = { } +for type, color in pairs(_G.DebuffTypeColor) do + DebuffTypeColor[type] = { color.r, color.g, color.b } +end + +------------------------------------------------------------------------ + +local unitDebuffType = { } + +local function applyDispelHighlight(self, event, unit, bar) + local debuffType = unitDebuffType[unit] + if debuffType then + bar:SetStatusBarColor(unpack(DebuffTypeColor[debuffType])) + end +end + +local function Update(self, event, unit) + if self.unit ~= unit then return end + -- print('Update', unit) + + if not UnitCanAssist('player', unit) then return end + -- print('not UnitCanAssist') + + local debuffType + + local i = 1 + while true do + local name, _, _, _, type = UnitAura(unit, i, 'HARMFUL') + if not name then break end + -- print('UnitAura', unit, i, name or 'NONE', type or 'NONE') + if type and (not debuffType or DebuffPriority[type] > DebuffPriority[debuffType]) then + -- print('debuffType', type) + debuffType = type + end + i = i + 1 + end + + if unitDebuffType[unit] ~= debuffType then + -- print('unitDebuffType', unitDebuffType[unit] or 'NONE', 'debuffType', debuffType or 'NONE') + + unitDebuffType[unit] = debuffType + + if type(self.DispelHighlight) == 'function' then + self:DispelHighlight(event, unit, debuffType, canDispel and canDispel[debuffType]) + else + if debuffType and self.DispelHighlightFilter and not (canDispel and canDispel[debuffType]) then return end + applyDispelHighlight(self, event, unit, self.Health) + end + end +end + +local function Enable(self) + if not self.DispelHighlight or (self.DispelHighlightFilter and not canDispel) then return end + + self:RegisterEvent('UNIT_AURA', Update) + + if type(self.DispelHighlight) ~= 'function' then + local o = self.PostUpdateHealth + self.PostUpdateHealth = function(...) + if o then o(...) end + applyDispelHighlight(...) + end + end +end + +local function Disable(self) + if not self.DispelHighlight or not canDispel then return end + + self:UnregisterEvent('UNIT_AURA', Update) +end + +oUF:AddElement('DispelHighlight', Update, Enable, Disable) diff --git a/modules/Smooth.lua b/modules/Smooth.lua new file mode 100644 index 0000000..0272e55 --- /dev/null +++ b/modules/Smooth.lua @@ -0,0 +1,45 @@ +if not oUF then return end + +local smoothing = {} +local function Smooth(self, value) + if value ~= self:GetValue() or value == 0 then + smoothing[self] = value + else + smoothing[self] = nil + end +end + +local function SmoothBar(self, bar) + bar.SetValue_ = bar.SetValue + bar.SetValue = Smooth +end + +local function hook(frame) + frame.SmoothBar = SmoothBar + if frame.Health and frame.Health.Smooth then + frame:SmoothBar(frame.Health) + end + if frame.Power and frame.Power.Smooth then + frame:SmoothBar(frame.Power) + end +end + +for i, frame in ipairs(oUF.objects) do hook(frame) end +oUF:RegisterInitCallback(hook) + +local f, min, max = CreateFrame('Frame'), math.min, math.max +f:SetScript('OnUpdate', function() + local limit = 30/GetFramerate() + for bar, value in pairs(smoothing) do + local cur = bar:GetValue() + local new = cur + min((value-cur)/3, max(value-cur, limit)) + if new ~= new then + new = value + end + bar:SetValue_(new) + if cur == value or abs(new - value) < 2 then + bar:SetValue_(value) + smoothing[bar] = nil + end + end +end) \ No newline at end of file diff --git a/modules/Threat.lua b/modules/Threat.lua new file mode 100644 index 0000000..6c0b28b --- /dev/null +++ b/modules/Threat.lua @@ -0,0 +1,67 @@ +--[[-------------------------------------------------------------------- + oUF_ThreatHighlight + Highlights oUF frames by threat level. + + Simple usage: + self.ThreatHighlight = true + + Advanced usage: + self.ThreatHighlight = function(self, unit, status) end +----------------------------------------------------------------------]] + +local _, ns = ... +local oUF = ns.oUF or oUF +if not oUF then return end + +local unitThreatStatus = { } + +local function applyThreatHighlight(self, unit) + local status = unitThreatStatus[unit] + if status then + local r, g, b = GetThreatStatusColor(status) + self:SetStatusBarColor(r, g, b) + end +end + +local function Update(self, event, unit) + if self.unit ~= unit then return end + + local status = UnitThreatSituation(unit) + -- local status = UnitIsFriend(unit, 'player') and UnitThreatSituation(unit) or UnitThreatSituation('player', unit) + -- print('ThreatHighlight Update', event, unit, status) + + if status and status > 0 then + if type(self.ThreatHighlight) == 'function' then + self.ThreatHighlight(self, unit, status) + else + unitThreatStatus[unit] = status + applyThreatHighlight(self.Health, unit) + end + elseif type(self.ThreatHighlight) == 'function' then + self.ThreatHighlight(self, unit, 0) + end +end + +local function Enable(self) + if not self.ThreatHighlight then return end + + self:RegisterEvent('UNIT_THREAT_SITUATION_UPDATE', Update) + + if type(self.ThreatHighlight) ~= 'function' then + local o = self.Health.PostUpdate + self.Health.PostUpdate = function(...) + if o then o(...) end + applyThreatHighlight(...) + end + end + + return true +end + +local function Disable(self) + if not self.ThreatHighlight then return end + + self:UnregisterEvent('UNIT_THREAT_SITUATION_UPDATE', Update) +end + +oUF:AddElement('ThreatHighlight', Update, Enable, Disable) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua new file mode 100644 index 0000000..4dcfe68 --- /dev/null +++ b/oUF_Lanerra.lua @@ -0,0 +1,1392 @@ +--[[ + Version = 1.13 + + Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. + + Special thanks to P3lim for inspiration, Neav for textures and inspiration, + Game92 for inspiration, and Phanx for inspiration and an inline border method +--]] + +---- Lazy Stuff Goes Here! +local _, ns = ... + +local playerClass = select(2, UnitClass('player')) +local isHealer = (playerClass == 'DRUID' or playerClass == 'PALADIN' or playerClass == 'PRIEST' or playerClass == 'SHAMAN') + +-- A little backdrop local to save us some typing...because I'm lazy +local backdrop = { + bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], + insets = {top = -1, left = -1, bottom = -1, right = -1}, +} + +local PlayerUnits = { player = true, pet = true, vehicle = true } + +-- Dummy function +local noop = function() return end + +fontstrings = { } + +-- Custom power colors +local PowerBarColor = PowerBarColor + +PowerBarColor['MANA'] = { r = 0/255, g = 0.55, b = 1 } +PowerBarColor['RAGE'] = { r = 240/255, g = 45/255, b = 75/255 } +PowerBarColor['FOCUS'] = { r = 255/255, g = 175/255, b = 0 } +PowerBarColor['ENERGY'] = { r = 1, g = 1, b = 35/255 } +PowerBarColor['RUNIC_POWER'] = { r = 0.45, g = 0.85, b = 1 } + +local Colors = oUF.colors + +-- Threat color handling +oUF.colors.threat = { } +for i = 1, 3 do + local r, g, b = GetThreatStatusColor(i) + oUF.colors.threat[i] = { r, g, b } +end + +-- Debuff color handling +Colors.Debuff = { } +for type, color in pairs(DebuffTypeColor) do + if (type ~= 'none') then + Colors.Debuff[type] = { color.r, color.g, color.b } + end +end + +-- Color conversion function +function hex(r, g, b) + if(type(r) == 'table') then + if(r.r) then r, g, b = r.r, r.g, r.b else r, g, b = unpack(r) end + end + return ('|cff%02x%02x%02x'):format(r * 255, g * 255, b * 255) +end + +---- No More Lazy Stuff! + +-- Border update function +local function UpdateBorder(self) + local Threat, Debuff, Dispellable = self.threatLevel, self.debuffType, self.debuffDispellable + + local Color + if Debuff and Dispellable then + Color = Colors.Debuff[Debuff] + elseif Threat and Threat > 1 then + Color = oUF.colors.threat[Threat] + elseif Debuff then + Color = Colors.Debuff[Debuff] + elseif Threat and Threat > 0 then + Color = oUF.colors.threat[Threat] + end + + if Color then + self:SetBackdropBorderColor(Color[1], Color[2], Color[3], 1) + else + self:SetBackdropBorderColor(0, 0, 0, 0) + end +end + +-- Formatting function for the display of health and power text +local function ShortValue(value) + if value >= 1e7 then + return ('%.1fm'):format(value / 1e6):gsub('%.?0+([km])$', '%1') + elseif value >= 1e6 then + return ('%.2fm'):format(value / 1e6):gsub('%.?0+([km])$', '%1') + elseif value >= 1e5 then + return ('%.0fk'):format(value / 1e3) + elseif value >= 1e3 then + return ('%.1fk'):format(value / 1e3):gsub('%.?0+([km])$', '%1') + else + return value + end +end + +-- Dropdown menu function for our unit frames +local function CreateDropDown(self) + local unit = self.unit:gsub('(.)', string.upper, 1) + if _G[unit..'FrameDropDown'] then + ToggleDropDownMenu(1, nil, _G[unit..'FrameDropDown'], 'cursor') + elseif (self.unit:match('party')) then + ToggleDropDownMenu(1, nil, _G['PartyMemberFrame'..self.id..'DropDown'], 'cursor') + else + FriendsDropDown.unit = self.unit + FriendsDropDown.id = self.id + FriendsDropDown.initialize = RaidFrameDropDown_Initialize + ToggleDropDownMenu(1, nil, FriendsDropDown, 'cursor') + end +end + +-- And now for our custom channeling function for our castbars +local function UpdateChannelStart(self, event, unit, name, rank, text) + self.Castbar.SafeZone:SetDrawLayer('ARTWORK') + self.Castbar.SafeZone:ClearAllPoints() + self.Castbar.SafeZone:SetPoint('TOPLEFT', self.Castbar) + self.Castbar.SafeZone:SetPoint('BOTTOMLEFT', self.Castbar) +end + +-- Now, the custom casting function +local function UpdateCastStart(self, event, unit, name, rank, text, castid) + self.Castbar.SafeZone:SetDrawLayer('BORDER') + self.Castbar.SafeZone:ClearAllPoints() + self.Castbar.SafeZone:SetPoint('TOPRIGHT', self.Castbar) + self.Castbar.SafeZone:SetPoint('BOTTOMRIGHT', self.Castbar) +end + +-- Health update function of doom! +local UpdateHealth = function(Health, unit, min, max) + if (Health:GetParent().unit ~= unit) then + return + end + + if (not unit == 'pet' or unit == 'focus' or unit == 'targettarget') then + if (not UnitIsConnected(unit)) then + Health:SetValue(0) + Health.Value:SetText('|cffD7BEA5'..'Offline') + + return + elseif (UnitIsDead(unit)) then + Health:SetValue(0) + Health.Value:SetText('|cffD7BEA5'..'Dead') + + return + elseif (UnitIsGhost(unit)) then + Health:SetValue(0) + Health.Value:SetText('|cffD7BEA5'..'Ghost') + + return + end + end + + if (unit == 'player') then + if (Settings.Units.Player.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Player.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Player.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + elseif (unit == 'target') then + if (Settings.Units.Target.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Target.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Target.Health.Current) then + Health.Value:SetText(ShortValue(min)) + else + Health.Value:SetText() + end + elseif (unit == 'targettarget') then + if (Settings.Units.ToT.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.ToT.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.ToT.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + elseif (unit == 'pet') then + if (Settings.Units.Pet.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Pet.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Pet.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + elseif (unit == 'focus') then + if (Settings.Units.Focus.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Focus.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Focus.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + end + + -- Bar Color Stuff + Health:SetStatusBarColor(.25, .25, .25) +end + +-- Group update health function +local function UpdateGroupHealth(Health, unit, min, max) + if (Health:GetParent().unit ~= unit) then + return + end + + if (not UnitIsConnected(unit)) then + Health:SetValue(0) + Health.Value:SetText('|cffD7BEA5'..'Offline') + elseif (UnitIsDead(unit)) then + Health.Value:SetText('|cffD7BEA5'..'Dead') + elseif (UnitIsGhost(unit)) then + Health.Value:SetText('|cffD7BEA5'..'Ghost') + end + + if (Settings.Units.Party.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Party.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Party.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + + local color = RAID_CLASS_COLORS[select(2, UnitClass(unit))] + if (Settings.Units.Party.Health.ClassColor) then + Health.colorClass = true + else + Health:SetStatusBarColor(.25, .25, .25) + end +end + +-- Raid update health function +local function UpdateRaidHealth(Health, unit, min, max) + if (Health:GetParent().unit ~= unit) then + return + end + + if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then + Health.Value:SetText((UnitIsDead(unit) and 'Dead') or (UnitIsGhost(unit) and 'Ghost') or (not UnitIsConnected(unit) and 'Offline')) + Health.Value:SetTextColor(.5, .5, .5) + Health:SetStatusBarColor(.5, .5, .5) + else + if (Settings.Units.Raid.Health.Percent) then + Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + elseif (Settings.Units.Raid.Health.Deficit) then + Health.Value:SetText((min ~= max) and format('%d', min - max) or '') + elseif (Settings.Units.Raid.Health.Current) then + Health.Value:SetText((min ~= max) and format('%d', min) or '') + else + Health.Value:SetText() + end + + local color = RAID_CLASS_COLORS[select(2, UnitClass(unit))] + if (Settings.Units.Raid.Health.ClassColor) then + Health.colorClass = true + else + Health:SetStatusBarColor(.25, .25, .25) + end + end +end + +-- Now for the update power function +local UpdatePower = function(Power, unit, min, max) + local self = Power:GetParent() + + local UnitHappiness = self.colors.happiness[GetPetHappiness()] + local _, PowerType, altR, altG, altB = UnitPowerType(unit) + local UnitPower = PowerBarColor[PowerType] + + if (min == 0 or UnitIsDead(unit) or UnitIsGhost(unit) or unit == 'pet' or unit == 'focus' or unit ~= 'player' or not UnitIsConnected(unit)) then + Power.Value:SetText() + else + Power.Value:SetText(min) + end + + if (unit == 'pet' and GetPetHappiness()) then + Power:SetStatusBarColor(UnitHappiness[1], UnitHappiness[2], UnitHappiness[3]) + else + Power:SetStatusBarColor(UnitPower.r, UnitPower.g, UnitPower.b) + end +end + +-- Add DruidPower support +local function UpdateDruidPower(self, event, unit) + if (unit and unit ~= self.unit) then + return + end + + local unitPower = PowerBarColor['MANA'] + local mana = UnitPowerType('player') == 0 + local index = GetShapeshiftForm() + + if (index == 1 or index == 3) then + if (unitPower) then + self.Druid.Power:SetStatusBarColor(unitPower.r, unitPower.g, unitPower.b) + end + + self.Druid.Power:SetAlpha(1) + + local min, max = UnitPower('player', 0), UnitPowerMax('player', 0) + + self.Druid.Power:SetMinMaxValues(0, max) + self.Druid.Power:SetValue(min) + else + self.Druid.Power:SetAlpha(0) + end +end + +-- Aura Icons for our unit frames +-- Aura Icon Show +local AuraIconCD_OnShow = function(cd) + local button = cd:GetParent() + button:SetBorderParent(cd) + button.count:SetParent(cd) +end + +-- Aura Icon Hide +local AuraIconCD_OnHide = function(cd) + local button = cd:GetParent() + button:SetBorderParent(button) + button.count:SetParent(button) +end + +-- Aura Icon Overlay +local AuraIconOverlay_SetBorderColor = function(overlay, r, g, b) + if not r or not g or not b then + r, g, b = unpack(Settings.Media.BorderColor) + end + overlay:GetParent():SetBorderColor(r, g, b) +end + +-- Aura Icon Creation Function +local function PostCreateAuraIcon(iconframe, button) + AddBorder(button, Settings.Media.BorderSize) + + button.cd:SetReverse(true) + button.cd:SetScript('OnHide', AuraIconCD_OnHide) + button.cd:SetScript('OnShow', AuraIconCD_OnShow) + if button.cd:IsShown() then + AuraIconCD_OnShow(button.cd) + end + + button.icon:SetTexCoord(0.03, 0.97, 0.03, 0.97) + + button.overlay:Hide() + button.overlay.Hide = AuraIconOverlay_SetBorderColor + button.overlay.SetVertexColor = AuraIconOverlay_SetBorderColor + button.overlay.Show = noop +end + +-- Aura Icon Update Function +local function PostUpdateAuraIcon(iconframe, unit, button, index, offset) + local name, _, texture, count, type, duration, timeLeft, caster, isStealable, shouldConsolidate, spellID = UnitAura(unit, index, button.filter) + + if PlayerUnits[caster] then + button.icon:SetDesaturated(false) + else + button.icon:SetDesaturated(true) + end + + if button.timer then return end + + if OmniCC then + for i = 1, button:GetNumChildren() do + local child = select(i, button:GetChildren()) + if child.text and (child.icon == button.icon or child.cooldown == button.cd) then + -- found it! + child.SetAlpha = noop + child.SetScale = noop + + child.text:ClearAllPoints() + child.text:SetPoint('CENTER', button, 'TOP', 0, 2) + + child.text:SetFont(Settings.Media.Font, unit:match('^party') and 14 or 18) + child.text.SetFont = noop + + child.text:SetTextColor(1, 0.8, 0) + child.text.SetTextColor = noop + child.text.SetVertexColor = noop + + tinsert(fontstrings, child.text) + + button.timer = child.text + + return + end + end + else + button.timer = true + end +end + +-- Dispel highlighting function +local function UpdateDispelHighlight(self, event, unit, debuffType, canDispel) + if (self.unit ~= unit) then + return + end + + if (self.debuffType == debuffType) then + return + end + + self.debuffType = debuffType + self.debuffDispellable = canDispel + + self:UpdateBorder() +end + +-- Threat highlighting function +local function UpdateThreatHighlight(self, unit, status) + if self.threatLevel == status then return end + + self.threatLevel = status + self:UpdateBorder() +end + +-- Time to give our solo unit frames some style! +local Stylish = function(self, unit, isSingle) + self.menu = CreateDropDown + self.ignoreHealComm = true + + self:EnableMouse(true) + self:RegisterForClicks('AnyUp') + + -- Health Bar-specific stylings + self.Health = CreateFrame('StatusBar', '$parentHealthBar', self) + self.Health:SetHeight(Settings.Units.Player.Height * .75) + self.Health:SetStatusBarTexture(Settings.Media.StatusBar) + + -- Turn on the smoothness + self.Health.Smooth = true + + self.Health.frequentUpdates = true + + self.Health:SetParent(self) + self.Health:SetPoint('TOP') + self.Health:SetPoint('LEFT') + self.Health:SetPoint('RIGHT') + + self:SetBackdrop(backdrop) + self:SetBackdropColor(0, 0, 0, .75) + + if (unit == 'player') then + local info = self.Health:CreateFontString('$parentInfo', 'OVERLAY', 'GameFontHighlightSmall') + info:SetPoint('CENTER', self.Health) + info.frequentUpdates = .25 + self:Tag(info, '[LanThreat] |cffff0000[LanPvPTime]|r') + end + + -- Setup our health text + self.Health.Value = self.Health:CreateFontString('$parentHealthValue', 'OVERLAY') + self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) + self.Health.Value:SetShadowOffset(1, -1) + self.Health.Value:SetTextColor(1, 1, 1) + self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) + + self.Health.PostUpdate = UpdateHealth + + -- And now for the power bar and text stuff + self.Power = CreateFrame('StatusBar', '$parentPowerBar', self) + self.Power:SetHeight(Settings.Units.Player.Height * .22) + self.Power:SetStatusBarTexture(Settings.Media.StatusBar) + + self.Power.colorTapping = true + self.Power.colorHappiness = true + self.Power.colorClass = true + self.Power.colorReaction = true + + -- We like to keep things smooth here + self.Power.frequentUpdates = true + + self.Power:SetParent(self) + self.Power:SetPoint('BOTTOM') + self.Power:SetPoint('LEFT', .2, 0) + self.Power:SetPoint('RIGHT', -.2, 0) + + -- Now, the power bar's text + self.Power.Value = self.Power:CreateFontString('$parentPowerValue', 'OVERLAY') + self.Power.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) + self.Power.Value:SetShadowOffset(1, -1) + self.Power.Value:SetPoint('LEFT', self.Health.Value, 'RIGHT', -195, 0) + self.Power.Value:SetTextColor(1, 1, 1) + self.Power.Value:SetJustifyH('LEFT') + self.Power.Value.frequentUpdates = 0.1 + + if (Settings.Units.Player.ShowPowerText) then + if (unit == 'player') then + local _, power = UnitPowerType(unit) + local c = PowerBarColor[power] + self:Tag(self.Power.Value, '[LanPower]') + self.Power.Value:SetTextColor(c.r, c.g, c.b) + end + else + if (unit == 'player') then + self.Power.Value:Hide() + self.Power.Value.Show = self.Power.Value.Hide + end + end + + if (Settings.Units.Target.ShowPowerText) then + if (unit == 'target') then + local _, power = UnitPowerType(unit) + local c = PowerBarColor[power] + self:Tag(self.Power.Value, '[LanPower]') + self.Power.Value:SetTextColor(c.r, c.g, c.b) + end + else + if (unit == 'target') then + self.Power.Value:Hide() + self.Power.Value.Show = self.Power.Value.Hide + end + end + + self.PostUpdatePower = UpdatePower + + if (unit == 'targettarget') then + self.Power:Hide() + self.Power.Show = self.Power.Hide + self.Health:SetAllPoints(self) + end + + if (unit == 'focus') then + self.Power:Hide() + self.Power.Show = self.Power.Hide + self.Health:SetAllPoints(self) + self.Health:SetOrientation('VERTICAL') + else + self.Health:SetOrientation('HORIZONTAL') + end + + -- Improve border drawing + self.Overlay = CreateFrame('Frame', nil, self) + self.Overlay:SetAllPoints(self) + self.Overlay:SetFrameLevel(self.Health:GetFrameLevel() + (self.Power and 3 or 2)) + + -- Now, to hammer out our castbars + if (Settings.Show.CastBars) then + if (unit == 'player') then + self.Castbar = CreateFrame('StatusBar', '$parentCastBar', self) + self.Castbar:SetStatusBarTexture(Settings.Media.StatusBar) + self.Castbar:SetScale(Settings.CastBars.Player.Scale) + self.Castbar:SetStatusBarColor(unpack(Settings.CastBars.Player.Color)) + + self.Border = CreateFrame('Frame', nil, self.Castbar) + self.Border:SetAllPoints(self.Castbar) + self.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) + + AddBorder(self.Border, Settings.Media.BorderSize) + + self.Castbar:SetHeight(Settings.CastBars.Player.Height) + self.Castbar:SetWidth(Settings.CastBars.Player.Width) + self.Castbar:SetParent(self) + self.Castbar:SetPoint(unpack(Settings.CastBars.Player.Position)) + + self.Castbar.Bg = self.Castbar:CreateTexture('$parentCastBarBackground', 'BACKGROUND') + self.Castbar.Bg:SetAllPoints(self.Castbar) + self.Castbar.Bg:SetTexture(Settings.Media.StatusBar) + self.Castbar.Bg:SetVertexColor(.1, .1, .1, .7) + + self.Castbar.Text = self.Castbar:CreateFontString('$parentCastBarText', 'OVERLAY') + self.Castbar.Text:SetFont(Settings.Media.Font, 13) + self.Castbar.Text:SetShadowOffset(1, -1) + self.Castbar.Text:SetPoint('LEFT', self.Castbar, 'LEFT', 2, 0) + self.Castbar.Text:SetHeight(Settings.Media.FontSize) + self.Castbar.Text:SetWidth(130) + self.Castbar.Text:SetJustifyH('LEFT') + self.Castbar.Text:SetParent(self.Castbar) + self.Castbar.CustomTimeText = function(self, duration) + self.Time:SetFormattedText('%.1f/%.1f', duration, self.max) + end + + self.Castbar.Time = self.Castbar:CreateFontString('$parentCastBarTime', 'OVERLAY') + self.Castbar.Time:SetFont(Settings.Media.Font, 13) + self.Castbar.Time:SetShadowOffset(1, -1) + self.Castbar.Time:SetPoint('RIGHT', self.Castbar, 'RIGHT', -2, 0) + self.Castbar.Time:SetParent(self.Castbar) + self.Castbar.Time:SetJustifyH('RIGHT') + self.Castbar.CustomDelayText = function(self, duration) + self.Time:SetFormattedText('[|cffff0000-%.1f|r] %.1f/%.1f', self.delay, duration, self.max) + end + + self.Castbar.SafeZone = self.Castbar:CreateTexture('$parentCastBarSafeZone', 'OVERLAY') + self.Castbar.SafeZone:SetTexture('Interface\\Buttons\\WHITE8x8') + self.Castbar.SafeZone:SetVertexColor(1, .5, 0, .25) + + self.PostChannelStart = UpdateChannelStart + self.PostCastStart = UpdateCastStart + elseif (unit == 'target') then + self.Castbar = CreateFrame('StatusBar', '$parentCastBar', self) + self.Castbar:SetStatusBarTexture(Settings.Media.StatusBar) + self.Castbar:SetStatusBarColor(unpack(Settings.CastBars.Target.Color)) + self.Castbar:SetWidth(Settings.CastBars.Target.Width) + self.Castbar:SetScale(Settings.CastBars.Target.Scale) + + self.Border = CreateFrame('Frame', nil, self.Castbar) + self.Border:SetAllPoints(self.Castbar) + self.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) + + AddBorder(self.Border, Settings.Media.BorderSize) + + self.Castbar:SetHeight(Settings.CastBars.Target.Height) + self.Castbar:SetParent(self) + self.Castbar:SetPoint(unpack(Settings.CastBars.Target.Position)) + + self.Castbar.Bg = self.Castbar:CreateTexture('$parentCastBarBackground', 'BORDER') + self.Castbar.Bg:SetAllPoints(self.Castbar) + self.Castbar.Bg:SetTexture(.1, .1, .1, .7) + + self.Castbar.Text = self.Castbar:CreateFontString('$parentCastBarText', 'OVERLAY') + self.Castbar.Text:SetFont(Settings.Media.Font, 13) + self.Castbar.Text:SetShadowOffset(1, -1) + self.Castbar.Text:SetPoint('LEFT', self.Castbar, 'LEFT', 2, 0) + self.Castbar.Text:SetHeight(Settings.Media.FontSize) + self.Castbar.Text:SetWidth(130) + self.Castbar.Text:SetJustifyH('LEFT') + + self.Castbar.Time = self.Castbar:CreateFontString('$parentCastBarTime', 'OVERLAY') + self.Castbar.Time:SetFont(Settings.Media.Font, 13) + self.Castbar.Time:SetShadowOffset(1, -1) + self.Castbar.Time:SetPoint('RIGHT', self.Castbar, 'RIGHT', -2, 0) + self.Castbar.CustomTimeText = function(self, duration) + if (self.casting) then + self.Time:SetFormattedText('%.1f', self.max - duration) + elseif (self.channeling) then + self.Time:SetFormattedText('%.1f', duration) + end + end + + self.PostChannelStart = UpdateChannelStart + self.PostCastStart = UpdateCastStart + end + end + + -- Now to skin and setup our Mirror Timers + for _, bar in pairs({ + 'MirrorTimer1', + 'MirrorTimer2', + 'MirrorTimer3', + }) do + for i, region in pairs({_G[bar]:GetRegions()}) do + if (region.GetTexture and region:GetTexture() == 'SolidTexture') then + region:Hide() + end + end + + MirrorBorder = CreateFrame('Frame', nil, _G[bar]) + MirrorBorder:SetAllPoints(_G[bar]) + MirrorBorder:SetFrameLevel(_G[bar]:GetFrameLevel() + 2) + AddBorder(MirrorBorder, Settings.Media.BorderSize) + + _G[bar..'Border']:Hide() + + _G[bar]:SetParent(UIParent) + _G[bar]:SetScale(1.135) + _G[bar]:SetHeight(18) + _G[bar]:SetWidth(200) + + _G[bar..'Background'] = _G[bar]:CreateTexture(bar..'Background', 'BACKGROUND', _G[bar]) + _G[bar..'Background']:SetTexture('Interface\\Buttons\\WHITE8x8') + _G[bar..'Background']:SetAllPoints(bar) + _G[bar..'Background']:SetVertexColor(0, 0, 0, .5) + + _G[bar..'Text']:SetFont(CastingBarFrameText:GetFont(), 13) + _G[bar..'Text']:ClearAllPoints() + _G[bar..'Text']:SetPoint('CENTER', MirrorTimer1StatusBar, 0, 1) + + _G[bar..'StatusBar']:SetAllPoints(_G[bar]) + end + + -- Display the names + if (unit ~= 'player') then + local name = self.Health:CreateFontString('$parentName', 'OVERLAY') + if (unit == 'targettarget' or unit == 'pet') then + name:SetPoint('CENTER') + else + name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) + name:SetJustifyH('LEFT') + end + + name:SetFont(Settings.Media.Font, Settings.Media.FontSize) + name:SetShadowOffset(1, -1) + name:SetTextColor(1, 1, 1) + name:SetWidth(130) + name:SetParent(self.Overlay) + name:SetHeight(Settings.Media.FontSize) + self.Info = name + if (unit == 'target') then + self:Tag(self.Info, '[LanLevel] [LanName]') + elseif (unit == 'focus') then + name:SetText() + else + self:Tag(self.Info, '[LanName]') + end + end + + if (isHealer) then + if (unit == 'target') then + local MHPB = CreateFrame('StatusBar', nil, self.Health) + MHPB:SetOrientation('HORIZONTAL') + MHPB:SetPoint('LEFT', self.Health:GetStatusBarTexture(), 'RIGHT', 0, 0) + MHPB:SetStatusBarTexture(Settings.Media.StatusBar) + MHPB:SetWidth(200) + MHPB:SetHeight(22) + MHPB:SetStatusBarColor(0, 1, 0.5, 0.25) + + local OHPB = CreateFrame('StatusBar', nil, self.Health) + OHPB:SetOrientation('HORIZONTAL') + OHPB:SetPoint('LEFT', MHPB:GetStatusBarTexture(), 'RIGHT', 0, 0) + OHPB:SetStatusBarTexture(Settings.Media.StatusBar) + OHPB:SetWidth(200) + OHPB:SetHeight(22) + OHPB:SetStatusBarColor(0, 1, 0, 0.25) + + self.HealPrediction = { + myBar = MHPB, + otherBar = OHPB, + maxOverflow = 1, + } + end + end + + -- Display icons + if (unit == 'player') then + self.Status = self.Health:CreateFontString(nil, 'OVERLAY') + self.Status:SetParent(self.Overlay) + self.Status:SetFont(Settings.Media.Font, Settings.Media.FontSize) + self.Status:SetPoint('LEFT', self.Health, 'TOPLEFT', 2, 2) + + self:Tag(self.Status, '[LanLeader][LanMaster]') + + self.Resting = self.Overlay:CreateTexture(nil, 'OVERLAY') + self.Resting:SetParent(self.Overlay) + self.Resting:SetPoint('CENTER', self.Health, 'BOTTOMLEFT', 0, -4) + self.Resting:SetSize(20, 20) + + self.Combat = self.Health:CreateTexture(nil, 'OVERLAY') + self.Combat:SetParent(self.Overlay) + self.Combat:SetPoint('CENTER', self.Health, 'BOTTOMRIGHT', 0, -4) + self.Combat:SetSize(24, 24) + end + + -- Aura/buff/debuff handling, update those suckers! + if (unit == 'player') then + local GAP = 6 + + self.Buffs = CreateFrame('Frame', nil, self) + self.Buffs:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', 0, 10) + self.Buffs:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 0, 10) + self.Buffs:SetHeight(30) + + self.Buffs['growth-x'] = 'LEFT' + self.Buffs['growth-y'] = 'UP' + self.Buffs['initialAnchor'] = 'BOTTOMRIGHT' + self.Buffs['num'] = math.floor((Settings.Units.Player.Width - 4 + GAP) / (30 + GAP)) + self.Buffs['size'] = 30 + self.Buffs['spacing-x'] = GAP + self.Buffs['spacing-y'] = GAP + + self.Buffs.CustomFilter = CustomAuraFilter + self.Buffs.PostCreateIcon = PostCreateAuraIcon + self.Buffs.PostUpdateIcon = PostUpdateAuraIcon + + self.Buffs.parent = self + elseif (unit == 'target') then + local GAP = 6 + +-- local MAX_ICONS = math.floor((Settings.Units.Target.Width - 4 + GAP) / (Settings.Units.Target.Height + GAP)) - 1 + local MAX_ICONS = 10 +-- local NUM_BUFFS = math.max(2, math.floor(MAX_ICONS * 0.4)) + local NUM_BUFFS = 4 +-- local NUM_DEBUFFS = math.min(MAX_ICONS - 1, math.floor(MAX_ICONS * 0.8)) + local NUM_DEBUFFS = 6 + + self.Debuffs = CreateFrame('Frame', nil, self) + + self.Debuffs['growth-x'] = 'RIGHT' + self.Debuffs['growth-y'] = 'UP' + self.Debuffs['initialAnchor'] = 'BOTTOMLEFT' + self.Debuffs['num'] = NUM_DEBUFFS + self.Debuffs['showType'] = false + self.Debuffs['size'] = 30 + self.Debuffs['spacing-x'] = GAP + self.Debuffs['spacing-y'] = GAP * 2 + + self.Debuffs:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', 0, 10) + self.Debuffs:SetWidth((Settings.Units.Target.Height * NUM_DEBUFFS) + (GAP * (NUM_DEBUFFS - 1))) + self.Debuffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) + + self.Debuffs.CustomFilter = CustomAuraFilter + self.Debuffs.PostCreateIcon = PostCreateAuraIcon + self.Debuffs.PostUpdateIcon = PostUpdateAuraIcon + + self.Debuffs.parent = self + + self.Buffs = CreateFrame('Frame', nil, self) + + self.Buffs['growth-x'] = 'LEFT' + self.Buffs['growth-y'] = 'UP' + self.Buffs['initialAnchor'] = 'BOTTOMRIGHT' + self.Buffs['num'] = NUM_BUFFS + self.Buffs['showType'] = false + self.Buffs['size'] = 30 + self.Buffs['spacing-x'] = GAP + self.Buffs['spacing-y'] = GAP * 2 + + self.Buffs:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 0, 10) + self.Buffs:SetWidth((Settings.Units.Target.Height * NUM_BUFFS) + (GAP * (NUM_BUFFS - 1))) + self.Buffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) + + self.Buffs.CustomFilter = CustomAuraFilter + self.Buffs.PostCreateIcon = PostCreateAuraIcon + self.Buffs.PostUpdateIcon = PostUpdateAuraIcon + + self.Buffs.parent = self + end + + -- DebuffHighlight Support + self.DebuffHighlightBackdrop = false + self.DebuffHighlightFilter = false + + -- Various oUF plugins support + if (unit == 'player') then + + -- oUF_RuneBar support + if(IsAddOnLoaded('oUF_RuneBar') and class == 'DEATHKNIGHT') then + self.RuneBar = {} + for i = 1, 6 do + self.RuneBar[i] = CreateFrame('StatusBar', '$parentRuneBar', self) + if(i == 1) then + self.RuneBar[i]:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', 0, -1) + else + self.RuneBar[i]:SetPoint('TOPLEFT', self.RuneBar[i-1], 'TOPRIGHT', 1, 0) + end + self.RuneBar[i]:SetStatusBarTexture(Settings.Media.StatusBar) + self.RuneBar[i]:SetHeight(5) + self.RuneBar[i]:SetWidth(200/6 - .85) + self.RuneBar[i]:SetBackdrop(backdrop) + self.RuneBar[i]:SetBackdropColor(0, 0, 0, .5) + self.RuneBar[i]:SetMinMaxValues(0, 1) + + self.RuneBar[i].bg = self.RuneBar[i]:CreateTexture('$parentRuneBackground', 'BORDER') + self.RuneBar[i].bg:SetAllPoints(self.RuneBar[i]) + self.RuneBar[i].bg:SetTexture(.1, .1, .1) + end + end + + -- DruidPower Support + if (select(2, UnitClass('player')) == 'DRUID') then + self.Druid = CreateFrame('Frame') + self.Druid:SetParent(self) + self.Druid:SetFrameStrata('LOW') + + self.Druid.Power = CreateFrame('StatusBar', nil, self) + self.Druid.Power:SetPoint('TOP', self.Power, 'BOTTOM', 0, -7) + self.Druid.Power:SetStatusBarTexture(Settings.Media.StatusBar) + self.Druid.Power:SetFrameStrata('LOW') + self.Druid.Power:SetFrameLevel(self.Druid:GetFrameLevel() - 1) + self.Druid.Power:SetHeight(10) + self.Druid.Power:SetWidth(200) + self.Druid.Power:SetBackdrop(backdrop) + self.Druid.Power:SetBackdropColor(0, 0, 0, 0.5) + + self.DruidBorder = CreateFrame('Frame', nil, self.Druid.Power) + self.DruidBorder:SetAllPoints(self.Druid.Power) + self.DruidBorder:SetFrameLevel(self.Druid.Power:GetFrameLevel() + 2) + + AddBorder(self.DruidBorder, Settings.Media.BorderSize) + + table.insert(self.__elements, UpdateDruidPower) + self:RegisterEvent('UNIT_MANA', UpdateDruidPower) + self:RegisterEvent('UNIT_RAGE', UpdateDruidPower) + self:RegisterEvent('UNIT_ENERGY', UpdateDruidPower) + self:RegisterEvent('UPDATE_SHAPESHIFT_FORM', UpdateDruidPower) + end + + -- Eclipse Bar Support + if (select(2, UnitClass('player')) == 'DRUID') then + local EclipseBar = CreateFrame('Frame', nil, self) + EclipseBar:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', 0, -10) + EclipseBar:SetPoint('TOPRIGHT', self, 'BOTTOMRIGHT', 0, -10) + EclipseBar:SetSize(200, 10) + EclipseBar:SetBackdrop(backdrop) + EclipseBar:SetBackdropColor(0, 0, 0, 0.6) + + local EclipseBarBorder = CreateFrame('Frame', nil, EclipseBar) + EclipseBarBorder:SetAllPoints(EclipseBar) + EclipseBarBorder:SetFrameLevel(EclipseBar:GetFrameLevel() + 2) + + AddBorder(EclipseBarBorder, Settings.Media.BorderSize) + + local LunarBar = CreateFrame('StatusBar', nil, EclipseBar) + LunarBar:SetPoint('LEFT', EclipseBar, 'LEFT', 0, 0) + LunarBar:SetSize(200, 10) + LunarBar:SetStatusBarTexture(Settings.Media.StatusBar) + LunarBar:SetStatusBarColor(1, 1, 1) + EclipseBar.LunarBar = LunarBar + + local SolarBar = CreateFrame('StatusBar', nil, EclipseBar) + SolarBar:SetPoint('LEFT', LunarBar:GetStatusBarTexture(), 'RIGHT', 0, 0) + SolarBar:SetSize(200, 10) + SolarBar:SetStatusBarTexture(Settings.Media.StatusBar) + SolarBar:SetStatusBarColor(1, 3/5, 0) + EclipseBar.SolarBar = SolarBar + + local EclipseBarText = EclipseBarBorder:CreateFontString(nil, 'OVERLAY') + EclipseBarText:SetPoint('CENTER', EclipseBarBorder, 'CENTER', 0, 0) + EclipseBarText:SetFont(Settings.Media.Font, Settings.Media.FontSize, 'OUTLINE') + self:Tag(EclipseBarText, '[pereclipse]%') + + self.EclipseBar = EclipseBar + end + + -- Soul Shard Support + if (select(2, UnitClass('player')) == 'WARLOCK') then + local Shards = self:CreateFontString(nil, 'OVERLAY') + Shards:SetPoint('CENTER', self, 'RIGHT', 17, -2) + Shards:SetFont(Settings.Media.Font, 24, 'OUTLINE') + Shards:SetJustifyH('CENTER') + self:Tag(Shards, '[LanShards]') + end + + -- Holy Power Support + if (select(2, UnitClass('player')) == 'PALADIN') then + local HolyPower = self:CreateFontString(nil, 'OVERLAY') + HolyPower:SetPoint('CENTER', self, 'RIGHT', 17, -2) + HolyPower:SetFont(Settings.Media.Font, 24, 'OUTLINE') + HolyPower:SetJustifyH('CENTER') + self:Tag(HolyPower, '[LanHolyPower]') + end + + -- Combo points display + if (select(2, UnitClass('player')) == 'ROGUE') or (select(2, UnitClass('player')) == 'DRUID') then + local ComboPoints = self:CreateFontString(nil, 'OVERLAY') + ComboPoints:SetPoint('CENTER', self, 'RIGHT', 17, -2) + ComboPoints:SetFont(Settings.Media.Font, 24, 'OUTLINE') + ComboPoints:SetJustifyH('CENTER') + self:Tag(ComboPoints, '[LanCombo]') + end + end + + -- Custom sizes for our frames + if (isSingle) then + if (unit == 'player') then + self:SetSize(Settings.Units.Player.Width, Settings.Units.Player.Height) + elseif (unit == 'target') then + self:SetSize(Settings.Units.Target.Width, Settings.Units.Target.Height) + elseif (unit == 'pet') then + self:SetSize(Settings.Units.Pet.Width, Settings.Units.Pet.Height) + end + + if (Settings.Show.ToT) then + if (unit == 'targettarget') then + self:SetSize(Settings.Units.ToT.Width, Settings.Units.ToT.Height) + end + end + + if (Settings.Show.Focus) then + if (unit == 'focus') then + self:SetSize(Settings.Units.Focus.Width, Settings.Units.Focus.Height) + end + end + end + + -- Hardcore border action! + AddBorder(self, Settings.Media.BorderSize) + self:SetBorderParent(self.Overlay) + + self.UpdateBorder = UpdateBorder + + -- Dispel highlight support + self.DispelHighlight = UpdateDispelHighlight + + -- Threat highlight support + self.threatLevel = 0 + self.ThreatHighlight = UpdateThreatHighlight + + return self +end + +-- First build the group style +local function StylishGroup(self, unit) + self.menu = CreateDropDown + self.ignoreHealComm = true + + self:EnableMouse(true) + self:RegisterForClicks('AnyUp') + + if (Settings.Show.Party) then + if (Settings.Units.Party.Healer) then + self:SetSize(100, 35) + else + self:SetSize(Settings.Units.Party.Width, Settings.Units.Party.Height) + end + end + + -- Health bar display for group frames + self.Health = CreateFrame('StatusBar', '$parentHealthBar', self) + self.Health:SetStatusBarTexture(Settings.Media.StatusBar, 'ARTWORK') + + self.Health:SetParent(self) + self.Health:SetPoint('TOPRIGHT') + self.Health:SetPoint('BOTTOMLEFT', 0, -1) + + self:SetBackdrop(backdrop) + self:SetBackdropColor(0, 0, 0, .5) + + self.Health.PostUpdate = UpdateGroupHealth + + if (Settings.Units.Party.Health.ClassColor) then + self.Health.colorClass = true + end + + self.Health.Smooth = true + + -- Health bar background display for group frames + self.Health.Background = self.Health:CreateTexture('$parentHealthBackground', 'BORDER') + self.Health.Background:SetAllPoints(self.Health) + + -- Background Color + self.Health.Background:SetTexture(.08, .08, .08) + + -- Health value settings + self.Health.Value = self.Health:CreateFontString('$parentHealthValue', 'OVERLAY') + self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) + + -- Improve border drawing + self.Overlay = CreateFrame('Frame', nil, self) + self.Overlay:SetAllPoints(self) + self.Overlay:SetFrameLevel(self.Health:GetFrameLevel() + (self.Power and 3 or 2)) + + -- Display group names + self.Name = self.Health:CreateFontString('$parentName', 'OVERLAY') + self.Name:SetPoint('LEFT', self.Health, 5, 1) + self.Name:SetFont(Settings.Media.Font, 13) + self.Name:SetShadowOffset(1, -1) + self:Tag(self.Name, '|cffffffff[LanName]|r') + + if (Settings.Units.Party.Healer) then + self.Name:SetPoint('CENTER', self.Health) + end + + if isHealer then + local MHPB = CreateFrame('StatusBar', nil, self.Health) + MHPB:SetOrientation('HORIZONTAL') + MHPB:SetPoint('LEFT', self.Health:GetStatusBarTexture(), 'RIGHT', 0, 0) + MHPB:SetStatusBarTexture(Settings.Media.StatusBar) + MHPB:SetWidth(100) + MHPB:SetHeight(35) + MHPB:SetStatusBarColor(0, 1, 0.5, 0.25) + + local OHPB = CreateFrame('StatusBar', nil, self.Health) + OHPB:SetOrientation('HORIZONTAL') + OHPB:SetPoint('LEFT', MHPB:GetStatusBarTexture(), 'RIGHT', 0, 0) + OHPB:SetStatusBarTexture(Settings.Media.StatusBar) + OHPB:SetWidth(100) + OHPB:SetHeight(35) + OHPB:SetStatusBarColor(0, 1, 0, 0.25) + + self.HealPrediction = { + myBar = MHPB, + otherBar = OHPB, + maxOverflow = 1, + } + end + + if unit == 'party' or unit == 'target' then + self.Status = self.Overlay:CreateFontString(nil, 'OVERLAY') + self.Status:SetFont(Settings.Media.Font, Settings.Media.FontSize) + self.Status:SetPoint('RIGHT', self.Health, 'BOTTOMRIGHT', -2, 0) + + self:Tag(self.Status, '[LanMaster][LanLeader]') + end + + -- Raid Icons + self.RaidIcon = self.Overlay:CreateTexture('$parentRaidIcon', 'ARTWORK') + self.RaidIcon:SetHeight(18) + self.RaidIcon:SetWidth(18) + self.RaidIcon:SetPoint('CENTER', self.Overlay, 'TOP') + self.RaidIcon:SetTexture('Interface\\TargettingFrame\\UI-RaidTargetingIcons') + + -- LFD Role + self.LFDRole = self.Overlay:CreateTexture(nil, 'OVERLAY') + self.LFDRole:SetPoint('CENTER', self, 'RIGHT', 2, 0) + self.LFDRole:SetSize(16, 16) + + -- Buffs + local GAP = 6 + + self.Buffs = CreateFrame('Frame', nil, self) + self.Buffs:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 0, 10) + self.Buffs:SetHeight(Settings.Units.Party.Height) + self.Buffs:SetWidth((Settings.Units.Party.Height * 4) + (GAP * 3)) + + self.Buffs['growth-x'] = 'LEFT' + self.Buffs['growth-y'] = 'DOWN' + self.Buffs['initialAnchor'] = 'TOPRIGHT' + self.Buffs['num'] = 4 + self.Buffs['size'] = Settings.Units.Party.Height + self.Buffs['spacing-x'] = GAP + self.Buffs['spacing-y'] = GAP + + self.Buffs.CustomFilter = CustomAuraFilter + self.Buffs.PostCreateIcon = PostCreateAuraIcon + self.Buffs.PostUpdateIcon = PostUpdateAuraIcon + + self.Buffs.parent = self + + -- Range-finding support + self.Range = { + insideAlpha = 1, + outsideAlpha = .3, + } + + self.SpellRange = true + + -- Hardcore border action! + AddBorder(self, Settings.Media.BorderSize) + self:SetBorderParent(self.Overlay) + + self.UpdateBorder = UpdateBorder + + -- Dispel highlight support + self.DispelHighlight = UpdateDispelHighlight + + -- Threat highlight support + self.threatLevel = 0 + self.ThreatHighlight = UpdateThreatHighlight + + return self +end + +-- Now the raid style +local function StylishRaid(self, unit) + self.menu = CreateDropDown + self.ignoreHealComm = true + + self:EnableMouse(true) + self:RegisterForClicks('AnyUp') + + if (Settings.Show.Raid) then + if (Settings.Units.Raid.Healer) then + self:SetSize(75, 35) + else + self:SetSize(Settings.Units.Raid.Width, Settings.Units.Raid.Height) + end + end + + -- Health bar display for group frames + self.Health = CreateFrame('StatusBar', '$parentHealthBar', self) + self.Health:SetStatusBarTexture(Settings.Media.StatusBar, 'ARTWORK') + + self.Health:SetParent(self) + self.Health:SetPoint('TOPRIGHT') + self.Health:SetPoint('BOTTOMLEFT', 0, -1) + + self:SetBackdrop(backdrop) + self:SetBackdropColor(0, 0, 0, .5) + + self.Health.PostUpdate = UpdateRaidHealth + + if (Settings.Units.Raid.Health.ClassColor) then + self.Health.colorClass = true + end + + self.Health.Smooth = true + + -- Health bar background display for group frames + self.Health.Background = self.Health:CreateTexture('$parentHealthBackground', 'BORDER') + self.Health.Background:SetAllPoints(self.Health) + + -- Background Color + self.Health.Background:SetTexture(.08, .08, .08) + + -- Health value settings + self.Health.Value = self.Health:CreateFontString('$parentHealthValue', 'OVERLAY') + self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) + + -- Improve border drawing + self.Overlay = CreateFrame('Frame', nil, self) + self.Overlay:SetAllPoints(self) + self.Overlay:SetFrameLevel(self.Health:GetFrameLevel() + (self.Power and 3 or 2)) + + -- Display group names + if (Settings.Units.Raid.Healer) then + self.Name = self.Health:CreateFontString('$parentName', 'OVERLAY') + self.Name:SetPoint('TOP', 0, -2) + self.Name:SetFont(Settings.Media.Font, 13) + self.Name:SetShadowOffset(1, -1) + self.Name:SetJustifyH('CENTER') + self:Tag(self.Name, '|cffffffff[LanRaidName]|r') + else + self.Name = self.Health:CreateFontString('$parentName', 'OVERLAY') + self.Name:SetPoint('LEFT', self.Health, 5, 1) + self.Name:SetFont(Settings.Media.Font, 13) + self.Name:SetShadowOffset(1, -1) + self:Tag(self.Name, '|cffffffff[LanName]|r') + self.Health:SetOrientation('HORIZONTAL') + end + + if isHealer then + local MHPB = CreateFrame('StatusBar', nil, self.Health) + MHPB:SetPoint('BOTTOM', self.Health:GetStatusBarTexture(), 'TOP', 0, 0) + MHPB:SetStatusBarTexture(Settings.Media.StatusBar) + MHPB:SetWidth(75) + MHPB:SetHeight(35) + MHPB:SetStatusBarColor(0, 1, 0.5, 0.25) + + local OHPB = CreateFrame('StatusBar', nil, self.Health) + OHPB:SetPoint('BOTTOM', MHPB:GetStatusBarTexture(), 'TOP', 0, 0) + OHPB:SetStatusBarTexture(Settings.Media.StatusBar) + OHPB:SetWidth(75) + OHPB:SetHeight(35) + OHPB:SetStatusBarColor(0, 1, 0, 0.25) + + self.HealPrediction = { + myBar = MHPB, + otherBar = OHPB, + maxOverflow = 1, + } + end + + -- Status Icons Display + self.Status = self.Overlay:CreateFontString(nil, 'OVERLAY') + self.Status:SetFont(Settings.Media.Font, Settings.Media.FontSize) + self.Status:SetPoint('RIGHT', self.Health, 'BOTTOMRIGHT', -2, 0) + + self:Tag(self.Status, '[LanMaster][LanLeader]') + + -- Raid Icons + self.RaidIcon = self.Overlay:CreateTexture('$parentRaidIcon', 'ARTWORK') + self.RaidIcon:SetHeight(18) + self.RaidIcon:SetWidth(18) + self.RaidIcon:SetPoint('CENTER', self.Overlay, 'TOP') + self.RaidIcon:SetTexture('Interface\\TargettingFrame\\UI-RaidTargetingIcons') + + -- Range-finding support + self.Range = { + insideAlpha = 1, + outsideAlpha = .3, + } + + self.SpellRange = true + + -- Hardcore border action! + AddBorder(self, Settings.Media.BorderSize) + self:SetBorderParent(self.Overlay) + + self.UpdateBorder = UpdateBorder + + -- Dispel highlight support + self.DispelHighlight = UpdateDispelHighlight + + return self +end + +-- Now, actually bring it all together by actually spawning the frames +-- First spawn the group and raid stuff +oUF:RegisterStyle('oUF_Lanerra_Group', StylishGroup) +oUF:RegisterStyle('oUF_Lanerra_Raid', StylishRaid) + +-- First up are the group frames +oUF:Factory(function(self) + if (Settings.Units.Party.Healer) then + local group = oUF:SpawnHeader('oUF_Lanerra_Group', nil, nil, 'showParty', true, 'showFocus', true, 'columnSpacing', 10, 'unitsPerColumn', 1, 'maxColumns', 5, 'columnAnchorPoint', 'LEFT', 'groupFilter', i) + group:SetPoint('CENTER', UIParent, 0, -240) + else + local group = oUF:SpawnHeader('oUF_Lanerra_Group', nil, nil, 'showParty', true, 'showPlayer', true, 'showFocus', true, 'yOffset', -10) + if (IsAddOnLoaded('Skada')) then + group:SetPoint(unpack(Settings.Units.Party.TinyPosition)) + else + group:SetPoint(unpack(Settings.Units.Party.Position)) + end + end +end) + +-- Now for the raid frames +oUF:Factory(function(self) + self:SetActiveStyle('oUF_Lanerra_Raid') + + if (Settings.Units.Raid.Healer) then + raid = oUF:SpawnHeader('oUF_Lanerra_Raid', nil, nil, 'showPlayer', true, 'showRaid', true, 'xOffset', 10, 'yOffset', -5, 'point', 'LEFT', 'groupFilter', '1,2,3,4,5', 'groupingOrder', '1,2,3,4,5', 'groupBy', 'GROUP', 'maxColumns', 10, 'unitsPerColumn', 5, 'columnSpacing', 10, 'columnAnchorPoint', 'TOP') + raid:SetPoint('CENTER', UIParent, 0, -310) + + if (Settings.Units.Raid.Healer) then + local RaidShift, raid = false + do + local UpdateRaid = CreateFrame'Frame' + UpdateRaid:RegisterEvent('RAID_ROSTER_UPDATE') + UpdateRaid:SetScript('OnEvent', function(self) + if RaidShift == false then return end + if(InCombatLockdown()) then + self:RegisterEvent('PLAYER_REGEN_ENABLED') + else + self:UnregisterEvent('PLAYER_REGEN_ENABLED') + if (GetNumRaidMembers() < 26 and GetNumRaidMembers() > 10) then + raid:SetPoint('CENTER', UIParent, -105, -200) + elseif (GetNumRaidMembers() < 11) then + raid:SetPoint('CENTER', UIParent, -21, -200) + end + end + end) + end + end + else + raid = {} + for i = 1, 5 do + raid[i] = oUF:SpawnHeader('oUF_Lanerra_Raid'..i, nil, nil, 'groupFilter', i, 'showRaid', true, 'showParty', true, 'showFocus', true, 'yOffset', -10) + table.insert(raid, raid[i]) + if (i == 1) then + if (IsAddOnLoaded('TinyDPS')) then + raid[i]:SetPoint(unpack(Settings.Units.Raid.TinyPosition)) + else + raid[i]:SetPoint(unpack(Settings.Units.Raid.Position)) + end + else + raid[i]:SetPoint('TOP', raid[i-1], 'BOTTOM', 0, -10) + end + raid[i]:Show() + end + end +end) + +-- Now all the solo stuff +oUF:RegisterStyle('oUF_Lanerra', Stylish) +oUF:Factory(function(self) + self:SetActiveStyle('oUF_Lanerra') + self:Spawn('player', 'oUF_Lanerra_Player'):SetPoint(unpack(Settings.Units.Player.Position)) + self:Spawn('target', 'oUF_Lanerra_Target'):SetPoint(unpack(Settings.Units.Target.Position)) + self:Spawn('targettarget', 'oUF_Lanerra_ToT'):SetPoint(unpack(Settings.Units.ToT.Position)) + self:Spawn('pet', 'oUF_Lanerra_Pet'):SetPoint(unpack(Settings.Units.Pet.Position)) + self:Spawn('focus', 'oUF_Lanerra_Focus'):SetPoint(unpack(Settings.Units.Focus.Position)) +end) + +-- Handling, whether the Raid- or the Party-frame is shown +-- FIX: Quick'n'dirty fix until the oUF-conditions work again +local partyToggle = CreateFrame('Frame') +partyToggle:RegisterEvent('PLAYER_LOGIN') +partyToggle:RegisterEvent('RAID_ROSTER_UPDATE') +partyToggle:RegisterEvent('PARTY_LEADER_CHANGED') +partyToggle:RegisterEvent('PARTY_MEMBERS_CHANGED') +partyToggle:SetScript('OnEvent', function(self) + if(InCombatLockdown()) then + self:RegisterEvent('PLAYER_REGEN_ENABLED') + else + self:UnregisterEvent('PLAYER_REGEN_ENABLED') + + --[[ This results in the following behavior: If you're in a raid, the party frame will be hidden, no matter how many members + your raid already has. This means, the party will be hidden if the party leader clicks the button to create a raid. + If you want to switch to raid view later (meaning, if the members no longer fit into the party frame), you may change the following line accordingly.--]] + + if (Settings.Units.Raid.Healer) and (Settings.Units.Party.Healer) then + if(GetNumRaidMembers() > 0) then + _G['oUF_Lanerra_Group']:Hide() + _G['oUF_Lanerra_Raid']:Show() + else + _G['oUF_Lanerra_Group']:Show() + _G['oUF_Lanerra_Raid']:Hide() + end + else + if(GetNumRaidMembers() > 0) then + _G['oUF_Lanerra_Group']:Hide() + _G['oUF_Lanerra_Raid1']:Show() + _G['oUF_Lanerra_Raid2']:Show() + _G['oUF_Lanerra_Raid3']:Show() + _G['oUF_Lanerra_Raid4']:Show() + _G['oUF_Lanerra_Raid5']:Show() + else + _G['oUF_Lanerra_Group']:Show() + _G['oUF_Lanerra_Raid1']:Hide() + _G['oUF_Lanerra_Raid2']:Hide() + _G['oUF_Lanerra_Raid3']:Hide() + _G['oUF_Lanerra_Raid4']:Hide() + _G['oUF_Lanerra_Raid5']:Hide() + end + end + end +end) \ No newline at end of file diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc new file mode 100644 index 0000000..915afdf --- /dev/null +++ b/oUF_Lanerra.toc @@ -0,0 +1,21 @@ +## Interface: 40000 +## Version: 1.14 + +## Title: oUF_Lanerra +## Notes: oUF layout by Lanerra +## Author: Lanerra +## X-Copyright: Copyright © 2010 Lanerra. +## X-License: See LICENSE file for license terms. +## RequiredDeps: oUF +## OptionalDeps: oUF_DebuffHighlight, oUF_RuneBar + + +modules\Smooth.lua +modules\DispellableDebuffs.lua +modules\Threat.lua + +oUF_Lanerra.lua +oUF_Lanerra_Config.lua +AuraFilter.lua +Borders.lua +Tags.lua \ No newline at end of file diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua new file mode 100644 index 0000000..afb2558 --- /dev/null +++ b/oUF_Lanerra_Config.lua @@ -0,0 +1,124 @@ +-- Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. +Settings = { + Show = { + CastBars = true, + Focus = true, + ToT = true, + Party = true, + Raid = true, + }, + Media = { + Border = 'Interface\\Addons\\oUF_Lanerra\\media\\borderTexture.tga', + StatusBar = 'Interface\\Addons\\oUF_Lanerra\\media\\statusbarTexture.tga', + Font = 'Interface\\Addons\\oUF_Lanerra\\media\\font.ttf', + FontSize = 15, + BorderSize = 14, + BorderColor = { .65, .65, .65 }, + }, + Units = { + Player = { + Height = 30, + Width = 200, + Position = {'CENTER', UIParent, -325, -175}, + Health = { + Percent = false, + Deficit = false, + Current = true, + }, + ShowPowerText = true, + }, + Pet = { + Height = 30, + Width = 80, + Position = {'CENTER', UIParent, -485, -175}, + Health = { + Percent = false, + Deficit = false, + Current = false, + }, + }, + Target = { + Height = 30, + Width = 200, + Position = {'CENTER', UIParent, 325, -175}, + Health = { + Percent = true, + Deficit = false, + Current = false, + }, + ShowPowerText = false, + }, + ToT = { + Height = 30, + Width = 80, + Position = {'CENTER', UIParent, 485, -175}, + Health = { + Percent = false, + Deficit = false, + Current = false, + }, + }, + Focus = { + Height = 30, + Width = 30, + Position = {'CENTER', UIParent, 0, -175}, + Health = { + Percent = false, + Deficit = false, + Current = false, + }, + }, + Party = { + Height = 20, + Width = 100, + TinyPosition = {'TOPLEFT', UIParent, 25, -210}, + Position = {'TOPLEFT', UIParent, 25, -25}, + Health = { + Percent = true, + Deficit = false, + Current = false, + ClassColor = true, + }, + HidePower = true, -- Reserved for future use + Healer = true, + }, + Raid = { + Height = 18, + Width = 100, + TinyPosition = {'TOPLEFT', UIParent, 25, -210}, + Position = {'TOPLEFT', UIParent, 25, -25}, + Health = { + Percent = false, + Deficit = true, + Current = false, + ClassColor = true, + }, + HidePower = true, -- Reserved for future use + Healer = true, + }, + }, + CastBars = { + Player = { + Show = true, + Height = 25, + Width = 200, + Scale = 1, + Position = {'CENTER', UIParent, -325, -232}, + ClassColor = false, + SafeZone = true, + Latency = false, + Color = {.25, .25, .25}, + }, + Target = { + Show = true, + Height = 25, + Width = 200, + Scale = 1, + Position = {'CENTER', UIParent, 325, -232}, + ClassColor = false, + Color = {.25, .25, .25}, + InterruptHighlight = false, + InterruptColor = {1, 0, 1}, + }, + }, +} \ No newline at end of file -- 1.7.9.5 From 3205ca129ae37be16eb81624ffc89bbab381f3a5 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Wed, 23 Feb 2011 15:12:05 -0800 Subject: [PATCH 03/23] Updated README --- README | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README b/README index e69de29..e04095f 100644 --- a/README +++ b/README @@ -0,0 +1,42 @@ +A simple unit frame addon based off of the oUF core framework. The design and purpose of these unit +frames is to allow as much configuration as possible via an attached configuration lua to alter +certain settings of the unit frames, while remaining as light on memory usage as possible. + +It also has a toggle-able 'healer mode' that alters the location of the raid and party frames +dependent on whether the "Healer" option has been toggled to true in the config for party or raid. + +----------- +Please Note +----------- + +Some knowledge of Lua is required for customization of these frames beyond the configuration file. +To alter options in the configuration file, simple open it in your favorite text editor and change +options from false to true, or vice versa. Also of note, for the displaying of health, more than +one health option cannot be functional at a time so please choose only one. + +--------------- +Supported Units +--------------- + + * Player + * Target + * Pet + * Target of Target + * Focus + * Party + * Raid + +----------------- +Supported Modules +----------------- + + * DebuffHighlight + * DruidPower + * Smooth + * RuneBar + +---------------------- +Currently Known Issues +---------------------- + + * None currently known. \ No newline at end of file -- 1.7.9.5 From 54447f8c09efc5587cb02763916921115cce39ad Mon Sep 17 00:00:00 2001 From: Lanerra Date: Fri, 25 Feb 2011 03:29:24 -0800 Subject: [PATCH 04/23] Fixed pet happiness updating (hopefully) not showing properly on the pet frame. Also, improved health and name updates to fix some display issues. --- Tags.lua | 1 + oUF_Lanerra.lua | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Tags.lua b/Tags.lua index b271ecb..35ba018 100644 --- a/Tags.lua +++ b/Tags.lua @@ -1,4 +1,5 @@ -- Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. + -- Define some custom oUF tags oUF.Tags['LanPvPTime'] = function(unit) return UnitIsPVP(unit) and not IsPVPTimerRunning() and '*' or IsPVPTimerRunning() and ('%d:%02d'):format((GetPVPTimer() / 1000) / 60, (GetPVPTimer() / 1000) % 60) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 4dcfe68..2851740 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -278,7 +278,6 @@ end local UpdatePower = function(Power, unit, min, max) local self = Power:GetParent() - local UnitHappiness = self.colors.happiness[GetPetHappiness()] local _, PowerType, altR, altG, altB = UnitPowerType(unit) local UnitPower = PowerBarColor[PowerType] @@ -288,8 +287,9 @@ local UpdatePower = function(Power, unit, min, max) Power.Value:SetText(min) end - if (unit == 'pet' and GetPetHappiness()) then - Power:SetStatusBarColor(UnitHappiness[1], UnitHappiness[2], UnitHappiness[3]) + if (unit == 'pet' and SPELL_POWER_HAPPINESS and GetPetHappiness()) then + local UnitHappiness = self.colors.happiness[GetPetHappiness()] + Power:SetStatusBarColor(UnitHappiness.r, UnitHappiness.g, UnitHappiness.b) else Power:SetStatusBarColor(UnitPower.r, UnitPower.g, UnitPower.b) end @@ -445,7 +445,7 @@ local Stylish = function(self, unit, isSingle) -- Turn on the smoothness self.Health.Smooth = true - self.Health.frequentUpdates = true + self.Health.frequentUpdates = 0.2 self.Health:SetParent(self) self.Health:SetPoint('TOP') @@ -481,8 +481,8 @@ local Stylish = function(self, unit, isSingle) self.Power.colorClass = true self.Power.colorReaction = true - -- We like to keep things smooth here - self.Power.frequentUpdates = true + -- We like to keep things smooth around here + self.Power.frequentUpdates = 0.2 self.Power:SetParent(self) self.Power:SetPoint('BOTTOM') @@ -698,6 +698,9 @@ local Stylish = function(self, unit, isSingle) name:SetWidth(130) name:SetParent(self.Overlay) name:SetHeight(Settings.Media.FontSize) + + name.frequentUpdates = 0.2 + self.Info = name if (unit == 'target') then self:Tag(self.Info, '[LanLevel] [LanName]') @@ -1026,6 +1029,7 @@ local function StylishGroup(self, unit) end self.Health.Smooth = true + self.Health.frequentUpdates = 0.3 -- Health bar background display for group frames self.Health.Background = self.Health:CreateTexture('$parentHealthBackground', 'BORDER') @@ -1048,6 +1052,8 @@ local function StylishGroup(self, unit) self.Name:SetPoint('LEFT', self.Health, 5, 1) self.Name:SetFont(Settings.Media.Font, 13) self.Name:SetShadowOffset(1, -1) + self.Name.frequentUpdates = 0.3 + self:Tag(self.Name, '|cffffffff[LanName]|r') if (Settings.Units.Party.Healer) then @@ -1178,6 +1184,7 @@ local function StylishRaid(self, unit) end self.Health.Smooth = true + self.Health.frequentUpdates = 0.3 -- Health bar background display for group frames self.Health.Background = self.Health:CreateTexture('$parentHealthBackground', 'BORDER') @@ -1212,6 +1219,8 @@ local function StylishRaid(self, unit) self.Health:SetOrientation('HORIZONTAL') end + self.Name.frequentUpdates = 0.3 + if isHealer then local MHPB = CreateFrame('StatusBar', nil, self.Health) MHPB:SetPoint('BOTTOM', self.Health:GetStatusBarTexture(), 'TOP', 0, 0) -- 1.7.9.5 From 868abe5f9a52f836d06a3974fcb4845b0f93fcaa Mon Sep 17 00:00:00 2001 From: Lanerra Date: Fri, 4 Mar 2011 19:43:27 -0800 Subject: [PATCH 05/23] Revert color power bar by Pet Happiness change. Can't seem to get it to work so am stepping back from it for a bit to come back to it with a clear head. --- oUF_Lanerra.lua | 79 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 2851740..96434ea 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,7 +1,7 @@ --[[ - Version = 1.13 + Version = 1.14 - Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. + Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. Special thanks to P3lim for inspiration, Neav for textures and inspiration, Game92 for inspiration, and Phanx for inspiration and an inline border method @@ -10,6 +10,8 @@ ---- Lazy Stuff Goes Here! local _, ns = ... +local colors = oUF.colors + local playerClass = select(2, UnitClass('player')) local isHealer = (playerClass == 'DRUID' or playerClass == 'PALADIN' or playerClass == 'PRIEST' or playerClass == 'SHAMAN') @@ -35,8 +37,6 @@ PowerBarColor['FOCUS'] = { r = 255/255, g = 175/255, b = 0 } PowerBarColor['ENERGY'] = { r = 1, g = 1, b = 35/255 } PowerBarColor['RUNIC_POWER'] = { r = 0.45, g = 0.85, b = 1 } -local Colors = oUF.colors - -- Threat color handling oUF.colors.threat = { } for i = 1, 3 do @@ -45,10 +45,10 @@ for i = 1, 3 do end -- Debuff color handling -Colors.Debuff = { } +colors.Debuff = { } for type, color in pairs(DebuffTypeColor) do if (type ~= 'none') then - Colors.Debuff[type] = { color.r, color.g, color.b } + colors.Debuff[type] = { color.r, color.g, color.b } end end @@ -66,19 +66,19 @@ end local function UpdateBorder(self) local Threat, Debuff, Dispellable = self.threatLevel, self.debuffType, self.debuffDispellable - local Color + local color if Debuff and Dispellable then - Color = Colors.Debuff[Debuff] + color = colors.Debuff[Debuff] elseif Threat and Threat > 1 then - Color = oUF.colors.threat[Threat] + color = colors.threat[Threat] elseif Debuff then - Color = Colors.Debuff[Debuff] + color = colors.Debuff[Debuff] elseif Threat and Threat > 0 then - Color = oUF.colors.threat[Threat] + color = colors.threat[Threat] end - if Color then - self:SetBackdropBorderColor(Color[1], Color[2], Color[3], 1) + if color then + self:SetBackdropBorderColor(color[1], color[2], color[3], 1) else self:SetBackdropBorderColor(0, 0, 0, 0) end @@ -278,23 +278,40 @@ end local UpdatePower = function(Power, unit, min, max) local self = Power:GetParent() - local _, PowerType, altR, altG, altB = UnitPowerType(unit) - local UnitPower = PowerBarColor[PowerType] - if (min == 0 or UnitIsDead(unit) or UnitIsGhost(unit) or unit == 'pet' or unit == 'focus' or unit ~= 'player' or not UnitIsConnected(unit)) then Power.Value:SetText() else Power.Value:SetText(min) end - - if (unit == 'pet' and SPELL_POWER_HAPPINESS and GetPetHappiness()) then - local UnitHappiness = self.colors.happiness[GetPetHappiness()] - Power:SetStatusBarColor(UnitHappiness.r, UnitHappiness.g, UnitHappiness.b) + + local color + if UnitIsPlayer(unit) then + local _, class = UnitClass(unit) + color = colors.class[class] + Power:SetStatusBarColor(color[1], color[2], color[3], 1) + elseif UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) then + color = colors.tapped + Power:SetStatusBarColor(color[1], color[2], color[3], 1) + elseif UnitIsUnit(unit, 'pet') and GetPetHappiness() then + color = colors.happiness[GetPetHappiness()] + Power:SetStatusBarColor(color[1], color[2], color[3], 1) + elseif UnitIsEnemy(unit, "player") then + color = colors.reaction[1] + Power:SetStatusBarColor(color[1], color[2], color[3], 1) else - Power:SetStatusBarColor(UnitPower.r, UnitPower.g, UnitPower.b) + if (unit ~= 'pet') then + color = colors.reaction[UnitReaction(unit, "player") or 5] + Power:SetStatusBarColor(color[1], color[2], color[3], 1) + end end end +--~ local PetUpdatePower = function(Power, unit, min, max) +--~ local color +--~ color = colors.happiness[GetPetHappiness()] +--~ Power:SetStatusBarColor(color[1], color[2], color[3], 1) +--~ end + -- Add DruidPower support local function UpdateDruidPower(self, event, unit) if (unit and unit ~= self.unit) then @@ -475,14 +492,15 @@ local Stylish = function(self, unit, isSingle) self.Power = CreateFrame('StatusBar', '$parentPowerBar', self) self.Power:SetHeight(Settings.Units.Player.Height * .22) self.Power:SetStatusBarTexture(Settings.Media.StatusBar) - - self.Power.colorTapping = true - self.Power.colorHappiness = true + self.Power.colorClass = true - self.Power.colorReaction = true + self.Power.colorTapping = true + self.Power.colorReaction = unit ~= 'pet' + self.Power.colorHappiness = unit == 'pet' -- We like to keep things smooth around here self.Power.frequentUpdates = 0.2 + self.Power.Smooth = true self.Power:SetParent(self) self.Power:SetPoint('BOTTOM') @@ -526,7 +544,14 @@ local Stylish = function(self, unit, isSingle) end end - self.PostUpdatePower = UpdatePower + if (unit == 'pet') then + self:RegisterEvent('UNIT_HAPPINESS', UpdatePower) + end + +--~ self.Power.PostUpdate = PetUpdatePower +--~ else + self.Power.PostUpdate = UpdatePower +--~ end if (unit == 'targettarget') then self.Power:Hide() @@ -706,7 +731,7 @@ local Stylish = function(self, unit, isSingle) self:Tag(self.Info, '[LanLevel] [LanName]') elseif (unit == 'focus') then name:SetText() - else + else self:Tag(self.Info, '[LanName]') end end -- 1.7.9.5 From 6a20dc52cd82df9678c354598e7863936cb10bdd Mon Sep 17 00:00:00 2001 From: Lanerra Date: Fri, 4 Mar 2011 22:11:31 -0800 Subject: [PATCH 06/23] Just kidding! Fixed the pet power bar happiness coloring/updating. Apparently, it only likes to update when being constantly refreshed with power change events (ie. In combat). Removed PostUpdate power function for simplicity. Also shortened the name on Target of Target frame for better readability. --- Tags.lua | 6 ++++++ oUF_Lanerra.lua | 49 +++---------------------------------------------- 2 files changed, 9 insertions(+), 46 deletions(-) diff --git a/Tags.lua b/Tags.lua index 35ba018..94d10a6 100644 --- a/Tags.lua +++ b/Tags.lua @@ -52,6 +52,12 @@ oUF.Tags['LanRaidName'] = function(unit) return Name end +oUF.TagEvents['LanShortName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' +oUF.Tags['LanShortName'] = function(unit) + local Name = string.sub(UnitName(unit), 1, 8)..'...' + return Name +end + oUF.TagEvents['LanPower'] = 'UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER' oUF.Tags['LanPower'] = function(unit) local min = UnitPower(unit) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 96434ea..b546027 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -274,44 +274,6 @@ local function UpdateRaidHealth(Health, unit, min, max) end end --- Now for the update power function -local UpdatePower = function(Power, unit, min, max) - local self = Power:GetParent() - - if (min == 0 or UnitIsDead(unit) or UnitIsGhost(unit) or unit == 'pet' or unit == 'focus' or unit ~= 'player' or not UnitIsConnected(unit)) then - Power.Value:SetText() - else - Power.Value:SetText(min) - end - - local color - if UnitIsPlayer(unit) then - local _, class = UnitClass(unit) - color = colors.class[class] - Power:SetStatusBarColor(color[1], color[2], color[3], 1) - elseif UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) then - color = colors.tapped - Power:SetStatusBarColor(color[1], color[2], color[3], 1) - elseif UnitIsUnit(unit, 'pet') and GetPetHappiness() then - color = colors.happiness[GetPetHappiness()] - Power:SetStatusBarColor(color[1], color[2], color[3], 1) - elseif UnitIsEnemy(unit, "player") then - color = colors.reaction[1] - Power:SetStatusBarColor(color[1], color[2], color[3], 1) - else - if (unit ~= 'pet') then - color = colors.reaction[UnitReaction(unit, "player") or 5] - Power:SetStatusBarColor(color[1], color[2], color[3], 1) - end - end -end - ---~ local PetUpdatePower = function(Power, unit, min, max) ---~ local color ---~ color = colors.happiness[GetPetHappiness()] ---~ Power:SetStatusBarColor(color[1], color[2], color[3], 1) ---~ end - -- Add DruidPower support local function UpdateDruidPower(self, event, unit) if (unit and unit ~= self.unit) then @@ -544,15 +506,8 @@ local Stylish = function(self, unit, isSingle) end end - if (unit == 'pet') then - self:RegisterEvent('UNIT_HAPPINESS', UpdatePower) - end +--~ self.Power.PostUpdate = UpdatePower ---~ self.Power.PostUpdate = PetUpdatePower ---~ else - self.Power.PostUpdate = UpdatePower ---~ end - if (unit == 'targettarget') then self.Power:Hide() self.Power.Show = self.Power.Hide @@ -731,6 +686,8 @@ local Stylish = function(self, unit, isSingle) self:Tag(self.Info, '[LanLevel] [LanName]') elseif (unit == 'focus') then name:SetText() + elseif (unit == 'targettarget') then + self:Tag(self.Info, '[LanShortName]') else self:Tag(self.Info, '[LanName]') end -- 1.7.9.5 From 5fa19960dc336d5cfb209d594d7d7b8c7d01a43c Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sun, 6 Mar 2011 06:22:26 -0800 Subject: [PATCH 07/23] Fixed unit frames not producing tooltips. YAY! --- oUF_Lanerra.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index b546027..29b6d82 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -411,6 +411,10 @@ end -- Time to give our solo unit frames some style! local Stylish = function(self, unit, isSingle) self.menu = CreateDropDown + + self:SetScript("OnEnter", UnitFrame_OnEnter) + self:SetScript("OnLeave", UnitFrame_OnLeave) + self.ignoreHealComm = true self:EnableMouse(true) @@ -980,6 +984,10 @@ end -- First build the group style local function StylishGroup(self, unit) self.menu = CreateDropDown + + self:SetScript("OnEnter", UnitFrame_OnEnter) + self:SetScript("OnLeave", UnitFrame_OnLeave) + self.ignoreHealComm = true self:EnableMouse(true) @@ -1135,6 +1143,10 @@ end -- Now the raid style local function StylishRaid(self, unit) self.menu = CreateDropDown + + self:SetScript("OnEnter", UnitFrame_OnEnter) + self:SetScript("OnLeave", UnitFrame_OnLeave) + self.ignoreHealComm = true self:EnableMouse(true) -- 1.7.9.5 From 79c21f86221370e363cbf09f1afb78d6f3bef9ba Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sun, 6 Mar 2011 16:15:50 -0800 Subject: [PATCH 08/23] Pet happiness updating now fixed completely. Updates properly in combat as well as out of combat --- oUF_Lanerra.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 29b6d82..1aa2c7d 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -465,8 +465,10 @@ local Stylish = function(self, unit, isSingle) self.Power.colorHappiness = unit == 'pet' -- We like to keep things smooth around here - self.Power.frequentUpdates = 0.2 - self.Power.Smooth = true + if (unit ~= 'pet') then + self.Power.frequentUpdates = 0.2 + self.Power.Smooth = true + end self.Power:SetParent(self) self.Power:SetPoint('BOTTOM') @@ -510,8 +512,6 @@ local Stylish = function(self, unit, isSingle) end end ---~ self.Power.PostUpdate = UpdatePower - if (unit == 'targettarget') then self.Power:Hide() self.Power.Show = self.Power.Hide -- 1.7.9.5 From 2ae6faf52c5952e5d3f0878864be98fb66cb217c Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sat, 12 Mar 2011 17:01:54 -0800 Subject: [PATCH 09/23] Restoring frequentUpdates to Pet unit frames for hunters, since the Happiness updating issue has been fixed on latest oUF from Github --- oUF_Lanerra.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 1aa2c7d..4d53d4a 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -465,11 +465,9 @@ local Stylish = function(self, unit, isSingle) self.Power.colorHappiness = unit == 'pet' -- We like to keep things smooth around here - if (unit ~= 'pet') then - self.Power.frequentUpdates = 0.2 - self.Power.Smooth = true - end - + self.Power.frequentUpdates = 0.2 + self.Power.Smooth = true + self.Power:SetParent(self) self.Power:SetPoint('BOTTOM') self.Power:SetPoint('LEFT', .2, 0) -- 1.7.9.5 From 4443aa9d7d65030da76e7cf1f2e6a0569d4c1b8f Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sun, 13 Mar 2011 16:15:03 -0700 Subject: [PATCH 10/23] Version bump to 1.15 --- oUF_Lanerra.lua | 2 +- oUF_Lanerra.toc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 4d53d4a..f8f6b03 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,5 +1,5 @@ --[[ - Version = 1.14 + Version = 1.15 Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index 915afdf..2e6b020 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ ## Interface: 40000 -## Version: 1.14 +## Version: 1.15 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5 From b11fe20c50e0d71cfecd28718e0a1933038906fd Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sun, 1 May 2011 04:38:55 -0700 Subject: [PATCH 11/23] Fixed quirk in display of shortnames on TargetTarget frame --- Tags.lua | 9 +++++++-- oUF_Lanerra.lua | 9 ++++----- oUF_Lanerra.toc | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Tags.lua b/Tags.lua index 94d10a6..5e00a1a 100644 --- a/Tags.lua +++ b/Tags.lua @@ -54,8 +54,13 @@ end oUF.TagEvents['LanShortName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' oUF.Tags['LanShortName'] = function(unit) - local Name = string.sub(UnitName(unit), 1, 8)..'...' - return Name + local name = UnitName(unit) + if strlen(name) > 8 then + local NewName = string.sub(UnitName(unit), 1, 8)..'...' + return NewName + else + return name + end end oUF.TagEvents['LanPower'] = 'UNIT_ENERGY UNIT_FOCUS UNIT_MANA UNIT_RAGE UNIT_MAXRUNIC_POWER UNIT_RUNIC_POWER' diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index f8f6b03..8352e65 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,5 +1,5 @@ --[[ - Version = 1.15 + Version = 1.17 Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. @@ -461,9 +461,8 @@ local Stylish = function(self, unit, isSingle) self.Power.colorClass = true self.Power.colorTapping = true - self.Power.colorReaction = unit ~= 'pet' - self.Power.colorHappiness = unit == 'pet' - + self.Power.colorReaction = true + -- We like to keep things smooth around here self.Power.frequentUpdates = 0.2 self.Power.Smooth = true @@ -685,7 +684,7 @@ local Stylish = function(self, unit, isSingle) self.Info = name if (unit == 'target') then - self:Tag(self.Info, '[LanLevel] [LanName]') + self:Tag(self.Info, '[LanLevel][shortclassification] [LanName]') elseif (unit == 'focus') then name:SetText() elseif (unit == 'targettarget') then diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index 2e6b020..b28268d 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ -## Interface: 40000 -## Version: 1.15 +## Interface: 40100 +## Version: 1.17 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5 From 3958d0d3b22c338fa14f8e6fde2e15590a88cb32 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Tue, 3 May 2011 05:34:37 -0700 Subject: [PATCH 12/23] - Redid the menu creation method for right-clicking in order to make it a bit more stable and intuitive. - Clarified Healer option in config file --- oUF_Lanerra.lua | 66 +++++++++++++++++++++++++++++++++--------------- oUF_Lanerra_Config.lua | 2 +- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 8352e65..23ef82d 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -99,19 +99,41 @@ local function ShortValue(value) end end --- Dropdown menu function for our unit frames +local dropdown = CreateFrame('Frame', 'oUF_LanerraDropDown', UIParent, 'UIDropDownMenuTemplate') + +UIDropDownMenu_Initialize(dropdown, function(self) + local unit = self:GetParent().unit + if not unit then return end + + local menu, name, id + if UnitIsUnit(unit, 'player') then + menu = 'SELF' + elseif UnitIsUnit(unit, 'vehicle') then + menu = 'VEHICLE' + elseif UnitIsUnit(unit, 'pet') then + menu = 'PET' + elseif UnitIsPlayer(unit) then + id = UnitInRaid(unit) + if id then + menu = 'RAID_PLAYER' + name = GetRaidRosterInfo(id) + elseif UnitInParty(unit) then + menu = 'PARTY' + else + menu = 'PLAYER' + end + else + menu = 'TARGET' + name = RAID_TARGET_ICON + end + if menu then + UnitPopup_ShowMenu(self, menu, unit, name, id) + end +end, 'MENU') + local function CreateDropDown(self) - local unit = self.unit:gsub('(.)', string.upper, 1) - if _G[unit..'FrameDropDown'] then - ToggleDropDownMenu(1, nil, _G[unit..'FrameDropDown'], 'cursor') - elseif (self.unit:match('party')) then - ToggleDropDownMenu(1, nil, _G['PartyMemberFrame'..self.id..'DropDown'], 'cursor') - else - FriendsDropDown.unit = self.unit - FriendsDropDown.id = self.id - FriendsDropDown.initialize = RaidFrameDropDown_Initialize - ToggleDropDownMenu(1, nil, FriendsDropDown, 'cursor') - end + dropdown:SetParent(self) + ToggleDropDownMenu(1, nil, dropdown, 'cursor', 15, -15) end -- And now for our custom channeling function for our castbars @@ -496,19 +518,19 @@ local Stylish = function(self, unit, isSingle) end if (Settings.Units.Target.ShowPowerText) then - if (unit == 'target') then - local _, power = UnitPowerType(unit) - local c = PowerBarColor[power] - self:Tag(self.Power.Value, '[LanPower]') - self.Power.Value:SetTextColor(c.r, c.g, c.b) - end + if (unit == 'target') then + local _, power = UnitPowerType(unit) + local c = PowerBarColor[power] + self:Tag(self.Power.Value, '[LanPower]') + self.Power.Value:SetTextColor(c.r, c.g, c.b) + end else if (unit == 'target') then self.Power.Value:Hide() self.Power.Value.Show = self.Power.Value.Hide end - end - + end + if (unit == 'targettarget') then self.Power:Hide() self.Power.Show = self.Power.Hide @@ -1265,6 +1287,10 @@ local function StylishRaid(self, unit) -- Dispel highlight support self.DispelHighlight = UpdateDispelHighlight + -- Threat highlight support + self.threatLevel = 0 + self.ThreatHighlight = UpdateThreatHighlight + return self end diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index afb2558..84f570f 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -94,7 +94,7 @@ Settings = { ClassColor = true, }, HidePower = true, -- Reserved for future use - Healer = true, + Healer = true, -- If true, overrides height and width in this section and gets set to a static amount }, }, CastBars = { -- 1.7.9.5 From 3d8ac02a08b5139cd8ca1982ddb5765ab0ce6a73 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Wed, 4 May 2011 05:00:28 -0700 Subject: [PATCH 13/23] - Remove Blizzard options rendered useless by unit frames - Attempt to fix status (Dead, Offline, etc.) not displaying as it should - Restored custom UpdatePower function in anticipation of changing how power value is displayed - Removed superfluous code in PowerText display code, optimized it for better functionality - Consolidated Name code to make it a bit more optimized, and make ready for future power text change - Added option to show PowerText for pet - Target power text will now display properly, but I have yet to work out a good place for it. --- oUF_Lanerra.lua | 158 +++++++++++++++++++++++++++++++----------------- oUF_Lanerra_Config.lua | 3 +- 2 files changed, 103 insertions(+), 58 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 23ef82d..df3d886 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -8,7 +8,37 @@ --]] ---- Lazy Stuff Goes Here! -local _, ns = ... + +-- Disable Blizzard options that are rendered useless by having a unit frame addon + +for _, button in pairs({ + 'UnitFramePanelPartyBackground', + 'UnitFramePanelPartyPets', + 'UnitFramePanelFullSizeFocusFrame', + + 'CombatPanelTargetOfTarget', + 'CombatPanelTOTDropDown', + 'CombatPanelTOTDropDownButton', + 'CombatPanelEnemyCastBarsOnPortrait', + + 'DisplayPanelShowAggroPercentage', + + 'FrameCategoriesButton9', +}) do + _G['InterfaceOptions'..button]:SetAlpha(0.35) + _G['InterfaceOptions'..button]:Disable() + _G['InterfaceOptions'..button]:EnableMouse(false) +end + +do + for k, v in pairs(UnitPopupMenus) do + for x, i in pairs(UnitPopupMenus[k]) do + if (i == 'SET_FOCUS' or i == 'CLEAR_FOCUS') then + table.remove(UnitPopupMenus[k],x) + end + end + end +end local colors = oUF.colors @@ -158,7 +188,7 @@ local UpdateHealth = function(Health, unit, min, max) return end - if (not unit == 'pet' or unit == 'focus' or unit == 'targettarget') then + if (not unit == 'pet' or unit == 'focus' or unit == 'targettarget' or unit == 'player') then if (not UnitIsConnected(unit)) then Health:SetValue(0) Health.Value:SetText('|cffD7BEA5'..'Offline') @@ -242,10 +272,16 @@ local function UpdateGroupHealth(Health, unit, min, max) if (not UnitIsConnected(unit)) then Health:SetValue(0) Health.Value:SetText('|cffD7BEA5'..'Offline') + + return elseif (UnitIsDead(unit)) then Health.Value:SetText('|cffD7BEA5'..'Dead') + + return elseif (UnitIsGhost(unit)) then Health.Value:SetText('|cffD7BEA5'..'Ghost') + + return end if (Settings.Units.Party.Health.Percent) then @@ -273,9 +309,11 @@ local function UpdateRaidHealth(Health, unit, min, max) end if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then - Health.Value:SetText((UnitIsDead(unit) and 'Dead') or (UnitIsGhost(unit) and 'Ghost') or (not UnitIsConnected(unit) and 'Offline')) + Health.Value:SetText((UnitIsDead(unit) and 'DEAD') or (UnitIsGhost(unit) and 'GHOST') or (not UnitIsConnected(unit) and 'OFFL')) Health.Value:SetTextColor(.5, .5, .5) Health:SetStatusBarColor(.5, .5, .5) + + return else if (Settings.Units.Raid.Health.Percent) then Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') @@ -296,19 +334,42 @@ local function UpdateRaidHealth(Health, unit, min, max) end end +-- Custom Power Updating Function +local function UpdatePower(Power, unit, min, max) + local self = Power:GetParent() + + local _, PowerType, altR, altG, altB = UnitPowerType(unit) + local UnitPower = PowerBarColor[PowerType] + + if (UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit)) then + Power:SetValue(0) + Power.Value:SetText('') + elseif (unit == 'player' and Settings.Units.Player.ShowPowerText or unit == 'target' and Settings.Units.Target.ShowPowerText or unit == 'pet' and Settings.Units.Pet.ShowPowerText) then + Power.Value:SetText((min/max * 100 < 100 and format('%d%%', min/max * 100))) + else + Power.Value:SetText() + end + + if (UnitPower) then + Power.Value:SetTextColor(UnitPower.r, UnitPower.g, UnitPower.b) + else + Power.Value:SetTextColor(altR, altG, altB) + end +end + -- Add DruidPower support local function UpdateDruidPower(self, event, unit) if (unit and unit ~= self.unit) then return end - local unitPower = PowerBarColor['MANA'] + local UnitPower = PowerBarColor['MANA'] local mana = UnitPowerType('player') == 0 local index = GetShapeshiftForm() if (index == 1 or index == 3) then - if (unitPower) then - self.Druid.Power:SetStatusBarColor(unitPower.r, unitPower.g, unitPower.b) + if (UnitPower) then + self.Druid.Power:SetStatusBarColor(UnitPower.r, UnitPower.g, UnitPower.b) end self.Druid.Power:SetAlpha(1) @@ -472,7 +533,7 @@ local Stylish = function(self, unit, isSingle) self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) self.Health.Value:SetShadowOffset(1, -1) self.Health.Value:SetTextColor(1, 1, 1) - self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) + self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) self.Health.PostUpdate = UpdateHealth @@ -498,46 +559,25 @@ local Stylish = function(self, unit, isSingle) self.Power.Value = self.Power:CreateFontString('$parentPowerValue', 'OVERLAY') self.Power.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) self.Power.Value:SetShadowOffset(1, -1) - self.Power.Value:SetPoint('LEFT', self.Health.Value, 'RIGHT', -195, 0) + if (unit == 'target') then + self.Power.Value:SetPoint('BOTTOM', self, 0, 2) + elseif (unit == 'player') then + self.Power.Value:SetPoint('LEFT', self.Health.Value, 'RIGHT', -195, 0) + elseif (unit == 'pet') then + self.Power.Value:SetPoint('CENTER', self.Health) + end + self.Power.Value:SetTextColor(1, 1, 1) self.Power.Value:SetJustifyH('LEFT') self.Power.Value.frequentUpdates = 0.1 - if (Settings.Units.Player.ShowPowerText) then - if (unit == 'player') then - local _, power = UnitPowerType(unit) - local c = PowerBarColor[power] - self:Tag(self.Power.Value, '[LanPower]') - self.Power.Value:SetTextColor(c.r, c.g, c.b) - end - else - if (unit == 'player') then - self.Power.Value:Hide() - self.Power.Value.Show = self.Power.Value.Hide - end - end - - if (Settings.Units.Target.ShowPowerText) then - if (unit == 'target') then - local _, power = UnitPowerType(unit) - local c = PowerBarColor[power] - self:Tag(self.Power.Value, '[LanPower]') - self.Power.Value:SetTextColor(c.r, c.g, c.b) - end - else - if (unit == 'target') then - self.Power.Value:Hide() - self.Power.Value.Show = self.Power.Value.Hide - end - end - if (unit == 'targettarget') then self.Power:Hide() self.Power.Show = self.Power.Hide self.Health:SetAllPoints(self) end - - if (unit == 'focus') then + + if (unit == 'focus') then self.Power:Hide() self.Power.Show = self.Power.Hide self.Health:SetAllPoints(self) @@ -688,34 +728,38 @@ local Stylish = function(self, unit, isSingle) -- Display the names if (unit ~= 'player') then local name = self.Health:CreateFontString('$parentName', 'OVERLAY') - if (unit == 'targettarget' or unit == 'pet') then - name:SetPoint('CENTER') - else - name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) - name:SetJustifyH('LEFT') - end - name:SetFont(Settings.Media.Font, Settings.Media.FontSize) name:SetShadowOffset(1, -1) name:SetTextColor(1, 1, 1) name:SetWidth(130) name:SetParent(self.Overlay) name:SetHeight(Settings.Media.FontSize) - name.frequentUpdates = 0.2 - self.Info = name - if (unit == 'target') then - self:Tag(self.Info, '[LanLevel][shortclassification] [LanName]') - elseif (unit == 'focus') then - name:SetText() - elseif (unit == 'targettarget') then + self.Info = name + if (unit == 'targettarget') then + name:SetPoint('CENTER') self:Tag(self.Info, '[LanShortName]') + elseif (unit == 'pet' and Settings.Units.Pet.ShowPowerText) then + name:Hide() + elseif (unit == 'pet' and not Settings.Units.Pet.ShowPowerText) then + name:SetPoint('CENTER', self.Health) + self:Tag(self.Info, '[LanName]') + elseif (unit == 'focus') then + name:SetText() + elseif (unit == 'target') then + name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) + name:SetJustifyH('LEFT') + self:Tag(self.Info, '[LanLevel][shortclassification] [LanName]') else - self:Tag(self.Info, '[LanName]') + name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) + name:SetJustifyH('LEFT') + self:Tag(self.Info, '[LanName]') end - end + end + self.Power.PostUpdate = UpdatePower + if (isHealer) then if (unit == 'target') then local MHPB = CreateFrame('StatusBar', nil, self.Health) @@ -1063,8 +1107,8 @@ local function StylishGroup(self, unit) self.Name:SetShadowOffset(1, -1) self.Name.frequentUpdates = 0.3 - self:Tag(self.Name, '|cffffffff[LanName]|r') - + self:Tag(self.Name, '|cffffffff[LanName]|r') + if (Settings.Units.Party.Healer) then self.Name:SetPoint('CENTER', self.Health) end @@ -1216,7 +1260,7 @@ local function StylishRaid(self, unit) self.Overlay:SetFrameLevel(self.Health:GetFrameLevel() + (self.Power and 3 or 2)) -- Display group names - if (Settings.Units.Raid.Healer) then + if (Settings.Units.Raid.Healer) then self.Name = self.Health:CreateFontString('$parentName', 'OVERLAY') self.Name:SetPoint('TOP', 0, -2) self.Name:SetFont(Settings.Media.Font, 13) diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index 84f570f..13bd8e8 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -36,6 +36,7 @@ Settings = { Deficit = false, Current = false, }, + ShowPowerText = false, }, Target = { Height = 30, @@ -46,7 +47,7 @@ Settings = { Deficit = false, Current = false, }, - ShowPowerText = false, + ShowPowerText = true, }, ToT = { Height = 30, -- 1.7.9.5 From 6e823a5d59fc5cdd04516d7f8c80f04adc5f6818 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Thu, 5 May 2011 14:45:31 -0700 Subject: [PATCH 14/23] - Changed position of Power Text for Target Frame - Added option for Vertical Healthbar toggle for Focus Frame - Added functionality that hides pet's name with either or both Health/Power displayed on that frame --- Tags.lua | 23 +++++++++++++++++++++++ oUF_Lanerra.lua | 28 +++++++++++++++++++++------- oUF_Lanerra_Config.lua | 1 + 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Tags.lua b/Tags.lua index 5e00a1a..66956fb 100644 --- a/Tags.lua +++ b/Tags.lua @@ -11,6 +11,29 @@ oUF.Tags['LanThreat'] = function() return perc and ('%s%d%%|r'):format(hex(GetThreatStatusColor(UnitThreatSituation('player', 'target'))), perc) end +oUF.TagEvents['LanClassification'] = 'UNIT_CLASSIFICATION_CHANGED' +oUF.Tags['LanClassification'] = function(unit) + local level = UnitLevel(unit) + local colorL = GetQuestDifficultyColor(level) + + if (level < 0) then + r, g, b = 1, 0, 0 + else + r, g, b = colorL.r, colorL.g, colorL.b + end + + local c = UnitClassification(unit) + if(c == 'rare') then + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, 'R') + elseif(c == 'eliterare') then + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, 'R+') + elseif(c == 'elite') then + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, '+') + elseif(c == 'worldboss') then + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, 'B') + end +end + oUF.Tags['LanLevel'] = function(unit) local level = UnitLevel(unit) local colorL = GetQuestDifficultyColor(level) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index df3d886..a33d08a 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -40,6 +40,11 @@ do end end +------------------------------------------------- +-- Variables for defining colors and appearance +------------------------------------------------- + + local colors = oUF.colors local playerClass = select(2, UnitClass('player')) @@ -129,6 +134,12 @@ local function ShortValue(value) end end +------------------------------------------ +-- Functions used to build Unit Frames +------------------------------------------ + + +-- Build dropdown menus local dropdown = CreateFrame('Frame', 'oUF_LanerraDropDown', UIParent, 'UIDropDownMenuTemplate') UIDropDownMenu_Initialize(dropdown, function(self) @@ -560,11 +571,9 @@ local Stylish = function(self, unit, isSingle) self.Power.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) self.Power.Value:SetShadowOffset(1, -1) if (unit == 'target') then - self.Power.Value:SetPoint('BOTTOM', self, 0, 2) + self.Power.Value:SetPoint('TOPRIGHT', self.Power, 'BOTTOMRIGHT', 0, -5) elseif (unit == 'player') then self.Power.Value:SetPoint('LEFT', self.Health.Value, 'RIGHT', -195, 0) - elseif (unit == 'pet') then - self.Power.Value:SetPoint('CENTER', self.Health) end self.Power.Value:SetTextColor(1, 1, 1) @@ -577,7 +586,7 @@ local Stylish = function(self, unit, isSingle) self.Health:SetAllPoints(self) end - if (unit == 'focus') then + if (unit == 'focus' and Settings.Units.Focus.VerticalHealth) then self.Power:Hide() self.Power.Show = self.Power.Hide self.Health:SetAllPoints(self) @@ -586,6 +595,8 @@ local Stylish = function(self, unit, isSingle) self.Health:SetOrientation('HORIZONTAL') end + self.Power.PostUpdate = UpdatePower + -- Improve border drawing self.Overlay = CreateFrame('Frame', nil, self) self.Overlay:SetAllPoints(self) @@ -742,6 +753,11 @@ local Stylish = function(self, unit, isSingle) self:Tag(self.Info, '[LanShortName]') elseif (unit == 'pet' and Settings.Units.Pet.ShowPowerText) then name:Hide() + + if (Settings.Units.Pet.Health.Percent or Settings.Units.Pet.Health.Deficit or Settings.Units.Pet.Health.Current) then + self.Power.Value:SetPoint('RIGHT', self.Health, -2, 0) + self.Health.Value:SetPoint('LEFT', self.Health, -40, -1) + end elseif (unit == 'pet' and not Settings.Units.Pet.ShowPowerText) then name:SetPoint('CENTER', self.Health) self:Tag(self.Info, '[LanName]') @@ -750,15 +766,13 @@ local Stylish = function(self, unit, isSingle) elseif (unit == 'target') then name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) name:SetJustifyH('LEFT') - self:Tag(self.Info, '[LanLevel][shortclassification] [LanName]') + self:Tag(self.Info, '[LanLevel][LanClassification] [LanName]') else name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) name:SetJustifyH('LEFT') self:Tag(self.Info, '[LanName]') end end - - self.Power.PostUpdate = UpdatePower if (isHealer) then if (unit == 'target') then diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index 13bd8e8..4698cb9 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -68,6 +68,7 @@ Settings = { Deficit = false, Current = false, }, + VerticalHealth = true, }, Party = { Height = 20, -- 1.7.9.5 From d84a85b9435e94223f23e30c1783c161f77fae18 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Thu, 5 May 2011 14:47:05 -0700 Subject: [PATCH 15/23] Version bump to 1.18 --- oUF_Lanerra.lua | 2 +- oUF_Lanerra.toc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index a33d08a..c527a31 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,5 +1,5 @@ --[[ - Version = 1.17 + Version = 1.18 Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index b28268d..ada4cd6 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ ## Interface: 40100 -## Version: 1.17 +## Version: 1.18 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5 From 613ae106eb2995f204ea5ccd11882146355638c6 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sat, 7 May 2011 06:41:29 -0700 Subject: [PATCH 16/23] - Added option to change backdrop color for unit frames - Repositioned ToT name to allow health text to show clearly - Adjust health position on other frames just slightly to line up better. --- oUF_Lanerra.lua | 26 +++++++++++++++++++++----- oUF_Lanerra_Config.lua | 1 + 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index c527a31..fb553d9 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -530,7 +530,7 @@ local Stylish = function(self, unit, isSingle) self.Health:SetPoint('RIGHT') self:SetBackdrop(backdrop) - self:SetBackdropColor(0, 0, 0, .75) + self:SetBackdropColor(unpack(Settings.Media.BackdropColor)) if (unit == 'player') then local info = self.Health:CreateFontString('$parentInfo', 'OVERLAY', 'GameFontHighlightSmall') @@ -544,7 +544,7 @@ local Stylish = function(self, unit, isSingle) self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) self.Health.Value:SetShadowOffset(1, -1) self.Health.Value:SetTextColor(1, 1, 1) - self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) +--~ self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) self.Health.PostUpdate = UpdateHealth @@ -747,16 +747,29 @@ local Stylish = function(self, unit, isSingle) name:SetHeight(Settings.Media.FontSize) name.frequentUpdates = 0.2 + self.Health.Value:SetParent(self.Overlay) + self.Info = name + self:Tag(self.Info, '[LanShortName]') + if (unit == 'targettarget') then - name:SetPoint('CENTER') - self:Tag(self.Info, '[LanShortName]') + self.Health.Value:SetPoint('BOTTOM', self.Health, 0, 1) + self.Health.Value:Hide() + + if (Settings.Units.ToT.Health.Percent or Settings.Units.ToT.Health.Deficit or Settings.Units.ToT.Health.Current) then + name:SetPoint('TOP', self.Health, 0, -1) + self.Health.Value:Show() + else + name:SetPoint('CENTER', self.Health) + name:Show() + self.Health.Value:Hide() + end elseif (unit == 'pet' and Settings.Units.Pet.ShowPowerText) then name:Hide() if (Settings.Units.Pet.Health.Percent or Settings.Units.Pet.Health.Deficit or Settings.Units.Pet.Health.Current) then self.Power.Value:SetPoint('RIGHT', self.Health, -2, 0) - self.Health.Value:SetPoint('LEFT', self.Health, -40, -1) + self.Health.Value:SetPoint('LEFT', self.Health, 2, -1) end elseif (unit == 'pet' and not Settings.Units.Pet.ShowPowerText) then name:SetPoint('CENTER', self.Health) @@ -767,11 +780,14 @@ local Stylish = function(self, unit, isSingle) name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) name:SetJustifyH('LEFT') self:Tag(self.Info, '[LanLevel][LanClassification] [LanName]') + self.Health.Value:SetPoint('RIGHT', self.Health, -2, -1) else name:SetPoint('LEFT', self.Health, 'LEFT', 1, 0) name:SetJustifyH('LEFT') self:Tag(self.Info, '[LanName]') end + else + self.Health.Value:SetPoint('RIGHT', self.Health, -2, -1) end if (isHealer) then diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index 4698cb9..c8e1afb 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -14,6 +14,7 @@ Settings = { FontSize = 15, BorderSize = 14, BorderColor = { .65, .65, .65 }, + BackdropColor = { 0, 0, 0, 0.75 } }, Units = { Player = { -- 1.7.9.5 From d7985ca1a8b1e6e79efdbbb6f809e75423aee2b4 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Sun, 8 May 2011 04:56:25 -0700 Subject: [PATCH 17/23] - Added option to show or hide player buffs/debuffs (disabled by default) - Fixed buff/debuff overlap issue --- oUF_Lanerra.lua | 11 ++++------- oUF_Lanerra_Config.lua | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index fb553d9..d966171 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -837,7 +837,7 @@ local Stylish = function(self, unit, isSingle) end -- Aura/buff/debuff handling, update those suckers! - if (unit == 'player') then + if (unit == 'player' and Settings.Units.Player.ShowBuffs) then local GAP = 6 self.Buffs = CreateFrame('Frame', nil, self) @@ -861,12 +861,9 @@ local Stylish = function(self, unit, isSingle) elseif (unit == 'target') then local GAP = 6 --- local MAX_ICONS = math.floor((Settings.Units.Target.Width - 4 + GAP) / (Settings.Units.Target.Height + GAP)) - 1 - local MAX_ICONS = 10 --- local NUM_BUFFS = math.max(2, math.floor(MAX_ICONS * 0.4)) - local NUM_BUFFS = 4 --- local NUM_DEBUFFS = math.min(MAX_ICONS - 1, math.floor(MAX_ICONS * 0.8)) - local NUM_DEBUFFS = 6 + local MAX_ICONS = math.floor((Settings.Units.Target.Width - 4 + GAP) / (Settings.Units.Target.Height + GAP)) - 1 + local NUM_BUFFS = math.max(2, math.floor(MAX_ICONS * 0.4)) + local NUM_DEBUFFS = math.min(MAX_ICONS - 1, math.floor(MAX_ICONS * 0.8)) self.Debuffs = CreateFrame('Frame', nil, self) diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index c8e1afb..bce1f93 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -27,6 +27,7 @@ Settings = { Current = true, }, ShowPowerText = true, + ShowBuffs = false, }, Pet = { Height = 30, -- 1.7.9.5 From edfa1864988bab04c0c4d86bf4daccdaa278ed8d Mon Sep 17 00:00:00 2001 From: Lanerra Date: Tue, 10 May 2011 03:16:03 -0700 Subject: [PATCH 18/23] - Finally completely corrected display of 'Offline', 'Dead', 'Ghost' text on all unit frames - Initial code put in place for interrupt coloring - Color changes to Status Bar when units are offline, dead, or ghost - Added option to display current health and percent at the same time on target frame - Made it so power text is always shown - Adjusted border padding on all frames, looks much cleaner now - Removed Pet power text --- Tags.lua | 52 +++++++++++++---- media/BorderInterrupt.tga | Bin 0 -> 4377 bytes oUF_Lanerra.lua | 138 +++++++++++++++++++++++---------------------- oUF_Lanerra_Config.lua | 7 ++- 4 files changed, 116 insertions(+), 81 deletions(-) create mode 100644 media/BorderInterrupt.tga diff --git a/Tags.lua b/Tags.lua index 66956fb..e7ef573 100644 --- a/Tags.lua +++ b/Tags.lua @@ -63,26 +63,56 @@ oUF.Tags['LanName'] = function(unit) end colorA = {1, 1, 1} - - r, g, b = colorA[1], colorA[2], colorA[3] - - return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, UnitName) + + if (not UnitIsConnected(unit)) then + Name = '|cffD7BEA5'..'OFFLINE' + return Name + elseif (UnitIsDead(unit)) then + Name = '|cffD7BEA5'..'DEAD' + return Name + elseif (UnitIsGhost(unit)) then + Name = '|cffD7BEA5'..'GHOST' + return Name + else + r, g, b = colorA[1], colorA[2], colorA[3] + return format('|cff%02x%02x%02x%s|r', r*255, g*255, b*255, UnitName) + end end oUF.TagEvents['LanRaidName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' -oUF.Tags['LanRaidName'] = function(unit) - local Name = string.sub(UnitName(unit), 1, 4) +oUF.Tags['LanRaidName'] = function(unit) + if (not UnitIsConnected(unit)) then + Name = '|cffD7BEA5'..'OFFLINE' + elseif (UnitIsDead(unit)) then + Name = '|cffD7BEA5'..'DEAD' + elseif (UnitIsGhost(unit)) then + Name = '|cffD7BEA5'..'GHOST' + else + Name = string.sub(UnitName(unit), 1, 4) + end + return Name end oUF.TagEvents['LanShortName'] = 'UNIT_NAME_UPDATE UNIT_HEALTH' oUF.Tags['LanShortName'] = function(unit) local name = UnitName(unit) - if strlen(name) > 8 then - local NewName = string.sub(UnitName(unit), 1, 8)..'...' - return NewName - else - return name + if (not UnitIsConnected(unit)) then + Name = '|cffD7BEA5'..'OFFLINE' + return Name + elseif (UnitIsDead(unit)) then + Name = '|cffD7BEA5'..'DEAD' + return Name + elseif (UnitIsGhost(unit)) then + Name = '|cffD7BEA5'..'GHOST' + return Name + else + if strlen(name) > 8 then + local NewName = string.sub(UnitName(unit), 1, 8)..'...' + return NewName + else + return name + end end end diff --git a/media/BorderInterrupt.tga b/media/BorderInterrupt.tga new file mode 100644 index 0000000000000000000000000000000000000000..25cd3e88412a2b45d0845e1986e4e3c5511fdeec GIT binary patch literal 4377 zcmdUzPfz1k6vds82{aoP{1qF@3W-lKUkX-8$ObVZCQXK(%dMcfR;g49W@l%^YPA|{Y-|K8o12@#3ZpWkN;(?A15Xej zEyfE1@iaa@9&T-IMZ3GZ#0tH|#l>)cX=y2Z!6Va{al;*0QFLyJhGA!CCtO%q2!eCOuoS^|HNt@e6kWMs`59UWc!UAnE||4z1#_)r@d6efmj4FpXCqM8g5 zEC}{jBKbRJC>LCwWD!TQa~MHGeK#P0LD_$u9M?4SSqjjc>z~x_uY+d8mI8u!+Xr zHqnSpv#y_$H>>woha!QA@&5_;fo2v6TU<4zDyP+cf8Yl1g-@mZb-=u!9;^NA%+Cam zjg9r#ihy|;C%cJ0H+aYj*843YzYLfcZwiiA5O2;tZ;|g7PHEqc{AXrQ;OczZ^|Wy; z!lxUDl~af6|5EUD&5_+DfxXkZ*Bqw!Fw;ACyqf#0xo-=H8%H3C$FVK_!)F^N-u!kx z>b+rDZ@}YI->1O$_V&8qO~tiyUA&$jNjEVu!9mBzy1&0~$J$Qv2A8gO5JlfC`N_%2 z6n=1U5Q#yi_6!_>dc)~Ca53Q28_27D$b-BtCT&CZhaVmu5+Vzy65!}ch23zSt$nP= z$H$REbi?T;x;_m*Jw5FWr<>@iFnzKU{Os%u2JmdxE}~7yYa&FL7|uEIr}haI0l1+D^8_@InU$O9)ktJW3-{UGO=sK(Qa;`TCG;I)R-}tZ(P#zi~G{U@6nwiZ+{b5B)gs&KR%;mu1+SJpzsZ literal 0 HcmV?d00001 diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index d966171..7b6b0ff 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -177,20 +177,46 @@ local function CreateDropDown(self) ToggleDropDownMenu(1, nil, dropdown, 'cursor', 15, -15) end --- And now for our custom channeling function for our castbars -local function UpdateChannelStart(self, event, unit, name, rank, text) - self.Castbar.SafeZone:SetDrawLayer('ARTWORK') - self.Castbar.SafeZone:ClearAllPoints() - self.Castbar.SafeZone:SetPoint('TOPLEFT', self.Castbar) - self.Castbar.SafeZone:SetPoint('BOTTOMLEFT', self.Castbar) -end +local Interrupt = 'Interface\\Addons\\oUF_Lanerra\\Media\\BorderInterrupt' +local Normal = 'Interface\\Addons\\oUF_Lanerra\\Media\\BorderNormal' --- Now, the custom casting function -local function UpdateCastStart(self, event, unit, name, rank, text, castid) +local function PostCastStart(Castbar, unit) self.Castbar.SafeZone:SetDrawLayer('BORDER') self.Castbar.SafeZone:ClearAllPoints() self.Castbar.SafeZone:SetPoint('TOPRIGHT', self.Castbar) self.Castbar.SafeZone:SetPoint('BOTTOMRIGHT', self.Castbar) + + if (unit == 'target') then + if (self.Castbar.interrupt) then + self.Castbar.Border:SetBorderTexture(Interrupt) + print('Changed the border, chief!') + self.Castbar.Border:SetBorderColor(1, 0, 1) + self.Castbar.Border:SetBorderShadowColor(1, 0, 1) + else + self.Castbar.Border:SetBorderTexture(Normal) + self.Castbar.Border:SetBorderColor(1, 1, 1) + self.Castbar.Border:SetBorderShadowColor(0, 0, 0) + end + end +end + +local function PostChannelStart(Castbar, unit) + self.Castbar.SafeZone:SetDrawLayer('ARTWORK') + self.Castbar.SafeZone:ClearAllPoints() + self.Castbar.SafeZone:SetPoint('TOPLEFT', self.Castbar) + self.Castbar.SafeZone:SetPoint('BOTTOMLEFT', self.Castbar) + + if (unit == 'target') then + if (self.interrupt) then + self.Castbar.Border:SetBorderTexture(Interrupt) + self.Castbar.Border:SetBorderColor(1, 0, 1) + self.Castbar.Border:SetBorderShadowColor(1, 0, 1) + else + self.Castbar.Border:SetBorderTexture(Normal) + self.Castbar.Border:SetBorderColor(1, 1, 1) + self.Castbar.Border:SetBorderShadowColor(0, 0, 0) + end + end end -- Health update function of doom! @@ -200,27 +226,15 @@ local UpdateHealth = function(Health, unit, min, max) end if (not unit == 'pet' or unit == 'focus' or unit == 'targettarget' or unit == 'player') then - if (not UnitIsConnected(unit)) then - Health:SetValue(0) - Health.Value:SetText('|cffD7BEA5'..'Offline') - - return - elseif (UnitIsDead(unit)) then - Health:SetValue(0) - Health.Value:SetText('|cffD7BEA5'..'Dead') - - return - elseif (UnitIsGhost(unit)) then + if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then Health:SetValue(0) - Health.Value:SetText('|cffD7BEA5'..'Ghost') - - return - end + Health:SetStatusBarColor(.5, .5, .5) + end end if (unit == 'player') then if (Settings.Units.Player.Health.Percent) then - Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + Health.Value:SetText((min / max * 100 and format('%d%%', min / max * 100)) or '') elseif (Settings.Units.Player.Health.Deficit) then Health.Value:SetText((min ~= max) and format('%d', min - max) or '') elseif (Settings.Units.Player.Health.Current) then @@ -230,12 +244,14 @@ local UpdateHealth = function(Health, unit, min, max) end elseif (unit == 'target') then if (Settings.Units.Target.Health.Percent) then - Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + Health.Value:SetText((min / max * 100 and format('%d%%', min / max * 100)) or '') elseif (Settings.Units.Target.Health.Deficit) then Health.Value:SetText((min ~= max) and format('%d', min - max) or '') elseif (Settings.Units.Target.Health.Current) then Health.Value:SetText(ShortValue(min)) - else + elseif (Settings.Units.Target.Health.PerCur) then + Health.Value:SetText((min/max * 100 and format('%s - %d%%', ShortValue(min), min/max * 100))) + else Health.Value:SetText() end elseif (unit == 'targettarget') then @@ -280,19 +296,9 @@ local function UpdateGroupHealth(Health, unit, min, max) return end - if (not UnitIsConnected(unit)) then + if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then Health:SetValue(0) - Health.Value:SetText('|cffD7BEA5'..'Offline') - - return - elseif (UnitIsDead(unit)) then - Health.Value:SetText('|cffD7BEA5'..'Dead') - - return - elseif (UnitIsGhost(unit)) then - Health.Value:SetText('|cffD7BEA5'..'Ghost') - - return + Health:SetStatusBarColor(.5, .5, .5) end if (Settings.Units.Party.Health.Percent) then @@ -320,14 +326,11 @@ local function UpdateRaidHealth(Health, unit, min, max) end if (UnitIsDead(unit) or UnitIsGhost(unit) or not UnitIsConnected(unit)) then - Health.Value:SetText((UnitIsDead(unit) and 'DEAD') or (UnitIsGhost(unit) and 'GHOST') or (not UnitIsConnected(unit) and 'OFFL')) - Health.Value:SetTextColor(.5, .5, .5) - Health:SetStatusBarColor(.5, .5, .5) - - return + Health:SetValue(0) + Health:SetStatusBarColor(.5, .5, .5) else if (Settings.Units.Raid.Health.Percent) then - Health.Value:SetText((min / max * 100 < 100 and format('%d%%', min / max * 100)) or '') + Health.Value:SetText((min / max * 100 and format('%d%%', min / max * 100)) or '') elseif (Settings.Units.Raid.Health.Deficit) then Health.Value:SetText((min ~= max) and format('%d', min - max) or '') elseif (Settings.Units.Raid.Health.Current) then @@ -355,8 +358,12 @@ local function UpdatePower(Power, unit, min, max) if (UnitIsDeadOrGhost(unit) or not UnitIsConnected(unit)) then Power:SetValue(0) Power.Value:SetText('') - elseif (unit == 'player' and Settings.Units.Player.ShowPowerText or unit == 'target' and Settings.Units.Target.ShowPowerText or unit == 'pet' and Settings.Units.Pet.ShowPowerText) then - Power.Value:SetText((min/max * 100 < 100 and format('%d%%', min/max * 100))) + elseif (unit == 'player' and Settings.Units.Player.ShowPowerText or unit == 'target' and Settings.Units.Target.ShowPowerText) then + if (unit == 'target' and max == 0) then + Power.Value:SetText('') + else + Power.Value:SetText((min/max * 100 and format('%d%%', min/max * 100))) + end else Power.Value:SetText() end @@ -408,7 +415,6 @@ local AuraIconCD_OnHide = function(cd) button:SetBorderParent(button) button.count:SetParent(button) end - -- Aura Icon Overlay local AuraIconOverlay_SetBorderColor = function(overlay, r, g, b) if not r or not g or not b then @@ -419,7 +425,7 @@ end -- Aura Icon Creation Function local function PostCreateAuraIcon(iconframe, button) - AddBorder(button, Settings.Media.BorderSize) + AddBorder(button, Settings.Media.BorderSize, Settings.Media.BorderPadding) button.cd:SetReverse(true) button.cd:SetScript('OnHide', AuraIconCD_OnHide) @@ -544,7 +550,6 @@ local Stylish = function(self, unit, isSingle) self.Health.Value:SetFont(Settings.Media.Font, Settings.Media.FontSize) self.Health.Value:SetShadowOffset(1, -1) self.Health.Value:SetTextColor(1, 1, 1) ---~ self.Health.Value:SetPoint('RIGHT', self.Health, -2, 0) self.Health.PostUpdate = UpdateHealth @@ -610,11 +615,11 @@ local Stylish = function(self, unit, isSingle) self.Castbar:SetScale(Settings.CastBars.Player.Scale) self.Castbar:SetStatusBarColor(unpack(Settings.CastBars.Player.Color)) - self.Border = CreateFrame('Frame', nil, self.Castbar) - self.Border:SetAllPoints(self.Castbar) - self.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) + self.Castbar.Border = CreateFrame('Frame', nil, self.Castbar) + self.Castbar.Border:SetAllPoints(self.Castbar) + self.Castbar.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) - AddBorder(self.Border, Settings.Media.BorderSize) + AddBorder(self.Castbar.Border, Settings.Media.BorderSize, Settings.Media.BorderPadding) self.Castbar:SetHeight(Settings.CastBars.Player.Height) self.Castbar:SetWidth(Settings.CastBars.Player.Width) @@ -661,11 +666,11 @@ local Stylish = function(self, unit, isSingle) self.Castbar:SetWidth(Settings.CastBars.Target.Width) self.Castbar:SetScale(Settings.CastBars.Target.Scale) - self.Border = CreateFrame('Frame', nil, self.Castbar) - self.Border:SetAllPoints(self.Castbar) - self.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) + self.Castbar.Border = CreateFrame('Frame', nil, self.Castbar) + self.Castbar.Border:SetAllPoints(self.Castbar) + self.Castbar.Border:SetFrameLevel(self.Castbar:GetFrameLevel() + 2) - AddBorder(self.Border, Settings.Media.BorderSize) + AddBorder(self.Castbar.Border, Settings.Media.BorderSize, Settings.Media.BorderPadding) self.Castbar:SetHeight(Settings.CastBars.Target.Height) self.Castbar:SetParent(self) @@ -695,8 +700,8 @@ local Stylish = function(self, unit, isSingle) end end - self.PostChannelStart = UpdateChannelStart - self.PostCastStart = UpdateCastStart + self.PostChannelStart = PostChannelStart + self.PostCastStart = PostCastStart end end @@ -715,7 +720,7 @@ local Stylish = function(self, unit, isSingle) MirrorBorder = CreateFrame('Frame', nil, _G[bar]) MirrorBorder:SetAllPoints(_G[bar]) MirrorBorder:SetFrameLevel(_G[bar]:GetFrameLevel() + 2) - AddBorder(MirrorBorder, Settings.Media.BorderSize) + AddBorder(MirrorBorder, Settings.Media.BorderSize, Settings.Media.BorderPadding) _G[bar..'Border']:Hide() @@ -914,7 +919,6 @@ local Stylish = function(self, unit, isSingle) -- Various oUF plugins support if (unit == 'player') then - -- oUF_RuneBar support if(IsAddOnLoaded('oUF_RuneBar') and class == 'DEATHKNIGHT') then self.RuneBar = {} @@ -958,7 +962,7 @@ local Stylish = function(self, unit, isSingle) self.DruidBorder:SetAllPoints(self.Druid.Power) self.DruidBorder:SetFrameLevel(self.Druid.Power:GetFrameLevel() + 2) - AddBorder(self.DruidBorder, Settings.Media.BorderSize) + AddBorder(self.DruidBorder, Settings.Media.BorderSize, Settings.Media.BorderPadding) table.insert(self.__elements, UpdateDruidPower) self:RegisterEvent('UNIT_MANA', UpdateDruidPower) @@ -980,7 +984,7 @@ local Stylish = function(self, unit, isSingle) EclipseBarBorder:SetAllPoints(EclipseBar) EclipseBarBorder:SetFrameLevel(EclipseBar:GetFrameLevel() + 2) - AddBorder(EclipseBarBorder, Settings.Media.BorderSize) + AddBorder(EclipseBarBorder, Settings.Media.BorderSize, Settings.Media.BorderPadding) local LunarBar = CreateFrame('StatusBar', nil, EclipseBar) LunarBar:SetPoint('LEFT', EclipseBar, 'LEFT', 0, 0) @@ -1056,7 +1060,7 @@ local Stylish = function(self, unit, isSingle) end -- Hardcore border action! - AddBorder(self, Settings.Media.BorderSize) + AddBorder(self, Settings.Media.BorderSize, Settings.Media.BorderPadding + 2) self:SetBorderParent(self.Overlay) self.UpdateBorder = UpdateBorder @@ -1215,7 +1219,7 @@ local function StylishGroup(self, unit) self.SpellRange = true -- Hardcore border action! - AddBorder(self, Settings.Media.BorderSize) + AddBorder(self, Settings.Media.BorderSize, Settings.Media.BorderPadding + 2) self:SetBorderParent(self.Overlay) self.UpdateBorder = UpdateBorder @@ -1350,7 +1354,7 @@ local function StylishRaid(self, unit) self.SpellRange = true -- Hardcore border action! - AddBorder(self, Settings.Media.BorderSize) + AddBorder(self, Settings.Media.BorderSize, Settings.Media.BorderPadding + 2) self:SetBorderParent(self.Overlay) self.UpdateBorder = UpdateBorder diff --git a/oUF_Lanerra_Config.lua b/oUF_Lanerra_Config.lua index bce1f93..44a54ed 100644 --- a/oUF_Lanerra_Config.lua +++ b/oUF_Lanerra_Config.lua @@ -13,8 +13,9 @@ Settings = { Font = 'Interface\\Addons\\oUF_Lanerra\\media\\font.ttf', FontSize = 15, BorderSize = 14, - BorderColor = { .65, .65, .65 }, - BackdropColor = { 0, 0, 0, 0.75 } + BorderColor = { 0.65, 0.65, 0.65 }, + BackdropColor = { 0, 0, 0, 0.75 }, + BorderPadding = 4 }, Units = { Player = { @@ -38,7 +39,6 @@ Settings = { Deficit = false, Current = false, }, - ShowPowerText = false, }, Target = { Height = 30, @@ -48,6 +48,7 @@ Settings = { Percent = true, Deficit = false, Current = false, + PerCur = false, }, ShowPowerText = true, }, -- 1.7.9.5 From 7bbb4da55297d25de0932ebd6a4e6317846adb8f Mon Sep 17 00:00:00 2001 From: Lanerra Date: Thu, 12 May 2011 12:40:31 -0700 Subject: [PATCH 19/23] - Prioritized the displaying of debuff border color over aggro color. No longer will you not know if the tank has a debuff or not - Fixed Druid Power not working properly - Added functionality that detects if you're a healing class or not, and if so show one more buff and one less debuff to help keep track of HOTs and the like. Debuff border coloring works regardless - Fixed raid icons not displaying on target frame - Fixed a long standing problem with an improperly terminated 'if' function in the oUF_RuneBar support - Added code for killing the default Blizzard Group/Raid frames - Bumping to 1.19 --- oUF_Lanerra.lua | 108 +++++++++++++++++++++++++++++++++++++++++-------------- oUF_Lanerra.toc | 2 +- 2 files changed, 82 insertions(+), 28 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 7b6b0ff..dba967b 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,5 +1,5 @@ --[[ - Version = 1.18 + Version = 1.19 Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. @@ -104,14 +104,17 @@ local function UpdateBorder(self) local color if Debuff and Dispellable then color = colors.Debuff[Debuff] + elseif Debuff and Threat then + color = colors.Debuff[Debuff] elseif Threat and Threat > 1 then color = colors.threat[Threat] elseif Debuff then color = colors.Debuff[Debuff] elseif Threat and Threat > 0 then color = colors.threat[Threat] - end - + end + + if color then self:SetBackdropBorderColor(color[1], color[2], color[3], 1) else @@ -381,13 +384,13 @@ local function UpdateDruidPower(self, event, unit) return end - local UnitPower = PowerBarColor['MANA'] + local unitPower = PowerBarColor['MANA'] local mana = UnitPowerType('player') == 0 local index = GetShapeshiftForm() if (index == 1 or index == 3) then - if (UnitPower) then - self.Druid.Power:SetStatusBarColor(UnitPower.r, UnitPower.g, UnitPower.b) + if (unitPower) then + self.Druid.Power:SetStatusBarColor(unitPower.r, unitPower.g, unitPower.b) end self.Druid.Power:SetAlpha(1) @@ -854,7 +857,7 @@ local Stylish = function(self, unit, isSingle) self.Buffs['growth-y'] = 'UP' self.Buffs['initialAnchor'] = 'BOTTOMRIGHT' self.Buffs['num'] = math.floor((Settings.Units.Player.Width - 4 + GAP) / (30 + GAP)) - self.Buffs['size'] = 30 + self.Buffs['size'] = Settings.Units.Player.Height self.Buffs['spacing-x'] = GAP self.Buffs['spacing-y'] = GAP @@ -866,25 +869,32 @@ local Stylish = function(self, unit, isSingle) elseif (unit == 'target') then local GAP = 6 - local MAX_ICONS = math.floor((Settings.Units.Target.Width - 4 + GAP) / (Settings.Units.Target.Height + GAP)) - 1 - local NUM_BUFFS = math.max(2, math.floor(MAX_ICONS * 0.4)) - local NUM_DEBUFFS = math.min(MAX_ICONS - 1, math.floor(MAX_ICONS * 0.8)) + local MAX_ICONS = math.floor((Settings.Units.Target.Width + GAP) / (Settings.Units.Target.Height + GAP)) - 1 + local NUM_BUFFS = math.max(1, math.floor(MAX_ICONS * 0.2)) + local NUM_DEBUFFS = math.min(MAX_ICONS - 1, math.floor(MAX_ICONS * 0.8)) - self.Debuffs = CreateFrame('Frame', nil, self) + if (isHealer) then + local debuff = NUM_DEBUFFS - 1 + local buff = NUM_BUFFS + 1 + else + local debuff = NUM_DEBUFFS - 1 + local buff = NUM_BUFFS + 1 + end + + self.Debuffs = CreateFrame('Frame', nil, self) + self.Debuffs:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, 24) + self.Debuffs:SetWidth((Settings.Units.Target.Height * NUM_DEBUFFS - 1) + (GAP * (NUM_DEBUFFS - 1))) + self.Debuffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) self.Debuffs['growth-x'] = 'RIGHT' self.Debuffs['growth-y'] = 'UP' self.Debuffs['initialAnchor'] = 'BOTTOMLEFT' - self.Debuffs['num'] = NUM_DEBUFFS + self.Debuffs['num'] = debuffs self.Debuffs['showType'] = false - self.Debuffs['size'] = 30 + self.Debuffs['size'] = Settings.Units.Target.Height self.Debuffs['spacing-x'] = GAP self.Debuffs['spacing-y'] = GAP * 2 - self.Debuffs:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', 0, 10) - self.Debuffs:SetWidth((Settings.Units.Target.Height * NUM_DEBUFFS) + (GAP * (NUM_DEBUFFS - 1))) - self.Debuffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) - self.Debuffs.CustomFilter = CustomAuraFilter self.Debuffs.PostCreateIcon = PostCreateAuraIcon self.Debuffs.PostUpdateIcon = PostUpdateAuraIcon @@ -892,19 +902,20 @@ local Stylish = function(self, unit, isSingle) self.Debuffs.parent = self self.Buffs = CreateFrame('Frame', nil, self) + self.Buffs:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 2, 24) + self.Buffs:SetWidth((Settings.Units.Target.Height * NUM_BUFFS + 1) + (GAP * (NUM_BUFFS - 1))) + self.Buffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) self.Buffs['growth-x'] = 'LEFT' self.Buffs['growth-y'] = 'UP' self.Buffs['initialAnchor'] = 'BOTTOMRIGHT' - self.Buffs['num'] = NUM_BUFFS + self.Buffs['num'] = buffs self.Buffs['showType'] = false - self.Buffs['size'] = 30 + self.Buffs['size'] = Settings.Units.Target.Height self.Buffs['spacing-x'] = GAP self.Buffs['spacing-y'] = GAP * 2 - self.Buffs:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 0, 10) - self.Buffs:SetWidth((Settings.Units.Target.Height * NUM_BUFFS) + (GAP * (NUM_BUFFS - 1))) - self.Buffs:SetHeight((Settings.Units.Target.Height * 2) + (GAP * 2)) + self.Buffs.CustomFilter = CustomAuraFilter self.Buffs.PostCreateIcon = PostCreateAuraIcon @@ -915,12 +926,12 @@ local Stylish = function(self, unit, isSingle) -- DebuffHighlight Support self.DebuffHighlightBackdrop = false - self.DebuffHighlightFilter = false + self.DebuffHighlightFilter = true -- Various oUF plugins support if (unit == 'player') then -- oUF_RuneBar support - if(IsAddOnLoaded('oUF_RuneBar') and class == 'DEATHKNIGHT') then + if (IsAddOnLoaded('oUF_RuneBar') and class == 'DEATHKNIGHT') then self.RuneBar = {} for i = 1, 6 do self.RuneBar[i] = CreateFrame('StatusBar', '$parentRuneBar', self) @@ -941,9 +952,10 @@ local Stylish = function(self, unit, isSingle) self.RuneBar[i].bg:SetTexture(.1, .1, .1) end end + end -- DruidPower Support - if (select(2, UnitClass('player')) == 'DRUID') then + if (unit == 'player' and select(2, UnitClass('player')) == 'DRUID') then self.Druid = CreateFrame('Frame') self.Druid:SetParent(self) self.Druid:SetFrameStrata('LOW') @@ -962,7 +974,7 @@ local Stylish = function(self, unit, isSingle) self.DruidBorder:SetAllPoints(self.Druid.Power) self.DruidBorder:SetFrameLevel(self.Druid.Power:GetFrameLevel() + 2) - AddBorder(self.DruidBorder, Settings.Media.BorderSize, Settings.Media.BorderPadding) + AddBorder(self.DruidBorder, Settings.Media.BorderSize, 5) table.insert(self.__elements, UpdateDruidPower) self:RegisterEvent('UNIT_MANA', UpdateDruidPower) @@ -1033,9 +1045,18 @@ local Stylish = function(self, unit, isSingle) ComboPoints:SetFont(Settings.Media.Font, 24, 'OUTLINE') ComboPoints:SetJustifyH('CENTER') self:Tag(ComboPoints, '[LanCombo]') - end + end + -- Raid Icons + if (unit == 'target') then + self.RaidIcon = self.Overlay:CreateTexture('$parentRaidIcon', 'ARTWORK') + self.RaidIcon:SetHeight(18) + self.RaidIcon:SetWidth(18) + self.RaidIcon:SetPoint('CENTER', self.Overlay, 'TOP') + self.RaidIcon:SetTexture('Interface\\TargettingFrame\\UI-RaidTargetingIcons') + end + -- Custom sizes for our frames if (isSingle) then if (unit == 'player') then @@ -1436,6 +1457,39 @@ oUF:Factory(function(self) end end) +-- Killin' those pesky raid frames +for _, frame in pairs({ + CompactPartyFrame, + CompactRaidFrameManager, + CompactRaidFrameContainer, +}) do + frame:UnregisterAllEvents() + + hooksecurefunc(frame, 'Show', function(self) + self:Hide() + end) +end + +for _, button in pairs({ + 'OptionsButton', + + 'LockedModeToggle', + 'HiddenModeToggle', +}) do + _G['CompactRaidFrameManagerDisplayFrame'..button]:Hide() + _G['CompactRaidFrameManagerDisplayFrame'..button]:Disable() + _G['CompactRaidFrameManagerDisplayFrame'..button]:EnableMouse(false) +end + +for _, button in pairs({ + 'UnitFramePanelRaidStylePartyFrames', + 'FrameCategoriesButton11', +}) do + _G['InterfaceOptions'..button]:SetAlpha(0.35) + _G['InterfaceOptions'..button]:Disable() + _G['InterfaceOptions'..button]:EnableMouse(false) +end + -- Now all the solo stuff oUF:RegisterStyle('oUF_Lanerra', Stylish) oUF:Factory(function(self) diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index ada4cd6..51696c0 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ ## Interface: 40100 -## Version: 1.18 +## Version: 1.19 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5 From 492e17b118dc56c55d9759ba3f7fa29b7bbcc3cc Mon Sep 17 00:00:00 2001 From: Lanerra Date: Mon, 16 May 2011 00:00:56 -0700 Subject: [PATCH 20/23] - Added shadow to buff countdown text to improve readability - Corrected elements of party/raid frame still showing up even when they are disabled in config --- oUF_Lanerra.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index dba967b..871bd6a 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -436,7 +436,7 @@ local function PostCreateAuraIcon(iconframe, button) if button.cd:IsShown() then AuraIconCD_OnShow(button.cd) end - + button.icon:SetTexCoord(0.03, 0.97, 0.03, 0.97) button.overlay:Hide() @@ -472,6 +472,7 @@ local function PostUpdateAuraIcon(iconframe, unit, button, index, offset) child.text.SetFont = noop child.text:SetTextColor(1, 0.8, 0) + child.text:SetShadowOffset(1, -1) child.text.SetTextColor = noop child.text.SetVertexColor = noop @@ -1114,6 +1115,8 @@ local function StylishGroup(self, unit) else self:SetSize(Settings.Units.Party.Width, Settings.Units.Party.Height) end + else + return end -- Health bar display for group frames @@ -1273,6 +1276,8 @@ local function StylishRaid(self, unit) else self:SetSize(Settings.Units.Raid.Width, Settings.Units.Raid.Height) end + else + return end -- Health bar display for group frames @@ -1421,7 +1426,7 @@ oUF:Factory(function(self) if (Settings.Units.Raid.Healer) then local RaidShift, raid = false do - local UpdateRaid = CreateFrame'Frame' + local UpdateRaid = CreateFrame('Frame') UpdateRaid:RegisterEvent('RAID_ROSTER_UPDATE') UpdateRaid:SetScript('OnEvent', function(self) if RaidShift == false then return end -- 1.7.9.5 From 16febfcd061118d7a0eaaecdba7695a46921a01d Mon Sep 17 00:00:00 2001 From: Lanerra Date: Mon, 16 May 2011 00:05:33 -0700 Subject: [PATCH 21/23] Fix update that does not warrant 1.20 release - Added shadow offset to buff/debuff text - Corrected elements from party/raid frame not hiding when disabled --- oUF_Lanerra.lua | 2 +- oUF_Lanerra.toc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index 871bd6a..aeec23a 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,5 +1,5 @@ --[[ - Version = 1.19 + Version = 1.19.1 Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index 51696c0..293e975 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ ## Interface: 40100 -## Version: 1.19 +## Version: 1.19.1 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5 From 2d547090c516460786ae57aca9c2c7dd9077d71c Mon Sep 17 00:00:00 2001 From: Lanerra Date: Tue, 28 Jun 2011 14:54:46 -0700 Subject: [PATCH 22/23] Updated for 4.2 Please let me know if there are any bugs I missed. --- oUF_Lanerra.lua | 9 --------- 1 file changed, 9 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index aeec23a..a71bc59 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1486,15 +1486,6 @@ for _, button in pairs({ _G['CompactRaidFrameManagerDisplayFrame'..button]:EnableMouse(false) end -for _, button in pairs({ - 'UnitFramePanelRaidStylePartyFrames', - 'FrameCategoriesButton11', -}) do - _G['InterfaceOptions'..button]:SetAlpha(0.35) - _G['InterfaceOptions'..button]:Disable() - _G['InterfaceOptions'..button]:EnableMouse(false) -end - -- Now all the solo stuff oUF:RegisterStyle('oUF_Lanerra', Stylish) oUF:Factory(function(self) -- 1.7.9.5 From e3f8b43d90519d785cf3dc8948ca5a00b1abec41 Mon Sep 17 00:00:00 2001 From: Lanerra Date: Tue, 28 Jun 2011 14:55:53 -0700 Subject: [PATCH 23/23] Okay, this time, seriously, updated for 4.2. Please let me know if there are any bugs I missed. --- oUF_Lanerra.lua | 2 -- oUF_Lanerra.toc | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/oUF_Lanerra.lua b/oUF_Lanerra.lua index a71bc59..e0bd571 100644 --- a/oUF_Lanerra.lua +++ b/oUF_Lanerra.lua @@ -1,6 +1,4 @@ --[[ - Version = 1.19.1 - Copyright © 2010-2011 Lanerra. See LICENSE file for license terms. Special thanks to P3lim for inspiration, Neav for textures and inspiration, diff --git a/oUF_Lanerra.toc b/oUF_Lanerra.toc index 293e975..cfe44da 100644 --- a/oUF_Lanerra.toc +++ b/oUF_Lanerra.toc @@ -1,5 +1,5 @@ -## Interface: 40100 -## Version: 1.19.1 +## Interface: 40200 +## Version: 1.2 ## Title: oUF_Lanerra ## Notes: oUF layout by Lanerra -- 1.7.9.5