From 39c0ed874e25197b4f04ce3ee438e14035734ca0 Mon Sep 17 00:00:00 2001 From: Andrew6810 <16847730+andrew6180@users.noreply.github.com> Date: Fri, 21 Oct 2022 06:50:13 -0700 Subject: [PATCH] init --- DBM-AQ20/Ayamiss.lua | 45 + DBM-AQ20/Buru.lua | 50 + DBM-AQ20/DBM-AQ20.toc | 32 + DBM-AQ20/Kurinnaxx.lua | 76 + DBM-AQ20/Moam.lua | 15 + DBM-AQ20/Ossirian.lua | 63 + DBM-AQ20/Rajaxx.lua | 36 + DBM-AQ20/localization.en.lua | 100 + DBM-AQ20/localization.es.lua | 98 + DBM-AQ20/localization.ru.lua | 99 + DBM-AQ40/CThun.lua | 497 ++ DBM-AQ40/DBM-AQ40.toc | 35 + DBM-AQ40/Fankriss.lua | 122 + DBM-AQ40/Huhuran.lua | 106 + DBM-AQ40/Ouro.lua | 92 + DBM-AQ40/Sartura.lua | 370 ++ DBM-AQ40/Skeram.lua | 42 + DBM-AQ40/ThreeBugs.lua | 50 + DBM-AQ40/TwinEmps.lua | 80 + DBM-AQ40/Viscidus.lua | 18 + DBM-AQ40/localization.en.lua | 140 + DBM-AQ40/localization.es.lua | 138 + DBM-AQ40/localization.ru.lua | 153 + DBM-BWL/Broodlord.lua | 39 + DBM-BWL/Chromaggus.lua | 90 + DBM-BWL/DBM-BWL.toc | 34 + DBM-BWL/Ebonroc.lua | 47 + DBM-BWL/Firemaw.lua | 39 + DBM-BWL/Flamegor.lua | 40 + DBM-BWL/Nefarian.lua | 106 + DBM-BWL/Razorgore.lua | 35 + DBM-BWL/Vaelastrasz.lua | 47 + DBM-BWL/localization.en.lua | 136 + DBM-BWL/localization.es.lua | 125 + DBM-BWL/localization.ru.lua | 125 + DBM-BurningCrusade/DBM-BurningCrusade.toc | 13 + DBM-BurningCrusade/DBMv3-Compatibility.lua | 466 ++ DBM-BurningCrusade/localization.cn.lua | 34 + DBM-BurningCrusade/localization.en.lua | 31 + DBM-BurningCrusade/localization.es.lua | 33 + DBM-BurningCrusade/localization.tw.lua | 34 + DBM-Core/Changelog.txt | 218 + DBM-Core/DBM-Arrow.lua | 250 + DBM-Core/DBM-BossHealth.lua | 284 ++ DBM-Core/DBM-BossHealth.xml | 71 + DBM-Core/DBM-Core.lua | 4050 +++++++++++++++++ DBM-Core/DBM-Core.toc | 29 + DBM-Core/DBM-HelpFunctions.lua | 569 +++ DBM-Core/DBM-RangeCheck.lua | 440 ++ DBM-Core/DBT-Template.xml | 96 + DBM-Core/DBT.lua | 990 ++++ DBM-Core/license.txt | 61 + DBM-Core/localization.cn.lua | 134 + DBM-Core/localization.de.lua | 193 + DBM-Core/localization.en.lua | 221 + DBM-Core/localization.es.lua | 194 + DBM-Core/localization.fr.lua | 194 + DBM-Core/localization.kr.lua | 187 + DBM-Core/localization.ru.lua | 196 + DBM-Core/localization.tw.lua | 191 + DBM-Core/sounds/Alarm.ogg | Bin 0 -> 19869 bytes DBM-Core/sounds/Alert.ogg | Bin 0 -> 14428 bytes .../Creative Commons Sampling Plus 1.0.txt | 71 + DBM-Core/sounds/Info.ogg | Bin 0 -> 12394 bytes DBM-Core/sounds/Long.ogg | Bin 0 -> 44895 bytes DBM-Core/sounds/VictoryClassic.ogg | Bin 0 -> 65382 bytes DBM-Core/sounds/alarm1.wav | Bin 0 -> 76822 bytes DBM-Core/sounds/alarmclockbeeps.ogg | Bin 0 -> 50050 bytes DBM-Core/sounds/blip_8.ogg | Bin 0 -> 15926 bytes DBM-Core/sounds/license info.txt | 3 + DBM-Core/textures/CryptFiendBurrow.blp | Bin 0 -> 3916 bytes DBM-Core/textures/CryptFiendUnBurrow.blp | Bin 0 -> 3916 bytes DBM-Core/textures/GuardTower.blp | Bin 0 -> 3916 bytes DBM-Core/textures/Minimap-Button-Down.blp | Bin 0 -> 2564 bytes DBM-Core/textures/Minimap-Button-Up.blp | Bin 0 -> 2564 bytes DBM-Core/textures/OrcTower.blp | Bin 0 -> 3916 bytes DBM-Core/textures/Spark.blp | Bin 0 -> 2564 bytes DBM-Core/textures/arrows/Arrow-UP.blp | Bin 0 -> 350724 bytes DBM-Core/textures/arrows/Arrow.blp | Bin 0 -> 350724 bytes DBM-Core/textures/arrows/Arrow.m2 | Bin 0 -> 2944 bytes DBM-Core/textures/arrows/ArrowLicense.txt | 5 + DBM-Core/textures/arrows/GoldGreenDot.tga | Bin 0 -> 1039 bytes .../textures/arrows/MinimapArrow-Green.tga | Bin 0 -> 910 bytes DBM-Core/textures/default.tga | Bin 0 -> 24620 bytes DBM-Core/textures/glaze.tga | Bin 0 -> 32812 bytes DBM-Core/textures/otravi.tga | Bin 0 -> 32812 bytes DBM-Core/textures/smooth.tga | Bin 0 -> 32812 bytes DBM-GUI/DBM-GUI.lua | 2087 +++++++++ DBM-GUI/DBM-GUI.toc | 32 + DBM-GUI/DBM-GUI_DropDown.lua | 229 + DBM-GUI/DBM-GUI_DropDown.xml | 132 + DBM-GUI/DBM-GUI_Templates.xml | 799 ++++ DBM-GUI/license.txt | 61 + DBM-GUI/localization.cn.lua | 139 + DBM-GUI/localization.de.lua | 138 + DBM-GUI/localization.en.lua | 143 + DBM-GUI/localization.es.lua | 143 + DBM-GUI/localization.fr.lua | 139 + DBM-GUI/localization.kr.lua | 141 + DBM-GUI/localization.ru.lua | 142 + DBM-GUI/localization.tw.lua | 140 + DBM-GUI/textures/dot.blp | Bin 0 -> 23044 bytes DBM-GruulsLair/DBM-GruulsLair.toc | 24 + DBM-GruulsLair/Gruul.lua | 133 + DBM-GruulsLair/Maulgar.lua | 149 + DBM-GruulsLair/localization.cn.lua | 103 + DBM-GruulsLair/localization.de.lua | 98 + DBM-GruulsLair/localization.en.lua | 75 + DBM-GruulsLair/localization.es.lua | 55 + DBM-GruulsLair/localization.fr.lua | 1 + DBM-GruulsLair/localization.ru.lua | 79 + DBM-GruulsLair/localization.tw.lua | 95 + DBM-Karazhan/Attumen.lua | 75 + DBM-Karazhan/BigBadWolf.lua | 68 + DBM-Karazhan/Curator.lua | 120 + DBM-Karazhan/DBM-Karazhan.toc | 37 + DBM-Karazhan/MaidenOfVirtue.lua | 96 + DBM-Karazhan/Moroes.lua | 212 + DBM-Karazhan/NamedBeasts.lua | 86 + DBM-Karazhan/Netherspite.lua | 114 + DBM-Karazhan/Nightbane.lua | 109 + DBM-Karazhan/Opera.lua | 33 + DBM-Karazhan/PrinceMalchezaar.lua | 248 + DBM-Karazhan/RomuloAndJulianne.lua | 154 + DBM-Karazhan/ShadeOfAran.lua | 259 ++ DBM-Karazhan/TerestianIllhoof.lua | 107 + DBM-Karazhan/WizardOfOz.lua | 94 + DBM-Karazhan/localization.cn.lua | 312 ++ DBM-Karazhan/localization.de.lua | 312 ++ DBM-Karazhan/localization.en.lua | 444 ++ DBM-Karazhan/localization.es.lua | 325 ++ DBM-Karazhan/localization.fr.lua | 312 ++ DBM-Karazhan/localization.ru.lua | 312 ++ DBM-Karazhan/localization.tw.lua | 312 ++ DBM-MC/DBM-MC.toc | 36 + DBM-MC/Garr.lua | 49 + DBM-MC/Geddon.lua | 63 + DBM-MC/Gehennas.lua | 61 + DBM-MC/Golemagg.lua | 65 + DBM-MC/Lucifron.lua | 55 + DBM-MC/Magmadar.lua | 116 + DBM-MC/Majordomo.lua | 37 + DBM-MC/Ragnaros.lua | 95 + DBM-MC/Shazzrah.lua | 84 + DBM-MC/Sulfuron.lua | 69 + DBM-MC/localization.en.lua | 164 + DBM-MC/localization.es.lua | 130 + DBM-MC/localization.ru.lua | 131 + DBM-MagtheridonsLair/DBM-MagtheridonsLair.toc | 23 + DBM-MagtheridonsLair/Magtheridon.lua | 290 ++ DBM-MagtheridonsLair/localization.cn.lua | 35 + DBM-MagtheridonsLair/localization.de.lua | 26 + DBM-MagtheridonsLair/localization.en.lua | 54 + DBM-MagtheridonsLair/localization.es.lua | 23 + DBM-MagtheridonsLair/localization.fr.lua | 1 + DBM-MagtheridonsLair/localization.ru.lua | 25 + DBM-MagtheridonsLair/localization.tw.lua | 28 + DBM-Naxx/ArachnidQuarter/Anub'Rekhan.lua | 74 + DBM-Naxx/ArachnidQuarter/Faerlina.lua | 112 + DBM-Naxx/ArachnidQuarter/Maexxna.lua | 84 + DBM-Naxx/ConstructQuarter/Gluth.lua | 35 + DBM-Naxx/ConstructQuarter/Grobbulus.lua | 131 + DBM-Naxx/ConstructQuarter/Patchwerk.lua | 51 + DBM-Naxx/ConstructQuarter/Textures/arrow.tga | Bin 0 -> 16921 bytes DBM-Naxx/ConstructQuarter/Textures/lgpl.txt | 165 + DBM-Naxx/ConstructQuarter/Thaddius.lua | 186 + DBM-Naxx/DBM-Naxx.toc | 62 + DBM-Naxx/FrostwyrmLair/Kel'Thuzad.lua | 540 +++ DBM-Naxx/FrostwyrmLair/Sapphiron.lua | 108 + DBM-Naxx/MapSizes.lua | 8 + DBM-Naxx/MilitaryQuarter/Gothik.lua | 68 + DBM-Naxx/MilitaryQuarter/Horsemen.lua | 49 + DBM-Naxx/MilitaryQuarter/Razuvious.lua | 131 + DBM-Naxx/PlagueQuarter/Heigan.lua | 84 + DBM-Naxx/PlagueQuarter/Loatheb.lua | 103 + DBM-Naxx/PlagueQuarter/Noth.lua | 87 + DBM-Naxx/localization.de.lua | 401 ++ DBM-Naxx/localization.en.lua | 409 ++ DBM-Naxx/localization.es.lua | 408 ++ DBM-Naxx/localization.fr.lua | 396 ++ DBM-Naxx/localization.kr.lua | 416 ++ DBM-Naxx/localization.ru.lua | 396 ++ DBM-Naxx/localization.tw.lua | 399 ++ DBM-Onyxia/DBM-Onyxia.toc | 14 + DBM-Onyxia/Onyxia.lua | 158 + DBM-Onyxia/localization.en.lua | 36 + DBM-Outlands/DBM-Outlands.toc | 23 + DBM-Outlands/Doomwalker.lua | 80 + DBM-Outlands/Kazzak.lua | 210 + DBM-Outlands/localization.cn.lua | 59 + DBM-Outlands/localization.de.lua | 10 + DBM-Outlands/localization.en.lua | 63 + DBM-Outlands/localization.es.lua | 37 + DBM-Outlands/localization.fr.lua | 1 + DBM-Outlands/localization.ru.lua | 45 + DBM-Outlands/localization.tw.lua | 51 + DBM-Party-BC/Auct_Crypts/Maladaar.lua | 57 + DBM-Party-BC/Auct_Crypts/Shirrak.lua | 39 + DBM-Party-BC/Auct_SethekkHalls/Anzu.lua | 77 + DBM-Party-BC/Auct_SethekkHalls/Ikiss.lua | 33 + DBM-Party-BC/Auct_SethekkHalls/Syth.lua | 37 + DBM-Party-BC/Auct_ShadowLabyrinth/Hellmaw.lua | 43 + DBM-Party-BC/Auct_ShadowLabyrinth/Inciter.lua | 47 + DBM-Party-BC/Auct_ShadowLabyrinth/Murmur.lua | 76 + DBM-Party-BC/Auct_ShadowLabyrinth/Vorpil.lua | 63 + DBM-Party-BC/Auct_Tombs/Pandemonius.lua | 37 + DBM-Party-BC/Auct_Tombs/Shaffar.lua | 42 + DBM-Party-BC/Auct_Tombs/Tavarok.lua | 52 + DBM-Party-BC/Auct_Tombs/Yor.lua | 11 + DBM-Party-BC/CoT_BlackMorass/Aeonus.lua | 29 + DBM-Party-BC/CoT_BlackMorass/Deja.lua | 12 + DBM-Party-BC/CoT_BlackMorass/PortalTimers.lua | 73 + DBM-Party-BC/CoT_BlackMorass/Temporus.lua | 44 + DBM-Party-BC/CoT_OldHillsbrad/Drake.lua | 28 + DBM-Party-BC/CoT_OldHillsbrad/EpochHunter.lua | 12 + DBM-Party-BC/CoT_OldHillsbrad/Skarloc.lua | 48 + DBM-Party-BC/Coil_Slavepens/Mennu.lua | 19 + DBM-Party-BC/Coil_Slavepens/Quagmirran.lua | 11 + DBM-Party-BC/Coil_Slavepens/Rokmar.lua | 22 + DBM-Party-BC/Coil_Steamvault/Kalithresh.lua | 61 + DBM-Party-BC/Coil_Steamvault/Steamrigger.lua | 40 + DBM-Party-BC/Coil_Steamvault/Thespia.lua | 52 + DBM-Party-BC/Coil_Underbog/Ghazan.lua | 29 + DBM-Party-BC/Coil_Underbog/Hungarfen.lua | 29 + DBM-Party-BC/Coil_Underbog/Muselek.lua | 46 + DBM-Party-BC/Coil_Underbog/Stalker.lua | 50 + DBM-Party-BC/DBM-Party-BC.toc | 85 + .../Hellfire_BloodFurnace/Broggok.lua | 11 + .../Hellfire_BloodFurnace/Keli'dan.lua | 11 + DBM-Party-BC/Hellfire_BloodFurnace/Maker.lua | 21 + DBM-Party-BC/Hellfire_Ramp/Gargolmar.lua | 13 + DBM-Party-BC/Hellfire_Ramp/Omor.lua | 38 + DBM-Party-BC/Hellfire_Ramp/Vazruden.lua | 12 + .../Hellfire_ShatteredHalls/Kargath.lua | 35 + .../Hellfire_ShatteredHalls/Nethekurse.lua | 49 + .../Hellfire_ShatteredHalls/O'mrogg.lua | 35 + .../Hellfire_ShatteredHalls/Porung.lua | 23 + DBM-Party-BC/MagistersTerrace/Delrissa.lua | 51 + DBM-Party-BC/MagistersTerrace/Kael'thas.lua | 65 + DBM-Party-BC/MagistersTerrace/Selin.lua | 19 + DBM-Party-BC/MagistersTerrace/Vexallus.lua | 19 + DBM-Party-BC/TK_Arcatraz/Dalliah.lua | 43 + DBM-Party-BC/TK_Arcatraz/Skyriss.lua | 54 + DBM-Party-BC/TK_Arcatraz/Soccothrates.lua | 19 + DBM-Party-BC/TK_Arcatraz/Zereketh.lua | 44 + DBM-Party-BC/TK_Botanica/Freywinn.lua | 19 + DBM-Party-BC/TK_Botanica/Laj.lua | 28 + DBM-Party-BC/TK_Botanica/Sarannis.lua | 12 + DBM-Party-BC/TK_Botanica/Thorngrin.lua | 21 + DBM-Party-BC/TK_Botanica/WarpSplinter.lua | 35 + DBM-Party-BC/TK_Mechanar/Capacitus.lua | 42 + DBM-Party-BC/TK_Mechanar/Gyrokill.lua | 34 + DBM-Party-BC/TK_Mechanar/Ironhand.lua | 53 + DBM-Party-BC/TK_Mechanar/Pathaleon.lua | 28 + DBM-Party-BC/TK_Mechanar/Sepethrea.lua | 12 + DBM-Party-BC/localization.de.lua | 1155 +++++ DBM-Party-BC/localization.en.lua | 1157 +++++ DBM-Party-BC/localization.es.lua | 1154 +++++ DBM-Party-BC/localization.ru.lua | 1156 +++++ .../Blackrock Depths/Angerforge.lua | 14 + .../Blackrock Depths/Anubshiah.lua | 14 + .../Blackrock Depths/Argelmach.lua | 14 + .../Blackrock Depths/Baelgar.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Coren.lua | 14 + .../Blackrock Depths/Darkvire.lua | 14 + .../Blackrock Depths/Emperor.lua | 14 + .../Blackrock Depths/Eviscerator.lua | 14 + .../Blackrock Depths/Flamelash.lua | 14 + .../Blackrock Depths/Gerstahn.lua | 14 + .../Blackrock Depths/Gorrosh.lua | 14 + .../Blackrock Depths/Grebmar.lua | 14 + .../Blackrock Depths/Grizzle.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Hedrum.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Hurley.lua | 14 + .../Blackrock Depths/Incendius.lua | 14 + .../Blackrock Depths/Loregrain.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Magmus.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Okthor.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Panzor.lua | 14 + .../Blackrock Depths/Phalanx.lua | 14 + .../Blackrock Depths/Plugger.lua | 14 + .../Blackrock Depths/Princess.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Ribbly.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Roccor.lua | 14 + .../Blackrock Depths/Stilgiss.lua | 14 + .../Blackrock Depths/Summonerstomb.lua | 14 + .../Blackrock Depths/Theldren.lua | 14 + DBM-Party-Vanilla/Blackrock Depths/Verek.lua | 14 + .../Blackrock Spire/Anvilcrack.lua | 14 + .../Blackrock Spire/Bashguud.lua | 14 + .../Blackrock Spire/Battlelord.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Beast.lua | 14 + .../Blackrock Spire/Blackhand.lua | 14 + .../Blackrock Spire/ButcherS.lua | 14 + .../Blackrock Spire/Crystalfang.lua | 14 + .../Blackrock Spire/Doomhowl.lua | 14 + .../Blackrock Spire/Drakkisath.lua | 14 + .../Blackrock Spire/Felguard.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Gizrul.lua | 14 + .../Blackrock Spire/Grayhoof.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Grimaxe.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Gyth.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Halycon.lua | 14 + .../Blackrock Spire/Lordmagus.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Omokk.lua | 14 + .../Blackrock Spire/Pyroguard.lua | 14 + .../Blackrock Spire/Runewatcher.lua | 14 + .../Blackrock Spire/Smolderweb.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Solakar.lua | 14 + .../Blackrock Spire/Valthalak.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Voone.lua | 14 + .../Blackrock Spire/Voshgajin.lua | 14 + .../Blackrock Spire/Wyrmthalak.lua | 14 + DBM-Party-Vanilla/Blackrock Spire/Zigris.lua | 14 + DBM-Party-Vanilla/DBM-Party-Vanilla.toc | 126 + DBM-Party-Vanilla/Dire Maul/Alzzin.lua | 14 + DBM-Party-Vanilla/Dire Maul/Fengus.lua | 14 + DBM-Party-Vanilla/Dire Maul/Gordok.lua | 14 + DBM-Party-Vanilla/Dire Maul/Helnurath.lua | 14 + DBM-Party-Vanilla/Dire Maul/Hydrospawn.lua | 14 + DBM-Party-Vanilla/Dire Maul/Illyanna.lua | 14 + DBM-Party-Vanilla/Dire Maul/Immolthar.lua | 14 + DBM-Party-Vanilla/Dire Maul/Isalien.lua | 14 + DBM-Party-Vanilla/Dire Maul/Kalendris.lua | 14 + DBM-Party-Vanilla/Dire Maul/Kreeg.lua | 14 + DBM-Party-Vanilla/Dire Maul/Kromcrush.lua | 14 + DBM-Party-Vanilla/Dire Maul/Lethtendris.lua | 14 + DBM-Party-Vanilla/Dire Maul/Moldar.lua | 14 + DBM-Party-Vanilla/Dire Maul/Pimgib.lua | 14 + DBM-Party-Vanilla/Dire Maul/Princet.lua | 14 + DBM-Party-Vanilla/Dire Maul/Pusillin.lua | 14 + DBM-Party-Vanilla/Dire Maul/Slipkik.lua | 14 + DBM-Party-Vanilla/Dire Maul/Tendris.lua | 14 + DBM-Party-Vanilla/Dire Maul/Thimblejack.lua | 14 + DBM-Party-Vanilla/Dire Maul/Tsuzee.lua | 14 + DBM-Party-Vanilla/Dire Maul/Zevrim.lua | 14 + DBM-Party-Vanilla/Scholomance/Alexei.lua | 14 + .../Scholomance/BloodSteward.lua | 14 + .../Scholomance/DKDarkreaver.lua | 14 + DBM-Party-Vanilla/Scholomance/Darkmaster.lua | 14 + DBM-Party-Vanilla/Scholomance/FTKrastinov.lua | 14 + DBM-Party-Vanilla/Scholomance/IMalicia.lua | 14 + DBM-Party-Vanilla/Scholomance/Illucia.lua | 14 + .../Scholomance/JandiceBarov.lua | 14 + DBM-Party-Vanilla/Scholomance/Kirtonos.lua | 14 + DBM-Party-Vanilla/Scholomance/Kormok.lua | 14 + DBM-Party-Vanilla/Scholomance/LKPolkelt.lua | 14 + DBM-Party-Vanilla/Scholomance/MBlackpool.lua | 14 + .../Scholomance/RFrostwhisper.lua | 14 + DBM-Party-Vanilla/Scholomance/Rattlegore.lua | 14 + DBM-Party-Vanilla/Scholomance/Ravenian.lua | 14 + DBM-Party-Vanilla/Scholomance/Vectus.lua | 14 + DBM-Party-Vanilla/Stratholme/Anastari.lua | 12 + DBM-Party-Vanilla/Stratholme/Atiesh.lua | 12 + DBM-Party-Vanilla/Stratholme/Balnazzar.lua | 12 + DBM-Party-Vanilla/Stratholme/Barthilas.lua | 14 + DBM-Party-Vanilla/Stratholme/Forresten.lua | 14 + DBM-Party-Vanilla/Stratholme/Galford.lua | 14 + DBM-Party-Vanilla/Stratholme/Hammersmith.lua | 14 + DBM-Party-Vanilla/Stratholme/Maleki.lua | 14 + DBM-Party-Vanilla/Stratholme/Nerubenkan.lua | 14 + DBM-Party-Vanilla/Stratholme/Ramstein.lua | 14 + DBM-Party-Vanilla/Stratholme/Rivendare.lua | 14 + DBM-Party-Vanilla/Stratholme/Siabi.lua | 14 + DBM-Party-Vanilla/Stratholme/Skul.lua | 14 + .../Stratholme/SothosandJarien.lua | 14 + DBM-Party-Vanilla/Stratholme/Stonespine.lua | 14 + DBM-Party-Vanilla/Stratholme/Swordsmith.lua | 14 + DBM-Party-Vanilla/Stratholme/Timmy.lua | 14 + DBM-Party-Vanilla/Stratholme/Unforgiven.lua | 13 + DBM-Party-Vanilla/Stratholme/Willey.lua | 11 + DBM-Party-Vanilla/localization.en.lua | 561 +++ DBM-PvP/Arenas/Arenas.lua | 36 + DBM-PvP/Battlegrounds/Alterac.lua | 286 ++ DBM-PvP/Battlegrounds/Arathi.lua | 387 ++ DBM-PvP/Battlegrounds/Battlegrounds.lua | 103 + DBM-PvP/Battlegrounds/EyeOfTheStorm.lua | 345 ++ DBM-PvP/Battlegrounds/IsleOfConquest.lua | 230 + DBM-PvP/Battlegrounds/Warsong.lua | 269 ++ DBM-PvP/DBM-PvP.toc | 56 + DBM-PvP/Textures/GuardTower.blp | Bin 0 -> 3916 bytes DBM-PvP/Textures/OrcTower.blp | Bin 0 -> 3916 bytes DBM-PvP/localization.cn.lua | 189 + DBM-PvP/localization.de.lua | 230 + DBM-PvP/localization.en.lua | 228 + DBM-PvP/localization.es.lua | 231 + DBM-PvP/localization.fr.lua | 233 + DBM-PvP/localization.kr.lua | 231 + DBM-PvP/localization.ru.lua | 230 + DBM-PvP/localization.tw.lua | 229 + DBM-Serpentshrine/DBM-Serpentshrine.toc | 35 + DBM-Serpentshrine/Fathomlord.lua | 115 + DBM-Serpentshrine/Hydross.lua | 148 + DBM-Serpentshrine/Leotheras.lua | 197 + DBM-Serpentshrine/TheLurkerBelow.lua | 103 + DBM-Serpentshrine/Tidewalker.lua | 145 + DBM-Serpentshrine/Vashj.lua | 337 ++ DBM-Serpentshrine/localization.cn.lua | 193 + DBM-Serpentshrine/localization.de.lua | 182 + DBM-Serpentshrine/localization.en.lua | 253 + DBM-Serpentshrine/localization.es.lua | 188 + DBM-Serpentshrine/localization.kr.lua | 169 + DBM-Serpentshrine/localization.ru.lua | 193 + DBM-Serpentshrine/localization.tw.lua | 170 + DBM-SpellTimers/DBM-SpellTimers.toc | 24 + DBM-SpellTimers/SpellTimers.lua | 402 ++ DBM-SpellTimers/localization.cn.lua | 18 + DBM-SpellTimers/localization.de.lua | 18 + DBM-SpellTimers/localization.en.lua | 24 + DBM-SpellTimers/localization.es.lua | 23 + DBM-SpellTimers/localization.fr.lua | 21 + DBM-SpellTimers/localization.kr.lua | 17 + DBM-SpellTimers/localization.tw.lua | 18 + DBM-TheEye/Alar.lua | 294 ++ DBM-TheEye/DBM-TheEye.toc | 43 + DBM-TheEye/KaelThas.lua | 907 ++++ DBM-TheEye/Mobs.lua | 21 + DBM-TheEye/Solarian.lua | 416 ++ DBM-TheEye/VoidReaver.lua | 154 + DBM-TheEye/localization.cn.lua | 313 ++ DBM-TheEye/localization.de.lua | 265 ++ DBM-TheEye/localization.en.lua | 313 ++ DBM-TheEye/localization.es.lua | 218 + DBM-TheEye/localization.ru.lua | 266 ++ DBM-TheEye/localization.tw.lua | 274 ++ DBM-WorldEvents/Ahune.lua | 41 + DBM-WorldEvents/ApothecaryTrio.lua | 55 + DBM-WorldEvents/CorenDirebrew.lua | 52 + DBM-WorldEvents/DBM-WorldEvents.toc | 36 + DBM-WorldEvents/HeadlessHorseman.lua | 39 + DBM-WorldEvents/localization.de.lua | 117 + DBM-WorldEvents/localization.en.lua | 110 + DBM-WorldEvents/localization.es.lua | 112 + DBM-WorldEvents/localization.fr.lua | 57 + DBM-WorldEvents/localization.kr.lua | 112 + DBM-WorldEvents/localization.ru.lua | 112 + DBM-WorldEvents/localization.tw.lua | 112 + DBM-ZG/Arlokk.lua | 60 + DBM-ZG/Bloodlord.lua | 34 + DBM-ZG/DBM-ZG.toc | 36 + DBM-ZG/EdgeOfMadness.lua | 86 + DBM-ZG/Gahzranka.lua | 24 + DBM-ZG/Hakkar.lua | 81 + DBM-ZG/Jeklik.lua | 85 + DBM-ZG/Jindo.lua | 63 + DBM-ZG/Marli.lua | 68 + DBM-ZG/Thekal.lua | 96 + DBM-ZG/Venoxis.lua | 72 + DBM-ZG/localization.en.lua | 130 + DBM-ZG/localization.es.lua | 128 + DBM-ZG/localization.ru.lua | 129 + DBM-ZulAman/Akil'zon.lua | 154 + DBM-ZulAman/DBM-ZulAman.toc | 24 + DBM-ZulAman/Halazzi.lua | 157 + DBM-ZulAman/Jan'alai.lua | 146 + DBM-ZulAman/Malacrass.lua | 209 + DBM-ZulAman/Nalorakk.lua | 134 + DBM-ZulAman/Zul'jin.lua | 236 + DBM-ZulAman/localization.cn.lua | 113 + DBM-ZulAman/localization.de.lua | 113 + DBM-ZulAman/localization.en.lua | 137 + DBM-ZulAman/localization.es.lua | 113 + DBM-ZulAman/localization.kr.lua | 113 + DBM-ZulAman/localization.ru.lua | 113 + DBM-ZulAman/localization.tw.lua | 113 + README.md | 4 +- 466 files changed, 52263 insertions(+), 2 deletions(-) create mode 100644 DBM-AQ20/Ayamiss.lua create mode 100644 DBM-AQ20/Buru.lua create mode 100644 DBM-AQ20/DBM-AQ20.toc create mode 100644 DBM-AQ20/Kurinnaxx.lua create mode 100644 DBM-AQ20/Moam.lua create mode 100644 DBM-AQ20/Ossirian.lua create mode 100644 DBM-AQ20/Rajaxx.lua create mode 100644 DBM-AQ20/localization.en.lua create mode 100644 DBM-AQ20/localization.es.lua create mode 100644 DBM-AQ20/localization.ru.lua create mode 100644 DBM-AQ40/CThun.lua create mode 100644 DBM-AQ40/DBM-AQ40.toc create mode 100644 DBM-AQ40/Fankriss.lua create mode 100644 DBM-AQ40/Huhuran.lua create mode 100644 DBM-AQ40/Ouro.lua create mode 100644 DBM-AQ40/Sartura.lua create mode 100644 DBM-AQ40/Skeram.lua create mode 100644 DBM-AQ40/ThreeBugs.lua create mode 100644 DBM-AQ40/TwinEmps.lua create mode 100644 DBM-AQ40/Viscidus.lua create mode 100644 DBM-AQ40/localization.en.lua create mode 100644 DBM-AQ40/localization.es.lua create mode 100644 DBM-AQ40/localization.ru.lua create mode 100644 DBM-BWL/Broodlord.lua create mode 100644 DBM-BWL/Chromaggus.lua create mode 100644 DBM-BWL/DBM-BWL.toc create mode 100644 DBM-BWL/Ebonroc.lua create mode 100644 DBM-BWL/Firemaw.lua create mode 100644 DBM-BWL/Flamegor.lua create mode 100644 DBM-BWL/Nefarian.lua create mode 100644 DBM-BWL/Razorgore.lua create mode 100644 DBM-BWL/Vaelastrasz.lua create mode 100644 DBM-BWL/localization.en.lua create mode 100644 DBM-BWL/localization.es.lua create mode 100644 DBM-BWL/localization.ru.lua create mode 100644 DBM-BurningCrusade/DBM-BurningCrusade.toc create mode 100644 DBM-BurningCrusade/DBMv3-Compatibility.lua create mode 100644 DBM-BurningCrusade/localization.cn.lua create mode 100644 DBM-BurningCrusade/localization.en.lua create mode 100644 DBM-BurningCrusade/localization.es.lua create mode 100644 DBM-BurningCrusade/localization.tw.lua create mode 100644 DBM-Core/Changelog.txt create mode 100644 DBM-Core/DBM-Arrow.lua create mode 100644 DBM-Core/DBM-BossHealth.lua create mode 100644 DBM-Core/DBM-BossHealth.xml create mode 100644 DBM-Core/DBM-Core.lua create mode 100644 DBM-Core/DBM-Core.toc create mode 100644 DBM-Core/DBM-HelpFunctions.lua create mode 100644 DBM-Core/DBM-RangeCheck.lua create mode 100644 DBM-Core/DBT-Template.xml create mode 100644 DBM-Core/DBT.lua create mode 100644 DBM-Core/license.txt create mode 100644 DBM-Core/localization.cn.lua create mode 100644 DBM-Core/localization.de.lua create mode 100644 DBM-Core/localization.en.lua create mode 100644 DBM-Core/localization.es.lua create mode 100644 DBM-Core/localization.fr.lua create mode 100644 DBM-Core/localization.kr.lua create mode 100644 DBM-Core/localization.ru.lua create mode 100644 DBM-Core/localization.tw.lua create mode 100644 DBM-Core/sounds/Alarm.ogg create mode 100644 DBM-Core/sounds/Alert.ogg create mode 100644 DBM-Core/sounds/Creative Commons Sampling Plus 1.0.txt create mode 100644 DBM-Core/sounds/Info.ogg create mode 100644 DBM-Core/sounds/Long.ogg create mode 100644 DBM-Core/sounds/VictoryClassic.ogg create mode 100644 DBM-Core/sounds/alarm1.wav create mode 100644 DBM-Core/sounds/alarmclockbeeps.ogg create mode 100644 DBM-Core/sounds/blip_8.ogg create mode 100644 DBM-Core/sounds/license info.txt create mode 100644 DBM-Core/textures/CryptFiendBurrow.blp create mode 100644 DBM-Core/textures/CryptFiendUnBurrow.blp create mode 100644 DBM-Core/textures/GuardTower.blp create mode 100644 DBM-Core/textures/Minimap-Button-Down.blp create mode 100644 DBM-Core/textures/Minimap-Button-Up.blp create mode 100644 DBM-Core/textures/OrcTower.blp create mode 100644 DBM-Core/textures/Spark.blp create mode 100644 DBM-Core/textures/arrows/Arrow-UP.blp create mode 100644 DBM-Core/textures/arrows/Arrow.blp create mode 100644 DBM-Core/textures/arrows/Arrow.m2 create mode 100644 DBM-Core/textures/arrows/ArrowLicense.txt create mode 100644 DBM-Core/textures/arrows/GoldGreenDot.tga create mode 100644 DBM-Core/textures/arrows/MinimapArrow-Green.tga create mode 100644 DBM-Core/textures/default.tga create mode 100644 DBM-Core/textures/glaze.tga create mode 100644 DBM-Core/textures/otravi.tga create mode 100644 DBM-Core/textures/smooth.tga create mode 100644 DBM-GUI/DBM-GUI.lua create mode 100644 DBM-GUI/DBM-GUI.toc create mode 100644 DBM-GUI/DBM-GUI_DropDown.lua create mode 100644 DBM-GUI/DBM-GUI_DropDown.xml create mode 100644 DBM-GUI/DBM-GUI_Templates.xml create mode 100644 DBM-GUI/license.txt create mode 100644 DBM-GUI/localization.cn.lua create mode 100644 DBM-GUI/localization.de.lua create mode 100644 DBM-GUI/localization.en.lua create mode 100644 DBM-GUI/localization.es.lua create mode 100644 DBM-GUI/localization.fr.lua create mode 100644 DBM-GUI/localization.kr.lua create mode 100644 DBM-GUI/localization.ru.lua create mode 100644 DBM-GUI/localization.tw.lua create mode 100644 DBM-GUI/textures/dot.blp create mode 100644 DBM-GruulsLair/DBM-GruulsLair.toc create mode 100644 DBM-GruulsLair/Gruul.lua create mode 100644 DBM-GruulsLair/Maulgar.lua create mode 100644 DBM-GruulsLair/localization.cn.lua create mode 100644 DBM-GruulsLair/localization.de.lua create mode 100644 DBM-GruulsLair/localization.en.lua create mode 100644 DBM-GruulsLair/localization.es.lua create mode 100644 DBM-GruulsLair/localization.fr.lua create mode 100644 DBM-GruulsLair/localization.ru.lua create mode 100644 DBM-GruulsLair/localization.tw.lua create mode 100644 DBM-Karazhan/Attumen.lua create mode 100644 DBM-Karazhan/BigBadWolf.lua create mode 100644 DBM-Karazhan/Curator.lua create mode 100644 DBM-Karazhan/DBM-Karazhan.toc create mode 100644 DBM-Karazhan/MaidenOfVirtue.lua create mode 100644 DBM-Karazhan/Moroes.lua create mode 100644 DBM-Karazhan/NamedBeasts.lua create mode 100644 DBM-Karazhan/Netherspite.lua create mode 100644 DBM-Karazhan/Nightbane.lua create mode 100644 DBM-Karazhan/Opera.lua create mode 100644 DBM-Karazhan/PrinceMalchezaar.lua create mode 100644 DBM-Karazhan/RomuloAndJulianne.lua create mode 100644 DBM-Karazhan/ShadeOfAran.lua create mode 100644 DBM-Karazhan/TerestianIllhoof.lua create mode 100644 DBM-Karazhan/WizardOfOz.lua create mode 100644 DBM-Karazhan/localization.cn.lua create mode 100644 DBM-Karazhan/localization.de.lua create mode 100644 DBM-Karazhan/localization.en.lua create mode 100644 DBM-Karazhan/localization.es.lua create mode 100644 DBM-Karazhan/localization.fr.lua create mode 100644 DBM-Karazhan/localization.ru.lua create mode 100644 DBM-Karazhan/localization.tw.lua create mode 100644 DBM-MC/DBM-MC.toc create mode 100644 DBM-MC/Garr.lua create mode 100644 DBM-MC/Geddon.lua create mode 100644 DBM-MC/Gehennas.lua create mode 100644 DBM-MC/Golemagg.lua create mode 100644 DBM-MC/Lucifron.lua create mode 100644 DBM-MC/Magmadar.lua create mode 100644 DBM-MC/Majordomo.lua create mode 100644 DBM-MC/Ragnaros.lua create mode 100644 DBM-MC/Shazzrah.lua create mode 100644 DBM-MC/Sulfuron.lua create mode 100644 DBM-MC/localization.en.lua create mode 100644 DBM-MC/localization.es.lua create mode 100644 DBM-MC/localization.ru.lua create mode 100644 DBM-MagtheridonsLair/DBM-MagtheridonsLair.toc create mode 100644 DBM-MagtheridonsLair/Magtheridon.lua create mode 100644 DBM-MagtheridonsLair/localization.cn.lua create mode 100644 DBM-MagtheridonsLair/localization.de.lua create mode 100644 DBM-MagtheridonsLair/localization.en.lua create mode 100644 DBM-MagtheridonsLair/localization.es.lua create mode 100644 DBM-MagtheridonsLair/localization.fr.lua create mode 100644 DBM-MagtheridonsLair/localization.ru.lua create mode 100644 DBM-MagtheridonsLair/localization.tw.lua create mode 100644 DBM-Naxx/ArachnidQuarter/Anub'Rekhan.lua create mode 100644 DBM-Naxx/ArachnidQuarter/Faerlina.lua create mode 100644 DBM-Naxx/ArachnidQuarter/Maexxna.lua create mode 100644 DBM-Naxx/ConstructQuarter/Gluth.lua create mode 100644 DBM-Naxx/ConstructQuarter/Grobbulus.lua create mode 100644 DBM-Naxx/ConstructQuarter/Patchwerk.lua create mode 100644 DBM-Naxx/ConstructQuarter/Textures/arrow.tga create mode 100644 DBM-Naxx/ConstructQuarter/Textures/lgpl.txt create mode 100644 DBM-Naxx/ConstructQuarter/Thaddius.lua create mode 100644 DBM-Naxx/DBM-Naxx.toc create mode 100644 DBM-Naxx/FrostwyrmLair/Kel'Thuzad.lua create mode 100644 DBM-Naxx/FrostwyrmLair/Sapphiron.lua create mode 100644 DBM-Naxx/MapSizes.lua create mode 100644 DBM-Naxx/MilitaryQuarter/Gothik.lua create mode 100644 DBM-Naxx/MilitaryQuarter/Horsemen.lua create mode 100644 DBM-Naxx/MilitaryQuarter/Razuvious.lua create mode 100644 DBM-Naxx/PlagueQuarter/Heigan.lua create mode 100644 DBM-Naxx/PlagueQuarter/Loatheb.lua create mode 100644 DBM-Naxx/PlagueQuarter/Noth.lua create mode 100644 DBM-Naxx/localization.de.lua create mode 100644 DBM-Naxx/localization.en.lua create mode 100644 DBM-Naxx/localization.es.lua create mode 100644 DBM-Naxx/localization.fr.lua create mode 100644 DBM-Naxx/localization.kr.lua create mode 100644 DBM-Naxx/localization.ru.lua create mode 100644 DBM-Naxx/localization.tw.lua create mode 100644 DBM-Onyxia/DBM-Onyxia.toc create mode 100644 DBM-Onyxia/Onyxia.lua create mode 100644 DBM-Onyxia/localization.en.lua create mode 100644 DBM-Outlands/DBM-Outlands.toc create mode 100644 DBM-Outlands/Doomwalker.lua create mode 100644 DBM-Outlands/Kazzak.lua create mode 100644 DBM-Outlands/localization.cn.lua create mode 100644 DBM-Outlands/localization.de.lua create mode 100644 DBM-Outlands/localization.en.lua create mode 100644 DBM-Outlands/localization.es.lua create mode 100644 DBM-Outlands/localization.fr.lua create mode 100644 DBM-Outlands/localization.ru.lua create mode 100644 DBM-Outlands/localization.tw.lua create mode 100644 DBM-Party-BC/Auct_Crypts/Maladaar.lua create mode 100644 DBM-Party-BC/Auct_Crypts/Shirrak.lua create mode 100644 DBM-Party-BC/Auct_SethekkHalls/Anzu.lua create mode 100644 DBM-Party-BC/Auct_SethekkHalls/Ikiss.lua create mode 100644 DBM-Party-BC/Auct_SethekkHalls/Syth.lua create mode 100644 DBM-Party-BC/Auct_ShadowLabyrinth/Hellmaw.lua create mode 100644 DBM-Party-BC/Auct_ShadowLabyrinth/Inciter.lua create mode 100644 DBM-Party-BC/Auct_ShadowLabyrinth/Murmur.lua create mode 100644 DBM-Party-BC/Auct_ShadowLabyrinth/Vorpil.lua create mode 100644 DBM-Party-BC/Auct_Tombs/Pandemonius.lua create mode 100644 DBM-Party-BC/Auct_Tombs/Shaffar.lua create mode 100644 DBM-Party-BC/Auct_Tombs/Tavarok.lua create mode 100644 DBM-Party-BC/Auct_Tombs/Yor.lua create mode 100644 DBM-Party-BC/CoT_BlackMorass/Aeonus.lua create mode 100644 DBM-Party-BC/CoT_BlackMorass/Deja.lua create mode 100644 DBM-Party-BC/CoT_BlackMorass/PortalTimers.lua create mode 100644 DBM-Party-BC/CoT_BlackMorass/Temporus.lua create mode 100644 DBM-Party-BC/CoT_OldHillsbrad/Drake.lua create mode 100644 DBM-Party-BC/CoT_OldHillsbrad/EpochHunter.lua create mode 100644 DBM-Party-BC/CoT_OldHillsbrad/Skarloc.lua create mode 100644 DBM-Party-BC/Coil_Slavepens/Mennu.lua create mode 100644 DBM-Party-BC/Coil_Slavepens/Quagmirran.lua create mode 100644 DBM-Party-BC/Coil_Slavepens/Rokmar.lua create mode 100644 DBM-Party-BC/Coil_Steamvault/Kalithresh.lua create mode 100644 DBM-Party-BC/Coil_Steamvault/Steamrigger.lua create mode 100644 DBM-Party-BC/Coil_Steamvault/Thespia.lua create mode 100644 DBM-Party-BC/Coil_Underbog/Ghazan.lua create mode 100644 DBM-Party-BC/Coil_Underbog/Hungarfen.lua create mode 100644 DBM-Party-BC/Coil_Underbog/Muselek.lua create mode 100644 DBM-Party-BC/Coil_Underbog/Stalker.lua create mode 100644 DBM-Party-BC/DBM-Party-BC.toc create mode 100644 DBM-Party-BC/Hellfire_BloodFurnace/Broggok.lua create mode 100644 DBM-Party-BC/Hellfire_BloodFurnace/Keli'dan.lua create mode 100644 DBM-Party-BC/Hellfire_BloodFurnace/Maker.lua create mode 100644 DBM-Party-BC/Hellfire_Ramp/Gargolmar.lua create mode 100644 DBM-Party-BC/Hellfire_Ramp/Omor.lua create mode 100644 DBM-Party-BC/Hellfire_Ramp/Vazruden.lua create mode 100644 DBM-Party-BC/Hellfire_ShatteredHalls/Kargath.lua create mode 100644 DBM-Party-BC/Hellfire_ShatteredHalls/Nethekurse.lua create mode 100644 DBM-Party-BC/Hellfire_ShatteredHalls/O'mrogg.lua create mode 100644 DBM-Party-BC/Hellfire_ShatteredHalls/Porung.lua create mode 100644 DBM-Party-BC/MagistersTerrace/Delrissa.lua create mode 100644 DBM-Party-BC/MagistersTerrace/Kael'thas.lua create mode 100644 DBM-Party-BC/MagistersTerrace/Selin.lua create mode 100644 DBM-Party-BC/MagistersTerrace/Vexallus.lua create mode 100644 DBM-Party-BC/TK_Arcatraz/Dalliah.lua create mode 100644 DBM-Party-BC/TK_Arcatraz/Skyriss.lua create mode 100644 DBM-Party-BC/TK_Arcatraz/Soccothrates.lua create mode 100644 DBM-Party-BC/TK_Arcatraz/Zereketh.lua create mode 100644 DBM-Party-BC/TK_Botanica/Freywinn.lua create mode 100644 DBM-Party-BC/TK_Botanica/Laj.lua create mode 100644 DBM-Party-BC/TK_Botanica/Sarannis.lua create mode 100644 DBM-Party-BC/TK_Botanica/Thorngrin.lua create mode 100644 DBM-Party-BC/TK_Botanica/WarpSplinter.lua create mode 100644 DBM-Party-BC/TK_Mechanar/Capacitus.lua create mode 100644 DBM-Party-BC/TK_Mechanar/Gyrokill.lua create mode 100644 DBM-Party-BC/TK_Mechanar/Ironhand.lua create mode 100644 DBM-Party-BC/TK_Mechanar/Pathaleon.lua create mode 100644 DBM-Party-BC/TK_Mechanar/Sepethrea.lua create mode 100644 DBM-Party-BC/localization.de.lua create mode 100644 DBM-Party-BC/localization.en.lua create mode 100644 DBM-Party-BC/localization.es.lua create mode 100644 DBM-Party-BC/localization.ru.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Angerforge.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Anubshiah.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Argelmach.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Baelgar.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Coren.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Darkvire.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Emperor.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Eviscerator.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Flamelash.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Gerstahn.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Gorrosh.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Grebmar.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Grizzle.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Hedrum.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Hurley.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Incendius.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Loregrain.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Magmus.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Okthor.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Panzor.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Phalanx.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Plugger.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Princess.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Ribbly.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Roccor.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Stilgiss.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Summonerstomb.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Theldren.lua create mode 100644 DBM-Party-Vanilla/Blackrock Depths/Verek.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Anvilcrack.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Bashguud.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Battlelord.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Beast.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Blackhand.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/ButcherS.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Crystalfang.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Doomhowl.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Drakkisath.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Felguard.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Gizrul.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Grayhoof.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Grimaxe.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Gyth.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Halycon.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Lordmagus.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Omokk.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Pyroguard.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Runewatcher.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Smolderweb.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Solakar.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Valthalak.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Voone.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Voshgajin.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Wyrmthalak.lua create mode 100644 DBM-Party-Vanilla/Blackrock Spire/Zigris.lua create mode 100644 DBM-Party-Vanilla/DBM-Party-Vanilla.toc create mode 100644 DBM-Party-Vanilla/Dire Maul/Alzzin.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Fengus.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Gordok.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Helnurath.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Hydrospawn.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Illyanna.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Immolthar.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Isalien.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Kalendris.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Kreeg.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Kromcrush.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Lethtendris.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Moldar.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Pimgib.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Princet.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Pusillin.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Slipkik.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Tendris.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Thimblejack.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Tsuzee.lua create mode 100644 DBM-Party-Vanilla/Dire Maul/Zevrim.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Alexei.lua create mode 100644 DBM-Party-Vanilla/Scholomance/BloodSteward.lua create mode 100644 DBM-Party-Vanilla/Scholomance/DKDarkreaver.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Darkmaster.lua create mode 100644 DBM-Party-Vanilla/Scholomance/FTKrastinov.lua create mode 100644 DBM-Party-Vanilla/Scholomance/IMalicia.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Illucia.lua create mode 100644 DBM-Party-Vanilla/Scholomance/JandiceBarov.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Kirtonos.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Kormok.lua create mode 100644 DBM-Party-Vanilla/Scholomance/LKPolkelt.lua create mode 100644 DBM-Party-Vanilla/Scholomance/MBlackpool.lua create mode 100644 DBM-Party-Vanilla/Scholomance/RFrostwhisper.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Rattlegore.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Ravenian.lua create mode 100644 DBM-Party-Vanilla/Scholomance/Vectus.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Anastari.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Atiesh.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Balnazzar.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Barthilas.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Forresten.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Galford.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Hammersmith.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Maleki.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Nerubenkan.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Ramstein.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Rivendare.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Siabi.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Skul.lua create mode 100644 DBM-Party-Vanilla/Stratholme/SothosandJarien.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Stonespine.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Swordsmith.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Timmy.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Unforgiven.lua create mode 100644 DBM-Party-Vanilla/Stratholme/Willey.lua create mode 100644 DBM-Party-Vanilla/localization.en.lua create mode 100644 DBM-PvP/Arenas/Arenas.lua create mode 100644 DBM-PvP/Battlegrounds/Alterac.lua create mode 100644 DBM-PvP/Battlegrounds/Arathi.lua create mode 100644 DBM-PvP/Battlegrounds/Battlegrounds.lua create mode 100644 DBM-PvP/Battlegrounds/EyeOfTheStorm.lua create mode 100644 DBM-PvP/Battlegrounds/IsleOfConquest.lua create mode 100644 DBM-PvP/Battlegrounds/Warsong.lua create mode 100644 DBM-PvP/DBM-PvP.toc create mode 100644 DBM-PvP/Textures/GuardTower.blp create mode 100644 DBM-PvP/Textures/OrcTower.blp create mode 100644 DBM-PvP/localization.cn.lua create mode 100644 DBM-PvP/localization.de.lua create mode 100644 DBM-PvP/localization.en.lua create mode 100644 DBM-PvP/localization.es.lua create mode 100644 DBM-PvP/localization.fr.lua create mode 100644 DBM-PvP/localization.kr.lua create mode 100644 DBM-PvP/localization.ru.lua create mode 100644 DBM-PvP/localization.tw.lua create mode 100644 DBM-Serpentshrine/DBM-Serpentshrine.toc create mode 100644 DBM-Serpentshrine/Fathomlord.lua create mode 100644 DBM-Serpentshrine/Hydross.lua create mode 100644 DBM-Serpentshrine/Leotheras.lua create mode 100644 DBM-Serpentshrine/TheLurkerBelow.lua create mode 100644 DBM-Serpentshrine/Tidewalker.lua create mode 100644 DBM-Serpentshrine/Vashj.lua create mode 100644 DBM-Serpentshrine/localization.cn.lua create mode 100644 DBM-Serpentshrine/localization.de.lua create mode 100644 DBM-Serpentshrine/localization.en.lua create mode 100644 DBM-Serpentshrine/localization.es.lua create mode 100644 DBM-Serpentshrine/localization.kr.lua create mode 100644 DBM-Serpentshrine/localization.ru.lua create mode 100644 DBM-Serpentshrine/localization.tw.lua create mode 100644 DBM-SpellTimers/DBM-SpellTimers.toc create mode 100644 DBM-SpellTimers/SpellTimers.lua create mode 100644 DBM-SpellTimers/localization.cn.lua create mode 100644 DBM-SpellTimers/localization.de.lua create mode 100644 DBM-SpellTimers/localization.en.lua create mode 100644 DBM-SpellTimers/localization.es.lua create mode 100644 DBM-SpellTimers/localization.fr.lua create mode 100644 DBM-SpellTimers/localization.kr.lua create mode 100644 DBM-SpellTimers/localization.tw.lua create mode 100644 DBM-TheEye/Alar.lua create mode 100644 DBM-TheEye/DBM-TheEye.toc create mode 100644 DBM-TheEye/KaelThas.lua create mode 100644 DBM-TheEye/Mobs.lua create mode 100644 DBM-TheEye/Solarian.lua create mode 100644 DBM-TheEye/VoidReaver.lua create mode 100644 DBM-TheEye/localization.cn.lua create mode 100644 DBM-TheEye/localization.de.lua create mode 100644 DBM-TheEye/localization.en.lua create mode 100644 DBM-TheEye/localization.es.lua create mode 100644 DBM-TheEye/localization.ru.lua create mode 100644 DBM-TheEye/localization.tw.lua create mode 100644 DBM-WorldEvents/Ahune.lua create mode 100644 DBM-WorldEvents/ApothecaryTrio.lua create mode 100644 DBM-WorldEvents/CorenDirebrew.lua create mode 100644 DBM-WorldEvents/DBM-WorldEvents.toc create mode 100644 DBM-WorldEvents/HeadlessHorseman.lua create mode 100644 DBM-WorldEvents/localization.de.lua create mode 100644 DBM-WorldEvents/localization.en.lua create mode 100644 DBM-WorldEvents/localization.es.lua create mode 100644 DBM-WorldEvents/localization.fr.lua create mode 100644 DBM-WorldEvents/localization.kr.lua create mode 100644 DBM-WorldEvents/localization.ru.lua create mode 100644 DBM-WorldEvents/localization.tw.lua create mode 100644 DBM-ZG/Arlokk.lua create mode 100644 DBM-ZG/Bloodlord.lua create mode 100644 DBM-ZG/DBM-ZG.toc create mode 100644 DBM-ZG/EdgeOfMadness.lua create mode 100644 DBM-ZG/Gahzranka.lua create mode 100644 DBM-ZG/Hakkar.lua create mode 100644 DBM-ZG/Jeklik.lua create mode 100644 DBM-ZG/Jindo.lua create mode 100644 DBM-ZG/Marli.lua create mode 100644 DBM-ZG/Thekal.lua create mode 100644 DBM-ZG/Venoxis.lua create mode 100644 DBM-ZG/localization.en.lua create mode 100644 DBM-ZG/localization.es.lua create mode 100644 DBM-ZG/localization.ru.lua create mode 100644 DBM-ZulAman/Akil'zon.lua create mode 100644 DBM-ZulAman/DBM-ZulAman.toc create mode 100644 DBM-ZulAman/Halazzi.lua create mode 100644 DBM-ZulAman/Jan'alai.lua create mode 100644 DBM-ZulAman/Malacrass.lua create mode 100644 DBM-ZulAman/Nalorakk.lua create mode 100644 DBM-ZulAman/Zul'jin.lua create mode 100644 DBM-ZulAman/localization.cn.lua create mode 100644 DBM-ZulAman/localization.de.lua create mode 100644 DBM-ZulAman/localization.en.lua create mode 100644 DBM-ZulAman/localization.es.lua create mode 100644 DBM-ZulAman/localization.kr.lua create mode 100644 DBM-ZulAman/localization.ru.lua create mode 100644 DBM-ZulAman/localization.tw.lua diff --git a/DBM-AQ20/Ayamiss.lua b/DBM-AQ20/Ayamiss.lua new file mode 100644 index 0000000..95f6b4c --- /dev/null +++ b/DBM-AQ20/Ayamiss.lua @@ -0,0 +1,45 @@ +local mod = DBM:NewMod("Ayamiss", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 171 $"):sub(12, -3)) +mod:SetCreatureID(15369) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_AURA_APPLIED", + "SPELL_AURA_REMOVED", + "UNIT_HEALTH", + "PLAYER_ALIVE" +) + +local warnPhase2 = mod:NewPhaseAnnounce(2) +local warnParalyze = mod:NewTargetAnnounce(25725, 3) +local timerParalyze = mod:NewTargetTimer(30, 25725) + +local warned_P2 = false + +function mod:OnCombatStart(delay) + warned_P2 = false + self.vb.phase = 1 +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(25725) then + warnParalyze:Show(args.destName) + timerParalyze:Start(args.destName) + end +end + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(25725) then + timerParalyze:Cancel() + end +end + +function mod:UNIT_HEALTH(uId) + if not warned_P2 and self:GetUnitCreatureId(uId) == 15369 and UnitHealth(uId) / UnitHealthMax(uId) <= 0.70 then + warned_P2 = true + warnPhase2:Show() + self.vb.phase = 2 + end +end \ No newline at end of file diff --git a/DBM-AQ20/Buru.lua b/DBM-AQ20/Buru.lua new file mode 100644 index 0000000..f17796e --- /dev/null +++ b/DBM-AQ20/Buru.lua @@ -0,0 +1,50 @@ +local mod = DBM:NewMod("Buru", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 156 $"):sub(12, -3)) +mod:SetCreatureID(15370) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "CHAT_MSG_MONSTER_EMOTE", + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE", + "PLAYER_ALIVE" +) + +local warnPursue = mod:NewAnnounce("WarnPursue", 3) +local specWarnPursue = mod:NewSpecialWarning("SpecWarnPursue") + +local berserkTimer = mod:NewBerserkTimer(600) + +local specWarnWeakened = mod:NewSpecialWarning("Buru is Weakened!", nil, "Special warning for Buru's weakened phase") + +local eggsDead + +function mod:OnCombatStart(delay) + berserkTimer:Start() + eggsDead = 0 +end + +function mod:CHAT_MSG_MONSTE_EMOTE(msg) + if not msg then return end + local _, target = msg.find(L.PursueEmote) + if target then + warnPursue:Show(target) + if target == UnitName("player") then + specWarnPursue:Show() + end + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(1002041) then + specWarnWeakened:Show(); + end +end + +function mod:SPELL_AURA_APPLIED_DOSE(args) + if args:IsSpellID(1002041) then -- Miasma (Eye Tentacles) + specWarnWeakened:Show(); + end +end \ No newline at end of file diff --git a/DBM-AQ20/DBM-AQ20.toc b/DBM-AQ20/DBM-AQ20.toc new file mode 100644 index 0000000..7cde067 --- /dev/null +++ b/DBM-AQ20/DBM-AQ20.toc @@ -0,0 +1,32 @@ +## Interface: 30300 +## Title:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Ruins of Ahn'Qiraj|r +## Title-esES:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Ruinas de Ahn'Qiraj|r +## Title-ruRU:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Руины Ан'Киража|r +## LoadOnDemand: 1 +## RequiredDeps: DBM-Core +## SavedVariablesPerCharacter: DBMAQ20_SavedVars, DBMAQ20_SavedStats +## X-DBM-Mod: 1 +## X-DBM-Mod-Category: Vanilla +## X-DBM-Mod-Has-Heroic-Mode: 0 +## X-DBM-Mod-Name: The Ruins of Ahn'Qiraj +## X-DBM-Mod-Name-esES: Ruinas de Ahn'Qiraj +## X-DBM-Mod-Name-ruRU: Руины Ан'Киража +## X-DBM-Mod-Sort: 114 +## X-DBM-Mod-LoadZone: Nitram,Ruins of Ahn'Qiraj +## X-DBM-Mod-LoadZone-esES: Nitram,Ruinas de Ahn'Qiraj +## X-DBM-Mod-LoadZone-ruRU: Nitram,Руины Ан'Киража +## X-DBM-Mod-LoadZone-frFR: Nitram,Ruines d'Ahn'Qiraj +## X-DBM-Mod-LoadZone-esMX: Nitram,Ruinas de Ahn'Qiraj +## X-DBM-Mod-LoadZone-zhTW: 安其拉廢墟 +## X-DBM-Mod-LoadZone-zhCN: 安其拉废墟 +## X-DBM-Mod-LoadZone-koKR: 안퀴라즈 폐허 +## X-DBM-Mod-LoadZone-deDE: Nitram,Ruinen von Ahn'Qiraj +localization.en.lua +localization.es.lua +localization.ru.lua +Kurinnaxx.lua +Rajaxx.lua +Moam.lua +Buru.lua +Ayamiss.lua +Ossirian.lua \ No newline at end of file diff --git a/DBM-AQ20/Kurinnaxx.lua b/DBM-AQ20/Kurinnaxx.lua new file mode 100644 index 0000000..1687b9a --- /dev/null +++ b/DBM-AQ20/Kurinnaxx.lua @@ -0,0 +1,76 @@ +local mod = DBM:NewMod("Kurinnaxx", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 167 $"):sub(12, -3)) +mod:SetCreatureID(15348) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "UNIT_HEALTH", + "PLAYER_ALIVE" +) + + + +local hpCounter +local prewarnEnrage = mod:NewAnnounce("Soft Enrage Soon", 3, 1002069) +local warnAdds = mod:NewAnnounce("Adds Spawned", 2, 985050) +local warnEnrage = mod:NewAnnounce("Soft Enrage", 2, 1002069) +local timerAdds = mod:NewTimer(3, "Adds Spawn", 985050) + +function mod:OnCombatStart(delay) + hpCounter = 0 +end + +function mod:preEnrage() + prewarnEnrage:Show() +end + +function mod:alertEnrage() + warnEnrage:Show() +end + +function mod:alertAdds() + warnAdds:Show() +end + +function mod:addSpawn() + timerAdds:Show() + self:ScheduleMethod(3, "alertAdds") +end + +function mod:UNIT_HEALTH(args) + kuriHealth = math.max(0, UnitHealth("boss1")) / math.max(1, UnitHealthMax("boss1")) * 100; + if kuriHealth <= 90 and hpCounter == 0 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 1 + elseif kuriHealth <= 80 and hpCounter == 1 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 2 + elseif kuriHealth == 70 and hpCounter == 2 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 3 + elseif kuriHealth == 60 and hpCounter == 3 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 4 + elseif kuriHealth == 50 and hpCounter == 4 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 5 + elseif kuriHealth == 40 and hpCounter == 5 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 6 + elseif kuriHealth == 35 and hpCounter == 6 then + self:ScheduleMethod(0, "preEnrage") + hpCounter = 7 + elseif kuriHealth == 30 and hpCounter == 7 then + self:ScheduleMethod(0, "addSpawn") + self:ScheduleMethod(0, "warnEnrage") + hpCounter = 8 + elseif kuriHealth == 20 and hpCounter == 8 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 9 + elseif kuriHealth == 10 and hpCounter == 9 then + self:ScheduleMethod(0, "addSpawn") + hpCounter = 10 + end +end \ No newline at end of file diff --git a/DBM-AQ20/Moam.lua b/DBM-AQ20/Moam.lua new file mode 100644 index 0000000..7002879 --- /dev/null +++ b/DBM-AQ20/Moam.lua @@ -0,0 +1,15 @@ +local mod = DBM:NewMod("Moam", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15340) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE" +) + + + +function mod:OnCombatStart(delay) +end \ No newline at end of file diff --git a/DBM-AQ20/Ossirian.lua b/DBM-AQ20/Ossirian.lua new file mode 100644 index 0000000..acd8374 --- /dev/null +++ b/DBM-AQ20/Ossirian.lua @@ -0,0 +1,63 @@ +local mod = DBM:NewMod("Ossirian", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 175 $"):sub(12, -3)) +mod:SetCreatureID(15339) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE" +) + + + +local prewarnVoid = mod:NewAnnounce("Void Singularity Soon", 3, 1002140) +local prewarnAdds = mod:NewAnnounce("Adds Soon", 3, 1002126) + +local warnVoid = mod:NewAnnounce("Void Singularity Spawned", 2, 1002140) +local warnAdds = mod:NewAnnounce("Adds Spawned", 2, 1002126) + +local timerVoid = mod:NewTimer(45, "Void Singularity Spawn", 1002140) +local timerAdds = mod:NewTimer(15, "Next Add Wave", 1002126) + +function mod:preVoid() + prewarnVoid:Show() +end +function mod:preAdds() + prewarnAdds:Show() +end +function mod:alertVoid() + warnVoid:Show() +end +function mod:alertAdds() + warnAdds:Show() +end + +function mod:OnCombatStart(delay) + self:ScheduleMethod(0-delay, "initialAdds") + self:ScheduleMethod(0-delay, "repeatVoid") +end + +function mod:initialAdds() + timer1 = 10 + timerAdds:Show(timer1) + self:ScheduleMethod(timer1-5, "preAdds") + self:ScheduleMethod(timer1, "alertAdds") + self:ScheduleMethod(timer1, "repeatAdds") +end + +function mod:repeatAdds() + timer2 = 15 + timerAdds:Show(timer2) + self:ScheduleMethod(timer2-5, "preAdds") + self:ScheduleMethod(timer2, "alertAdds") + self:ScheduleMethod(timer2, "repeatAdds") +end + +function mod:repeatVoid() + timer4 = 45 + timerVoid:Show(timer4) + self:ScheduleMethod(timer4-5, "preVoid") + self:ScheduleMethod(timer4, "alertVoid") + self:ScheduleMethod(timer4, "repeatVoid") +end \ No newline at end of file diff --git a/DBM-AQ20/Rajaxx.lua b/DBM-AQ20/Rajaxx.lua new file mode 100644 index 0000000..421e0e5 --- /dev/null +++ b/DBM-AQ20/Rajaxx.lua @@ -0,0 +1,36 @@ +local mod = DBM:NewMod("Rajaxx", "DBM-AQ20", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 171 $"):sub(12, -3)) +mod:SetCreatureID(15341) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE", + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE" +) + + + +function mod:OnCombatStart(delay) +end + +local specWarnSpellReflect = mod:NewSpecialWarning("Spell Reflect: Stop Casting", nil, "Special warning for Spell Reflect") --4500009) +local warnSpellReflect = mod:NewSpellAnnounce(1002113) +local warnLust = mod:NewSpellAnnounce(1002090, 4) + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(1002113) then + warnSpellReflect:Show() + specWarnSpellReflect:Show() + elseif args:IsSpellID(1002090) then + warnLust:Show() + end +end + +function mod:SPELL_AURA_APPLIED_DOSE(args) + if args:IsSpellID(1002090) then + warnLust:Show() + end +end \ No newline at end of file diff --git a/DBM-AQ20/localization.en.lua b/DBM-AQ20/localization.en.lua new file mode 100644 index 0000000..9669a76 --- /dev/null +++ b/DBM-AQ20/localization.en.lua @@ -0,0 +1,100 @@ +local L + +--------------- +-- Kurinnaxx -- +--------------- +L = DBM:GetModLocalization("Kurinnaxx") + +L:SetGeneralLocalization{ + name = "Kurinnaxx" +} +L:SetWarningLocalization{ + WarnWound = "%s on >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown"), +} +------------ +-- Rajaxx -- +------------ +L = DBM:GetModLocalization("Rajaxx") + +L:SetGeneralLocalization{ + name = "General Rajaxx" +} +L:SetWarningLocalization{ + WarnWave = "Wave %s", + WarnBoss = "Boss incoming" +} +L:SetOptionLocalization{ + WarnWave = "Show announce for next incoming wave" +} +L:SetMiscLocalization{ + NpcPull = "They come now. Try not to get yourself killed, young blood.", + Wave1 = "Remember, Rajaxx, when I said I'd kill you last?", + Wave3 = "The time of our retribution is at hand! Let darkness reign in the hearts of our enemies!", + Wave4 = "No longer will we wait behind barred doors and walls of stone! No longer will our vengeance be denied! The dragons themselves will tremble before our wrath!", + Wave5 = "Fear is for the enemy! Fear and death!", + Wave6 = "Staghelm will whimper and beg for his life, just as his whelp of a son did! One thousand years of injustice will end this day!", + Wave7 = "Fandral! Your time has come! Go and hide in the Emerald Dream and pray we never find you!", + Wave8 = "Impudent fool! I will kill you myself!" +} + +---------- +-- Moam -- +---------- +L = DBM:GetModLocalization("Moam") + +L:SetGeneralLocalization{ + name = "Moam" +} + +---------- +-- Buru -- +---------- +L = DBM:GetModLocalization("Buru") + +L:SetGeneralLocalization{ + name = "Buru the Gorger" +} +L:SetWarningLocalization{ + WarnPursue = "Pursue on >%s<", + SpecWarnPursue = "Pursue on you", + WarnDismember = "%s on >%s< (%s)" +} +L:SetOptionLocalization{ + WarnPursue = "Announce pursue targets", + SpecWarnPursue = "Show special warning when you are being pursued", + WarnDismember = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(96, GetSpellInfo(96) or "unknown"), +} +L:SetMiscLocalization{ + PursueEmote = "%s sets eyes on %s!" +} + +------------- +-- Ayamiss -- +------------- +L = DBM:GetModLocalization("Ayamiss") + +L:SetGeneralLocalization{ + name = "Ayamiss the Hunter" +} + +-------------- +-- Ossirian -- +-------------- +L = DBM:GetModLocalization("Ossirian") + +L:SetGeneralLocalization{ + name = "Ossirian the Unscarred" +} +L:SetWarningLocalization{ + WarnVulnerable = "%s" +} +L:SetTimerLocalization{ + TimerVulnerable = "%s" +} +L:SetOptionLocalization{ + WarnVulnerable = "Announce weaknesses", + TimerVulnerable = "Show timer for weaknesses" +} \ No newline at end of file diff --git a/DBM-AQ20/localization.es.lua b/DBM-AQ20/localization.es.lua new file mode 100644 index 0000000..7a60153 --- /dev/null +++ b/DBM-AQ20/localization.es.lua @@ -0,0 +1,98 @@ +if GetLocale() ~= "esES" and GetLocale() ~= "esMX" then return end +local L + +--------------- +-- Kurinnaxx -- +--------------- +L = DBM:GetModLocalization("Kurinnaxx") + +L:SetGeneralLocalization{ + name = "Kurinnaxx" +} +L:SetWarningLocalization{ + WarnWound = "%s en >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown"), +} +------------ +-- Rajaxx -- +------------ +L = DBM:GetModLocalization("Rajaxx") + +L:SetGeneralLocalization{ + name = "General Rajaxx" +} +L:SetWarningLocalization{ + WarnWave = "Oleada %s", + WarnBoss = "Sale Boss" +} +L:SetOptionLocalization{ + WarnWave = "Mostrar aviso para oleada siguiente" +} +L:SetMiscLocalization{ + Wave1 = "They come now. Try not to get yourself killed, young blood.", --translate + Wave3 = "The time of our retribution is at hand! Let darkness reign in the hearts of our enemies!", --translate + Wave4 = "No longer will we wait behind barred doors and walls of stone! No longer will our vengeance be denied! The dragons themselves will tremble before our wrath!", --translate + Wave5 = "Fear is for the enemy! Fear and death!", --translate + Wave6 = "Staghelm will whimper and beg for his life, just as his whelp of a son did! One thousand years of injustice will end this day!", --translate + Wave7 = "Fandral! Your time has come! Go and hide in the Emerald Dream and pray we never find you!", --translate + Wave8 = "Impudent fool! I will kill you myself!" --translate +} + +---------- +-- Moam -- +---------- +L = DBM:GetModLocalization("Moam") + +L:SetGeneralLocalization{ + name = "Moam" +} + +---------- +-- Buru -- +---------- +L = DBM:GetModLocalization("Buru") + +L:SetGeneralLocalization{ + name = "Buru el Manducador" +} +L:SetWarningLocalization{ + WarnPursue = "Persigue a >%s<", + SpecWarnPursue = "Te persigue a ti" +} +L:SetOptionLocalization{ + WarnPursue = "Anunciar los objetivos perseguidos", + SpecWarnPursue = "Mostrar aviso especial cuando te persigan" +} +L:SetMiscLocalization{ + PursueEmote = "%s fija su mirada en %s!" +} + +------------- +-- Ayamiss -- +------------- +L = DBM:GetModLocalization("Ayamiss") + +L:SetGeneralLocalization{ + name = "Ayamiss el Cazador" +} + +-------------- +-- Ossirian -- +-------------- +L = DBM:GetModLocalization("Ossirian") + +L:SetGeneralLocalization{ + name = "Osirio el Sinmarcas" +} +L:SetWarningLocalization{ + WarnVulnerable = "%s" +} +L:SetTimerLocalization{ + TimerVulnerable = "%s" +} +L:SetOptionLocalization{ + WarnVulnerable = "Anunciar Debilidad", + TimerVulnerable = "Mostrar tiempo para Debilidad" +} \ No newline at end of file diff --git a/DBM-AQ20/localization.ru.lua b/DBM-AQ20/localization.ru.lua new file mode 100644 index 0000000..2be3868 --- /dev/null +++ b/DBM-AQ20/localization.ru.lua @@ -0,0 +1,99 @@ +if GetLocale() ~= "ruRU" then return end + +local L + +--------------- +-- Kurinnaxx -- +--------------- +L = DBM:GetModLocalization("Kurinnaxx") + +L:SetGeneralLocalization{ + name = "Куриннакс" +} +L:SetWarningLocalization{ + WarnWound = "%s на >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown"), +} +------------ +-- Rajaxx -- +------------ +L = DBM:GetModLocalization("Rajaxx") + +L:SetGeneralLocalization{ + name = "Генерал Раджакс" +} +L:SetWarningLocalization{ + WarnWave = "Волна %s", + WarnBoss = "Появление босса" +} +L:SetOptionLocalization{ + WarnWave = "Показывать предупреждение о следующей волне" +} +L:SetMiscLocalization{ + Wave1 = "They come now. Try not to get yourself killed, young blood.", + Wave3 = "The time of our retribution is at hand! Let darkness reign in the hearts of our enemies!", + Wave4 = "No longer will we wait behind barred doors and walls of stone! No longer will our vengeance be denied! The dragons themselves will tremble before our wrath!", + Wave5 = "Fear is for the enemy! Fear and death!", + Wave6 = "Staghelm will whimper and beg for his life, just as his whelp of a son did! One thousand years of injustice will end this day!", + Wave7 = "Fandral! Your time has come! Go and hide in the Emerald Dream and pray we never find you!", + Wave8 = "Impudent fool! I will kill you myself!" +} + +---------- +-- Moam -- +---------- +L = DBM:GetModLocalization("Moam") + +L:SetGeneralLocalization{ + name = "Моам" +} + +---------- +-- Buru -- +---------- +L = DBM:GetModLocalization("Buru") + +L:SetGeneralLocalization{ + name = "Буру Ненасытный" +} +L:SetWarningLocalization{ + WarnPursue = "Преследует >%s<", + SpecWarnPursue = "Преследует вас!" +} +L:SetOptionLocalization{ + WarnPursue = "Называть преследуемые цели", + SpecWarnPursue = "Показывать специальное предупреждение, когда преследование на вас" +} +L:SetMiscLocalization{ + PursueEmote = "%s sets eyes on %s!" +} + +------------- +-- Ayamiss -- +------------- +L = DBM:GetModLocalization("Ayamiss") + +L:SetGeneralLocalization{ + name = "Аямисса Охотница" +} + +-------------- +-- Ossirian -- +-------------- +L = DBM:GetModLocalization("Ossirian") + +L:SetGeneralLocalization{ + name = "Оссириан Неуязвимый" +} +L:SetWarningLocalization{ + WarnVulnerable = "%s" +} +L:SetTimerLocalization{ + TimerVulnerable = "%s" +} +L:SetOptionLocalization{ + WarnVulnerable = "Объявлять слабость", + TimerVulnerable = "Показывать таймер до слабости" +} \ No newline at end of file diff --git a/DBM-AQ40/CThun.lua b/DBM-AQ40/CThun.lua new file mode 100644 index 0000000..9525059 --- /dev/null +++ b/DBM-AQ40/CThun.lua @@ -0,0 +1,497 @@ +local mod = DBM:NewMod("CThun", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15727, 15589) +mod:RegisterCombat("combat") +mod:RegisterEvents( + "SPELL_SUMMON", + "CHAT_MSG_RAID_BOSS_EMOTE", + "UNIT_DIED", + "PLAYER_ALIVE", + "SPELL_PERIODIC_DAMAGE", + "SPELL_PERIODIC_HEAL", + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE", + "SPELL_CAST_START" +) + + + +----------Pre-warnings---------- +local prewarnEyeTentacleShadow = mod:NewAnnounce("Eye Tentacles: Shadow Soon", 3, 4500000) +local prewarnEyeTentacleFire = mod:NewAnnounce("Eye Tentacles: Fire Soon", 3, 4500054) +local prewarnEyeTentacleNature = mod:NewAnnounce("Eye Tentacles: Nature Soon", 3, 4500061) +local prewarnLookAway = mod:NewAnnounce("Look Away Soon", 3, 4500009) +local prewarnDarkGlare = mod:NewAnnounce("Dark Glare Soon", 3, 26029) +local prewarnManipulator = mod:NewAnnounce("Manipulator Tentacle Soon", 3, 4500067) +local prewarnDevastator = mod:NewAnnounce("Devastator Tentacle Soon", 3, 4500007) +local prewarnMalignant = mod:NewAnnounce("Malignant Tentacle Soon", 3, 4500053) +local prewarnGiantEye = mod:NewAnnounce("Giant Eye Tentacle Soon", 3, 4500060) +local prewarnGiantClaw = mod:NewAnnounce("Giant Claw Tentacle Soon", 3, 6524) +local prewarnStomach = mod:NewAnnounce("Stomach Phase Soon", 3, 26476) +----------Warnings---------- +local warnEyeTentacleShadow = mod:NewAnnounce("Eye Tentacles Spawned: Shadow", 2, 4500000) +local warnEyeTentacleFire = mod:NewAnnounce("Eye Tentacles Spawned: Fire", 2, 4500054) +local warnEyeTentacleNature = mod:NewAnnounce("Eye Tentacles Spawned: Nature", 2, 4500061) +local warnDarkGlare = mod:NewTargetAnnounce(26029, 4) +local warnEldritch = mod:NewSpellAnnounce(4500009, 4) +local warnManipulator = mod:NewAnnounce("Manipulator Tentacle Spawned", 2, 4500067) +local warnDevastator = mod:NewAnnounce("Devastator Tentacle Spawned", 2, 4500007) +local warnMalignant = mod:NewAnnounce("Malignant Tentacle Spawned", 2, 4500053) +local warnGiantEyeTentacle = mod:NewAnnounce("Giant Eye Tentacle Spawned", 2, 4500060) +local warnGiantClawTentacle = mod:NewAnnounce("Giant Claw Tentacle Spawned", 2, 6524) +local warnWeakened = mod:NewAnnounce("C'thun is Weakened!", 4, 25383) +local warnStomach = mod:NewAnnounce("Stomach Phase Now", 2, 26476) +local warnPhase2 = mod:NewPhaseAnnounce(2) + +----------Timers---------- +local timerDarkGlareCD = mod:NewCDTimer(87.5, 26029) +local timerDarkGlare = mod:NewTimer(37.5, "Dark Glare: Time Remaining", 26029) +local timerEldritch = mod:NewTimer(5, "LOOK AWAY", 4500009) +local timerEldritchCD = mod:NewCDTimer(30, 4500009) +local timerEyeTentacleShadow = mod:NewTimer(45, "Eye Tentacles: Shadow", 4500000) +local timerEyeTentacleFire = mod:NewTimer(45, "Eye Tentacles: Fire", 4500054) +local timerEyeTentacleNature = mod:NewTimer(45, "Eye Tentacles: Nature", 4500061) +local timerStomach = mod:NewTimer(21, "Enter Stomach", 26476) +local timerGiantEyeTentacle = mod:NewTimer(44, "Giant Eye Tentacle", 4500060) +local timerGiantClawTentacle = mod:NewTimer(14, "Giant Claw Tentacle", 6524) +local timerWeakened = mod:NewTimer(30, "Weakened: Time Remaining", 25383) +local timerManipulator = mod:NewTimer(15, "Manipulator Tentacle", 4500067) +local timerDevastator = mod:NewTimer(15, "Devastator Tentacle", 4500007) +local timerMalignant = mod:NewTimer(15, "Malignant Tentacle", 4500053) + +----------Misc---------- +mod:AddBoolOption("RangeFrame", true) +local etent +local lasttent +local phase +local miniadd +local eldfixglare + +----------JuniorStuff---------- +local specWarnEradicate = mod:NewSpecialWarning("Eradicate damage too high!", nil, "Special warning when taking >1000 damage from Eradicate", true) -- 4500054 +local specWarnConsume = mod:NewSpecialWarning("Consume healing too high!", nil, "Special warning when healing >4000 from Consume Essence", true) -- 4500061 +local specWarnMiasma = mod:NewSpecialWarningMove(4500001, true, "Special warning when standing in Miasma", true) +local warnMalignantGrasp = mod:NewAnnounce("%s Grabbed!", 4, nil, nil, "Announce when someone within 28 yards is grabbed by a Malignant Tentacle") +local specWarnRevelations = mod:NewSpecialWarning("Look Away", nil, "Special warning for Eldritch Revelations cast") --4500009) +local specWarnSensoryOverload = mod:NewSpecialWarningYou(4500068) +local specWarnDigestiveAcid = mod:NewSpecialWarningStack(26476, nil, 4) --(mod.Options.NumAcidStacks or 4)) +----------PreWarning Functions---------- +function mod:preShadow() + prewarnEyeTentacleShadow:Show() +end +function mod:preFire() + prewarnEyeTentacleFire:Show() +end +function mod:preNature() + prewarnEyeTentacleNature:Show() +end +function mod:preStomach() + prewarnStomach:Show() +end +function mod:preGiantClaw() + prewarnGiantClaw:Show() +end +function mod:preGiantEye() + prewarnGiantEye:Show() +end +function mod:preGlare() + prewarnDarkGlare:Show() +end +function mod:preFear() + if phase == 1 then + prewarnLookAway:Show() + end +end +function mod:preManipulator() + prewarnManipulator:Show() +end +function mod:preDevastator() + prewarnDevastator:Show() +end +function mod:preMalignant() + prewarnMalignant:Show() +end + +----------Alert Functions---------- +function mod:alertShadow() + warnEyeTentacleShadow:Show() + lasttent = 1 +end +function mod:alertFire() + warnEyeTentacleFire:Show() + lasttent = 2 +end +function mod:alertNature() + warnEyeTentacleNature:Show() + lasttent = 3 +end +function mod:alertStomach() + warnStomach:Show() +end +function mod:alertGiantClaw() + warnGiantClawTentacle:Show() +end +function mod:alertGiantEye() + warnGiantEyeTentacle:Show() +end +function mod:alertGlare() + warnDarkGlare:Show() +end +function mod:alertFear() + if phase == 1 then + warnEldritch:Show() + end +end +function mod:alertManipulator() + warnManipulator:Show() +end +function mod:alertDevastator() + warnDevastator:Show() +end +function mod:alertMalignant() + warnMalignant:Show() +end + +----------Real Functions---------- + +function mod:OnCombatStart(delay) + phase = 1 + self.vb.phase = 1 + etent = 1 + lasttent = 3 + miniadd = 1 + eldfixglare = 1 + self:ScheduleMethod(0-delay, "eldFearInitial") + self:ScheduleMethod(0-delay, "darkGlareInitial") + self:ScheduleMethod(0-delay, "eyeTentacle") +-- self:ScheduleMethod(0-delay, "miniAddInitial") + if self.Options.RangeFrame then + DBM.RangeCheck:Show(13) + end +end + +--function mod:miniAddInitial() +-- local timer14 = 10 +-- timerDevastator:Start(timer14) +-- miniadd = 2 +-- self:ScheduleMethod(timer14-5, "warnDevastator") +-- self:ScheduleMethod(timer14, "alertDevastator") +-- self:ScheduleMethod(timer14, "miniAdd") +--end + +--function mod:miniAdd() +-- local timer15 = 15 +--end + +function mod:eyeTentacle() + local timer1 = 45 + if etent == 1 then + timerEyeTentacleShadow:Start(timer1) + self:ScheduleMethod(timer1-5, "preShadow") + self:ScheduleMethod(timer1, "alertShadow") + etent = 2 + elseif etent == 2 then + timerEyeTentacleFire:Start(timer1) + self:ScheduleMethod(timer1-5, "preFire") + self:ScheduleMethod(timer1, "alertFire") + etent = 3 + elseif etent == 3 then + timerEyeTentacleNature:Start(timer1) + self:ScheduleMethod(timer1-5, "preNature") + self:ScheduleMethod(timer1, "alertNature") + etent = 1 + end + self:ScheduleMethod(timer1, "eyeTentacle") +end + +function mod:darkGlareInitial() + local timer11 = 50 + timerDarkGlareCD:Start(timer11) + self:ScheduleMethod(timer11-5, "preGlare") + self:ScheduleMethod(timer11, "alertGlare") +end + +function mod:darkGlare() + local timer10 = 87.5 + timerDarkGlare:Start() + timerDarkGlareCD:Start(timer10) + self:ScheduleMethod(timer10-5, "preGlare") + self:ScheduleMethod(timer10, "alertGlare") +end + +function mod:eldFearInitial() + if phase == 1 then + local timer13 = 30 + timerEldritchCD:Start(timer13) + self:ScheduleMethod(timer13-5, "preFear") + self:ScheduleMethod(timer13, "alertFear") + end +end + +function mod:eldFear() + if phase == 1 then + local timer12 = 30 + if eldfixglare == 1 then + timerEldritch:Start() + elseif eldfixglare == 2 then + self:ScheduleMethod(timer12-5, "eldFearGlareFix") + end + timerEldritchCD:Start(timer12) + self:ScheduleMethod(timer12-5, "preFear") + self:ScheduleMethod(timer12, "alertFear") + end +end + +function mod:eldFearGlareFix() + eldfixglare = 1 +end + +function mod:enterStomach() + local timer2a = 20 + local timer2b = 10 + if mod:IsDifficulty("heroic10") then + timerStomach:Start(timer2a) + self:ScheduleMethod(timer2a-5, "preStomach") + self:ScheduleMethod(timer2a, "alertStomach") + self:ScheduleMethod(timer2a, "enterStomach") + elseif mod:IsDifficulty("heroic25") then + timerStomach:Start(timer2b) + self:ScheduleMethod(timer2b-5, "preStomach") + self:ScheduleMethod(timer2b, "alertStomach") + self:ScheduleMethod(timer2b, "enterStomach") + end +end + +function mod:stomachWeaknessFix() + local timer4a = 15 + local timer4b = 5 + if mod:IsDifficulty("heroic10") then + timerStomach:Start(timer4a) + self:ScheduleMethod(timer4a-5, "preStomach") + self:ScheduleMethod(timer4a, "alertStomach") + self:ScheduleMethod(timer4a, "enterStomach") + elseif mod:IsDifficulty("heroic25") then + timerStomach:Start(timer4b) + self:ScheduleMethod(timer4b-5, "preStomach") + self:ScheduleMethod(timer4b, "alertStomach") + self:ScheduleMethod(timer4b, "enterStomach") + end +end + +function mod:clawTentacleInitial() + local timer3 = 14 + timerGiantClawTentacle:Start(timer3) + self:ScheduleMethod(timer3-5, "preGiantClaw") + self:ScheduleMethod(timer3, "alertGiantClaw") + self:ScheduleMethod(timer3, "clawTentacle") +end + +function mod:clawTentacleWeaknessFix() + local timer5 = 10 + timerGiantClawTentacle:Start(timer5) + self:ScheduleMethod(timer5-5, "preGiantClaw") + self:ScheduleMethod(timer5, "alertGiantClaw") + self:ScheduleMethod(timer5, "clawTentacle") +end + +function mod:clawTentacle() + local timer6 = 60 + timerGiantClawTentacle:Start(timer6) + self:ScheduleMethod(timer6-5, "preGiantClaw") + self:ScheduleMethod(timer6, "alertGiantClaw") + self:ScheduleMethod(timer6, "clawTentacle") +end + +function mod:geyeTentacleInitial() + local timer7 = 44 + timerGiantEyeTentacle:Start(timer7) + self:ScheduleMethod(timer7-5, "preGiantEye") + self:ScheduleMethod(timer7, "alertGiantEye") + self:ScheduleMethod(timer7, "geyeTentacle") +end + +function mod:geyeTentacleWeaknessFix() + local timer8 = 40 + timerGiantEyeTentacle:Start(timer8) + self:ScheduleMethod(timer8-5, "preGiantEye") + self:ScheduleMethod(timer8, "alertGiantEye") + self:ScheduleMethod(timer8, "geyeTentacle") +end + +function mod:geyeTentacle() + local timer9 = 60 + timerGiantEyeTentacle:Start(timer9) + self:ScheduleMethod(timer9-5, "preGiantEye") + self:ScheduleMethod(timer9, "alertGiantEye") + self:ScheduleMethod(timer9, "geyeTentacle") +end + +function mod:fixweaknessTimers() + self:ScheduleMethod(0, "stomachWeaknessFix") + self:ScheduleMethod(0, "clawTentacleWeaknessFix") + self:ScheduleMethod(0, "geyeTentacleWeaknessFix") + if lasttent == 1 then + etent = 2 + elseif lasttent == 2 then + etent = 3 + elseif lasttent == 3 then + etent = 1 + end + self:ScheduleMethod(0, "eyeTentacle") +end +function mod:phaseTwo() + warnPhase2:Show() + self.vb.phase = 2 + if lasttent == 1 then + etent = 2 + elseif lasttent == 2 then + etent = 3 + elseif lasttent == 3 then + etent = 1 + end + self:ScheduleMethod(6, "eyeTentacle") + self:ScheduleMethod(1, "enterStomach") + self:ScheduleMethod(0, "clawTentacleInitial") + self:ScheduleMethod(0, "geyeTentacleInitial") +end + +function mod:UNIT_DIED(args) + local cid = self:GetCIDFromGUID(args.destGUID) + if cid == 15589 and phase == 1 then + phase = 2 + timerEldritch:Stop() + timerEldritchCD:Stop() + timerDarkGlare:Stop() + timerDarkGlareCD:Stop() + timerEyeTentacleFire:Stop() + timerEyeTentacleNature:Stop() + timerEyeTentacleShadow:Stop() + self:UnscheduleMethod("eyeTentacle") + self:UnscheduleMethod("preShadow") + self:UnscheduleMethod("preFire") + self:UnscheduleMethod("preNature") + self:UnscheduleMethod("alertShadow") + self:UnscheduleMethod("alertFire") + self:UnscheduleMethod("alertNature") + self:UnscheduleMethod("eldFear") + self:UnscheduleMethod("eldFearInitial") + self:UnscheduleMethod("preFear") + self:UnscheduleMethod("alertFear") + self:UnscheduleMethod("darkGlare") + self:UnscheduleMethod("darkGlareInitial") + self:UnscheduleMethod("preGlare") + self:UnscheduleMethod("alertGlare") + + self:ScheduleMethod(0, "phaseTwo") + end +end + +function mod:CHAT_MSG_RAID_BOSS_EMOTE(msg) + if msg:find(L.Eldritch) then + self:ScheduleMethod(0, "eldFear") + specWarnRevelations:Show(); + end + if msg:find(L.Glare) then + eldfixglare = 2 + timerEldritchCD:Stop() + self:UnscheduleMethod("eldFearInitial") + self:UnscheduleMethod("eldFear") + self:UnscheduleMethod("preFear") + self:UnscheduleMethod("alertFear") + self:ScheduleMethod(0, "darkGlare") + self:ScheduleMethod(37.5, "eldFear") + end + if msg:find(L.weakened) then + timerWeakened:Start() + warnWeakened:Show() + timerGiantClawTentacle:Stop() + timerGiantEyeTentacle:Stop() + timerStomach:Stop() + timerEyeTentacleFire:Stop() + timerEyeTentacleNature:Stop() + timerEyeTentacleShadow:Stop() + self:UnscheduleMethod("enterStomach") + self:UnscheduleMethod("stomachWeaknessFix") + self:UnscheduleMethod("preStomach") + self:UnscheduleMethod("alertStomach") + self:UnscheduleMethod("clawTentacleInitial") + self:UnscheduleMethod("clawTentacleWeaknessFix") + self:UnscheduleMethod("clawTentacle") + self:UnscheduleMethod("preGiantClaw") + self:UnscheduleMethod("alertGiantClaw") + self:UnscheduleMethod("geyeTentacle") + self:UnscheduleMethod("geyeTentacleInitial") + self:UnscheduleMethod("geyeTentacleWeaknessFix") + self:UnscheduleMethod("preGiantEye") + self:UnscheduleMethod("alertGiantEye") + self:UnscheduleMethod("eyeTentacle") + self:UnscheduleMethod("preShadow") + self:UnscheduleMethod("alertShadow") + self:UnscheduleMethod("preFire") + self:UnscheduleMethod("alertFire") + self:UnscheduleMethod("preNature") + self:UnscheduleMethod("alertNature") + self:ScheduleMethod(30, "fixweaknessTimers") + end +end + +function mod:SPELL_PERIODIC_DAMAGE(args) + if args:IsSpellID(4500054) then -- Eradicate (Eye Tentacles) + if args:IsPlayer() and (((args.amount or 0) + (args.resisted or 0) + (args.absorbed or 0)) > 1000) then + specWarnEradicate:Show(); + end + end +end + +function mod:SPELL_PERIODIC_HEAL(args) + if args:IsSpellID(4500062) then -- Consume Essence (Eye Tentacles, debuff is 61) + if args:IsPlayerSource() and ((args.amount or 0) > 4000) then + specWarnConsume:Show(); + end + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(4500026) then -- Malignant Grasp (Malignant Tentacle) + if args.destName then + if (not args:IsPlayer()) then + local uId = DBM:GetRaidUnitId(args.destName) + if uId then + local inRange = CheckInteractDistance(uId, 4) + if inRange then + warnMalignantGrasp:Show(args.destName); + end + else + warnMalignantGrasp:Show(args.destName); + end + end + end +elseif args:IsSpellID(4500001) then -- Miasma (Eye Tentacles) + if args:IsPlayer() then + specWarnMiasma:Show(); + end + end +end + +function mod:SPELL_AURA_APPLIED_DOSE(args) + if args:IsSpellID(26476) then -- Digestive Acid (Stomach) + if args:IsPlayer() and ((args.amount or 1) >= 4) then --(self.Options.NumAcidStacks or 4)) then + specWarnDigestiveAcid:Show(args.amount); + end + elseif args:IsSpellID(4500001) then -- Miasma (Eye Tentacles) + if args:IsPlayer() then + specWarnMiasma:Show(); + end + end +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(4500068) then + local targetname = self:GetBossTarget(26180) -- self:GetBossTarget(157252) + if targetname and (targetname == UnitName("PLAYER")) then + specWarnSensoryOverload:Show(); + end + end +end \ No newline at end of file diff --git a/DBM-AQ40/DBM-AQ40.toc b/DBM-AQ40/DBM-AQ40.toc new file mode 100644 index 0000000..611b3ec --- /dev/null +++ b/DBM-AQ40/DBM-AQ40.toc @@ -0,0 +1,35 @@ +## Interface: 30300 +## Title:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Temple of Ahn'Qiraj|r +## Title-esES:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Templo de Ahn'Qiraj|r +## Title-ruRU:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Храм Ан'Киража|r +## LoadOnDemand: 1 +## RequiredDeps: DBM-Core +## SavedVariablesPerCharacter: DBMAQ40_SavedVars, DBMAQ40_SavedStats +## X-DBM-Mod: 1 +## X-DBM-Mod-Category: Vanilla +## X-DBM-Mod-Has-Heroic-Mode: 0 +## X-DBM-Mod-Name: The Temple of Ahn'Qiraj +## X-DBM-Mod-Name-esES: Templo de Ahn'Qiraj +## X-DBM-Mod-Name-ruRU: Храм Ан'Киража +## X-DBM-Mod-Sort: 115 +## X-DBM-Mod-LoadZone: Nitram,Ahn'Qiraj +## X-DBM-Mod-LoadZone-esES: Nitram,Ahn'Qiraj +## X-DBM-Mod-LoadZone-ruRU: Nitram,Ан'Кираж +## X-DBM-Mod-LoadZone-frFR: Nitram,Ahn'Qiraj +## X-DBM-Mod-LoadZone-esMX: Nitram,Ahn'Qiraj +## X-DBM-Mod-LoadZone-zhTW: 安其拉 +## X-DBM-Mod-LoadZone-zhCN: 安其拉 +## X-DBM-Mod-LoadZone-koKR: 안퀴라즈 +## X-DBM-Mod-LoadZone-deDE: Nitram,Ahn'Qiraj +localization.en.lua +localization.es.lua +localization.ru.lua +Skeram.lua +ThreeBugs.lua +Sartura.lua +Fankriss.lua +Viscidus.lua +Huhuran.lua +TwinEmps.lua +CThun.lua +Ouro.lua \ No newline at end of file diff --git a/DBM-AQ40/Fankriss.lua b/DBM-AQ40/Fankriss.lua new file mode 100644 index 0000000..8ad199a --- /dev/null +++ b/DBM-AQ40/Fankriss.lua @@ -0,0 +1,122 @@ +local mod = DBM:NewMod("Fankriss", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 184 $"):sub(12, -3)) +mod:SetCreatureID(15510) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE" +) + +local warnHatchlingssoon = mod:NewAnnounce("Vekniss Hatchlings Soon", 2, 1002256) +local warnExplodingsoon = mod:NewAnnounce("Volatile Explosion Soon", 3, 1002356) +local warnSpawnssoon = mod:NewAnnounce("Spawn of Fankriss Soon", 4, 1002297) +local warnHatchlings = mod:NewAnnounce("Vekniss Hatchlings Spawned", 2, 1002256) +local warnExploding = mod:NewAnnounce("Volatile Hatchlings Exploding", 3, 1002356) +local warnSpawns = mod:NewAnnounce("Spawn of Fankriss Spawned", 4, 1002297) + +local timerHatchlingA = mod:NewTimer(60, "Hatchlings", 1002256) +local timerExplosionA = mod:NewTimer(60, "Explosion", 1002356) +local timerHatchlingB = mod:NewTimer(60, "Hatchlings", 1002256) +local timerExplosionB = mod:NewTimer(60, "Explosion", 1002356) +local timerSpawnofFankriss = mod:NewTimer(60, "Spawn of Fankriss", 1002297) + +local berserkTimer = mod:NewBerserkTimer(540) + +function mod:veknissSoon() + warnHatchlingssoon:Show() +end + +function mod:veknissWarning() + warnHatchlings:Show() +end + +function mod:explosionSoon() + warnExplodingsoon:Show() +end + +function mod:explosionWarning() + warnExploding:Show() +end + +function mod:spawnSoon() + warnSpawnssoon:Show() +end + +function mod:spawnWarning() + warnSpawns:Show() +end + +function mod:OnCombatStart(delay) + berserkTimer:Start(-delay) + self:ScheduleMethod(10-delay, "veknissHatchlingA") + self:ScheduleMethod(45-delay, "veknissHatchlingB") + self:ScheduleMethod(30-delay, "spawnofFankriss") + self:ScheduleMethod(0-delay, "veknissInitial") + self:ScheduleMethod(0-delay, "spawnInitial") +end + +function mod:veknissInitial() + local timeri = 10 + timerHatchlingB:Start(timeri) + self:ScheduleMethod(timeri-5,"veknissSoon") + self:ScheduleMethod(timeri, "veknissWarning") + self:ScheduleMethod(timeri, "explosionInitial") +end + +function mod:explosionInitial() + local timere = 40 + timerExplosionB:Start(timere) + self:ScheduleMethod(timere-5, "explosionSoon") + self:ScheduleMethod(timere, "explosionWarning") +end + +function mod:spawnInitial() + local timers = 30 + timerSpawnofFankriss:Start(30) + self:ScheduleMethod(timers-5, "spawnSoon") + self:ScheduleMethod(timers, "spawnWarning") +end + +function mod:veknissHatchlingA() + local timera = 35 + timerHatchlingA:Start(timera) + self:ScheduleMethod(timera*2, "veknissHatchlingA") + self:ScheduleMethod(timera-5,"veknissSoon") + self:ScheduleMethod(timera, "veknissWarning") + self:ScheduleMethod(timera, "explosionA") +end + +function mod:explosionA() + local timeraa = 40 + timerExplosionA:Start(timeraa) + self:ScheduleMethod(timeraa-5, "explosionSoon") + self:ScheduleMethod(timeraa, "explosionWarning") + self:ScheduleMethod(timeraa*2, "explosionA") +end + +function mod:veknissHatchlingB() + local timerb = 35 + timerHatchlingB:Start(timerb) + self:ScheduleMethod(timerb*2, "veknissHatchlingB") + self:ScheduleMethod(timerb-5,"veknissSoon") + self:ScheduleMethod(timerb, "veknissWarning") + self:ScheduleMethod(timerb, "explosionB") +end + +function mod:explosionB() + local timerbb = 40 + timerExplosionB:Start(timerbb) + self:ScheduleMethod(timerbb-5, "explosionSoon") + self:ScheduleMethod(timerbb, "explosionWarning") + self:ScheduleMethod(timerbb*2, "explosionB") +end + +function mod:spawnofFankriss() + local timerc = 35 + timerSpawnofFankriss:Start(timerc) + self:ScheduleMethod(timerc, "spawnofFankriss") + self:ScheduleMethod(timerc-5, "spawnSoon") + self:ScheduleMethod(timerc, "spawnWarning") +end diff --git a/DBM-AQ40/Huhuran.lua b/DBM-AQ40/Huhuran.lua new file mode 100644 index 0000000..d5de1e9 --- /dev/null +++ b/DBM-AQ40/Huhuran.lua @@ -0,0 +1,106 @@ +local mod = DBM:NewMod("Huhuran", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 185 $"):sub(12, -3)) +mod:SetCreatureID(15509) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE", + "SPELL_AURA_REMOVED", + "UNIT_HEALTH", + "PLAYER_ALIVE" +) + +local warnSoon = mod:NewAnnounce("Adds Soon", 2, 1002077) +local warnSpawn = mod:NewAnnounce("Adds Spawned", 3, 1002077) +local prewarnSoftEnrage = mod:NewAnnounce("Soft Enrage Soon", 2, 1002304) +local warnSoftEnrage = mod:NewAnnounce("Soft Enrage Now", 3, 1002304) +local timerSpawn = mod:NewTimer(60, "Adds", 1002077) + +local warnSting = mod:NewTargetAnnounce(26180, 2) +local warnAcid = mod:NewAnnounce("WarnAcid", 3) + +local timerSting = mod:NewBuffActiveTimer(12, 26180) +local timerStingCD = mod:NewCDTimer(20, 26180) +local timerAcid = mod:NewTargetTimer(30, 26050) + +local specWarnAcid = mod:NewSpecialWarningStack(26050, nil, 10) + +local berserkTimer = mod:NewBerserkTimer(420) +local StingTargets = {} +local check1 +local check2 + +function mod:OnCombatStart(delay) + berserkTimer:Start(-delay) + self:ScheduleMethod(0-delay, "initialSpawn") + table.wipe(StingTargets) + check1 = 0 + check2 = 0 +end + +function mod:Soon() + warnSoon:Show() +end + +function mod:Spawn() + warnSpawn:Show() +end + +function mod:initialSpawn() + timera = 10 + timerSpawn:Start(timera) + self:ScheduleMethod(timera-5,"Soon") + self:ScheduleMethod(timera, "Spawn") + self:ScheduleMethod(timera, "reSpawn") +end + +function mod:reSpawn() + timerb = 30 + timerSpawn:Start(timerb) + self:ScheduleMethod(timerb-5,"Soon") + self:ScheduleMethod(timerb, "Spawn") + self:ScheduleMethod(timerb, "reSpawn") +end + +local function warnStingTargets() + warnSting:Show(table.concat(StingTargets, "<, >")) + timerSting:Start() + table.wipe(StingTargets) +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(26180) then + StingTargets[#StingTargets + 1] = args.destName + self:Unschedule(warnStingTargets) + self:Schedule(0.3, warnStingTargets) + elseif args:IsSpellID(26050) then + warnAcid:Show(args.spellName, args.destName, args.amount or 1) + timerAcid:Start(args.destName) + if args:IsPlayer() and (args.amount or 1) >= 10 then + specWarnAcid:Show() + end + end +end + +mod.SPELL_AURA_APPLIED_DOSE = mod.SPELL_AURA_APPLIED + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(26180) then + timerSting:Cancel() + end +end + +function mod:UNIT_HEALTH(args) + huhuranHealth = math.max(0, UnitHealth("boss1")) / math.max(1, UnitHealthMax("boss1")) * 100; + + if huhuranHealth < 35 and check1 == 0 then + check1 = 1 + prewarnSoftEnrage:Show() + elseif skeramHealth < 30 and check2 == 0 then + check2 = 1 + warnSoftEnrage:Show() + end +end \ No newline at end of file diff --git a/DBM-AQ40/Ouro.lua b/DBM-AQ40/Ouro.lua new file mode 100644 index 0000000..1a1dc90 --- /dev/null +++ b/DBM-AQ40/Ouro.lua @@ -0,0 +1,92 @@ +local mod = DBM:NewMod("Ouro", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15517) +mod:RegisterCombat("combat") +mod:RegisterEvents( + "UNIT_HEALTH", + "UNIT_DIED", + "COMBAT_LOG_EVENT_UNFILTERED", + "PLAYER_ALIVE" +) + + + +local prewarnShard = mod:NewAnnounce("Shard Spawn Soon", 3, 1002340) +local warnShard = mod:NewAnnounce("Mind-Corrupting Shard Spawned", 2, 1002340) +local timerShard = mod:NewTimer(35, "Shard Spawn", 1002340) +local berserkTimer = mod:NewBerserkTimer(360) + +local shardsDead +local maxShards +local ouroHealth +local shardNumber + + +function mod:OnCombatStart(delay) + berserkTimer:Start() + self:ScheduleMethod(0, "initialShardSpawn") + self:ScheduleMethod(0.1, "deadShards") + maxShards = 1 + shardNumber = 1 +end + +function mod:preShard() + prewarnShard:Show() +end + +function mod:alertShard() + warnShard:Show() + self:ScheduleMethod(0, "alarmSound") +end + +function mod:initialShardSpawn() + shardsDead = 0 + local timer1 = 30 + timerShard:Show(timer1) + self:ScheduleMethod(timer1-5, "preShard") + self:ScheduleMethod(timer1, "alertShard") + self:ScheduleMethod(timer1, "checkShards") +end + +function mod:shardSpawn() + shardsDead = 0 + local timer2 = 35 + timerShard:Show(timer2) + self:ScheduleMethod(timer2-5, "preShard") + self:ScheduleMethod(timer2, "alertShard") + self:ScheduleMethod(timer2, "checkShards") +end + +function mod:deadShards() + if maxShards == 1 and shardsDead == 1 then + self:ScheduleMethod(0, "shardSpawn") + elseif maxShards == 2 and shardsDead == 2 then + self:ScheduleMethod(0, "shardSpawn") + elseif maxShards == 3 and shardsDead == 3 then + self:ScheduleMethod(0, "shardSpawn") + end + self:ScheduleMethod(0.1, "deadShards") +end + +function mod:checkShards() + if ouroHealth > 75 then + maxShards = 1 + elseif ouroHealth < 75 and ouroHealth > 33 then + maxShards = 2 + elseif ouroHealth < 33 then + maxShards = 3 + end +end + +function mod:UNIT_HEALTH(args) + ouroHealth = math.max(0, UnitHealth("boss1")) / math.max(1, UnitHealthMax("boss1")) * 100; +end + +function mod:UNIT_DIED(args) + local recapID = self:GetCIDFromGUID(args.destGUID) + if recapID == 19045 then + shardsDead = shardsDead + 1 + end +end \ No newline at end of file diff --git a/DBM-AQ40/Sartura.lua b/DBM-AQ40/Sartura.lua new file mode 100644 index 0000000..2630a6e --- /dev/null +++ b/DBM-AQ40/Sartura.lua @@ -0,0 +1,370 @@ +local mod = DBM:NewMod("Sartura", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15516) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE", + "PLAYER_ALIVE" +) + + + +function mod:PLAYER_ALIVE() + if UnitIsDeadOrGhost("PLAYER") and self.Options.ResetOnRelease then + --self:wipeAllTimers(); + self:Stop(); + end +end + +-----MISC TIMERS----- +local berserkTimer = mod:NewBerserkTimer(360) +local kickAnnounce = mod:NewSpellAnnounce(1766, 4) +local meleeOut = mod:NewAnnounce("MELEE OUT - MELEE OUT", 4) +-----PREWARNINGS----- +local prewarnSTranslocation = mod:NewAnnounce("Song of Translocation Soon", 3, 1002345) +local prewarnDTranslocation = mod:NewAnnounce("Dance of Translocation Soon", 3, 1002323) +local prewarnSColossus = mod:NewAnnounce("Song of The Colossus Soon", 3, 1002346) +local prewarnDHaste = mod:NewAnnounce("Dance of Haste Soon", 3, 1002324) +local prewarnSOppression = mod:NewAnnounce("Song of Oppression Soon", 3, 1002347) +local prewarnDHunt = mod:NewAnnounce("Dance of The Hunt Soon", 3, 1002325) +local prewarnSDeath = mod:NewAnnounce("Song of Death Soon", 3, 1002348) +-----ALERTS----- +local warn1Stacks = mod:NewAnnounce("1 Stack of Suspended Assault", 3, 1002328) +local warn2Stacks = mod:NewAnnounce("2 Stacks of Suspended Assault", 3, 1002328) +local warn3Stacks = mod:NewAnnounce("3 Stacks of Suspended Assault", 3, 1002328) +local warn4Stacks = mod:NewAnnounce("4 Stacks of Suspended Assault", 3, 1002328) +local warnSTranslocation = mod:NewSpellAnnounce(1002345, 2) +local warnDTranslocation = mod:NewSpellAnnounce(1002323, 2) +local warnSColossus = mod:NewSpellAnnounce(1002346, 2) +local warnDHaste = mod:NewSpellAnnounce(1002324, 2) +local warnSOppression = mod:NewSpellAnnounce(1002347, 2) +local warnDHunt = mod:NewSpellAnnounce(1002325, 2) +local warnSDeath = mod:NewSpellAnnounce(1002348, 2) +-----TIMERS----- +local timerStacks = mod:NewTimer(60, "Time Remaining: Stacks", 1002328) +local timerSongRemaining = mod:NewTimer(15, "Time Remaining: Song", 1002345) +local timerDanceRemaining = mod:NewTimer(15, "Time Remaining: Dance", 1002323) +local timerSTranslocation = mod:NewCDTimer(30, 1002345) +local timerDTranslocation = mod:NewCDTimer(30, 1002323) +local timerSColossus = mod:NewCDTimer(30, 1002346) +local timerDHaste = mod:NewCDTimer(30, 1002324) +local timerSOppression = mod:NewCDTimer(30, 1002347) +local timerDHunt = mod:NewCDTimer(30, 1002325) +local timerSDeath = mod:NewCDTimer(30, 1002348) +-----SOUND----- +local castNumber +local dancetimerFix +local songtimerFix +local isFourStack +local latestAuraFix +-----PREWARNING FUNCTIONS----- +function mod:preSTranslocation() + prewarnSTranslocation:Show() +end +function mod:preDTranslocation() + prewarnDTranslocation:Show() +end +function mod:preSColossus() + prewarnSColossus:Show() +end +function mod:preDHaste() + prewarnDHaste:Show() +end +function mod:preSOppression() + prewarnSOppression:Show() +end +function mod:preDHunt() + prewarnDHunt:Show() +end +function mod:preSDeath() + prewarnSDeath:Show() +end + +-----ALERT FUNCTIONS----- +function mod:alertSTranslocation() + warnSTranslocation:Show() +end +function mod:alertDTranslocation() + warnDTranslocation:Show() +end +function mod:alertSColossus() + warnSColossus:Show() +end +function mod:alertDHaste() + warnDHaste:Show() +end +function mod:alertSOppression() + warnSOppression:Show() +end +function mod:alertDHunt() + warnDHunt:Show() +end +function mod:alertSDeath() + warnSDeath:Show() +end + +-----MISC FUNCTIONS----- +function mod:kickThisCunt() + kickAnnounce:Show() +end +function mod:runBitch() + meleeOut:Show() +end + +-----ACTUAL FUNCTIONS----- +function mod:OnCombatStart(delay) + berserkTimer:Start() + self:ScheduleMethod(0, "initialSarturaCast") + castNumber = 0 + dancetimerFix = 0 + songtimerFix = 0 + isFourStack = 0 + latestAuraFix = 0 +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(1002328) and (args.amount or 1) == 1 then + warn1Stacks:Show() + timerStacks:Start() + elseif args:IsSpellID(1002328) and (args.amount or 1) == 2 then + warn2Stacks:Show() + timerStacks:Start() + elseif args:IsSpellID(1002328) and (args.amount or 1) == 3 then + warn3Stacks:Show() + timerStacks:Start() + elseif args:IsSpellID(1002328) and (args.amount or 1) == 4 then + warn4Stacks:Show() + timerStacks:Start() + if isFourStack == 0 then + isFourStack = 1 + self:ScheduleMethod(60, "fixFourStack") + end + elseif args:IsSpellID(1002323) then + if dancetimerFix == 0 then + timerDanceRemaining:Start() + dancetimerFix = 1 + self:ScheduleMethod(20, "fixDanceTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002323 + end + elseif args:IsSpellID(1002324) then + if dancetimerFix == 0 then + timerDanceRemaining:Start() + dancetimerFix = 1 + self:ScheduleMethod(20, "fixDanceTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002324 + end + elseif args:IsSpellID(1002325) then + if dancetimerFix == 0 then + timerDanceRemaining:Start() + dancetimerFix = 1 + self:ScheduleMethod(20, "fixDanceTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002325 + end + elseif args:IsSpellID(1002345) then + if songtimerFix == 0 then + timerSongRemaining:Start() + songtimerFix = 1 + self:ScheduleMethod(20, "fixSongTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002345 + end + elseif args:IsSpellID(1002346) then + if songtimerFix == 0 then + timerSongRemaining:Start() + songtimerFix = 1 + self:ScheduleMethod(20, "fixSongTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002346 + end + elseif args:IsSpellID(1002347) then + if songtimerFix == 0 then + timerSongRemaining:Start() + songtimerFix = 1 + self:ScheduleMethod(20, "fixSongTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002347 + end + elseif args:IsSpellID(1002348) then + if songtimerFix == 0 then + timerSongRemaining:Start() + songtimerFix = 1 + self:ScheduleMethod(20, "fixSongTimer") + self:ScheduleMethod(1, "checkFourCast") + latestAuraFix = 1002348 + end + end +end + +function mod:fixDanceTimer() + dancetimerFix = 0 +end + +function mod:fixSongTimer() + songtimerFix = 0 +end + +function mod:fixFourStack() + isFourStack = 0 +end + +function mod:checkFourCast() + if isFourStack == 1 then + if latestAuraFix == 1002323 then + self:ScheduleMethod(0, "dotCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002324 then + self:ScheduleMethod(0, "dohCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002325 then + self:ScheduleMethod(0, "dothCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002345 then + self:ScheduleMethod(0, "sotCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002346 then + self:ScheduleMethod(0, "socCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002347 then + self:ScheduleMethod(0, "sooCast") + latestAuraFix = 0 + elseif latestAuraFix == 1002348 then + self:ScheduleMethod(0, "sodCast") + latestAuraFix = 0 + end + end +end + +mod.SPELL_AURA_APPLIED_DOSE = mod.SPELL_AURA_APPLIED + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(1002345) then --Song of Translocation + self:ScheduleMethod(0, "sotCast") + mod:alarmSound() + mod:kickThisCunt() + if castNumber == 0 then + castNumber = castNumber+1 + elseif castNumber == 7 then + castNumber = castNumber+1 + end + elseif args:IsSpellID(1002323) then --Dance of Translocation + self:ScheduleMethod(0, "dotCast") + mod:alarmSound() + mod:kickThisCunt() + if castNumber == 1 then + castNumber = castNumber+1 + elseif castNumber == 8 then + castNumber = castNumber+1 + end + elseif args:IsSpellID(1002346) then --Song of the Colossus + self:ScheduleMethod(0, "socCast") + if castNumber == 2 then + castNumber = castNumber+1 + mod:alarmSound() + mod:kickThisCunt() + elseif castNumber == 9 then + castNumber = castNumber+1 + mod:runBitch() + end + elseif args:IsSpellID(1002324) then --Dance of Haste + self:ScheduleMethod(0, "dohCast") + if castNumber == 3 then + castNumber = castNumber+1 + elseif castNumber == 10 then + castNumber = castNumber+1 + end + elseif args:IsSpellID(1002347) then --Song of Oppression + self:ScheduleMethod(0, "sooCast") + if castNumber == 4 then + castNumber = castNumber+1 + mod:runBitch() + elseif castNumber == 11 then + castNumber = castNumber+1 + mod:alarmSound() + mod:kickThisCunt() + end + elseif args:IsSpellID(1002325) then --Dance of the Hunt + self:ScheduleMethod(0, "dothCast") + if castNumber == 5 then + castNumber = castNumber+1 + elseif castNumber == 12 then + castNumber = castNumber+1 + mod:alarmSound() + mod:kickThisCunt() + end + elseif args:IsSpellID(1002348) then --Song of Death + self:ScheduleMethod(0, "sodCast") + mod:alarmSound() + mod:kickThisCunt() + if castNumber == 6 then + castNumber = castNumber+1 + elseif castNumber == 13 then + castNumber = castNumber+1 + mod:alarmSound() + mod:kickThisCunt() + end + end +end + +function mod:initialSarturaCast() + local timer1 = 10 + timerSTranslocation:Start(timer1) + self:ScheduleMethod(timer1-5, "preSTranslocation") + self:ScheduleMethod(timer1, "alertSTranslocation") +end + +function mod:sodCast() + local timer1 = 50 + timerSTranslocation:Start(timer1) + self:ScheduleMethod(timer1-5, "preSTranslocation") + self:ScheduleMethod(timer1, "alertSTranslocation") +end + +function mod:sotCast() + local timer2 = 25 + timerDTranslocation:Start(timer2) + self:ScheduleMethod(timer2-5, "preDTranslocation") + self:ScheduleMethod(timer2, "alertDTranslocation") +end + +function mod:dotCast() + local timer3 = 25 + timerSColossus:Start(timer3) + self:ScheduleMethod(timer3-5, "preSColossus") + self:ScheduleMethod(timer3, "alertSColossus") +end + +function mod:socCast() + local timer4 = 25 + timerDHaste:Start(timer4) + self:ScheduleMethod(timer4-5, "preDHaste") + self:ScheduleMethod(timer4, "alertDHaste") +end + +function mod:dohCast() + local timer5 = 25 + timerSOppression:Start(timer5) + self:ScheduleMethod(timer5-5, "preSOppression") + self:ScheduleMethod(timer5, "alertSOppression") +end + +function mod:sooCast() + local timer6 = 25 + timerDHunt:Start(timer6) + self:ScheduleMethod(timer6-5, "preDHunt") + self:ScheduleMethod(timer6, "alertDHunt") +end + +function mod:dothCast() + local timer7 = 25 + timerSDeath:Start(timer7) + self:ScheduleMethod(timer7-5, "preSDeath") + self:ScheduleMethod(timer7, "alertSDeath") +end \ No newline at end of file diff --git a/DBM-AQ40/Skeram.lua b/DBM-AQ40/Skeram.lua new file mode 100644 index 0000000..80f2c69 --- /dev/null +++ b/DBM-AQ40/Skeram.lua @@ -0,0 +1,42 @@ +local mod = DBM:NewMod("Skeram", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 133 $"):sub(12, -3)) +mod:SetCreatureID(15263) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE", + "UNIT_HEALTH" +) + + + +local berserkTimer = mod:NewBerserkTimer(540) +local prewarnClones = mod:NewAnnounce("Skeram Clones Soon", 3, 1002181) + +local check1 +local check2 +local check3 + +function mod:OnCombatStart(delay) + berserkTimer:Start() + check1 = 0 + check2 = 0 + check3 = 0 +end + +function mod:UNIT_HEALTH(args) + skeramHealth = math.max(0, UnitHealth("boss1")) / math.max(1, UnitHealthMax("boss1")) * 100; + + if skeramHealth < 80 and check1 == 0 then + check1 = 1 + prewarnClones:Show() + elseif skeramHealth < 55 and check2 == 0 then + check2 = 1 + prewarnClones:Show() + elseif skeramHealth < 30 and check3 == 0 then + check3 = 1 + prewarnClones:Show() + end +end \ No newline at end of file diff --git a/DBM-AQ40/ThreeBugs.lua b/DBM-AQ40/ThreeBugs.lua new file mode 100644 index 0000000..783ecc8 --- /dev/null +++ b/DBM-AQ40/ThreeBugs.lua @@ -0,0 +1,50 @@ +local mod = DBM:NewMod("ThreeBugs", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 184 $"):sub(12, -3)) +mod:SetCreatureID(15544, 15511, 15543) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE" +) +mod:SetBossHealthInfo( + 15543, L.Yauj, + 15544, L.Vem, + 15511, L.Kri +) +local berserkTimer = mod:NewBerserkTimer(540) +local prewarnBurrow = mod:NewAnnounce("Burrow Soon", 3, 1002251) +local warnBurrow = mod:NewAnnounce("Burrow Now", 2, 1002251) +local timerBurrow = mod:NewTimer(35, "Vem: Next Burrow", 1002251) + +local timerCheck + +function mod:OnCombatStart(delay) + berserkTimer:Start() + timerCheck = 1 + self:ScheduleMethod(0.1, "checkIsVis") +end + +function mod:preBurrow() + prewarnBurrow:Show() +end + +function mod:alertBurrow() + warnBurrow:Show() + self:ScheduleMethod(0, "alarmSound") +end + +function mod:checkIsVis() + if UnitIsVisible(15544) and timerCheck == 1 then + elseif UnitIsVisible(15544) and timerCheck == 2 then + timerCheck = 1 + local timera = 35 + timerBurrow:Show(timera) + self:ScheduleMethod(timera-5, "preBurrow") + self:ScheduleMethod(timera, "alertBurrow") + elseif not UnitIsVisible(15544) then + timerCheck = 2 + end + self:ScheduleMethod(0.1, "checkIsVis") +end \ No newline at end of file diff --git a/DBM-AQ40/TwinEmps.lua b/DBM-AQ40/TwinEmps.lua new file mode 100644 index 0000000..dc7139c --- /dev/null +++ b/DBM-AQ40/TwinEmps.lua @@ -0,0 +1,80 @@ +local mod = DBM:NewMod("TwinEmpsAQ", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15276, 15275) +mod:RegisterCombat("combat") +mod:RegisterEvents( + "PLAYER_ALIVE", + "SPELL_AURA_APPLIED", + "SPELL_AURA_APPLIED_DOSE" +) +mod:SetBossHealthInfo( + 15276, L.Veklor, + 15275, L.Veknil +) + +local warnTeleportSoon = mod:NewSoonAnnounce(800, 2) +local warnTeleport = mod:NewSpellAnnounce(800, 3) +local timerTeleport = mod:NewNextTimer(30, 800) +local berserkTimer = mod:NewBerserkTimer(600) + + +local specWarnBlizzard = mod:NewSpecialWarningMove(26607, true, "Special warning when standing in Blizzard", true) + +function mod:OnCombatStart(delay) + berserkTimer:Start(-delay) + self:ScheduleMethod(-delay, "twinTeleport") + self:ScheduleMethod(30-delay, "twinTeleport") + self:ScheduleMethod(60-delay, "twinTeleport") + self:ScheduleMethod(90-delay, "twinTeleport") + self:ScheduleMethod(120-delay, "twinTeleport") + self:ScheduleMethod(150-delay, "twinTeleport") + self:ScheduleMethod(180-delay, "twinTeleport") + self:ScheduleMethod(210-delay, "twinTeleport") + self:ScheduleMethod(240-delay, "twinTeleport") + self:ScheduleMethod(270-delay, "twinTeleport") + self:ScheduleMethod(300-delay, "twinTeleport") + self:ScheduleMethod(330-delay, "twinTeleport") + self:ScheduleMethod(360-delay, "twinTeleport") + self:ScheduleMethod(390-delay, "twinTeleport") + self:ScheduleMethod(420-delay, "twinTeleport") + self:ScheduleMethod(450-delay, "twinTeleport") + self:ScheduleMethod(480-delay, "twinTeleport") + self:ScheduleMethod(510-delay, "twinTeleport") + self:ScheduleMethod(540-delay, "twinTeleport") + self:ScheduleMethod(570-delay, "twinTeleport") + self:ScheduleMethod(600-delay, "twinTeleport") +end + +function mod:teleSoon() + warnTeleportSoon:Show() +end + +function mod:teleNow() + warnTeleport:Show() + self:ScheduleMethod(0, "alarmSound") +end + +function mod:twinTeleport() + timer = 30 + timerTeleport:Start(timer) + self:ScheduleMethod(timer-5, "teleSoon") + self:ScheduleMethod(timer, "teleNow") +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(26607) then + if args:IsPlayer() then + specWarnBlizzard:Show(); + end + end +end + +function mod:SPELL_AURA_APPLIED_DOSE(args) + if args:IsSpellID(26607) then -- Miasma (Eye Tentacles) + if args:IsPlayer() then + specWarnBlizzard:Show(); + end + end +end \ No newline at end of file diff --git a/DBM-AQ40/Viscidus.lua b/DBM-AQ40/Viscidus.lua new file mode 100644 index 0000000..17277ed --- /dev/null +++ b/DBM-AQ40/Viscidus.lua @@ -0,0 +1,18 @@ +local mod = DBM:NewMod("Viscidus", "DBM-AQ40", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 132 $"):sub(12, -3)) +mod:SetCreatureID(15299) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "PLAYER_ALIVE" +) + + + +local berserkTimer = mod:NewBerserkTimer(420) + +function mod:OnCombatStart(delay) + berserkTimer:Start() +end \ No newline at end of file diff --git a/DBM-AQ40/localization.en.lua b/DBM-AQ40/localization.en.lua new file mode 100644 index 0000000..a24b71c --- /dev/null +++ b/DBM-AQ40/localization.en.lua @@ -0,0 +1,140 @@ +local L + +------------ +-- Skeram -- +------------ +L = DBM:GetModLocalization("Skeram") + +L:SetGeneralLocalization{ + name = "The Prophet Skeram" +} + +L:SetOptionLocalization{ + SetIconOnMC = DBM_CORE_AUTO_ICONS_OPTION_TEXT:format(785) +} + +---------------- +-- Three Bugs -- +---------------- +L = DBM:GetModLocalization("ThreeBugs") + +L:SetGeneralLocalization{ + name = "Bug Trio" +} +L:SetMiscLocalization{ + Yauj = "Princess Yauj", + Vem = "Vem", + Kri = "Lord Kri" +} + +------------- +-- Sartura -- +------------- +L = DBM:GetModLocalization("Sartura") + +L:SetGeneralLocalization{ + name = "Battleguard Sartura" +} + +-------------- +-- Fankriss -- +-------------- +L = DBM:GetModLocalization("Fankriss") + +L:SetGeneralLocalization{ + name = "Fankriss the Unyielding" +} +L:SetWarningLocalization{ + WarnWound = "%s on >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown") +} +-------------- +-- Viscidus -- +-------------- +L = DBM:GetModLocalization("Viscidus") + +L:SetGeneralLocalization{ + name = "Viscidus" +} +L:SetWarningLocalization{ + WarnFreeze = "Freeze: %d/3", + WarnShatter = "Shatter: %d/3" +} +L:SetOptionLocalization{ + WarnFreeze = "Announce Freeze status", + WarnShatter = "Announce Shatter status", +} +L:SetMiscLocalization{ + Phase4 = "begins to crack", + Phase5 = "looks ready to shatter", + Phase6 = "Explodes." +} +------------- +-- Huhuran -- +------------- +L = DBM:GetModLocalization("Huhuran") + +L:SetGeneralLocalization{ + name = "Princess Huhuran" +} +L:SetWarningLocalization{ + WarnAcid = "%s on >%s< (%s)" +} +L:SetOptionLocalization{ + WarnAcid = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(26180, GetSpellInfo(26180) or "unknown") + } +--------------- +-- Twin Emps -- +--------------- +L = DBM:GetModLocalization("TwinEmpsAQ") + +L:SetGeneralLocalization{ + name = "Twin Emperors" +} +L:SetMiscLocalization{ + Veklor = "Emperor Vek'lor", + Veknil = "Emperor Vek'nilash" +} + +------------ +-- C'Thun -- +------------ +L = DBM:GetModLocalization("CThun") + +L:SetGeneralLocalization{ + name = "C'Thun" +} +L:SetOptionLocalization{ + RangeFrame = "Show range frame" +} +---------------- +-- Ouro -- +---------------- +L = DBM:GetModLocalization("Ouro") + +L:SetGeneralLocalization{ + name = "Ouro" +} +L:SetMiscLocalization{ + Shard = "Mind-Corrupting Shard" +} +L:SetWarningLocalization{ + WarnSubmerge = "Submerge", + WarnEmerge = "Emerge", + WarnSubmergeSoon = "Submerge soon", + WarnEmergeSoon = "Emerge soon" +} +L:SetTimerLocalization{ + TimerSubmerge = "Submerge", + TimerEmerge = "Emerge" +} +L:SetOptionLocalization{ + WarnSubmerge = "Show warning for submerge", + WarnSubmergeSoon = "Show pre-warning for submerge", + TimerSubmerge = "Show timer for submerge", + WarnEmerge = "Show warning for emerge", + WarnEmergeSoon = "Show pre-warning for emerge", + TimerEmerge = "Show timer for emerge" +} \ No newline at end of file diff --git a/DBM-AQ40/localization.es.lua b/DBM-AQ40/localization.es.lua new file mode 100644 index 0000000..9790831 --- /dev/null +++ b/DBM-AQ40/localization.es.lua @@ -0,0 +1,138 @@ +if GetLocale() ~= "esES" and GetLocale() ~= "esMX" then return end +local L + +------------ +-- Skeram -- +------------ +L = DBM:GetModLocalization("Skeram") + +L:SetGeneralLocalization{ + name = "El profeta Skeram" +} + +L:SetOptionLocalization{ + SetIconOnMC = DBM_CORE_AUTO_ICONS_OPTION_TEXT:format(785) +} + +---------------- +-- Three Bugs -- +---------------- +L = DBM:GetModLocalization("ThreeBugs") + +L:SetGeneralLocalization{ + name = "Los tres bichos" +} +L:SetMiscLocalization{ + Yauj = "Princesa Yauj", + Vem = "Vem", + Kri = "Lord Kri" +} + +------------- +-- Sartura -- +------------- +L = DBM:GetModLocalization("Sartura") + +L:SetGeneralLocalization{ + name = "Guardia de batalla Sartura" +} + +-------------- +-- Fankriss -- +-------------- +L = DBM:GetModLocalization("Fankriss") + +L:SetGeneralLocalization{ + name = "Fankriss el Implacable" +} +L:SetWarningLocalization{ + WarnWound = "%s en >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown") +} +-------------- +-- Viscidus -- +-------------- +L = DBM:GetModLocalization("Viscidus") + +L:SetGeneralLocalization{ + name = "Viscidus" +} +L:SetWarningLocalization{ + WarnFreeze = "Congelación: %d/3", + WarnShatter = "Roto: %d/3" +} +L:SetOptionLocalization{ + WarnFreeze = "Anunciar el estado de Congelación", + WarnShatter = "Anunciar el estado de rotura", +} +L:SetMiscLocalization{ + Phase4 = "empieza a agrietarse", + Phase5 = "parece listo para romper", + Phase6 = "explota" +} +------------- +-- Huhuran -- +------------- +L = DBM:GetModLocalization("Huhuran") + +L:SetGeneralLocalization{ + name = "Princesa Huhuran" +} +L:SetWarningLocalization{ + WarnAcid = "%s en >%s< (%s)" +} +L:SetOptionLocalization{ + WarnAcid = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(26180, GetSpellInfo(26180) or "unknown") + } +--------------- +-- Twin Emps -- +--------------- +L = DBM:GetModLocalization("TwinEmpsAQ") + +L:SetGeneralLocalization{ + name = "Gemelos emperadores" +} +L:SetMiscLocalization{ + Veklor = "Emperador Vek'lor", + Veknil = "Emperador Vek'nilash" +} + +------------ +-- C'Thun -- +------------ +L = DBM:GetModLocalization("CThun") + +L:SetGeneralLocalization{ + name = "C'Thun" +} +L:SetOptionLocalization{ + RangeFrame = "Mostrar distancia" +} +---------------- +-- Ouro -- +---------------- +L = DBM:GetModLocalization("Ouro") + +L:SetGeneralLocalization{ + name = "Ouro" +} +L:SetWarningLocalization{ + WarnSubmerge = "Se Sumerge", + WarnEmerge = "Emerge", + WarnSubmergeSoon = "Se Sumerge pronto", + WarnEmergeSoon = "Emerge pronto" +} +L:SetTimerLocalization{ + TimerSubmerge = "Se Sumerge", + TimerEmerge = "Emerge" +} +L:SetOptionLocalization{ + WarnSubmerge = "Mostrar aviso para sumersión", + WarnSubmergeSoon = "Mostrar pre-aviso para sumersión", + TimerSubmerge = "Mostrar tiempo para sumersión", + WarnEmerge = "Mostrar aviso para emersión", + WarnEmergeSoon = "Mostrar pre-aviso para emersión", + TimerEmerge = "Mostrar tiempo para emersión" +} \ No newline at end of file diff --git a/DBM-AQ40/localization.ru.lua b/DBM-AQ40/localization.ru.lua new file mode 100644 index 0000000..1031e3f --- /dev/null +++ b/DBM-AQ40/localization.ru.lua @@ -0,0 +1,153 @@ +if GetLocale() ~= "ruRU" then return end + +local L + +------------ +-- Skeram -- +------------ +L = DBM:GetModLocalization("Skeram") + +L:SetGeneralLocalization{ + name = "Пророк Скерам" +} + +L:SetOptionLocalization{ + SetIconOnMC = DBM_CORE_AUTO_ICONS_OPTION_TEXT:format(785) +} + +---------------- +-- Three Bugs -- +---------------- +L = DBM:GetModLocalization("ThreeBugs") + +L:SetGeneralLocalization{ + name = "Семейство жуков" +} +L:SetMiscLocalization{ + Yauj = "Принцесса Яудж", + Vem = "Вем", + Kri = "Лорд Кри" +} + +------------- +-- Sartura -- +------------- +L = DBM:GetModLocalization("Sartura") + +L:SetGeneralLocalization{ + name = "Боевой страж Сартура" +} + +-------------- +-- Fankriss -- +-------------- +L = DBM:GetModLocalization("Fankriss") + +L:SetGeneralLocalization{ + name = "Фанкрисс Непреклонный" +} +L:SetWarningLocalization{ + WarnWound = "%s на >%s< (%s)" +} +L:SetOptionLocalization{ + WarnWound = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(25646, GetSpellInfo(25646) or "unknown") +} +-------------- +-- Viscidus -- +-------------- +L = DBM:GetModLocalization("Viscidus") + +L:SetGeneralLocalization{ + name = "Нечистотон" +} +L:SetWarningLocalization{ + WarnFreeze = "Заморожен: %d/3", + WarnShatter = "Shatter: %d/3" +} +L:SetOptionLocalization{ + WarnFreeze = "Announce Freeze status", + WarnShatter = "Announce Shatter status", +} +L:SetMiscLocalization{ + Phase4 = "Нечистотон начинает раскалываться!", + Phase5 = "Нечистотон едва держится!", + Phase6 = "Explodes." +} +------------- +-- Huhuran -- +------------- +L = DBM:GetModLocalization("Huhuran") + +L:SetGeneralLocalization{ + name = "Принцесса Хухуран" +} +L:SetWarningLocalization{ + WarnAcid = "%s на >%s< (%s)" +} +L:SetOptionLocalization{ + WarnAcid = DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell:format(26180, GetSpellInfo(26180) or "unknown") + } +--------------- +-- Twin Emps -- +--------------- +L = DBM:GetModLocalization("TwinEmpsAQ") + +L:SetGeneralLocalization{ + name = "Императоры-близнецы" +} +L:SetMiscLocalization{ + Veklor = "Император Век'лор", + Veknil = "Император Век'нилаш" +} + +------------ +-- C'Thun -- +------------ +L = DBM:GetModLocalization("CThun") + +L:SetGeneralLocalization{ + name = "К'Тун" +} +L:SetWarningLocalization{ + WarnEyeTentacle = "Появляются глазные отростки!", + WarnClawTentacle = "Появляется когтещупальце!", + WarnGiantEyeTentacle = "Появляется гигантский глазной отросток!", + WarnGiantClawTentacle = "Появляется гигантское когтещупальце!", + WarnWeakened = "К'Тун ослаблен! Бейте его!" +} +L:SetTimerLocalization{ + TimerEyeTentacle = "Глазных отроски", + TimerGiantEyeTentacle = "Гигантский глазной отросток", + TimerClawTentacle = "Когтещупальце", + TimerGiantClawTentacle = "Гигантское когтещупальце", + TimerWeakened = "К'Тун ослаблен" +} +L:SetOptionLocalization{ + RangeFrame = "Показывать окно дистанции" +} +---------------- +-- Ouro -- +---------------- +L = DBM:GetModLocalization("Ouro") + +L:SetGeneralLocalization{ + name = "Оуро" +} +L:SetWarningLocalization{ + WarnSubmerge = "Закапывание", + WarnEmerge = "Появление", + WarnSubmergeSoon = "Скоро закапывание", + WarnEmergeSoon = "Скоро появление" +} +L:SetTimerLocalization{ + TimerSubmerge = "Закапывание", + TimerEmerge = "Появление" +} +L:SetOptionLocalization{ + WarnSubmerge = "Показывать предупреждение о закапывании", + WarnSubmergeSoon = "Предупреждать заранее о закапывании", + TimerSubmerge = "Показывать таймер до закапывания", + WarnEmerge = "Показывать предупреждение о появлении", + WarnEmergeSoon = "Предупреждать заранее о появлении", + TimerEmerge = "Показывать таймер до появления" +} \ No newline at end of file diff --git a/DBM-BWL/Broodlord.lua b/DBM-BWL/Broodlord.lua new file mode 100644 index 0000000..90a4398 --- /dev/null +++ b/DBM-BWL/Broodlord.lua @@ -0,0 +1,39 @@ +local mod = DBM:NewMod("Broodlord", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 168 $"):sub(12, -3)) +mod:SetCreatureID(12017) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_SUCCESS", + "SPELL_AURA_APPLIED" +) + +local warnBlastWave = mod:NewSpellAnnounce(23331) +local warnKnockAway = mod:NewSpellAnnounce(18670) +local warnMortal = mod:NewTargetAnnounce(24573) + +local timerMortal = mod:NewTargetTimer(5, 24573) + +function mod:OnCombatStart(delay) +end + +--It's unfortunate this is a shared spellid. +--cause you are almost always in combat before pulling this boss which breaks "IsInCombat" detection +--these 2 of these warnings will never work. + +function mod:SPELL_CAST_SUCCESS(args) + if args:IsSpellID(23331) then + warnBlastWave:Show() + elseif args:IsSpellID(18670) and self:IsInCombat() then + warnKnockAway:Show() + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(24573) and self:IsInCombat() then + warnMortal:Show(args.destName) + timerMortal:Start(args.destName) + end +end \ No newline at end of file diff --git a/DBM-BWL/Chromaggus.lua b/DBM-BWL/Chromaggus.lua new file mode 100644 index 0000000..101e96c --- /dev/null +++ b/DBM-BWL/Chromaggus.lua @@ -0,0 +1,90 @@ +local mod = DBM:NewMod("Chromaggus", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 188 $"):sub(12, -3)) +mod:SetCreatureID(14020) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_CAST_SUCCESS", + "SPELL_AURA_APPLIED", + "SPELL_AURA_REMOVED", + "UNIT_HEALTH" +) + +local warnBreathSoon = mod:NewAnnounce("WarnBreathSoon", 1, 23316) +local warnBreath = mod:NewAnnounce("WarnBreath", 2, 23316) +local warnRed = mod:NewTargetAnnounce(23155, 2, nil, false) +local warnGreen = mod:NewTargetAnnounce(23169, 2, nil, false) +local warnBlue = mod:NewTargetAnnounce(23153, 2, nil, false) +local warnBlack = mod:NewTargetAnnounce(23154, 2, nil, false) +local warnBronze = mod:NewSpellAnnounce(23170, 2) +local warnEnrage = mod:NewSpellAnnounce(23128) +local warnPhase2Soon = mod:NewAnnounce("WarnPhase2Soon") +local warnPhase2 = mod:NewPhaseAnnounce(2) + +local specWarnBronze = mod:NewSpecialWarningYou(23170) + +local timerBreath = mod:NewTimer(2, "TimerBreath") +local timerBreathCD = mod:NewTimer(60, "TimerBreathCD") +local timerEnrage = mod:NewBuffActiveTimer(8, 23128) + + +local prewarn_P2 +function mod:OnCombatStart(delay) + warnBreathSoon:Schedule(25-delay) + timerBreathCD:Start(30-delay, "Breath 1") + timerBreathCD:Start(-delay, "Breath 2") + prewarn_P2 = false; +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(23309, 23313, 23189, 23316) or args:IsSpellID(23312) then + warnBreathSoon:Cancel() + warnBreathSoon:Schedule(25) + warnBreath:Show(args.spellName) + timerBreath:Start(args.spellName) + end +end + +function mod:SPELL_CAST_SUCCESS(args) + if args:IsSpellID(23170) then + warnBronze:Show() + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(23155) then + warnRed:Show(args.destName) + elseif args:IsSpellID(23169) then + warnGreen:Show(args.destName) + elseif args:IsSpellID(23153) then + warnBlue:Show(args.destName) + elseif args:IsSpellID(23154) then + warnBlack:Show(args.destName) + elseif args:IsSpellID(23170) then + warnBronze:Show() + if args:IsPlayer() then + specWarnBronze:Show() + end + elseif args:IsSpellID(23128) then + warnEnrage:Show() + timerEnrage:Start() + elseif args:IsSpellID(23537) then + warnPhase2:Show() + end +end + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(23128) then + timerEnrage:Cancel() + end +end + +function mod:UNIT_HEALTH(uId) + if UnitHealth(uId) / UnitHealthMax(uId) <= 0.25 and self:GetUnitCreatureId(uId) == 14020 and not prewarn_P2 then + warnPhase2Soon:Show() + prewarn_P2 = true + end +end \ No newline at end of file diff --git a/DBM-BWL/DBM-BWL.toc b/DBM-BWL/DBM-BWL.toc new file mode 100644 index 0000000..f8db899 --- /dev/null +++ b/DBM-BWL/DBM-BWL.toc @@ -0,0 +1,34 @@ +## Interface: 30300 +## Title:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Blackwing Lair|r +## Title-esES:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Guarida Alanegra|r +## Title-ruRU:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Логово Крыла Тьмы|r +## LoadOnDemand: 1 +## RequiredDeps: DBM-Core +## SavedVariablesPerCharacter: DBMBWL_SavedVars, DBMBWL_SavedStats +## X-DBM-Mod: 1 +## X-DBM-Mod-Category: Vanilla +## X-DBM-Mod-Has-Heroic-Mode: 0 +## X-DBM-Mod-Name: Blackwing Lair +## X-DBM-Mod-Name-esES: Guarida Alanegra +## X-DBM-Mod-Name-ruRU: Логово Крыла Тьмы +## X-DBM-Mod-Sort: 113 +## X-DBM-Mod-LoadZone: Blackwing Lair +## X-DBM-Mod-LoadZone-esES: Guarida Alanegra +## X-DBM-Mod-LoadZone-ruRU: Логово Крыла Тьмы +## X-DBM-Mod-LoadZone-frFR: Repaire de l'Aile noire +## X-DBM-Mod-LoadZone-esMX: Guarida Alanegra +## X-DBM-Mod-LoadZone-zhTW: 黑翼之巢 +## X-DBM-Mod-LoadZone-zhCN: 黑翼之巢 +## X-DBM-Mod-LoadZone-koKR: 검은날개 둥지 +## X-DBM-Mod-LoadZone-deDE: Pechschwingenhort +localization.en.lua +localization.es.lua +localization.ru.lua +Razorgore.lua +Vaelastrasz.lua +Broodlord.lua +Firemaw.lua +Ebonroc.lua +Flamegor.lua +Chromaggus.lua +Nefarian.lua \ No newline at end of file diff --git a/DBM-BWL/Ebonroc.lua b/DBM-BWL/Ebonroc.lua new file mode 100644 index 0000000..2945da7 --- /dev/null +++ b/DBM-BWL/Ebonroc.lua @@ -0,0 +1,47 @@ +local mod = DBM:NewMod("Ebonroc", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 168 $"):sub(12, -3)) +mod:SetCreatureID(14601) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_AURA_APPLIED", + "SPELL_AURA_REMOVED" +) + +local warnWingBuffet = mod:NewCastAnnounce(23339) +local warnShadowFlame = mod:NewCastAnnounce(22539) +local warnShadow = mod:NewTargetAnnounce(23340) + +local timerWingBuffet = mod:NewNextTimer(31, 23339) +local timerShadowFlame = mod:NewCastTimer(2, 22539) +local timerShadow = mod:NewTargetTimer(8, 23340) + +function mod:OnCombatStart(delay) + timerWingBuffet:Start(-delay) +end + +function mod:SPELL_CAST_START(args)--did not see ebon use any of these abilities + if args:IsSpellID(23339) and self:IsInCombat() then + warnWingBuffet:Show() + timerWingBuffet:Start() + elseif args:IsSpellID(22539) and self:IsInCombat() then + timerShadowFlame:Start() + warnShadowFlame:Show() + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(23340) then + warnShadow:Show(args.destName) + timerShadow:Start(args.destName) + end +end + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(23340) then + timerShadow:Cancel(args.destName) + end +end \ No newline at end of file diff --git a/DBM-BWL/Firemaw.lua b/DBM-BWL/Firemaw.lua new file mode 100644 index 0000000..66bd760 --- /dev/null +++ b/DBM-BWL/Firemaw.lua @@ -0,0 +1,39 @@ +local mod = DBM:NewMod("Firemaw", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 168 $"):sub(12, -3)) +mod:SetCreatureID(11983) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_CAST_SUCCESS" +) + +local warnWingBuffet = mod:NewCastAnnounce(23339) +local warnShadowFlame = mod:NewCastAnnounce(22539) +local warnFlameBuffet = mod:NewSpellAnnounce(23341) + +local timerWingBuffet = mod:NewNextTimer(31, 23339) +local timerShadowFlame = mod:NewCastTimer(2, 22539) +--local timerFlameBuffetCD = mod:NewCDTimer(10, 23341) + +function mod:OnCombatStart(delay) + timerWingBuffet:Start(-delay) +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(23339) and self:IsInCombat() then + warnWingBuffet:Show() + timerWingBuffet:Start() + elseif args:IsSpellID(22539) and self:IsInCombat() then + timerShadowFlame:Start() + warnShadowFlame:Show() + end +end + +function mod:SPELL_CAST_SUCCESS(args) + if args:IsSpellID(23341) then + warnFlameBuffet:Show() + end +end \ No newline at end of file diff --git a/DBM-BWL/Flamegor.lua b/DBM-BWL/Flamegor.lua new file mode 100644 index 0000000..5e8e1ad --- /dev/null +++ b/DBM-BWL/Flamegor.lua @@ -0,0 +1,40 @@ +local mod = DBM:NewMod("Flamegor", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 168 $"):sub(12, -3)) +mod:SetCreatureID(11981) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_CAST_SUCCESS" +) + +local warnWingBuffet = mod:NewCastAnnounce(23339) +local warnShadowFlame = mod:NewCastAnnounce(22539) +local warnEnrage = mod:NewSpellAnnounce(23342) + +local timerWingBuffet = mod:NewNextTimer(31, 23339) +local timerShadowFlame = mod:NewCastTimer(2, 22539) +local timerEnrageNext = mod:NewNextTimer(10, 23342) + +function mod:OnCombatStart(delay) + timerWingBuffet:Start(-delay) +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(23339) and self:IsInCombat() then + warnWingBuffet:Show() + timerWingBuffet:Start() + elseif args:IsSpellID(22539) and self:IsInCombat() then + timerShadowFlame:Start() + warnShadowFlame:Show() + end +end + +function mod:SPELL_CAST_SUCCESS(args) + if args:IsSpellID(23342) then + warnEnrage:Show() + timerEnrageNext:Start() + end +end \ No newline at end of file diff --git a/DBM-BWL/Nefarian.lua b/DBM-BWL/Nefarian.lua new file mode 100644 index 0000000..5d4b4f5 --- /dev/null +++ b/DBM-BWL/Nefarian.lua @@ -0,0 +1,106 @@ +local mod = DBM:NewMod("Nefarian", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 188 $"):sub(12, -3)) +mod:SetCreatureID(11583) +mod:RegisterCombat("yell", L.YellPull) +mod:SetWipeTime(25)--guesswork + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_AURA_APPLIED", + "SPELL_AURA_REMOVED", + "UNIT_HEALTH", + "CHAT_MSG_MONSTER_YELL" +) + +local warnClassCallSoon = mod:NewAnnounce("WarnClassCallSoon", 2) +local warnClassCall = mod:NewAnnounce("WarnClassCall", 3) +local warnPhaseSoon = mod:NewAnnounce("WarnPhaseSoon", 2) +local warnPhase = mod:NewAnnounce("WarnPhase", 3) +local warnShadowFlame = mod:NewCastAnnounce(22539, 2) +local warnFear = mod:NewCastAnnounce(22686, 2) +local warnVeilShadow = mod:NewTargetAnnounce(22687, 3) +local warnMC = mod:NewTargetAnnounce(22667, 4) + +local timerClassCall = mod:NewTimer(30, "TimerClassCall") +local timerShadowFlame = mod:NewCastTimer(2, 22539) +local timerFearNext = mod:NewNextTimer(30, 22686) +local timerVeilShadow = mod:NewTargetTimer(6, 22687) +local timerMC = mod:NewTargetTimer(15, 22667) + +local prewarn_P3 +function mod:OnCombatStart(delay) + prewarn_P3 = false +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(22539) and self:IsInCombat() then + warnShadowFlame:Show() + timerShadowFlame:Start() + elseif args:IsSpellID(22686) and self:IsInCombat() then + warnFear:Show() + timerFearNext:Start() + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(22687) then + warnVeilShadow:Show(args.destName) + timerVeilShadow:Start(args.destName) + elseif args:IsSpellID(22667) then + warnMC:Show(args.destName) + timerMC:Start(args.destName) + end +end + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(22687) then + timerVeilShadow:Cancel(args.destName) + end +end + +function mod:UNIT_HEALTH(uId) + if UnitHealth(uId) / UnitHealthMax(uId) <= 0.25 and self:GetUnitCreatureId(uId) == 11583 and not prewarn_P3 then + warnPhaseSoon:Show("3") + prewarn_P3 = true + end +end + +function mod:CHAT_MSG_MONSTER_YELL(msg) + if msg == L.YellDK then + self:SendSync("ClassCall", "DK") + elseif msg == L.YellDruid then + self:SendSync("ClassCall", "Druid") + elseif msg == L.YellHunter then + self:SendSync("ClassCall", "Hunter") + elseif msg == L.YellMage then + self:SendSync("ClassCall", "Mage") + elseif msg == L.YellPaladin then + self:SendSync("ClassCall", "Paladin") + elseif msg == L.YellPriest then + self:SendSync("ClassCall", "Priest") + elseif msg == L.YellRogue then + self:SendSync("ClassCall", "Rogue") + elseif msg == L.YellShaman then + self:SendSync("ClassCall", "Shaman") + elseif msg == L.YellWarlock then + self:SendSync("ClassCall", "Warlock") + elseif msg == L.YellWarrior then + self:SendSync("ClassCall", "Warrior") + elseif msg == L.YellPhase2 then + self:SendSync("Phase", 2) + elseif msg == L.YellPhase3 then + self:SendSync("Phase", 3) + end +end + +function mod:OnSync(msg, arg) + if msg == "ClassCall" then + warnClassCallSoon:Schedule(25) + warnClassCall:Show(arg) + timerClassCall:Start(arg) + elseif msg == "Phase" then + warnPhase:Show(arg) + end +end \ No newline at end of file diff --git a/DBM-BWL/Razorgore.lua b/DBM-BWL/Razorgore.lua new file mode 100644 index 0000000..87f1dde --- /dev/null +++ b/DBM-BWL/Razorgore.lua @@ -0,0 +1,35 @@ +local mod = DBM:NewMod("Razorgore", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 188 $"):sub(12, -3)) +mod:SetCreatureID(12435) +mod:SetMinSyncRevision(168) +mod:RegisterCombat("yell", L.YellPull)--Will fail if msg find isn't used, msg match won't find yell since a line break is omitted +mod:SetWipeTime(45)--guesswork + +mod:RegisterEvents( + "SPELL_AURA_APPLIED", + "SPELL_AURA_REMOVED" +) + +local warnConflagration = mod:NewTargetAnnounce(23023) + +local timerConflagration = mod:NewTargetTimer(10, 23023) +local timerAddsSpawn = mod:NewTimer(47, "TimerAddsSpawn", 19879)--Only for start of adds, not adds after the adds heh. + +function mod:OnCombatStart(delay) + timerAddsSpawn:Start() +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(23023) and args:IsDestTypePlayer() then + warnConflagration:Show(args.destName) + timerConflagration:Start(args.destName) + end +end + +function mod:SPELL_AURA_REMOVED(args) + if args:IsSpellID(23023) then + timerConflagration:Start(args.destName) + end +end \ No newline at end of file diff --git a/DBM-BWL/Vaelastrasz.lua b/DBM-BWL/Vaelastrasz.lua new file mode 100644 index 0000000..06c3ad9 --- /dev/null +++ b/DBM-BWL/Vaelastrasz.lua @@ -0,0 +1,47 @@ +local mod = DBM:NewMod("Vaelastrasz", "DBM-BWL", 1) +local L = mod:GetLocalizedStrings() + +mod:SetRevision(("$Revision: 175 $"):sub(12, -3)) +mod:SetCreatureID(13020) +mod:RegisterCombat("combat") + +mod:RegisterEvents( + "SPELL_CAST_START", + "SPELL_AURA_APPLIED", + "CHAT_MSG_MONSTER_YELL" +) + +local warnBreath = mod:NewCastAnnounce(23461) +local warnAdrenaline = mod:NewTargetAnnounce(18173) + +local specWarnAdrenaline = mod:NewSpecialWarningYou(18173) + +local timerBreath = mod:NewCastTimer(2, 23461) +local timerAdrenaline = mod:NewTargetTimer(20, 18173) +local timerCombatStart = mod:NewTimer(43, "TimerCombatStart", 2457) + +function mod:OnCombatStart(delay) +end + +function mod:SPELL_CAST_START(args) + if args:IsSpellID(23461) then + warnBreath:Show() + timerBreath:Start() + end +end + +function mod:SPELL_AURA_APPLIED(args) + if args:IsSpellID(18173) then + warnAdrenaline:Show(args.destName) + timerAdrenaline:Start(args.destName) + if args:IsPlayer() then + specWarnAdrenaline:Show() + end + end +end + +function mod:CHAT_MSG_MONSTER_YELL(msg) + if msg == L.Event or msg:find(L.Event) then + timerCombatStart:Start() + end +end diff --git a/DBM-BWL/localization.en.lua b/DBM-BWL/localization.en.lua new file mode 100644 index 0000000..cf77a03 --- /dev/null +++ b/DBM-BWL/localization.en.lua @@ -0,0 +1,136 @@ +local L + +----------------- +-- Razorgore -- +----------------- +L = DBM:GetModLocalization("Razorgore") + +L:SetGeneralLocalization{ + name = "Razorgore the Untamed" +} +L:SetTimerLocalization{ + TimerAddsSpawn = "Adds spawning" +} +L:SetOptionLocalization{ + TimerAddsSpawn = "Show timer for first adds spawning" +} +L:SetMiscLocalization{ + YellPull = "Intruders have breached the hatchery! Sound the alarm! Protect the eggs at all costs!\r\n"--Yes this yell actually has a return and new line in it. as grabbed by transcriptor +} +------------------- +-- Vaelastrasz -- +------------------- +L = DBM:GetModLocalization("Vaelastrasz") + +L:SetGeneralLocalization{ + name = "Vaelastrasz the Corrupt" +} + +L:SetTimerLocalization{ + TimerCombatStart = "Combat starts" +} + +L:SetOptionLocalization{ + TimerCombatStart = "Show time for start of combat" +} + +L:SetMiscLocalization{ + Event = "Too late, friends! Nefarius' corruption has taken hold...I cannot...control myself." +} +----------------- +-- Broodlord -- +----------------- +L = DBM:GetModLocalization("Broodlord") + +L:SetGeneralLocalization{ + name = "Broodlord Lashlayer" +} + +--------------- +-- Firemaw -- +--------------- +L = DBM:GetModLocalization("Firemaw") + +L:SetGeneralLocalization{ + name = "Firemaw" +} + +--------------- +-- Ebonroc -- +--------------- +L = DBM:GetModLocalization("Ebonroc") + +L:SetGeneralLocalization{ + name = "Ebonroc" +} + +---------------- +-- Flamegor -- +---------------- +L = DBM:GetModLocalization("Flamegor") + +L:SetGeneralLocalization{ + name = "Flamegor" +} + +------------------ +-- Chromaggus -- +------------------ +L = DBM:GetModLocalization("Chromaggus") + +L:SetGeneralLocalization{ + name = "Chromaggus" +} +L:SetWarningLocalization{ + WarnBreathSoon = "Breath soon", + WarnBreath = "%s", + WarnPhase2Soon = "Phase 2 soon" +} +L:SetTimerLocalization{ + TimerBreathCD = "%s CD" +} +L:SetOptionLocalization{ + WarnBreathSoon = "Show pre-warning for Chromaggus' Breaths", + WarnBreath = "Show warning when Chromaggus casts one of his Breaths", + TimerBreathCD = "Show Breath CD", + WarnPhase2Soon = "Announce when phase 2 is about to start" +} + +---------------- +-- Nefarian -- +---------------- +L = DBM:GetModLocalization("Nefarian") + +L:SetGeneralLocalization{ + name = "Nefarian" +} +L:SetWarningLocalization{ + WarnClassCallSoon = "Class call soon", + WarnClassCall = "%s call", + WarnPhaseSoon = "Phase %s soon", + WarnPhase = "Phase %s" +} +L:SetTimerLocalization{ + TimerClassCall = "%s call" +} +L:SetOptionLocalization{ + TimerClassCall = "Show timer for class call duration", + WarnClassCallSoon = "Pre-warn class calls", + WarnClassCall = "Announce class calls", + WarnPhaseSoon = "Announce when a new phase is about to start", + WarnPhase = "Announce phase change" +} +L:SetMiscLocalization{ + YellPull = "Let the games begin!", + YellP2 = "Well done, my minions. The mortals' courage begins to wane! Now, let's see how they contend with the true Lord of Blackrock Spire!!!", + YellP3 = "Impossible! Rise my minions! Serve your master once more!", + YellShaman = "Shamans, show me", + YellPaladin = "Paladins... I've heard you have many lives. Show me.", + YellDruid = "Druids and your silly shapeshifting. Lets see it in action!", + YellPriest = "Priests! If you're going to keep healing like that, we might as well make it a little more interesting!", + YellWarrior = "Warriors, I know you can hit harder than that! Lets see it!", + YellRogue = "Rogues? Stop hiding and face me!", + YellWarlock = "Warlocks, you shouldn't be playing with magic you don't understand. See what happens?", + YellHunter = "Hunters and your annoying pea-shooters!", + YellMage = "Mages too? You should be more careful when you play with magic..." +} \ No newline at end of file diff --git a/DBM-BWL/localization.es.lua b/DBM-BWL/localization.es.lua new file mode 100644 index 0000000..729190b --- /dev/null +++ b/DBM-BWL/localization.es.lua @@ -0,0 +1,125 @@ +if GetLocale() ~= "esES" and GetLocale() ~= "esMX" then return end +local L + +----------------- +-- Razorgore -- +----------------- +L = DBM:GetModLocalization("Razorgore") + +L:SetGeneralLocalization{ + name = "Sangrevaja el Indomable" +} +L:SetTimerLocalization{ + TimerAddsSpawn = "Salen adds" +} +L:SetOptionLocalization{ + TimerAddsSpawn = "Mostrar tiempo para que salgan los primeros adds" +} + +------------------- +-- Vaelastrasz -- +------------------- +L = DBM:GetModLocalization("Vaelastrasz") + +L:SetGeneralLocalization{ + name = "Vaelastrasz el Corrupto" +} + +----------------- +-- Broodlord -- +----------------- +L = DBM:GetModLocalization("Broodlord") + +L:SetGeneralLocalization{ + name = "Señor de linaje Capazote" +} + +--------------- +-- Firemaw -- +--------------- +L = DBM:GetModLocalization("Firemaw") + +L:SetGeneralLocalization{ + name = "Faucefogo" +} + +--------------- +-- Ebonroc -- +--------------- +L = DBM:GetModLocalization("Ebonroc") + +L:SetGeneralLocalization{ + name = "Ebanorroca" +} + +---------------- +-- Flamegor -- +---------------- +L = DBM:GetModLocalization("Flamegor") + +L:SetGeneralLocalization{ + name = "Flamagor" +} + +------------------ +-- Chromaggus -- +------------------ +L = DBM:GetModLocalization("Chromaggus") + +L:SetGeneralLocalization{ + name = "Chromaggus" +} +L:SetWarningLocalization{ + WarnBreathSoon = "Aliento pronto", + WarnBreath = "%s", + WarnPhase2Soon = "Fase 2 pronto" +} +L:SetTimerLocalization{ + TimerBreathCD = "%s CD" +} +L:SetOptionLocalization{ + WarnBreathSoon = "Mostrar pre-aviso para los Alientos", + WarnBreath = "Mostrar aviso cuando castea Aliento", + TimerBreathCD = "Mostrar tiempo para siguiente Aliento", + WarnPhase2Soon = "Mostrar pre-aviso para la fase 2" +} + +---------------- +-- Nefarian -- +---------------- +L = DBM:GetModLocalization("Nefarian") + +L:SetGeneralLocalization{ + name = "Nefarian" +} +L:SetWarningLocalization{ + WarnClassCallSoon = "Debuff de clase pronto", + WarnClassCall = "Debuff de %s", + WarnPhaseSoon = "Fase %s pronto", + WarnPhase = "Fase %s" +} +L:SetTimerLocalization{ + TimerClassCall = "%s Debuff de clase" +} +L:SetOptionLocalization{ + TimerClassCall = "Mostrar duración de debuff de clase", + WarnClassCallSoon = "Mostrar pre-aviso para debuff de clase", + WarnClassCall = "Mostrar aviso para debuff de clase", + WarnPhaseSoon = "Mostrar pre-aviso para cambio de fase", + WarnPhase = "Mostrar aviso al cambiar de fase" +} +L:SetMiscLocalization{ + YellPull = "¡Que comiencen los juegos!", + YellP2 = "Bien hecho, mis esbirros. El coraje de los mortales empieza a mermar. ¡Veamos ahora cómo se enfrentan al verdadero Señor de la Cubre de Roca Negra!", + YellP3 = "¡Imposible! ¡Erguíos, mis esbirros! ¡Servid a vuestro maestro una vez más!", + YellShaman = "Shamans, show me",--translate + YellPaladin = "Paladins... I've heard you have many lives. Show me.",--translate + YellDruid = "Druids and your silly shapeshifting. Lets see it in action!",--translate + YellPriest = "¡Sacerdotes! Si vais a seguir curando de esa forma, ¡podíamos hacerlo más interesante!", + YellWarrior = "¡Vamos guerreros, sé que podéis golpear más fuerte que eso! ¡Veámoslo!", + YellRogue = "Rogues? Stop hiding and face me!",--translate + YellWarlock = "Warlocks, you shouldn't be playing with magic you don't understand. See what happens?",--translate + YellHunter = "Hunters and your annoying pea-shooters!",--translate + YellMage = "¿Magos también? Deberíais tener más cuidado cuando jugáis con la magia...", + YellDK = "Caballero de la muerte"--translate +} \ No newline at end of file diff --git a/DBM-BWL/localization.ru.lua b/DBM-BWL/localization.ru.lua new file mode 100644 index 0000000..3c61f68 --- /dev/null +++ b/DBM-BWL/localization.ru.lua @@ -0,0 +1,125 @@ +if GetLocale() ~= "ruRU" then return end + +local L + +----------------- +-- Razorgore -- +----------------- +L = DBM:GetModLocalization("Razorgore") + +L:SetGeneralLocalization{ + name = "Бритвосмерт Неукротимый" +} +L:SetTimerLocalization{ + TimerAddsSpawn = "Появление аддов" +} +L:SetOptionLocalization{ + TimerAddsSpawn = "Показывать таймер до первого появления аддов" +} + +------------------- +-- Vaelastrasz -- +------------------- +L = DBM:GetModLocalization("Vaelastrasz") + +L:SetGeneralLocalization{ + name = "Валестраз Порочный" +} + +----------------- +-- Broodlord -- +----------------- +L = DBM:GetModLocalization("Broodlord") + +L:SetGeneralLocalization{ + name = "Предводитель драконов Разящий Бич" +} + +--------------- +-- Firemaw -- +--------------- +L = DBM:GetModLocalization("Firemaw") + +L:SetGeneralLocalization{ + name = "Огнечрев" +} + +--------------- +-- Ebonroc -- +--------------- +L = DBM:GetModLocalization("Ebonroc") + +L:SetGeneralLocalization{ + name = "Черноскал" +} + +---------------- +-- Flamegor -- +---------------- +L = DBM:GetModLocalization("Flamegor") + +L:SetGeneralLocalization{ + name = "Пламегор" +} + +------------------ +-- Chromaggus -- +------------------ +L = DBM:GetModLocalization("Chromaggus") + +L:SetGeneralLocalization{ + name = "Хромаггус" +} +L:SetWarningLocalization{ + WarnBreathSoon = "Скоро дыхание", + WarnBreath = "%s", + WarnPhase2Soon = "Скоро 2-ая фаза" +} +L:SetTimerLocalization{ + TimerBreathCD = "%s восстановление" +} +L:SetOptionLocalization{ + WarnBreathSoon = "Предварительное предупреждение Дыхания Хромаггуса", + WarnBreath = "Показывать предупреждение о дыханиях Хромаггуса", + TimerBreathCD = "Показывать время восстановления дыханий", + WarnPhase2Soon = "Предупреждать о второй фазе" +} + +---------------- +-- Nefarian -- +---------------- +L = DBM:GetModLocalization("Nefarian") + +L:SetGeneralLocalization{ + name = "Нефариан" +} +L:SetWarningLocalization{ + WarnClassCallSoon = "Скоро вызов класса", + WarnClassCall = "Дебафф на %s", + WarnPhaseSoon = "Скоро фаза %s", + WarnPhase = "Фаза %s" +} +L:SetTimerLocalization{ + TimerClassCall = "%s зов" +} +L:SetOptionLocalization{ + TimerClassCall = "Показывать таймер классовых вызовов", + WarnClassCallSoon = "Предупреждение классовых вызовов", + WarnClassCall = "Объявлять классовый вызов", + WarnPhaseSoon = "Объявлять, когда следующая фаза скоро начнется", + WarnPhase = "Объявлять смену фаз" +} +L:SetMiscLocalization{ + YellPull = "Ну что ж, поиграем!", + YellP2 = "Браво, слуги мои! Смертные утрачивают мужество! Поглядим же, как они справятся с истинным Повелителем Черной горы!!!", + YellP3 = "Не может быть! Восстаньте, мои прислужники! Послужите господину еще раз!", + YellShaman = "Шаманы, покажите, на что способны ваши тотемы!", + YellPaladin = "Паладины… Я слышал, у вас несколько жизней. Докажите.", + YellDruid = "Друиды и их дурацкие превращения… Ну что ж, поглядим!", + YellPriest = "Жрецы! Если вы собираетесь продолжать так лечить, то давайте хоть немного разнообразим процесс!", + YellWarrior = "Воины! Я знаю, вы можете бить сильнее! Ну-ка, покажите!", + YellRogue = "Rogues? Stop hiding and face me!", + YellWarlock = "Чернокнижники, ну не беритесь вы за волшебство, которого сами не понимаете! Видите, что получилось?", + YellHunter = "Охотники со своими жалкими пугачами!", + YellMage = "И маги тоже? Осторожнее надо быть, когда играешь с магией…" +} \ No newline at end of file diff --git a/DBM-BurningCrusade/DBM-BurningCrusade.toc b/DBM-BurningCrusade/DBM-BurningCrusade.toc new file mode 100644 index 0000000..6765cfe --- /dev/null +++ b/DBM-BurningCrusade/DBM-BurningCrusade.toc @@ -0,0 +1,13 @@ +## Interface: 30300 +## Title:|cffffe00a<|r|cffff7d0aDBM|r|cffffe00a>|r |cff69ccf0Burning Crusade|r +## SavedVariablesPerCharacter: DBMBurningCrusade_SavedModOptions +## LoadOnDemand: 0 +## RequiredDeps: DBM-Core +## DefaultState: enabled +## Author: Tandanu and Nitram, updated by Junior and Szyler. +## X-Website: https://discord.gg/4ZHfgskSvM +localization.en.lua +localization.cn.lua +localization.tw.lua +localization.es.lua +DBMv3-Compatibility.lua diff --git a/DBM-BurningCrusade/DBMv3-Compatibility.lua b/DBM-BurningCrusade/DBMv3-Compatibility.lua new file mode 100644 index 0000000..ffa8e45 --- /dev/null +++ b/DBM-BurningCrusade/DBMv3-Compatibility.lua @@ -0,0 +1,466 @@ +-- ********************************************************* +-- ** Deadly Boss Mods - Core ** +-- ** http://www.deadlybossmods.com ** +-- ********************************************************* +-- +-- This addon is written and copyrighted by: +-- * Paul Emmerich (Tandanu @ EU-Aegwynn) (DBM-Core) +-- * Martin Verges (Nitram @ EU-Azshara) (DBM-GUI) +-- +-- The localizations are written by: +-- * deDE: Tandanu/Nitram +-- * enGB: Nitram/Tandanu +-- * zhCN: yleaf(yaroot@gmail.com) +-- * zhTW: yleaf(yaroot@gmail.com) +-- * (add your names here!) +-- +-- +-- The code of this addon is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. (see license.txt) +-- All included textures and sounds are copyrighted by their respective owners. +-- +-- +-- You are free: +-- * to Share to copy, distribute, display, and perform the work +-- * to Remix to make derivative works +-- Under the following conditions: +-- * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). +-- * Noncommercial. You may not use this work for commercial purposes. +-- * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. + + +--------------- +-- Globals -- +--------------- +DBMBurningCrusade_SavedModOptions = {} +DBMBC = {} +DBM_SBT = {} + + +-------------- +-- Locals -- +-------------- +local frame = CreateFrame("Frame") + +local function checkEntry(t, val) + for i, v in ipairs(t) do + if v == val then + return true + end + end + return false +end + +---------------------- +-- Event Handling -- +---------------------- +local function registerEvents(...) + for i = 1, select("#", ...) do + frame:RegisterEvent(select(i, ...)) + end +end + +frame:SetScript("OnEvent", function(self, event, ...) if DBMBC[event] then DBMBC[event](DBMBC, ...) end end) + +registerEvents("ADDON_LOADED") + + +--------------- +-- Options -- +--------------- +local function loadModOptions(mod) +end + + +-------------- +-- OnLoad -- +-------------- +local bannedMods = { -- a list of "banned" (meaning they are replaced by another mod like DBM-Battlegrounds (replaced by DBM-PvP)) boss mods, these mods will not be loaded by DBM (and they wont show up in the GUI) + "DBM_Auchindoun", -- replaced by DBM-Party-BC + "DBM_Coilfang", -- replaced by DBM-Party-BC + "DBM_CoT", -- replaced by DBM-Party-BC + "DBM_TempestKeep", -- replaced by DBM-Party-BC + "DBM_Terrace", -- replaced by DBM-Party-BC +} + +function DBMBC:ADDON_LOADED(mod) + if mod ~= "DBM-BurningCrusade" then return end + for i = 1, GetNumAddOns() do + if GetAddOnMetadata(i, "X-DBM-BC-AddOn") and not checkEntry(bannedMods, GetAddOnInfo(i)) then + table.insert(DBM.AddOns, { + sort = tonumber(GetAddOnMetadata(i, "X-DBM-Tab-Sort") or math.huge) or math.huge, + category = "BC", + name = GetAddOnMetadata(i, "X-DBM-Name") or "", + zone = {strsplit(",", GetAddOnMetadata(i, "X-DBM-Tab-LoadZone") or "")}, + zoneId = {strsplit(",", GetAddOnMetadata(i, "X-DBM-Mod-LoadZoneID") or "")}, + v3Tab = GetAddOnMetadata(i, "X-DBM-Tab-ID"), + modId = GetAddOnInfo(i), + }) + end + for i = #DBM.AddOns[#DBM.AddOns].zoneId, 1, -1 do + local id = tonumber(DBM.AddOns[#DBM.AddOns].zoneId[i]) + if id then + DBM.AddOns[#DBM.AddOns].zoneId[i] = id + else + table.remove(DBM.AddOns[#DBM.AddOns].zoneId, i) + end + end + end + table.sort(DBM.AddOns, function(v1, v2) return v1.sort < v2.sort end) + registerEvents("RAID_ROSTER_UPDATE") + DBM:ZONE_CHANGED_NEW_AREA() +end + + +------------------------------- +-- (Faked) Boss Mod Object -- +------------------------------- +local proxy = {} + + +----------------------- +-- General Methods -- +----------------------- +function proxy:RegisterEvents(...) + self.mod:RegisterEvents(...) + for i = 1, select("#", ...) do + local event = select(i, ...) + self.mod[event] = function(self, ...) + if self.proxy.OnEvent then self.proxy:OnEvent(event, ...) end + end + end +end + +function proxy:SendSync(...) + self.mod:SendSync(...) +end + +function proxy:AddMsg(...) + self.mod:AddMsg(...) +end + +function proxy:AddOption(id, default, name, func) + self.mod:AddBoolOption(id, default, "announce", func) + DBM:GetModLocalization(self.mod.id):SetOptionLocalization({ + [id] = name + }) +end + +function proxy:AddBarOption(bar, default) + self.mod:AddBoolOption(bar:gsub("%(%.%*%)", DBM_BC_TARGET), default, "timer") + if DBM_SBT[bar] then + DBM:GetModLocalization(self.mod.id):SetOptionLocalization({ + [bar] = DBM_SBT[bar] + }) + end +end + +function proxy:IsWipe() + local dead = 0 + for i = 1, GetNumRaidMembers() do + dead = dead + ((UnitIsDeadOrGhost("raid"..i) and 1) or 0) + end + return dead >= 18 +end + +function proxy:SetBossHealthInfo(...) + self.mod:SetBossHealthInfo(...) +end + + +-------------- +-- Combat -- +-------------- +function proxy:RegisterCombat(type, ...) + self.mod:RegisterCombat(((type and type:lower()) or "combat"), ...) +end + +function proxy:SetMinCombatTime(...) + self.mod:SetMinCombatTime(...) +end + +function proxy:SetCreatureID(...) + self.mod:SetCreatureID(...) +end + + +------------------- +-- Range Check -- +------------------- +function DBM_Gui_DistanceFrame_Show() + DBM.RangeCheck:Show() +end + +function DBM_Gui_DistanceFrame_Hide() + DBM.RangeCheck:Hide() +end + +function DBM_Gui_DistanceFrame_SetDistance(distance) + DBM.RangeCheck:Show(distance) +end + +----------------- +-- Announces -- +----------------- +local lastWarning +local lastWarningTime = 0 +function proxy:Announce(msg, color, noBroadcast) + local warning = self["warning"..(color or 1)] + if not warning then return end + msg = msg:gsub("%s*%*%*%*%s*", "") + if msg ~= lastWarning or GetTime() - lastWarningTime > 1.5 then + lastWarning = msg + lastWarningTime = GetTime() + warning:Show(msg) + end +end + +function proxy:ScheduleAnnounce(timer, msg, color) + local warning = self["warning"..(color or 1)] + if not warning then return end + warning:Schedule(timer, msg) +end + +function proxy:UnScheduleAnnounce(msg, color) + local warning = self["warning"..(color or 1)] + if not warning then return end + warning:Cancel(msg) +end + +function proxy:AddSpecialWarning(text) + self.specWarning:Show(text) +end + +function proxy:SendHiddenWhisper(msg, player) + self.mod:SendWhisper(msg, player) +end + + +---------------- +-- Schedule -- +---------------- +function proxy:Schedule(timer, func, ...) + self.mod:Schedule(timer, func, ...) +end + +function proxy:ScheduleEvent(timer, ...) + self.mod:Schedule(timer, self.OnEvent, self, ...) +end +proxy.ScheduleSelf = proxy.ScheduleEvent + + +function proxy:ScheduleMethod(timer, method, ...) + self.mod:Schedule(timer, self[method], self, ...) +end + +function proxy:UnSchedule(...) + self.mod:Unschedule(...) +end + +function proxy:UnScheduleEvent(...) + self.mod:Unschedule(self.OnEvent, self, ...) +end +proxy.UnScheduleSelf = proxy.UnScheduleEvent + +function proxy:UnScheduleMethod(method, ...) + self.mod:Unschedule(self[method], self, ...) +end + +function proxy:UnScheduleAll() + self.mod:Unschedule() +end + +function proxy:GetEventScheduleTimeLeft() + return 0 +end +proxy.GetSelfScheduleTimeLeft = proxy.GetEventScheduleTimeLeft + +function proxy:GetAnnounceScheduleTimeLeft() + return 0 +end + +function proxy:GetMethodScheduleTimeLeft() + return 0 +end + + + +------------ +-- Bars -- +------------ +function proxy:StartStatusBarTimer(timer, name, icon, noBroadcast, repetitions, colorR, colorG, colorB, colorA) + if self.Options[name] == false then return end + self.timer:Start(timer, name) + self.timer:UpdateIcon(icon, name) +end + +function proxy:UpdateStatusBarTimer(name, elapsed, timer, newName, newIcon, noBroadcast) + self.timer:Update(elapsed, timer, name) + if newIcon then self.timer:UpdateIcon(newIcon, name) end + if newName then self.timer:UpdateName(newName, name) end +end + +function proxy:GetStatusBarTimerTimeLeft(name) + self.timer:GetTime(name) +end + +function proxy:EndStatusBarTimer(name) + self.timer:Stop(name) +end + +------------- +-- Icons -- +------------- +function proxy:SetIcon(target, timer, icon) + self.mod:SetIcon(target, icon, timer) +end + +function proxy:RemoveIcon(...) + self.mod:RemoveIcon(...) +end + +function proxy:RemoveAllIcons() + return 0 +end + + +-------------------------- +-- Callback Functions -- +-------------------------- +local function onSync(self, ...) + if self.proxy.OnSync then self.proxy:OnSync(...) end +end + +local function onCombatStart(self, delay, ...) + self.proxy.InCombat = true + if self.proxy.OnCombatStart then self.proxy:OnCombatStart(delay or 0, ...) end +end + +local function onCombatEnd(self, ...) + if self.proxy.OnCombatEnd then self.proxy:OnCombatEnd(...) end + self.proxy.InCombat = false +end + +local function onUpdate(self, elapsed) + if self.proxy.OnUpdate then self.proxy:OnUpdate(elapsed) end +end + +local function onInitialize(self) + if self.proxy.OnUpdate then self:RegisterOnUpdateHandler(onUpdate, self.proxy.UpdateInterval or 0.2) end + self.proxy.Options = self.Options +end + +------------------- +-- Constructor -- +------------------- +do + local function getAddOnIDByTab(tab) + for i, v in ipairs(DBM.AddOns) do + if tab == v.v3Tab then + return v.modId + end + end + end + local mt = {__index = proxy} + + function DBM:NewBossMod(id, name, _, zone, tab) + local obj = setmetatable({}, mt) + local mod = DBM:NewMod(id, getAddOnIDByTab(tab)) + mod:SetRevision(1) + mod:SetZone(zone) + obj.timer = mod:NewTimer(10, "%s", nil, nil, false) + obj.warning1 = mod:NewAnnounce("%s", 1, nil, nil, false) + obj.warning2 = mod:NewAnnounce("%s", 2, nil, nil, false) + obj.warning3 = mod:NewAnnounce("%s", 3, nil, nil, false) + obj.warning4 = mod:NewAnnounce("%s", 4, nil, nil, false) + obj.specWarning = mod:NewSpecialWarning("%s", nil, false) + DBM:GetModLocalization(id):SetGeneralLocalization({ + name = name + }) + obj.mod = mod + obj.Options = mod.Options + mod.proxy = obj + mod.OnSync = onSync + mod.OnCombatStart = onCombatStart + mod.OnCombatEnd = onCombatEnd + mod.OnInitialize = onInitialize + return obj + end +end + +---------------------- +-- Misc functions -- +---------------------- + +function DBM:GetMod(id) + return DBM:GetModByName(id) and DBM:GetModByName(id).proxy +end + +function DBM.SecondsToTime(t) + if t <= 60 then + return ("%.1f"):format(t) + else + return ("%d:%0.2d"):format(t/60, math.fmod(t, 60)) + end +end + +function DBM.GetBuff(unitID, buff) + local i = 1 + while UnitBuff(unitID, i) do + if UnitBuff(unitID, i) == buff then + return i + end + i = i + 1 + end +end + +function DBM.GetDebuff(unitID, buff) + local i = 1 + while UnitDebuff(unitID, i) do + if UnitDebuff(unitID, i) == buff then + return i + end + i = i + 1 + end +end + +DBM.Rank = 0 +do + local function updateRank() + DBM.Rank = DBM:GetRaidRank() or 0 + end + + function DBMBC:RAID_ROSTER_UPDATE() + DBM:Schedule(0, updateRank) + end +end + + +------------------------------- +-- Schedule function hooks -- +------------------------------- + +do + local old = DBM.Schedule + function DBM:Schedule(t, ...) + if type(self) == "number" then + return old(DBM, self, t, ...) + else + return old(self, t, ...) + end + end +end + +do + local old = DBM.Unschedule + function DBM:Unschedule(...) + if type(self) == "function" then + return old(DBM, self, ...) + else + return old(self, ...) + end + end +end + +function DBM.UnSchedule(...) + return DBM:Unschedule(...) +end diff --git a/DBM-BurningCrusade/localization.cn.lua b/DBM-BurningCrusade/localization.cn.lua new file mode 100644 index 0000000..6b20d64 --- /dev/null +++ b/DBM-BurningCrusade/localization.cn.lua @@ -0,0 +1,34 @@ +-- yleaf(yaroot@gmail.com) +if GetLocale() ~= "zhCN" then return end + +DBM_BC_TARGET = "<目标>" + +DBM_GRUULS_LAIR = "格鲁尔的巢穴" +DBM_MAGS_LAIR = "玛瑟里顿的巢穴" +DBM_HELLFIRE = "地狱火半岛" +DBM_SHADOWMOON = "影月谷" + +DBM_GENERIC_ENRAGE_WARN = "%s%s后激怒" +DBM_GENERIC_PHASE_MSG = "第%s阶段" +DBM_GENERIC_PHASE_MSG2 = "第%s阶段" +DBM_PHASE = "第%s阶段" +DBM_WAVE = "第%s波" +DBM_SET_ICON = "设置图标" +DBM_SEND_WHISPER = "发送密语" +DBM_HP_UNKNOWN = "未知血量" +DBM_NO_TARGET = "<没有目标>" +DBM_UPGRADE = "升级" +DBM_MISC = "其他" +DBM_IS = "是" +DBM_OR = "或" +DBM_AND = "和" +DBM_UNKNOWN = "未知" +DBM_LOCAL = "本地" +DBM_SEC = "秒" +DBM_MIN = "分" +DBM_SECOND = "秒" +DBM_SECONDS = "秒" +DBM_MINUTES = "分钟" +DBM_MINUTE = "分钟" +DBM_DEAD = "死亡" +DBM_OFFLINE = "离线" diff --git a/DBM-BurningCrusade/localization.en.lua b/DBM-BurningCrusade/localization.en.lua new file mode 100644 index 0000000..311a20a --- /dev/null +++ b/DBM-BurningCrusade/localization.en.lua @@ -0,0 +1,31 @@ +DBM_BC_TARGET = "" + +DBM_GRUULS_LAIR = "Gruul's Lair" +DBM_MAGS_LAIR = "Magtheridon's Lair" +DBM_HELLFIRE = "Hellfire Peninsula" +DBM_SHADOWMOON = "Shadowmoon Valley" + +DBM_GENERIC_ENRAGE_WARN = "Enrage in %s %s" +DBM_GENERIC_PHASE_MSG = "Phase %s" +DBM_GENERIC_PHASE_MSG2 = "%s phase" +DBM_PHASE = "Phase %s" +DBM_WAVE = "Wave %s" +DBM_SET_ICON = "Set icon" +DBM_SEND_WHISPER = "Send whisper" +DBM_HP_UNKNOWN = "HP unknown" +DBM_NO_TARGET = "" +DBM_UPGRADE = "upgrade" +DBM_MISC = "miscellaneous" +DBM_IS = "is" +DBM_OR = "or" +DBM_AND = "and" +DBM_UNKNOWN = "unknown" +DBM_LOCAL = "local" +DBM_SEC = "sec" +DBM_MIN = "min" +DBM_SECOND = "second" +DBM_SECONDS = "seconds" +DBM_MINUTES = "minutes" +DBM_MINUTE = "minute" +DBM_DEAD = "Dead" +DBM_OFFLINE = "Offline" diff --git a/DBM-BurningCrusade/localization.es.lua b/DBM-BurningCrusade/localization.es.lua new file mode 100644 index 0000000..647a60c --- /dev/null +++ b/DBM-BurningCrusade/localization.es.lua @@ -0,0 +1,33 @@ +if GetLocale() ~= "esES" and GetLocale() ~= "esMX" then return end + +DBM_BC_TARGET = "" + +DBM_GRUULS_LAIR = "Guarida de Gruul" +DBM_MAGS_LAIR = "Guarida de Magtheridon" +DBM_HELLFIRE = "Península del Fuego Infernal" +DBM_SHADOWMOON = "Valle Sombraluna" + +DBM_GENERIC_ENRAGE_WARN = "Enrage en %s %s" +DBM_GENERIC_PHASE_MSG = "Fase %s" +DBM_GENERIC_PHASE_MSG2 = "%s fase" +DBM_PHASE = "Fase %s" +DBM_WAVE = "Oleada %s" +DBM_SET_ICON = "Poner icono" +DBM_SEND_WHISPER = "Enviar susurro" +DBM_HP_UNKNOWN = "HP Desconocida" +DBM_NO_TARGET = "" +DBM_UPGRADE = "actualizar" +DBM_MISC = "miscelanea" +DBM_IS = "es" +DBM_OR = "o" +DBM_AND = "y" +DBM_UNKNOWN = "desconocido" +DBM_LOCAL = "local" +DBM_SEC = "seg" +DBM_MIN = "min" +DBM_SECOND = "segundo" +DBM_SECONDS = "segundos" +DBM_MINUTES = "minutos" +DBM_MINUTE = "minuto" +DBM_DEAD = "Muerto" +DBM_OFFLINE = "Desconectado" \ No newline at end of file diff --git a/DBM-BurningCrusade/localization.tw.lua b/DBM-BurningCrusade/localization.tw.lua new file mode 100644 index 0000000..38f3e9f --- /dev/null +++ b/DBM-BurningCrusade/localization.tw.lua @@ -0,0 +1,34 @@ +-- yleaf(yaroot@gmail.com) +if GetLocale() ~= "zhTW" then return end + +DBM_BC_TARGET = "<目標>" + +DBM_GRUULS_LAIR = "戈魯爾之巢" +DBM_MAGS_LAIR = "瑪瑟里頓的巢穴" +DBM_HELLFIRE = "地獄火半島" +DBM_SHADOWMOON = "影月谷" + +DBM_GENERIC_ENRAGE_WARN = "%s%s后激怒" +DBM_GENERIC_PHASE_MSG = "第%s階段" +DBM_GENERIC_PHASE_MSG2 = "第%s階段" +DBM_PHASE = "第%s階段" +DBM_WAVE = "第%s波" +DBM_SET_ICON = "設置圖標" +DBM_SEND_WHISPER = "發送密語" +DBM_HP_UNKNOWN = "未知血量" +DBM_NO_TARGET = "<沒有目標>" +DBM_UPGRADE = "升級" +DBM_MISC = "其他" +DBM_IS = "是" +DBM_OR = "或" +DBM_AND = "和" +DBM_UNKNOWN = "未知" +DBM_LOCAL = "本地" +DBM_SEC = "秒" +DBM_MIN = "分" +DBM_SECOND = "秒" +DBM_SECONDS = "秒" +DBM_MINUTES = "分鐘" +DBM_MINUTE = "分鐘" +DBM_DEAD = "死亡" +DBM_OFFLINE = "離線" diff --git a/DBM-Core/Changelog.txt b/DBM-Core/Changelog.txt new file mode 100644 index 0000000..f690606 --- /dev/null +++ b/DBM-Core/Changelog.txt @@ -0,0 +1,218 @@ +v5.00 29/12/2020 +- Initial release +---------------------------------------------------------------------------------------------------- +v5.01 13/01/2021 +- Added hateful strikes to Gruul/Curator +- Added Magtheridon Heroic +- Added Enrage timer on Maulgar +- Added Yell on Gruul Cave In +- Added Heroic version of Gruul's Cave In +- Fixed Heroic Prince +- Fixed Heroic Illhoof +- SpellTimers (player cooldowns) are now separate bars from boss timers +- Added vanilla mods from ABM +---------------------------------------------------------------------------------------------------- +v5.02 06/03/2021 +- Added Animal bosses in Karazhan +- Added warning for Magtheridon debris +- Added timer for Infernals on Magtheridon +- Added sunder armor stacks on Prince +- Improved heroic Magtheridon targeting search for fingers and hands casts. +- Fixed Moroes PWS cast trigger +- Fixed new Gruul timers +- Fixed timers running too long on Illhoof +- Fixed the timers on Maulgar +- Fixed the berserk timer on Kazzak +- Raidleaders/assist can now opt in to autmatically whisper the target of the Kazzak bomb +---------------------------------------------------------------------------------------------------- +v5.03 09/04/2021 +- Added Heroic Romulo and Julianne mechanic +- Added missing onyxia vanilla module +- Fixed error on Magtheridon's Infernals +- Fixed wrong spell name on Rokad (Animal boss) +- Fixed the first infernal timer of Prince +- Fixed an error on Heroic Aran +- Fixed a missing translation on Opera in Karazhan +- Fixed timer for Holy Wrath on Maiden +- Fixed the Black Morass portal timer +---------------------------------------------------------------------------------------------------- +v5.04 05/05/2021 +- Added support for phases for vanilla and T4 bosses in Details! +- Added support for Raid Ability Timeline (WeakAura2) +- Added support for /Pull as an alias for /dbm pull +- Added a backup CD timer for Maiden's Holy Wrath +- Fixed a bunch of bosses for Naxxramas +- Fixed a bunch of lua errors in vanilla raids +- Fixed Whirlwind timers for Maulgar +- Fixed Magtheridon back to working state +- Fixed missing start timers on Prince +- Fixed broken Sunder warning on Prince +- Fixed Into Shadow Realm timer for Heroic 25 man Prince +- Fixed heroic Romulo and Julianne mechanic +- Fixed Boulder and Cave in CD timers for Gruul +- Added different versions for heroic Hateful Strike/Hateful Bolt on Gruul/Curator +---------------------------------------------------------------------------------------------------- +v5.05 23/05/2021 +- Added special warning to purge Power word: Shield on Maulgar +- Added Repentance cast timer to Maiden +- Added Moroes enrage timer +- Added a phase 2 warning on Moroes +- Fixed whirlwind timers on Maulgar +- Fixed Fingers and Hands timer on Magtheridon to only show on Ascended +- Added a timer for Attumen curse +- Fixed Moroes Vanish timer +- Fixed Desperate prayer on heroic Flex +- Fixed Romulo and Julianne lua error +- Fixed timers for Howl and Red riding hood on Big Bad Wolf +- Fixed Julianne heal cast time per difficutly +- Fixed Hateful timer in phase 2 Curator +- Fixed Smoldering announce text +- Fixed Illhoof curse timer +- Fixed cast timer on Netherspite's Breath +- Fixed Aran Flame Wreath cast timer +- Fixed Phase 3 warning on Prince +- Fixed different timers on Prince per difficulty +- Fixed Infernal timers on Prince +- Fixed Sunder armor announce on Prince +---------------------------------------------------------------------------------------------------- +v5.06 13/06/2021 +- Added "/pull 0" and "/pull cancel" as commands to stop a pull timer. + (Requires everyone to update for everyone to see the timer cancelled) +- Added mechanics in the following Mythic Dungeons: + - Shattered Halls + - Underbog + - Steamvault + - Auchenai Crypts + - Mana Tombs + - Sethekk Halls + - Shadow Labs + +- Fixed missing timers on Maiden +- Fixed the timer on Curator being off after the first Evocate +- Fixed Big Bad Wolf timers of Red Riding Hood mechanic +---------------------------------------------------------------------------------------------------- +v5.07 05/08/2021 +- Added all SSC bosses +- Fixed icon for Big Bad Wolf opera event's Red Riding Hood target in Karazhan +- Fixed Magtheridon's Hand and Fingers timers to only appear in Ascended +- Added warning for standing in Conflaguration on Magtheridon +- Fixed lua error in Shadow Labs +- Removed warning for Immolate on Garr (casts too often with no danger) +---------------------------------------------------------------------------------------------------- +v5.07.14 17/08/2021 +- Rework of Lady Vashj module, all timers fixed. +- Ascended mechanics added for Lady Vashj. +- Fixed Free for All not activating correctly on Lady Vashj. +- Added Toxic Sporebat timer for Phase 3. +- Fixed Tainted Core not being correctly tracked. +- Fixed Coilfang Enchantress spam. +- Fixed Static Charge targets not being shown on Lady Vashj. +- Added missing icons for Phase 2 timers on Lady Vashj. +- Added absent Special Warning for standing in Toxic Spore. +- Fixed Berserk timer being incorrect for Lady Vashj. +---------------------------------------------------------------------------------------------------- +v5.08 20/08/2021 +- Fixed timers for Ascended Fathomlord +- Fixed timers for Ascended Tidewalker +- Fixed Generator timer on Vashj in phase 3 +- Added tentacle for Lurker +- Fixed Geyser timer stop on breath on Lurker +- Added new Hakkar mechanics +- Added new mechanics to several other ZG bosses +---------------------------------------------------------------------------------------------------- +v5.08.1 25/09/2021 +- Added new Mechancis to MC bosses: + - Lucifron + - Magmadar + - Gehennas + - Garr + - Baron + - Golemagg +---------------------------------------------------------------------------------------------------- +v5.08.2 27/09/2021 +- Added new Mechancis to MC bosses: + - Shazzrah + - Ragnaros +- Adding all difficulty IDs to Geddon +- Fixed the spam on Reverb on Garr +- Added timers to Golemagg's cave in +- Removed spammy abilities from Sulfuron +---------------------------------------------------------------------------------------------------- +v5.09 20/10/2021 +- Added new mechanics to Onyxia Ascended +- Added warning for Poison pool on Hakkar + +- Fixed lua error on Tidewalker +- Fixed timer for Son of Hakkar on Hakkar +- Fixed lua error in Black Morass +---------------------------------------------------------------------------------------------------- +v5.10 24/01/2022 +- Added all Tempest Keep bosses + +- Fixed lua error on Tidewalker +---------------------------------------------------------------------------------------------------- +v5.11 17/02/2022 +- Fixed combatstop on Al'ar +- Fixed repeat timer of Pound on Void Reaver +- Fixed Void Reaver fire warning to only show on self +- Fixed timer on Solarian's Lunar/Solar Wrath +- Fixed timer on Solarian's Void Spawn (New timer on killing Void Spawn) +---------------------------------------------------------------------------------------------------- +v5.12 19/04/2022 +- Fixed Dive Bomb timers on Al'ar +- Fixed Flame Cascade (fire tornado) timer on Al'ar +- Fixed Next Breath timer on Al'ar +- Fixed platform change timer on Al'ar +- Fixed Phasing issues on Al'ar +- Fixed error on Feather refresh on Al'ar +- Added Opt-In option to yell on receiving Living Bomb on Al'ar +- Removed unused code, combat log events and simplified codings on Void Reaver +- Fixed the wording of upcoming priest announcements on High Astromancer Solarian +- Fixed timers for Solar and Lunar Wrath yells on High Astromancer Solarian +- Changed the warning level of Solar and Lunar stacks from 3 to 5 on High Astromancer Solarian +- Added timer and warning for Void Seed on High Astromancer Solarian +- Added Opt-In option to yell on receiving Panic on High Astromancer Solarian +- Added Opt-In option to set icon on Ascended Orbital Blast target on High Astromancer Solarian +- Fixed the combat entry point on Kael'thas Sunstrider +- Fixed the timer delays in last phase for Ascended difficulty on Kael'thas Sunstrider +- Fixed first pull timer for Telonicus on Kael'thas Sunstrider +- Fixed timers for council resurrection on Kael'thas Sunstrider +- Fixed timer for Kael'thas Sunstrider activation on Kael'thas Sunstrider +- Fixed Thaladred the Darkener's Gaze and Bladestorm mechanic change timers and warnings on Ascended difficulties on Kael'thas Sunstrider +- Added warnings and timers for Focused Burst on Kael'thas Sunstrider +- Added warnings and timers for World in Flames on Kael'thas Sunstrider +- Added warnings and timers for Bladestorm on Kael'thas Sunstrider +- Added warnings and timers for Blood Leech on Kael'thas Sunstrider +- Added warnings and timers for Mana Shield on Kael'thas Sunstrider +- Added warnings and timers for Rebirth on Kael'thas Sunstrider +- Added warnings and timers for Flamestrike on Kael'thas Sunstrider +- Added warnings and timers for Form Dying Star on Kael'thas Sunstrider +- Added warnings and timers for Gravity Lapse on Kael'thas Sunstrider +- Added Opt-In option to set icon on Gaze of the Darkener target on Kael'thas Sunstrider +- Added Opt-In option to set icon on Focused Burst target on Kael'thas Sunstrider +- Added checks for ascended only mechanics to only show on Ascended difficulties in Tempest Keep +---------------------------------------------------------------------------------------------------- +v5.13 dd/mm/yyyy +- Fixed the combat entry point for long and short intro on Kael'thas Sunstrider +- Fixed a LUA error on Solarian +- Fixed the "Lunar Fire (Unknown" bug on Solarion +- Fixed double Voidspawn timers going off on Solarion +- Fixed Lunar Wrath yell countdown timer on Solarion + +- Added some of the mechanics for the mobs in Tempest Keep +---------------------------------------------------------------------------------------------------- +v5.14 08/08/2022 +- Added failsafe to avoid double triggers going into Phase 2 +- Redid timers for Finger and Hand of Death +- Fixed Phase 3 timers +- Added timer for the Debris damage going into Phase 3 +- Added a proper target function to always show who's the target of Finger and Hand of Death +---------------------------------------------------------------------------------------------------- +v5.15 16/10/2022 +- Added all Zul'Aman bosses and timers for Normal and Heroic + +- Fixed pull cancel message to no longer have an extra "0" at the end +- Fixed and improved yellLivingBomb on Alar. This option is now turned on by default and will now count down from 3 before you drop it. +- Fixed warnFlameBreath on Alar not working for all difficulties +- Solarion now warns stack number at 4 instead of 5 +---------------------------------------------------------------------------------------------------- diff --git a/DBM-Core/DBM-Arrow.lua b/DBM-Core/DBM-Arrow.lua new file mode 100644 index 0000000..a0e533e --- /dev/null +++ b/DBM-Core/DBM-Arrow.lua @@ -0,0 +1,250 @@ +-- This file uses models and textures taken from TomTom. The 3d arrow model was created by Guillotine (curse.guillotine@gmail.com) and 2d minimap textures by Cladhaire. + +---------------------------- +-- Initialize variables -- +---------------------------- +-- globals +DBM.Arrow = {} + +-- locals +local runAwayArrow +local targetType +local targetPlayer +local targetX, targetY +local hideTime, hideDistance + +-- cached variables +local pi, pi2 = math.pi, math.pi * 2 +local floor = math.floor +local sin, cos, atan2, sqrt, min = math.sin, math.cos, math.atan2, math.sqrt, math.min +local GetPlayerMapPosition = GetPlayerMapPosition + +-------------------- +-- Create Frame -- +-------------------- +local frame = CreateFrame("Button", nil, UIParent) +frame:Hide() +frame:SetFrameStrata("HIGH") +frame:SetWidth(56) +frame:SetHeight(42) +frame:SetMovable(true) +frame:EnableMouse(false) +frame:RegisterForDrag("LeftButton", "RightButton") +frame:SetScript("OnDragStart", function(self) + self:StartMoving() +end) +frame:SetScript("OnDragStop", function(self) + self:StopMovingOrSizing() + local point, _, _, x, y = self:GetPoint(1) + DBM.Options.ArrowPoint = point + DBM.Options.ArrowPosX = x + DBM.Options.ArrowPosY = y +end) +local arrow = frame:CreateTexture(nil, "OVERLAY") +arrow:SetTexture("Interface\\AddOns\\DBM-Core\\textures\\arrows\\Arrow.blp") +arrow:SetAllPoints(frame) + +--------------------- +-- Map Utilities -- +--------------------- +local SetMapToCurrentZone -- throttled SetMapToCurrentZone function to prevent lag issues with unsupported WorldMap addons +do + local lastMapUpdate = 0 + function SetMapToCurrentZone(...) + if GetTime() - lastMapUpdate > 1 then + lastMapUpdate = GetTime() + return _G.SetMapToCurrentZone(...) + end + end +end + +local calculateDistance +do + local mapSizes = DBM.MapSizes + function calculateDistance(x1, y1, x2, y2) + local mapName = GetMapInfo() + local floors = mapSizes[mapName] + if not floors then + return + end + local dims = floors[GetCurrentMapDungeonLevel()] + if not dims and levels and GetCurrentMapDungeonLevel() == 0 then -- we are in a known zone but the dungeon level seems to be wrong + SetMapToCurrentZone() -- fixes the dungeon level (if it was wrong for some reason) + dims = levels[GetCurrentMapDungeonLevel()] -- try again + end + if not dims then -- we are in an unknown dungeon :( + return + end + local dX = (x1 - x2) * dims[1] + local dY = (y1 - y2) * dims[2] + return sqrt(dX * dX + dY * dY) + end +end + +-- GetPlayerFacing seems to return values between -pi and pi instead of 0 - 2pi sometimes since 3.3.3 +local GetPlayerFacing = function(...) + local result = GetPlayerFacing(...) + if result < 0 then + result = result + pi2 + end + return result +end + +------------------------ +-- Update the arrow -- +------------------------ +local updateArrow +do + local currentCell + function updateArrow(direction, distance) + local cell = floor(direction / pi2 * 108 + 0.5) % 108 + if cell ~= currentCell then + currentCell = cell + local column = cell % 9 + local row = floor(cell / 9) + local xStart = (column * 56) / 512 + local yStart = (row * 42) / 512 + local xEnd = ((column + 1) * 56) / 512 + local yEnd = ((row + 1) * 42) / 512 + arrow:SetTexCoord(xStart, xEnd, yStart, yEnd) + end + if distance then + if runAwayArrow then + local perc = distance / hideDistance + arrow:SetVertexColor(1 - perc, perc, 0) + if distance >= hideDistance then + frame:Hide() + end + else + local perc = min(distance, 100) / 100 + arrow:SetVertexColor(1, 1 - perc, 0) + if distance <= hideDistance then + frame:Hide() + end + end + else + if runAwayArrow then + arrow:SetVertexColor(1, 0.3, 0) + else + arrow:SetVertexColor(1, 1, 0) + end + end + end +end + +------------------------ +-- OnUpdate Handler -- +------------------------ +do + local rotateState = 0 +-- local skipFrame -- todo: skipping frames makes the arrow laggy, maybe skip frames if frame rate >= 45 + frame:SetScript("OnUpdate", function(self, elapsed) + if WorldMapFrame:IsShown() then -- it doesn't work while the world map frame is shown + arrow:Hide() + return + end +-- skipFrame = not skipFrame +-- if skipFrame then +-- return +-- end + if hideTime and GetTime() > hideTime then + frame:Hide() + end + arrow:Show() + local x, y = GetPlayerMapPosition("player") + if x == 0 and y == 0 then + SetMapToCurrentZone() + x, y = GetPlayerMapPosition("player") + if x == 0 and y == 0 then + self:Hide() -- hide the arrow if you enter a zone without a map + return + end + end + if targetType == "player" then + targetX, targetY = GetPlayerMapPosition(targetPlayer) + if targetX == 0 and targetY == 0 then + self:Hide() -- hide the player if the target doesn't exist. TODO: just hide the texture and add a timeout + end + elseif targetType == "rotate" then + rotateState = rotateState + elapsed + targetX = x + cos(rotateState) + targetY = y + sin(rotateState) + end + if not targetX or not targetY then + return + end + local angle = atan2(x - targetX, targetY - y) + if angle <= 0 then -- -pi < angle < pi but we need/want a value between 0 and 2 pi + if runAwayArrow then + angle = -angle -- 0 < angle < pi + else + angle = pi - angle -- pi < angle < 2pi + end + else + if runAwayArrow then + angle = pi2 - angle -- pi < angle < 2pi + else + angle = pi - angle -- 0 < angle < pi + end + end + updateArrow(angle - GetPlayerFacing(), calculateDistance(x, y, targetX, targetY)) + end) +end + + +---------------------- +-- Public Methods -- +---------------------- +local function show(runAway, x, y, distance, time) + local player + if type(x) == "string" then + player, hideDistance, hideTime = x, y, hideDistance + end + frame:Show() + runAwayArrow = runAway + hideDistance = distance or runAway and 100 or 3 + if time then + hideTime = time + GetTime() + else + hideTime = nil + end + if player then + targetType = "player" + targetPlayer = player + else + targetType = "fixed" + targetX, targetY = x, y + end +end + +function DBM.Arrow:ShowRunTo(...) + return show(false, ...) +end + +function DBM.Arrow:ShowRunAway(...) + return show(true, ...) +end + +function DBM.Arrow:Hide(autoHide) + frame:Hide() +end + +local function endMove() + frame:EnableMouse(false) + DBM.Arrow:Hide() +end + +function DBM.Arrow:Move() + targetType = "rotate" + runAwayArrow = false + hideDistance = 5 + frame:EnableMouse(true) + frame:Show() + DBM.Bars:CreateBar(25, DBM_ARROW_MOVABLE, "Interface\\Icons\\Spell_Holy_BorrowedTime") + DBM:Unschedule(endMove) + DBM:Schedule(25, endMove) +end + +function DBM.Arrow:LoadPosition() + frame:SetPoint(DBM.Options.ArrowPoint, DBM.Options.ArrowPosX, DBM.Options.ArrowPosY) +end diff --git a/DBM-Core/DBM-BossHealth.lua b/DBM-Core/DBM-BossHealth.lua new file mode 100644 index 0000000..fdd8ca7 --- /dev/null +++ b/DBM-Core/DBM-BossHealth.lua @@ -0,0 +1,284 @@ +--------------- +-- Globals -- +--------------- +DBM.BossHealth = {} + + +------------- +-- Locals -- +------------- +local bossHealth = DBM.BossHealth +local bars = {} +local barCache = {} +local updateFrame +local getBarId +local updateBar +local anchor +local header +local dropdownFrame +--local sortingEnabled + +do + local id = 0 + function getBarId() + id = id + 1 + return id + end +end + +------------ +-- Menu -- +------------ +local menu +menu = { + { + text = DBM_CORE_RANGECHECK_LOCK, + checked = false, -- requires DBM.Options which is not available yet + func = function() + menu[1].checked = not menu[1].checked + DBM.Options.HealthFrameLocked = menu[1].checked + end + }, + { + text = DBM_CORE_BOSSHEALTH_HIDE_FRAME, + notCheckable = true, + func = function() bossHealth:Hide() end + } +} + + +----------------------- +-- Script Handlers -- +----------------------- +local function onMouseDown(self, button) + if button == "LeftButton" and not DBM.Options.HealthFrameLocked then + anchor.moving = true + anchor:StartMoving() + end +end + +local function onMouseUp(self, button) + anchor.moving = nil + anchor:StopMovingOrSizing() + local point, _, _, x, y = anchor:GetPoint(1) + DBM.Options.HPFramePoint = point + DBM.Options.HPFrameX = x + DBM.Options.HPFrameY = y + if button == "RightButton" then + EasyMenu(menu, dropdownFrame, "cursor", nil, nil, "MENU") + end +end + +local onHide = onMouseUp + + +----------------- +-- Apply Style -- +----------------- +local function updateBarStyle(bar, id) + bar:ClearAllPoints() + if DBM.Options.HealthFrameGrowUp then + bar:SetPoint("BOTTOM", bars[id - 1] or anchor, "TOP", 0, 0) + else + bar:SetPoint("TOP", bars[id - 1] or anchor, "BOTTOM", 0, 0) + end + local barborder = _G[bar:GetName().."BarBorder"] + local barbar = _G[bar:GetName().."Bar"] + local width = DBM.Options.HealthFrameWidth + if width < 175 then -- these health frames really suck :( + barbar:ClearAllPoints() + barbar:SetPoint("CENTER", barbar:GetParent(), "CENTER", -6, 0) + bar:SetWidth(DBM.Options.HealthFrameWidth) + barborder:SetWidth(DBM.Options.HealthFrameWidth * 0.99) + barbar:SetWidth(DBM.Options.HealthFrameWidth * 0.95) + elseif width >= 225 then + barbar:ClearAllPoints() + barbar:SetPoint("CENTER", barbar:GetParent(), "CENTER", 5, 0) + bar:SetWidth(DBM.Options.HealthFrameWidth) + barborder:SetWidth(DBM.Options.HealthFrameWidth * 0.995) + barbar:SetWidth(DBM.Options.HealthFrameWidth * 0.965) + else + bar:SetWidth(DBM.Options.HealthFrameWidth) + barborder:SetWidth(DBM.Options.HealthFrameWidth * 0.99) + barbar:SetWidth(DBM.Options.HealthFrameWidth * 0.95) + end +end + +----------------------- +-- Create the Frame -- +----------------------- +local function createFrame(self) + anchor = CreateFrame("Frame", nil, UIParent) + anchor:SetWidth(60) + anchor:SetHeight(10) + anchor:SetMovable(1) + anchor:EnableMouse(1) + anchor:SetPoint(DBM.Options.HPFramePoint, UIParent, DBM.Options.HPFramePoint, DBM.Options.HPFrameX, DBM.Options.HPFrameY) + header = anchor:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + header:SetPoint("BOTTOM", anchor, "BOTTOM") + anchor:SetScript("OnUpdate", updateFrame) + anchor:SetScript("OnMouseDown", onMouseDown) + anchor:SetScript("OnMouseUp", onMouseUp) + anchor:SetScript("OnHide", onHide) + dropdownFrame = CreateFrame("Frame", "DBMBossHealthDropdown", anchor, "UIDropDownMenuTemplate") + menu[1].checked = DBM.Options.HealthFrameLocked +end + +local function createBar(self, cId, name) + local bar = table.remove(barCache, #barCache) or CreateFrame("Frame", "DBM_BossHealth_Bar_"..getBarId(), anchor, "DBMBossHealthBarTemplate") + bar:Show() + local bartext = _G[bar:GetName().."BarName"] + local barborder = _G[bar:GetName().."BarBorder"] + local barbar = _G[bar:GetName().."Bar"] + barborder:SetScript("OnMouseDown", onMouseDown) + barborder:SetScript("OnMouseUp", onMouseUp) + barborder:SetScript("OnHide", onHide) + bar.id = cId + bar.hidden = false + bar:ClearAllPoints() + bartext:SetText(name) + updateBar(bar, 100) + return bar +end + + + +------------------ +-- Bar Update -- +------------------ +function updateBar(bar, percent, dontShowDead) + local bartimer = _G[bar:GetName().."BarTimer"] + local barbar = _G[bar:GetName().."Bar"] + bartimer:SetText((percent > 0 or dontShowDead) and math.floor(percent).."%" or DBM_CORE_DEAD) + barbar:SetValue(percent) + barbar:SetStatusBarColor((100 - percent) / 100, percent/100, 0) + bar.value = percent + local bossAlive = false + for i = 1, #bars do + if bars[i].value > 0 then + bossAlive = true + break + end + end + if not bossAlive and #bars > 0 then + bossHealth:Hide() + end +end + +do + local t = 0 + local targetCache = {} + local function getCIDfromGUID(guid) + if not guid then + return -1 + end + local cType = bit.band(guid:sub(0, 5), 0x00F) + return (cType == 3 or cType == 5) and tonumber(guid:sub(9, 12), 16) or -1 + end + +-- local function compareBars(b1, b2) +-- return b1.value > b2.value +-- end + + function updateFrame(self, e) + t = t + e + if t >= 0.5 then + t = 0 +-- if #bars > DBM.Options.HPFrameMaxEntries then +-- sortingEnabled = true +-- end +-- if sortingEnabled then +-- table.sort(bars, compareBars) +-- end + for i, v in ipairs(bars) do +-- if i > DBM.Options.HPFrameMaxEntries then +-- v:Hide() +-- else +-- v:Show() +-- end + if type(v.id) == "number" then + local id = targetCache[v.id] -- ask the cache if we already know where the mob is + if getCIDfromGUID(UnitGUID(id or "")) ~= v.id then -- the cache doesn't know it, update the cache + targetCache[v.id] = nil + -- check focus target + if getCIDfromGUID(UnitGUID("focus")) == v.id then + targetCache[v.id] = "focus" + else + -- check target and raid/party targets + local uId = ((GetNumRaidMembers() == 0) and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + id = (i == 0 and "target") or uId..i.."target" + if getCIDfromGUID(UnitGUID(id or "")) == v.id then + targetCache[v.id] = id + break + end + end + end + end + if getCIDfromGUID(UnitGUID(id or "")) == v.id then -- did we find the mob? if yes: update the health bar + updateBar(v, ((UnitHealth(id)) / (UnitHealthMax(id)) * 100 or 100)) + end + elseif type(v.id) == "function" then -- generic bars + updateBar(v, v.id(), true) + end + end + end + end +end + +----------------------- +-- General Methods -- +----------------------- +function bossHealth:Show(name) + if not anchor then createFrame(bossHealth) end + header:SetText(name) + anchor:Show() + bossHealth:Clear() +end + +function bossHealth:Clear() + if not anchor or not anchor:IsShown() then return end + for i = #bars, 1, -1 do + local bar = bars[i] + bar:Hide() + bar:ClearAllPoints() + barCache[#barCache + 1] = bar + bars[i] = nil + end +-- sortingEnabled = false +end + +function bossHealth:Hide() + if anchor then anchor:Hide() end +end + +function bossHealth:AddBoss(cId, name) + if not anchor or not anchor:IsShown() then return end + table.insert(bars, createBar(self, cId, name)) + updateBarStyle(bars[#bars], #bars) +end + +function bossHealth:RemoveBoss(cId) + if not anchor or not anchor:IsShown() then return end + for i = #bars, 1, -1 do + local bar = bars[i] + if bar.id == cId then + if bars[i + 1] then + local next = bars[i + 1] + next:SetPoint("TOP", bars[i - 1] or anchor, "BOTTOM", 0, 0) + end + bar:Hide() + bar:ClearAllPoints() + barCache[#barCache + 1] = bar + table.remove(bars, i) + end + end +end + +function bossHealth:UpdateSettings() + if not anchor then createFrame(bossHealth) end + anchor:SetPoint(DBM.Options.HPFramePoint, UIParent, DBM.Options.HPFramePoint, DBM.Options.HPFrameX, DBM.Options.HPFrameY) + for i, v in ipairs(bars) do + updateBarStyle(v, i) + end +end diff --git a/DBM-Core/DBM-BossHealth.xml b/DBM-Core/DBM-BossHealth.xml new file mode 100644 index 0000000..700ee94 --- /dev/null +++ b/DBM-Core/DBM-BossHealth.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + self:SetMinMaxValues(0, 100) + + + + + + diff --git a/DBM-Core/DBM-Core.lua b/DBM-Core/DBM-Core.lua new file mode 100644 index 0000000..161a1b2 --- /dev/null +++ b/DBM-Core/DBM-Core.lua @@ -0,0 +1,4050 @@ +-- ********************************************************* +-- ** Deadly Boss Mods - Core ** +-- ** http://www.deadlybossmods.com ** +-- ********************************************************* +-- +-- This addon is written and copyrighted by: +-- * Paul Emmerich (Tandanu @ EU-Aegwynn) (DBM-Core) +-- * Martin Verges (Nitram @ EU-Azshara) (DBM-GUI) +-- +-- The localizations are written by: +-- * enGB/enUS: Tandanu http://www.deadlybossmods.com +-- * deDE: Tandanu http://www.deadlybossmods.com +-- * zhCN: Diablohu http://wow.gamespot.com.cn +-- * ruRU: BootWin bootwin@gmail.com +-- * ruRU: Vampik admin@vampik.ru +-- * zhTW: Hman herman_c1@hotmail.com +-- * zhTW: Azael/kc10577 kc10577@hotmail.com +-- * koKR: BlueNyx bluenyx@gmail.com +-- * esES: Interplay/1nn7erpLaY http://www.1nn7erpLaY.com +-- +-- Special thanks to: +-- * Arta (DBM-Party) +-- * Omegal @ US-Whisperwind (continuing mod support for 3.2+) +-- * Tennberg (a lot of fixes in the enGB/enUS localization) +-- +-- +-- The code of this addon is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. (see license.txt) +-- All included textures and sounds are copyrighted by their respective owners, license information for these media files can be found in the modules that make use of them. +-- +-- +-- You are free: +-- * to Share - to copy, distribute, display, and perform the work +-- * to Remix - to make derivative works +-- Under the following conditions: +-- * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). (A link to http://www.deadlybossmods.com is sufficient) +-- * Noncommercial. You may not use this work for commercial purposes. +-- * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. +-- + + +------------------------------- +-- Globals/Default Options -- +------------------------------- +DBM = { + Revision = ("$Revision: 5015 $"):sub(12, -3), + Version = "5.15", + DisplayVersion = "5.15", -- the string that is shown as version + ReleaseRevision = 5013 -- the revision of the latest stable version that is available (for /dbm ver2) +} + +DBM_SavedOptions = {} + +DBM.DefaultOptions = { + WarningColors = { + {r = 0.41, g = 0.80, b = 0.94}, -- Color 1 - #69CCF0 - Turqoise + {r = 0.95, g = 0.95, b = 0.00}, -- Color 2 - #F2F200 - Yellow + {r = 1.00, g = 0.50, b = 0.00}, -- Color 3 - #FF8000 - Orange + {r = 1.00, g = 0.10, b = 0.10}, -- Color 4 - #FF1A1A - Red + }, + RaidWarningSound = "Sound\\Doodad\\BellTollNightElf.wav", + SpecialWarningSound = "Sound\\Spells\\PVPFlagTaken.wav", + RaidWarningPosition = { + Point = "TOP", + X = 0, + Y = -185, + }, + StatusEnabled = true, + AutoRespond = true, + Enabled = true, + ShowWarningsInChat = true, + ShowFakedRaidWarnings = false, + WarningIconLeft = true, + WarningIconRight = true, + HideBossEmoteFrame = false, + SpamBlockRaidWarning = true, + SpamBlockBossWhispers = false, + ShowMinimapButton = true, + FixCLEUOnCombatStart = false, + BlockVersionUpdatePopup = true, + ShowSpecialWarnings = true, + AlwaysShowHealthFrame = false, + ShowBigBrotherOnCombatStart = false, + RangeFramePoint = "CENTER", + RangeFrameX = 50, + RangeFrameY = -50, + RangeFrameSound1 = "none", + RangeFrameSound2 = "none", + RangeFrameLocked = false, + HPFramePoint = "CENTER", + HPFrameX = -50, + HPFrameY = 50, + HPFrameMaxEntries = 5, + SpecialWarningPoint = "CENTER", + SpecialWarningX = 0, + SpecialWarningY = 75, + SpecialWarningFont = STANDARD_TEXT_FONT, + SpecialWarningFontSize = 50, + SpecialWarningFontColor = {0.0, 0.0, 1.0}, + HealthFrameGrowUp = false, + HealthFrameLocked = false, + HealthFrameWidth = 200, + ArrowPosX = 0, + ArrowPosY = -150, + ArrowPoint = "TOP", + -- global boss mod settings (overrides mod-specific settings for some options) + DontShowBossAnnounces = false, + DontSendBossAnnounces = false, + DontSendBossWhispers = false, + DontSetIcons = false, + LatencyThreshold = 250, + BigBrotherAnnounceToRaid = false, +-- HelpMessageShown = false, +} + +DBM.BarGroups = {}; +function DBM:CreateBarGroup(id) + local barGroup = DBT:New(); + DBM.BarGroups[id] = barGroup; + return barGroup; +end +DBM.Bars = DBM:CreateBarGroup("DBM"); + +function DBM:GetBarGroup(id) + return DBM.BarGroups[id] or DBM.Bars; +end + +DBM.Mods = {} + +------------------------ +-- Global Identifiers -- +------------------------ +DBM_DISABLE_ZONE_DETECTION = newproxy(false) +DBM_OPTION_SPACER = newproxy(false) + +-------------- +-- Locals -- +-------------- +local inCombat = {} +local combatInfo = {} +local updateFunctions = {} +local raid = {} +local modSyncSpam = {} +local autoRespondSpam = {} +local chatPrefix = " " +local chatPrefixShort = " " +local ver = ("%s (r%d)"):format(DBM.DisplayVersion, DBM.Revision) +local mainFrame = CreateFrame("Frame") +local showedUpdateReminder = false +local combatInitialized = false +local schedule +local unschedule +local loadOptions +local loadModOptions +local checkWipe +local fireEvent +local wowVersion = select(4, GetBuildInfo()) + +local enableIcons = true -- set to false when a raid leader or a promoted player has a newer version of DBM + +local bannedMods = { -- a list of "banned" (meaning they are replaced by another mod like DBM-Battlegrounds (replaced by DBM-PvP)) boss mods, these mods will not be loaded by DBM (and they wont show up in the GUI) + "DBM-Battlegrounds", --replaced by DBM-PvP +} + +-------------------------------------------------------- +-- Cache frequently used global variables in locals -- +-------------------------------------------------------- +local DBM = DBM +-- these global functions are accessed all the time by the event handler +-- so caching them is worth the effort +local ipairs, pairs, next = ipairs, pairs, next +local tinsert, tremove, twipe = table.insert, table.remove, table.wipe +local type = type +local select = select +local floor = math.floor + +-- for Phanx' Class Colors +local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS + +--------------------------------- +-- General (local) functions -- +--------------------------------- +-- checks if a given value is in an array +-- returns true if it finds the value, false otherwise +local function checkEntry(t, val) + for i, v in ipairs(t) do + if v == val then + return true + end + end + return false +end + +-- removes all occurrences of a value in an array +-- returns true if at least one occurrence was remove, false otherwise +local function removeEntry(t, val) + local existed = false + for i = #t, 1, -1 do + if t[i] == val then + table.remove(t, i) + existed = true + end + end + return existed +end + +-- automatically sends an addon message to the appropriate channel (BATTLEGROUND, RAID or PARTY) +local function sendSync(prefix, msg) + local zoneType = select(2, IsInInstance()) + if zoneType == "pvp" or zoneType == "arena" then + SendAddonMessage(prefix, msg, "BATTLEGROUND") + elseif GetRealNumRaidMembers() > 0 then + SendAddonMessage(prefix, msg, "RAID") + elseif GetRealNumPartyMembers() > 0 then + SendAddonMessage(prefix, msg, "PARTY") + end +end + +-- +local function strFromTime(time) + if type(time) ~= "number" then time = 0 end + time = math.floor(time) + if time < 60 then + return DBM_CORE_TIMER_FORMAT_SECS:format(time) + elseif time % 60 == 0 then + return DBM_CORE_TIMER_FORMAT_MINS:format(time/60) + else + return DBM_CORE_TIMER_FORMAT:format(time/60, time % 60) + end +end + +local pformat +do + -- fail-safe format, replaces missing arguments with unknown + -- note: doesn't handle cases like %%%s correctly at the moment (should become %unknown, but becomes %%s) + -- also, the end of the format directive is not detected in all cases, but handles everything that occurs in our boss mods ;) + --> not suitable for general-purpose use, just for our warnings and timers (where an argument like a spell-target might be nil due to missing target information from unreliable detection methods) + + local function replace(cap1, cap2) + return cap1 == "%" and DBM_CORE_UNKNOWN + end + + function pformat(fstr, ...) + local ok, str = pcall(format, fstr, ...) + return ok and str or fstr:gsub("(%%+)([^%%%s<]+)", replace):gsub("%%%%", "%%") + end +end + +-- sends a whisper to a player by his or her character name or BNet presence id +-- returns true if the message was sent, nil otherwise +local function sendWhisper(target, msg) + if type(target) == "number" then + if not BNIsSelf(target) then -- never send BNet whispers to ourselves + BNSendWhisper(target, msg) + return true + end + elseif type(target) == "string" then + -- whispering to ourselves here is okay and somewhat useful for whisper-warnings + SendChatMessage(msg, "WHISPER", nil, target) + return true + end +end +local BNSendWhisper = sendWhisper + + +-------------- +-- Events -- +-------------- +do + local registeredEvents = {} + local argsMT = {__index = {}} + local args = setmetatable({}, argsMT) + + function argsMT.__index:IsSpellID(a1, a2, a3, a4) + local v = self.spellId + return v == a1 or v == a2 or v == a3 or v == a4 + end + + function argsMT.__index:IsPlayer() + return bit.band(args.destFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) ~= 0 and bit.band(args.destFlags, COMBATLOG_OBJECT_TYPE_PLAYER) ~= 0 + end + + function argsMT.__index:IsPlayerSource() + return bit.band(args.sourceFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) ~= 0 and bit.band(args.sourceFlags, COMBATLOG_OBJECT_TYPE_PLAYER) ~= 0 + end + + function argsMT.__index:IsPet() + return bit.band(args.destFlags, COMBATLOG_OBJECT_TYPE_PET) ~= 0 + end + + function argsMT.__index:IsPetSource() + return bit.band(args.sourceFlags, COMBATLOG_OBJECT_TYPE_PET) ~= 0 + end + + function argsMT.__index:IsSrcTypePlayer() + return bit.band(args.sourceFlags, COMBATLOG_OBJECT_TYPE_PLAYER) ~= 0 + end + + function argsMT.__index:IsDestTypePlayer() + return bit.band(args.destFlags, COMBATLOG_OBJECT_TYPE_PLAYER) ~= 0 + end + + function argsMT.__index:IsSrcTypeHostile() + return bit.band(args.sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) ~= 0 + end + + function argsMT.__index:IsDestTypeHostile() + return bit.band(args.destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) ~= 0 + end + + function argsMT.__index:GetSrcCreatureID() + return tonumber(self.sourceGUID:sub(9, 12), 16) or 0 + end + + function argsMT.__index:GetDestCreatureID() + return tonumber(self.destGUID:sub(9, 12), 16) or 0 + end + + local function handleEvent(self, event, ...) + if not registeredEvents[event] or DBM.Options and not DBM.Options.Enabled then return end + for i, v in ipairs(registeredEvents[event]) do + if type(v[event]) == "function" and (not v.zones or checkEntry(v.zones, GetRealZoneText()) or checkEntry(v.zones, GetCurrentMapAreaID())) and (not v.Options or v.Options.Enabled) then + v[event](v, ...) + end + end + end + + function DBM:RegisterEvents(...) + for i = 1, select("#", ...) do + local ev = select(i, ...) + registeredEvents[ev] = registeredEvents[ev] or {} + tinsert(registeredEvents[ev], self) + mainFrame:RegisterEvent(ev) + end + end + + function DBM:UnregisterAllEvents() + for i, v in pairs(registeredEvents) do + for i = #v, 1 do + if v[i] == self then + tremove(v, i) + end + end + if #v == 0 then + registeredEvents[i] = nil + mainFrame:UnregisterEvent(i) + end + end + end + + DBM:RegisterEvents("ADDON_LOADED") + + function DBM:FilterRaidBossEmote(msg, ...) + return handleEvent(nil, "CHAT_MSG_RAID_BOSS_EMOTE_FILTERED", msg:gsub("\124c%x+(.-)\124r", "%1"), ...) + end + + function DBM:COMBAT_LOG_EVENT_UNFILTERED(timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...) + if not registeredEvents[event] then return end + twipe(args) + args.timestamp = timestamp + args.event = event + args.sourceGUID = sourceGUID + args.sourceName = sourceName + args.sourceFlags = sourceFlags + args.destGUID = destGUID + args.destName = destName + args.destFlags = destFlags + -- taken from Blizzard_CombatLog.lua + if event == "SWING_DAMAGE" then + args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(1, ...) + elseif event == "SWING_MISSED" then + args.spellName = ACTION_SWING + args.missType = select(1, ...) + elseif event:sub(1, 5) == "RANGE" then + args.spellId, args.spellName, args.spellSchool = select(1, ...) + if event == "RANGE_DAMAGE" then + args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(4, ...) + elseif event == "RANGE_MISSED" then + args.missType = select(4, ...) + end + elseif event:sub(1, 5) == "SPELL" then + args.spellId, args.spellName, args.spellSchool = select(1, ...) + if event == "SPELL_DAMAGE" then + args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(4, ...) + elseif event == "SPELL_MISSED" then + args.missType, args.amountMissed = select(4, ...) + elseif event == "SPELL_HEAL" then + args.amount, args.overheal, args.absorbed, args.critical = select(4, ...) + args.school = args.spellSchool + elseif event == "SPELL_ENERGIZE" then + args.valueType = 2 + args.amount, args.powerType = select(4, ...) + elseif event:sub(1, 14) == "SPELL_PERIODIC" then + if event == "SPELL_PERIODIC_MISSED" then + args.missType = select(4, ...) + elseif event == "SPELL_PERIODIC_DAMAGE" then + args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(4, ...) + elseif event == "SPELL_PERIODIC_HEAL" then + args.amount, args.overheal, args.absorbed, args.critical = select(4, ...) + args.school = args.spellSchool + elseif event == "SPELL_PERIODIC_DRAIN" then + args.amount, args.powerType, args.extraAmount = select(4, ...) + args.valueType = 2 + elseif event == "SPELL_PERIODIC_LEECH" then + args.amount, args.powerType, args.extraAmount = select(4, ...) + args.valueType = 2 + elseif event == "SPELL_PERIODIC_ENERGIZE" then + args.amount, args.powerType = select(4, ...) + args.valueType = 2 + end + elseif event == "SPELL_DRAIN" then + args.amount, args.powerType, args.extraAmount = select(4, ...) + args.valueType = 2 + elseif event == "SPELL_LEECH" then + args.amount, args.powerType, args.extraAmount = select(4, ...) + args.valueType = 2 + elseif event == "SPELL_INTERRUPT" then + args.extraSpellId, args.extraSpellName, args.extraSpellSchool = select(4, ...) + elseif event == "SPELL_EXTRA_ATTACKS" then + args.amount = select(4, ...) + elseif event == "SPELL_DISPEL_FAILED" then + args.extraSpellId, args.extraSpellName, args.extraSpellSchool = select(4, ...) + elseif event == "SPELL_AURA_DISPELLED" then + args.extraSpellId, args.extraSpellName, args.extraSpellSchool = select(4, ...) + args.auraType = select(7, ...) + elseif event == "SPELL_AURA_STOLEN" then + args.extraSpellId, args.extraSpellName, args.extraSpellSchool = select(4, ...) + args.auraType = select(7, ...) + elseif event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REMOVED" then + args.auraType = select(4, ...) + args.sourceName = args.destName + args.sourceGUID = args.destGUID + args.sourceFlags = args.destFlags + elseif event == "SPELL_AURA_APPLIED_DOSE" or event == "SPELL_AURA_REMOVED_DOSE" then + args.auraType, args.amount = select(4, ...) + args.sourceName = args.destName + args.sourceGUID = args.destGUID + args.sourceFlags = args.destFlags + elseif event == "SPELL_CAST_FAILED" then + args.missType = select(4, ...) + end + elseif event == "DAMAGE_SHIELD" then + args.spellId, args.spellName, args.spellSchool = select(1, ...) + args.amount, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(4, ...) + elseif event == "DAMAGE_SHIELD_MISSED" then + args.spellId, args.spellName, args.spellSchool = select(1, ...) + args.missType = select(4, ...) + elseif event == "ENCHANT_APPLIED" then + args.spellName = select(1,...) + args.itemId, args.itemName = select(2,...) + elseif event == "ENCHANT_REMOVED" then + args.spellName = select(1,...) + args.itemId, args.itemName = select(2,...) + elseif event == "UNIT_DIED" or event == "UNIT_DESTROYED" then + args.sourceName = args.destName + args.sourceGUID = args.destGUID + args.sourceFlags = args.destFlags + elseif event == "ENVIRONMENTAL_DAMAGE" then + args.environmentalType = select(1,...) + args.amount, args.overkill, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(2, ...) + args.spellName = _G["ACTION_"..event.."_"..args.environmentalType] + args.spellSchool = args.school + elseif event == "DAMAGE_SPLIT" then + args.spellId, args.spellName, args.spellSchool = select(1, ...) + args.amount, args.school, args.resisted, args.blocked, args.absorbed, args.critical, args.glancing, args.crushing = select(4, ...) + end + return handleEvent(nil, event, args) + end + mainFrame:SetScript("OnEvent", handleEvent) +end + + +----------------- +-- Callbacks -- +----------------- +do + local callbacks = {} + + function fireEvent(event, ...) + if not callbacks[event] then return end + for i, v in ipairs(callbacks[event]) do + local ok, err = pcall(v, event, ...) + if not ok then DBM:AddMsg(("Error while executing callback %s for event %s: %s"):format(tostring(v), tostring(event), err)) end + end + end + + function DBM:IsCallbackRegistered(event, f) + if not event or type(f) ~= "function" then + error("Usage: IsCallbackRegistered(event, callbackFunc)", 2) + end + if not callbacks[event] then return end + for i = 1, #callbacks[event] do + if callbacks[event][i] == f then return true end + end + return false + end + + function DBM:RegisterCallback(event, f) + if not event or type(f) ~= "function" then + error("Usage: DBM:RegisterCallback(event, callbackFunc)", 2) + end + callbacks[event] = callbacks[event] or {} + table.insert(callbacks[event], f) + return #callbacks[event] + end +end + + +-------------------------- +-- OnUpdate/Scheduler -- +-------------------------- +do + -- stack that stores a few tables (up to 8) which will be recycled + local popCachedTable, pushCachedTable + local numChachedTables = 0 + do + local tableCache = nil + + -- gets a table from the stack, it will then be recycled. + function popCachedTable() + local t = tableCache + if t then + tableCache = t.next + numChachedTables = numChachedTables - 1 + end + return t + end + + -- tries to push a table on the stack + -- only tables with <= 4 array entries are accepted as cached tables are only used for tasks with few arguments for performance reasons + -- also, the maximum number of cached tables is limited to 8 as DBM rarely has more than eight scheduled tasks with less than 4 arguments at the same time + -- this is just to re-use all the tables of the small tasks that are scheduled all the time (like the wipe detection) + -- note that the cache does not use weak references anywhere for performance reasons, so a cached table will never be deleted by the garbage collector + function pushCachedTable(t) + if numChachedTables < 8 and #t <= 4 then + twipe(t) + t.next = tableCache + tableCache = t + numChachedTables = numChachedTables + 1 + end + end + end + + -- priority queue (min-heap) that stores all scheduled tasks. + -- insert: O(log n) + -- deleteMin: O(log n) + -- getMin: O(1) + -- removeAllMatching: O(n) + local insert, removeAllMatching, getMin, deleteMin + do + local heap = {} + local firstFree = 1 + + -- gets the next task + function getMin() + return heap[1] + end + + -- restores the heap invariant by moving an item up + local function siftUp(n) + local parent = floor(n / 2) + while n > 1 and heap[parent].time > heap[n].time do -- move the element up until the heap invariant is restored, meaning the element is at the top or the element's parent is <= the element + heap[n], heap[parent] = heap[parent], heap[n] -- swap the element with its parent + n = parent + parent = floor(n / 2) + end + end + + -- restores the heap invariant by moving an item down + local function siftDown(n) + local m -- position of the smaller child + while 2 * n < firstFree do -- #children >= 1 + -- swap the element with its smaller child + if 2 * n + 1 == firstFree then -- n does not have a right child --> it only has a left child as #children >= 1 + m = 2 * n -- left child + elseif heap[2 * n].time < heap[2 * n + 1].time then -- #children = 2 and left child < right child + m = 2 * n -- left child + else -- #children = 2 and right child is smaller than the left one + m = 2 * n + 1 -- right + end + if heap[n].time <= heap[m].time then -- n is <= its smallest child --> heap invariant restored + return + end + heap[n], heap[m] = heap[m], heap[n] + n = m + end + end + + -- inserts a new element into the heap + function insert(ele) + heap[firstFree] = ele + siftUp(firstFree) + firstFree = firstFree + 1 + end + + -- deletes the min element + function deleteMin() + local min = heap[1] + firstFree = firstFree - 1 + heap[1] = heap[firstFree] + heap[firstFree] = nil + siftDown(1) + return min + end + + -- removes multiple scheduled tasks from the heap + -- note that this function is comparatively slow by design as it has to check all tasks and allows partial matches + function removeAllMatching(f, mod, ...) + -- remove all elements that match the signature, this destroyes the heap and leaves a normal array + local v, match + for i = #heap, 1, -1 do -- iterate backwards over the array to allow usage of table.remove + v = heap[i] + if (not f or v.func == f) and (not mod or v.mod == mod) then + match = true + for i = 1, select("#", ...) do + if select(i, ...) ~= v[i] then + match = false + break + end + end + if match then + table.remove(heap, i) + firstFree = firstFree - 1 + end + end + end + -- rebuild the heap from the array in O(n) + for i = floor((firstFree - 1) / 2), 1, -1 do + siftDown(i) + end + end + end + + mainFrame:SetScript("OnUpdate", function(self, elapsed) + local time = GetTime() + + -- execute scheduled tasks + local nextTask = getMin() + while nextTask and nextTask.time <= time do + deleteMin() + nextTask.func(unpack(nextTask)) + pushCachedTable(nextTask) + nextTask = getMin() + end + + -- execute OnUpdate handlers of all modules + for i, v in pairs(updateFunctions) do + if i.Options.Enabled and (not i.zones or checkEntry(i.zones, GetRealZoneText()) or checkEntry(i.zones, GetCurrentMapAreaID())) then + i.elapsed = (i.elapsed or 0) + elapsed + if i.elapsed >= (i.updateInterval or 0) then + v(i, i.elapsed) + i.elapsed = 0 + end + end + end + + -- clean up sync spam timers and auto respond spam blockers + -- TODO: optimize this; using next(t, k) all the time on nearly empty hash tables is not a good idea...doesn't really matter here as modSyncSpam only very rarely contains more than 4 entries... + local k, v = next(modSyncSpam, nil) + if v and (time - v > 2.5) then + modSyncSpam[k] = nil + end + end) + + function schedule(t, f, mod, ...) + local v + if numChachedTables > 0 and select("#", ...) <= 4 then -- a cached table is available and all arguments fit into an array with four slots + v = popCachedTable() + v.time = GetTime() + t + v.func = f + v.mod = mod + for i = 1, select("#", ...) do + v[i] = select(i, ...) + end + else -- create a new table + v = {time = GetTime() + t, func = f, mod = mod, ...} + end + insert(v) + end + + function scheduleCountdown(time, numAnnounces, func, mod, self, ...) + time = time or 5 + numAnnounces = numAnnounces or 3 + for i = 1, numAnnounces do + schedule(time - i, func, mod, self, i, ...) + end + end + + function unschedule(f, mod, ...) + return removeAllMatching(f, mod, ...) + end +end + +function DBM:Schedule(t, f, ...) + return schedule(t, f, nil, ...) +end + +function DBM:Unschedule(f, ...) + return unschedule(f, nil, ...) +end + +function DBM:ForceUpdate() + mainFrame:GetScript("OnUpdate")(mainFrame, 0) +end + +---------------------- +-- Slash Commands -- +---------------------- +SLASH_DEADLYBOSSMODS1 = "/dbm" +SLASH_PULL1 = "/pull" +SlashCmdList["DEADLYBOSSMODS"] = function(msg) + local cmd = msg:lower() + if cmd == "ver" or cmd == "version" then + DBM:ShowVersions(false) + elseif cmd == "ver2" or cmd == "version2" then + DBM:ShowVersions(true) + elseif cmd == "unlock" or cmd == "move" then + for id,barGroup in pairs(DBM.BarGroups) do + barGroup:ShowMovableBar() + end + elseif cmd == "help" then + for i, v in ipairs(DBM_CORE_SLASHCMD_HELP) do DBM:AddMsg(v) end + elseif cmd:sub(1, 5) == "timer" then + local time, text = msg:match("^%w+ ([%d:]+) (.+)$") + if not (time and text) then + DBM:AddMsg(DBM_PIZZA_ERROR_USAGE) + return + end + local min, sec = string.split(":", time) + min = tonumber(min or "") or 0 + sec = tonumber(sec or "") + if min and not sec then + sec = min + min = 0 + end + time = min * 60 + sec + DBM:CreatePizzaTimer(time, text) + elseif cmd:sub(1, 15) == "broadcast timer" then + local time, text = msg:match("^%w+ %w+ ([%d:]+) (.+)$") + if DBM:GetRaidRank() == 0 then + DBM:AddMsg(DBM_ERROR_NO_PERMISSION) + end + if not (time and text) then + DBM:AddMsg(DBM_PIZZA_ERROR_USAGE) + return + end + local min, sec = string.split(":", time) + min = tonumber(min or "") or 0 + sec = tonumber(sec or "") + if min and not sec then + sec = min + min = 0 + end + time = min * 60 + sec + DBM:CreatePizzaTimer(time, text, true) + elseif cmd:sub(0,5) == "break" then + if DBM:GetRaidRank() == 0 then + DBM:AddMsg(DBM_ERROR_NO_PERMISSION) + return + end + local timer = tonumber(cmd:sub(6)) or 5 + local timer = timer * 60 + local channel = ((GetNumRaidMembers() == 0) and "PARTY") or "RAID_WARNING" + DBM:CreatePizzaTimer(timer, DBM_CORE_TIMER_BREAK, true) + DBM:Unschedule(SendChatMessage) + SendChatMessage(DBM_CORE_BREAK_START:format(timer/60), channel) + if timer/60 > 5 then DBM:Schedule(timer - 5*60, SendChatMessage, DBM_CORE_BREAK_MIN:format(5), channel) end + if timer/60 > 2 then DBM:Schedule(timer - 2*60, SendChatMessage, DBM_CORE_BREAK_MIN:format(2), channel) end + if timer/60 > 1 then DBM:Schedule(timer - 1*60, SendChatMessage, DBM_CORE_BREAK_MIN:format(1), channel) end + if timer > 30 then DBM:Schedule(timer - 30, SendChatMessage, DBM_CORE_BREAK_SEC:format(30), channel) end + DBM:Schedule(timer, SendChatMessage, DBM_CORE_ANNOUNCE_BREAK_OVER, channel) + elseif cmd:sub(1, 4) == "pull" then + if DBM:GetRaidRank() == 0 then + return DBM:AddMsg(DBM_ERROR_NO_PERMISSION) + end + DBM:Unschedule(SendChatMessage) + fireEvent("DBM_TimerStop", "DBMPizzaTimer") + if DBM:GetRaidRank() >= 1 then + sendSync("DBMv4-Pizza-Cancel") + end + local timer = tonumber(cmd:sub(5)) or 10 + local pullMessage = string.lower(cmd:sub(6) or "") + local channel = ((GetNumRaidMembers() == 0) and "PARTY") or "RAID_WARNING" + DBM:CreatePizzaTimer(timer, DBM_CORE_TIMER_PULL, true) + if timer > 1 then SendChatMessage(DBM_CORE_ANNOUNCE_PULL:format(timer), channel) end + if timer > 10 then DBM:Schedule(timer - 10, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(10), channel) end + if timer > 7 then DBM:Schedule(timer - 7, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(7), channel) end + if timer > 5 then DBM:Schedule(timer - 5, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(5), channel) end + if timer > 3 then DBM:Schedule(timer - 3, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(3), channel) end + if timer > 2 then DBM:Schedule(timer - 2, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(2), channel) end + if timer > 1 then DBM:Schedule(timer - 1, SendChatMessage, DBM_CORE_ANNOUNCE_PULL:format(1), channel) end + if timer > 1 then DBM:Schedule(timer, SendChatMessage, DBM_CORE_ANNOUNCE_PULL_NOW, channel) end + if timer < 1 or pullMessage == "cancel" then + DBM:Unschedule(SendChatMessage) + fireEvent("DBM_TimerStop", "DBMPizzaTimer") + if DBM:GetRaidRank() >= 1 then + sendSync("DBMv4-Pizza-Cancel") + end + SendChatMessage(DBM_CORE_ANNOUNCE_PULL_CANCEL, channel) + end + if timer == 0 then end + elseif cmd:sub(1, 5) == "arrow" then + if not DBM:IsInRaid() then + DBM:AddMsg(DBM_ARROW_NO_RAIDGROUP) + return false + end + local x, y = string.split(" ", cmd:sub(6):trim()) + xNum, yNum = tonumber(x or ""), tonumber(y or "") + local success + if xNum and yNum then + DBM.Arrow:ShowRunTo(xNum / 100, yNum / 100, 0) + success = true + elseif type(x) == "string" and x:trim() ~= "" then + local subCmd = x:trim() + if subCmd:upper() == "HIDE" then + DBM.Arrow:Hide() + success = true + elseif subCmd:upper() == "MOVE" then + DBM.Arrow:Move() + success = true + elseif subCmd:upper() == "TARGET" then + DBM.Arrow:ShowRunTo("target") + success = true + elseif subCmd:upper() == "FOCUS" then + DBM.Arrow:ShowRunTo("focus") + success = true + elseif DBM:GetRaidUnitId(DBM:Capitalize(subCmd)) ~= "none" then + DBM.Arrow:ShowRunTo(DBM:Capitalize(subCmd)) + success = true + end + end + if not success then + for i, v in ipairs(DBM_ARROW_ERROR_USAGE) do + DBM:AddMsg(v) + end + end + else + DBM:LoadGUI() + end +end +SlashCmdList["PULL"] = function(msg) SlashCmdList["DEADLYBOSSMODS"]("pull "..msg) end +SLASH_DBMRANGE1 = "/range" +SLASH_DBMRANGE2 = "/distance" +SlashCmdList["DBMRANGE"] = function(msg) + if DBM.RangeCheck:IsShown() then + DBM.RangeCheck:Hide() + else + local r = tonumber(msg) + if r and (r == 10 or r == 11 or r == 15 or r == 28 or r == 12 or r == 6 or r == 8 or r == 20) then + DBM.RangeCheck:Show(r) + else + DBM.RangeCheck:Show(10) + end + end +end + +do + local sortMe = {} + local function sort(v1, v2) + return (v1.revision or 0) > (v2.revision or 0) + end + function DBM:ShowVersions(notify) + for i, v in pairs(raid) do + table.insert(sortMe, v) + end + table.sort(sortMe, sort) + self:AddMsg(DBM_CORE_VERSIONCHECK_HEADER) + for i, v in ipairs(sortMe) do + if v.displayVersion then + self:AddMsg(DBM_CORE_VERSIONCHECK_ENTRY:format(v.name, v.displayVersion, v.revision)) + if notify and v.displayVersion ~= DBM.Version and v.revision < DBM.ReleaseRevision then + SendChatMessage(chatPrefixShort..DBM_CORE_YOUR_VERSION_OUTDATED, "WHISPER", nil, v.name) + end + else + self:AddMsg(DBM_CORE_VERSIONCHECK_ENTRY_NO_DBM:format(v.name)) + end + end + for i = #sortMe, 1, -1 do + if not sortMe[i].revision then + table.remove(sortMe, i) + end + end + self:AddMsg(DBM_CORE_VERSIONCHECK_FOOTER:format(#sortMe)) + for i = #sortMe, 1, -1 do + sortMe[i] = nil + end + end + --[[ hmm don't think that this is realy good, so disabled for the moment + function DBM:ElectMaster() + -- FIXME: Add Zonecheck for raidmates + local elect_player = nil + local elect_revision = tonumber(DBM.Revision) + local electd_raidlead = false + + -- first of all, we only import the ranked mates + for i, v in pairs(raid) do + if v.rank >= 1 then + table.insert(sortMe, v) + end + end + table.sort(sortMe, sort) + if not #sortMe then return nil end -- no raid, no election + + local p = sortMe[1] + if p.revision >= tonumber(DBM.Revision) then -- first we check the latest revision + DBM:AddMsg("Newest Version seems to be Revision of "..p.name.." r"..p.revision.." - local revision = r"..DBM.Revision) + elect_revision = tonumber(p.revision) + end + for i, v in ipairs(sortMe) do -- now we kick all assists with a revision lower than the hightest + if tonumber(v.revision) < elect_revision then + table.remove(sortMe, i) + end + end + for i, v in ipairs(sortMe) do -- we prefere to elect the Raidleader so we try this + if v.rank >= 2 then + DBM:AddMsg("Revision of "..v.name.." is "..v.revision.." and thats the RaidLeader") + elect_player = v.name + elect_revision = tonumber(v.revision) + elect_raidlead = true + end + end + if not elect_raidlead then + table.sort(sortMe, function(v1, v2) return v1.name > v2.name end) -- order by Name + if sortMe[#sortMe] then + p = sortMe[#sortMe] + DBM:AddMsg("Elected "..p.name.." is assist and best name") + elect_player = p.name + elect_revision = tonumber(p.revision) + end + end + + table.wipe(sortMe) + return elect_player, elect_revision, elect_raidlead + end + --]] +end + +------------------- +-- Pizza Timer -- +------------------- +do + local ignore = {} + function DBM:CreatePizzaTimer(time, text, broadcast, sender) + if sender and ignore[sender] then return end + text = text:sub(1, 16) + text = text:gsub("%%t", UnitName("target") or "") + if text == DBM_CORE_TIMER_PULL then + self.Bars:CreateBar(time, text, "Interface\\Icons\\Ability_Warrior_Charge") + fireEvent("DBM_TimerStart", "DBMPizzaTimer", text, time, "Interface\\Icons\\Ability_Warrior_Charge", 0) + else + self.Bars:CreateBar(time, text) + fireEvent("DBM_TimerStart", "DBMPizzaTimer", text, time, "Interface\\Icons\\_DeathCoilV2_Red", 0) + end + if broadcast and self:GetRaidRank() >= 1 then + sendSync("DBMv4-Pizza", ("%s\t%s"):format(time, text)) + end + if sender then DBM:ShowPizzaInfo(text, sender) end + end + + function DBM:AddToPizzaIgnore(name) + ignore[name] = true + end +end + +function DBM:ShowPizzaInfo(id, sender) + self:AddMsg(DBM_PIZZA_SYNC_INFO:format(sender, id)) +end + + + +------------------ +-- Hyperlinks -- +------------------ +do + local ignore, cancel + StaticPopupDialogs["DBM_CONFIRM_IGNORE"] = { + text = DBM_PIZZA_CONFIRM_IGNORE, + button1 = YES, + button2 = NO, + OnAccept = function(self) + DBM:AddToPizzaIgnore(ignore) + DBM.Bars:CancelBar(cancel) + end, + timeout = 0, + hideOnEscape = 1, + } + + DEFAULT_CHAT_FRAME:HookScript("OnHyperlinkClick", function(self, link, string, button, ...) + local linkType, arg1, arg2, arg3 = strsplit(":", link) + if linkType == "DBM" and arg1 == "cancel" then + DBM.Bars:CancelBar(link:match("DBM:cancel:(.+):nil$")) + elseif linkType == "DBM" and arg1 == "ignore" then + cancel = link:match("DBM:ignore:(.+):[^%s:]+$") + ignore = link:match(":([^:]+)$") + StaticPopup_Show("DBM_CONFIRM_IGNORE", ignore) + elseif linkType == "DBM" and arg1 == "update" then + DBM:ShowUpdateReminder(arg2, arg3) -- displayVersion, revision + end + end) +end + +do + local old = ItemRefTooltip.SetHyperlink -- we have to hook this function since the default ChatFrame code assumes that all links except for player and channel links are valid arguments for this function + function ItemRefTooltip:SetHyperlink(link, ...) + if link:match("^DBM") then return end + return old(self, link, ...) + end +end + + +----------------- +-- GUI Stuff -- +----------------- +do + local callOnLoad = {} + function DBM:LoadGUI() + if not IsAddOnLoaded("DBM-GUI") then + local _, _, _, enabled = GetAddOnInfo("DBM-GUI") + if not enabled then + EnableAddOn("DBM-GUI") + end + local loaded, reason = LoadAddOn("DBM-GUI") + if not loaded then + if reason then + self:AddMsg(DBM_CORE_LOAD_GUI_ERROR:format(tostring(_G["ADDON_"..reason or ""]))) + else + self:AddMsg(DBM_CORE_LOAD_GUI_ERROR:format(DBM_CORE_UNKNOWN)) + end + return false + end + table.sort(callOnLoad, function(v1, v2) return v1[2] < v2[2] end) + for i, v in ipairs(callOnLoad) do v[1]() end + collectgarbage("collect") + end + return DBM_GUI:ShowHide() + end + + function DBM:RegisterOnGuiLoadCallback(f, sort) + table.insert(callOnLoad, {f, sort or math.huge}) + end +end + + +---------------------- +-- Minimap Button -- +---------------------- +do + local dragMode = nil + + local function moveButton(self) + if dragMode == "free" then + local centerX, centerY = Minimap:GetCenter() + local x, y = GetCursorPosition() + x, y = x / self:GetEffectiveScale() - centerX, y / self:GetEffectiveScale() - centerY + self:ClearAllPoints() + self:SetPoint("CENTER", x, y) + else + local centerX, centerY = Minimap:GetCenter() + local x, y = GetCursorPosition() + x, y = x / self:GetEffectiveScale() - centerX, y / self:GetEffectiveScale() - centerY + centerX, centerY = math.abs(x), math.abs(y) + centerX, centerY = (centerX / math.sqrt(centerX^2 + centerY^2)) * 80, (centerY / sqrt(centerX^2 + centerY^2)) * 80 + centerX = x < 0 and -centerX or centerX + centerY = y < 0 and -centerY or centerY + self:ClearAllPoints() + self:SetPoint("CENTER", centerX, centerY) + end + end + + local button = CreateFrame("Button", "DBMMinimapButton", Minimap) + button:SetHeight(32) + button:SetWidth(32) + button:SetFrameStrata("MEDIUM") + button:SetPoint("CENTER", -65.35, -38.8) + button:SetMovable(true) + button:SetUserPlaced(true) + button:SetNormalTexture("Interface\\AddOns\\DBM-Core\\textures\\Minimap-Button-Up") + button:SetPushedTexture("Interface\\AddOns\\DBM-Core\\textures\\Minimap-Button-Down") + button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") + + button:SetScript("OnMouseDown", function(self, button) + if IsShiftKeyDown() and IsAltKeyDown() then + dragMode = "free" + self:SetScript("OnUpdate", moveButton) + elseif IsShiftKeyDown() or button == "RightButton" then + dragMode = nil + self:SetScript("OnUpdate", moveButton) + end + end) + button:SetScript("OnMouseUp", function(self) + self:SetScript("OnUpdate", nil) + end) + button:SetScript("OnClick", function(self, button) + if IsShiftKeyDown() or button == "RightButton" then return end + DBM:LoadGUI() + end) + button:SetScript("OnEnter", function(self) + GameTooltip_SetDefaultAnchor(GameTooltip, self) + GameTooltip:SetText(DBM_CORE_MINIMAP_TOOLTIP_HEADER, 1, 1, 1) + GameTooltip:AddLine(ver, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) + GameTooltip:AddLine(" ") + GameTooltip:AddLine(DBM_CORE_MINIMAP_TOOLTIP_FOOTER, RAID_CLASS_COLORS.MAGE.r, RAID_CLASS_COLORS.MAGE.g, RAID_CLASS_COLORS.MAGE.b, 1) + GameTooltip:Show() + end) + button:SetScript("OnLeave", function(self) + GameTooltip:Hide() + end) + + function DBM:ToggleMinimapButton() + self.Options.ShowMinimapButton = not self.Options.ShowMinimapButton + if self.Options.ShowMinimapButton then + button:Show() + else + button:Hide() + end + end + + function DBM:HideMinimapButton() + return button:Hide() + end +end + + +--------------------------- +-- Raid/Party Handling -- +--------------------------- +do + local inRaid = false + local playerRank = 0 + + function DBM:RAID_ROSTER_UPDATE() + if GetNumRaidMembers() >= 1 then + local playerWithHigherVersionPromoted = false + for i = 1, GetNumRaidMembers() do + local name, rank, subgroup, _, _, fileName = GetRaidRosterInfo(i) + if (not raid[name]) and inRaid then + fireEvent("raidJoin", name) + end + raid[name] = raid[name] or {} + raid[name].name = name + raid[name].rank = rank + raid[name].subgroup = subgroup + raid[name].class = fileName + raid[name].id = "raid"..i + raid[name].updated = true + if not playerWithHigherVersionPromoted and rank >= 1 and raid[name].version and raid[name].version > tonumber(DBM.Version) then + playerWithHigherVersionPromoted = true + end + end + enableIcons = not playerWithHigherVersionPromoted + if not inRaid then + inRaid = true + sendSync("DBMv4-Ver", "Hi!") + self:Schedule(2, DBM.RequestTimers, DBM) + fireEvent("raidJoin", UnitName("player")) + end + for i, v in pairs(raid) do + if not v.updated then + raid[i] = nil + fireEvent("raidLeave", i) + else + v.updated = nil + end + end + else + inRaid = false + enableIcons = true + fireEvent("raidLeave", UnitName("player")) + end + end + + function DBM:PARTY_MEMBERS_CHANGED() + if GetNumRaidMembers() > 0 then return end + if GetNumPartyMembers() >= 1 then + if not inRaid then + inRaid = true + sendSync("DBMv4-Ver", "Hi!") + self:Schedule(2, DBM.RequestTimers, DBM) + fireEvent("partyJoin", UnitName("player")) + end + for i = 0, GetNumPartyMembers() do + local id + if (i == 0) then + id = "player" + else + id = "party"..i + end + local name, server = UnitName(id) + local rank, _, fileName = UnitIsPartyLeader(id), UnitClass(id) + if server and server ~= "" then + name = name.."-"..server + end + if (not raid[name]) and inRaid then + fireEvent("partyJoin", name) + end + raid[name] = raid[name] or {} + raid[name].name = name + if rank then + raid[name].rank = 2 + else + raid[name].rank = 0 + end + raid[name].class = fileName + raid[name].id = id + raid[name].updated = true + end + for i, v in pairs(raid) do + if not v.updated then + raid[i] = nil + fireEvent("partyLeave", i) + else + v.updated = nil + end + end + else + inRaid = false + enableIcons = true + end + end + + function DBM:IsInRaid() + return inRaid + end + + function DBM:GetRaidRank(name) + name = name or UnitName("player") + return (raid[name] and raid[name].rank) or 0 + end + + function DBM:GetRaidSubgroup(name) + name = name or UnitName("player") + return (raid[name] and raid[name].subgroup) or 0 + end + + function DBM:GetRaidClass(name) + name = name or UnitName("player") + return (raid[name] and raid[name].class) or "UNKNOWN" + end + + function DBM:GetRaidUnitId(name) + name = name or UnitName("player") + return (raid[name] and raid[name].id) or "none" + end +end + + +--------------- +-- Options -- +--------------- +do + local function addDefaultOptions(t1, t2) + for i, v in pairs(t2) do + if t1[i] == nil then + t1[i] = v + elseif type(v) == "table" then + addDefaultOptions(v, t2[i]) + end + end + end + + local function setRaidWarningPositon() + RaidWarningFrame:ClearAllPoints() + RaidWarningFrame:SetPoint(DBM.Options.RaidWarningPosition.Point, UIParent, DBM.Options.RaidWarningPosition.Point, DBM.Options.RaidWarningPosition.X, DBM.Options.RaidWarningPosition.Y) + end + + function loadOptions() + DBM.Options = DBM_SavedOptions + addDefaultOptions(DBM.Options, DBM.DefaultOptions) + -- load special warning options + DBM:UpdateSpecialWarningOptions() + -- set this with a short delay to prevent issues with other addons also trying to do the same thing with another position ;) + DBM:Schedule(5, setRaidWarningPositon) + end + + local defaultStats = { + kills = 0, + pulls = 0, + heroic10Kills = 0, + heroic25Kills = 0, + heroic10Pulls = 0, + heroic25Pulls = 0, + }; + function loadModOptions(modId) + local savedOptions = _G[modId:gsub("-", "").."_SavedVars"] or {} + local savedStats = _G[modId:gsub("-", "").."_SavedStats"] or {} + for i, v in ipairs(DBM.Mods) do + if v.modId == modId then + savedOptions[v.id] = savedOptions[v.id] or v.Options + for option, optionValue in pairs(v.Options) do + if savedOptions[v.id][option] == nil then + savedOptions[v.id][option] = optionValue + end + end + v.Options = savedOptions[v.id] or {} + savedStats[v.id] = savedStats[v.id] or {}; + for x,y in pairs(defaultStats) do + savedStats[v.id][x] = savedStats[v.id][x] or y; + end + v.stats = savedStats[v.id] + if v.OnInitialize then v:OnInitialize() end + for i, cat in ipairs(v.categorySort) do -- temporary hack + if cat == "misc" then + table.remove(v.categorySort, i) + table.insert(v.categorySort, cat) + break + end + end + end + end + _G[modId:gsub("-", "").."_SavedVars"] = savedOptions + _G[modId:gsub("-", "").."_SavedStats"] = savedStats + end +end + + +-------------- +-- OnLoad -- +-------------- +do + local function showOldVerWarning() + StaticPopupDialogs["DBM_OLD_VERSION"] = { + text = DBM_CORE_ERROR_DBMV3_LOADED, + button1 = DBM_CORE_OK, + OnAccept = function() + DisableAddOn("DBM_API") + ReloadUI() + end, + timeout = 0, + exclusive = 1, + whileDead = 1, + } + StaticPopup_Show("DBM_OLD_VERSION") + end + + local function setCombatInitialized() + combatInitialized = true + end + + function DBM:ADDON_LOADED(modname) + if modname == "DBM-Core" then + loadOptions() + DBM.Bars:LoadOptions("DBM"); + --for id,barGroup in pairs(DBM.BarGroups) do + -- barGroup:LoadOptions(id) + --end + DBM.Arrow:LoadPosition() + if not DBM.Options.ShowMinimapButton then DBM:HideMinimapButton() end + self.AddOns = {} + for i = 1, GetNumAddOns() do + if GetAddOnMetadata(i, "X-DBM-Mod") and not checkEntry(bannedMods, GetAddOnInfo(i)) then + table.insert(self.AddOns, { + sort = tonumber(GetAddOnMetadata(i, "X-DBM-Mod-Sort") or math.huge) or math.huge, + category = GetAddOnMetadata(i, "X-DBM-Mod-Category") or "Other", + name = GetAddOnMetadata(i, "X-DBM-Mod-Name") or "", + zone = {strsplit(",", GetAddOnMetadata(i, "X-DBM-Mod-LoadZone") or "")}, + zoneId = {strsplit(",", GetAddOnMetadata(i, "X-DBM-Mod-LoadZoneID") or "")}, + subTabs = GetAddOnMetadata(i, "X-DBM-Mod-SubCategories") and {strsplit(",", GetAddOnMetadata(i, "X-DBM-Mod-SubCategories"))}, + hasHeroic = tonumber(GetAddOnMetadata(i, "X-DBM-Mod-Has-Heroic-Mode") or 1) == 1, + modId = GetAddOnInfo(i), + }) + for k, v in ipairs(self.AddOns[#self.AddOns].zone) do + self.AddOns[#self.AddOns].zone[k] = (self.AddOns[#self.AddOns].zone[k]):trim() + end + for i = #self.AddOns[#self.AddOns].zoneId, 1, -1 do + local id = tonumber(self.AddOns[#self.AddOns].zoneId[i]) + if id then + self.AddOns[#self.AddOns].zoneId[i] = id + else + table.remove(self.AddOns[#self.AddOns].zoneId, i) + end + end + if self.AddOns[#self.AddOns].subTabs then + for k, v in ipairs(self.AddOns[#self.AddOns].subTabs) do + self.AddOns[#self.AddOns].subTabs[k] = (self.AddOns[#self.AddOns].subTabs[k]):trim() + end + end + end + end + table.sort(self.AddOns, function(v1, v2) return v1.sort < v2.sort end) + self:RegisterEvents( + "COMBAT_LOG_EVENT_UNFILTERED", + "ZONE_CHANGED_NEW_AREA", + "RAID_ROSTER_UPDATE", + "PARTY_MEMBERS_CHANGED", + "CHAT_MSG_ADDON", + "PLAYER_REGEN_DISABLED", + "UNIT_DIED", + "UNIT_DESTROYED", + "CHAT_MSG_WHISPER", + "CHAT_MSG_BN_WHISPER", + "CHAT_MSG_MONSTER_YELL", + "CHAT_MSG_MONSTER_EMOTE", + "CHAT_MSG_MONSTER_SAY", + "CHAT_MSG_RAID_BOSS_EMOTE", + "PLAYER_ENTERING_WORLD", + "LFG_PROPOSAL_SHOW", + "LFG_PROPOSAL_FAILED", + "LFG_UPDATE" + ) + self:ZONE_CHANGED_NEW_AREA() + self:RAID_ROSTER_UPDATE() + self:PARTY_MEMBERS_CHANGED() + DBM:Schedule(1.5, setCombatInitialized) + local enabled, loadable = select(4, GetAddOnInfo("DBM_API")) + if enabled and loadable then showOldVerWarning() end + end + end +end + +function DBM:LFG_PROPOSAL_SHOW() + DBM.Bars:CreateBar(40, DBM_LFG_INVITE, "Interface\\Icons\\Spell_Holy_BorrowedTime") + fireEvent("DBM_TimerStart", "DBMLFGTimer", "Dungeon Finder", 40, "237538", "extratimer", nil, 0) +end + +function DBM:LFG_PROPOSAL_FAILED() + DBM.Bars:CancelBar(DBM_LFG_INVITE) + fireEvent("DBM_TimerStop", "DBMLFGTimer") +end + +function DBM:LFG_UPDATE() + local _, joined = GetLFGInfoServer() + if not joined then + DBM.Bars:CancelBar(DBM_LFG_INVITE) + end +end + +-------------------------------- +-- Load Boss Mods on Demand -- +-------------------------------- +function DBM:ZONE_CHANGED_NEW_AREA() + local zoneName = GetRealZoneText() + local zoneId = GetCurrentMapAreaID() + for i, v in ipairs(self.AddOns) do + if not IsAddOnLoaded(v.modId) and (checkEntry(v.zone, zoneName) or checkEntry(v.zoneId, zoneId)) then + -- srsly, wtf? LoadAddOn doesn't work properly on ZONE_CHANGED_NEW_AREA when reloading the UI + -- TODO: is this still necessary? this was a WotLK beta bug + DBM:Unschedule(DBM.LoadMod, DBM, v) + DBM:Schedule(3, DBM.LoadMod, DBM, v) + end + end + if select(2, IsInInstance()) == "pvp" and not DBM:GetModByName("AlteracValley") then + for i, v in ipairs(DBM.AddOns) do + if v.modId == "DBM-PvP" then + DBM:LoadMod(v) + break + end + end + end +end + +function DBM:LoadMod(mod) + if type(mod) ~= "table" then return false end + local _, _, _, enabled = GetAddOnInfo(mod.modId) + if not enabled then + EnableAddOn(mod.modId) + end + + local loaded, reason = LoadAddOn(mod.modId) + if not loaded then + if reason then + self:AddMsg(DBM_CORE_LOAD_MOD_ERROR:format(tostring(mod.name), tostring(_G["ADDON_"..reason or ""]))) + else +-- self:AddMsg(DBM_CORE_LOAD_MOD_ERROR:format(tostring(mod.name), DBM_CORE_UNKNOWN)) -- wtf, this should never happen....(but it does happen sometimes if you reload your UI in an instance...) + end + return false + else + self:AddMsg(DBM_CORE_LOAD_MOD_SUCCESS:format(tostring(mod.name))) + loadModOptions(mod.modId) + for i, v in ipairs(DBM.Mods) do -- load the hasHeroic attribute from the toc into all boss mods as required by the GetDifficulty() method + if v.modId == mod.modId then + v.hasHeroic = mod.hasHeroic + end + end + if DBM_GUI then + DBM_GUI:UpdateModList() + end + collectgarbage("collect") + return true + end +end + +do + if select(4, GetAddOnInfo("DBM-PvP")) and select(5, GetAddOnInfo("DBM-PvP")) then + local checkBG + function checkBG() + if not DBM:GetModByName("AlteracValley") and MAX_BATTLEFIELD_QUEUES then + for i = 1, MAX_BATTLEFIELD_QUEUES do + if GetBattlefieldStatus(i) == "confirm" then + for i, v in ipairs(DBM.AddOns) do + if v.modId == "DBM-PvP" then + DBM:LoadMod(v) + return + end + end + end + end + DBM:Schedule(1, checkBG) + end + end + DBM:Schedule(1, checkBG) + end +end + + + +----------------------------- +-- Handle Incoming Syncs -- +----------------------------- +do + local syncHandlers = {} + local whisperSyncHandlers = {} + + syncHandlers["DBMv4-Mod"] = function(msg, channel, sender) + local mod, revision, event, arg = strsplit("\t", msg) + mod = DBM:GetModByName(mod or "") + if mod and event and arg and revision then + revision = tonumber(revision) or 0 + mod:ReceiveSync(event, arg, sender, revision) + end + end + + syncHandlers["DBMv4-Pull"] = function(msg, channel, sender) + if select(2, IsInInstance()) == "pvp" then return end + local delay, mod, revision = strsplit("\t", msg) + local lag = select(3, GetNetStats()) / 1000 + delay = tonumber(delay or 0) or 0 + revision = tonumber(revision or 0) or 0 + mod = DBM:GetModByName(mod or "") + if mod and delay and (not mod.zones or #mod.zones == 0 or checkEntry(mod.zones, GetRealZoneText()) or checkEntry(mod.zones, GetCurrentMapAreaID())) and (not mod.minSyncRevision or revision >= mod.minSyncRevision) then + DBM:StartCombat(mod, delay + lag, true) + end + end + + syncHandlers["DBMv4-Kill"] = function(msg, channel, sender) + if select(2, IsInInstance()) == "pvp" then return end + local cId = tonumber(msg) + if cId then DBM:OnMobKill(cId, true) end + end + + syncHandlers["DBMv4-Ver"] = function(msg, channel, sender) + if msg == "Hi!" then + sendSync("DBMv4-Ver", ("%s\t%s\t%s\t%s"):format(DBM.Revision, DBM.Version, DBM.DisplayVersion, GetLocale())) + else + local revision, version, displayVersion, locale = strsplit("\t", msg) + revision, version = tonumber(revision or ""), tonumber(version or "") + if revision and version and displayVersion and raid[sender] then + raid[sender].revision = revision + raid[sender].version = version + raid[sender].displayVersion = displayVersion + raid[sender].locale = locale + if version > tonumber(DBM.Version) then + if raid[sender].rank >= 1 then + enableIcons = false + end + if not showedUpdateReminder then + local found = false + for i, v in pairs(raid) do + if v.version == version and v ~= raid[sender] then + found = true + break + end + end + if found then + showedUpdateReminder = true + if not DBM.Options.BlockVersionUpdatePopup then + DBM:ShowUpdateReminder(displayVersion, revision) + else + DBM:AddMsg(DBM_CORE_UPDATEREMINDER_HEADER:match("([^\n]*)")) + DBM:AddMsg(DBM_CORE_UPDATEREMINDER_HEADER:match("\n(.*)"):format(displayVersion, revision)) + DBM:AddMsg(("|HDBM:update:%s:%s|h|cff3588ff[https://discord.gg/4ZHfgskSvM]"):format(displayVersion, revision)) + end + end + end + end + end + end + end + + syncHandlers["DBMv4-Pizza"] = function(msg, channel, sender) + if select(2, IsInInstance()) == "pvp" then return end + if DBM:GetRaidRank(sender) == 0 then return end + if sender == UnitName("player") then return end + local time, text = strsplit("\t", msg) + time = tonumber(time or 0) + text = tostring(text) + if time and text then + DBM:CreatePizzaTimer(time, text, nil, sender) + end + end + + syncHandlers["DBMv4-Pizza-Cancel"] = function(msg, channel, sender) + if select(2, IsInInstance()) == "pvp" then return end + if DBM:GetRaidRank(sender) == 0 then return end + if sender == UnitName("player") then return end + DBM:Unschedule(SendChatMessage) + fireEvent("DBM_TimerStop", "DBMPizzaTimer") + end + + whisperSyncHandlers["DBMv4-RequestTimers"] = function(msg, channel, sender) + DBM:SendTimers(sender) + end + + whisperSyncHandlers["DBMv4-CombatInfo"] = function(msg, channel, sender) + local mod, time = strsplit("\t", msg) + mod = DBM:GetModByName(mod or "") + time = tonumber(time or 0) + if mod and time then + DBM:ReceiveCombatInfo(sender, mod, time) + end + end + + whisperSyncHandlers["DBMv4-TimerInfo"] = function(msg, channel, sender) + local mod, timeLeft, totalTime, id = strsplit("\t", msg) + mod = DBM:GetModByName(mod or "") + timeLeft = tonumber(timeLeft or 0) + totalTime = tonumber(totalTime or 0) + if mod and timeLeft and timeLeft > 0 and totalTime and totalTime > 0 and id then + DBM:ReceiveTimerInfo(sender, mod, timeLeft, totalTime, id, select(5, strsplit("\t", msg))) + end + end + + function DBM:CHAT_MSG_ADDON(prefix, msg, channel, sender) + if msg and channel ~= "WHISPER" and channel ~= "GUILD" then + local handler = syncHandlers[prefix] + if handler then handler(msg, channel, sender) end + elseif msg and channel == "WHISPER" and self:GetRaidUnitId(sender) ~= "none" then + local handler = whisperSyncHandlers[prefix] + if handler then handler(msg, channel, sender) end + end + end +end + + +----------------------- +-- Update Reminder -- +----------------------- +function DBM:ShowUpdateReminder(newVersion, newRevision) + local frame = CreateFrame("Frame", nil, UIParent) + frame:SetFrameStrata("DIALOG") + frame:SetWidth(430) + frame:SetHeight(155) + frame:SetPoint("TOP", 0, -230) + frame:SetBackdrop({ + bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", tile = true, tileSize = 32, edgeSize = 32, + insets = {left = 11, right = 12, top = 12, bottom = 11}, + }) + local fontstring = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal") + fontstring:SetWidth(410) + fontstring:SetHeight(0) + fontstring:SetPoint("TOP", 0, -16) + fontstring:SetText(DBM_CORE_UPDATEREMINDER_HEADER:format(newVersion, newRevision)) + local editBox = CreateFrame("EditBox", nil, frame) + do + local editBoxLeft = editBox:CreateTexture(nil, "BACKGROUND") + local editBoxRight = editBox:CreateTexture(nil, "BACKGROUND") + local editBoxMiddle = editBox:CreateTexture(nil, "BACKGROUND") + editBoxLeft:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left") + editBoxLeft:SetHeight(32) + editBoxLeft:SetWidth(32) + editBoxLeft:SetPoint("LEFT", -14, 0) + editBoxLeft:SetTexCoord(0, 0.125, 0, 1) + editBoxRight:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right") + editBoxRight:SetHeight(32) + editBoxRight:SetWidth(32) + editBoxRight:SetPoint("RIGHT", 6, 0) + editBoxRight:SetTexCoord(0.875, 1, 0, 1) + editBoxMiddle:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right") + editBoxMiddle:SetHeight(32) + editBoxMiddle:SetWidth(1) + editBoxMiddle:SetPoint("LEFT", editBoxLeft, "RIGHT") + editBoxMiddle:SetPoint("RIGHT", editBoxRight, "LEFT") + editBoxMiddle:SetTexCoord(0, 0.9375, 0, 1) + end + editBox:SetHeight(32) + editBox:SetWidth(250) + editBox:SetPoint("TOP", fontstring, "BOTTOM", 0, -4) + editBox:SetFontObject("GameFontHighlight") + editBox:SetTextInsets(0, 0, 0, 1) + editBox:SetFocus() + editBox:SetText("https://discord.gg/4ZHfgskSvM") + editBox:HighlightText() + editBox:SetScript("OnTextChanged", function(self) + editBox:SetText("https://discord.gg/4ZHfgskSvM") + editBox:HighlightText() + end) + local fontstring = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal") + fontstring:SetWidth(410) + fontstring:SetHeight(0) + fontstring:SetPoint("TOP", editBox, "BOTTOM", 0, 0) + fontstring:SetText(DBM_CORE_UPDATEREMINDER_FOOTER) + local button = CreateFrame("Button", nil, frame) + button:SetHeight(24) + button:SetWidth(75) + button:SetPoint("BOTTOM", 0, 13) + button:SetNormalFontObject("GameFontNormal") + button:SetHighlightFontObject("GameFontHighlight") + button:SetNormalTexture(button:CreateTexture(nil, nil, "UIPanelButtonUpTexture")) + button:SetPushedTexture(button:CreateTexture(nil, nil, "UIPanelButtonDownTexture")) + button:SetHighlightTexture(button:CreateTexture(nil, nil, "UIPanelButtonHighlightTexture")) + button:SetText(DBM_CORE_OK) + button:SetScript("OnClick", function(self) + frame:Hide() + end) +end + +---------------------- +-- Pull Detection -- +---------------------- +do + local targetList = {} + local function buildTargetList() + local uId = ((GetNumRaidMembers() == 0) and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + local id = (i == 0 and "target") or uId..i.."target" + local guid = UnitGUID(id) + if guid and (bit.band(guid:sub(1, 5), 0x00F) == 3 or bit.band(guid:sub(1, 5), 0x00F) == 5) then + local cId = tonumber(guid:sub(9, 12), 16) + targetList[cId] = id + end + end + end + + local function clearTargetList() + table.wipe(targetList) + end + + local function scanForCombat(mod, mob) + if not checkEntry(inCombat, mod) then + buildTargetList() + if targetList[mob] and UnitAffectingCombat(targetList[mob]) then + DBM:StartCombat(mod, 3) + end + clearTargetList() + end + end + + local function checkForPull(mob, combatInfo) + local uId = targetList[mob] + if uId and UnitAffectingCombat(uId) then + DBM:StartCombat(combatInfo.mod, 0) + return true + elseif uId then + DBM:Schedule(3, scanForCombat, combatInfo.mod, mob) + end + end + + function DBM:PLAYER_REGEN_DISABLED() + if not combatInitialized then return end + if combatInfo[GetRealZoneText()] or combatInfo[GetCurrentMapAreaID()] then + buildTargetList() + if combatInfo[GetRealZoneText()] then + for i, v in ipairs(combatInfo[GetRealZoneText()]) do + if v.type == "combat" then + if v.multiMobPullDetection then + for _, mob in ipairs(v.multiMobPullDetection) do + if checkForPull(mob, v) then + break + end + end + else + checkForPull(v.mob, v) + end + end + end + end + -- copy & paste, lol + if combatInfo[GetCurrentMapAreaID()] then + for i, v in ipairs(combatInfo[GetCurrentMapAreaID()]) do + if v.type == "combat" then + if v.multiMobPullDetection then + for _, mob in ipairs(v.multiMobPullDetection) do + if checkForPull(mob, v) then + break + end + end + else + checkForPull(v.mob, v) + end + end + end + end + clearTargetList() + end + end +end + +do + -- called for all mob chat events + local function onMonsterMessage(type, msg) + -- pull detection + if combatInfo[GetRealZoneText()] then + for i, v in ipairs(combatInfo[GetRealZoneText()]) do + if v.type == type and checkEntry(v.msgs, msg) then + DBM:StartCombat(v.mod, 0) + end + end + end + -- copy & paste, lol + if combatInfo[GetCurrentMapAreaID()] then + for i, v in ipairs(combatInfo[GetCurrentMapAreaID()]) do + if v.type == type and checkEntry(v.msgs, msg) then + DBM:StartCombat(v.mod, 0) + end + end + end + -- kill detection (wipe detection would also be nice to have) + -- todo: add sync + for i = #inCombat, 1, -1 do + local v = inCombat[i] + if not v.combatInfo then return end + if v.combatInfo.killType == type and v.combatInfo.killMsgs[msg] then + DBM:EndCombat(v) + end + end + end + + function DBM:CHAT_MSG_MONSTER_YELL(msg) + return onMonsterMessage("yell", msg) + end + + function DBM:CHAT_MSG_MONSTER_EMOTE(msg) + return onMonsterMessage("emote", msg) + end + + function DBM:CHAT_MSG_RAID_BOSS_EMOTE(msg, ...) + onMonsterMessage("emote", msg) + return self:FilterRaidBossEmote(msg, ...) + end + + function DBM:CHAT_MSG_MONSTER_SAY(msg) + return onMonsterMessage("say", msg) + end +end + + +--------------------------- +-- Kill/Wipe Detection -- +--------------------------- +function checkWipe(confirm) + if #inCombat > 0 then + local wipe = true + local uId = ((GetNumRaidMembers() == 0) and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + local id = (i == 0 and "player") or uId..i + if UnitAffectingCombat(id) and not UnitIsDeadOrGhost(id) then + wipe = false + break + end + end + if not wipe then + DBM:Schedule(3, checkWipe) + elseif confirm then + for i = #inCombat, 1, -1 do + DBM:EndCombat(inCombat[i], true) + end + else + local maxDelayTime = 5 + for i, v in ipairs(inCombat) do + maxDelayTime = v.combatInfo and v.combatInfo.wipeTimer and v.combatInfo.wipeTimer > maxDelayTime and v.combatInfo.wipeTimer or maxDelayTime + end + DBM:Schedule(maxDelayTime, checkWipe, true) + end + end +end + +function DBM:StartCombat(mod, delay, synced) + if not checkEntry(inCombat, mod) then + if not mod.combatInfo then return end + if mod.combatInfo.noCombatInVehicle and UnitInVehicle("player") then -- HACK + return + end + table.insert(inCombat, mod) + self:AddMsg(DBM_CORE_COMBAT_STARTED:format(mod.combatInfo.name)) + if mod:IsDifficulty("heroic5", "heroic10") then + mod.stats.heroic10Pulls = mod.stats.heroic10Pulls + 1 + elseif mod:IsDifficulty("heroic25") then + mod.stats.heroic25Pulls = mod.stats.heroic25Pulls + 1 + elseif mod:IsDifficulty("normal5", "normal10", "normal25") then + mod.stats.pulls = mod.stats.pulls + 1 + end + mod.inCombat = true + mod.blockSyncs = nil + mod.combatInfo.pull = GetTime() - (delay or 0) + self:Schedule(mod.minCombatTime or 3, checkWipe) + if (DBM.Options.AlwaysShowHealthFrame or mod.Options.HealthFrame) and mod.Options.Enabled then + DBM.BossHealth:Show(mod.localization.general.name) + if mod.bossHealthInfo then + for i = 1, #mod.bossHealthInfo, 2 do + DBM.BossHealth:AddBoss(mod.bossHealthInfo[i], mod.bossHealthInfo[i + 1]) + end + else + DBM.BossHealth:AddBoss(mod.combatInfo.mob, mod.localization.general.name) + end + end + if mod.OnCombatStart and mod.Options.Enabled then mod:OnCombatStart(delay or 0) end + if not synced then + sendSync("DBMv4-Pull", (delay or 0).."\t"..mod.id.."\t"..(mod.revision or 0)) + end + fireEvent("pull", mod, delay, synced) + -- http://www.deadlybossmods.com/forum/viewtopic.php?t=1464 + if DBM.Options.ShowBigBrotherOnCombatStart and BigBrother and type(BigBrother.ConsumableCheck) == "function" then + if DBM.Options.BigBrotherAnnounceToRaid then + BigBrother:ConsumableCheck("RAID") + else + BigBrother:ConsumableCheck("SELF") + end + end + if DBM.Options.FixCLEUOnCombatStart then + CombatLogClearEntries() + end + end +end + + +function DBM:EndCombat(mod, wipe) + if removeEntry(inCombat, mod) then + mod:Stop() + mod.inCombat = false + mod.blockSyncs = true + if mod.combatInfo.killMobs then + for i, v in pairs(mod.combatInfo.killMobs) do + mod.combatInfo.killMobs[i] = true + end + end + if wipe then + local thisTime = GetTime() - mod.combatInfo.pull + if thisTime < 30 then + if mod:IsDifficulty("heroic5", "heroic10") then + mod.stats.heroic10Pulls = mod.stats.heroic10Pulls - 1 + elseif mod:IsDifficulty("heroic25") then + mod.stats.heroic25Pulls = mod.stats.heroic25Pulls - 1 + elseif mod:IsDifficulty("normal5", "normal10", "normal25") then + mod.stats.pulls = mod.stats.pulls - 1 + end + end + self:AddMsg(DBM_CORE_COMBAT_ENDED:format(mod.combatInfo.name, strFromTime(thisTime))) + local msg + for k, v in pairs(autoRespondSpam) do + msg = msg or chatPrefixShort..DBM_CORE_WHISPER_COMBAT_END_WIPE:format(UnitName("player"), (mod.combatInfo.name or "")) + sendWhisper(k, msg) + end + fireEvent("wipe", mod) + fireEvent("DBM_Wipe", mod) + else + local thisTime = GetTime() - mod.combatInfo.pull + local lastTime = (mod:IsDifficulty("heroic5", "heroic10") and mod.stats.heroic10LastTime) or (mod:IsDifficulty("heroic25") and mod.stats.heroic25LastTime) or mod:IsDifficulty("normal5", "normal10", "normal25") and mod.stats.lastTime + local bestTime = (mod:IsDifficulty("heroic5", "heroic10") and mod.stats.heroic10BestTime) or (mod:IsDifficulty("heroic25") and mod.stats.heroic25BestTime) or mod:IsDifficulty("normal5", "normal10", "normal25") and mod.stats.bestTime + if mod:IsDifficulty("heroic5", "heroic10") then + mod.stats.heroic10Kills = mod.stats.heroic10Kills + 1 + mod.stats.heroic10LastTime = thisTime + mod.stats.heroic10BestTime = math.min(bestTime or math.huge, thisTime) + elseif mod:IsDifficulty("heroic25") then + mod.stats.heroic25Kills = mod.stats.heroic25Kills + 1 + mod.stats.heroic25LastTime = thisTime + mod.stats.heroic25BestTime = math.min(bestTime or math.huge, thisTime) + elseif mod:IsDifficulty("normal5", "normal10", "normal25") then + mod.stats.kills = mod.stats.kills + 1 + mod.stats.lastTime = thisTime + mod.stats.bestTime = math.min(bestTime or math.huge, thisTime) + end + if not lastTime then + self:AddMsg(DBM_CORE_BOSS_DOWN:format(mod.combatInfo.name, strFromTime(thisTime))) + elseif thisTime < (bestTime or math.huge) then + self:AddMsg(DBM_CORE_BOSS_DOWN_NEW_RECORD:format(mod.combatInfo.name, strFromTime(thisTime), strFromTime(bestTime))) + else + self:AddMsg(DBM_CORE_BOSS_DOWN_LONG:format(mod.combatInfo.name, strFromTime(thisTime), strFromTime(lastTime), strFromTime(bestTime))) + end + local msg + for k, v in pairs(autoRespondSpam) do + msg = msg or chatPrefixShort..DBM_CORE_WHISPER_COMBAT_END_KILL:format(UnitName("player"), (mod.combatInfo.name or "")) + sendWhisper(k, msg) + end + fireEvent("kill", mod) + fireEvent("DBM_Kill", mod) + end + table.wipe(autoRespondSpam) + if mod.OnCombatEnd then mod:OnCombatEnd(wipe) end + DBM.BossHealth:Hide() + DBM.Arrow:Hide(true) + end +end + +function DBM:OnMobKill(cId, synced) + for i = #inCombat, 1, -1 do + local v = inCombat[i] + if not v.combatInfo then + return + end + if v.combatInfo.killMobs and v.combatInfo.killMobs[cId] then + if not synced then + sendSync("DBMv4-Kill", cId) + end + v.combatInfo.killMobs[cId] = false + local allMobsDown = true + for i, v in pairs(v.combatInfo.killMobs) do + if v then + allMobsDown = false + break + end + end + if allMobsDown then + self:EndCombat(v) + end + elseif cId == v.combatInfo.mob and not v.combatInfo.killMobs and not v.combatInfo.multiMobPullDetection then + if not synced then + sendSync("DBMv4-Kill", cId) + end + self:EndCombat(v) + end + end +end + +function DBM:UNIT_DIED(args) + if bit.band(args.destGUID:sub(1, 5), 0x00F) == 3 or bit.band(args.destGUID:sub(1, 5), 0x00F) == 5 then + self:OnMobKill(tonumber(args.destGUID:sub(9, 12), 16)) + end +end +DBM.UNIT_DESTROYED = DBM.UNIT_DIED + + +---------------------- +-- Timer recovery -- +---------------------- +do + local requestedFrom = nil + local requestTime = 0 + + function DBM:RequestTimers() + local bestClient + for i, v in pairs(raid) do + if v.name ~= UnitName("player") and UnitIsConnected(v.id) and (not UnitIsGhost(v.id)) and (v.revision or 0) > ((bestClient and bestClient.revision) or 0) then + bestClient = v + end + end + if not bestClient then return end + requestedFrom = bestClient.name + requestTime = GetTime() + SendAddonMessage("DBMv4-RequestTimers", "", "WHISPER", bestClient.name) + end + + function DBM:ReceiveCombatInfo(sender, mod, time) + if sender == requestedFrom and (GetTime() - requestTime) < 5 and #inCombat == 0 then + local lag = select(3, GetNetStats()) / 1000 + if not mod.combatInfo then return end + table.insert(inCombat, mod) + mod.inCombat = true + mod.blockSyncs = nil + mod.combatInfo.pull = GetTime() - time + lag + self:Schedule(3, checkWipe) + end + end + + function DBM:ReceiveTimerInfo(sender, mod, timeLeft, totalTime, id, ...) + if sender == requestedFrom and (GetTime() - requestTime) < 5 then + local lag = select(3, GetNetStats()) / 1000 + for i, v in ipairs(mod.timers) do + if v.id == id then + v:Start(totalTime, ...) + v:Update(totalTime - timeLeft + lag, totalTime, ...) + end + end + end + end +end + +do + local spamProtection = 0 + function DBM:SendTimers(target) + if GetTime() - spamProtection < 0.4 then + return + end + spamProtection = GetTime() + if UnitInBattleground("player") then + DBM:SendBGTimers(target) + end + if #inCombat < 1 then return end + local mod + for i, v in ipairs(inCombat) do + mod = not v.isCustomMod and v + end + mod = mod or inCombat[1] + self:SendCombatInfo(mod, target) + self:SendTimerInfo(mod, target) + end +end + +function DBM:SendBGTimers(target) + local mod + if IsActiveBattlefieldArena() then + mod = self:GetModByName("Arenas") + else + -- FIXME: this doesn't work for non-english clients + local zone = GetRealZoneText():gsub(" ", "") + mod = self:GetModByName(zone) + end + if mod and mod.timers then + self:SendTimerInfo(mod, target) + end +end + +function DBM:SendCombatInfo(mod, target) + return SendAddonMessage("DBMv4-CombatInfo", ("%s\t%s"):format(mod.id, GetTime() - mod.combatInfo.pull), "WHISPER", target) +end + +function DBM:SendTimerInfo(mod, target) + for i, v in ipairs(mod.timers) do + for _, uId in ipairs(v.startedTimers) do + local elapsed, totalTime, timeLeft + if select("#", string.split("\t", uId)) > 1 then + elapsed, totalTime = v:GetTime(select(2, string.split("\t", uId))) + else + elapsed, totalTime = v:GetTime() + end + timeLeft = totalTime - elapsed + if timeLeft > 0 and totalTime > 0 then + SendAddonMessage("DBMv4-TimerInfo", ("%s\t%s\t%s\t%s"):format(mod.id, timeLeft, totalTime, uId), "WHISPER", target) + end + end + end +end + +do + local function requestTimers() + local uId = ((GetNumRaidMembers() == 0) and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + local id = (i == 0 and "player") or uId..i + if UnitAffectingCombat(id) and not UnitIsDeadOrGhost(id) then + DBM:RequestTimers() + break + end + end + end + + function DBM:PLAYER_ENTERING_WORLD() + if #inCombat == 0 then + DBM:Schedule(0, requestTimers) + end + self:LFG_UPDATE() +-- self:Schedule(10, function() if not DBM.Options.HelpMessageShown then DBM.Options.HelpMessageShown = true DBM:AddMsg(DBM_CORE_NEED_SUPPORT) end end) + end +end + + +------------------------------------ +-- Auto-respond/Status whispers -- +------------------------------------ +do + local function getNumAlivePlayers() + local alive = 0 + if GetNumRaidMembers() > 0 then + for i = 1, GetNumRaidMembers() do + alive = alive + ((UnitIsDeadOrGhost("raid"..i) and 0) or 1) + end + else + alive = (UnitIsDeadOrGhost("player") and 0) or 1 + for i = 1, GetNumPartyMembers() do + alive = alive + ((UnitIsDeadOrGhost("party"..i) and 0) or 1) + end + end + return alive + end + + + local function isOnSameServer(presenceId) + local toonID, client = select(5, BNGetFriendInfoByID(presenceId)) + if client ~= "WoW" then + return false + end + return GetRealmName() == select(4, BNGetToonInfo(toonID)) + end + + -- sender is a presenceId for real id messages, a character name otherwise + local function onWhisper(msg, sender, isRealIdMessage) + if msg == "status" and #inCombat > 0 and DBM.Options.StatusEnabled then + local mod + for i, v in ipairs(inCombat) do + mod = not v.isCustomMod and v + end + mod = mod or inCombat[1] + sendWhisper(sender, chatPrefix..DBM_CORE_STATUS_WHISPER:format((mod.combatInfo.name or ""), mod:GetHP() or "unknown", getNumAlivePlayers(), math.max(GetNumRaidMembers(), GetNumPartyMembers() + 1))) + elseif #inCombat > 0 and DBM.Options.AutoRespond and + (isRealIdMessage and (not isOnSameServer(sender) or DBM:GetRaidUnitId((select(4, BNGetFriendInfoByID(sender)))) == "none") or not isRealIdMessage and DBM:GetRaidUnitId(sender) == "none") then + local mod + for i, v in ipairs(inCombat) do + mod = not v.isCustomMod and v + end + mod = mod or inCombat[1] + if not autoRespondSpam[sender] then + sendWhisper(sender, chatPrefix..DBM_CORE_AUTO_RESPOND_WHISPER:format(UnitName("player"), mod.combatInfo.name or "", mod:GetHP() or "unknown", getNumAlivePlayers(), math.max(GetNumRaidMembers(), GetNumPartyMembers() + 1))) + DBM:AddMsg(DBM_CORE_AUTO_RESPONDED) + end + autoRespondSpam[sender] = true + end + end + + function DBM:CHAT_MSG_WHISPER(msg, name) + return onWhisper(msg, name, false) + end + + function DBM:CHAT_MSG_BN_WHISPER(msg, ...) + local presenceId = select(12, ...) -- srsly? + return onWhisper(msg, presenceId, true) + end +end + + +------------------- +-- Chat Filter -- +------------------- +do + local function filterOutgoing(self, event, ...) + local msg = ... + if not msg and self then -- compatibility mode! + -- we also check if self exists to prevent a possible freeze if the function is called without arguments at all + -- as this would be even worse than the issue with missing whisper messages ;) + return filterOutgoing(nil, nil, self, event) + end + return msg:sub(1, chatPrefix:len()) == chatPrefix or msg:sub(1, chatPrefixShort:len()) == chatPrefixShort, ... + end + + local function filterIncoming(self, event, ...) + local msg = ... + if not msg and self then -- compatibility mode! + return filterIncoming(nil, nil, self, event) + end + if DBM.Options.SpamBlockBossWhispers then + return #inCombat > 0 and (msg == "status" or msg:sub(1, chatPrefix:len()) == chatPrefix or msg:sub(1, chatPrefixShort:len()) == chatPrefixShort), ... + else + return msg == "status" and #inCombat > 0, ... + end + end + + local function filterRaidWarning(self, event, ...) + local msg = ... + if not msg and self then -- compatibility mode! + return filterRaidWarning(nil, nil, self, event) + end + return DBM.Options.SpamBlockRaidWarning and type(msg) == "string" and (not not msg:match("^%s*%*%*%*")), ... + end + + ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER_INFORM", filterOutgoing) + ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER_INFORM", filterOutgoing) + ChatFrame_AddMessageEventFilter("CHAT_MSG_WHISPER", filterIncoming) + ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER", filterIncoming) + ChatFrame_AddMessageEventFilter("CHAT_MSG_RAID_WARNING", filterRaidWarning) + ChatFrame_AddMessageEventFilter("CHAT_MSG_PARTY", filterRaidWarning) + ChatFrame_AddMessageEventFilter("CHAT_MSG_PARTY_LEADER", filterRaidWarning) +end + + +do + local old = RaidWarningFrame:GetScript("OnEvent") + RaidWarningFrame:SetScript("OnEvent", function(self, event, msg, ...) + if DBM.Options.SpamBlockRaidWarning and msg:find("%*%*%* .* %*%*%*") then + return + end + return old(self, event, msg, ...) + end) +end + +do + local old = RaidBossEmoteFrame:GetScript("OnEvent") + RaidBossEmoteFrame:SetScript("OnEvent", function(...) + if DBM.Options.HideBossEmoteFrame and #inCombat > 0 then + return + end + return old(...) + end) +end + + +-------------------------- +-- Enable/Disable DBM -- +-------------------------- +function DBM:Disable() + unschedule() + self.Options.Enabled = false +end + +function DBM:Enable() + self.Options.Enabled = true +end + +function DBM:IsEnabled() + return self.Options.Enabled +end + + +----------------------- +-- Misc. Functions -- +----------------------- +function DBM:AddMsg(text, prefix) + prefix = prefix or (self.localization and self.localization.general.name) or "Deadly Boss Mods" + DEFAULT_CHAT_FRAME:AddMessage(("|cffff7d0a<|r|cffffd200%s|r|cffff7d0a>|r %s"):format(tostring(prefix), tostring(text)), 0.41, 0.8, 0.94) +end + +do + local testMod + local testWarning1, testWarning2, testWarning3 + -- local testTimer + local testSpecialWarning + local timerTestBar, timerPewPewPew, timerEvilSpell, timerBoom + function DBM:DemoMode() + if not testMod then + testMod = DBM:NewMod("TestMod", "DBM-PvP") -- temp fix, as it requires a modId + testWarning1 = testMod:NewAnnounce("%s", 1, "Interface\\Icons\\Spell_Nature_WispSplode") + testWarning2 = testMod:NewAnnounce("%s", 2, "Interface\\Icons\\Spell_Shadow_ShadesOfDarkness") + testWarning3 = testMod:NewAnnounce("%s", 3, "Interface\\Icons\\Spell_Fire_SelfDestruct") + -- testTimer = testMod:NewTimer(20, "%s", "%%s") + timerTestBar = testMod:NewTimer(10, "Test Bar", "Interface\\Icons\\Spell_Nature_WispSplode") + timerPewPewPew = testMod:NewTimer(20, "Pew Pew Pew...", "Interface\\Icons\\Spell_Nature_Starfall") + timerEvilSpell = testMod:NewTimer(43, "Evil Spell", "Interface\\Icons\\Spell_Shadow_ShadesOfDarkness") + timerBoom = testMod:NewTimer(60, "Boom!", "Interface\\Icons\\Spell_Fire_SelfDestruct") + testSpecialWarning = testMod:NewSpecialWarning("%s") + end + timerTestBar:Start() + timerPewPewPew:Start() + timerEvilSpell:Start() + timerBoom:Start() + + testWarning1:Cancel() + testWarning2:Cancel() + testWarning3:Cancel() + testSpecialWarning:Cancel() + testWarning1:Show("Test-mode started...") + testWarning1:Schedule(10, "Test bar expired!") + testWarning3:Schedule(20, "Pew Pew Laser Owl!") + testWarning2:Schedule(38, "Evil Spell in 5 sec!") + testWarning2:Schedule(43, "Evil Spell!") + testWarning3:Schedule(50, "Boom in 10 sec!") + testWarning1:Schedule(62, "Test-mode finished!") + testSpecialWarning:Schedule(60, "Boom!") + end +end + +DBM.Bars:SetAnnounceHook(function(bar) + local prefix + if bar.color and bar.color.r == 1 and bar.color.g == 0 and bar.color.b == 0 then + prefix = DBM_CORE_HORDE + elseif bar.color and bar.color.r == 0 and bar.color.g == 0 and bar.color.b == 1 then + prefix = DBM_CORE_ALLIANCE + end + if prefix then + return ("%s: %s %d:%02d"):format(prefix, _G[bar.frame:GetName().."BarName"]:GetText(), math.floor(bar.timer / 60), bar.timer % 60) + end +end) + +function DBM:Capitalize(str) + local firstByte = str:byte(1, 1) + local numBytes = 1 + if firstByte >= 0xF0 then -- firstByte & 0b11110000 + numBytes = 4 + elseif firstByte >= 0xE0 then -- firstByte & 0b11100000 + numBytes = 3 + elseif firstByte >= 0xC0 then -- firstByte & 0b11000000 + numBytes = 2 + end + return str:sub(1, numBytes):upper()..str:sub(numBytes + 1):lower() +end + +----------------- +-- Map Sizes -- +----------------- +DBM.MapSizes = {} + +function DBM:RegisterMapSize(zone, ...) + if not DBM.MapSizes[zone] then + DBM.MapSizes[zone] = {} + end + local zone = DBM.MapSizes[zone] + for i = 1, select("#", ...), 3 do + local level, width, height = select(i, ...) + zone[level] = {width, height} + end +end + + +-------------------------- +-- Boss Mod Prototype -- +-------------------------- +local bossModPrototype = {} + + +---------------------------- +-- Boss Mod Constructor -- +---------------------------- +do + local modsById = setmetatable({}, {__mode = "v"}) + local mt = {__index = bossModPrototype} + + function DBM:NewMod(name, modId, modSubTab) + if modsById[name] then error("DBM:NewMod(): Mod names are used as IDs and must therefore be unique.", 2) end + local obj = setmetatable( + { + Options = { + Enabled = true, + Announce = false, + }, + subTab = modSubTab, + optionCategories = { + }, + categorySort = {}, + id = name, + announces = {}, + specwarns = {}, + vb = {}, -- variables table, used by details to check phase + timers = {}, + modId = modId, + revision = 0, + localization = self:GetModLocalization(name) + }, + mt + ) + for i, v in ipairs(self.AddOns) do + if v.modId == modId then + obj.addon = v + break + end + end + if obj.localization.general.name == "name" then obj.localization.general.name = name end + table.insert(self.Mods, obj) + modsById[name] = obj + obj:AddBoolOption("HealthFrame", false, "misc") + obj:SetZone() + return obj + end + + function DBM:GetModByName(name) + return modsById[name] + end +end + + +----------------------- +-- General Methods -- +----------------------- +bossModPrototype.RegisterEvents = DBM.RegisterEvents +bossModPrototype.UnregisterAllEvents = DBM.UnregisterAllEvents +bossModPrototype.AddMsg = DBM.AddMsg + +function bossModPrototype:SetZone(...) + if select("#", ...) == 0 then + if self.addon.zone and #self.addon.zone > 0 and self.addon.zoneId and #self.addon.zoneId > 0 then + self.zones = {} + for i, v in ipairs(self.addon.zone) do + self.zones[#self.zones + 1] = v + end + for i, v in ipairs(self.addon.zoneId) do + self.zones[#self.zones + 1] = v + end + else + self.zones = self.addon.zone and #self.addon.zone > 0 and self.addon.zone or self.addon.zoneId and #self.addon.zoneId > 0 and self.addon.zoneId or {} + end + elseif select(1, ...) ~= DBM_DISABLE_ZONE_DETECTION then + self.zones = {...} + else -- disable zone detection + self.zones = nil + end +end + +function bossModPrototype:SetCreatureID(...) + self.creatureId = ... + if select("#", ...) > 1 then + self.multiMobPullDetection = {...} + if self.combatInfo then + self.combatInfo.multiMobPullDetection = self.multiMobPullDetection + end + end +end + +function bossModPrototype:Toggle() + if self.Options.Enabled then + self:DisableMod() + else + self:EnableMod() + end +end + +function bossModPrototype:EnableMod() + self.Options.Enabled = true +end + +function bossModPrototype:DisableMod() + self:Stop() + self.Options.Enabled = false +end + +function bossModPrototype:RegisterOnUpdateHandler(func, interval) + if type(func) ~= "function" then return end + self.elapsed = 0 + self.updateInterval = interval or 0 + updateFunctions[self] = func +end + +function bossModPrototype:SetRevision(revision) + self.revision = revision +end + +function bossModPrototype:SendWhisper(msg, target) + return not DBM.Options.DontSendBossWhispers and sendWhisper(target, chatPrefixShort..msg) +end + +function bossModPrototype:GetUnitCreatureId(uId) + local guid = UnitGUID(uId) + return (guid and tonumber(guid:sub(9, 12), 16)) or 0 +end + +function bossModPrototype:GetCIDFromGUID(guid) + return (guid and tonumber(guid:sub(9, 12), 16)) or 0 +end + +function bossModPrototype:GetBossTarget(cid) + cid = cid or self.creatureId + for i = 1, GetNumRaidMembers() do + if self:GetUnitCreatureId("raid"..i.."target") == cid then + return UnitName("raid"..i.."targettarget"), "raid"..i.."targettarget" + elseif self:GetUnitCreatureId("focus") == cid then -- we check our own focus frame, maybe the boss is there ;) + return UnitName("focustarget"), "focustarget" + end + end +end + +function bossModPrototype:GetThreatTarget(cid) + cid = cid or self.creatureId + for i = 1, GetNumRaidMembers() do + if self:GetUnitCreatureId("raid"..i.."target") == cid then + for x = 1, GetNumRaidMembers() do + if UnitDetailedThreatSituation("raid"..x, "raid"..i.."target") == 1 then + return "raid"..x + end + end + end + end +end + +function bossModPrototype:Stop(cid) + for i, v in ipairs(self.timers) do + v:Stop() + end + self:Unschedule() +end + +-- hard coded party-mod support, yay :) +-- returns heroic for old instances that do not have a heroic mode (Naxx, Ulduar...) +function bossModPrototype:GetDifficulty() + local _, instanceType, difficulty, _, _, playerDifficulty, isDynamicInstance = GetInstanceInfo() + if instanceType == "raid" and isDynamicInstance then -- "new" instance (ICC) + if (difficulty == 1) then -- 10 men + return playerDifficulty == 0 and "normal10" or playerDifficulty == 1 and "heroic10" or "unknown" + elseif (difficulty == 2) then -- 25 men + return playerDifficulty == 0 and "normal25" or playerDifficulty == 1 and "heroic25" or "unknown" + elseif (difficulty == 3) then + return "heroic10"; + elseif (difficulty == 4) then + return "heroic25"; + end + else -- support for "old" instances + if GetInstanceDifficulty() == 1 then + return (self.modId == "DBM-Party-WotLK" or self.modId == "DBM-Party-BC") and "normal5" or + self.hasHeroic and "normal10" or "heroic10" + elseif GetInstanceDifficulty() == 2 then + return (self.modId == "DBM-Party-WotLK" or self.modId == "DBM-Party-BC") and "heroic5" or + self.hasHeroic and "normal25" or "heroic25" + elseif GetInstanceDifficulty() == 3 then + return "heroic10" + elseif GetInstanceDifficulty() == 4 then + return "heroic25" + end + end +end + +function bossModPrototype:IsDifficulty(...) + local diff = self:GetDifficulty() + for i = 1, select("#", ...) do + if diff == select(i, ...) then + return true + end + end + return false +end + +function bossModPrototype:SetUsedIcons(...) + self.usedIcons = {} + for i = 1, select("#", ...) do + self.usedIcons[select(i, ...)] = true + end +end + +function bossModPrototype:LatencyCheck() + return select(3, GetNetStats()) < DBM.Options.LatencyThreshold +end + +local function getTalentpointsSpent(spellID) + local spellName = GetSpellInfo(spellID) + for tabIndex=1, GetNumTalentTabs() do + for talentID=1, GetNumTalents(tabIndex) do + local name, _, _, _, spent = GetTalentInfo(tabIndex, talentID) + if(name == spellName) then + return spent + end + end + end + return 0 +end + +function bossModPrototype:IsMelee() + return select(2, UnitClass("player")) == "ROGUE" + or select(2, UnitClass("player")) == "WARRIOR" + or select(2, UnitClass("player")) == "DEATHKNIGHT" + or (select(2, UnitClass("player")) == "PALADIN" and select(3, GetTalentTabInfo(1)) < 51) + or (select(2, UnitClass("player")) == "SHAMAN" and select(3, GetTalentTabInfo(2)) >= 51) + or (select(2, UnitClass("player")) == "DRUID" and select(3, GetTalentTabInfo(2)) >= 51) +end + +function bossModPrototype:IsRanged() + return select(2, UnitClass("player")) == "MAGE" + or select(2, UnitClass("player")) == "HUNTER" + or select(2, UnitClass("player")) == "WARLOCK" + or select(2, UnitClass("player")) == "PRIEST" + or (select(2, UnitClass("player")) == "PALADIN" and select(3, GetTalentTabInfo(1)) >= 51) + or (select(2, UnitClass("player")) == "SHAMAN" and select(3, GetTalentTabInfo(2)) < 51) + or (select(2, UnitClass("player")) == "DRUID" and select(3, GetTalentTabInfo(2)) < 51) +end + +function bossModPrototype:IsPhysical() + return self:IsMelee() or select(2, UnitClass("player")) == "HUNTER" +end + +function bossModPrototype:CanRemoveEnrage() + return select(2, UnitClass("player")) == "HUNTER" or select(2, UnitClass("player")) == "ROGUE" +end + +local function IsDeathKnightTank() + -- idea taken from addon 'ElitistJerks' + local tankTalents = (getTalentpointsSpent(16271) >= 5 and 1 or 0) + -- Anticipation + (getTalentpointsSpent(49042) >= 5 and 1 or 0) + -- Toughness + (getTalentpointsSpent(55225) >= 5 and 1 or 0) -- Blade Barrier + return tankTalents >= 2 +end + +local function IsDruidTank() + -- idea taken from addon 'ElitistJerks' + local tankTalents = (getTalentpointsSpent(57881) >= 2 and 1 or 0) + -- Natural Reaction + (getTalentpointsSpent(16929) >= 3 and 1 or 0) + -- Thick Hide + (getTalentpointsSpent(61336) >= 1 and 1 or 0) + -- Survival Instincts + (getTalentpointsSpent(57877) >= 3 and 1 or 0) -- Protector of the Pack + return tankTalents >= 3 +end + +function bossModPrototype:IsTank() + return (select(2, UnitClass("player")) == "WARRIOR" and select(3, GetTalentTabInfo(3)) >= 51) + or (select(2, UnitClass("player")) == "DEATHKNIGHT" and IsDeathKnightTank()) + or (select(2, UnitClass("player")) == "PALADIN" and select(3, GetTalentTabInfo(2)) >= 51) + or (select(2, UnitClass("player")) == "DRUID" and select(3, GetTalentTabInfo(2)) >= 51 and IsDruidTank()) +end + +function bossModPrototype:IsHealer() + return (select(2, UnitClass("player")) == "PALADIN" and select(3, GetTalentTabInfo(1)) >= 51) + or (select(2, UnitClass("player")) == "SHAMAN" and select(3, GetTalentTabInfo(3)) >= 51) + or (select(2, UnitClass("player")) == "DRUID" and select(3, GetTalentTabInfo(3)) >= 51) + or (select(2, UnitClass("player")) == "PRIEST" and select(3, GetTalentTabInfo(3)) < 51) +end + +-- An anti spam function to throttle spammy events (e.g. SPELL_AURA_APPLIED on all group members) +-- @param time the time to wait between two events (optional, default 2.5 seconds) +-- @param id the id to distinguish different events (optional, only necessary if your mod keeps track of two different spam events at the same time) +function DBM:AntiSpam(time, id) + if GetTime() - (id and (self["lastAntiSpam" .. tostring(id)] or 0) or self.lastAntiSpam or 0) > (time or 2.5) then + if id then + self["lastAntiSpam" .. tostring(id)] = GetTime() + else + self.lastAntiSpam = GetTime() + end + return true + else + return false + end +end + + +------------------------- +-- Boss Health Frame -- +------------------------- +function bossModPrototype:SetBossHealthInfo(...) + self.bossHealthInfo = {...} +end + + +----------------------- +-- Announce Object -- +----------------------- +do + local textureCode = " |T%s:12:12|t " + local textureExp = " |T(%S+):12:12|t " + local announcePrototype = {} + local mt = {__index = announcePrototype} + + local cachedColorFunctions = setmetatable({}, {__mode = "kv"}) + + function announcePrototype:Show(...) -- todo: reduce amount of unneeded strings + if not self.option or self.mod.Options[self.option] then + if self.mod.Options.Announce and not DBM.Options.DontSendBossAnnounces and (DBM:GetRaidRank() > 0 or (GetNumRaidMembers() == 0 and GetNumPartyMembers() >= 1)) then + local message = pformat(self.text, ...) + message = message:gsub("|3%-%d%((.-)%)", "%1") -- for |3-id(text) encoding in russian localization + SendChatMessage(("*** %s ***"):format(message), GetNumRaidMembers() > 0 and "RAID_WARNING" or "PARTY") + end + if DBM.Options.DontShowBossAnnounces then return end -- don't show the announces if the spam filter option is set + local colorCode = ("|cff%.2x%.2x%.2x"):format(self.color.r * 255, self.color.g * 255, self.color.b * 255) + local text = ("%s%s%s|r%s"):format( + (DBM.Options.WarningIconLeft and self.icon and textureCode:format(self.icon)) or "", + colorCode, + pformat(self.text, ...), + (DBM.Options.WarningIconRight and self.icon and textureCode:format(self.icon)) or "" + ) + if not cachedColorFunctions[self.color] then + local color = self.color -- upvalue for the function to colorize names, accessing self in the colorize closure is not safe as the color of the announce object might change (it would also prevent the announce from being garbage-collected but announce objects are never destroyed) + cachedColorFunctions[color] = function(cap) + cap = cap:sub(2, -2) + if DBM:GetRaidClass(cap) then + local playerColor = RAID_CLASS_COLORS[DBM:GetRaidClass(cap)] or color + cap = ("|r|cff%.2x%.2x%.2x%s|r|cff%.2x%.2x%.2x"):format(playerColor.r * 255, playerColor.g * 255, playerColor.b * 255, cap, color.r * 255, color.g * 255, color.b * 255) + end + return cap + end + end + text = text:gsub(">.-<", cachedColorFunctions[self.color]) + RaidNotice_AddMessage(RaidWarningFrame, text, ChatTypeInfo["RAID_WARNING"]) -- the color option doesn't work (at least it didn't work during the WotLK beta...todo: check this) + if DBM.Options.ShowWarningsInChat then + text = text:gsub(textureExp, "") -- textures @ chat frame can (and will) distort the font if using certain combinations of UI scale, resolution and font size + if DBM.Options.ShowFakedRaidWarnings then + for i = 1, select("#", GetFramesRegisteredForEvent("CHAT_MSG_RAID_WARNING")) do + local frame = select(i, GetFramesRegisteredForEvent("CHAT_MSG_RAID_WARNING")) + if frame ~= RaidWarningFrame and frame:GetScript("OnEvent") then + frame:GetScript("OnEvent")(frame, "CHAT_MSG_RAID_WARNING", text, UnitName("player"), GetDefaultLanguage("player"), "", UnitName("player"), "", 0, 0, "", 0, 99, "") + end + end + else + self.mod:AddMsg(text, nil) + end + end + PlaySoundFile(DBM.Options.RaidWarningSound) + fireEvent("DBM_Announce", message, self.icon, self.type, self.spellId, self.mod.id, false) + end + end + + function announcePrototype:Schedule(t, ...) + return schedule(t, self.Show, self.mod, self, ...) + end + + function announcePrototype:Cancel(...) + return unschedule(self.Show, self.mod, self, ...) + end + + -- old constructor (no auto-localize) + function bossModPrototype:NewAnnounce(text, color, icon, optionDefault, optionName) + local obj = setmetatable( + { + text = self.localization.warnings[text], + color = DBM.Options.WarningColors[color or 1] or DBM.Options.WarningColors[1], + option = optionName or text, + mod = self, + icon = (type(icon) == "number" and select(3, GetSpellInfo(icon))) or icon, + }, + mt + ) + if optionName == false then + obj.option = nil + else + self:AddBoolOption(optionName or text, optionDefault, "announce") + end + table.insert(self.announces, obj) + return obj + end + + -- new constructor (auto-localized warnings and options, yay!) + local function newAnnounce(self, announceType, spellId, color, icon, optionDefault, optionName, castTime, preWarnTime) + spellName = GetSpellInfo(spellId) or "unknown" + icon = icon or spellId + local text + if announceType == "cast" then + local spellHaste = select(7, GetSpellInfo(53142)) / 10000 -- 53142 = Dalaran Portal, should have 10000 ms cast time + local timer = (select(7, GetSpellInfo(spellId)) or 1000) / spellHaste + text = DBM_CORE_AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, castTime or (timer / 1000)) + elseif announceType == "prewarn" then + if type(preWarnTime) == "string" then + text = DBM_CORE_AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, preWarnTime) + else + text = DBM_CORE_AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, DBM_CORE_SEC_FMT:format(preWarnTime or 5)) + end + elseif announceType == "phase" then + text = DBM_CORE_AUTO_ANNOUNCE_TEXTS[announceType]:format(spellId) + else + text = DBM_CORE_AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName) + end + local obj = setmetatable( -- todo: fix duplicate code + { + text = text, + announceType = announceType, + color = DBM.Options.WarningColors[color or 1] or DBM.Options.WarningColors[1], + option = optionName or text, + mod = self, + icon = (type(icon) == "number" and select(3, GetSpellInfo(icon))) or icon, + }, + mt + ) + if optionName == false then + obj.option = nil + else + self:AddBoolOption(optionName or text, optionDefault, "announce") + end + table.insert(self.announces, obj) + self.localization.options[text] = DBM_CORE_AUTO_ANNOUNCE_OPTIONS[announceType]:format(spellId, spellName) + return obj + end + + function bossModPrototype:NewTargetAnnounce(spellId, color, ...) + return newAnnounce(self, "target", spellId, color or 2, ...) + end + + function bossModPrototype:NewSpellAnnounce(spellId, color, ...) + return newAnnounce(self, "spell", spellId, color or 3, ...) + end + + function bossModPrototype:NewCastAnnounce(spellId, color, castTime, icon, optionDefault, optionName) + return newAnnounce(self, "cast", spellId, color or 3, icon, optionDefault, optionName, castTime) + end + + function bossModPrototype:NewSoonAnnounce(spellId, color, ...) + return newAnnounce(self, "soon", spellId, color or 1, ...) + end + + function bossModPrototype:NewPreWarnAnnounce(spellId, time, color, icon, optionDefault, optionName) + return newAnnounce(self, "prewarn", spellId, color or 1, icon, optionDefault, optionName, nil, time) + end + + function bossModPrototype:NewPhaseAnnounce(phase, color, icon, ...) + return newAnnounce(self, "phase", phase, color or 1, icon or "Interface\\Icons\\Spell_Nature_WispSplode", ...) + end + + function bossModPrototype:NewInterruptAnnounce(spellId, color, ...) + return newAnnounce(self, "interrupt", spellId, color or 3, ...) + end +end + +-------------------- +-- Sound Object -- +-------------------- +do + local soundPrototype = {} + local mt = { __index = soundPrototype } + function bossModPrototype:NewSound(spellId, optionName, optionDefault) + self.numSounds = self.numSounds and self.numSounds + 1 or 1 + local obj = setmetatable( + { + option = optionName or DBM_CORE_AUTO_SOUND_OPTION_TEXT:format(spellId), + mod = self, + }, + mt + ) + if optionName == false then + obj.option = nil + else + self:AddBoolOption(obj.option, optionDefault, "misc") + end + return obj + end + bossModPrototype.NewRunAwaySound = bossModPrototype.NewSound + + function soundPrototype:Play(file) + if not self.option or self.mod.Options[self.option] then + PlaySoundFile(file or "Sound\\Creature\\HoodWolf\\HoodWolfTransformPlayer01.wav") + end + end + + function soundPrototype:Schedule(t, ...) + return schedule(t, self.Play, self.mod, self, ...) + end + + function soundPrototype:Cancel(...) + return unschedule(self.Play, self.mod, self, ...) + end +end + +-------------------- +-- Yell Object -- +-------------------- +do + local yellPrototype = {} + local mt = { __index = yellPrototype } + local function newYell(self, yellType, spellId, yellText, optionDefault, optionName, chatType) + if not spellId and not yellText then + error("NewYell: you must provide either spellId or yellText", 2) + return + end + -- if type(spellId) == "string" and spellId:match("OptionVersion") then + -- print("newYell for: "..yellText.." is using OptionVersion hack. This is depricated") + -- return + -- end + -- local optionVersion + -- if type(optionName) == "number" then + -- optionVersion = optionName + -- optionName = nil + -- end + local displayText + if not yellText then + -- if type(spellId) == "string" and spellId:match("ej%d+") then + -- displayText = DBM_CORE_AUTO_YELL_ANNOUNCE_TEXT[yellType]:format(EJ_GetSectionInfo(string.sub(spellId, 3)) or DBM_CORE_UNKNOWN) + -- else + displayText = DBM_CORE_AUTO_YELL_ANNOUNCE_TEXT[yellType]:format(GetSpellInfo(spellId) or DBM_CORE_UNKNOWN) + -- end + else + displayText = DBM_CORE_AUTO_YELL_ANNOUNCE_TEXT[yellType]:format(GetSpellInfo(spellId), yellText) + end + --Passed spellid as yellText. + --Auto localize spelltext using yellText instead + -- if yellText then --and type(yellText) == "number" + -- end + local obj = setmetatable( + { + text = displayText or yellText, + mod = self, + chatType = chatType, + yellType = yellType + }, + mt + ) + if optionName then + obj.option = optionName + self:AddBoolOption(obj.option, optionDefault, "misc") + elseif not (optionName == false) then + obj.option = "Yell"..(spellId or yellText)..(yellType ~= "yell" and yellType or "")..(optionVersion or "") + self:AddBoolOption(obj.option, optionDefault, "misc") + self.localization.options[obj.option] = DBM_CORE_AUTO_YELL_OPTION[yellType]:format(spellId) + end + return obj + end + + function yellPrototype:Yell(...) + if self.yellType and self.yellType == "position" then return end + if not self.option or self.mod.Options[self.option] then + if self.yellType == "combo" then + SendChatMessage(pformat(self.text, ...), self.chatType or "YELL") + else + SendChatMessage(pformat(self.text, ...), self.chatType or "SAY") + end + end + end + yellPrototype.Show = yellPrototype.Yell + + function yellPrototype:Schedule(t, ...) + return schedule(t, self.Yell, self.mod, self, ...) + end + + function yellPrototype:Countdown(time, numAnnounces, ...) + scheduleCountdown(time, numAnnounces, self.Yell, self.mod, self, ...) + end + + function yellPrototype:Cancel(...) + return unschedule(self.Yell, self.mod, self, ...) + end + + function bossModPrototype:NewYell(...) + return newYell(self, "yell", ...) + end + + function bossModPrototype:NewShortYell(...) + return newYell(self, "shortyell", ...) + end + + function bossModPrototype:NewCountYell(...) + return newYell(self, "count", ...) + end + + function bossModPrototype:NewFadesYell(...) + return newYell(self, "fade", ...) + end + + function bossModPrototype:NewShortFadesYell(...) + return newYell(self, "shortfade", ...) + end + + function bossModPrototype:NewIconFadesYell(...) + return newYell(self, "iconfade", ...) + end + + function bossModPrototype:NewPosYell(...) + return newYell(self, "position", ...) + end + + function bossModPrototype:NewComboYell(...) + return newYell(self, "combo", ...) + end +end + +------------------------------ +-- Special Warning Object -- +------------------------------ +do + local frame = CreateFrame("Frame", nil, UIParent) + local font = frame:CreateFontString(nil, "OVERLAY", "ZoneTextFont") + frame:SetMovable(1) + frame:SetWidth(1) + frame:SetHeight(1) + frame:SetFrameStrata("HIGH") + frame:SetClampedToScreen() + frame:Hide() + font:SetWidth(1024) + font:SetHeight(0) + font:SetPoint("CENTER", 0, 0) + + local moving + local specialWarningPrototype = {} + local mt = {__index = specialWarningPrototype} + + function DBM:UpdateSpecialWarningOptions() + frame:ClearAllPoints() + frame:SetPoint(DBM.Options.SpecialWarningPoint, UIParent, DBM.Options.SpecialWarningPoint, DBM.Options.SpecialWarningX, DBM.Options.SpecialWarningY) + font:SetFont(DBM.Options.SpecialWarningFont, DBM.Options.SpecialWarningFontSize, "THICKOUTLINE") + font:SetTextColor(unpack(DBM.Options.SpecialWarningFontColor)) + end + + local shakeFrame = CreateFrame("Frame") + shakeFrame:SetScript("OnUpdate", function(self, elapsed) + self.timer = self.timer - elapsed + end) + shakeFrame:Hide() + + frame:SetScript("OnUpdate", function(self, elapsed) + self.timer = self.timer - elapsed + if self.timer >= 3 and self.timer <= 4 then + LowHealthFrame:SetAlpha(self.timer - 3) + elseif self.timer <= 2 then + frame:SetAlpha(self.timer/2) + elseif self.timer <= 0 then + frame:Hide() + end + end) + + function specialWarningPrototype:Show(...) + if DBM.Options.ShowSpecialWarnings and (not self.option or self.mod.Options[self.option]) and not moving and frame then + font:SetText(pformat(self.text, ...)) + LowHealthFrame:Show() + LowHealthFrame:SetAlpha(1) + frame:Show() + frame:SetAlpha(1) + frame.timer = 5 + if self.sound then + PlaySoundFile(DBM.Options.SpecialWarningSound) + end + fireEvent("DBM_Announce", message, self.icon, self.type, self.spellId, self.mod.id, false) + end + end + + function specialWarningPrototype:Schedule(t, ...) + return schedule(t, self.Show, self.mod, self, ...) + end + + function specialWarningPrototype:Cancel(t, ...) + return unschedule(self.Show, self.mod, self, ...) + end + + function bossModPrototype:NewSpecialWarning(text, optionDefault, optionName, noSound, runSound) + local obj = setmetatable( + { + text = self.localization.warnings[text], + option = optionName or text, + mod = self, + sound = not noSound, + }, + mt + ) + if optionName == false then + obj.option = nil + else + self:AddBoolOption(optionName or text, optionDefault, "announce") + end + table.insert(self.specwarns, obj) + return obj + end + + local function newSpecialWarning(self, announceType, spellId, stacks, optionDefault, optionName, noSound, runSound) + spellName = GetSpellInfo(spellId) or "unknown" + local text = DBM_CORE_AUTO_SPEC_WARN_TEXTS[announceType]:format(spellName) + local obj = setmetatable( -- todo: fix duplicate code + { + text = text, + announceType = announceType, + option = optionName or text, + mod = self, + sound = not noSound, + }, + mt + ) + if optionName == false then + obj.option = nil + else + self:AddBoolOption(optionName or text, optionDefault, "announce") -- todo cleanup core code from that indexing type using options[text] is very bad!!! ;) + end + table.insert(self.specwarns, obj) + if announceType == "stack" then + self.localization.options[text] = DBM_CORE_AUTO_SPEC_WARN_OPTIONS[announceType]:format(stacks or 3, spellId) + else + self.localization.options[text] = DBM_CORE_AUTO_SPEC_WARN_OPTIONS[announceType]:format(spellId) + end + return obj + end + + function bossModPrototype:NewSpecialWarningSpell(text, optionDefault, ...) + return newSpecialWarning(self, "spell", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningDispel(text, optionDefault, ...) + return newSpecialWarning(self, "dispel", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningInterupt(text, optionDefault, ...) + return newSpecialWarning(self, "interupt", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningYou(text, optionDefault, ...) + return newSpecialWarning(self, "you", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningTarget(text, optionDefault, ...) + return newSpecialWarning(self, "target", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningClose(text, optionDefault, ...) + return newSpecialWarning(self, "close", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningMove(text, optionDefault, ...) + return newSpecialWarning(self, "move", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningRun(text, optionDefault, ...) + return newSpecialWarning(self, "run", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningCast(text, optionDefault, ...) + return newSpecialWarning(self, "cast", text, nil, optionDefault, ...) + end + + function bossModPrototype:NewSpecialWarningStack(text, optionDefault, stacks, ...) + return newSpecialWarning(self, "stack", text, stacks, optionDefault, ...) + end + + do + local anchorFrame + local function moveEnd() + moving = false + anchorFrame:Hide() + frame.timer = 1.5 -- fade out + frame:SetFrameStrata("HIGH") + DBM:Unschedule(moveEnd) + DBM.Bars:CancelBar(DBM_CORE_MOVE_SPECIAL_WARNING_BAR) + end + + function DBM:MoveSpecialWarning() + if not anchorFrame then + anchorFrame = CreateFrame("Frame", nil, frame) + anchorFrame:SetWidth(32) + anchorFrame:SetHeight(32) + anchorFrame:EnableMouse(true) + anchorFrame:SetPoint("CENTER", 0, -32) + anchorFrame:RegisterForDrag("LeftButton") + anchorFrame:SetClampedToScreen() + anchorFrame:Hide() + local texture = anchorFrame:CreateTexture() + texture:SetTexture("Interface\\Addons\\DBM-GUI\\textures\\dot.blp") + texture:SetPoint("CENTER", anchorFrame, "CENTER", 0, 0) + texture:SetWidth(32) + texture:SetHeight(32) + anchorFrame:SetScript("OnDragStart", function() + frame:StartMoving() + DBM:Unschedule(moveEnd) + DBM.Bars:CancelBar(DBM_CORE_MOVE_SPECIAL_WARNING_BAR) + end) + anchorFrame:SetScript("OnDragStop", function() + frame:StopMovingOrSizing() + local point, _, _, xOfs, yOfs = frame:GetPoint(1) + DBM.Options.SpecialWarningPoint = point + DBM.Options.SpecialWarningX = xOfs + DBM.Options.SpecialWarningY = yOfs + DBM:Schedule(15, moveEnd) + DBM.Bars:CreateBar(15, DBM_CORE_MOVE_SPECIAL_WARNING_BAR) + end) + end + if anchorFrame:IsShown() then + moveEnd() + else + moving = true + anchorFrame:Show() + self:Schedule(15, moveEnd) + DBM.Bars:CreateBar(15, DBM_CORE_MOVE_SPECIAL_WARNING_BAR) + font:SetText(DBM_CORE_MOVE_SPECIAL_WARNING_TEXT) + frame:Show() + frame:SetFrameStrata("TOOLTIP") + frame:SetAlpha(1) + frame.timer = math.huge + end + end + end + + local function testWarningEnd() + frame:SetFrameStrata("HIGH") + end + + function DBM:ShowTestSpecialWarning(text) + if moving then + return + end + font:SetText(DBM_CORE_MOVE_SPECIAL_WARNING_TEXT) + frame:Show() + frame:SetAlpha(1) + frame:SetFrameStrata("TOOLTIP") + self:Unschedule(testWarningEnd) + self:Schedule(3, testWarningEnd) + frame.timer = 3 + end +end + + +-------------------- +-- Timer Object -- +-------------------- +do + local timerPrototype = {} + local mt = {__index = timerPrototype} + + function timerPrototype:Start(timer, ...) + if timer and type(timer) ~= "number" then + return self:Start(nil, timer, ...) -- first argument is optional! + end + if not self.option or self.mod.Options[self.option] then + local timer = timer and ((timer > 0 and timer) or self.timer + timer) or self.timer + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:CreateBar(timer, id, self.icon) + if not bar then + return false, "error" -- creating the timer failed somehow, maybe hit the hard-coded timer limit of 15 + end + if self.type and not self.text then + msg = pformat(self.mod:GetLocalizedTimerText(self.type, self.spellId), ...) + else + msg = pformat(self.text, ...) + end + bar:SetText(msg) + if not msg then + msg = "Something went wrong" + end + fireEvent("DBM_TimerStart", id, msg, timer, self.icon, self.type, self.spellId, colorId) + table.insert(self.startedTimers, id) + self.mod:Unschedule(removeEntry, self.startedTimers, id) + self.mod:Schedule(timer, removeEntry, self.startedTimers, id) + self.mod:Schedule(timer, fireEvent, "DBM_Announce", message, self.icon, self.type, self.spellId, self.mod.id, false) + return bar + else + return false, "disabled" + end + end + timerPrototype.Show = timerPrototype.Start + + function timerPrototype:Schedule(t, ...) + return schedule(t, self.Start, self.mod, self, ...) + end + + function timerPrototype:Unschedule(t, ...) + return unschedule(self.Start, self.mod, self, ...) + end + + function timerPrototype:Stop(...) + fireEvent("DBM_Announce", message, self.icon, self.type, self.spellId, self.mod.id, false) + local barGroup = self.mod.barGroup or DBM.Bars; + if select("#", ...) == 0 then + for i = #self.startedTimers, 1, -1 do + fireEvent("DBM_TimerStop", self.startedTimers[i]) + barGroup:CancelBar(self.startedTimers[i]) + self.startedTimers[i] = nil + end + else + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + for i = #self.startedTimers, 1, -1 do + if self.startedTimers[i] == id then + fireEvent("DBM_TimerStop", id, guid) + barGroup:CancelBar(id) + table.remove(self.startedTimers, i) + end + end + end + end + + function timerPrototype:Cancel(...) + self:Stop(...) + self:Unschedule(...) + end + + function timerPrototype:GetTime(...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + return bar and (bar.totalTime - bar.timer) or 0, (bar and bar.totalTime) or 0 + end + + function timerPrototype:IsStarted(...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + return bar and true + end + + function timerPrototype:SetTimer(timer) + self.timer = timer + end + + function timerPrototype:Update(elapsed, totalTime, ...) + if self:GetTime(...) == 0 then + self:Start(totalTime, ...) + end + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + fireEvent("DBM_TimerUpdate", id, elapsed, totalTime) + return barGroup:UpdateBar(id, elapsed, totalTime) + end + + function timerPrototype:AddTime(extendAmount, ...) + -- if DBM.Options.DontShowBossTimers then return end + if self:GetTime(...) == 0 then + return self:Start(extendAmount, ...) + else + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local bar = DBM.Bars:GetBar(id) + if bar then + local elapsed, total = (bar.totalTime - bar.timer), bar.totalTime + if elapsed and total then + -- if bar.countdown then --Unused in our 3.3.5 version + -- DBM:Unschedule(playCountSound, id) + -- if not bar.fade then--Don't start countdown voice if it's faded bar + -- local newRemaining = (total+extendAmount) - elapsed + -- playCountdown(id, newRemaining, bar.countdown, bar.countdownMax)--timerId, timer, voice, count + -- DBM:Debug("Updating a countdown after a timer AddTime call for timer ID:"..id) + -- end + -- end + fireEvent("DBM_TimerUpdate", id, elapsed, total+extendAmount) + return DBM.Bars:UpdateBar(id, elapsed, total+extendAmount) + end + end + end + end + + function timerPrototype:RemoveTime(reduceAmount, ...) + -- if DBM.Options.DontShowBossTimers then return end + if self:GetTime(...) == 0 then + return--Do nothing + else + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local bar = DBM.Bars:GetBar(id) + if bar then + local elapsed, total = (bar.totalTime - bar.timer), bar.totalTime + if elapsed and total then + local newRemaining = (total-reduceAmount) - elapsed + if newRemaining > 0 then + -- if bar.countdown and newRemaining > 2 then--Unused in our 3.3.5 version + -- DBM:Unschedule(playCountSound, id) + -- if not bar.fade then--Don't start countdown voice if it's faded bar + -- playCountdown(id, newRemaining, bar.countdown, bar.countdownMax)--timerId, timer, voice, count + -- DBM:Debug("Updating a countdown after a timer RemoveTime call for timer ID:"..id) + -- end + -- end + fireEvent("DBM_TimerUpdate", id, elapsed, total-reduceAmount) + return DBM.Bars:UpdateBar(id, elapsed, total-reduceAmount) + else--New remaining less than 0 + if bar.countdown then + DBM:Unschedule(playCountSound, id) + end + fireEvent("DBM_TimerStop", id) + return DBM.Bars:CancelBar(id) + end + end + end + end + end + + function timerPrototype:UpdateIcon(icon, ...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + if bar then + return bar:SetIcon((type(icon) == "number" and select(3, GetSpellInfo(icon))) or icon) + end + end + + function timerPrototype:UpdateName(name, ...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + if bar then + return bar:SetText(name) + end + end + + function timerPrototype:SetColor(c, ...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + if bar then + return bar:SetColor(c) + end + end + + function timerPrototype:DisableEnlarge(...) + local id = self.id..pformat((("\t%s"):rep(select("#", ...))), ...) + local barGroup = self.mod.barGroup or DBM.Bars; + local bar = barGroup:GetBar(id) + if bar then + bar.small = true + end + end + + function timerPrototype:AddOption(optionDefault, optionName) + if optionName ~= false then + self.option = optionName or self.id + self.mod:AddBoolOption(self.option, optionDefault, "timer") + end + end + + function bossModPrototype:NewTimer(timer, name, icon, optionDefault, optionName, r, g, b) + local icon = type(icon) == "number" and select(3, GetSpellInfo(icon)) or icon + local obj = setmetatable( + { + text = self.localization.timers[name], + timer = timer, + id = name, + icon = icon, + r = r, + g = g, + b = b, + startedTimers = {}, + mod = self, + }, + mt + ) + obj:AddOption(optionDefault, optionName) + table.insert(self.timers, obj) + return obj + end + + -- new constructor for the new auto-localized timer types + -- note that the function might look unclear because it needs to handle different timer types, especially achievement timers need special treatment + -- todo: disable the timer if the player already has the achievement and when the ACHIEVEMENT_EARNED event is fired + -- problem: heroic/normal achievements :[ + -- local achievementTimers = {} + local function newTimer(self, timerType, timer, spellId, timerText, optionDefault, optionName, texture, r, g, b) + -- new argument timerText is optional (usually only required for achievement timers as they have looooong names) + if type(timerText) == "boolean" or type(optionDefault) == "string" then -- check if the argument was skipped + return newTimer(self, timerType, timer, spellId, nil, timerText, optionDefault, optionName, texture, r, g, b) + end + local spellName, icon + if timerType == "achievement" then + spellName = select(2, GetAchievementInfo(spellId)) + icon = type(texture) == "number" and select(10, GetAchievementInfo(texture)) or texture or spellId and select(10, GetAchievementInfo(spellId)) +-- if optionDefault == nil then +-- local completed = select(4, GetAchievementInfo(spellId)) +-- optionDefault = not completed +-- end + else + spellName = GetSpellInfo(spellId or 0) + if spellName then + icon = type(texture) == "number" and select(3, GetSpellInfo(texture)) or texture or spellId and select(3, GetSpellInfo(spellId)) + else + icon = nil + end + end + spellName = spellName or tostring(spellId) + local id = "Timer"..(spellId or 0)..self.id..#self.timers + local obj = setmetatable( + { + text = self.localization.timers[timerText], + type = timerType, + spellId = spellId, + timer = timer, + id = id, + icon = icon, + r = r, + g = g, + b = b, + startedTimers = {}, + mod = self, + }, + mt + ) + obj:AddOption(optionDefault, optionName) + table.insert(self.timers, obj) + -- todo: move the string creation to the GUI with SetFormattedString... + if timerType == "achievement" then + self.localization.options[id] = DBM_CORE_AUTO_TIMER_OPTIONS[timerType]:format(GetAchievementLink(spellId):gsub("%[(.+)%]", "%1")) + elseif timerType == "phase" then + self.localization.options[id] = DBM_CORE_AUTO_TIMER_OPTIONS[timerType]:format(spellId, timerText) + else + self.localization.options[id] = DBM_CORE_AUTO_TIMER_OPTIONS[timerType]:format(spellId, spellName) + end + return obj + end + + function bossModPrototype:NewTargetTimer(...) + return newTimer(self, "target", ...) + end + + function bossModPrototype:NewBuffActiveTimer(...) + return newTimer(self, "active", ...) + end + + function bossModPrototype:NewCastTimer(timer, ...) + if timer > 1000 then -- hehe :) best hack in DBM. This makes the first argument optional, so we can omit it to use the cast time from the spell id ;) + local spellId = timer + timer = select(7, GetSpellInfo(spellId)) or 1000 -- GetSpellInfo takes YOUR spell haste into account...WTF? + local spellHaste = select(7, GetSpellInfo(53142)) / 10000 -- 53142 = Dalaran Portal, should have 10000 ms cast time + timer = timer / spellHaste -- calculate the real cast time of the spell... + return self:NewCastTimer(timer / 1000, spellId, ...) + end + return newTimer(self, "cast", timer, ...) + end + + function bossModPrototype:NewCDTimer(...) + return newTimer(self, "cd", ...) + end + + function bossModPrototype:NewNextTimer(...) + return newTimer(self, "next", ...) + end + + function bossModPrototype:NewAchievementTimer(...) + return newTimer(self, "achievement", ...) + end + + function bossModPrototype:NewPhaseTimer(...) + return newTimer(self, "phase", ...) + end + + function bossModPrototype:GetLocalizedTimerText(timerType, spellId) + local spellName + if timerType == "achievement" then + spellName = select(2, GetAchievementInfo(spellId)) + else + spellName = GetSpellInfo(spellId) + end + return pformat(DBM_CORE_AUTO_TIMER_TEXTS[timerType], spellName) + end +end + + +--------------------- +-- Enrage Object -- +--------------------- +do + local enragePrototype = {} + local mt = {__index = enragePrototype} + + function enragePrototype:Start(timer) + timer = timer or self.timer or 600 + timer = timer <= 0 and self.timer - timer or timer + self.bar:SetTimer(timer) + self.bar:Start() + if timer > 660 then self.warning1:Schedule(timer - 600, 10, DBM_CORE_MIN) end + if timer > 300 then self.warning1:Schedule(timer - 300, 5, DBM_CORE_MIN) end + if timer > 180 then self.warning2:Schedule(timer - 180, 3, DBM_CORE_MIN) end + if timer > 60 then self.warning2:Schedule(timer - 60, 1, DBM_CORE_MIN) end + if timer > 30 then self.warning2:Schedule(timer - 30, 30, DBM_CORE_SEC) end + if timer > 10 then self.warning2:Schedule(timer - 10, 10, DBM_CORE_SEC) end + end + + function enragePrototype:Schedule(t) + return self.owner:Schedule(t, self.Start, self) + end + + function enragePrototype:Cancel() + self.owner:Unschedule(self.Start, self) + self.warning1:Cancel() + self.warning2:Cancel() + self.bar:Stop() + end + enragePrototype.Stop = enragePrototype.Cancel + + function bossModPrototype:NewBerserkTimer(timer, text, barText, barIcon, spellID) + if type(text) == "number" then + spellID = text; + text = nil; + end + spellID = spellID or 26662; + local spellName,_,spellIcon = GetSpellInfo(spellID); + timer = timer or 600 + local warning1 = self:NewAnnounce(text or DBM_CORE_GENERIC_WARNING_BERSERK, 1, nil, "warning_berserk", false) + local warning2 = self:NewAnnounce(text or DBM_CORE_GENERIC_WARNING_BERSERK, 4, nil, "warning_berserk", false) + local bar = self:NewTimer(timer or 600, barText or DBM_CORE_GENERIC_TIMER_BERSERK, barIcon or spellIcon or spellID, nil, "timer_berserk") + local obj = setmetatable( + { + warning1 = warning1, + warning2 = warning2, + bar = bar, + timer = timer, + owner = self + }, + mt + ) + self.localization.options.timer_berserk = DBM_CORE_OPTION_TIMER_BERSERK_CUSTOM:format(spellID, spellName or "Berserk"); + return obj + end +end + + +--------------- +-- Options -- +--------------- +function bossModPrototype:AddBoolOption(name, default, cat, func) + cat = cat or "misc" + self.Options[name] = (default == nil) or default + self:SetOptionCategory(name, cat) + if func then + self.optionFuncs = self.optionFuncs or {} + self.optionFuncs[name] = func + end +end + +function bossModPrototype:RemoveOption(name) + self.Options[name] = nil + for i, options in pairs(self.optionCategories) do + removeEntry(options, name) + if #options == 0 then + self.optionCategories[i] = nil + removeEntry(self.categorySort, i) + end + end + if self.optionFuncs then + self.optionFuncs[name] = nil + end +end + +function bossModPrototype:AddSliderOption(name, minValue, maxValue, valueStep, default, cat, func) + cat = cat or "misc" + self.Options[name] = default or 0 + self:SetOptionCategory(name, cat) + self.sliders = self.sliders or {} + self.sliders[name] = { + minValue = minValue, + maxValue = maxValue, + valueStep = valueStep, + } + if func then + self.optionFuncs = self.optionFuncs or {} + self.optionFuncs[name] = func + end +end + +function bossModPrototype:AddButton(name, onClick, cat, func) + cat = cat or misc + self:SetOptionCategory(name, cat) + self.buttons = self.buttons or {} + self.buttons[name] = onClick + if func then + self.optionFuncs = self.optionFuncs or {} + self.optionFuncs[name] = func + end +end + +function bossModPrototype:AddDropdownOption(name, options, default, cat, func) + cat = cat or "misc" + self.Options[name] = default + self:SetOptionCategory(name, cat) + self.dropdowns = self.dropdowns or {} + self.dropdowns[name] = options + if func then + self.optionFuncs = self.optionFuncs or {} + self.optionFuncs[name] = func + end +end + +function bossModPrototype:AddOptionSpacer(cat) + cat = cat or "misc" + if self.optionCategories[cat] then + table.insert(self.optionCategories[cat], DBM_OPTION_SPACER) + end +end + +function bossModPrototype:AddAnnounceSpacer() + return self:AddOptionSpacer("announce") +end + +function bossModPrototype:AddTimerSpacer() + return self:AddOptionSpacer("timer") +end + + +function bossModPrototype:SetOptionCategory(name, cat) + for _, options in pairs(self.optionCategories) do + removeEntry(options, name) + end + if not self.optionCategories[cat] then + self.optionCategories[cat] = {} + table.insert(self.categorySort, cat) + end + table.insert(self.optionCategories[cat], name) +end + + +-------------- +-- Combat -- +-------------- +function bossModPrototype:RegisterCombat(cType, ...) + if cType then + cType = cType:lower() + end + local info = { + type = cType, + mob = self.creatureId, + name = self.localization.general.name or self.id, + msgs = (cType ~= "combat") and {...}, + mod = self + } + if self.multiMobPullDetection then + info.multiMobPullDetection = self.multiMobPullDetection + end + local addedKillMobs = false + for i = 1, select("#", ...) do + local v = select(i, ...) + if type(v) == "number" then + info.killMobs = info.killMobs or {} + info.killMobs[select(i, ...)] = true + addedKillMobs = true + end + end + if not addedKillMobs and self.multiMobPullDetection then + for i, v in ipairs(self.multiMobPullDetection) do + info.killMobs = info.killMobs or {} + info.killMobs[v] = true + end + end + self.combatInfo = info + if not self.zones then return end + for i, v in ipairs(self.zones) do + combatInfo[v] = combatInfo[v] or {} + table.insert(combatInfo[v], info) + end +end + +-- needs to be called _AFTER_ RegisterCombat +function bossModPrototype:RegisterKill(msgType, ...) + if cType then + cType = cType:lower() + end + if not self.combatInfo then + return + end + self.combatInfo.killType = msgType + self.combatInfo.killMsgs = {} + for i = 1, select("#", ...) do + local v = select(i, ...) + self.combatInfo.killMsgs[v] = true + end +end + +-- needs to be called _AFTER_ RegisterCombat +function bossModPrototype:SetDetectCombatInVehicle(flag) + if not self.combatInfo then + return + end + self.combatInfo.noCombatInVehicle = not flag +end + +function bossModPrototype:IsInCombat() + return self.inCombat +end + +function bossModPrototype:SetMinCombatTime(t) + self.minCombatTime = t +end + +-- needs to be called after RegisterCombat +function bossModPrototype:SetWipeTime(t) + self.combatInfo.wipeTimer = t +end + +function bossModPrototype:GetBossHPString(cId) + local idType = (GetNumRaidMembers() == 0 and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + local unitId = ((i == 0) and "target") or idType..i.."target" + local guid = UnitGUID(unitId) + if guid and tonumber(guid:sub(9, 12), 16) == cId then + return math.floor(UnitHealth(unitId)/UnitHealthMax(unitId) * 100).."%" + end + end + return DBM_CORE_UNKNOWN +end + +function bossModPrototype:GetHP() + return self:GetBossHPString((self.combatInfo and self.combatInfo.mob) or self.creatureId) +end + +function bossModPrototype:IsWipe() + local wipe = true + local uId = ((GetNumRaidMembers() == 0) and "party") or "raid" + for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do + local id = (i == 0 and "player") or uId..i + if UnitAffectingCombat(id) and not UnitIsDeadOrGhost(id) then + wipe = false + break + end + end + return wipe +end + + + +----------------------- +-- Synchronization -- +----------------------- +function bossModPrototype:SendSync(event, arg) + event = event or "" + arg = arg or "" + local str = ("%s\t%s\t%s\t%s"):format(self.id, self.revision or 0, event, arg) + local spamId = self.id..event..arg + local time = GetTime() + if not modSyncSpam[spamId] or (time - modSyncSpam[spamId]) > 2.5 then + self:ReceiveSync(event, arg, nil, self.revision or 0) + sendSync("DBMv4-Mod", str) + end +end + +function bossModPrototype:ReceiveSync(event, arg, sender, revision) + local spamId = self.id..event..arg + local time = GetTime() + if (not modSyncSpam[spamId] or (time - modSyncSpam[spamId]) > 2.5) and self.OnSync and (not (self.blockSyncs and sender)) and (not sender or (not self.minSyncRevision or revision >= self.minSyncRevision)) then + modSyncSpam[spamId] = time + self:OnSync(event, arg, sender) + end +end + +function bossModPrototype:SetMinSyncRevision(revision) + self.minSyncRevision = revision +end + + +----------------- +-- Scheduler -- +----------------- +function bossModPrototype:Schedule(t, f, ...) + return schedule(t, f, self, ...) +end + +function bossModPrototype:Unschedule(f, ...) + return unschedule(f, self, ...) +end + +function bossModPrototype:ScheduleMethod(t, method, ...) + if not self[method] then + error(("Method %s does not exist"):format(tostring(method)), 2) + end + return self:Schedule(t, self[method], self, ...) +end +bossModPrototype.ScheduleEvent = bossModPrototype.ScheduleMethod + +function bossModPrototype:UnscheduleMethod(method, ...) + if not self[method] then + error(("Method %s does not exist"):format(tostring(method)), 2) + end + return self:Unschedule(self[method], self, ...) +end +bossModPrototype.UnscheduleEvent = bossModPrototype.UnscheduleMethod + + +------------- +-- Icons -- +------------- +function bossModPrototype:SetIcon(target, icon, timer) + if DBM.Options.DontSetIcons or not enableIcons or DBM:GetRaidRank() == 0 then + return + end + icon = icon and icon >= 0 and icon <= 8 and icon or 8 + local oldIcon = self:GetIcon(target) or 0 + SetRaidTarget(DBM:GetRaidUnitId(target), icon) + self:UnscheduleMethod("SetIcon", target) + if timer then + self:ScheduleMethod(timer, "RemoveIcon", target) + if oldIcon then + self:ScheduleMethod(timer + 1, "SetIcon", target, oldIcon) + end + end +end + +function bossModPrototype:GetIcon(target) + return GetRaidTargetIndex(DBM:GetRaidUnitId(target)) +end + +function bossModPrototype:RemoveIcon(target, timer) + return self:SetIcon(target, 0, timer) +end + +function bossModPrototype:ClearIcons() + if GetNumRaidMembers() > 0 then + for i = 1, GetNumRaidMembers() do + if UnitExists("raid"..i) and GetRaidTargetIndex("raid"..i) then + SetRaidTarget("raid"..i, 0) + end + end + else + for i = 1, GetNumPartyMembers() do + if UnitExists("party"..i) and GetRaidTargetIndex("party"..i) then + SetRaidTarget("party"..i, 0) + end + end + end +end + +----------------------- +-- Model Functions -- +----------------------- +function bossModPrototype:SetModelScale(scale) + self.modelScale = scale +end + +function bossModPrototype:SetModelOffset(x, y, z) + self.modelOffsetX = x + self.modelOffsetY = y + self.modelOffsetZ = z +end + +function bossModPrototype:SetModelRotation(r) + self.modelRotation = r +end + +function bossModPrototype:SetModelMoveSpeed(v) + self.modelMoveSpeed = v +end + +function bossModPrototype:SetModelID(id) + self.modelId = id +end + +function bossModPrototype:EnableModel() + self.modelEnabled = true +end + +function bossModPrototype:DisableModel() + self.modelEnabled = nil +end + +function bossModPrototype:GetHealth(unit) + local hp = UnitHealth(unit) / UnitHealthMax(unit) * 100 + return hp +end + + +-------------------- +-- Localization -- +-------------------- +function bossModPrototype:GetLocalizedStrings() + return self.localization.miscStrings +end + +-- Not really good, needs a few updates +do + local modLocalizations = {} + local modLocalizationPrototype = {} + local mt = {__index = modLocalizationPrototype} + local returnKey = {__index = function(t, k) return k end} + local defaultCatLocalization = { + __index = setmetatable({ + timer = DBM_CORE_OPTION_CATEGORY_TIMERS, + announce = DBM_CORE_OPTION_CATEGORY_WARNINGS, + misc = DBM_CORE_OPTION_CATEGORY_MISC + }, returnKey) + } + local defaultTimerLocalization = { + __index = setmetatable({ + timer_berserk = DBM_CORE_GENERIC_TIMER_BERSERK, + TimerSpeedKill = DBM_CORE_ACHIEVEMENT_TIMER_SPEED_KILL + }, returnKey) + } + local defaultAnnounceLocalization = { + __index = setmetatable({ + warning_berserk = DBM_CORE_GENERIC_WARNING_BERSERK + }, returnKey) + } + local defaultOptionLocalization = { + __index = setmetatable({ + timer_berserk = DBM_CORE_OPTION_TIMER_BERSERK, + HealthFrame = DBM_CORE_OPTION_HEALTH_FRAME + }, returnKey) + } + local defaultMiscLocalization = { + __index = function(t, k) + return t.misc.general[k] or t.misc.options[k] or t.misc.warnings[k] or t.misc.timers[k] or t.misc.cats[k] or k + end + } + + function modLocalizationPrototype:SetGeneralLocalization(t) + for i, v in pairs(t) do + self.general[i] = v + end + end + + function modLocalizationPrototype:SetWarningLocalization(t) + for i, v in pairs(t) do + self.warnings[i] = v + end + end + + function modLocalizationPrototype:SetTimerLocalization(t) + for i, v in pairs(t) do + self.timers[i] = v + end + end + + function modLocalizationPrototype:SetOptionLocalization(t) + for i, v in pairs(t) do + self.options[i] = v + end + end + + function modLocalizationPrototype:SetOptionCatLocalization(t) + for i, v in pairs(t) do + self.cats[i] = v + end + end + + function modLocalizationPrototype:SetMiscLocalization(t) + for i, v in pairs(t) do + self.miscStrings[i] = v + end + end + + function DBM:CreateModLocalization(name) + local obj = { + general = setmetatable({}, returnKey), + warnings = setmetatable({}, defaultAnnounceLocalization), + options = setmetatable({}, defaultOptionLocalization), + timers = setmetatable({}, defaultTimerLocalization), + miscStrings = setmetatable({}, defaultMiscLocalization), + cats = setmetatable({}, defaultCatLocalization), + } + obj.miscStrings.misc = obj + setmetatable(obj, mt) + modLocalizations[name] = obj + return obj + end + + function DBM:GetModLocalization(name) + return modLocalizations[name] or self:CreateModLocalization(name) + end +end + diff --git a/DBM-Core/DBM-Core.toc b/DBM-Core/DBM-Core.toc new file mode 100644 index 0000000..536ca90 --- /dev/null +++ b/DBM-Core/DBM-Core.toc @@ -0,0 +1,29 @@ +## Interface: 30300 +## Title:|cffffd200Deadly Boss Mods|r |cff69ccf0Core|r +## Title-zhCN: |cffffd200Deadly Boss Mods|r |cff69ccf0核心|r +## Title-ruRU:|cffffd200Deadly Boss Mods|r |cff69ccf0Основной|r +## Title-zhTW:|cffffd200Deadly Boss Mods|r |cff69ccf0核心|r +## Title-koKR:|cffffd200Deadly Boss Mods|r |cff69ccf0핵심 모듈|r +## Title-esES:|cffffd200Deadly Boss Mods|r |cff69ccf0Proceso|r +## Title-esMX:|cffffd200Deadly Boss Mods|r |cff69ccf0Proceso|r +## Notes: Deadly Boss Mods - heavily modified to suit Project Ascension raid fights. +## SavedVariablesPerCharacter: DBM_SavedOptions, DBT_SavedOptions +## LoadOnDemand: 0 +## DefaultState: enabled +## Author: Tandanu and Nitram, updated by Junior and Szyler. +## X-Website: https://discord.gg/4ZHfgskSvM +localization.en.lua +localization.de.lua +localization.cn.lua +localization.ru.lua +localization.fr.lua +localization.tw.lua +localization.kr.lua +localization.es.lua +DBT-Template.xml +DBT.lua +DBM-Core.lua +DBM-Arrow.lua +DBM-RangeCheck.lua +DBM-BossHealth.lua +DBM-BossHealth.xml \ No newline at end of file diff --git a/DBM-Core/DBM-HelpFunctions.lua b/DBM-Core/DBM-HelpFunctions.lua new file mode 100644 index 0000000..bfeea0c --- /dev/null +++ b/DBM-Core/DBM-HelpFunctions.lua @@ -0,0 +1,569 @@ +--[[ + ******************************************************************** + ******************************************************************** + Help Constants at top + + Help functions: + DBM_GetGroupNr(name) + DBM_GetRaidId(name) + DBM_GetClass(name) + DBM_GetRank(name) + DBM_GetSmoothColor(percent) + DBM_GetTextClassColor(class) + DBM_GetColorText(r,g,b) + DBM_StringIcon(icon) + DBM_StringTexture(texture) + DBM_GetTextReactionColor(reaction) + DBM_GetRealBossName(boss) + DBM_GetMarkNameFromNumber(marknr) + DBM_round(tal) + DBM_UnitHealthPercent(unitid,CountOneHpAsZero = false) + DBM_UnitPowerPercent(unitid) + DBM_PlaySoundFile(file) + DBM_UnitClassification(uid) + DBM_BoostPlaySound(file,boost,duration); + DBM_CheckForBuff(buffname, unit) + DBM_CheckForDebuff(buffname, unit) + DBM_GetBuffText(buffindex, unit) + DBM_GetDeBuffText(buffindex, unit) + DBM_band(mask,...) bitands all args with mask and return true or false + DBM_bor(b1,...) bitors all args + DBM_linebreakStr(str,b,tryspace) tryspace if try to break the line at spaces isntead + DBM_SetTrue(varname) + DBM_SetFalse(varname) + DBM_SearchBags(itemname) + DBM_SplitItemToEmptySlots(bag,item,stacksize) + DBM_FormatMoney(money) + DBM_FlagsColor(flags) + DBM_CPUWarning() + DBM_NoneEnglish() + DBM_CreateExtraMenuFrames(nrFrames,nrButtons) + + Sync functions: + DBM_GetPlayerMapPosition(uid) + + ******************************************************************** + ******************************************************************** +]]-- + +DBM_c_w = "|cFFFFFFFF"; +DBM_c_r = "|cFFFF0000"; +DBM_c_g = "|cFF00FF00"; +DBM_c_dg = "|cFF007F00"; +DBM_c_b = "|cFF0000FF"; +DBM_c_lb = "|cFF66AAFF"; +DBM_c_t = "|cFF00FFFF"; +DBM_c_y = "|cFFFFFF00"; +DBM_c_purple = "|cFFFF00FF"; +DBM_c_p = "|cFF8888CC"; +DBM_c_v = "|cFF4444CC"; +DBM_c_tt = "|cFFFED100"; +DBM_c_bronze = "|cFFFF8800"; +DBM_c_black = "|cFF000000"; +DBM_c_grey = "|cFFB4B4B4"; +DBM_c_pink = "|cFFFFA3B1"; +DBM_c_ = "|r"; DBM_c = "|r"; +DBM_ICONS = "Interface\\Icons\\"; +DBM_FONT_TVCENMT = "Interface\\AddOns\\AscensionBuffFrames\\Fonts\\Tw_Cen_MT_Bold.ttf"; +DBM_FONT_DBM = "Interface\\AddOns\\AscensionBuffFrames\\Fonts\\DBM.ttf"; +DBM_TEXTURE_BANTOBAR = "Interface\\AddOns\\AscensionBuffFrames\\Textures\\BantoBar"; + +function DBM_GetGroupNr(name) + local i,n,g; + for i=1,GetNumRaidMembers() do + n,_,g = GetRaidRosterInfo(i); + if(n==name) then + return g; + end + end + return false; +end + +function DBM_GetRaidId(name) + local i,n; + for i=1,GetNumRaidMembers() do + n = GetRaidRosterInfo(i); + if(string.lower(n)==string.lower(name)) then + return i; + end + end + return false; +end + +function DBM_GetClass(name) + local i,n,c; + for i=1,GetNumRaidMembers() do + n,_,_,_,c = GetRaidRosterInfo(i); + if(n==name) then + return c; + end + end + return ""; +end + +function DBM_GetRank(name) + local i,n,c; + for i=1,GetNumRaidMembers() do + n,c = GetRaidRosterInfo(i); + if(n==name) then + return c; + end + end + return false; +end + +function DBM_GetSmoothColor(percent) + local r, g, b; + if(percent > 0.5) then + r = (1.0 - percent) * 2; + g = 1.0; + else + r = 1.0; + g = percent * 2; + end + b = 0.0; + return r,g,b; +end + +function DBM_GetTextClassColor(class) + local color = RAID_CLASS_COLORS[string.gsub(string.upper(class)," ","")]; + if(color) then + local colorText = ("|cff%.2x%.2x%.2x"):format(color.r*255,color.g*255,color.b*255); + return colorText; + end + return "|cFFFFFFFF"; +end + +function DBM_GetColorText(r,g,b) + local colorText = ("|cff%.2x%.2x%.2x"):format(r*255,g*255,b*255); + return colorText; +end + +function DBM_StringIcon(icon) + return DBM_StringTexture(DBM_ICONS..icon) +end + +function DBM_StringTexture(texture) + return "|T"..texture..":0|t"; +end + +function DBM_GetTextReactionColor(reaction) + if(reaction<4) then + return DBM_c_r; + elseif(reaction==4) then + return DBM_c_y; + else + return DBM_c_g; + end +end + +function DBM_GetRealBossName(boss) + local n; + if(DBM_BOSS_DATA[boss].realname) then + n = DBM_BOSS_DATA[boss].realname; + else + n = boss; + end + return n; +end + +function DBM_GetMarkNameFromNumber(marknr) + if(marknr==1) then + return "star"; + elseif(marknr==2) then + return "circle"; + elseif(marknr==3) then + return "diamond"; + elseif(marknr==4) then + return "triangle"; + elseif(marknr==5) then + return "moon"; + elseif(marknr==6) then + return "square"; + elseif(marknr==7) then + return "cross"; + elseif(marknr==8) then + return "skull"; + end +end + +function DBM_round(tal) + if (tal < 0) then + return math.ceil(tal-0.5) + else + return math.floor(tal+0.5) + end +end + +function DBM_UnitHealthPercent(unit,CountOneHpAsZero) + if(UnitExists(unit)) then + local hp = UnitHealth(unit) / UnitHealthMax(unit) * 100; + if(CountOneHpAsZero and UnitHealth(unit)==1) then + return 0; + elseif(hp == 0) then + return 0; + elseif(hp < 1) then + return 1; + else + return math.floor(hp); + end + else + return 0; + end +end + +function DBM_UnitPowerPercent(unitid) + if(UnitExists(unitid)) then + return math.floor(UnitPower(unitid) / UnitPowerMax(unitid) * 100); + else + return 0; + end +end + +function DBM_PlaySoundFile(file) + if(DBM_GetS("SoundWarnings")) then + PlaySoundFile(file); + end +end + +function DBM_UnitClassification(uid) + local c = UnitClassification(uid); + local name = UnitName(uid); + + if(DBM_ZONE and (name == "Captain Qeez" or name == "Captain Tuubid" or name == "Captain Xurrem" or name == "Major Pakkon" or name =="Colonel Zerran" or name == "Major Yeggeth" or name == "Captain Drenn")) then + return "worldboss"; + end + if(DBM_ZONE and c=="elite" and UnitLevel(uid)==-1) then + return "worldboss"; + end + return c; +end + +function DBM_BoostPlaySound(file,boost,duration) + local savemaster = GetCVar("Sound_MasterVolume"); + + DBM_debug("Boosting Master Volume to: "..savemaster + boost); + SetCVar("Sound_MasterVolume",savemaster + boost); + + DBM_PlaySoundFile(file); + + DBM_Delay(duration,function() + DBM_debug("Restoring Master to: "..savemaster); + SetCVar("Sound_MasterVolume",savemaster); + end); +end + +function DBM_CheckForBuff(buffname, unit) + if (not unit) then unit = "player"; end if (not UnitExists(unit)) then return; end + local name = UnitBuff(unit,buffname); + if(name) then + return true; + end + return false; +end + +function DBM_CheckForDebuff(buffname, unit) + if (not unit) then unit = "player"; end if (not UnitExists(unit)) then return; end + local name = UnitDebuff(unit,buffname); + if(name) then + return true; + end + return false; +end + +function DBM_GetBuffText(buffindex, unit) + if (not unit) then unit = "player"; end if (not UnitExists(unit)) then return nil; end + AscensionBuffFrames_TTTextLeft2:SetText(); + AscensionBuffFrames_TT:SetUnitBuff(unit, buffindex); + return AscensionBuffFrames_TTTextLeft2:GetText(); +end + +function DBM_GetDeBuffText(buffindex, unit) + if (not unit) then unit = "player"; end if (not UnitExists(unit)) then return nil; end + AscensionBuffFrames_TTTextLeft2:SetText(); + AscensionBuffFrames_TT:SetUnitDeBuff(unit, buffindex); + return AscensionBuffFrames_TTTextLeft2:GetText(); +end + + +function DBM_band(mask,...) + args = {...}; + local i; + for i=1,#args do + if(bit.band(mask,args[i]) == 0) then + return false; + end + end + return true; +end +function DBM_bor(b1,...) + args = {...}; + local i; + for i=1,#args do + b1 = bit.bor(b1,args[i]); + end + return b1; +end + +function DBM_linebreakStr(str,b,tryspace) + local l = {}; + local t; + while(string.len(str) > 0) do + if(tryspace) then + if(string.len(str) <= b) then + --if the full line fits, just take it all + l[#l+1] = str; + break; + end + t = string.find(string.reverse(string.sub(str,1,b+1))," ",1,true); + if(not t) then + --just break the line + l[#l+1] = string.sub(str,1,b); + str = string.sub(str,b+1); + else + --break the line and remove the space + l[#l+1] = string.sub(str,1,b-(t-1)); + str = string.sub(str,b+2-(t-1)); + end + else + l[#l+1] = string.sub(str,1,b); + str = string.sub(str,b+1); + end + end + return table.concat(l,"\n"); +end + +function DBM_SetTrue(varname) + _G[varname] = true; +end + +function DBM_SetFalse(varname) + _G[varname] = false; +end + +function DBM_SearchBags(itemname) + local bag,slot; + for bag=0,NUM_BAG_SLOTS do + for slot=1,GetContainerNumSlots(bag) do + if (GetContainerItemLink(bag,slot) and string.find(GetContainerItemLink(bag,slot), itemname)) then + return bag,slot; + end + end + end + + return false,false; +end + +function DBM_SplitItemToEmptySlots(bag,item,stacksize) + local valid = GetContainerItemLink(bag,item); + if(valid and stacksize > 0) then + local _,itemcount,locked = GetContainerItemInfo(bag,item); + --it not locked searchbags for free slots + if(not locked) then + local sbag,sslot; + for sbag=0,NUM_BAG_SLOTS do + for sslot=1,GetContainerNumSlots(sbag) do + if(GetContainerItemLink(sbag,sslot)==nil) then + --free slot found split stack into this slot + if(itemcount > stacksize) then + SplitContainerItem(bag,item,stacksize); + itemcount = itemcount - stacksize; + --find correct bag to click + for i=1, NUM_CONTAINER_FRAMES do + local bagframe = getglobal("ContainerFrame"..i); + if ( bagframe:IsShown() and bagframe:GetID() == sbag ) then + --bag found, now find slot + for j=1, GetContainerNumSlots(sbag) do + local slotframe = getglobal(bagframe:GetName().."Item"..j); + if(slotframe:GetID()==sslot) then + --found correct slot, click it + slotframe:Click(); + DBM_Delay(0.6,DBM_SplitItemToEmptySlots,bag,item,stacksize); + return; + end + end + end + end + end + end + end + end + end + end +end + +function DBM_FormatMoney(money) + local SILVER = "|cFFC0C0C0"; + local COPPER = "|cFFCC9900"; + local GOLD = "|cFFFFFF66"; + local WHITE = "|cFFFFFFFF"; + local c,s,g; + local retstr = ""; + g = floor(money/10000); + s = mod(floor(money/100),100); + c = mod(money,100); + + if(g>0) then + retstr = retstr..WHITE..g..GOLD.." Gold"; + end + if(s>0) then + if ( retstr ~= "" ) then retstr = retstr .. " " end; + retstr = retstr..WHITE..s..SILVER.." Silver"; + end + if(c>0) then + if ( retstr ~= "" ) then retstr = retstr .. " " end; + retstr = retstr..WHITE..c..COPPER.." Copper"; + end + return retstr; +end + +function DBM_FlagsColor(flags) + if(DBM_band(flags,COMBATLOG_OBJECT_REACTION_HOSTILE) ) then + return "|cFFFF0000"; + elseif(DBM_band(flags,COMBATLOG_OBJECT_REACTION_NEUTRAL) ) then + return "|cFFFFFF00"; + elseif(DBM_band(flags,COMBATLOG_OBJECT_REACTION_FRIENDLY) ) then + return "|cFF00FF00"; + end +end + +function DBM_StripNames(name) + name = string.gsub(name,"%'",""); + name = string.gsub(name," ","_"); + return name; +end + +function DBM_NoneEnglish() + if(GetLocale() ~= "enUS") then + DBM_printc("AscensionBuffFramess text parsing is made for the english client only, so some features may not work for you."); + end +end + +function DBM_CPUWarning() + if(GetCVar("scriptProfile")=="1") then + DBM_printc("|cFFFFFFFFWarning|cFF8888CC CPU Profileing is on, this may have a huge performance impact on gameplay"); + DBM_printc("If this is unintentional you can disable it by typing /DBMcpuoff"); + end +end + +function DBM_CPUPOff() + if(GetCVar("scriptProfile")~="0") then + SetCVar("scriptProfile", "0"); + ReloadUI(); + end +end + +function DBM_CreateExtraMenuFrames(nrFrames,nrButtons) + --[[ + for i=UIDROPDOWNMENU_MAXLEVELS+1,nrFrames do + UIDropDownMenu_CreateFrames(i, 0); + end]]-- + + --[[ + local i,j; + for i=3,nrFrames do + --first check if frame exists + if(not getglobal("DropDownList"..i)) then + --Create the frame + local frame = CreateFrame("Button","DropDownList"..i,UIParent,"UIDropDownListTemplate"); + frame:SetToplevel(true); + frame:SetID(i); + frame:SetFrameStrata("FULLSCREEN_DIALOG"); + frame:Hide(); + end + --now add extra buttons if they dont exists + for j=9,nrButtons do + if(not getglobal("DropDownList"..i.."Button"..j)) then + local frame = CreateFrame("Button","DropDownList"..i.."Button"..j,getglobal("DropDownList"..i),"UIDropDownMenuButtonTemplate"); + frame:SetID(j); + end + end + end]]-- +end + +--[[ + ******************************************************************** + ******************************************************************** + Sync Functions + ******************************************************************** + ******************************************************************** +]]-- + +--[[ ******************************** + Position Sync + ********************************]]-- + +local pos_sync_table = {}; +local pos_last_asked_sync = {}; +local pos_send_my_until = 0; +local pos_dont_send_to_fast = 0; + +local function send_position_sync() + SetMapToCurrentZone(); + local x,y = GetPlayerMapPosition("player"); + local zone = GetRealZoneText(); + DBM_send_mess("MYPOS "..x.."@"..y.."@"..zone); +end + +function DBM_PositionSync_OnUpdate() + --runs 20 times each second + if(pos_send_my_until > GetTime()) then + --dont send to fast + if(pos_dont_send_to_fast + 1 > GetTime()) then + return; + end + pos_dont_send_to_fast = GetTime(); + --send + send_position_sync(); + end +end + +function DBM_PositionSyncRequest_Recive(msg,from) + if(msg==DBM_YOU) then + pos_send_my_until = GetTime()+8; + end +end + +function DBM_PositionSync_Recive(msg,from) + local found,_,x,y,zone = string.find(msg,"(.+)@(.+)@(.+)"); + if(found) then + pos_sync_table[from] = { + x = tonumber(x), + y = tonumber(y), + zone = zone, + }; + end +end + +function DBM_GetPlayerMapPosition(uid) + if(UnitExists(uid)) then + --fix your own coords + SetMapToCurrentZone(); + --get coords + local x,y = GetPlayerMapPosition(uid); + --if we got coords from blizz UI just pass them + if(x+y > 0) then + return x,y; + end + --get coords from sync + local name = UnitName(uid); + --check if we should ask for new sync + if(not pos_last_asked_sync[name]) then pos_last_asked_sync[name] = 0; end --to be failsafe + if(pos_last_asked_sync[name]+5 < GetTime()) then + pos_last_asked_sync[name] = GetTime(); + DBM_send_mess("REQUESTPOS "..name); + end + --okay no fetch from table + if(not pos_sync_table[name]) then + --not updated yet or no DBM + return 0,0; + end + --check if we are in same zone + if(GetRealZoneText()==pos_sync_table[name].zone) then + return pos_sync_table[name].x,pos_sync_table[name].y; + else + return 0,0; + end + end + return GetPlayerMapPosition(uid); +end \ No newline at end of file diff --git a/DBM-Core/DBM-RangeCheck.lua b/DBM-Core/DBM-RangeCheck.lua new file mode 100644 index 0000000..b40ef3c --- /dev/null +++ b/DBM-Core/DBM-RangeCheck.lua @@ -0,0 +1,440 @@ +-- *************************************************** +-- ** DBM Range Check Frame ** +-- ** http://www.deadlybossmods.com ** +-- *************************************************** +-- +-- This addon is written and copyrighted by: +-- * Paul Emmerich (Tandanu @ EU-Aegwynn) (DBM-Core) +-- * Martin Verges (Nitram @ EU-Azshara) (DBM-GUI) +-- +-- The localizations are written by: +-- * enGB/enUS: Tandanu +-- * deDE: Tandanu/Nitram +-- * ruRU: BootWin +-- * zhTW: Azael/kc10577 +-- * (add your names here!) +-- +-- Special thanks to: +-- * Arta (DBM-Party) +-- +-- +-- The code of this addon is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. (see license.txt) +-- All included textures and sounds are copyrighted by their respective owners. +-- +-- +-- You are free: +-- * to Share to copy, distribute, display, and perform the work +-- * to Remix to make derivative works +-- Under the following conditions: +-- * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). +-- * Noncommercial. You may not use this work for commercial purposes. +-- * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. +-- +-- +-- This file makes use of the following free (Creative Commons Sampling Plus 1.0) sounds: +-- * alarmclockbeeps.ogg by tedthetrumpet (http://www.freesound.org/usersViewSingle.php?id=177) +-- * blip_8.ogg by Corsica_S (http://www.freesound.org/usersViewSingle.php?id=7037) +-- The full of text of the license can be found in the file "Sounds\Creative Commons Sampling Plus 1.0.txt". + +--------------- +-- Globals -- +--------------- +DBM.RangeCheck = {} + + +-------------- +-- Locals -- +-------------- +local rangeCheck = DBM.RangeCheck +local checkFuncs = {} +local frame +local createFrame +local onUpdate +local dropdownFrame +local initializeDropdown +local initRangeCheck -- initializes the range check for a specific range (if necessary), returns false if the initialization failed (because of a map range check in an unknown zone) + +-- for Phanx' Class Colors +local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS + +--------------------- +-- Dropdown Menu -- +--------------------- + +-- todo: this dropdown menu is somewhat ugly and unflexible.... +do + local function setRange(self, range) + rangeCheck:Show(range) + end + + local sound0 = "none" + local sound1 = "Interface\\AddOns\\DBM-Core\\Sounds\\blip_8.ogg" + local sound2 = "Interface\\AddOns\\DBM-Core\\Sounds\\alarmclockbeeps.ogg" + local function setSound(self, option, sound) + DBM.Options[option] = sound + if sound ~= "none" then + PlaySoundFile(sound) + end + end + + local function toggleLocked() + DBM.Options.RangeFrameLocked = not DBM.Options.RangeFrameLocked + end + + function initializeDropdown(dropdownFrame, level, menu) + local info + if level == 1 then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE + info.notCheckable = true + info.hasArrow = true + info.menuList = "range" + UIDropDownMenu_AddButton(info, 1) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUNDS + info.notCheckable = true + info.hasArrow = true + info.menuList = "sounds" + UIDropDownMenu_AddButton(info, 1) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_LOCK + if DBM.Options.RangeFrameLocked then + info.checked = true + end + info.func = toggleLocked + UIDropDownMenu_AddButton(info, 1) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_HIDE + info.notCheckable = true + info.func = rangeCheck.Hide + info.arg1 = rangeCheck + UIDropDownMenu_AddButton(info, 1) + elseif level == 2 then + if menu == "range" then + + if initRangeCheck() then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(6) + info.func = setRange + info.arg1 = 6 + info.checked = (frame.range == 6) + UIDropDownMenu_AddButton(info, 2) + end + + if initRangeCheck() then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(8) + info.func = setRange + info.arg1 = 8 + info.checked = (frame.range == 8) + UIDropDownMenu_AddButton(info, 2) + end + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(10) + info.func = setRange + info.arg1 = 10 + info.checked = (frame.range == 10) + UIDropDownMenu_AddButton(info, 2) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(11) + info.func = setRange + info.arg1 = 11 + info.checked = (frame.range == 11) + UIDropDownMenu_AddButton(info, 2) + + if initRangeCheck() then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(12) + info.func = setRange + info.arg1 = 12 + info.checked = (frame.range == 12) + UIDropDownMenu_AddButton(info, 2) + end + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(15) + info.func = setRange + info.arg1 = 15 + info.checked = (frame.range == 15) + UIDropDownMenu_AddButton(info, 2) + + if initRangeCheck() then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(20) + info.func = setRange + info.arg1 = 20 + info.checked = (frame.range == 20) + UIDropDownMenu_AddButton(info, 2) + end + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SETRANGE_TO:format(28) + info.func = setRange + info.arg1 = 28 + info.checked = (frame.range == 28) + UIDropDownMenu_AddButton(info, 2) + elseif menu == "sounds" then + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUND_OPTION_1 + info.notCheckable = true + info.hasArrow = true + info.menuList = "RangeFrameSound1" + UIDropDownMenu_AddButton(info, 2) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUND_OPTION_2 + info.notCheckable = true + info.hasArrow = true + info.menuList = "RangeFrameSound2" + UIDropDownMenu_AddButton(info, 2) + end + elseif level == 3 then + local option = menu + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUND_0 + info.func = setSound + info.arg1 = option + info.arg2 = sound0 + info.checked = (DBM.Options[option] == sound0) + UIDropDownMenu_AddButton(info, 3) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUND_1 + info.func = setSound + info.arg1 = option + info.arg2 = sound1 + info.checked = (DBM.Options[option] == sound1) + UIDropDownMenu_AddButton(info, 3) + + info = UIDropDownMenu_CreateInfo() + info.text = DBM_CORE_RANGECHECK_SOUND_2 + info.func = setSound + info.arg1 = option + info.arg2 = sound2 + info.checked = (DBM.Options[option] == sound2) + UIDropDownMenu_AddButton(info, 3) + end + end +end + +----------------- +-- Play Sounds -- +----------------- +local function updateSound(numPlayers) -- called every 5 seconds + if not UnitAffectingCombat("player") then + return + end + if numPlayers == 1 then + if DBM.Options.RangeFrameSound1 ~= "none" then + PlaySoundFile(DBM.Options.RangeFrameSound1) + end + elseif numPlayers > 1 then + if DBM.Options.RangeFrameSound2 ~= "none" then + PlaySoundFile(DBM.Options.RangeFrameSound2) + end + end +end + +------------------------ +-- Create the frame -- +------------------------ +function createFrame() + local elapsed = 0 + local frame = CreateFrame("GameTooltip", "DBMRangeCheck", UIParent, "GameTooltipTemplate") + dropdownFrame = CreateFrame("Frame", "DBMRangeCheckDropdown", frame, "UIDropDownMenuTemplate") + frame:SetFrameStrata("DIALOG") + frame:SetPoint(DBM.Options.RangeFramePoint, UIParent, DBM.Options.RangeFramePoint, DBM.Options.RangeFrameX, DBM.Options.RangeFrameY) + frame:SetHeight(64) + frame:SetWidth(64) + frame:EnableMouse(true) + frame:SetToplevel(true) + frame:SetMovable() + GameTooltip_OnLoad(frame) + frame:SetPadding(16) + frame:RegisterForDrag("LeftButton") + frame:SetScript("OnDragStart", function(self) + if not DBM.Options.RangeFrameLocked then + self:StartMoving() + end + end) + frame:SetScript("OnDragStop", function(self) + self:StopMovingOrSizing() + ValidateFramePosition(self) + local point, _, _, x, y = self:GetPoint(1) + DBM.Options.RangeFrameX = x + DBM.Options.RangeFrameY = y + DBM.Options.RangeFramePoint = point + end) + frame:SetScript("OnUpdate", function(self, e) + elapsed = elapsed + e + if elapsed >= 0.5 and self.checkFunc then + onUpdate(self, elapsed) + elapsed = 0 + end + end) + frame:SetScript("OnMouseDown", function(self, button) + if button == "RightButton" then + UIDropDownMenu_Initialize(dropdownFrame, initializeDropdown, "MENU") + ToggleDropDownMenu(1, nil, dropdownFrame, "cursor", 5, -10) + end + end) + return frame +end + + +---------------- +-- OnUpdate -- +---------------- +local soundUpdate = 0 +function onUpdate(self, elapsed) + local color + local j = 0 + self:ClearLines() + self:SetText(DBM_CORE_RANGECHECK_HEADER:format(self.range), 1, 1, 1) + if initRangeCheck(self.range) then + for i = 1, GetNumRaidMembers() do + local uId = "raid"..i + if not UnitIsUnit(uId, "player") and not UnitIsDeadOrGhost(uId) and self.checkFunc(uId, self.range) and (not self.filter or self.filter(uId)) then + j = j + 1 + color = RAID_CLASS_COLORS[select(2, UnitClass(uId))] or NORMAL_FONT_COLOR + local icon = GetRaidTargetIndex(uId) + local text = icon and ("|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_%d:0|t %s"):format(icon, UnitName(uId)) or UnitName(uId) + self:AddLine(text, color.r, color.g, color.b) + if j >= 5 then + break + end + end + end + else + self:AddLine(DBM_CORE_RANGE_CHECK_ZONE_UNSUPPORTED:format(self.range)) + end + soundUpdate = soundUpdate + elapsed + if soundUpdate >= 5 and j > 0 then + updateSound(j) + soundUpdate = 0 + end + self:Show() +end + + +----------------------- +-- Check functions -- +----------------------- +checkFuncs[11] = function(uId) + return CheckInteractDistance(uId, 2) +end + + +checkFuncs[10] = function(uId) + return CheckInteractDistance(uId, 3) +end + +checkFuncs[28] = function(uId) + return CheckInteractDistance(uId, 4) +end + + +local getDistanceBetween +do + local mapSizes = DBM.MapSizes + + function getDistanceBetween(uId, x, y) + local startX, startY = GetPlayerMapPosition(uId) + local mapName = GetMapInfo() + local dims = mapSizes[mapName] and mapSizes[mapName][GetCurrentMapDungeonLevel()] + if not dims then + return + end + local dX = (startX - x) * dims[1] + local dY = (startY - y) * dims[2] + return math.sqrt(dX * dX + dY * dY) + end + + local function mapRangeCheck(uId, range) + return getDistanceBetween(uId, GetPlayerMapPosition("player")) < range + end + + function initRangeCheck(range) + if checkFuncs[range] ~= mapRangeCheck then + return true + end + local pX, pY = GetPlayerMapPosition("player") + if pX == 0 and pY == 0 then + SetMapToCurrentZone() + pX, pY = GetPlayerMapPosition("player") + end + local levels = mapSizes[GetMapInfo()] + if not levels then + return false + end + local dims = levels[GetCurrentMapDungeonLevel()] + if not dims and levels and GetCurrentMapDungeonLevel() == 0 then -- we are in a known zone but the dungeon level seems to be wrong + SetMapToCurrentZone() -- fixes the dungeon level + dims = levels[GetCurrentMapDungeonLevel()] -- try again + if not dims then -- there is actually a level 0 in this zone but we don't know about it...too bad :( + return false + end + elseif not dims then + return false + end + return true -- everything ok! + end + + setmetatable(checkFuncs, { + __index = function(t, k) + return mapRangeCheck + end + }) +end + +do + local bandages = {21991, 34721, 38643, 34722, 34721, 34722} -- you should have one of these bandages in your cache + + checkFuncs[15] = function(uId) + for i, v in ipairs(bandages) do + if IsItemInRange(v, uId) == 1 then + return true + elseif IsItemInRange(v, uId) == 0 then + return false + end + end + end +end + + +--------------- +-- Methods -- +--------------- +function rangeCheck:Show(range, filter) + if type(range) == "function" then -- the first argument is optional + return self:Show(nil, range) + end + range = range or 10 + frame = frame or createFrame() + frame.checkFunc = checkFuncs[range] or error(("Range \"%d yd\" is not supported."):format(range), 2) + frame.range = range + frame.filter = filter + frame:Show() + frame:SetOwner(UIParent, "ANCHOR_PRESERVE") + onUpdate(frame, 0) +end + +function rangeCheck:Hide() + if frame then frame:Hide() end +end + +function rangeCheck:IsShown() + return frame and frame:IsShown() +end + +function rangeCheck:GetDistance(...) + if initRangeCheck() then + return getDistanceBetween(...) + end +end + diff --git a/DBM-Core/DBT-Template.xml b/DBM-Core/DBT-Template.xml new file mode 100644 index 0000000..af1424c --- /dev/null +++ b/DBM-Core/DBT-Template.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DBM-Core/DBT.lua b/DBM-Core/DBT.lua new file mode 100644 index 0000000..dd6d09d --- /dev/null +++ b/DBM-Core/DBT.lua @@ -0,0 +1,990 @@ +-- *************************************************** +-- ** Deadly Bar Timers ** +-- ** http://www.deadlybossmods.com ** +-- *************************************************** +-- +-- This addon is written and copyrighted by: +-- * Paul Emmerich (Tandanu @ EU-Aegwynn) (DBM-Core) +-- * Martin Verges (Nitram @ EU-Azshara) (DBM-GUI) +-- +-- The localizations are written by: +-- * enGB/enUS: Tandanu http://www.deadlybossmods.com +-- * deDE: Tandanu http://www.deadlybossmods.com +-- * zhCN: Diablohu http://wow.gamespot.com.cn +-- * ruRU: BootWin bootwin@gmail.com +-- * zhTW: Azael/kc10577 kc10577@hotmail.com +-- * esES: Interplay/1nn7erpLaY http://www.1nn7erpLaY.com +-- * (add your names here!) +-- +-- Special thanks to: +-- * Arta (DBM-Party) +-- +-- +-- The code of this addon is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License. (see license.txt) +-- All included textures and sounds are copyrighted by their respective owners. +-- +-- +-- You are free: +-- * to Share ?to copy, distribute, display, and perform the work +-- * to Remix ?to make derivative works +-- Under the following conditions: +-- * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). +-- * Noncommercial. You may not use this work for commercial purposes. +-- * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. + + +--------------- +-- Globals -- +--------------- +DBT = {} +DBT_SavedOptions = {} + + +-------------- +-- Locals -- +-------------- +local fCounter = 1 +local barPrototype = {} +local unusedBars = {} +local unusedBarObjects = setmetatable({}, {__mode = "kv"}) +local instances = {} +local updateClickThrough +local options +local function stringFromTimer(t) + if t <= 60 then + return ("%.1f"):format(t) + else + return ("%d:%0.2d"):format(t/60, math.fmod(t, 60)) + end +end + + +----------------------- +-- Default Options -- +----------------------- +options = { + BarXOffset = { + type = "number", + default = 0, + }, + BarYOffset = { + type = "number", + default = 0, + }, + HugeBarXOffset = { + type = "number", + default = 0, + }, + HugeBarYOffset = { + type = "number", + default = 0, + }, + ExpandUpwards = { + type = "boolean", + default = false, + }, + Flash = { + type = "boolean", + default = true, + }, + FadeIn = { + type = "boolean", + default = true, + }, + Break = { + type = "boolean", + default = true, + }, + IconLeft = { + type = "boolean", + default = true, + }, + IconRight = { + type = "boolean", + default = false, + }, + Texture = { + type = "string", + default = "Interface\\AddOns\\DBM-Core\\textures\\default.tga", + }, + StartColorR = { + type = "number", + default = 1, + }, + StartColorG = { + type = "number", + default = 0.7, + }, + StartColorB = { + type = "number", + default = 0, + }, + EndColorR = { + type = "number", + default = 1, + }, + EndColorG = { + type = "number", + default = 0, + }, + EndColorB = { + type = "number", + default = 0, + }, + TextColorR = { + type = "number", + default = 1, + }, + TextColorG = { + type = "number", + default = 1, + }, + TextColorB = { + type = "number", + default = 1, + }, + DynamicColor = { + type = "boolean", + default = true, + }, + Width = { + type = "number", + default = 183, + }, + Scale = { + type = "number", + default = 0.9, + }, + HugeBarsEnabled = { + type = "boolean", + default = true, + }, + HugeWidth = { + type = "number", + default = 200, + }, + HugeScale = { + type = "number", + default = 1.03, + }, + TimerPoint = { + type = "string", + default = "TOPRIGHT", + }, + TimerX = { + type = "number", + default = -223, + }, + TimerY = { + type = "number", + default = -260, + }, + HugeTimerPoint = { + type = "string", + default = "CENTER", + }, + HugeTimerX = { + type = "number", + default = 0, + }, + HugeTimerY = { + type = "number", + default = -120, + }, + EnlargeBarsTime = { + type = "number", + default = 8, + }, + EnlargeBarsPercent = { + type = "number", + default = 0.125, + }, + FillUpBars = { + type = "boolean", + default = true, + }, + ClickThrough = { + type = "boolean", + default = false, + }, + Font = { + type = "string", + default = STANDARD_TEXT_FONT, + }, + FontSize = { + type = "number", + default = 10 + } +} + +-------------------------- +-- Double Linked List -- +-------------------------- +-- +-- this linked list can only contain tables that do not use the fields "prev" and "next" +-- this restriction especially means that an object must not be in two different linked lists at the same time +-- but this is sufficient for DBT here, having a wrapper object would just be an unnecessary overhead +-- special table keys for "prev"/"next" (e.g. userdata values) would add unnecessary complexity + +local DLL = {} +DLL.__index = DLL + +function DLL:Append(obj) + if self.first == nil then -- list is empty + self.first = obj + self.last = obj + else -- list is not empty + obj.prev = self.last + self.last.next = obj + self.last = obj + end + return obj +end + +function DLL:Remove(obj) + if self.first == nil then -- list is empty... + -- ...meaning the object is not even in the list, nothing we can do here expect for removing the "prev" and "next" entries from obj + elseif self.first == obj and self.last == obj then -- list has only one element + self.first = nil + self.last = nil + elseif self.first == obj then -- trying to remove the first element + self.first = obj.next + self.first.prev = nil + elseif self.last == obj then -- trying to remove the last element + self.last = obj.prev + self.last.next = nil + elseif obj.prev and obj.next then -- trying to remove something in the middle of the list + obj.prev.next, obj.next.prev = obj.next, obj.prev + end + obj.prev = nil + obj.next = nil +end + +function DLL:New() + return setmetatable({ + first = nil, + last = nil + }, self) +end +setmetatable(DLL, {__call = DLL.New}) + + +------------------------------- +-- DBT Constructor/Options -- +------------------------------- +do + local mt = {__index = DBT} + local optionMT = { + __index = function(t, k) + if options[k] then + return options[k].default + else + return nil + end + end + } + + function DBT:New() + local obj = setmetatable( + { + options = setmetatable({}, optionMT), + defaultOptions = setmetatable({}, optionMT), + mainAnchor = CreateFrame("Frame", nil, UIParent), + secAnchor = CreateFrame("Frame", nil, UIParent), + bars = {}, + smallBars = DLL(), + hugeBars = DLL() + }, + mt + ) + obj.mainAnchor:SetHeight(1) + obj.mainAnchor:SetWidth(1) + obj.mainAnchor:SetPoint("TOPRIGHT", 223, -260) + obj.mainAnchor:SetClampedToScreen(true) + obj.mainAnchor:SetMovable(true) + obj.mainAnchor:Show() + obj.secAnchor:SetHeight(1) + obj.secAnchor:SetWidth(1) + obj.secAnchor:SetPoint("CENTER", 0, -120) + obj.secAnchor:SetClampedToScreen(true) + obj.secAnchor:SetMovable(true) + obj.secAnchor:Show() + table.insert(instances, obj) + return obj + end + + function DBT:LoadOptions(id) + local savedOptions; + if type(id) == "table" then + savedOptions = id; + else + DBT_SavedOptions[id] = DBT_SavedOptions[id] or {}; + savedOptions = DBT_SavedOptions[id]; + end + self.options = setmetatable(savedOptions, optionMT) + self.mainAnchor:ClearAllPoints() + self.secAnchor:ClearAllPoints() + self.mainAnchor:SetPoint(self.options.TimerPoint, UIParent, self.options.TimerPoint, self.options.TimerX, self.options.TimerY) + self.secAnchor:SetPoint(self.options.HugeTimerPoint, UIParent, self.options.HugeTimerPoint, self.options.HugeTimerX, self.options.HugeTimerY) + end +end + +function DBT:SetOption(option, value) + if not options[option] then + error(("Invalid option: %s"):format(tostring(option)), 2) + elseif options[option].type and type(value) ~= options[option].type then + error(("The option %s requires a %s value. (tried to assign a %s value)"):format(tostring(option), tostring(options[option].type), tostring(type(value))), 2) + elseif options[option].checkFunc then + local ok, errMsg = options[option].checkFunc(self, option, value) + if not ok then + error(("Error while setting option %s to %s: %s"):format(tostring(option), tostring(value), tostring(errMsg)), 2) + end + end + local oldValue = self.options[option] + self.options[option] = value + if options[option].onChange then + options[option].onChange(self, value, oldValue) + end + self:ApplyStyle() +end + +function DBT:GetOption(option) + return self.options[option] +end + +function DBT:GetDefaultOption(option) + return self.defaultOptions[option] +end + + +----------------------- +-- Bar Constructor -- +----------------------- +do + local function createBarFrame(self) + local frame + if unusedBars[#unusedBars] then + frame = unusedBars[#unusedBars] + unusedBars[#unusedBars] = nil + frame:Show() + else + frame = CreateFrame("Frame", "DBT_Bar_"..fCounter, self.mainAnchor, "DBTBarTemplate") + fCounter = fCounter + 1 + end + frame:EnableMouse(not self.options.ClickThrough or self.movable) + return frame + end + local mt = {__index = barPrototype} + + function DBT:CreateBar(timer, id, icon, huge, small, color, isDummy) + if timer <= 0 then return end + if (self.numBars or 0) >= 15 and not isDummy then return end + local newBar = self:GetBar(id) + if newBar then -- update an existing bar + newBar:SetTimer(timer) -- this can kill the timer and the timer methods don't like dead timers + if newBar.dead then return end + newBar:SetElapsed(0) -- same + if newBar.dead then return end + newBar:ApplyStyle() + newBar:SetText(id) + newBar:SetIcon(icon) + else -- create a new one + newBar = next(unusedBarObjects, nil) + local newFrame = createBarFrame(self) + if newBar then + unusedBarObjects[newBar] = nil + newBar.dead = nil -- resurrected it :) + newBar.frame = newFrame + newBar.id = id + newBar.timer = timer + newBar.totalTime = timer + newBar.owner = self + newBar.moving = nil + newBar.enlarged = nil + newBar.fadingIn = 0 + newBar.small = small + newBar.color = color + newBar.flashing = nil + else -- duplicate code ;( + newBar = setmetatable({ + frame = newFrame, + id = id, + timer = timer, + totalTime = timer, + owner = self, + moving = nil, + enlarged = nil, + fadingIn = 0, + small = small, + color = color, + flashing = nil + }, mt) + end + newFrame.obj = newBar + self.numBars = (self.numBars or 0) + 1 + if (timer <= self.options.EnlargeBarsTime or huge) and self:GetOption("HugeBarsEnabled") then -- starts enlarged? + newBar.enlarged = true + self.hugeBars:Append(newBar) + else + self.smallBars:Append(newBar) + end + newBar:ApplyStyle() + newBar:SetText(id) + newBar:SetIcon(icon) + newBar:SetPosition() + newBar:Update(0) + self.bars[newBar] = true + end + return newBar + end +end + + +----------------- +-- Dummy Bar -- +----------------- +do + local dummyBars = 0 + local function dummyCancel(self) + self.timer = self.totalTime + self.flashing = nil + self:Update(0) + self.flashing = nil + getglobal(self.frame:GetName().."BarSpark"):SetAlpha(1) + end + function DBT:CreateDummyBar() + dummyBars = dummyBars + 1 + local dummy = self:CreateBar(25, "dummy"..dummyBars, "Interface\\Icons\\Spell_Nature_WispSplode", nil, true, nil, true) + dummy:SetText("Dummy") + dummy:Cancel() + self.bars[dummy] = true + unusedBars[#unusedBars] = nil + unusedBarObjects[dummy] = nil + dummy.frame.obj = dummy + dummy.frame:SetParent(UIParent) + dummy.frame:ClearAllPoints() + dummy.frame:SetScript("OnUpdate", nil) + dummy.Cancel = dummyCancel + dummy:ApplyStyle() + dummy.dummy = true + return dummy + end +end + + +----------------------------- +-- General Bar Functions -- +----------------------------- +--do +-- local function iterator(self, frame) +-- return not frame and self.mainFirstBar or frame and frame.next +-- end +-- +-- local function reverseIterator(self, frame) +-- return (not frame and self.mainLastBar) or frame and frame.prev +-- end +-- +-- function DBT:GetBarIterator(reverse) +-- return (reverse and reverseIterator) or iterator, self, nil +-- end +--end +function DBT:GetBarIterator() + return pairs(self.bars) +end + +function DBT:GetBar(id) + for bar in self:GetBarIterator() do + if id == bar.id then + return bar + end + end +end + +function DBT:CancelBar(id) + for bar in self:GetBarIterator() do + if id == bar.id then + bar:Cancel() + return true + end + end + return false +end + +function DBT:UpdateBar(id, elapsed, totalTime) + for bar in self:GetBarIterator() do + if id == bar.id then + bar:SetTimer(totalTime or bar.totalTime) + bar:SetElapsed(elapsed or self.totalTime - self.timer) + return true + end + end + return false +end + + +--------------------------- +-- General Bar Methods -- +--------------------------- +function DBT:ShowTestBars() + self:CreateBar(10, "Test 1", "Interface\\Icons\\Spell_Nature_WispSplode") + self:CreateBar(14, "Test 2", "Interface\\Icons\\Spell_Nature_WispSplode") + self:CreateBar(20, "Test 3", "Interface\\Icons\\Spell_Nature_WispSplode") + self:CreateBar(12, "Test 4", "Interface\\Icons\\Spell_Nature_WispSplode") + self:CreateBar(21.5, "Test 5", "Interface\\Icons\\Spell_Nature_WispSplode") +end + +function barPrototype:SetTimer(timer) + self.totalTime = timer + self:Update(0) +end + +function barPrototype:SetElapsed(elapsed) + self.timer = self.totalTime - elapsed + if (self.enlarged or self.moving == "enlarge") and not (self.timer <= self.owner.options.EnlargeBarsTime or (self.timer/self.totalTime) <= self.owner.options.EnlargeBarsPercent) then + local next = self.next + self:RemoveFromList() + self.enlarged = nil + self.moving = nil + if next then + next:MoveToNextPosition() + end + self.owner.smallBars:Append(self) + self:SetPosition() + end + self:Update(0) +end + +function barPrototype:SetText(text) + getglobal(self.frame:GetName().."BarName"):SetText(text) +end + +function barPrototype:SetIcon(icon) + getglobal(self.frame:GetName().."BarIcon1"):SetTexture("") + getglobal(self.frame:GetName().."BarIcon1"):SetTexture(icon) + getglobal(self.frame:GetName().."BarIcon2"):SetTexture("") + getglobal(self.frame:GetName().."BarIcon2"):SetTexture(icon) +end + +function barPrototype:SetColor(color) + self.color = color + getglobal(self.frame:GetName().."Bar"):SetStatusBarColor(color.r, color.g, color.b) + getglobal(self.frame:GetName().."BarSpark"):SetVertexColor(color.r, color.g, color.b) +end + +------------------ +-- Bar Update -- +------------------ +function barPrototype:Update(elapsed) + local frame = self.frame + local bar = getglobal(frame:GetName().."Bar") + local texture = getglobal(frame:GetName().."BarTexture") + local spark = getglobal(frame:GetName().."BarSpark") + local timer = getglobal(frame:GetName().."BarTimer") + local obj = self.owner + self.timer = self.timer - elapsed + if obj.options.DynamicColor and not self.color then + local r = obj.options.StartColorR + (obj.options.EndColorR - obj.options.StartColorR) * (1 - self.timer/self.totalTime) + local g = obj.options.StartColorG + (obj.options.EndColorG - obj.options.StartColorG) * (1 - self.timer/self.totalTime) + local b = obj.options.StartColorB + (obj.options.EndColorB - obj.options.StartColorB) * (1 - self.timer/self.totalTime) + bar:SetStatusBarColor(r, g, b) + spark:SetVertexColor(r, g, b) + end + if self.timer <= 0 then + return self:Cancel() + else + if obj.options.FillUpBars then + bar:SetValue(1 - self.timer/self.totalTime) + else + bar:SetValue(self.timer/self.totalTime) + end + spark:ClearAllPoints() + spark:SetPoint("CENTER", bar, "LEFT", bar:GetValue() * bar:GetWidth(), -1) + timer:SetText(stringFromTimer(self.timer)) + end + if obj.options.FadeIn and self.fadingIn and self.fadingIn < 0.5 then + self.fadingIn = self.fadingIn + elapsed + frame:SetAlpha((self.fadingIn) / 0.5) + elseif self.fadingIn then + self.fadingIn = nil + end + + if self.timer <= 7.75 and not self.flashing and obj.options.Flash then + self.flashing = true + self.ftimer = 0 + end + if self.flashing then + local ftime = self.ftimer % 1.25 + if ftime >= 0.5 then + texture:SetAlpha(1) + spark:SetAlpha(1) + elseif ftime >= 0.25 then + texture:SetAlpha(1 - (0.5 - ftime) / 0.25) + spark:SetAlpha(1 - (0.5 - ftime) / 0.25) + else + texture:SetAlpha(1 - (ftime / 0.25)) + spark:SetAlpha(1 - (ftime / 0.25)) + end + self.ftimer = self.ftimer + elapsed + end + if self.moving == "move" and self.moveElapsed <= 0.5 then + self.moveElapsed = self.moveElapsed + elapsed + local newX = self.moveOffsetX + (obj.options.BarXOffset - self.moveOffsetX) * (self.moveElapsed / 0.5) + local newY + if self.owner.options.ExpandUpwards then + newY = self.moveOffsetY + 40 + (obj.options.BarYOffset - self.moveOffsetY) * (self.moveElapsed / 0.5) + else + newY = self.moveOffsetY + (-obj.options.BarYOffset - self.moveOffsetY) * (self.moveElapsed / 0.5) + end + frame:ClearAllPoints() + frame:SetPoint(self.movePoint, self.moveAnchor, self.moveRelPoint, newX, newY) + elseif self.moving == "move" then + self.moving = nil + self:SetPosition() + elseif self.moving == "enlarge" and self.moveElapsed <= 1 then + self:AnimateEnlarge(elapsed) + elseif self.moving == "enlarge" then + self.moving = nil + self.enlarged = true + self.owner.hugeBars:Append(self) + self:ApplyStyle() + self:SetPosition() + end + if (self.timer <= self.owner.options.EnlargeBarsTime or (self.timer/self.totalTime) <= self.owner.options.EnlargeBarsPercent) and (not self.small) and not self.enlarged and self.moving ~= "enlarge" and self.owner:GetOption("HugeBarsEnabled") then + local next = self.next + self:RemoveFromList() + if next then + local oldX = next.frame:GetRight() - next.frame:GetWidth()/2 -- the next frame's point needs to be cleared before we enlarge the bar to prevent the frame from "jumping around" + local oldY = next.frame:GetTop() -- so we need to save the old point for :MoveToNextPosition() as :GetTop() and :GetRight() might return nil (sometimes? happened only once in 2 weeks of raiding...but it crashed DBT...) after :ClearAllPoints() + next.frame:ClearAllPoints() + end + self:Enlarge() + if next then + next:MoveToNextPosition(oldX, oldY) -- ugly? + end + end +end + +do + local frame = CreateFrame("Frame") + frame:SetScript("OnUpdate", function(self, elapsed) + if UIParent:IsShown() then return end + for i, v in ipairs(instances) do + for bar in v:GetBarIterator() do + bar:Update(elapsed) + end + end + end) +end + + +------------------- +-- Movable Bar -- +------------------- +function DBT:SavePosition() + local point, _, _, x, y = self.mainAnchor:GetPoint(1) + self:SetOption("TimerPoint", point) + self:SetOption("TimerX", x) + self:SetOption("TimerY", y) + local point, _, _, x, y = self.secAnchor:GetPoint(1) + self:SetOption("HugeTimerPoint", point) + self:SetOption("HugeTimerX", x) + self:SetOption("HugeTimerY", y) +end + +do + local function moveEnd(self) + updateClickThrough(self, self:GetOption("ClickThrough")) + self.movable = false + end + + function DBT:ShowMovableBar(small, large) + if small or small == nil then + local bar1 = self:CreateBar(20, "Move1", "Interface\\Icons\\Spell_Nature_WispSplode", nil, true) + bar1:SetText(DBM_CORE_MOVABLE_BAR) + end + if large or large == nil then + local bar2 = self:CreateBar(20, "Move2", "Interface\\Icons\\Spell_Nature_WispSplode", true) + bar2:SetText(DBM_CORE_MOVABLE_BAR) + end + updateClickThrough(self, false) + self.movable = true + DBM:Unschedule(moveEnd, self) + DBM:Schedule(20, moveEnd, self) + end +end + + +-------------------- +-- Bar Handling -- +-------------------- +function barPrototype:RemoveFromList() + if self.moving ~= "enlarge" then + (self.enlarged and self.owner.hugeBars or self.owner.smallBars):Remove(self) + end +end + + +------------------ +-- Bar Cancel -- +------------------ +function barPrototype:Cancel() + local next = self.next + table.insert(unusedBars, self.frame) + self.frame:Hide() + self.frame.obj = nil + self:RemoveFromList() + if next then + next:MoveToNextPosition() + end + self.owner.bars[self] = nil + unusedBarObjects[self] = self + self.dead = true + self.owner.numBars = (self.owner.numBars or 1) - 1 +end + + +----------------- +-- Bar Style -- +----------------- +function DBT:ApplyStyle() + for bar in self:GetBarIterator() do + bar:ApplyStyle() + end +end + +function barPrototype:ApplyStyle() + local frame = self.frame + local bar = getglobal(frame:GetName().."Bar") + local spark = getglobal(frame:GetName().."BarSpark") + local texture = getglobal(frame:GetName().."BarTexture") + local icon1 = getglobal(frame:GetName().."BarIcon1") + local icon2 = getglobal(frame:GetName().."BarIcon2") + local name = getglobal(frame:GetName().."BarName") + local timer = getglobal(frame:GetName().."BarTimer") + texture:SetTexture(self.owner.options.Texture) + if self.color then + bar:SetStatusBarColor(self.color.r, self.color.g, self.color.b) + spark:SetVertexColor(self.color.r, self.color.g, self.color.b) + else + bar:SetStatusBarColor(self.owner.options.StartColorR, self.owner.options.StartColorG, self.owner.options.StartColorB) + spark:SetVertexColor(self.owner.options.StartColorR, self.owner.options.StartColorG, self.owner.options.StartColorB) + end + name:SetTextColor(self.owner.options.TextColorR, self.owner.options.TextColorG, self.owner.options.TextColorB) + timer:SetTextColor(self.owner.options.TextColorR, self.owner.options.TextColorG, self.owner.options.TextColorB) + if self.owner.options.IconLeft then icon1:Show() else icon1:Hide() end + if self.owner.options.IconRight then icon2:Show() else icon2:Hide() end + if self.enlarged then frame:SetWidth(self.owner.options.HugeWidth) else frame:SetWidth(self.owner.options.Width) end + if self.enlarged then bar:SetWidth(self.owner.options.HugeWidth) else bar:SetWidth(self.owner.options.Width) end + if self.enlarged then frame:SetScale(self.owner.options.HugeScale) else frame:SetScale(self.owner.options.Scale) end + self.frame:Show() + spark:SetAlpha(1) + texture:SetAlpha(1) + bar:SetAlpha(1) + frame:SetAlpha(1) + name:SetFont(self.owner.options.Font, self.owner.options.FontSize) + timer:SetFont(self.owner.options.Font, self.owner.options.FontSize) + self:Update(0) +end + +local function updateOrientation(self) + for bar in self:GetBarIterator() do + if not bar.dummy then + if bar.moving == "enlarge" then + bar.enlarged = true + bar.moving = false + self.owner.hugeBars:Append(self) + bar:ApplyStyle() + end + bar.moving = nil + bar:SetPosition() + end + end +end +options.ExpandUpwards.onChange = updateOrientation +options.BarYOffset.onChange = updateOrientation +options.BarXOffset.onChange = updateOrientation + +function updateClickThrough(self, newValue) + if not self.movable then + for bar in self:GetBarIterator() do + if not bar.dummy then + bar.frame:EnableMouse(not newValue) + end + end + end +end + +options.ClickThrough.onChange = updateClickThrough + + +-------------------- +-- Bar Announce -- +-------------------- +function barPrototype:Announce() + local msg + if self.owner.announceHook then + msg = self.owner.announceHook(self) + end + msg = msg or ("%s %d:%02d"):format(getglobal(self.frame:GetName().."BarName"):GetText(), math.floor(self.timer / 60), self.timer % 60) + local chatWindow = ChatEdit_GetActiveWindow() + if chatWindow then + chatWindow:Insert(msg) + else + SendChatMessage(msg, (select(2, IsInInstance()) == "pvp" and "BATTLEGROUND") or (GetNumRaidMembers() > 0 and "RAID") or "PARTY") + end +end + +function DBT:SetAnnounceHook(f) + self.announceHook = f +end + +----------------------- +-- Bar Positioning -- +----------------------- +function barPrototype:SetPosition() + if self.moving == "enlarge" then return end + local anchor = (self.prev and self.prev.frame) or (self.enlarged and self.owner.secAnchor) or self.owner.mainAnchor + self.frame:ClearAllPoints() + if self.owner.options.ExpandUpwards then + self.frame:SetPoint("TOP", anchor, "BOTTOM", self.owner.options.BarXOffset, 40 + self.owner.options.BarYOffset) + else + self.frame:SetPoint("TOP", anchor, "BOTTOM", self.owner.options.BarXOffset, -self.owner.options.BarYOffset) + end +end + +function barPrototype:MoveToNextPosition(oldX, oldY) + if self.moving == "enlarge" then return end + local newAnchor = (self.prev and self.prev.frame) or (self.enlarged and self.owner.secAnchor) or self.owner.mainAnchor + local oldX = oldX or (self.frame:GetRight() - self.frame:GetWidth()/2) + local oldY = oldY or (self.frame:GetTop()) + self.frame:ClearAllPoints() + if self.owner.options.ExpandUpwards then + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", self.owner.options.BarXOffset, 40 + self.owner.options.BarYOffset) + else + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", self.owner.options.BarXOffset, -self.owner.options.BarYOffset) + end + local newX = self.frame:GetRight() - self.frame:GetWidth()/2 + local newY = self.frame:GetTop() + self.frame:ClearAllPoints() + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", -(newX - oldX), -(newY - oldY)) + self.moving = "move" + self.movePoint = "TOP" + self.moveRelPoint = "BOTTOM" + self.moveAnchor = newAnchor + self.moveOffsetX = -(newX - oldX) + self.moveOffsetY = -(newY - oldY) + self.moveElapsed = 0 +end + +function barPrototype:Enlarge() + local newAnchor = (self.owner.hugeBars.last and self.owner.hugeBars.last.frame) or self.owner.secAnchor + local oldX = self.frame:GetRight() - self.frame:GetWidth()/2 + local oldY = self.frame:GetTop() + self.frame:ClearAllPoints() + if self.owner.options.ExpandUpwards then + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", self.owner.options.BarXOffset, 40 + self.owner.options.BarYOffset) + else + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", self.owner.options.BarXOffset, -self.owner.options.BarYOffset) + end + local newX = self.frame:GetRight() - self.frame:GetWidth()/2 + local newY = self.frame:GetTop() + self.frame:ClearAllPoints() + self.frame:SetPoint("TOP", newAnchor, "BOTTOM", -(newX - oldX), -(newY - oldY)) + self.moving = "enlarge" + self.movePoint = "TOP" + self.moveRelPoint = "BOTTOM" + self.moveAnchor = newAnchor + self.moveOffsetX = -(newX - oldX) + self.moveOffsetY = -(newY - oldY) + self.moveElapsed = 0 +end + + +--------------------------- +-- Bar Special Effects -- +--------------------------- +function barPrototype:AnimateEnlarge(elapsed) + self.moveElapsed = self.moveElapsed + elapsed + local newX = self.moveOffsetX + (self.owner.options.BarXOffset - self.moveOffsetX) * (self.moveElapsed / 1) + local newY + if self.owner.options.ExpandUpwards then + newY = self.moveOffsetY + 50 + (self.owner.options.BarYOffset - self.moveOffsetY) * (self.moveElapsed / 1) + else + newY = self.moveOffsetY + (self.owner.options.BarYOffset - self.moveOffsetY) * (self.moveElapsed / 1) + end + local newWidth = self.owner.options.Width + (self.owner.options.HugeWidth - self.owner.options.Width ) * (self.moveElapsed / 1) + local newScale = self.owner.options.Scale + (self.owner.options.HugeScale - self.owner.options.Scale) * (self.moveElapsed / 1) + if (self.moveOffsetY > 0 and newY > self.owner.options.BarYOffset) or (self.moveOffsetY < 0 and newY < self.owner.options.BarYOffset) then + self.frame:ClearAllPoints() + self.frame:SetPoint(self.movePoint, self.moveAnchor, self.moveRelPoint, newX, newY) + self.frame:SetScale(newScale) + self.frame:SetWidth(newWidth) + getglobal(self.frame:GetName().."Bar"):SetWidth(newWidth) + else + self.moving = nil + self.enlarged = true + self.owner.hugeBars:Append(self) + self:ApplyStyle() + self:SetPosition() + end +end + +--[[ +do + local breakFrames = {} + function barPrototype:Break() -- coming soon + local frame = table.remove(breakTextures, #breakTextures) or CreateFrame("Frame", nil, self.owner.mainAnchor) + frame:SetParent(self.owner.mainAnchor) + frame.tex1 = frame.tex1 or frame:CreateTexture(nil, "OVERLAY") + frame.tex2 = frame.tex2 or frame:CreateTexture(nil, "OVERLAY") + local tex1 = frame.tex1 + local tex2 = frame.tex2 + tex1:SetTexture(self.owner.options.Texture) + tex2:SetTexture(self.owner.options.Texture) + -- tex1:SetTexCoordModifiesRect(true) + tex1:SetHorizTile(true) + tex1:SetVertTile(true) + tex1:SetTexCoord(0, 0.5, 0, 1) + end +end +]]-- + +---------------------------------------- +-- Functions used by the XML Template -- +---------------------------------------- +function DBT_Bar_OnLoad(self) + self:SetMinMaxValues(0, 1) + self:SetValue(1) +end + +function DBT_Bar_OnUpdate(self, elapsed) + self.obj:Update(elapsed) +end + +function DBT_Bar_OnMouseDown(self, btn) + if self.obj.owner.movable and btn == "LeftButton" then + if self.obj.enlarged then + self.obj.owner.secAnchor:StartMoving() + else + self.obj.owner.mainAnchor:StartMoving() + end + end +end + +function DBT_Bar_OnMouseUp(self, btn) + self.obj.owner.mainAnchor:StopMovingOrSizing() + self.obj.owner.secAnchor:StopMovingOrSizing() + self.obj.owner:SavePosition() + if btn == "RightButton" then + self.obj:Cancel() + elseif btn == "LeftButton" and IsShiftKeyDown() then + self.obj:Announce() + end +end + +function DBT_Bar_OnHide(self) + if self.obj then + self.obj.owner.mainAnchor:StopMovingOrSizing() + self.obj.owner.secAnchor:StopMovingOrSizing() + end +end diff --git a/DBM-Core/license.txt b/DBM-Core/license.txt new file mode 100644 index 0000000..7ab2484 --- /dev/null +++ b/DBM-Core/license.txt @@ -0,0 +1,61 @@ +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(g) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. + c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. + d. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike. + e. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. + f. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. + g. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. + h. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + i. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. + j. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights described in Section 4(e). + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested. + b. You may Distribute or Publicly Perform an Adaptation only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). You must include a copy of, or the URI, for Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. + c. You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in con-nection with the exchange of copyrighted works. + d. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. + e. For the avoidance of doubt: + i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and, + iii. Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c). + f. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. diff --git a/DBM-Core/localization.cn.lua b/DBM-Core/localization.cn.lua new file mode 100644 index 0000000..2e6e478 --- /dev/null +++ b/DBM-Core/localization.cn.lua @@ -0,0 +1,134 @@ +-- Simplified Chinese by Diablohu/yleaf(yaroot@gmail.com) +-- http://wow.gamespot.com.cn +-- Last Update: 12/13/2008 + +-- yleaf (yaroot@gmail.com) 9-19-2009 + +if GetLocale() ~= "zhCN" then return end + +DBM_CORE_LOAD_MOD_ERROR = "读取%s模块时发生错误:%s" +DBM_CORE_LOAD_MOD_SUCCESS = "成功读取%s模块!" +DBM_CORE_LOAD_GUI_ERROR = "无法读取图形界面:%s" + +DBM_CORE_COMBAT_STARTED = "%s作战开始,祝你走运 :)"; +DBM_CORE_BOSS_DOWN = "%s被击杀!用时%s。" +DBM_CORE_BOSS_DOWN_LONG = "%s被击杀!本次用时%s,上次用时%s,最快击杀用时%s。" +DBM_CORE_BOSS_DOWN_NEW_RECORD = "%s被击杀!用时%s,新的击杀纪录诞生了!(原纪录为%s)" +DBM_CORE_COMBAT_ENDED = "%s作战结束,用时%s。" + +DBM_CORE_TIMER_FORMAT_SECS = "%d秒" +DBM_CORE_TIMER_FORMAT_MINS = "%d分钟" +DBM_CORE_TIMER_FORMAT = "%d分%d秒" + +DBM_CORE_MIN = "分" +DBM_CORE_MIN_FMT = "%d 分" +DBM_CORE_SEC = "秒" +DBM_CORE_SEC_FMT = "%d 秒" +DBM_CORE_DEAD = "死亡" +DBM_CORE_OK = "确定" + +DBM_CORE_GENERIC_WARNING_BERSERK = "%s%s后激怒" +DBM_CORE_GENERIC_TIMER_BERSERK = "激怒" +DBM_CORE_OPTION_TIMER_BERSERK = "显示激怒倒计时" +DBM_CORE_OPTION_HEALTH_FRAME = "显示首领生命值窗口" + +DBM_CORE_OPTION_CATEGORY_TIMERS = "计时条" +DBM_CORE_OPTION_CATEGORY_WARNINGS = "警报" +DBM_CORE_OPTION_CATEGORY_MISC = "其它" + +DBM_CORE_AUTO_RESPONDED = "已自动回复密语。" +DBM_CORE_STATUS_WHISPER = "%s:%s,%d/%d存活" +DBM_CORE_AUTO_RESPOND_WHISPER = "%s正在与%s交战,(当前%s,%d/%d存活)" + +DBM_CORE_VERSIONCHECK_HEADER = "Deadly Boss Mods - 版本检测" +DBM_CORE_VERSIONCHECK_ENTRY = "%s:%s(r%d)" +DBM_CORE_VERSIONCHECK_ENTRY_NO_DBM = "%s:尚未安装DBM" +DBM_CORE_VERSIONCHECK_FOOTER = "团队中有%d名成员正在使用Deadly Boss Mods" + +DBM_CORE_UPDATEREMINDER_HEADER = "你的Deadly Boss Mods版本已过期。\n你可以在如下地址下载到新版本%s(r%d):" +DBM_CORE_UPDATEREMINDER_FOOTER = "Ctrl-C:复制下载地址到剪切板。" +DBM_CORE_UPDATEREMINDER_NOTAGAIN = "发现新版本后弹出提示框" + +DBM_CORE_MOVABLE_BAR = "拖动我!" + +DBM_PIZZA_SYNC_INFO = "|Hplayer:%1$s|h[%1$s]|h向你发送了一个倒计时:'%2$s'\n|HDBM:cancel:%2$s:nil|h|cff3588ff[取消该计时]|r|h |HDBM:ignore:%2$s:%1$s|h|cff3588ff[忽略来自%1$s的计时]|r|h" +DBM_PIZZA_CONFIRM_IGNORE = "是否要在该次游戏连接中屏蔽来自%s的计时?" +DBM_PIZZA_ERROR_USAGE = "命令:/dbm [broadcast] timer <时间(秒)> <文本>" + +DBM_CORE_ERROR_DBMV3_LOADED = "目前有2个版本的Deadly Boss Mods正在运行:DBMv3和DBMv4。\n单击“确定”按钮可将DBMv3关闭并重载用户界面。\n我们建议将插件目录下的DBMv3删除。" + +DBM_CORE_MINIMAP_TOOLTIP_HEADER = "Deadly Boss Mods" +DBM_CORE_MINIMAP_TOOLTIP_FOOTER = "Shift+单击或右键点击即可移动" + +DBM_CORE_RANGECHECK_HEADER = "距离监视(%d码)" +DBM_CORE_RANGECHECK_SETRANGE = "设置距离" +DBM_CORE_RANGECHECK_SOUNDS = "声音" +DBM_CORE_RANGECHECK_SOUND_OPTION_1 = "当有玩家接近时播放声音提示" +DBM_CORE_RANGECHECK_SOUND_OPTION_2 = "多名玩家接近提示" +DBM_CORE_RANGECHECK_SOUND_0 = "无" +DBM_CORE_RANGECHECK_SOUND_1 = "默认声音" +DBM_CORE_RANGECHECK_SOUND_2 = "蜂鸣" +DBM_CORE_RANGECHECK_HIDE = "隐藏" +DBM_CORE_RANGECHECK_SETRANGE_TO = "%d码" + +DBM_CORE_SLASHCMD_HELP = { + "可用命令:", + "/dbm version:进行团队范围的DBM版本检测(也可使用:ver)", + "/dbm unlock:显示一个可移动的计时条,可通过对它来移动所有DBM计时条的位置(也可使用:move)", + "/dbm timer <文本>:开始一个以<文本>为名称的时间为秒的倒计时", + "/dbm broadcast timer <文本>:向团队广播一个以<文本>为名称的时间为秒的倒计时(需要团队领袖或助理权限)", + "/dbm help:显示该帮助信息", +} + +DBM_ERROR_NO_PERMISSION = "无权进行该操作。" + +DBM_CORE_BOSSHEALTH_HIDE_FRAME = "隐藏" + +DBM_CORE_ALLIANCE = "联盟" +DBM_CORE_HORDE = "部落" + +DBM_CORE_UNKNOWN = "未知" + +DBM_CORE_TIMER_PULL = "开怪倒计时" +DBM_CORE_ANNOUNCE_PULL = "%d 秒后开怪" +DBM_CORE_ANNOUNCE_PULL_NOW = "开怪!" + +DBM_CORE_ACHIEVEMENT_TIMER_SPEED_KILL = "快速击杀" + +-- Auto-generated Timer Localizations +DBM_CORE_AUTO_TIMER_TEXTS.target = "%s: %%s" +DBM_CORE_AUTO_TIMER_TEXTS.cast = "%s" +DBM_CORE_AUTO_TIMER_TEXTS.active = "%s" +DBM_CORE_AUTO_TIMER_TEXTS.cd = "%s 冷却" +DBM_CORE_AUTO_TIMER_TEXTS.next = "下一次 %s" +DBM_CORE_AUTO_TIMER_TEXTS.achievement = "%s" + +DBM_CORE_AUTO_TIMER_OPTIONS.target = "显示 |cff71d5ff|Hspell:%d|h%s|h|r debuff计时" +DBM_CORE_AUTO_TIMER_OPTIONS.cast = "显示 |cff71d5ff|Hspell:%d|h%s|h|r 施法计时" +DBM_CORE_AUTO_TIMER_OPTIONS.active = "显示 |cff71d5ff|Hspell:%d|h%s|h|r 持续计时" +DBM_CORE_AUTO_TIMER_OPTIONS.cd = "显示 |cff71d5ff|Hspell:%d|h%s|h|r 冷却计时" +DBM_CORE_AUTO_TIMER_OPTIONS.next = "显示下一次 |cff71d5ff|Hspell:%d|h%s|h|r 计时" +DBM_CORE_AUTO_TIMER_OPTIONS.achievement = "显示成就: %s 计时" + +-- Auto-generated Warning Localizations +DBM_CORE_AUTO_ANNOUNCE_TEXTS.target = "%s 于 >%%s<" +DBM_CORE_AUTO_ANNOUNCE_TEXTS.spell = "%s" +DBM_CORE_AUTO_ANNOUNCE_TEXTS.cast = "%s: %.1f sec" +DBM_CORE_AUTO_ANNOUNCE_TEXTS.soon = "即将 %s" +DBM_CORE_AUTO_ANNOUNCE_TEXTS.prewarn = "%2$s 后 %1$s" +DBM_CORE_AUTO_ANNOUNCE_TEXTS.phase = "第 %d 阶段" + +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.target = "警报目标的: |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.spell = "显示警报: |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.cast = "施法提示: |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.soon = "提前警报 |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.prewarn = "提前警报 |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS.phase = "显示阶段提示: %d" + +-- New special warnings +DBM_CORE_MOVE_SPECIAL_WARNING_BAR = "移动特殊警报" +DBM_CORE_MOVE_SPECIAL_WARNING_TEXT = "特殊警报" + + + + diff --git a/DBM-Core/localization.de.lua b/DBM-Core/localization.de.lua new file mode 100644 index 0000000..800ff50 --- /dev/null +++ b/DBM-Core/localization.de.lua @@ -0,0 +1,193 @@ +if GetLocale() ~= "deDE" then return end + +DBM_CORE_NEED_SUPPORT = "Hey, bist du ein Programmierer oder gut in Fremdsprachen? Falls ja, benötigt das DBM-Team deine Hilfe, damit DBM das beste BossMod in WoW bleibt. Trete dem Team auf www.deadlybossmods.com bei oder sende eine E-Mail an tandanu@deadlybossmods.com oder nitram@deadlybossmods.com." + +DBM_CORE_LOAD_MOD_ERROR = "Fehler beim Laden von Boss Mods für %s: %s" +DBM_CORE_LOAD_MOD_SUCCESS = "Boss Mods für '%s' geladen, für weitere Optionen gib /dbm in deinem Chatfenster ein!" +DBM_CORE_LOAD_GUI_ERROR = "Konnte das GUI nicht laden: %s" + +DBM_CORE_COMBAT_STARTED = "Kampf gegen %s hat begonnen. Viel Glück! :)"; +DBM_CORE_BOSS_DOWN = "%s tot nach %s!" +DBM_CORE_BOSS_DOWN_LONG = "%s tot nach %s! Euer letzter kill hat %s gedauert und der schnellste %s." +DBM_CORE_BOSS_DOWN_NEW_RECORD = "%s tot nach %s! Das ist ein neuer Rekord! (der alte Rekord war %s)" +DBM_CORE_COMBAT_ENDED = "Kampf gegen %s hat nach %s aufgehört." + +DBM_CORE_TIMER_FORMAT_SECS = "%d |4Sekunde:Sekunden;" +DBM_CORE_TIMER_FORMAT_MINS = "%d |4Minute:Minuten;" +DBM_CORE_TIMER_FORMAT = "%d |4Minute:Minuten; und %d |4Sekunde:Sekunden;" + +DBM_CORE_MIN = "Min" +DBM_CORE_MIN_FMT = "%d Min" +DBM_CORE_SEC = "Sek" +DBM_CORE_SEC_FMT = "%d Sek" +DBM_CORE_DEAD = "Tot" +DBM_CORE_OK = "Okay" + +DBM_CORE_GENERIC_WARNING_BERSERK = "Berserker in %s %s" +DBM_CORE_GENERIC_TIMER_BERSERK = "Berserker" +DBM_CORE_OPTION_TIMER_BERSERK = "Berserker-Timer anzeigen" +DBM_CORE_OPTION_HEALTH_FRAME = "BossHealth-Frame anzeigen" + +DBM_CORE_OPTION_CATEGORY_TIMERS = "Timer" +DBM_CORE_OPTION_CATEGORY_WARNINGS = "Ansagen" +DBM_CORE_OPTION_CATEGORY_MISC = "Verschiedenes" + +DBM_CORE_AUTO_RESPONDED = "Automatisch geantwortet." +DBM_CORE_STATUS_WHISPER = "%s: %s, %d/%d Spieler am Leben" +DBM_CORE_AUTO_RESPOND_WHISPER = "%s ist damit beschäftigt, gegen %s zu kämpfen! (%s, %d/%d Spieler am Leben)" +DBM_CORE_WHISPER_COMBAT_END_KILL = "%s hat %s besiegt!" +DBM_CORE_WHISPER_COMBAT_END_WIPE = "%s ist an %s gewipet" + +DBM_CORE_VERSIONCHECK_HEADER = "Deadly Boss Mods - Versionen" +DBM_CORE_VERSIONCHECK_ENTRY = "%s: %s (r%d)" +DBM_CORE_VERSIONCHECK_ENTRY_NO_DBM = "%s: DBM nicht installiert" +DBM_CORE_VERSIONCHECK_FOOTER = "%d Spieler mit Deadly Boss Mods gefunden" +DBM_CORE_YOUR_VERSION_OUTDATED = "Deine Version von Deadly Boss Mods ist veraltet! Bitte besuche www.deadlybossmods.com, um die neueste Version herunterzuladen." +DBM_CORE_DISABLED_ICON_FUNCTION = "Dein DBM setzt keine Zeichen mehr, weil deine Version veraltet ist. Bitte aktualisiere dein DBM so bald wie möglich und aktiviere die Zeichenfunktion wieder." + +DBM_CORE_UPDATEREMINDER_HEADER = "Deine Version von Deadly Boss Mods ist veraltet.\n Version %s (r%d) ist hier zum Download verfügbar:" +DBM_CORE_UPDATEREMINDER_FOOTER = "Drücke Strg+C um den Download-Link in die Zwischenablage zu kopieren" +DBM_CORE_UPDATEREMINDER_NOTAGAIN = "Zeige Popup wenn eine neue Version verfügbar ist" + +DBM_CORE_MOVABLE_BAR = "Zieh mich!" + +DBM_PIZZA_SYNC_INFO = "|Hplayer:%1$s|h[%1$s]|h hat dir einen DBM-Timer geschickt: '%2$s'\n|HDBM:cancel:%2$s:nil|h|cff3588ff[Diesen Timer abbrechen]|r|h |HDBM:ignore:%2$s:%1$s|h|cff3588ff[Pizza-Timer von %1$s ignorieren]|r|h" +DBM_PIZZA_CONFIRM_IGNORE = "Willst du wirklich DBM-Timer von %s für diese Session ignorieren?" +DBM_PIZZA_ERROR_USAGE = "Benutzung: /dbm [broadcast] timer " + +DBM_CORE_ERROR_DBMV3_LOADED = "Deadly Boss Mods läuft doppelt, da du DBMv3 und DBMv4 installiert und aktiviert hast!\nKlick auf \"Okay\" um DBMv3 zu deaktivieren und dein Interface neu zu laden.\nAußerdem solltest du deinen AddOn-Ordner aufräumen, indem du alle DBMv3 Mods löschst." + +DBM_CORE_MINIMAP_TOOLTIP_HEADER = "Deadly Boss Mods" +DBM_CORE_MINIMAP_TOOLTIP_FOOTER = "Shift+Klick oder Rechtsklick zum Bewegen" + +DBM_CORE_RANGECHECK_HEADER = "Abstandscheck (%d m)" +DBM_CORE_RANGECHECK_SETRANGE = "Abstand einstellen" +DBM_CORE_RANGECHECK_SOUNDS = "Sounds" +DBM_CORE_RANGECHECK_SOUND_OPTION_1 = "Sound wenn ein Spieler in Reichweite ist" +DBM_CORE_RANGECHECK_SOUND_OPTION_2 = "Sound wenn mehr als ein Spieler in Reichweite ist" +DBM_CORE_RANGECHECK_SOUND_0 = "Kein Sound" +DBM_CORE_RANGECHECK_SOUND_1 = "Standard-Sound" +DBM_CORE_RANGECHECK_SOUND_2 = "Nerviges Piepsen" +DBM_CORE_RANGECHECK_HIDE = "Verstecken" +DBM_CORE_RANGECHECK_SETRANGE_TO = "%d m" +DBM_CORE_RANGECHECK_LOCK = "Frame sperren" + +DBM_CORE_SLASHCMD_HELP = { + "Verfügbare Slash-Commands:", + "/dbm version: führt einen raidweiten Versionscheck durch (alias: ver)", + "/dbm unlock: zeigt einen bewegbaren Timer an (alias: move)", + "/dbm timer : startet einen Sekunden langen DBM-Timer mit dem Namen ", + "/dbm broadcast timer : schickt einen Sekunden langen DBM-Timer mit dem Namen an den Raid (benötigt (A) oder (L))", + "/dbm break : startet einen Pause-Timer für Minuten. Schickt allen Raidmitgliedern mit DBM einen Pause-Timer (benötigt (A) oder (L)).", + "/dbm help: zeigt diese Hilfe", +} + +DBM_ERROR_NO_PERMISSION = "Du hast nicht die benötigte Berechtigung für diesen Befehl!" + +DBM_CORE_BOSSHEALTH_HIDE_FRAME = "Verstecken" + +DBM_CORE_ALLIANCE = "Allianz" +DBM_CORE_HORDE = "Horde" + +DBM_CORE_UNKNOWN = "unbekannt" + +DBM_CORE_BREAK_START = "Pause startet jetzt -- du hast %s Minute(n)!" +DBM_CORE_BREAK_MIN = "Pause endet in %s Minute(n)!" +DBM_CORE_BREAK_SEC = "Pause endet in %s Sekunden!" +DBM_CORE_TIMER_BREAK = "Pause" +DBM_CORE_ANNOUNCE_BREAK_OVER = "Pause vorbei" + +DBM_CORE_TIMER_PULL = "Pull in" +DBM_CORE_ANNOUNCE_PULL = "Pull in %d Sek" +DBM_CORE_ANNOUNCE_PULL_NOW = "Pull jetzt!" + +DBM_CORE_ACHIEVEMENT_TIMER_SPEED_KILL = "Speed-Kill" + +-- Auto-generated Timer Localizations +-- Auto-generated Timer Localizations +DBM_CORE_AUTO_TIMER_TEXTS = { + target = "%s: %%s", + cast = "%s", + active = "%s", + cd = "%s CD", + next = "Nächster %s", + achievement = "%s", +} + +DBM_CORE_AUTO_TIMER_OPTIONS = { + target = "Debufftimer für |cff71d5ff|Hspell:%d|h%s|h|r anzeigen", + cast = "Casttimer für |cff71d5ff|Hspell:%d|h%s|h|r anzeigen", + active = "Timer während |cff71d5ff|Hspell:%d|h%s|h|r aktiv ist anzeigen", + cd = "Cooldowntimer für |cff71d5ff|Hspell:%d|h%s|h|r anzeigen", + next = "Timer für nächstes |cff71d5ff|Hspell:%d|h%s|h|r anzeigen", + achievement = "Timer für %s anzeigen", +} + +-- Auto-generated Warning Localizations +DBM_CORE_AUTO_ANNOUNCE_TEXTS = { + target = "%s auf >%%s<", + spell = "%s", + cast = "%s in %.1f Sek", + soon = "%s bald", + prewarn = "%s in %s", + phase = "Phase %d", +} + +local prewarnOption = "Zeige Vorwarnung für |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_AUTO_ANNOUNCE_OPTIONS = { + target = "Ziel von |cff71d5ff|Hspell:%d|h%s|h|r ansagen", + spell = "Zeige Warnung für |cff71d5ff|Hspell:%d|h%s|h|r", + cast = "Zeige Warnung wenn |cff71d5ff|Hspell:%d|h%s|h|r gezaubert wird", + soon = prewarnOption, + prewarn = prewarnOption, + phase = "Zeige Warnung für Phase %d" +} + +-- Auto-generated Special Warning Localizations +DBM_CORE_AUTO_SPEC_WARN_OPTIONS = { + spell = "Zeige Spezialwarnung für $spell:%d", + dispel = "Zeige Spezialwarnung zum Reinigen/Rauben von \n $spell:%d", + interupt = "Zeige Spezialwarnung zum Unterbrechen von $spell:%d", + you = "Zeige Spezialwarnung wenn du von \n $spell:%d betroffen bist", + target = "Zeige Spezialwarnung wenn jemand von \n $spell:%d betroffen ist", + close = "Zeige Spezialwarnung wenn jemand in deiner Nähe von \n $spell:%d betroffen ist", + move = "Zeige Spezialwarnung wenn du von \n $spell:%d betroffen bist", + run = "Zeige Spezialwarnung für $spell:%d", + cast = "Zeige Spezialwarnung für Zaubern von $spell:%d", + stack = "Zeige Spezialwarnung für >=%d Stacks von \n $spell:%d" +} + +DBM_CORE_AUTO_SPEC_WARN_TEXTS = { + spell = "%s!", + dispel = "%s auf %%s - jetzt reinigen", + interupt = "%s - jetzt unterbrechen", + you = "%s auf dir", + target = "%s auf %%s", + close = "%s auf %%s in deiner Nähe", + move = "%s - geh weg", + run = "%s - lauf weg", + cast = "%s - stoppe Zauber", + stack = "%s (%%d)" +} + + +DBM_CORE_AUTO_ICONS_OPTION_TEXT = "Setze Zeichen auf Ziele von $spell:%d" +DBM_CORE_AUTO_SOUND_OPTION_TEXT = "Spiele Sound bei $spell:%d" + + +-- New special warnings +DBM_CORE_MOVE_SPECIAL_WARNING_BAR = "Spezialwarnung beweglich" +DBM_CORE_MOVE_SPECIAL_WARNING_TEXT = "Spezialwarnung" + + +DBM_CORE_RANGE_CHECK_ZONE_UNSUPPORTED = "Eine %d-m-Abstacksprüfung wird in dieser Zone nicht unterstützt.\nUnterstützte Abstände sind 10, 11, 15 und 28 m." + +DBM_ARROW_MOVABLE = "Pfeil beweglich" +DBM_ARROW_NO_RAIDGROUP = "Diese Funktion steht nur in Schlachtzügen und innerhalb von Instanzen zu Verfügung." +DBM_ARROW_ERROR_USAGE = { + "Benutzung von DBM-Arrow:", + "/dbm arrow erzeugt einen Pfeil, der auf bestimmte Koordinaten zeigt (0 < x/y < 100)", + "/dbm arrow erzeugt einen Pfeil, der auf einen bestimmten Spieler in deiner Gruppe oder deinem Schlachtzug zeigt", + "/dbm arrow hide versteckt den Pfeil", + "/dbm arrow move macht den Pfeil beweglich", +} diff --git a/DBM-Core/localization.en.lua b/DBM-Core/localization.en.lua new file mode 100644 index 0000000..284dc19 --- /dev/null +++ b/DBM-Core/localization.en.lua @@ -0,0 +1,221 @@ + +DBM_CORE_NEED_SUPPORT = "Are you good with programming or languages? If yes, the DBM team needs your help to keep DBM the best boss mod for WoW. Join the team by visiting https://discord.gg/4ZHfgskSvM or sending a message to Szyler on Discord or on Area52." + +DBM_CORE_LOAD_MOD_ERROR = "Error while loading boss mods for %s: %s" +DBM_CORE_LOAD_MOD_SUCCESS = "Loaded '%s' boss mods. For more options, type /dbm in your chat." +DBM_CORE_LOAD_GUI_ERROR = "Could not load GUI: %s" + +DBM_CORE_COMBAT_STARTED = "%s engaged. Good luck and have fun! :)"; +DBM_CORE_BOSS_DOWN = "%s down after %s!" +DBM_CORE_BOSS_DOWN_LONG = "%s down after %s! Your last kill took %s and your fastest kill took %s." +DBM_CORE_BOSS_DOWN_NEW_RECORD = "%s down after %s! This is a new record! (Old record was %s)" +DBM_CORE_COMBAT_ENDED = "Combat against %s ended after %s." + +DBM_CORE_TIMER_FORMAT_SECS = "%d |4second:seconds;" +DBM_CORE_TIMER_FORMAT_MINS = "%d |4minute:minutes;" +DBM_CORE_TIMER_FORMAT = "%d |4minute:minutes; and %d |4second:seconds;" + +DBM_CORE_MIN = "min" +DBM_CORE_MIN_FMT = "%d min" +DBM_CORE_SEC = "sec" +DBM_CORE_SEC_FMT = "%d sec" +DBM_CORE_DEAD = "dead" +DBM_CORE_OK = "Okay" + +DBM_CORE_GENERIC_WARNING_BERSERK = "Berserk in %s %s" +DBM_CORE_GENERIC_TIMER_BERSERK = "Berserk" +DBM_CORE_OPTION_TIMER_BERSERK = "Show timer for $spell:26662" +DBM_CORE_OPTION_TIMER_BERSERK_CUSTOM = "Show timer for |cff71d5ff|Hspell:%d|h%s|h|r" +DBM_CORE_OPTION_HEALTH_FRAME = "Show boss health frame" + +DBM_CORE_OPTION_CATEGORY_TIMERS = "Bars" +DBM_CORE_OPTION_CATEGORY_WARNINGS = "Announces" +DBM_CORE_OPTION_CATEGORY_MISC = "Miscellaneous" + +DBM_CORE_AUTO_RESPONDED = "Auto-responded." +DBM_CORE_STATUS_WHISPER = "%s: %s, %d/%d people alive" +DBM_CORE_AUTO_RESPOND_WHISPER = "%s is busy fighting against %s (%s, %d/%d people alive)" +DBM_CORE_WHISPER_COMBAT_END_KILL = "%s has defeated %s!" +DBM_CORE_WHISPER_COMBAT_END_WIPE = "%s has wiped on %s" + +DBM_CORE_VERSIONCHECK_HEADER = "Deadly Boss Mods - Versions" +DBM_CORE_VERSIONCHECK_ENTRY = "%s: %s (r%d)" +DBM_CORE_VERSIONCHECK_ENTRY_NO_DBM = "%s: DBM not installed" +DBM_CORE_VERSIONCHECK_FOOTER = "Found %d players with Deadly Boss Mods" +DBM_CORE_YOUR_VERSION_OUTDATED = "Your version of Deadly Boss Mods is out-of-date. Please visit https://discord.gg/4ZHfgskSvM to get the latest version." + +DBM_CORE_UPDATEREMINDER_HEADER = "Your version of Deadly Boss Mods is out-of-date.\n Version %s (r%d) is available for download here:" +DBM_CORE_UPDATEREMINDER_FOOTER = "Press Ctrl-C to copy the download link to your clipboard." +DBM_CORE_UPDATEREMINDER_NOTAGAIN = "Show popup when a new version is available" + +DBM_CORE_MOVABLE_BAR = "Drag me!" + +DBM_PIZZA_SYNC_INFO = "|Hplayer:%1$s|h[%1$s]|h sent you a DBM timer: '%2$s'\n|HDBM:cancel:%2$s:nil|h|cff3588ff[Cancel this timer]|r|h |HDBM:ignore:%2$s:%1$s|h|cff3588ff[Ignore timers from %1$s]|r|h" +DBM_PIZZA_CONFIRM_IGNORE = "Do you really want to ignore DBM timers from %s for this session?" +DBM_PIZZA_ERROR_USAGE = "Usage: /dbm [broadcast] timer