diff --git a/Locales/Locale_deDE.lua b/Locales/Locale_deDE.lua
index 1a99fcd..28aa6aa 100644
--- a/Locales/Locale_deDE.lua
+++ b/Locales/Locale_deDE.lua
@@ -1,142 +1,37 @@
--- Locale_deDE.lua - German Localization for ModernMapMarkers
-ModernMapMarkers_Locale:RegisterZones("deDE", {
- -- Continent names
- ["Kalimdor"] = "Kalimdor",
- ["Eastern Kingdoms"] = "Östliche Königreiche",
- ["Ashenvale"] = "Eschental",
- ["Azshara"] = "Azshara",
- ["Badlands"] = "Ödland",
- ["Balor"] = "Balor",
- ["Blasted Lands"] = "Verwüstete Lande",
- ["Burning Steppes"] = "Brennende Steppe",
- ["Darkshore"] = "Dunkelküste",
- ["Deadwind Pass"] = "Gebirgspass der Totenwinde",
- ["Desolace"] = "Desolace",
- ["Dun Morogh"] = "Dun Morogh",
- ["Durotar"] = "Durotar",
- ["Duskwood"] = "Dämmerwald",
- ["Dustwallow Marsh"] = "Düstermarschen",
- ["Eastern Plaguelands"] = "Östliche Pestländer",
- ["Elwynn Forest"] = "Wald von Elwynn",
- ["Feralas"] = "Feralas",
- ["Gilneas"] = "Gilneas",
- ["Hyjal"] = "Hyjal",
- ["Ironforge"] = "Eisenschmiede",
- ["Moonwhisper Coast"] = "Mondflüsterküste",
- ["Orgrimmar"] = "Orgrimmar",
- ["Searing Gorge"] = "Sengende Schlucht",
- ["Silithus"] = "Silithus",
- ["Silverpine Forest"] = "Silberwald",
- ["Stormwind City"] = "Sturmwind",
- ["Stranglethorn Vale"] = "Schlingendorntal",
- ["Swamp of Sorrows"] = "Sümpfe des Elends",
- ["Tanaris"] = "Tanaris",
- ["Teldrassil"] = "Teldrassil",
- ["The Barrens"] = "Brachland",
- ["The Hinterlands"] = "Hinterland",
- ["Thunder Bluff"] = "Donnerfels",
- ["Tirisfal Glades"] = "Tirisfal",
- ["Western Plaguelands"] = "Westliche Pestländer",
- ["Westfall"] = "Westfall",
- ["Wetlands"] = "Sumpfland",
- ["Alah'Thalas"] = "Alah'Thalas",
-})
-
-ModernMapMarkers_Locale:RegisterMarkers("deDE", {
- -- Dungeons
- ["Blackfathom Deeps"] = "Tiefschwarze Grotte",
- ["Crescent Grove"] = "Mondsichelhain",
- ["Maraudon"] = "Maraudon",
- ["Ragefire Chasm"] = "Der Flammenschlund",
- ["Dire Maul"] = "Düsterbruch",
- ["Dire Maul - East"] = "Düsterbruch - Ost",
- ["Dire Maul - North"] = "Düsterbruch - Nord",
- ["Dire Maul - West"] = "Düsterbruch - West",
- ["Razorfen Downs"] = "Hügel der Klingenhauer",
- ["Razorfen Kraul"] = "Kral der Klingenhauer",
- ["Wailing Caverns"] = "Höhlen des Wehklagens",
- ["Zul'Farrak"] = "Zul'Farrak",
- ["Black Morass"] = "Höhlen der Zeit: Schwarzer Morast",
- ["Gnomeregan"] = "Gnomeregan",
- ["Stormwrought Ruins"] = "Ruine von Sturmschmied",
- ["Stormwrought Ruins - Back Entrance"] = "Ruine von Sturmschmied - Hintereingang",
- ["Blackrock Depths"] = "Schwarzfelstiefen",
- ["Hateforge Quarry"] = "Hassschmieden Steinbruch",
- ["Lower Blackrock Spire"] = "Untere Schwarzfelsspitze",
- ["Upper Blackrock Spire"] = "Obere Schwarzfelsspitze",
- ["Karazhan Crypt"] = "Karazhan Krypta",
- ["Scarlet Monastery"] = "Das Scharlachrote Kloster",
- ["Scarlet Monastery - Armory"] = "Das Scharlachrote Kloster - Waffenkammer",
- ["Scarlet Monastery - Cathedral"] = "Das Scharlachrote Kloster - Kathedrale",
- ["Scarlet Monastery - Graveyard"] = "Das Scharlachrote Kloster - Friedhof",
- ["Scarlet Monastery - Library"] = "Das Scharlachrote Kloster - Bibliothek",
- ["Scholomance"] = "Scholomance",
- ["Shadowfang Keep"] = "Burg Schattenfang",
- ["Stratholme"] = "Stratholme",
- ["Stratholme - Back Gate"] = "Stratholme - Hintertor",
- ["The Deadmines"] = "Die Todesminen",
- ["The Stockade"] = "Das Verlies",
- ["Stormwind Vault"] = "Tresor von Sturmwind",
- ["Stormwind Vault - Horde Entrance"] = "Tresor von Sturmwind - Horde-Eingang",
- ["Gilneas City"] = "Stadt von Gilneas",
- ["The Sunken Temple"] = "Der Versunkene Tempel",
- ["Dragonmaw Retreat"] = "Zuflucht des Drachenmals",
- ["Uldaman"] = "Uldaman",
- ["Uldaman - Main Entrance"] = "Uldaman - Haupteingang",
- ["Uldaman - Back Entrance"] = "Uldaman - Hintereingang",
- ["Frostmane Hollow"] = "Frostmane Hollow",
- ["Windhorn Canyon"] = "Schlucht der heulenden Winde",
-
- -- Raids
- ["Onyxia's Lair"] = "Onyxias Hort",
- ["Emerald Sanctum"] = "Smaragdsanktum",
- ["Ruins of Ahn'Qiraj"] = "Ruinen von Ahn'Qiraj",
- ["Temple of Ahn'Qiraj"] = "Tempel von Ahn'Qiraj",
- ["Blackwing Lair"] = "Pechschwingenhort",
- ["Molten Core"] = "Geschmolzener Kern",
- ["Lower Karazhan Halls"] = "Untere Hallen von Karazhan",
- ["Tower of Karazhan"] = "Turm von Karazhan",
- ["Naxxramas"] = "Naxxramas",
- ["Zul'Gurub"] = "Zul'Gurub",
- ["Timbermaw Hold"] = "Holzschlundfeste",
-
- -- World Bosses
- ["Taerar"] = "Taerar",
- ["Lethon"] = "Lethon",
- ["Emeriss"] = "Emeriss",
- ["Ysondre"] = "Ysondre",
- ["Azuregos"] = "Azuregos",
- ["Cla'ckora"] = "Cla'ckora",
- ["Concavius"] = "Concavius",
- ["Father Lycan"] = "Vater Lycan",
- ["Lord Kazzak"] = "Lord Kazzak",
- ["Ostarius"] = "Ostarius",
- ["Dark Reaver of Karazhan"] = "Dunkler Plünderer von Karazhan",
- ["Nerubian Overseer"] = "Nerubischer Aufseher",
-
- -- Transports
- ["Boat to Stormwind"] = "Schiff nach Sturmwind",
- ["Boat to Alah'Thalas"] = "Schiff nach Alah'Thalas",
- ["Boat to Rut'Theran Village"] = "Schiff nach Rut'Theran",
- ["Boat to Auberdine"] = "Schiff nach Auberdine",
- ["Boat to Menethil Harbor"] = "Schiff zum Hafen von Menethil",
- ["Boat to Theramore Isle"] = "Schiff zur Insel Theramore",
- ["Boat to Booty Bay"] = "Schiff zur Beutebucht",
- ["Boat to Ratchet"] = "Schiff nach Ratschet",
- ["Boat to Forgotten Coast"] = "Schiff zur Vergessenen Küste",
- ["Boat to Sardor Isle"] = "Schiff zur Sardor-Insel",
- ["Boat to Revantusk Village"] = "Schiff zum Dorf der Bruchhauer",
- ["Boat to Sparkwater Port"] = "Schiff zum Funkelwasserhafen",
- ["Boat to Shadowprey Village"] = "Schiff nach Schattenflucht",
- ["Boat to Moonhoof Village"] = "Schiff nach Mondhuf",
- ["Zeppelins to UC & Grom'Gol"] = "Zeppeline nach Unterstadt & Grom’gol",
- ["Zeppelins to TB & Kargath"] = "Zeppeline nach Donnerfels & Kargath",
- ["Zeppelin to Orgrimmar"] = "Zeppelin nach Orgrimmar",
- ["Zeppelins to Orgrimmar & Grom'Gol"] = "Zeppeline nach Orgrimmar & Grom'Gol",
- ["Zeppelins to UC & Orgrimmar"] = "Zeppeline nach Unterstadt & Orgrimmar",
- ["Tram to Stormwind"] = "Tiefenbahn nach Sturmwind",
- ["Tram to Ironforge"] = "Tiefenbahn nach Eisenschmiede",
+ModernMapMarkers_Locale:RegisterDictionary("deDE", {
+ ["Kalimdor"] = "Kalimdor",
+ ["Eastern Kingdoms"] = "Östliche Königreiche",
+ -- Dungeon masks
+ -- %s will get substituted with the localized Dungeon name
+ ["%s - East"] = "%s - Ost", -- Dire Maul
+ ["%s - West"] = "%s - West", -- Dire Maul
+ ["%s - North"] = "%s - Nord", -- Dire Maul
+ ["%s - Back Entrance"] = "%s - Hintereingang", -- Stormwrought Ruins, Uldaman
+ ["Lower %s"] = "Untere %s", -- Lower Blackrock Spire
+ ["Upper %s"] = "Obere %s", -- Upper Blackrock Spire
+ ["%s - Armory"] = "%s - Waffenkammer", -- Scarlet Monastery
+ ["%s - Cathedral"] = "%s - Kathedrale", -- Scarlet Monastery
+ ["%s - Graveyard"] = "%s - Friedhof", -- Scarlet Monastery
+ ["%s - Library"] = "%s - Bibliothek", -- Scarlet Monastery
+ ["%s - Back Gate"] = "%s - Hintertor", -- Stratholme
+ ["%s - Horde Entrance"] = "%s - Horde-Eingang", -- Stormwind Vault
+ ["%s - Main Entrance"] = "%s - Haupteingang", -- Uldaman
+
+ -- Transport masks
+ -- %s will get substituted with the destination's localized name
+ ["Boat to %s"] = "Schiff nach %s",
+ ["Zeppelin to %s"] = "Zeppelin nach %s",
+ ["Tram to %s"] = "Tiefenbahn nach %s",
+ -- translations that need manual adjustment
+ ["Boat to Menethil Harbor"] = "Schiff zum Hafen von Menethil",
+ ["Boat to Theramore Isle"] = "Schiff zur Insel Theramore",
+ ["Boat to Sparkwater Port"] = "Schiff zum Funkelwasserhafen",
+ ["Boat to The Forgotten Coast"] = "Schiff zur Vergessenen Küste",
+ ["Boat to Sardor Isle"] = "Schiff zur Insel Sardor",
+ ["Boat to Revantusk Village"] = "Schiff zum Dorf der Bruchhauer",
+ ["Zeppelin to Grom'gol Base Camp"] = "Zeppelin zum Basislager von Grom'gol",
-- Tooltip labels
["Level"] = "Stufe",
diff --git a/Locales/Locale_esES.lua b/Locales/Locale_esES.lua
index 6b2320e..8ab86d2 100644
--- a/Locales/Locale_esES.lua
+++ b/Locales/Locale_esES.lua
@@ -1,140 +1,29 @@
--- Locale_esES.lua - Spanish Localization for ModernMapMarkers
-ModernMapMarkers_Locale:RegisterZones("esES", {
- -- Continent names
+ModernMapMarkers_Locale:RegisterDictionary("esES", {
["Kalimdor"] = "Kalimdor",
["Eastern Kingdoms"] = "Reinos del Este",
- ["Ashenvale"] = "Vallefresno",
- ["Azshara"] = "Azshara",
- ["Badlands"] = "Tierras Inhóspitas",
- ["Balor"] = "Balor",
- ["Blasted Lands"] = "Las Tierras Devastadas",
- ["Burning Steppes"] = "Las Estepas Ardientes",
- ["Darkshore"] = "Costa Oscura",
- ["Deadwind Pass"] = "Paso de la Muerte",
- ["Desolace"] = "Desolace",
- ["Dun Morogh"] = "Dun Morogh",
- ["Durotar"] = "Durotar",
- ["Duskwood"] = "Bosque del Ocaso",
- ["Dustwallow Marsh"] = "Marjal Revolcafango",
- ["Eastern Plaguelands"] = "Tierras de la Peste del Este",
- ["Elwynn Forest"] = "Bosque de Elwynn",
- ["Feralas"] = "Feralas",
- ["Gilneas"] = "Gilneas",
- ["Hyjal"] = "Hyjal",
- ["Ironforge"] = "Forjaz",
- ["Moonwhisper Coast"] = "Costa del Susurro de Luna",
- ["Orgrimmar"] = "Orgrimmar",
- ["Searing Gorge"] = "La Garganta de Fuego",
- ["Silithus"] = "Silithus",
- ["Silverpine Forest"] = "Bosque de Argénteos",
- ["Stormwind City"] = "Ciudad de Ventormenta",
- ["Stranglethorn Vale"] = "Vega de Tuercespina",
- ["Swamp of Sorrows"] = "Pantano de las Penas",
- ["Tanaris"] = "Tanaris",
- ["Teldrassil"] = "Teldrassil",
- ["The Barrens"] = "Los Baldíos",
- ["The Hinterlands"] = "Tierras del Interior",
- ["Thunder Bluff"] = "Cima del Trueno",
- ["Tirisfal Glades"] = "Claros de Tirisfal",
- ["Western Plaguelands"] = "Tierras de la Peste del Oeste",
- ["Westfall"] = "Páramos de Poniente",
- ["Wetlands"] = "Los Humedales",
- ["Alah'Thalas"] = "Alah'Thalas",
-})
-
-ModernMapMarkers_Locale:RegisterMarkers("esES", {
- -- Dungeons
- ["Blackfathom Deeps"] = "Cavernas de Brazanegra",
- ["Crescent Grove"] = "Arboleda de la Media Luna",
- ["Maraudon"] = "Maraudon",
- ["Ragefire Chasm"] = "Sima Ígnea",
- ["Dire Maul"] = "La Masacre",
- ["Dire Maul - East"] = "La Masacre - Este",
- ["Dire Maul - North"] = "La Masacre - Norte",
- ["Dire Maul - West"] = "La Masacre - Oeste",
- ["Razorfen Downs"] = "Zahúrda Rajacieno",
- ["Razorfen Kraul"] = "Madriguera de Rajacieno",
- ["Wailing Caverns"] = "Cuevas de los Lamentos",
- ["Zul'Farrak"] = "Zul'Farrak",
- ["Black Morass"] = "La Ciénaga Negra",
- ["Gnomeregan"] = "Gnomeregan",
- ["Stormwrought Ruins"] = "Ruinas Forjatormenta",
- ["Stormwrought Ruins - Back Entrance"] = "Ruinas Forjatormenta - Entrada Trasera",
- ["Blackrock Depths"] = "Profundidades de Roca Negra",
- ["Hateforge Quarry"] = "Cantera Hateforge",
- ["Lower Blackrock Spire"] = "Cumbre de Roca Negra Inferior",
- ["Upper Blackrock Spire"] = "Cumbre de Roca Negra Superior",
- ["Karazhan Crypt"] = "Cripta de Karazhan",
- ["Scarlet Monastery"] = "Monasterio Escarlata",
- ["Scarlet Monastery - Armory"] = "Monasterio Escarlata - Armería",
- ["Scarlet Monastery - Cathedral"] = "Monasterio Escarlata - Catedral",
- ["Scarlet Monastery - Graveyard"] = "Monasterio Escarlata - Cementerio",
- ["Scarlet Monastery - Library"] = "Monasterio Escarlata - Biblioteca",
- ["Scholomance"] = "Scholomance",
- ["Shadowfang Keep"] = "Fortaleza Colmillo Oscuro",
- ["Stratholme"] = "Stratholme",
- ["Stratholme - Back Gate"] = "Stratholme - Puerta Trasera",
- ["The Deadmines"] = "Las Minas de la Muerte",
- ["The Stockade"] = "La Mazmorras",
- ["Stormwind Vault"] = "Bóveda de Ventormenta",
- ["Stormwind Vault - Horde Entrance"] = "Bóveda de Ventormenta - Entrada Horda",
- ["Gilneas City"] = "Ciudad Gilneas",
- ["The Sunken Temple"] = "El Templo Sumergido",
- ["Dragonmaw Retreat"] = "Refugio Faucedrago",
- ["Uldaman"] = "Uldaman",
- ["Uldaman - Main Entrance"] = "Uldaman - Entrada Principal",
- ["Uldaman - Back Entrance"] = "Uldaman - Entrada Trasera",
- ["Frostmane Hollow"] = "Frostmane Hollow",
- ["Windhorn Canyon"] = "Windhorn Canyon",
-
- -- Raids
- ["Onyxia's Lair"] = "Guarida de Onyxia",
- ["Emerald Sanctum"] = "Sanctum Esmeralda",
- ["Ruins of Ahn'Qiraj"] = "Ruinas de Ahn'Qiraj",
- ["Temple of Ahn'Qiraj"] = "Templo de Ahn'Qiraj",
- ["Blackwing Lair"] = "Guarida Alanegra",
- ["Molten Core"] = "Núcleo de Magma",
- ["Lower Karazhan Halls"] = "Salas Inferiores de Karazhan",
- ["Tower of Karazhan"] = "Torre de Karazhan",
- ["Naxxramas"] = "Naxxramas",
- ["Zul'Gurub"] = "Zul'Gurub",
- ["Timbermaw Hold"] = "Timbermaw Hold",
-
- -- World Bosses
- ["Taerar"] = "Taerar",
- ["Lethon"] = "Lethon",
- ["Emeriss"] = "Emeriss",
- ["Ysondre"] = "Ysondre",
- ["Azuregos"] = "Azuregos",
- ["Cla'ckora"] = "Cla'ckora",
- ["Concavius"] = "Concavius",
- ["Father Lycan"] = "Padre Lycan",
- ["Lord Kazzak"] = "Lord Kazzak",
- ["Ostarius"] = "Ostarius",
- ["Dark Reaver of Karazhan"] = "Saqueador Oscuro de Karazhan",
- ["Nerubian Overseer"] = "Supervisor Nerubiano",
-
- -- Transports
- ["Boat to Stormwind"] = "Barco a Ventormenta",
- ["Boat to Alah'Thalas"] = "Barco a Alah'Thalas",
- ["Boat to Rut'Theran Village"] = "Barco a Aldea Rut'Theran",
- ["Boat to Auberdine"] = "Barco a Auberdine",
- ["Boat to Menethil Harbor"] = "Barco a Puerto de Menethil",
- ["Boat to Theramore Isle"] = "Barco a Isla Theramore",
- ["Boat to Booty Bay"] = "Barco a Bahía del Botín",
- ["Boat to Ratchet"] = "Barco a Trinquete",
- ["Boat to Forgotten Coast"] = "Barco a Costa Olvidada",
- ["Boat to Sardor Isle"] = "Barco a Isla Sardor",
- ["Boat to Revantusk Village"] = "Barco a Aldea Revantusk",
- ["Boat to Sparkwater Port"] = "Barco a Puerto Chispeagua",
- ["Zeppelins to UC & Grom'Gol"] = "Zepelines a UC y Grom'Gol",
- ["Zeppelins to TB & Kargath"] = "Zepelines a TB y Kargath",
- ["Zeppelin to Orgrimmar"] = "Zepelín a Orgrimmar",
- ["Zeppelins to Orgrimmar & Grom'Gol"] = "Zepelines a Orgrimmar y Grom'Gol",
- ["Zeppelins to UC & Orgrimmar"] = "Zepelines a UC y Orgrimmar",
- ["Tram to Stormwind"] = "Tranvía a Ventormenta",
- ["Tram to Ironforge"] = "Tranvía a Forjaz",
+ -- Dungeon masks
+ -- %s will get substituted with the localized Dungeon name
+ ["%s - East"] = "%s - Este", -- Dire Maul
+ ["%s - West"] = "%s - Oeste", -- Dire Maul
+ ["%s - North"] = "%s - Norte", -- Dire Maul
+ ["%s - Back Entrance"] = "%s - Entrada Trasera", -- Stormwrought Ruins, Uldaman
+ ["Lower %s"] = "%s Inferior", -- Lower Blackrock Spire
+ ["Upper %s"] = "%s Superior", -- Upper Blackrock Spire
+ ["%s - Armory"] = "%s - Armería", -- Scarlet Monastery
+ ["%s - Cathedral"] = "%s - Catedral", -- Scarlet Monastery
+ ["%s - Graveyard"] = "%s - Cementerio", -- Scarlet Monastery
+ ["%s - Library"] = "%s - Biblioteca", -- Scarlet Monastery
+ ["%s - Back Gate"] = "%s - Puerta Trasera", -- Stratholme
+ ["%s - Horde Entrance"] = "%s - Entrada Horda", -- Stormwind Vault
+ ["%s - Main Entrance"] = "%s - Entrada Principal", -- Uldaman
+
+ -- Transport masks
+ -- %s will get substituted with the destination's localized name
+ ["Boat to %s"] = "Barco a %s",
+ ["Zeppelin to %s"] = "Zepelín a %s",
+ ["Tram to %s"] = "Tranvía a %s",
-- Tooltip labels
["Level"] = "Nivel",
diff --git a/Locales/Locale_ptBR.lua b/Locales/Locale_ptBR.lua
index 64a78db..3371536 100644
--- a/Locales/Locale_ptBR.lua
+++ b/Locales/Locale_ptBR.lua
@@ -1,140 +1,29 @@
--- Locale_ptBR.lua - Portuguese (Brazil) Localization for ModernMapMarkers
-ModernMapMarkers_Locale:RegisterZones("ptBR", {
- -- Continent names
+ModernMapMarkers_Locale:RegisterDictionary("ptBR", {
["Kalimdor"] = "Kalimdor",
["Eastern Kingdoms"] = "Reinos do Leste",
- ["Ashenvale"] = "Vale Gris",
- ["Azshara"] = "Azshara",
- ["Badlands"] = "Ermos",
- ["Balor"] = "Balor",
- ["Blasted Lands"] = "Barreira do Inferno",
- ["Burning Steppes"] = "Estepes Ardentes",
- ["Darkshore"] = "Costa Negra",
- ["Deadwind Pass"] = "Trilha do Vento Morto",
- ["Desolace"] = "Desolação",
- ["Dun Morogh"] = "Dun Morogh",
- ["Durotar"] = "Durotar",
- ["Duskwood"] = "Floresta do Crepúsculo",
- ["Dustwallow Marsh"] = "Pântano Vadeoso",
- ["Eastern Plaguelands"] = "Terras Pestilentas Orientais",
- ["Elwynn Forest"] = "Floresta de Elwynn",
- ["Feralas"] = "Feralas",
- ["Gilneas"] = "Gilneas",
- ["Hyjal"] = "Hyjal",
- ["Ironforge"] = "Altaforja",
- ["Moonwhisper Coast"] = "Costa do Sussurro da Lua",
- ["Orgrimmar"] = "Orgrimmar",
- ["Searing Gorge"] = "Garganta Abrasadora",
- ["Silithus"] = "Silithus",
- ["Silverpine Forest"] = "Floresta de Pinhaprata",
- ["Stormwind City"] = "Ventobravo",
- ["Stranglethorn Vale"] = "Selva do Espinhaço",
- ["Swamp of Sorrows"] = "Pântano das Mágoas",
- ["Tanaris"] = "Tanaris",
- ["Teldrassil"] = "Teldrassil",
- ["The Barrens"] = "Sertões",
- ["The Hinterlands"] = "Terras Agrestes",
- ["Thunder Bluff"] = "Penhasco do Trovão",
- ["Tirisfal Glades"] = "Clareiras de Tirisfal",
- ["Western Plaguelands"] = "Terras Pestilentas Ocidentais",
- ["Westfall"] = "Cerro Oeste",
- ["Wetlands"] = "Pantanal",
- ["Alah'Thalas"] = "Alah'Thalas",
-})
-
-ModernMapMarkers_Locale:RegisterMarkers("ptBR", {
- -- Dungeons
- ["Blackfathom Deeps"] = "Profundezas Negras",
- ["Crescent Grove"] = "Bosque do Crescente",
- ["Maraudon"] = "Maraudon",
- ["Ragefire Chasm"] = "Cavernas Ragefire",
- ["Dire Maul"] = "Gládio Cruel",
- ["Dire Maul - East"] = "Gládio Cruel - Leste",
- ["Dire Maul - North"] = "Gládio Cruel - Norte",
- ["Dire Maul - West"] = "Gládio Cruel - Oeste",
- ["Razorfen Downs"] = "Urzal dos Mortos",
- ["Razorfen Kraul"] = "Razorfen Kraul",
- ["Wailing Caverns"] = "Cavernas Uivantes",
- ["Zul'Farrak"] = "Zul'Farrak",
- ["Black Morass"] = "O Morass Negro",
- ["Gnomeregan"] = "Gnomeregan",
- ["Stormwrought Ruins"] = "Ruínas da Tempestade",
- ["Stormwrought Ruins - Back Entrance"] = "Ruínas da Tempestade - Entrada Traseira",
- ["Blackrock Depths"] = "Abismo Rocha Negra",
- ["Hateforge Quarry"] = "Pedreira Forja do Ódio",
- ["Lower Blackrock Spire"] = "Pico da Rocha Negra Inferior",
- ["Upper Blackrock Spire"] = "Pico da Rocha Negra Superior",
- ["Karazhan Crypt"] = "Cripta Karazhan",
- ["Scarlet Monastery"] = "Monastério Scarlet",
- ["Scarlet Monastery - Armory"] = "Monastério Scarlet - Arsenal",
- ["Scarlet Monastery - Cathedral"] = "Monastério Scarlet - Catedral",
- ["Scarlet Monastery - Graveyard"] = "Monastério Scarlet - Cemitério",
- ["Scarlet Monastery - Library"] = "Monastério Scarlet - Biblioteca",
- ["Scholomance"] = "Scholomance",
- ["Shadowfang Keep"] = "Fortaleza Shadowfang",
- ["Stratholme"] = "Stratholme",
- ["Stratholme - Back Gate"] = "Stratholme - Portão Traseiro",
- ["The Deadmines"] = "As Minas Mortas",
- ["The Stockade"] = "A Prisão",
- ["Stormwind Vault"] = "Cofre Stormwind",
- ["Stormwind Vault - Horde Entrance"] = "Cofre Stormwind - Entrada da Horda",
- ["Gilneas City"] = "Cidade de Gilneas",
- ["The Sunken Temple"] = "O Templo Abaixo d'Água",
- ["Dragonmaw Retreat"] = "Refúgio Presa do Dragão",
- ["Uldaman"] = "Uldaman",
- ["Uldaman - Main Entrance"] = "Uldaman - Entrada Principal",
- ["Uldaman - Back Entrance"] = "Uldaman - Entrada Traseira",
- ["Frostmane Hollow"] = "Frostmane Hollow",
- ["Windhorn Canyon"] = "Windhorn Canyon",
-
- -- Raids
- ["Onyxia's Lair"] = "Caverna de Onyxia",
- ["Emerald Sanctum"] = "Santuário Esmeralda",
- ["Ruins of Ahn'Qiraj"] = "Ruínas de Ahn'Qiraj",
- ["Temple of Ahn'Qiraj"] = "Templo de Ahn'Qiraj",
- ["Blackwing Lair"] = "Covil Asa Negra",
- ["Molten Core"] = "Núcleo Derretido",
- ["Lower Karazhan Halls"] = "Salões Inferiores de Karazhan",
- ["Tower of Karazhan"] = "Torre de Karazhan",
- ["Naxxramas"] = "Naxxramas",
- ["Zul'Gurub"] = "Zul'Gurub",
- ["Timbermaw Hold"] = "Timbermaw Hold",
-
- -- World Bosses
- ["Taerar"] = "Taerar",
- ["Lethon"] = "Lethon",
- ["Emeriss"] = "Emeriss",
- ["Ysondre"] = "Ysondre",
- ["Azuregos"] = "Azuregos",
- ["Cla'ckora"] = "Cla'ckora",
- ["Concavius"] = "Concavius",
- ["Father Lycan"] = "Pai Lycan",
- ["Lord Kazzak"] = "Lorde Kazzak",
- ["Ostarius"] = "Ostarius",
- ["Dark Reaver of Karazhan"] = "Saqueador Sombrio de Karazhan",
- ["Nerubian Overseer"] = "Supervisor Nerubiano",
-
- -- Transports
- ["Boat to Stormwind"] = "Barco para Ventobravo",
- ["Boat to Alah'Thalas"] = "Barco para Alah'Thalas",
- ["Boat to Rut'Theran Village"] = "Barco para Aldeia Rut'Theran",
- ["Boat to Auberdine"] = "Barco para Auberdine",
- ["Boat to Menethil Harbor"] = "Barco para Puerto Menethil",
- ["Boat to Theramore Isle"] = "Barco para Ilha Theramore",
- ["Boat to Booty Bay"] = "Barco para Angra do Butim",
- ["Boat to Ratchet"] = "Barco para Carroça",
- ["Boat to Forgotten Coast"] = "Barco para Costa Esquecida",
- ["Boat to Sardor Isle"] = "Barco para Ilha Sardor",
- ["Boat to Revantusk Village"] = "Barco para Aldeia Revantusk",
- ["Boat to Sparkwater Port"] = "Barco para Porto Sparkwater",
- ["Zeppelins to UC & Grom'Gol"] = "Zepelins para UC e Grom'Gol",
- ["Zeppelins to TB & Kargath"] = "Zepelins para TB e Kargath",
- ["Zeppelin to Orgrimmar"] = "Zepelim para Orgrimmar",
- ["Zeppelins to Orgrimmar & Grom'Gol"] = "Zepelins para Orgrimmar e Grom'Gol",
- ["Zeppelins to UC & Orgrimmar"] = "Zepelins para UC e Orgrimmar",
- ["Tram to Stormwind"] = "Trem para Ventobravo",
- ["Tram to Ironforge"] = "Trem para Altaforja",
+ -- Dungeon masks
+ -- %s will get substituted with the localized Dungeon name
+ ["%s - East"] = "%s - Leste", -- Dire Maul
+ ["%s - West"] = "%s - Oeste", -- Dire Maul
+ ["%s - North"] = "%s - Norte", -- Dire Maul
+ ["%s - Back Entrance"] = "%s - Entrada Traseira", -- Stormwrought Ruins, Uldaman
+ ["Lower %s"] = "%s Inferior", -- Lower Blackrock Spire
+ ["Upper %s"] = "%s Superior", -- Upper Blackrock Spire
+ ["%s - Armory"] = "%s - Arsenal", -- Scarlet Monastery
+ ["%s - Cathedral"] = "%s - Catedral", -- Scarlet Monastery
+ ["%s - Graveyard"] = "%s - Cemitério", -- Scarlet Monastery
+ ["%s - Library"] = "%s - Biblioteca", -- Scarlet Monastery
+ ["%s - Back Gate"] = "%s - Portão Traseiro", -- Stratholme
+ ["%s - Horde Entrance"] = "%s - Entrada da Horda", -- Stormwind Vault
+ ["%s - Main Entrance"] = "%s - Entrada Principal", -- Uldaman
+
+ -- Transport masks
+ -- %s will get substituted with the destination's localized name
+ ["Boat to %s"] = "Barco para %s",
+ ["Zeppelin to %s"] = "Zepelim para %s",
+ ["Tram to %s"] = "Trem para %s",
-- Tooltip labels
["Level"] = "Nível",
diff --git a/Locales/Locale_zhCN.lua b/Locales/Locale_zhCN.lua
index 91bcc5a..b8f9bd6 100644
--- a/Locales/Locale_zhCN.lua
+++ b/Locales/Locale_zhCN.lua
@@ -1,140 +1,29 @@
--- Locale_zhCN.lua - Simplified Chinese Localization for ModernMapMarkers
-ModernMapMarkers_Locale:RegisterZones("zhCN", {
- -- Continent names
+ModernMapMarkers_Locale:RegisterDictionary("zhCN", {
["Kalimdor"] = "卡利姆多",
["Eastern Kingdoms"] = "东部王国",
- ["Ashenvale"] = "灰谷",
- ["Azshara"] = "艾萨拉",
- ["Badlands"] = "荒芜之地",
- ["Balor"] = "巴洛",
- ["Blasted Lands"] = "诅咒之地",
- ["Burning Steppes"] = "燃烧平原",
- ["Darkshore"] = "黑海岸",
- ["Deadwind Pass"] = "逆风小径",
- ["Desolace"] = "凄凉之地",
- ["Dun Morogh"] = "丹莫罗",
- ["Durotar"] = "杜隆塔尔",
- ["Duskwood"] = "暮色森林",
- ["Dustwallow Marsh"] = "尘泥沼泽",
- ["Eastern Plaguelands"] = "东瘟疫之地",
- ["Elwynn Forest"] = "艾尔文森林",
- ["Feralas"] = "菲拉斯",
- ["Gilneas"] = "吉尔尼斯",
- ["Hyjal"] = "海加尔山",
- ["Ironforge"] = "铁炉堡",
- ["Moonwhisper Coast"] = "月语海岸",
- ["Orgrimmar"] = "奥格瑞玛",
- ["Searing Gorge"] = "灼热峡谷",
- ["Silithus"] = "希利苏斯",
- ["Silverpine Forest"] = "银松森林",
- ["Stormwind City"] = "暴风城",
- ["Stranglethorn Vale"] = "荆棘谷",
- ["Swamp of Sorrows"] = "悲伤沼泽",
- ["Tanaris"] = "塔纳利斯",
- ["Teldrassil"] = "泰达希尔",
- ["The Barrens"] = "贫瘠之地",
- ["The Hinterlands"] = "辛特兰",
- ["Thunder Bluff"] = "雷霆崖",
- ["Tirisfal Glades"] = "提瑞斯法林地",
- ["Western Plaguelands"] = "西瘟疫之地",
- ["Westfall"] = "西部荒野",
- ["Wetlands"] = "湿地",
- ["Alah'Thalas"] = "阿尔萨拉斯",
-})
-
-ModernMapMarkers_Locale:RegisterMarkers("zhCN", {
- -- Dungeons
- ["Blackfathom Deeps"] = "黑暗深渊",
- ["Crescent Grove"] = "新月林地",
- ["Maraudon"] = "玛拉顿",
- ["Ragefire Chasm"] = "怒焰裂谷",
- ["Dire Maul"] = "厄运之槌",
- ["Dire Maul - East"] = "厄运之槌 - 东",
- ["Dire Maul - North"] = "厄运之槌 - 北",
- ["Dire Maul - West"] = "厄运之槌 - 西",
- ["Razorfen Downs"] = "剃刀高地",
- ["Razorfen Kraul"] = "剃刀沼泽",
- ["Wailing Caverns"] = "哀嚎洞穴",
- ["Zul'Farrak"] = "祖尔法拉克",
- ["Black Morass"] = "黑色沼泽",
- ["Gnomeregan"] = "诺莫瑞根",
- ["Stormwrought Ruins"] = "风暴废墟",
- ["Stormwrought Ruins - Back Entrance"] = "风暴废墟 - 后门",
- ["Blackrock Depths"] = "黑石深渊",
- ["Hateforge Quarry"] = "仇恨熔炉采石场",
- ["Lower Blackrock Spire"] = "黑石塔下层",
- ["Upper Blackrock Spire"] = "黑石塔上层",
- ["Karazhan Crypt"] = "卡拉赞墓穴",
- ["Scarlet Monastery"] = "血色修道院",
- ["Scarlet Monastery - Armory"] = "血色修道院 - 兵器库",
- ["Scarlet Monastery - Cathedral"] = "血色修道院 - 教堂",
- ["Scarlet Monastery - Graveyard"] = "血色修道院 - 墓地",
- ["Scarlet Monastery - Library"] = "血色修道院 - 图书馆",
- ["Scholomance"] = "通灵学院",
- ["Shadowfang Keep"] = "影牙城堡",
- ["Stratholme"] = "斯坦索姆",
- ["Stratholme - Back Gate"] = "斯坦索姆 - 后门",
- ["The Deadmines"] = "死亡矿井",
- ["The Stockade"] = "监狱",
- ["Stormwind Vault"] = "暴风城地牢",
- ["Stormwind Vault - Horde Entrance"] = "暴风城地牢 - 部落入口",
- ["Gilneas City"] = "吉尔尼斯城",
- ["The Sunken Temple"] = "沉没的神庙",
- ["Dragonmaw Retreat"] = "龙喉居所",
- ["Uldaman"] = "奥达曼",
- ["Uldaman - Main Entrance"] = "奥达曼 - 主入口",
- ["Uldaman - Back Entrance"] = "奥达曼 - 后入口",
- ["Frostmane Hollow"] = "霜鬃谷",
- ["Windhorn Canyon"] = "风角峡谷",
-
- -- Raids
- ["Onyxia's Lair"] = "奥妮克希亚的巢穴",
- ["Emerald Sanctum"] = "翡翠圣殿",
- ["Ruins of Ahn'Qiraj"] = "安其拉废墟",
- ["Temple of Ahn'Qiraj"] = "安其拉神殿",
- ["Blackwing Lair"] = "黑翼之巢",
- ["Molten Core"] = "熔火之心",
- ["Lower Karazhan Halls"] = "卡拉赞下层大厅",
- ["Tower of Karazhan"] = "卡拉赞之塔",
- ["Naxxramas"] = "纳克萨玛斯",
- ["Zul'Gurub"] = "祖尔格拉布",
- ["Timbermaw Hold"] = "木喉要塞",
-
- -- World Bosses
- ["Taerar"] = "塔拉尔",
- ["Lethon"] = "莱索恩",
- ["Emeriss"] = "艾莫莉丝",
- ["Ysondre"] = "伊森德雷",
- ["Azuregos"] = "艾索雷葛斯",
- ["Cla'ckora"] = "克拉科拉",
- ["Concavius"] = "空卡维斯",
- ["Father Lycan"] = "人狼神父",
- ["Lord Kazzak"] = "卡扎克",
- ["Ostarius"] = "奥兹塔里亚斯",
- ["Dark Reaver of Karazhan"] = "卡拉赞黑暗掠夺者",
- ["Nerubian Overseer"] = "蛛怪监工",
-
- -- Transports
- ["Boat to Stormwind"] = "前往暴风城",
- ["Boat to Alah'Thalas"] = "前往阿尔萨拉斯",
- ["Boat to Rut'Theran Village"] = "前往鲁瑟兰村",
- ["Boat to Auberdine"] = "前往奥伯丁",
- ["Boat to Menethil Harbor"] = "前往米奈希尔港",
- ["Boat to Theramore Isle"] = "前往塞拉摩岛",
- ["Boat to Booty Bay"] = "前往藏宝海湾",
- ["Boat to Ratchet"] = "前往棘齿城",
- ["Boat to Forgotten Coast"] = "前往被遗忘的海岸",
- ["Boat to Sardor Isle"] = "前往萨多尔岛",
- ["Boat to Revantusk Village"] = "前往恶齿村",
- ["Boat to Sparkwater Port"] = "前往怒水港",
- ["Zeppelins to UC & Grom'Gol"] = "飞艇前往幽暗城和格罗姆高",
- ["Zeppelins to TB & Kargath"] = "飞艇前往雷霆崖和卡加斯",
- ["Zeppelin to Orgrimmar"] = "飞艇前往奥格瑞玛",
- ["Zeppelins to Orgrimmar & Grom'Gol"] = "飞艇前往奥格瑞玛和格罗姆高",
- ["Zeppelins to UC & Orgrimmar"] = "飞艇前往幽暗城和奥格瑞玛",
- ["Tram to Stormwind"] = "地铁前往暴风城",
- ["Tram to Ironforge"] = "地铁前往铁炉堡",
+ -- Dungeon masks
+ -- %s will get substituted with the localized Dungeon name
+ ["%s - East"] = "%s - 东", -- Dire Maul
+ ["%s - West"] = "%s - 西", -- Dire Maul
+ ["%s - North"] = "%s - 北", -- Dire Maul
+ ["%s - Back Entrance"] = "%s - 后门", -- Stormwrought Ruins, Uldaman
+ ["Lower %s"] = "%s下层", -- Lower Blackrock Spire
+ ["Upper %s"] = "%s上层", -- Upper Blackrock Spire
+ ["%s - Armory"] = "%s - 军械库", -- Scarlet Monastery
+ ["%s - Cathedral"] = "%s - 大教堂", -- Scarlet Monastery
+ ["%s - Graveyard"] = "%s - 墓地", -- Scarlet Monastery
+ ["%s - Library"] = "%s - 图书馆", -- Scarlet Monastery
+ ["%s - Back Gate"] = "%s - 后门", -- Stratholme
+ ["%s - Horde Entrance"] = "%s - 部落入口", -- Stormwind Vault
+ ["%s - Main Entrance"] = "%s - 主入口", -- Uldaman
+
+ -- Transport masks
+ -- %s will get substituted with the destination's localized name
+ ["Boat to %s"] = "乘船前往%s",
+ ["Zeppelin to %s"] = "乘飞艇前往%s",
+ ["Tram to %s"] = "地铁前往%s",
-- Tooltip labels
["Level"] = "等级",
diff --git a/Localization.lua b/Localization.lua
index 0a95d27..4033b32 100644
--- a/Localization.lua
+++ b/Localization.lua
@@ -1,77 +1,117 @@
---- Localization.lua - ModernMapMarkers Localization Core
---- Provides zone name reverse-lookup (localized -> English) and
---- marker name translation (English -> localized) for non-English clients.
-
-ModernMapMarkers_Locale = {}
-
--- Registered locale tables: ["deDE"] = { Zones = {}, Markers = {} }, ...
-local LocaleData = {}
-
--- Active locale tables (set on VARIABLES_LOADED)
-local ActiveZones = nil -- English -> Localized zone names
-local ReverseZones = nil -- Localized -> English zone names
-local ActiveMarkers = nil -- English -> Localized marker/instance names
-
-local detectedLocale = nil
-
---- Register a locale's zone translations.
-function ModernMapMarkers_Locale:RegisterZones(locale, tbl)
- if not LocaleData[locale] then LocaleData[locale] = {} end
- LocaleData[locale].Zones = tbl
-end
-
---- Register a locale's marker/instance name translations.
-function ModernMapMarkers_Locale:RegisterMarkers(locale, tbl)
- if not LocaleData[locale] then LocaleData[locale] = {} end
- LocaleData[locale].Markers = tbl
-end
-
-function ModernMapMarkers_Locale:Initialize()
- detectedLocale = GetLocale() or "enUS"
-
- local data = LocaleData[detectedLocale]
-
- if data and data.Zones then
- ActiveZones = data.Zones
- ReverseZones = {}
- for eng, loc in pairs(ActiveZones) do
- ReverseZones[loc] = eng
- end
- end
-
- if data and data.Markers then
- ActiveMarkers = data.Markers
- end
-end
-
---- Given a localized zone name (from GetMapZones), return the English key
---- used in MarkerData.lua. Returns the input unchanged for enUS or unknown names.
-function ModernMapMarkers_Locale:GetEnglishZoneName(localizedName)
- if ReverseZones and ReverseZones[localizedName] then
- return ReverseZones[localizedName]
- end
- return localizedName
-end
-
---- Given an English zone name, return the localized display name.
---- Falls back to the English name if no translation exists.
-function ModernMapMarkers_Locale:GetLocalizedZoneName(englishName)
- if ActiveZones and ActiveZones[englishName] then
- return ActiveZones[englishName]
- end
- return englishName
-end
-
---- Given an English marker/instance name, return the localized display name.
---- Falls back to the English name if no translation exists.
-function ModernMapMarkers_Locale:GetLocalizedMarkerName(englishName)
- if ActiveMarkers and ActiveMarkers[englishName] then
- return ActiveMarkers[englishName]
- end
- return englishName
-end
-
-function ModernMapMarkers_Locale:GetLocale()
- return detectedLocale or "enUS"
-
-end
+--- Localization.lua - ModernMapMarkers Localization Core
+--- Provides zone name reverse-lookup (localized -> English) and
+--- all other translations (English -> localized) for non-English clients. See ./Locales
+
+ModernMapMarkers_Locale = {}
+
+-- Registered locale tables: ["deDE"] = { Zones = {}, Markers = {} }, ...
+local LocaleData = {}
+
+-- Active locale tables (set on VARIABLES_LOADED)
+local Dictionary = nil -- English -> Localized
+
+local detectedLocale = nil
+
+--- Register a locale's dictionary containing all translations from ./Locales/Locale_xxXX.lua
+function ModernMapMarkers_Locale:RegisterDictionary(locale, tbl)
+ if not LocaleData[locale] then LocaleData[locale] = {} end
+ LocaleData[locale].Dictionary = tbl
+end
+
+function ModernMapMarkers_Locale:Initialize()
+ detectedLocale = GetLocale()
+ if not detectedLocale or detectedLocale == "enGB" then
+ detectedLocale = "enUS" -- trusty enUS fallback. working out greatly atm
+ end
+
+ local data = LocaleData[detectedLocale]
+ if data and data.Dictionary then
+ Dictionary = data.Dictionary
+ end
+end
+
+--- Given a localized zone name (from GetMapZones), return the English key
+--- used in MarkerData.lua. Returns the input unchanged for enUS or unknown names.
+function ModernMapMarkers_Locale:GetEnglishZoneName(localizedName)
+ local zoneId = MMM_PfDBHelper:GetZoneId(localizedName, detectedLocale)
+ if not zoneId then
+ return localizedName
+ end
+ local zoneName = MMM_PfDBHelper:GetZoneName(zoneId, "enUS")
+ --self:Print("GetEnglishZoneName - localizedName: '" .. tostring(localizedName) .. "' --> '" .. tostring(zoneName) .. "'(id:" .. zoneId .. ")")
+ return zoneName
+end
+
+--- Given an English zone name, return the localized display name.
+--- Falls back to the English name if no translation exists.
+function ModernMapMarkers_Locale:GetLocalizedZoneName(englishName)
+ local zoneId = MMM_PfDBHelper:GetZoneId(englishName, "enUS")
+ if not zoneId then
+ return englishName
+ end
+ local zoneName = MMM_PfDBHelper:GetZoneName(zoneId, detectedLocale)
+ --self:Print("GetLocalizedZoneName - englishName: '" .. tostring(englishName) .. "' --> '" .. tostring(zoneName) .. "'(id:" .. zoneId .. ")")
+ return zoneName
+end
+
+--- Given an English string, returns the localized equivalent, either from pfDB or the Dictionary. Applies a string mask.
+--- Falls back to the English name if no translation exists.
+-- englishName : the string in English
+-- type : type can be "dungeon", "raid", "worldboss", "boat", "zepp", "tram"
+-- mask : string mask that should be applied like "Boat to %s"
+function ModernMapMarkers_Locale:GetLocalizedString(englishName, type, mask)
+ --self:Print("GetLocalizedString - englishName: " .. tostring(englishName) .. ", type: " .. tostring(type) .. ", mask: " .. tostring(mask))
+ local localizedName = nil
+ if not mask then -- see if we automatically need to choose a mask because of the type
+ if type == "BOAT" then
+ mask = "Boat to %s"
+ elseif type == "ZEPP" or type == "ZEPPELIN" then
+ mask = "Zeppelin to %s"
+ elseif type == "TRAM" then
+ mask = "Tram to %s"
+ end
+ end
+ if Dictionary then
+ local englishNameEnglishMask = englishName
+ if mask then -- apply non-localized english mask if it exists
+ englishNameEnglishMask = string.format(mask, englishName)
+ end
+ if Dictionary[englishNameEnglishMask] then
+ return Dictionary[englishNameEnglishMask] -- we have a custom translation for this string, use it
+ end
+ end
+ if type == "WORLDBOSS" then -- worldboss is the only type that is a unit name
+ local unitId = MMM_PfDBHelper:GetUnitId(englishName, "enUS") -- first we get the unit id from pfDB
+ if unitId then
+ localizedName = MMM_PfDBHelper:GetUnitName(unitId, detectedLocale) -- now get localized unit name from pfDB
+ end
+ else -- all other types are zone names
+ local zoneId = MMM_PfDBHelper:GetZoneId(englishName, "enUS") -- first we get the zone id from pfDB
+ if zoneId then
+ localizedName = MMM_PfDBHelper:GetZoneName(zoneId, detectedLocale) -- get localized zone name from pfDB
+ end
+ end
+ if not localizedName and Dictionary then -- couldn't find it in zone or unit tables, try to get custom translation from ./Locales
+ localizedName = Dictionary[englishName]
+ end
+ if mask then -- apply mask
+ local localizedMask = mask
+ if Dictionary and Dictionary[mask] then
+ localizedMask = Dictionary[mask] -- we have a localized mask
+ end
+ if localizedName then -- all good. we have a localized name
+ localizedName = string.format(localizedMask, localizedName)
+ else -- second best option. we have no localized name. fallback to english name
+ localizedName = string.format(localizedMask, englishName)
+ end
+ end
+ if not localizedName then -- couldn't find anything anywhere. fall back to original english name
+ localizedName = englishName
+ end
+ return localizedName
+end
+
+-- Safe call helper for listener
+function ModernMapMarkers_Locale:Print(msg)
+ DEFAULT_CHAT_FRAME:AddMessage("ModernMapMarkers_Locale: " .. tostring(msg))
+end
\ No newline at end of file
diff --git a/MarkerData.lua b/MarkerData.lua
index 8857aa0..1938ccb 100644
--- a/MarkerData.lua
+++ b/MarkerData.lua
@@ -14,31 +14,33 @@ ModernMapMarkers_Points = {
{ x=0.385, y=0.34, name="Timbermaw Hold", type="raid", info="60" },
},
["Darkshore"] = {
- { x=0.324, y=0.44, name="Boat to Stormwind", type="boat", info="Alliance" },
- { x=0.304, y=0.41, name="Boat to Alah'Thalas", type="boat", info="Alliance" },
- { x=0.333, y=0.399, name="Boat to Rut'Theran Village", type="boat", info="Alliance" },
+ { x=0.324, y=0.44, name="Stormwind City", destContinentId=2, destZone="Stormwind City", type="boat", info="Alliance" },
+ { x=0.304, y=0.41, name="Alah'Thalas", destContinentId=2, destZone="Alah'Thalas", type="boat", info="Alliance" },
+ { x=0.333, y=0.399, name="Rut'theran Village", destContinentId=1, destZone="Teldrassil", type="boat", info="Alliance" },
},
["Desolace"] = {
{ x=0.29, y=0.629, name="Maraudon", type="dungeon", info="46-55" },
{ x=0.82, y=0.80, name="Concavius", type="worldboss", info="60" },
- { x=0.205, y=0.75, name="Boat to Moonhoof Village", type="boat", info="Horde" },
+ { x=0.205, y=0.75, name="Moonhoof Village", destContinentId=1, destZone="Moonwhisper Coast", type="boat", info="Horde" },
},
["Durotar"] = {
- { x=0.512, y=0.135, name="Zeppelins to UC & Grom'Gol", type="zepp", info="Horde" },
- { x=0.415, y=0.184, name="Zeppelins to TB & Kargath", type="zepp", info="Horde" },
- { x=0.598, y=0.236, name="Boat to Revantusk Village", type="boat", info="Horde" },
+ { x=0.508, y=0.15, name="Undercity", destContinentId=2, destZone="Tirisfal Glades", type="zepp", info="Horde" },
+ { x=0.505, y=0.124, name="Grom'gol Base Camp", destContinentId=2, destZone="Stranglethorn Vale", type="zepp", info="Horde" },
+ { x=0.412, y=0.195, name="Thunder Bluff", destContinentId=1, destZone="Thunder Bluff", type="zepp", info="Horde" },
+ { x=0.413, y=0.165, name="Kargath", destContinentId=2, destZone="Badlands", type="zepp", info="Horde" },
+ { x=0.598, y=0.236, name="Revantusk Village", destContinentId=2, destZone="The Hinterlands", type="boat", info="Horde" },
},
["Dustwallow Marsh"] = {
{ x=0.53, y=0.76, name="Onyxia's Lair", type="raid", info="60" },
- { x=0.718, y=0.566, name="Boat to Menethil Harbor", type="boat", info="Alliance" },
+ { x=0.718, y=0.566, name="Menethil Harbor", destContinentId=2, destZone="Wetlands", type="boat", info="Alliance" },
},
["Feralas"] = {
- { x=0.648, y=0.303, name="Dire Maul - East", type="dungeon", info="55-58" },
- { x=0.624, y=0.249, name="Dire Maul - North", type="dungeon", info="57-60" },
- { x=0.604, y=0.311, name="Dire Maul - West", type="dungeon", info="57-60" },
+ { x=0.648, y=0.303, name="Dire Maul", mask="%s - East", type="dungeon", info="55-58" },
+ { x=0.624, y=0.249, name="Dire Maul", mask="%s - North", type="dungeon", info="57-60" },
+ { x=0.604, y=0.311, name="Dire Maul", mask="%s - West", type="dungeon", info="57-60" },
{ x=0.512, y=0.108, name="Lethon", type="worldboss", info="60" },
- { x=0.311, y=0.395, name="Boat to Forgotten Coast", type="boat", info="Alliance" },
- { x=0.431, y=0.428, name="Boat to Sardor Isle", type="boat", info="Alliance" },
+ { x=0.311, y=0.395, name="The Forgotten Coast", type="boat", info="Alliance" },
+ { x=0.431, y=0.428, name="Sardor Isle", type="boat", info="Alliance" },
},
["Hyjal"] = {
{ x=0.207, y=0.592, name="Emerald Sanctum", type="raid", info="60" },
@@ -46,22 +48,22 @@ ModernMapMarkers_Points = {
},
["Moonwhisper Coast"] = {
{ x=0.506, y=0.959, name="Timbermaw Hold", type="raid", info="60" },
- { x=0.71, y=0.38, name="Boat to Shadowprey Village", type="boat", info="Horde" },
+ { x=0.71, y=0.38, name="Shadowprey Village", destContinentId=1, destZone="Desolace", type="boat", info="Horde" },
},
["Orgrimmar"] = {
{ x=0.53, y=0.486, name="Ragefire Chasm", type="dungeon", info="13-18" },
},
["Silithus"] = {
{ x=0.296, y=0.960, name="Ruins of Ahn'Qiraj", type="raid", info="60" },
- { x=0.282, y=0.956, name="Temple of Ahn'Qiraj", type="raid", info="60" },
+ { x=0.282, y=0.956, name="Ahn'Qiraj", type="raid", info="60" },
},
["Tanaris"] = {
- { x=0.66, y=0.49, name="Black Morass", type="dungeon", info="60" },
+ { x=0.66, y=0.49, name="The Black Morass", type="dungeon", info="60" },
{ x=0.389, y=0.184, name="Zul'Farrak", type="dungeon", info="44-54" },
{ x=0.361, y=0.762, name="Ostarius", type="worldboss", info="60" },
},
["Teldrassil"] = {
- { x=0.552, y=0.949, name="Boat to Auberdine", type="boat", info="Alliance" },
+ { x=0.552, y=0.949, name="Auberdine", destContinentId=1, destZone="Darkshore", type="boat", info="Alliance" },
},
["Thousand Needles"] = {
{ x=0.648, y=0.460, name="Windhorn Canyon", type="dungeon", info="26-30" },
@@ -70,24 +72,24 @@ ModernMapMarkers_Points = {
{ x=0.488, y=0.919, name="Razorfen Downs", type="dungeon", info="37-46" },
{ x=0.407, y=0.873, name="Razorfen Kraul", type="dungeon", info="29-38" },
{ x=0.462, y=0.357, name="Wailing Caverns", type="dungeon", info="17-24" },
- { x=0.636, y=0.389, name="Boat to Booty Bay", type="boat", info="Neutral" },
+ { x=0.644, y=0.388, name="Booty Bay", destContinentId=2, destZone="Stranglethorn Vale", type="boat", info="Neutral" },
},
["Thunder Bluff"] = {
- { x=0.165, y=0.230, name="Zeppelin to Orgrimmar", type="zepp", info="Horde" },
+ { x=0.165, y=0.230, name="Orgrimmar", destContinentId=1, destZone="Durotar", type="zepp", info="Horde" },
},
},
[2] = { -- Eastern Kingdoms
["Alah'Thalas"] = {
- { x=0.531, y=0.047, name="Boat to Auberdine", type="boat", info="Alliance" },
+ { x=0.531, y=0.047, name="Auberdine", destContinentId=1, destZone="Darkshore", type="boat", info="Alliance" },
},
["Badlands"] = {
- { x=0.429, y=0.130, name="Uldaman - Main Entrance", type="dungeon", info="41-51" },
- { x=0.657, y=0.438, name="Uldaman - Back Entrance", type="dungeon", info="41-51" },
- { x=0.075, y=0.480, name="Zeppelin to Orgrimmar", type="zepp", info="Horde" },
+ { x=0.429, y=0.130, name="Uldaman", mask="%s - Main Entrance", type="dungeon", info="41-51" },
+ { x=0.657, y=0.438, name="Uldaman", mask="%s - Back Entrance", type="dungeon", info="41-51" },
+ { x=0.075, y=0.480, name="Orgrimmar", destContinentId=1, destZone="Durotar", type="zepp", info="Horde" },
},
["Balor"] = {
{ x=0.57, y=0.598, name="Stormwrought Ruins", type="dungeon", info="35-41" },
- { x=0.561, y=0.846, name="Stormwrought Ruins - Back Entrance", type="dungeon", info="35-41" },
+ { x=0.561, y=0.846, name="Stormwrought Ruins", mask="%s - Back Entrance", type="dungeon", info="35-41" },
},
["Blasted Lands"] = {
{ x=0.36, y=0.753, name="Lord Kazzak", type="worldboss", info="60" },
@@ -95,8 +97,8 @@ ModernMapMarkers_Points = {
["Burning Steppes"] = {
{ x=0.328, y=0.362, name="Blackrock Depths", type="dungeon", info="52-60" },
{ x=0.95, y=0.53, name="Hateforge Quarry", type="dungeon", info="52-60" },
- { x=0.321, y=0.386, name="Lower Blackrock Spire", type="dungeon", info="55-60" },
- { x=0.312, y=0.365, name="Upper Blackrock Spire", type="dungeon", info="55-60" },
+ { x=0.321, y=0.386, name="Blackrock Spire", mask="Lower %s", type="dungeon", info="55-60" },
+ { x=0.312, y=0.365, name="Blackrock Spire", mask="Upper %s", type="dungeon", info="55-60" },
{ x=0.273, y=0.363, name="Blackwing Lair", type="raid", info="60" },
{ x=0.273, y=0.387, name="Molten Core", type="raid", info="60" },
},
@@ -115,23 +117,23 @@ ModernMapMarkers_Points = {
},
["Eastern Plaguelands"] = {
{ x=0.31, y=0.14, name="Stratholme", type="dungeon", info="58-60" },
- { x=0.47, y=0.24, name="Stratholme - Back Gate", type="dungeon", info="58-60" },
+ { x=0.47, y=0.24, name="Stratholme", mask="%s - Back Gate", type="dungeon", info="58-60" },
{ x=0.40, y=0.28, name="Naxxramas", type="raid", info="60" },
{ x=0.082, y=0.38, name="Nerubian Overseer", type="worldboss", info="60" },
},
["Elwynn Forest"] = {
- { x=0.29, y=0.61, name="Stormwind Vault - Horde Entrance", type="dungeon", info="60" },
+ { x=0.29, y=0.61, name="Stormwind Vault", mask="%s - Horde Entrance", type="dungeon", info="60" },
},
["Gilneas"] = {
{ x=0.30, y=0.27, name="Gilneas City", type="dungeon", info="43" },
},
["Ironforge"] = {
- { x=0.762, y=0.511, name="Tram to Stormwind", type="tram", info="Alliance" },
+ { x=0.762, y=0.511, name="Stormwind City", destContinentId=2, destZone="Stormwind City", type="tram", info="Alliance" },
},
["Searing Gorge"] = {
{ x=0.371, y=0.857, name="Blackrock Depths", type="dungeon", info="52-60" },
- { x=0.364, y=0.879, name="Lower Blackrock Spire", type="dungeon", info="55-60" },
- { x=0.355, y=0.855, name="Upper Blackrock Spire", type="dungeon", info="55-60" },
+ { x=0.364, y=0.879, name="Blackrock Spire", mask="Lower %s", type="dungeon", info="55-60" },
+ { x=0.355, y=0.855, name="Blackrock Spire", mask="Upper %s", type="dungeon", info="55-60" },
{ x=0.332, y=0.851, name="Blackwing Lair", type="raid", info="60" },
{ x=0.336, y=0.879, name="Molten Core", type="raid", info="60" },
},
@@ -141,27 +143,29 @@ ModernMapMarkers_Points = {
["Stormwind City"] = {
{ x=0.51, y=0.675, name="The Stockade", type="dungeon", info="24-31" },
{ x=0.63, y=0.58, name="Stormwind Vault", type="dungeon", info="60" },
- { x=0.694, y=0.294, name="Tram to Ironforge", type="tram", info="Alliance" },
- { x=0.218, y=0.563, name="Boat to Auberdine", type="boat", info="Alliance" },
+ { x=0.694, y=0.294, name="Ironforge", destContinentId=2, destZone="Ironforge", type="tram", info="Alliance" },
+ { x=0.218, y=0.563, name="Auberdine", destContinentId=1, destZone="Darkshore", type="boat", info="Alliance" },
},
["Stranglethorn Vale"] = {
{ x=0.53, y=0.172, name="Zul'Gurub", type="raid", info="60" },
- { x=0.257, y=0.73, name="Boat to Ratchet", type="boat", info="Neutral" },
- { x=0.312, y=0.298, name="Zeppelins to UC & Orgrimmar", type="zepp", info="Horde" },
+ { x=0.259, y=0.731, name="Ratchet", destContinentId=1, destZone="The Barrens", type="boat", info="Neutral" },
+ { x=0.310, y=0.280, name="Undercity", destContinentId=2, destZone="Tirisfal Glades", type="zepp", info="Horde" },
+ { x=0.308, y=0.304, name="Orgrimmar", destContinentId=1, destZone="Durotar", type="zepp", info="Horde" },
},
["Swamp of Sorrows"] = {
- { x=0.701, y=0.55, name="The Sunken Temple", type="dungeon", info="50-60" },
+ { x=0.701, y=0.55, name="Sunken Temple", type="dungeon", info="50-60" },
},
["The Hinterlands"] = {
{ x=0.632, y=0.217, name="Ysondre", type="worldboss", info="60" },
- { x=0.812, y=0.794, name="Boat to Sparkwater Port", type="boat", info="Horde" },
+ { x=0.812, y=0.794, name="Sparkwater Port", destContinentId=1, destZone="Durotar", type="boat", info="Horde" },
},
["Tirisfal Glades"] = {
- { x=0.869, y=0.323, name="Scarlet Monastery - Armory", type="dungeon", info="32-42" },
- { x=0.862, y=0.295, name="Scarlet Monastery - Cathedral", type="dungeon", info="35-45" },
- { x=0.839, y=0.283, name="Scarlet Monastery - Graveyard", type="dungeon", info="26-36" },
- { x=0.850, y=0.338, name="Scarlet Monastery - Library", type="dungeon", info="29-39" },
- { x=0.616, y=0.571, name="Zeppelins to Orgrimmar & Grom'Gol", type="zepp", info="Horde" },
+ { x=0.869, y=0.323, name="Scarlet Monastery", mask="%s - Armory", type="dungeon", info="32-42" },
+ { x=0.862, y=0.295, name="Scarlet Monastery", mask="%s - Cathedral", type="dungeon", info="35-45" },
+ { x=0.839, y=0.283, name="Scarlet Monastery", mask="%s - Graveyard", type="dungeon", info="26-36" },
+ { x=0.850, y=0.338, name="Scarlet Monastery", mask="%s - Library", type="dungeon", info="29-39" },
+ { x=0.595, y=0.575, name="Orgrimmar", destContinentId=1, destZone="Durotar", type="zepp", info="Horde" },
+ { x=0.615, y=0.575, name="Grom'gol Base Camp", destContinentId=2, destZone="Stranglethorn Vale", type="zepp", info="Horde" },
},
["Western Plaguelands"] = {
{ x=0.69, y=0.74, name="Scholomance", type="dungeon", info="58-60" },
@@ -171,7 +175,7 @@ ModernMapMarkers_Points = {
},
["Wetlands"] = {
{ x=0.67, y=0.634, name="Dragonmaw Retreat", type="dungeon", info="27-33" },
- { x=0.068, y=0.613, name="Boat to Theramore Isle", type="boat", info="Alliance" },
+ { x=0.068, y=0.613, name="Theramore Isle", destContinentId=1, destZone="Dustwallow Marsh", type="boat", info="Alliance" },
},
}
}
diff --git a/ModernMapMarkers.lua b/ModernMapMarkers.lua
index a6a0220..ea2355b 100644
--- a/ModernMapMarkers.lua
+++ b/ModernMapMarkers.lua
@@ -1,655 +1,657 @@
-local MMM = CreateFrame("Frame", "ModernMapMarkersCore", UIParent)
-MMM.markers = {}
-MMM.Data = {}
-MMM.ZoneMarkers = {}
-MMM.FlatDropdownData = nil
-MMM.ijInstalled = false
-
--- Localization shorthand
-local L = ModernMapMarkers_Locale
-
--- Filter constants
-local ALL_TYPES = { "DUNGEON", "RAID", "WORLDBOSS", "BOAT", "ZEPPELIN", "TRAM" }
-
--- InstanceJournal Integration
--- MMM markers to hide when IJ is installed (covered by IJ POI entrances)
-local IJ_HIDDEN_MARKERS = {
- ["Blackrock Depths"] = true,
- ["Lower Blackrock Spire"] = true,
- ["Upper Blackrock Spire"] = true,
- ["Blackwing Lair"] = true,
- ["Molten Core"] = true,
- ["Dire Maul - East"] = true,
- ["Dire Maul - North"] = true,
- ["Dire Maul - West"] = true,
- ["Black Morass"] = true,
- ["The Deadmines"] = true,
- ["Temple of Ahn'Qiraj"] = true,
- ["Ruins of Ahn'Qiraj"] = true,
- ["Gnomeregan"] = true,
- ["Maraudon"] = true,
- ["Scarlet Monastery - Armory"] = true,
- ["Scarlet Monastery - Cathedral"] = true,
- ["Scarlet Monastery - Graveyard"] = true,
- ["Scarlet Monastery - Library"] = true,
- ["Uldaman - Main Entrance"] = true,
- ["Uldaman - Back Entrance"] = true,
- ["Wailing Caverns"] = true,
- ["Windhorn Canyon"] = true,
- ["Timbermaw Hold"] = true,
-}
-
--- Map MMM marker names to IJ database keys for click behavior
--- { db = "DG" or "R", key = IJDB key }
-local MMM_TO_IJ = {
- -- Dungeons
- ["Blackfathom Deeps"] = { db = "DG", key = "BFD" },
- ["Crescent Grove"] = { db = "DG", key = "CG" },
- ["Ragefire Chasm"] = { db = "DG", key = "RFC" },
- ["Razorfen Downs"] = { db = "DG", key = "RFD" },
- ["Razorfen Kraul"] = { db = "DG", key = "RFK" },
- ["Zul'Farrak"] = { db = "DG", key = "ZF" },
- ["Shadowfang Keep"] = { db = "DG", key = "SFK" },
- ["The Stockade"] = { db = "DG", key = "STOCKADES" },
- ["Stormwind Vault"] = { db = "DG", key = "SV" },
- ["Stormwind Vault - Horde Entrance"] = { db = "DG", key = "SV" },
- ["Stratholme"] = { db = "DG", key = "STRAT" },
- ["Stratholme - Back Gate"] = { db = "DG", key = "STRAT" },
- ["Scholomance"] = { db = "DG", key = "SCHOLO" },
- ["The Sunken Temple"] = { db = "DG", key = "ST" },
- ["Hateforge Quarry"] = { db = "DG", key = "HQ" },
- ["Dragonmaw Retreat"] = { db = "DG", key = "DMR" },
- ["Karazhan Crypt"] = { db = "DG", key = "KC" },
- ["Lower Blackrock Spire"] = { db = "DG", key = "LBRS" },
- ["Upper Blackrock Spire"] = { db = "DG", key = "UBRS" },
- ["Gilneas City"] = { db = "DG", key = "GC" },
- ["Stormwrought Ruins"] = { db = "DG", key = "SWR" },
- ["Stormwrought Ruins - Back Entrance"] = { db = "DG", key = "SWR" },
- ["Frostmane Hollow"] = { db = "DG", key = "FH" },
- ["Windhorn Canyon"] = { db = "DG", key = "WHC" },
- -- Raids
- ["Onyxia's Lair"] = { db = "R", key = "ONY" },
- ["Emerald Sanctum"] = { db = "R", key = "ES" },
- ["Lower Karazhan Halls"] = { db = "R", key = "KARA10" },
- ["Blackwing Lair"] = { db = "R", key = "BWL" },
- ["Tower of Karazhan"] = { db = "R", key = "KARA40" },
- ["Naxxramas"] = { db = "R", key = "NAXX" },
- ["Zul'Gurub"] = { db = "R", key = "ZG" },
- ["Timbermaw Hold"] = { db = "R", key = "TH" },
-}
-
--- Keep POI submap zone markers
-local IJ_SUBMAP_ZONES = {}
-
--- Default filters
-MMM.filters = {
- DUNGEON = true,
- RAID = true,
- BOAT = true,
- ZEPPELIN = true,
- TRAM = true,
- WORLDBOSS = true,
- FACTION = "ALL"
-}
-
--- Dirty-check state for RefreshMarkers
-local lastContinent = nil
-local lastZone = nil
-local forceRefresh = false
-
--- Textures
-local TEX_BASE = "Interface\\AddOns\\ModernMapMarkers\\Textures\\"
-local TEXTURE_MAP = {
- DUNGEON = TEX_BASE .. "dungeon.tga",
- RAID = TEX_BASE .. "raid.tga",
- BOAT = TEX_BASE .. "boat.tga",
- ZEPPELIN = TEX_BASE .. "zepp.tga",
- TRAM = TEX_BASE .. "tram.tga",
- WORLDBOSS = TEX_BASE .. "worldboss.tga",
-}
-local HIGHLIGHT_MAP = {
- DUNGEON = TEX_BASE .. "dungeon-highlight.tga",
- RAID = TEX_BASE .. "raid-highlight.tga",
-}
-
--- Zone name cache
-local ZONE_CACHE = {}
-function MMM:CacheZones()
- ZONE_CACHE[1] = { GetMapZones(1) } -- Kalimdor
- ZONE_CACHE[2] = { GetMapZones(2) } -- Eastern Kingdoms
-end
-
--- Map multi-entrance dungeons to a single zone
-local CANONICAL_ZONE = {
- ["Blackrock Depths"] = "Burning Steppes",
- ["Lower Blackrock Spire"] = "Burning Steppes",
- ["Upper Blackrock Spire"] = "Burning Steppes",
- ["Blackwing Lair"] = "Burning Steppes",
- ["Molten Core"] = "Burning Steppes",
- ["Stormwind Vault"] = "Stormwind City",
- ["Timbermaw Hold"] = "Azshara",
-}
-
--- Data Parsing
-function MMM:BuildData()
- if self.dataBuilt then return end
- if not ModernMapMarkers_Points then return end
-
- MMM.Data = {}
- MMM.ZoneMarkers = {}
-
- local index = 1
-
- for contID, zones in pairs(ModernMapMarkers_Points) do
- MMM.ZoneMarkers[contID] = MMM.ZoneMarkers[contID] or {}
-
- for zoneName, markers in pairs(zones) do
- MMM.ZoneMarkers[contID][zoneName] = {}
-
- for _, m in ipairs(markers) do
- local typeUpper = string.upper(m.type or "UNKNOWN")
- if typeUpper == "ZEPP" then typeUpper = "ZEPPELIN" end
-
- local markerData = {
- continent = contID,
- zoneName = zoneName,
- x = m.x,
- y = m.y,
- name = m.name,
- type = typeUpper,
- description = m.info,
- id = index
- }
-
- table.insert(MMM.Data, markerData)
- table.insert(MMM.ZoneMarkers[contID][zoneName], markerData)
-
- index = index + 1
- end
- end
- end
- self.dataBuilt = true
-end
-
--- Get deduplicated data for dropdowns
-function ModernMapMarkers_GetFlatData()
- MMM:BuildData()
-
- if MMM.FlatDropdownData then return MMM.FlatDropdownData end
-
- local filteredData = {}
- local seenNames = {}
-
- for _, data in ipairs(MMM.Data) do
- if data.type == "DUNGEON" or data.type == "RAID" or data.type == "WORLDBOSS" then
- local baseName = data.name or ""
-
- local dashIndex = string.find(baseName, " %- ")
- if dashIndex then
- baseName = string.sub(baseName, 1, dashIndex - 1)
- end
-
- local existingIndex = seenNames[baseName]
- if not existingIndex then
- local dropData = {}
- for k, v in pairs(data) do dropData[k] = v end
- dropData.name = baseName
- table.insert(filteredData, dropData)
- seenNames[baseName] = table.getn(filteredData)
- else
- local canon = CANONICAL_ZONE[baseName]
- if canon and data.zoneName == canon then
- local dropData = {}
- for k, v in pairs(data) do dropData[k] = v end
- dropData.name = baseName
- filteredData[existingIndex] = dropData
- end
- end
- end
- end
-
- MMM.FlatDropdownData = filteredData
- return filteredData
-end
-
--- Drawing Logic
-function MMM:RefreshMarkers()
- if not WorldMapFrame:IsVisible() then return end
-
- self:BuildData()
-
- local currentContinent = GetCurrentMapContinent()
- local currentZone = GetCurrentMapZone()
-
- -- Dirty-check: skip full redraw if zone hasn't changed and no force refresh
- if not forceRefresh
- and currentContinent == lastContinent
- and currentZone == lastZone then
- return
- end
-
- lastContinent = currentContinent
- lastZone = currentZone
- forceRefresh = false
-
- if currentContinent == 0 or currentZone == 0 then
- self:HideAllMarkers()
- return
- end
-
- local zoneNames = ZONE_CACHE[currentContinent]
- local currentZoneName = zoneNames and zoneNames[currentZone]
- if not currentZoneName then
- self:HideAllMarkers()
- return
- end
-
- -- Translate localized zone name to English key used in MarkerData
- local englishZoneName = L:GetEnglishZoneName(currentZoneName)
-
- local zoneMarkers = MMM.ZoneMarkers[currentContinent] and MMM.ZoneMarkers[currentContinent][englishZoneName]
-
- local markerIndex = 0
-
- if zoneMarkers then
- for _, data in ipairs(zoneMarkers) do
-
- local showMarker = (MMM.filters[data.type] == true)
-
- -- Hide markers replaced by InstanceJournal
- if showMarker and MMM.ijInstalled then
- if data.type == "WORLDBOSS" then
- showMarker = false
- elseif IJ_HIDDEN_MARKERS[data.name] then
- showMarker = false
- end
- end
-
- -- Check faction filter
- if showMarker and (data.type == "BOAT" or data.type == "ZEPPELIN" or data.type == "TRAM") then
- if MMM.filters.FACTION ~= "ALL"
- and data.description ~= "Neutral"
- and data.description ~= MMM.filters.FACTION then
- showMarker = false
- end
- end
-
- if showMarker then
- markerIndex = markerIndex + 1
- local marker = self:GetOrCreateMarker(markerIndex)
-
- -- Sizing
- if data.type == "DUNGEON" or data.type == "RAID" or data.type == "WORLDBOSS" then
- marker:SetWidth(32)
- marker:SetHeight(32)
- else
- marker:SetWidth(24)
- marker:SetHeight(24)
- end
-
- -- Position
- local width = WorldMapDetailFrame:GetWidth()
- local height = WorldMapDetailFrame:GetHeight()
- marker:SetPoint("CENTER", WorldMapDetailFrame, "TOPLEFT", data.x * width, -data.y * height)
-
- -- Texture
- local tex = TEXTURE_MAP[data.type] or "Interface\\Minimap\\POIIcons"
- if marker.lastTexture ~= tex then
- marker.texture:SetTexture(tex)
- marker.lastTexture = tex
- end
-
- local hlTex = HIGHLIGHT_MAP[data.type]
- if marker.lastHighlight ~= hlTex then
- if hlTex then
- marker:SetHighlightTexture(hlTex)
- elseif marker.lastHighlight then
- marker:SetHighlightTexture("")
- end
- marker.lastHighlight = hlTex
- end
-
- -- Metadata
- marker.name = L:GetLocalizedMarkerName(data.name)
- marker.nameEN = data.name
- marker.description = data.description
- marker.markerType = data.type
- marker.continent = data.continent
- marker.zoneName = data.zoneName
-
- marker:Show()
- end
- end
- end
-
- -- Hide any excess markers from the previous zone
- self:HideMarkersFrom(markerIndex + 1)
-end
-
--- Hide all markers in the pool
-function MMM:HideAllMarkers()
- self:HideMarkersFrom(1)
-end
-
--- Hide markers from startIndex onwards
-function MMM:HideMarkersFrom(startIndex)
- for i = startIndex, table.getn(self.markers) do
- if self.markers[i] then
- self.markers[i]:Hide()
- end
- end
-end
-
--- Invalidate dirty-check so next RefreshMarkers does a full redraw
-function MMM:InvalidateCache()
- forceRefresh = true
-end
-
--- Get IJ instance from MMM marker English name
-function MMM:GetIJInstance(nameEN)
- if not MMM.ijInstalled or not nameEN then return nil end
- local mapping = MMM_TO_IJ[nameEN]
- if not mapping then return nil end
- local dbTable = (mapping.db == "R") and IJDB.R or IJDB.DG
- if not dbTable then return nil end
- return dbTable[mapping.key]
-end
-
--- InstanceJournal Integration
-function MMM:OnMarkerClick(marker, button)
- if not MMM.ijInstalled then return end
-
- local instance = MMM:GetIJInstance(marker.nameEN)
- if not instance then return end
-
- if button == "RightButton" then
- -- Right-click: open IJ journal page
- if not IJ_InstanceJournalFrame:IsShown() then
- IJ_InstanceJournalFrame:Show()
- end
- if instance.Type == IJLib.InstanceType.Raid then
- IJ_ShowRaids = true
- PanelTemplates_SetTab(IJ_InstanceJournalFrame, 2)
- else
- IJ_ShowRaids = false
- PanelTemplates_SetTab(IJ_InstanceJournalFrame, 1)
- end
- IJ_ShowEncounter(instance)
- WorldMapFrame:Hide()
- else
- -- Left-click: navigate to instance submap
- SetMapZoom(tonumber(instance.MapId), 1)
- end
-end
-
--- Marker Pool
-function MMM:GetOrCreateMarker(index)
- if not self.markers[index] then
- local marker = CreateFrame("Button", "ModernMapMarkerIcon"..index, WorldMapButton)
- marker:SetWidth(24)
- marker:SetHeight(24)
- marker:SetFrameLevel(WorldMapButton:GetFrameLevel() + 5)
-
- local tex = marker:CreateTexture(nil, "OVERLAY")
- tex:SetAllPoints(marker)
- marker.texture = tex
-
- marker:RegisterForClicks("LeftButtonUp", "RightButtonUp")
- marker:SetScript("OnClick", function() MMM:OnMarkerClick(this, arg1) end)
-
- marker:SetScript("OnEnter", function()
- WorldMapTooltip:SetOwner(this, "ANCHOR_RIGHT")
- WorldMapTooltip:AddLine(this.name, 1, 0.82, 0)
-
- if this.description then
- if this.markerType == "DUNGEON" or this.markerType == "RAID" or this.markerType == "WORLDBOSS" then
- local lvlLabel = L:GetLocalizedMarkerName("Level") or "Level"
- WorldMapTooltip:AddLine(lvlLabel .. ": " .. this.description, 1, 1, 1, 1)
- elseif this.description == "Alliance" then
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Alliance"), 0.0, 0.47, 1.0, 1)
- elseif this.description == "Horde" then
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Horde"), 1.0, 0.0, 0.0, 1)
- else
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName(this.description), 1, 1, 1, 1)
- end
- end
-
- -- IJ tooltips for dungeon/raid markers
- if MMM.ijInstalled and MMM:GetIJInstance(this.nameEN) then
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Left-Click: View Map"), 0.5, 0.5, 0.5)
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Right-Click: Instance Journal"), 0.5, 0.5, 0.5)
- end
-
- WorldMapTooltip:Show()
- end)
-
- marker:SetScript("OnLeave", function() WorldMapTooltip:Hide() end)
-
- self.markers[index] = marker
- end
- return self.markers[index]
-end
-
--- Hooks & API
-function ModernMapMarkers_SetFilter(key, state)
- if key == "ALL" then
- for _, t in ipairs(ALL_TYPES) do
- MMM.filters[t] = state
- end
- else
- MMM.filters[key] = state
- end
- MMM:InvalidateCache()
- MMM:RefreshMarkers()
-end
-
-function ModernMapMarkers_SetFactionFilter(factionStr)
- MMM.filters.FACTION = factionStr
- MMM:InvalidateCache()
- MMM:RefreshMarkers()
-end
-
-function MMM:GetZoneIndex(continentID, zoneName)
- local zones = ZONE_CACHE[continentID] or {}
- -- zoneName is English (from MarkerData), ZONE_CACHE has localized names
- local localizedName = L:GetLocalizedZoneName(zoneName)
- for i, name in ipairs(zones) do
- if name == localizedName then return i end
- end
- -- Fallback: try direct match (for enUS or untranslated zones)
- for i, name in ipairs(zones) do
- if name == zoneName then return i end
- end
- return 0
-end
-
-function ModernMapMarkers_FindMarker(dataIndex)
- MMM:BuildData()
- local data = MMM.Data[dataIndex]
- if not data then return end
-
- local zoneIndex = MMM:GetZoneIndex(data.continent, data.zoneName)
- if zoneIndex > 0 then
- SetMapZoom(data.continent, zoneIndex)
- end
-
- if not MMM.filters[data.type] then
- MMM.filters[data.type] = true
- if ModernMapMarkers_SyncFilterUI then
- ModernMapMarkers_SyncFilterUI(data.type, true)
- end
- end
-
- MMM:InvalidateCache()
- MMM:RefreshMarkers()
-end
-
-local original_WorldMapFrame_Update = WorldMapFrame_Update
-function WorldMapFrame_Update()
- if original_WorldMapFrame_Update then
- original_WorldMapFrame_Update()
- end
- MMM:RefreshMarkers()
-end
-
-MMM:SetScript("OnEvent", function()
- if event == "VARIABLES_LOADED" then
- if ModernMapMarkersDB and ModernMapMarkersDB.filters then
- for k, v in pairs(ModernMapMarkersDB.filters) do
- MMM.filters[k] = v
- end
- else
- -- First load: auto-detect player faction for transport filter
- local faction = UnitFactionGroup("player")
- if faction == "Alliance" or faction == "Horde" then
- MMM.filters.FACTION = faction
- end
- end
-
- L:Initialize()
- MMM:CacheZones()
- MMM:BuildData()
-
- -- Detect InstanceJournal
- if IJDB and IJLib and IJ_ShowInstanceEntrancesIcon then
- MMM.ijInstalled = true
-
- -- Build POI submap zone lookup
- if IJDB.POI then
- for _, poi in pairs(IJDB.POI) do
- if poi.MapContinentId and poi.MapZoneId then
- local key = tostring(poi.MapContinentId) .. ":" .. tostring(poi.MapZoneId)
- IJ_SUBMAP_ZONES[key] = true
- end
- end
- end
-
- -- Also include instance entrance locations inside other instance maps
- -- (e.g., Molten Core entrance inside BRD submap)
- local instanceMapIds = {}
- local allInstances = {}
- for _, inst in pairs(IJDB.DG or {}) do
- if inst.MapId then instanceMapIds[tostring(inst.MapId)] = true end
- table.insert(allInstances, inst)
- end
- for _, inst in pairs(IJDB.R or {}) do
- if inst.MapId then instanceMapIds[tostring(inst.MapId)] = true end
- table.insert(allInstances, inst)
- end
- for _, inst in ipairs(allInstances) do
- if inst.Entrances then
- for _, ent in pairs(inst.Entrances) do
- if ent.MapContinentId and ent.MapZoneId
- and instanceMapIds[tostring(ent.MapContinentId)] then
- local key = tostring(ent.MapContinentId) .. ":" .. tostring(ent.MapZoneId)
- IJ_SUBMAP_ZONES[key] = true
- end
- end
- end
- end
-
- -- Hook IJ entrance markers: hide on world zones, keep in POI submaps
- -- In submaps, reskin entrance pins with MMM dungeon/raid textures
- local original_ShowEntrance = IJ_ShowInstanceEntrancesIcon
- IJ_ShowInstanceEntrancesIcon = function(instance)
- local key = tostring(GetCurrentMapContinent()) .. ":" .. tostring(GetCurrentMapZone())
- if IJ_SUBMAP_ZONES[key] then
- original_ShowEntrance(instance)
-
- -- Determine marker type and apply MMM filter
- local isRaid = instance.Type == IJLib.InstanceType.Raid
- local filterKey = isRaid and "RAID" or "DUNGEON"
- local mmmTex = isRaid and TEXTURE_MAP.RAID or TEXTURE_MAP.DUNGEON
-
- local cleanName = string.gsub(instance.Name or "", "%s+", "")
- local prefix = "IJ_EntrancePin_" .. cleanName
-
- for _, pin in ipairs(IJ_CreatedMapInstanceEntrance) do
- local pinName = pin:GetName() or ""
- if string.find(pinName, prefix, 1, true) and pin:IsVisible() then
- -- Hide pin if MMM filter is disabled for this type
- if not MMM.filters[filterKey] then
- pin:Hide()
- end
- pin.icon:SetTexture(mmmTex)
- pin.mmmInstance = instance
-
- if not pin.mmmReskinned then
- pin.mmmReskinned = true
- pin.mmmTexture = mmmTex
-
- local mmmHL = instance.Type == IJLib.InstanceType.Raid and HIGHLIGHT_MAP.RAID or HIGHLIGHT_MAP.DUNGEON
- if mmmHL then
- pin:SetHighlightTexture(mmmHL)
- end
-
- local origOnEnter = pin:GetScript("OnEnter")
- local origOnLeave = pin:GetScript("OnLeave")
-
- pin:SetScript("OnEnter", function()
- if origOnEnter then origOnEnter() end
- this.icon:SetTexture(this.mmmTexture)
-
- -- MMM tooltip for IJ POI submap markers
- local inst = this.mmmInstance
- if inst then
- WorldMapTooltip:SetOwner(this, "ANCHOR_RIGHT")
- WorldMapTooltip:AddLine(inst.Name or "", 1, 0.82, 0)
- local minLvl = inst.MinLevel or 0
- local maxLvl = inst.MaxLevel or 0
- local lvlLabel = L:GetLocalizedMarkerName("Level") or "Level"
- WorldMapTooltip:AddLine(lvlLabel .. ": " .. minLvl .. "-" .. maxLvl, 1, 1, 1, 1)
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Left-Click: View Map"), 0.5, 0.5, 0.5)
- WorldMapTooltip:AddLine(L:GetLocalizedMarkerName("Right-Click: Instance Journal"), 0.5, 0.5, 0.5)
- WorldMapTooltip:Show()
- end
- end)
-
- pin:SetScript("OnLeave", function()
- if origOnLeave then origOnLeave() end
- this.icon:SetTexture(this.mmmTexture)
- WorldMapTooltip:Hide()
- end)
-
- -- Right click for IJ journal
- pin:RegisterForClicks("LeftButtonUp", "RightButtonUp")
- local origOnClick = pin:GetScript("OnClick")
- pin:SetScript("OnClick", function()
- if arg1 == "RightButton" and this.mmmInstance then
- local inst = this.mmmInstance
- if not IJ_InstanceJournalFrame:IsShown() then
- IJ_InstanceJournalFrame:Show()
- end
- if inst.Type == IJLib.InstanceType.Raid then
- IJ_ShowRaids = true
- PanelTemplates_SetTab(IJ_InstanceJournalFrame, 2)
- else
- IJ_ShowRaids = false
- PanelTemplates_SetTab(IJ_InstanceJournalFrame, 1)
- end
- IJ_ShowEncounter(inst)
- WorldMapFrame:Hide()
- else
- if origOnClick then origOnClick() end
- end
- end)
- end
- end
- end
- end
- end
- end
-
- MMM:InvalidateCache()
- MMM:RefreshMarkers()
-
- elseif event == "PLAYER_LOGOUT" then
- if not ModernMapMarkersDB then ModernMapMarkersDB = {} end
- ModernMapMarkersDB.filters = {}
- for k, v in pairs(MMM.filters) do
- ModernMapMarkersDB.filters[k] = v
- end
- end
-end)
-MMM:RegisterEvent("VARIABLES_LOADED")
-MMM:RegisterEvent("PLAYER_LOGOUT")
+local MMM = CreateFrame("Frame", "ModernMapMarkersCore", UIParent)
+MMM.markers = {}
+MMM.Data = {}
+MMM.ZoneMarkers = {}
+MMM.FlatDropdownData = nil
+MMM.ijInstalled = false
+
+-- Localization shorthand
+local L = ModernMapMarkers_Locale
+
+-- Filter constants
+local ALL_TYPES = { "DUNGEON", "RAID", "WORLDBOSS", "BOAT", "ZEPPELIN", "TRAM" }
+
+-- InstanceJournal Integration
+-- MMM markers to hide when IJ is installed (covered by IJ POI entrances)
+local IJ_HIDDEN_MARKERS = {
+ ["Blackrock Depths"] = true,
+ ["Blackrock Spire"] = true,
+ ["Blackwing Lair"] = true,
+ ["Molten Core"] = true,
+ ["Dire Maul"] = true,
+ ["The Black Morass"] = true,
+ ["The Deadmines"] = true,
+ ["Ahn'Qiraj"] = true,
+ ["Ruins of Ahn'Qiraj"] = true,
+ ["Gnomeregan"] = true,
+ ["Maraudon"] = true,
+ ["Scarlet Monastery"] = true,
+ ["Uldaman"] = true,
+ ["Wailing Caverns"] = true,
+ ["Windhorn Canyon"] = true,
+ ["Timbermaw Hold"] = true,
+}
+
+-- Map MMM marker names to IJ database keys for click behavior
+-- { db = "DG" or "R", key = IJDB key }
+local MMM_TO_IJ = {
+ -- Dungeons
+ ["Blackfathom Deeps"] = { db = "DG", key = "BFD" },
+ ["Crescent Grove"] = { db = "DG", key = "CG" },
+ ["Ragefire Chasm"] = { db = "DG", key = "RFC" },
+ ["Razorfen Downs"] = { db = "DG", key = "RFD" },
+ ["Razorfen Kraul"] = { db = "DG", key = "RFK" },
+ ["Zul'Farrak"] = { db = "DG", key = "ZF" },
+ ["Shadowfang Keep"] = { db = "DG", key = "SFK" },
+ ["The Stockade"] = { db = "DG", key = "STOCKADES" },
+ ["Stormwind Vault"] = { db = "DG", key = "SV" },
+ ["Stormwind Vault - Horde Entrance"] = { db = "DG", key = "SV" },
+ ["Stratholme"] = { db = "DG", key = "STRAT" },
+ ["Stratholme - Back Gate"] = { db = "DG", key = "STRAT" },
+ ["Scholomance"] = { db = "DG", key = "SCHOLO" },
+ ["Sunken Temple"] = { db = "DG", key = "ST" },
+ ["Hateforge Quarry"] = { db = "DG", key = "HQ" },
+ ["Dragonmaw Retreat"] = { db = "DG", key = "DMR" },
+ ["Karazhan Crypt"] = { db = "DG", key = "KC" },
+ ["Lower Blackrock Spire"] = { db = "DG", key = "LBRS" },
+ ["Upper Blackrock Spire"] = { db = "DG", key = "UBRS" },
+ ["Gilneas City"] = { db = "DG", key = "GC" },
+ ["Stormwrought Ruins"] = { db = "DG", key = "SWR" },
+ ["Stormwrought Ruins - Back Entrance"] = { db = "DG", key = "SWR" },
+ ["Frostmane Hollow"] = { db = "DG", key = "FH" },
+ ["Windhorn Canyon"] = { db = "DG", key = "WHC" },
+ -- Raids
+ ["Onyxia's Lair"] = { db = "R", key = "ONY" },
+ ["Emerald Sanctum"] = { db = "R", key = "ES" },
+ ["Lower Karazhan Halls"] = { db = "R", key = "KARA10" },
+ ["Blackwing Lair"] = { db = "R", key = "BWL" },
+ ["Tower of Karazhan"] = { db = "R", key = "KARA40" },
+ ["Naxxramas"] = { db = "R", key = "NAXX" },
+ ["Zul'Gurub"] = { db = "R", key = "ZG" },
+ ["Timbermaw Hold"] = { db = "R", key = "TH" },
+}
+
+-- Keep POI submap zone markers
+local IJ_SUBMAP_ZONES = {}
+
+-- Default filters
+MMM.filters = {
+ DUNGEON = true,
+ RAID = true,
+ BOAT = true,
+ ZEPPELIN = true,
+ TRAM = true,
+ WORLDBOSS = true,
+ FACTION = "ALL"
+}
+
+-- Dirty-check state for RefreshMarkers
+local lastContinent = nil
+local lastZone = nil
+local forceRefresh = false
+
+-- Textures
+local TEX_BASE = "Interface\\AddOns\\ModernMapMarkers\\Textures\\"
+local TEXTURE_MAP = {
+ DUNGEON = TEX_BASE .. "dungeon.tga",
+ RAID = TEX_BASE .. "raid.tga",
+ BOAT = TEX_BASE .. "boat.tga",
+ ZEPPELIN = TEX_BASE .. "zepp.tga",
+ TRAM = TEX_BASE .. "tram.tga",
+ WORLDBOSS = TEX_BASE .. "worldboss.tga",
+}
+local HIGHLIGHT_MAP = {
+ DUNGEON = TEX_BASE .. "dungeon-highlight.tga",
+ RAID = TEX_BASE .. "raid-highlight.tga",
+}
+
+-- Zone name cache. Directly from the client, so they are localized in the client's language.
+local LOCALIZED_ZONE_CACHE = {}
+function MMM:CacheZones()
+ LOCALIZED_ZONE_CACHE[1] = { GetMapZones(1) } -- Kalimdor
+ LOCALIZED_ZONE_CACHE[2] = { GetMapZones(2) } -- Eastern Kingdoms
+end
+
+-- Map multi-entrance dungeons to a single zone
+local CANONICAL_ZONE = {
+ ["Blackrock Depths"] = "Burning Steppes",
+ ["Blackrock Spire"] = "Burning Steppes",
+ ["Blackwing Lair"] = "Burning Steppes",
+ ["Molten Core"] = "Burning Steppes",
+ ["Stormwind Vault"] = "Stormwind City",
+ ["Timbermaw Hold"] = "Azshara",
+}
+
+-- Data Parsing
+function MMM:BuildData()
+ if self.dataBuilt then return end
+ if not ModernMapMarkers_Points then return end
+
+ MMM.Data = {}
+ MMM.ZoneMarkers = {}
+
+ local index = 1
+
+ for contID, zones in pairs(ModernMapMarkers_Points) do
+ MMM.ZoneMarkers[contID] = MMM.ZoneMarkers[contID] or {}
+
+ for zoneName, markers in pairs(zones) do
+ MMM.ZoneMarkers[contID][zoneName] = {}
+
+ for _, m in ipairs(markers) do
+ local typeUpper = string.upper(m.type or "UNKNOWN")
+ if typeUpper == "ZEPP" then typeUpper = "ZEPPELIN" end
+
+ local markerData = {
+ continent = contID,
+ zoneName = zoneName,
+ x = m.x,
+ y = m.y,
+ name = m.name,
+ type = typeUpper,
+ mask = m.mask,
+ description = m.info,
+ id = index,
+ destContinentId = m.destContinentId,
+ destZone = m.destZone
+ }
+
+ table.insert(MMM.Data, markerData)
+ table.insert(MMM.ZoneMarkers[contID][zoneName], markerData)
+
+ index = index + 1
+ end
+ end
+ end
+ self.dataBuilt = true
+end
+
+-- Get deduplicated data for dropdowns
+function ModernMapMarkers_GetFlatData()
+ MMM:BuildData()
+
+ if MMM.FlatDropdownData then return MMM.FlatDropdownData end
+
+ local filteredData = {}
+ local seenNames = {}
+
+ for _, data in ipairs(MMM.Data) do
+ if data.type == "DUNGEON" or data.type == "RAID" or data.type == "WORLDBOSS" then
+ local baseName = data.name or ""
+
+ local existingIndex = seenNames[baseName]
+ if not existingIndex then
+ local dropData = {}
+ for k, v in pairs(data) do dropData[k] = v end
+ dropData.name = baseName
+ table.insert(filteredData, dropData)
+ seenNames[baseName] = table.getn(filteredData)
+ else
+ local canon = CANONICAL_ZONE[baseName]
+ if canon and data.zoneName == canon then
+ local dropData = {}
+ for k, v in pairs(data) do dropData[k] = v end
+ dropData.name = baseName
+ filteredData[existingIndex] = dropData
+ end
+ end
+ end
+ end
+
+ MMM.FlatDropdownData = filteredData
+ return filteredData
+end
+
+-- Drawing Logic
+function MMM:RefreshMarkers()
+ if not WorldMapFrame:IsVisible() then return end
+
+ self:BuildData()
+
+ local currentContinent = GetCurrentMapContinent()
+ local currentZone = GetCurrentMapZone()
+
+ -- Dirty-check: skip full redraw if zone hasn't changed and no force refresh
+ if not forceRefresh
+ and currentContinent == lastContinent
+ and currentZone == lastZone then
+ return
+ end
+
+ lastContinent = currentContinent
+ lastZone = currentZone
+ forceRefresh = false
+
+ if currentContinent == 0 or currentZone == 0 then
+ self:HideAllMarkers()
+ return
+ end
+
+ local zoneNames = LOCALIZED_ZONE_CACHE[currentContinent]
+ local currentZoneName = zoneNames and zoneNames[currentZone]
+ if not currentZoneName then
+ self:HideAllMarkers()
+ return
+ end
+
+ -- Translate localized zone name to English key used in MarkerData
+ local englishZoneName = L:GetEnglishZoneName(currentZoneName)
+
+ local zoneMarkers = MMM.ZoneMarkers[currentContinent] and MMM.ZoneMarkers[currentContinent][englishZoneName]
+
+ local markerIndex = 0
+
+ if zoneMarkers then
+ for _, data in ipairs(zoneMarkers) do
+
+ local showMarker = (MMM.filters[data.type] == true)
+
+ -- Hide markers replaced by InstanceJournal
+ if showMarker and MMM.ijInstalled then
+ if data.type == "WORLDBOSS" then
+ showMarker = false
+ elseif IJ_HIDDEN_MARKERS[data.name] then
+ showMarker = false
+ end
+ end
+
+ -- Check faction filter
+ if showMarker and (data.type == "BOAT" or data.type == "ZEPPELIN" or data.type == "TRAM") then
+ if MMM.filters.FACTION ~= "ALL"
+ and data.description ~= "Neutral"
+ and data.description ~= MMM.filters.FACTION then
+ showMarker = false
+ end
+ end
+
+ if showMarker then
+ markerIndex = markerIndex + 1
+ local marker = self:GetOrCreateMarker(markerIndex)
+
+ -- Sizing
+ if data.type == "DUNGEON" or data.type == "RAID" or data.type == "WORLDBOSS" then
+ marker:SetWidth(32)
+ marker:SetHeight(32)
+ else
+ marker:SetWidth(24)
+ marker:SetHeight(24)
+ end
+
+ -- Position
+ local width = WorldMapDetailFrame:GetWidth()
+ local height = WorldMapDetailFrame:GetHeight()
+ marker:SetPoint("CENTER", WorldMapDetailFrame, "TOPLEFT", data.x * width, -data.y * height)
+
+ -- Texture
+ local tex = TEXTURE_MAP[data.type] or "Interface\\Minimap\\POIIcons"
+ if marker.lastTexture ~= tex then
+ marker.texture:SetTexture(tex)
+ marker.lastTexture = tex
+ end
+
+ local hlTex = HIGHLIGHT_MAP[data.type]
+ if marker.lastHighlight ~= hlTex then
+ if hlTex then
+ marker:SetHighlightTexture(hlTex)
+ elseif marker.lastHighlight then
+ marker:SetHighlightTexture("")
+ end
+ marker.lastHighlight = hlTex
+ end
+
+ -- Metadata
+ marker.name = L:GetLocalizedString(data.name, data.type, data.mask)
+ marker.nameEN = data.name
+ marker.description = data.description
+ marker.markerType = data.type
+ marker.continent = data.continent
+ marker.zoneName = data.zoneName
+ marker.destContinentId = data.destContinentId
+ marker.destZone = data.destZone
+
+ marker:Show()
+ end
+ end
+ end
+
+ -- Hide any excess markers from the previous zone
+ self:HideMarkersFrom(markerIndex + 1)
+end
+
+-- Hide all markers in the pool
+function MMM:HideAllMarkers()
+ self:HideMarkersFrom(1)
+end
+
+-- Hide markers from startIndex onwards
+function MMM:HideMarkersFrom(startIndex)
+ for i = startIndex, table.getn(self.markers) do
+ if self.markers[i] then
+ self.markers[i]:Hide()
+ end
+ end
+end
+
+-- Invalidate dirty-check so next RefreshMarkers does a full redraw
+function MMM:InvalidateCache()
+ forceRefresh = true
+end
+
+-- Get IJ instance from MMM marker English name
+function MMM:GetIJInstance(nameEN)
+ if not MMM.ijInstalled or not nameEN then return nil end
+ local mapping = MMM_TO_IJ[nameEN]
+ if not mapping then return nil end
+ local dbTable = (mapping.db == "R") and IJDB.R or IJDB.DG
+ if not dbTable then return nil end
+ return dbTable[mapping.key]
+end
+
+-- InstanceJournal Integration
+function MMM:OnMarkerClick(marker, button)
+ local instance = nil
+ if MMM.ijInstalled then
+ instance = MMM:GetIJInstance(marker.nameEN)
+ end
+
+ if button == "RightButton" then
+ if instance then
+ -- Right-click: open IJ journal page
+ if not IJ_InstanceJournalFrame:IsShown() then
+ IJ_InstanceJournalFrame:Show()
+ end
+ if instance.Type == IJLib.InstanceType.Raid then
+ IJ_ShowRaids = true
+ PanelTemplates_SetTab(IJ_InstanceJournalFrame, 2)
+ else
+ IJ_ShowRaids = false
+ PanelTemplates_SetTab(IJ_InstanceJournalFrame, 1)
+ end
+ IJ_ShowEncounter(instance)
+ WorldMapFrame:Hide()
+ end
+ else
+ if marker.destContinentId and marker.destZone then -- we have a transport marker
+ local zoneIndex = MMM:GetZoneIndex(marker.destContinentId, marker.destZone)
+ if zoneIndex > 0 then
+ SetMapZoom(marker.destContinentId, zoneIndex)
+ return
+ end
+ elseif instance then
+ -- Left-click: navigate to instance submap
+ SetMapZoom(tonumber(instance.MapId), 1)
+ end
+ end
+end
+
+-- Marker Pool
+function MMM:GetOrCreateMarker(index)
+ if not self.markers[index] then
+ local marker = CreateFrame("Button", "ModernMapMarkerIcon"..index, WorldMapButton)
+ marker:SetWidth(24)
+ marker:SetHeight(24)
+ marker:SetFrameLevel(WorldMapButton:GetFrameLevel() + 5)
+
+ local tex = marker:CreateTexture(nil, "OVERLAY")
+ tex:SetAllPoints(marker)
+ marker.texture = tex
+
+ marker:RegisterForClicks("LeftButtonUp", "RightButtonUp")
+ marker:SetScript("OnClick", function() MMM:OnMarkerClick(this, arg1) end)
+
+ marker:SetScript("OnEnter", function()
+ WorldMapTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ WorldMapTooltip:AddLine(this.name, 1, 0.82, 0)
+
+ if this.description then
+ if this.markerType == "DUNGEON" or this.markerType == "RAID" or this.markerType == "WORLDBOSS" then
+ local lvlLabel = L:GetLocalizedString("Level") or "Level"
+ WorldMapTooltip:AddLine(lvlLabel .. ": " .. this.description, 1, 1, 1, 1)
+ elseif this.description == "Alliance" then
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Alliance"), 0.0, 0.47, 1.0, 1)
+ elseif this.description == "Horde" then
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Horde"), 1.0, 0.0, 0.0, 1)
+ else
+ WorldMapTooltip:AddLine(L:GetLocalizedString(this.description), 1, 1, 1, 1)
+ end
+ end
+
+ -- IJ tooltips for dungeon/raid markers
+ if MMM.ijInstalled and MMM:GetIJInstance(this.nameEN) then
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Left-Click: View Map"), 0.5, 0.5, 0.5)
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Right-Click: Instance Journal"), 0.5, 0.5, 0.5)
+ end
+
+ WorldMapTooltip:Show()
+ end)
+
+ marker:SetScript("OnLeave", function() WorldMapTooltip:Hide() end)
+
+ self.markers[index] = marker
+ end
+ return self.markers[index]
+end
+
+-- Hooks & API
+function ModernMapMarkers_SetFilter(key, state)
+ if key == "ALL" then
+ for _, t in ipairs(ALL_TYPES) do
+ MMM.filters[t] = state
+ end
+ else
+ MMM.filters[key] = state
+ end
+ MMM:InvalidateCache()
+ MMM:RefreshMarkers()
+end
+
+function ModernMapMarkers_SetFactionFilter(factionStr)
+ MMM.filters.FACTION = factionStr
+ MMM:InvalidateCache()
+ MMM:RefreshMarkers()
+end
+
+function MMM:GetZoneIndex(continentID, zoneName)
+ local zones = LOCALIZED_ZONE_CACHE[continentID] or {}
+ -- zoneName is English (from MarkerData), LOCALIZED_ZONE_CACHE has localized names
+ local localizedName = L:GetLocalizedZoneName(zoneName)
+ for i, name in ipairs(zones) do
+ if name == localizedName then return i end
+ end
+ -- Fallback: try direct match (for enUS or untranslated zones)
+ for i, name in ipairs(zones) do
+ if name == zoneName then return i end
+ end
+ return 0
+end
+
+function ModernMapMarkers_FindMarker(dataIndex)
+ MMM:BuildData()
+ local data = MMM.Data[dataIndex]
+ if not data then return end
+
+ local zoneIndex = MMM:GetZoneIndex(data.continent, data.zoneName)
+ if zoneIndex > 0 then
+ SetMapZoom(data.continent, zoneIndex)
+ end
+
+ if not MMM.filters[data.type] then
+ MMM.filters[data.type] = true
+ if ModernMapMarkers_SyncFilterUI then
+ ModernMapMarkers_SyncFilterUI(data.type, true)
+ end
+ end
+
+ MMM:InvalidateCache()
+ MMM:RefreshMarkers()
+end
+
+local original_WorldMapFrame_Update = WorldMapFrame_Update
+function WorldMapFrame_Update()
+ if original_WorldMapFrame_Update then
+ original_WorldMapFrame_Update()
+ end
+ MMM:RefreshMarkers()
+end
+
+MMM:SetScript("OnEvent", function()
+ if event == "VARIABLES_LOADED" then
+ if ModernMapMarkersDB and ModernMapMarkersDB.filters then
+ for k, v in pairs(ModernMapMarkersDB.filters) do
+ MMM.filters[k] = v
+ end
+ else
+ -- First load: auto-detect player faction for transport filter
+ local faction = UnitFactionGroup("player")
+ if faction == "Alliance" or faction == "Horde" then
+ MMM.filters.FACTION = faction
+ end
+ end
+
+ L:Initialize()
+ MMM:CacheZones()
+ MMM:BuildData()
+
+ -- Detect InstanceJournal
+ if IJDB and IJLib and IJ_ShowInstanceEntrancesIcon then
+ MMM.ijInstalled = true
+
+ -- Build POI submap zone lookup
+ if IJDB.POI then
+ for _, poi in pairs(IJDB.POI) do
+ if poi.MapContinentId and poi.MapZoneId then
+ local key = tostring(poi.MapContinentId) .. ":" .. tostring(poi.MapZoneId)
+ IJ_SUBMAP_ZONES[key] = true
+ end
+ end
+ end
+
+ -- Also include instance entrance locations inside other instance maps
+ -- (e.g., Molten Core entrance inside BRD submap)
+ local instanceMapIds = {}
+ local allInstances = {}
+ for _, inst in pairs(IJDB.DG or {}) do
+ if inst.MapId then instanceMapIds[tostring(inst.MapId)] = true end
+ table.insert(allInstances, inst)
+ end
+ for _, inst in pairs(IJDB.R or {}) do
+ if inst.MapId then instanceMapIds[tostring(inst.MapId)] = true end
+ table.insert(allInstances, inst)
+ end
+ for _, inst in ipairs(allInstances) do
+ if inst.Entrances then
+ for _, ent in pairs(inst.Entrances) do
+ if ent.MapContinentId and ent.MapZoneId
+ and instanceMapIds[tostring(ent.MapContinentId)] then
+ local key = tostring(ent.MapContinentId) .. ":" .. tostring(ent.MapZoneId)
+ IJ_SUBMAP_ZONES[key] = true
+ end
+ end
+ end
+ end
+
+ -- Hook IJ entrance markers: hide on world zones, keep in POI submaps
+ -- In submaps, reskin entrance pins with MMM dungeon/raid textures
+ local original_ShowEntrance = IJ_ShowInstanceEntrancesIcon
+ IJ_ShowInstanceEntrancesIcon = function(instance)
+ local key = tostring(GetCurrentMapContinent()) .. ":" .. tostring(GetCurrentMapZone())
+ if IJ_SUBMAP_ZONES[key] then
+ original_ShowEntrance(instance)
+
+ -- Determine marker type and apply MMM filter
+ local isRaid = instance.Type == IJLib.InstanceType.Raid
+ local filterKey = isRaid and "RAID" or "DUNGEON"
+ local mmmTex = isRaid and TEXTURE_MAP.RAID or TEXTURE_MAP.DUNGEON
+
+ local cleanName = string.gsub(instance.Name or "", "%s+", "")
+ local prefix = "IJ_EntrancePin_" .. cleanName
+
+ for _, pin in ipairs(IJ_CreatedMapInstanceEntrance) do
+ local pinName = pin:GetName() or ""
+ if string.find(pinName, prefix, 1, true) and pin:IsVisible() then
+ -- Hide pin if MMM filter is disabled for this type
+ if not MMM.filters[filterKey] then
+ pin:Hide()
+ end
+ pin.icon:SetTexture(mmmTex)
+ pin.mmmInstance = instance
+
+ if not pin.mmmReskinned then
+ pin.mmmReskinned = true
+ pin.mmmTexture = mmmTex
+
+ local mmmHL = instance.Type == IJLib.InstanceType.Raid and HIGHLIGHT_MAP.RAID or HIGHLIGHT_MAP.DUNGEON
+ if mmmHL then
+ pin:SetHighlightTexture(mmmHL)
+ end
+
+ local origOnEnter = pin:GetScript("OnEnter")
+ local origOnLeave = pin:GetScript("OnLeave")
+
+ pin:SetScript("OnEnter", function()
+ if origOnEnter then origOnEnter() end
+ this.icon:SetTexture(this.mmmTexture)
+
+ -- MMM tooltip for IJ POI submap markers
+ local inst = this.mmmInstance
+ if inst then
+ WorldMapTooltip:SetOwner(this, "ANCHOR_RIGHT")
+ WorldMapTooltip:AddLine(inst.Name or "", 1, 0.82, 0)
+ local minLvl = inst.MinLevel or 0
+ local maxLvl = inst.MaxLevel or 0
+ local lvlLabel = L:GetLocalizedString("Level") or "Level"
+ WorldMapTooltip:AddLine(lvlLabel .. ": " .. minLvl .. "-" .. maxLvl, 1, 1, 1, 1)
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Left-Click: View Map"), 0.5, 0.5, 0.5)
+ WorldMapTooltip:AddLine(L:GetLocalizedString("Right-Click: Instance Journal"), 0.5, 0.5, 0.5)
+ WorldMapTooltip:Show()
+ end
+ end)
+
+ pin:SetScript("OnLeave", function()
+ if origOnLeave then origOnLeave() end
+ this.icon:SetTexture(this.mmmTexture)
+ WorldMapTooltip:Hide()
+ end)
+
+ -- Right click for IJ journal
+ pin:RegisterForClicks("LeftButtonUp", "RightButtonUp")
+ local origOnClick = pin:GetScript("OnClick")
+ pin:SetScript("OnClick", function()
+ if arg1 == "RightButton" and this.mmmInstance then
+ local inst = this.mmmInstance
+ if not IJ_InstanceJournalFrame:IsShown() then
+ IJ_InstanceJournalFrame:Show()
+ end
+ if inst.Type == IJLib.InstanceType.Raid then
+ IJ_ShowRaids = true
+ PanelTemplates_SetTab(IJ_InstanceJournalFrame, 2)
+ else
+ IJ_ShowRaids = false
+ PanelTemplates_SetTab(IJ_InstanceJournalFrame, 1)
+ end
+ IJ_ShowEncounter(inst)
+ WorldMapFrame:Hide()
+ else
+ if origOnClick then origOnClick() end
+ end
+ end)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ MMM:InvalidateCache()
+ MMM:RefreshMarkers()
+
+ elseif event == "PLAYER_LOGOUT" then
+ if not ModernMapMarkersDB then ModernMapMarkersDB = {} end
+ ModernMapMarkersDB.filters = {}
+ for k, v in pairs(MMM.filters) do
+ ModernMapMarkersDB.filters[k] = v
+ end
+ end
+end)
+MMM:RegisterEvent("VARIABLES_LOADED")
+MMM:RegisterEvent("PLAYER_LOGOUT")
diff --git a/ModernMapMarkers.toc b/ModernMapMarkers.toc
index b6dfa04..1c5d6be 100644
--- a/ModernMapMarkers.toc
+++ b/ModernMapMarkers.toc
@@ -3,9 +3,10 @@
## Author: tilr & Heallios
## Notes: Shows dungeon, raid, world boss, boat, zeppelin, and tram locations on the world map
## Version: 2.4
-## OptionalDeps: pfUI, pfQuest, InstanceJournal
+## OptionalDeps: pfUI, pfQuest, pfQuest-turtle, InstanceJournal
## SavedVariables: ModernMapMarkersDB
+pfDB\pfDB.xml
Localization.lua
Locales\Locale_deDE.lua
Locales\Locale_esES.lua
diff --git a/ModernMapMarkers_UI.lua b/ModernMapMarkers_UI.lua
index 7686ff3..1bbe3bd 100644
--- a/ModernMapMarkers_UI.lua
+++ b/ModernMapMarkers_UI.lua
@@ -1,547 +1,580 @@
-local MMM_GUI = {}
-local isPfUI = IsAddOnLoaded("pfUI")
-local L = ModernMapMarkers_Locale
-
-function MMM_GUI.InitializeWorldMapControls()
-
- -- Filter Definitions
- local filterOptions = {
- { label = L:GetLocalizedMarkerName("All Markers"), value = "ALL", isCheck = true, checked = true },
- { label = L:GetLocalizedMarkerName("Dungeons"), value = "DUNGEON", isCheck = true, checked = true },
- { label = L:GetLocalizedMarkerName("Raids"), value = "RAID", isCheck = true, checked = true },
- { label = L:GetLocalizedMarkerName("World Bosses"), value = "WORLDBOSS", isCheck = true, checked = true },
- { label = L:GetLocalizedMarkerName("Transports"), isHeader = true },
- { label = " " .. L:GetLocalizedMarkerName("Boats"), value = "BOAT", isCheck = true, checked = true },
- { label = " " .. L:GetLocalizedMarkerName("Zeppelins"), value = "ZEPPELIN", isCheck = true, checked = true },
- { label = " " .. L:GetLocalizedMarkerName("Trams"), value = "TRAM", isCheck = true, checked = true },
- { label = L:GetLocalizedMarkerName("Transport Faction"), isHeader = true },
- { label = " " .. L:GetLocalizedMarkerName("Show All"), value = "ALL", isCheck = true, checked = true, isRadio = true, group = "fac" },
- { label = " " .. L:GetLocalizedMarkerName("Alliance"), value = "Alliance", isCheck = true, checked = false, isRadio = true, group = "fac" },
- { label = " " .. L:GetLocalizedMarkerName("Horde"), value = "Horde", isCheck = true, checked = false, isRadio = true, group = "fac" },
- }
-
- if ModernMapMarkersDB and ModernMapMarkersDB.filters then
- local saved = ModernMapMarkersDB.filters
- for _, item in ipairs(filterOptions) do
- if item.isCheck and item.value and item.value ~= "ALL" then
- if item.isRadio then
- if saved.FACTION ~= nil then
- item.checked = (saved.FACTION == item.value) or (item.value == "ALL" and saved.FACTION == "ALL")
- end
- elseif saved[item.value] ~= nil then
- item.checked = saved[item.value]
- end
- end
- end
- -- Auto-check "All Markers" if all individual filters are on
- local allOn = true
- for _, item in ipairs(filterOptions) do
- if item.isCheck and not item.isRadio and item.value ~= "ALL" then
- if not item.checked then allOn = false; break end
- end
- end
- filterOptions[1].checked = allOn
- end
-
- function ModernMapMarkers_SyncFilterUI(typeKey, state)
- for _, item in ipairs(filterOptions) do
- if item.isCheck and not item.isRadio and item.value == typeKey then
- item.checked = state
- end
- end
- local allOn = true
- for _, item in ipairs(filterOptions) do
- if item.isCheck and not item.isRadio and item.value ~= "ALL" then
- if not item.checked then allOn = false; break end
- end
- end
- filterOptions[1].checked = allOn
- end
-
- -- Determine Parent Frame (Standard or pfQuest)
- local dropdownParent = WorldMapFrame
-
- -- Dynamic dropdown width based on longest label
- local MIN_DROPDOWN_WIDTH = 120
- local PADDING = 35
- local longestLabel = 0
-
- -- Measure filter labels
- for _, item in ipairs(filterOptions) do
- local text = string.gsub(item.label, "^%s+", "")
- local len = string.len(text)
- if len > longestLabel then longestLabel = len end
- end
-
- -- Also measure button texts and find dropdown labels
- local buttonTexts = {
- L:GetLocalizedMarkerName("Filter Markers"),
- L:GetLocalizedMarkerName("Find Marker"),
- L:GetLocalizedZoneName("Kalimdor"),
- L:GetLocalizedZoneName("Eastern Kingdoms"),
- L:GetLocalizedMarkerName("Dungeons"),
- L:GetLocalizedMarkerName("Raids"),
- }
- for _, text in ipairs(buttonTexts) do
- local len = string.len(text)
- if len > longestLabel then longestLabel = len end
- end
-
- -- Approximate width: ~7px per character + padding, with minimum
- local dropdownWidth = math.max(MIN_DROPDOWN_WIDTH, longestLabel * 7 + PADDING)
- local dropdownButtonWidth = dropdownWidth + 5
-
- if _G.pfQuestMapDropdown then
- dropdownParent = _G.pfQuestMapDropdown:GetParent() or dropdownParent
- end
-
- -- Filter Dropdown
- local filterFrame = CreateFrame("Frame", "ModernMapMarkersFilter_Blizz", dropdownParent, "UIDropDownMenuTemplate")
-
- filterFrame:SetFrameStrata(dropdownParent:GetFrameStrata())
- filterFrame:SetFrameLevel(dropdownParent:GetFrameLevel() + 10)
-
- local filterBtn = getglobal(filterFrame:GetName().."Button")
- if filterBtn then filterBtn:SetFrameLevel(filterFrame:GetFrameLevel() + 2) end
-
- -- Positioning
- if _G.pfQuestMapDropdown then
- filterFrame:SetPoint("TOPRIGHT", _G.pfQuestMapDropdown, "BOTTOMRIGHT", 0, 0)
- else
- filterFrame:SetPoint("TOPRIGHT", WorldMapFrame, "TOPRIGHT", -10, -40)
- end
-
- UIDropDownMenu_Initialize(filterFrame, function(level)
- for _, itemRaw in ipairs(filterOptions) do
- local item = itemRaw
-
- if item.isHeader then
- local info = {}
- info.text = item.label
- info.isTitle = 1
- info.notCheckable = 1
- UIDropDownMenu_AddButton(info, level)
- else
- local info = {}
- info.text = string.gsub(item.label, "^%s+", "")
- info.value = item.value
- info.checked = item.checked
- info.keepShownOnClick = 1
- info.func = function()
- if item.isRadio then
- if item.checked then return end
- for _, v in ipairs(filterOptions) do
- if v.isRadio and v.group == item.group then v.checked = false end
- end
- item.checked = true
- ModernMapMarkers_SetFactionFilter(item.value)
- else
- item.checked = not item.checked
- if item.value == "ALL" then
- for _, v in ipairs(filterOptions) do
- if v.isCheck and not v.isRadio and v.value ~= "ALL" then
- v.checked = item.checked
- end
- end
- ModernMapMarkers_SetFilter("ALL", item.checked)
- else
- ModernMapMarkers_SetFilter(item.value, item.checked)
- local allOn = true
- for _, v in ipairs(filterOptions) do
- if v.isCheck and not v.isRadio and v.value ~= "ALL" then
- if not v.checked then allOn = false; break end
- end
- end
- filterOptions[1].checked = allOn
- end
- end
- ToggleDropDownMenu(1, nil, filterFrame)
- end
- UIDropDownMenu_AddButton(info, level)
- end
- end
- end)
-
- UIDropDownMenu_SetWidth(dropdownWidth, filterFrame)
- UIDropDownMenu_SetButtonWidth(dropdownButtonWidth, filterFrame)
- UIDropDownMenu_SetText(L:GetLocalizedMarkerName("Filter Markers"), filterFrame)
- UIDropDownMenu_JustifyText("RIGHT", filterFrame)
-
-
- -- Find Marker Toggle (UIDropDownMenuTemplate to match filter style)
- local findFrame = CreateFrame("Frame", "ModernMapMarkersFind_Blizz", dropdownParent, "UIDropDownMenuTemplate")
- findFrame:SetFrameStrata(filterFrame:GetFrameStrata())
- findFrame:SetFrameLevel(filterFrame:GetFrameLevel())
-
- local findBtn = getglobal(findFrame:GetName().."Button")
- if findBtn then findBtn:SetFrameLevel(findFrame:GetFrameLevel() + 2) end
-
- findFrame:SetPoint("TOPRIGHT", filterFrame, "BOTTOMRIGHT", 0, 0)
-
- UIDropDownMenu_SetWidth(dropdownWidth, findFrame)
- UIDropDownMenu_SetButtonWidth(dropdownButtonWidth, findFrame)
- UIDropDownMenu_SetText(L:GetLocalizedMarkerName("Find Marker"), findFrame)
- UIDropDownMenu_JustifyText("RIGHT", findFrame)
-
- if findBtn then
- findBtn:SetScript("OnClick", function()
- local panel = getglobal("ModernMapMarkersFind_Panel")
- if panel then
- if panel:IsVisible() then
- panel:Hide()
- else
- UpdateFindPanel()
- panel:Show()
- end
- end
- end)
- end
-
- UIDropDownMenu_Initialize(findFrame, function() end)
-
- -- Find Marker Panel
- local PANEL_WIDTH = 220
- local ROW_HEIGHT = 16
- local MAX_VISIBLE_ROWS = 12
- local BUTTON_HEIGHT = 20
- local BUTTON_SPACING = 2
- local PANEL_PADDING = 8
-
- local findPanel = CreateFrame("Frame", "ModernMapMarkersFind_Panel", WorldMapFrame)
- findPanel:SetFrameStrata(filterFrame:GetFrameStrata())
- findPanel:SetFrameLevel(filterFrame:GetFrameLevel() + 20)
- findPanel:SetWidth(PANEL_WIDTH)
- findPanel:SetPoint("TOPRIGHT", findFrame, "BOTTOMRIGHT", -16, 0)
- findPanel:SetBackdrop({
- bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- tile = true, tileSize = 16, edgeSize = 16,
- insets = { left = 4, right = 4, top = 4, bottom = 4 },
- })
- findPanel:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
- findPanel:SetBackdropBorderColor(0.6, 0.6, 0.6, 1)
- findPanel:Hide()
-
- local activeContinent = 1
- local activeType = "DUNGEON"
-
- local function CreateSelectorButton(name, parent, width, text)
- local btn = CreateFrame("Button", name, parent)
- btn:SetWidth(width)
- btn:SetHeight(BUTTON_HEIGHT)
-
- btn:SetBackdrop({
- bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- tile = true, tileSize = 16, edgeSize = 12,
- insets = { left = 2, right = 2, top = 2, bottom = 2 },
- })
-
- local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
- label:SetPoint("CENTER", 0, 0)
- label:SetText(text)
- btn.label = label
-
- btn:SetScript("OnEnter", function()
- if not btn.isActive then
- btn:SetBackdropColor(0.3, 0.3, 0.3, 1)
- end
- end)
- btn:SetScript("OnLeave", function()
- if not btn.isActive then
- btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
- end
- end)
-
- btn.isActive = false
- btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
- btn:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
-
- return btn
- end
-
- local function SetButtonActive(btn, active)
- btn.isActive = active
- if active then
- btn:SetBackdropColor(0.2, 0.4, 0.7, 1)
- btn:SetBackdropBorderColor(0.4, 0.6, 1.0, 1)
- btn.label:SetTextColor(1, 1, 1)
- else
- btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
- btn:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
- btn.label:SetTextColor(0.8, 0.8, 0.8)
- end
- end
-
- -- Continent buttons
- local halfWidth = (PANEL_WIDTH - PANEL_PADDING * 2 - BUTTON_SPACING) / 2
-
- local btnKalimdor = CreateSelectorButton("MMMFind_Kalimdor", findPanel, halfWidth, L:GetLocalizedZoneName("Kalimdor"))
- btnKalimdor:SetPoint("TOPLEFT", findPanel, "TOPLEFT", PANEL_PADDING, -PANEL_PADDING)
-
- local btnEK = CreateSelectorButton("MMMFind_EK", findPanel, halfWidth, L:GetLocalizedZoneName("Eastern Kingdoms"))
- btnEK:SetPoint("TOPLEFT", btnKalimdor, "TOPRIGHT", BUTTON_SPACING, 0)
-
- -- Type buttons
- local thirdWidth = (PANEL_WIDTH - PANEL_PADDING * 2 - BUTTON_SPACING * 2) / 3
-
- local btnDungeon = CreateSelectorButton("MMMFind_Dungeon", findPanel, thirdWidth, L:GetLocalizedMarkerName("Dungeons"))
- btnDungeon:SetPoint("TOPLEFT", btnKalimdor, "BOTTOMLEFT", 0, -BUTTON_SPACING)
-
- local btnRaid = CreateSelectorButton("MMMFind_Raid", findPanel, thirdWidth, L:GetLocalizedMarkerName("Raids"))
- btnRaid:SetPoint("TOPLEFT", btnDungeon, "TOPRIGHT", BUTTON_SPACING, 0)
-
- local btnWorldBoss = CreateSelectorButton("MMMFind_WorldBoss", findPanel, thirdWidth, L:GetLocalizedMarkerName("World Bosses"))
- btnWorldBoss:SetPoint("TOPLEFT", btnRaid, "TOPRIGHT", BUTTON_SPACING, 0)
-
- -- Scroll frame for the instance list
- local listAreaTop = PANEL_PADDING + BUTTON_HEIGHT * 2 + BUTTON_SPACING * 2 + 4
-
- local scrollFrame = CreateFrame("ScrollFrame", "MMMFind_Scroll", findPanel, "FauxScrollFrameTemplate")
- scrollFrame:SetPoint("TOPLEFT", findPanel, "TOPLEFT", PANEL_PADDING, -listAreaTop)
- scrollFrame:SetPoint("BOTTOMRIGHT", findPanel, "BOTTOMRIGHT", -PANEL_PADDING - 22, PANEL_PADDING)
-
- -- Row buttons
- local rowButtons = {}
- for i = 1, MAX_VISIBLE_ROWS do
- local row = CreateFrame("Button", "MMMFind_Row" .. i, findPanel)
- row:SetHeight(ROW_HEIGHT)
- row:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, -((i - 1) * ROW_HEIGHT))
- row:SetPoint("RIGHT", scrollFrame, "RIGHT", 0, 0)
-
- local highlight = row:CreateTexture(nil, "HIGHLIGHT")
- highlight:SetAllPoints(row)
- highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
- highlight:SetBlendMode("ADD")
- highlight:SetAlpha(0.4)
-
- local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
- nameText:SetPoint("LEFT", row, "LEFT", 4, 0)
- nameText:SetJustifyH("LEFT")
- row.nameText = nameText
-
- local lvlText = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
- lvlText:SetPoint("RIGHT", row, "RIGHT", -4, 0)
- lvlText:SetJustifyH("RIGHT")
- row.lvlText = lvlText
-
- row:SetScript("OnEnter", function()
- if row.dataID then
- WorldMapTooltip:SetOwner(row, "ANCHOR_LEFT")
- WorldMapTooltip:AddLine(row.nameText:GetText(), 1, 0.82, 0)
- if row.lvlText:GetText() and row.lvlText:GetText() ~= "" then
- WorldMapTooltip:AddLine(row.lvlText:GetText(), 1, 1, 1)
- end
- WorldMapTooltip:Show()
- end
- end)
- row:SetScript("OnLeave", function()
- WorldMapTooltip:Hide()
- end)
-
- row:Hide()
- rowButtons[i] = row
- end
-
- -- Current filtered + sorted list cache
- local currentList = {}
-
- -- Sort function: level then name
- local function sortLvl(a, b)
- local _, _, alvlStr = string.find(a.description or "", "^(%d+)")
- local _, _, blvlStr = string.find(b.description or "", "^(%d+)")
- local alvl = tonumber(alvlStr) or 0
- local blvl = tonumber(blvlStr) or 0
- if alvl == blvl then return (a.name or "") < (b.name or "") end
- return alvl < blvl
- end
-
- local function RefreshFindList()
- local flatData = ModernMapMarkers_GetFlatData()
- currentList = {}
-
- for _, data in ipairs(flatData) do
- if data.continent == activeContinent and data.type == activeType then
- table.insert(currentList, data)
- end
- end
- table.sort(currentList, sortLvl)
-
- local totalRows = table.getn(currentList)
- local displayRows = math.min(totalRows, MAX_VISIBLE_ROWS)
-
- local panelHeight = listAreaTop + (displayRows * ROW_HEIGHT) + PANEL_PADDING + 4
- if totalRows > MAX_VISIBLE_ROWS then
- panelHeight = listAreaTop + (MAX_VISIBLE_ROWS * ROW_HEIGHT) + PANEL_PADDING + 4
- end
- findPanel:SetHeight(panelHeight)
-
- FauxScrollFrame_Update(scrollFrame, totalRows, MAX_VISIBLE_ROWS, ROW_HEIGHT)
-
- local offset = FauxScrollFrame_GetOffset(scrollFrame)
- local lvlLabel = L:GetLocalizedMarkerName("Level") or "Level"
-
- for i = 1, MAX_VISIBLE_ROWS do
- local row = rowButtons[i]
- local dataIndex = offset + i
-
- if dataIndex <= totalRows then
- local data = currentList[dataIndex]
- local localizedName = L:GetLocalizedMarkerName(data.name)
- row.nameText:SetText(localizedName)
-
- if data.description then
- row.lvlText:SetText(lvlLabel .. " " .. data.description)
- else
- row.lvlText:SetText("")
- end
-
- row.nameText:SetWidth(row:GetWidth() - row.lvlText:GetStringWidth() - 16)
-
- row.dataID = data.id
- row:SetScript("OnClick", function()
- ModernMapMarkers_FindMarker(data.id)
- end)
- row:Show()
- else
- row.nameText:SetText("")
- row.lvlText:SetText("")
- row.dataID = nil
- row:Hide()
- end
- end
- end
-
- scrollFrame:SetScript("OnVerticalScroll", function()
- FauxScrollFrame_OnVerticalScroll(ROW_HEIGHT, RefreshFindList)
- end)
-
- -- Button click handlers
- local function UpdateButtonStates()
- SetButtonActive(btnKalimdor, activeContinent == 1)
- SetButtonActive(btnEK, activeContinent == 2)
- SetButtonActive(btnDungeon, activeType == "DUNGEON")
- SetButtonActive(btnRaid, activeType == "RAID")
- SetButtonActive(btnWorldBoss, activeType == "WORLDBOSS")
- end
-
- btnKalimdor:SetScript("OnClick", function()
- activeContinent = 1
- UpdateButtonStates()
- RefreshFindList()
- end)
-
- btnEK:SetScript("OnClick", function()
- activeContinent = 2
- UpdateButtonStates()
- RefreshFindList()
- end)
-
- btnDungeon:SetScript("OnClick", function()
- activeType = "DUNGEON"
- UpdateButtonStates()
- RefreshFindList()
- end)
-
- btnRaid:SetScript("OnClick", function()
- activeType = "RAID"
- UpdateButtonStates()
- RefreshFindList()
- end)
-
- btnWorldBoss:SetScript("OnClick", function()
- activeType = "WORLDBOSS"
- UpdateButtonStates()
- RefreshFindList()
- end)
-
- function UpdateFindPanel()
- UpdateButtonStates()
- RefreshFindList()
- end
-
- -- Close panel when the world map closes
- local origOnHide = WorldMapFrame:GetScript("OnHide")
- WorldMapFrame:SetScript("OnHide", function()
- if origOnHide then origOnHide() end
- findPanel:Hide()
- end)
-
- UpdateButtonStates()
-
- -- pfUI Skinning
- if isPfUI and _G.pfUI and _G.pfUI.api then
- if _G.pfUI.api.SkinDropDown then
- _G.pfUI.api.SkinDropDown(filterFrame)
- _G.pfUI.api.SkinDropDown(findFrame)
- end
- if _G.pfUI.api.SkinButton then
- _G.pfUI.api.SkinButton(btnKalimdor)
- _G.pfUI.api.SkinButton(btnEK)
- _G.pfUI.api.SkinButton(btnDungeon)
- _G.pfUI.api.SkinButton(btnRaid)
- _G.pfUI.api.SkinButton(btnWorldBoss)
- end
- end
-
- -- Apply saved visibility state
- if ModernMapMarkersDB and ModernMapMarkersDB.hideDropdowns then
- filterFrame:Hide()
- findFrame:Hide()
- end
-
-end
-
--- Debug Utilities
-SLASH_MMMDEBUG1 = "/mmmzones"
-SlashCmdList["MMMDEBUG"] = function()
- if not DEFAULT_CHAT_FRAME then return end
- for contID = 1, 2 do
- local contName = (contID == 1) and "Kalimdor" or "Eastern Kingdoms"
- DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModernMapMarkers:|r Zone Indexes for " .. contName .. ":")
- local zones = { GetMapZones(contID) }
- for i, zoneName in ipairs(zones) do
- DEFAULT_CHAT_FRAME:AddMessage(" " .. i .. ": " .. zoneName)
- end
- end
-end
-
--- Slash command to toggle dropdown visibility
-SLASH_MMM1 = "/mmm"
-SlashCmdList["MMM"] = function()
- if type(ModernMapMarkersDB) ~= "table" then
- ModernMapMarkersDB = { filters = {} }
- end
- ModernMapMarkersDB.hideDropdowns = not ModernMapMarkersDB.hideDropdowns
-
- local filterFrame = getglobal("ModernMapMarkersFilter_Blizz")
- local findFrame = getglobal("ModernMapMarkersFind_Blizz")
- local findPanel = getglobal("ModernMapMarkersFind_Panel")
-
- if filterFrame and findFrame then
- if ModernMapMarkersDB.hideDropdowns then
- filterFrame:Hide()
- findFrame:Hide()
- if findPanel then findPanel:Hide() end
- if DEFAULT_CHAT_FRAME then
- DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModern|rMapMarkers: " .. L:GetLocalizedMarkerName("Dropdown menus hidden."))
- end
- else
- filterFrame:Show()
- findFrame:Show()
- if DEFAULT_CHAT_FRAME then
- DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModern|rMapMarkers: " .. L:GetLocalizedMarkerName("Dropdown menus shown."))
- end
- end
- end
-end
-
-local initFrame = CreateFrame("Frame")
-initFrame:RegisterEvent("VARIABLES_LOADED")
-initFrame:SetScript("OnEvent", function()
- L:Initialize()
- MMM_GUI.InitializeWorldMapControls()
- this:UnregisterEvent("VARIABLES_LOADED")
-end)
-
+local MMM_GUI = {}
+local isPfUI = IsAddOnLoaded("pfUI")
+local L = ModernMapMarkers_Locale
+
+function MMM_GUI.InitializeWorldMapControls()
+
+ -- Filter Definitions
+ local filterOptions = {
+ { label = L:GetLocalizedString("All Markers"), value = "ALL", isCheck = true, checked = true },
+ { label = L:GetLocalizedString("Dungeons"), value = "DUNGEON", isCheck = true, checked = true },
+ { label = L:GetLocalizedString("Raids"), value = "RAID", isCheck = true, checked = true },
+ { label = L:GetLocalizedString("World Bosses"), value = "WORLDBOSS", isCheck = true, checked = true },
+ { label = L:GetLocalizedString("Transports"), isHeader = true },
+ { label = " " .. L:GetLocalizedString("Boats"), value = "BOAT", isCheck = true, checked = true },
+ { label = " " .. L:GetLocalizedString("Zeppelins"), value = "ZEPPELIN", isCheck = true, checked = true },
+ { label = " " .. L:GetLocalizedString("Trams"), value = "TRAM", isCheck = true, checked = true },
+ { label = L:GetLocalizedString("Transport Faction"), isHeader = true },
+ { label = " " .. L:GetLocalizedString("Show All"), value = "ALL", isCheck = true, checked = true, isRadio = true, group = "fac" },
+ { label = " " .. L:GetLocalizedString("Alliance"), value = "Alliance", isCheck = true, checked = false, isRadio = true, group = "fac" },
+ { label = " " .. L:GetLocalizedString("Horde"), value = "Horde", isCheck = true, checked = false, isRadio = true, group = "fac" },
+ }
+
+ if ModernMapMarkersDB and ModernMapMarkersDB.filters then
+ local saved = ModernMapMarkersDB.filters
+ for _, item in ipairs(filterOptions) do
+ if item.isCheck and item.value and item.value ~= "ALL" then
+ if item.isRadio then
+ if saved.FACTION ~= nil then
+ item.checked = (saved.FACTION == item.value) or (item.value == "ALL" and saved.FACTION == "ALL")
+ end
+ elseif saved[item.value] ~= nil then
+ item.checked = saved[item.value]
+ end
+ end
+ end
+ -- Auto-check "All Markers" if all individual filters are on
+ local allOn = true
+ for _, item in ipairs(filterOptions) do
+ if item.isCheck and not item.isRadio and item.value ~= "ALL" then
+ if not item.checked then allOn = false; break end
+ end
+ end
+ filterOptions[1].checked = allOn
+ end
+
+ function ModernMapMarkers_SyncFilterUI(typeKey, state)
+ for _, item in ipairs(filterOptions) do
+ if item.isCheck and not item.isRadio and item.value == typeKey then
+ item.checked = state
+ end
+ end
+ local allOn = true
+ for _, item in ipairs(filterOptions) do
+ if item.isCheck and not item.isRadio and item.value ~= "ALL" then
+ if not item.checked then allOn = false; break end
+ end
+ end
+ filterOptions[1].checked = allOn
+ end
+
+ -- Determine Parent Frame (Standard or pfQuest)
+ local dropdownParent = WorldMapFrame
+
+ -- Dynamic dropdown width based on longest label
+ local MIN_DROPDOWN_WIDTH = 120
+ local PADDING = 35
+ local longestLabel = 0
+
+ -- Measure filter labels
+ for _, item in ipairs(filterOptions) do
+ local text = string.gsub(item.label, "^%s+", "")
+ local len = string.len(text)
+ if len > longestLabel then longestLabel = len end
+ end
+
+ -- Also measure button texts and find dropdown labels
+ local buttonTexts = {
+ L:GetLocalizedString("Filter Markers"),
+ L:GetLocalizedString("Find Marker"),
+ L:GetLocalizedZoneName("Kalimdor"),
+ L:GetLocalizedZoneName("Eastern Kingdoms"),
+ L:GetLocalizedString("Dungeons"),
+ L:GetLocalizedString("Raids"),
+ }
+ for _, text in ipairs(buttonTexts) do
+ local len = string.len(text)
+ if len > longestLabel then longestLabel = len end
+ end
+
+ -- Approximate width: ~7px per character + padding, with minimum
+ local dropdownWidth = math.max(MIN_DROPDOWN_WIDTH, longestLabel * 7 + PADDING)
+ local dropdownButtonWidth = dropdownWidth + 5
+
+ if _G.pfQuestMapDropdown then
+ dropdownParent = _G.pfQuestMapDropdown:GetParent() or dropdownParent
+ end
+
+ -- Filter Dropdown
+ local filterFrame = CreateFrame("Frame", "ModernMapMarkersFilter_Blizz", dropdownParent, "UIDropDownMenuTemplate")
+
+ filterFrame:SetFrameStrata(dropdownParent:GetFrameStrata())
+ filterFrame:SetFrameLevel(dropdownParent:GetFrameLevel() + 10)
+
+ local filterBtn = getglobal(filterFrame:GetName().."Button")
+ if filterBtn then filterBtn:SetFrameLevel(filterFrame:GetFrameLevel() + 2) end
+
+ -- Positioning
+ if _G.pfQuestMapDropdown then
+ filterFrame:SetPoint("TOPRIGHT", _G.pfQuestMapDropdown, "BOTTOMRIGHT", 0, 0)
+ else
+ filterFrame:SetPoint("TOPRIGHT", WorldMapFrame, "TOPRIGHT", -10, -40)
+ end
+
+ UIDropDownMenu_Initialize(filterFrame, function(level)
+ for _, itemRaw in ipairs(filterOptions) do
+ local item = itemRaw
+
+ if item.isHeader then
+ local info = {}
+ info.text = item.label
+ info.isTitle = 1
+ info.notCheckable = 1
+ UIDropDownMenu_AddButton(info, level)
+ else
+ local info = {}
+ info.text = string.gsub(item.label, "^%s+", "")
+ info.value = item.value
+ info.checked = item.checked
+ info.keepShownOnClick = 1
+ info.func = function()
+ if item.isRadio then
+ if item.checked then return end
+ for _, v in ipairs(filterOptions) do
+ if v.isRadio and v.group == item.group then v.checked = false end
+ end
+ item.checked = true
+ ModernMapMarkers_SetFactionFilter(item.value)
+ else
+ item.checked = not item.checked
+ if item.value == "ALL" then
+ for _, v in ipairs(filterOptions) do
+ if v.isCheck and not v.isRadio and v.value ~= "ALL" then
+ v.checked = item.checked
+ end
+ end
+ ModernMapMarkers_SetFilter("ALL", item.checked)
+ else
+ ModernMapMarkers_SetFilter(item.value, item.checked)
+ local allOn = true
+ for _, v in ipairs(filterOptions) do
+ if v.isCheck and not v.isRadio and v.value ~= "ALL" then
+ if not v.checked then allOn = false; break end
+ end
+ end
+ filterOptions[1].checked = allOn
+ end
+ end
+ ToggleDropDownMenu(1, nil, filterFrame)
+ end
+ UIDropDownMenu_AddButton(info, level)
+ end
+ end
+ end)
+
+ UIDropDownMenu_SetWidth(dropdownWidth, filterFrame)
+ UIDropDownMenu_SetButtonWidth(dropdownButtonWidth, filterFrame)
+ UIDropDownMenu_SetText(L:GetLocalizedString("Filter Markers"), filterFrame)
+ UIDropDownMenu_JustifyText("RIGHT", filterFrame)
+
+
+ -- Find Marker Toggle (UIDropDownMenuTemplate to match filter style)
+ local findFrame = CreateFrame("Frame", "ModernMapMarkersFind_Blizz", dropdownParent, "UIDropDownMenuTemplate")
+ findFrame:SetFrameStrata(filterFrame:GetFrameStrata())
+ findFrame:SetFrameLevel(filterFrame:GetFrameLevel())
+
+ local findBtn = getglobal(findFrame:GetName().."Button")
+ if findBtn then findBtn:SetFrameLevel(findFrame:GetFrameLevel() + 2) end
+
+ findFrame:SetPoint("TOPRIGHT", filterFrame, "BOTTOMRIGHT", 0, 0)
+
+ UIDropDownMenu_SetWidth(dropdownWidth, findFrame)
+ UIDropDownMenu_SetButtonWidth(dropdownButtonWidth, findFrame)
+ UIDropDownMenu_SetText(L:GetLocalizedString("Find Marker"), findFrame)
+ UIDropDownMenu_JustifyText("RIGHT", findFrame)
+
+ if findBtn then
+ findBtn:SetScript("OnClick", function()
+ local panel = getglobal("ModernMapMarkersFind_Panel")
+ if panel then
+ if panel:IsVisible() then
+ panel:Hide()
+ else
+ UpdateFindPanel()
+ panel:Show()
+ end
+ end
+ end)
+ end
+
+ UIDropDownMenu_Initialize(findFrame, function() end)
+
+ -- Find Marker Panel
+ local PANEL_WIDTH = 240
+ local ROW_HEIGHT = 16
+ local MAX_VISIBLE_ROWS = 12
+ local BUTTON_HEIGHT = 20
+ local BUTTON_SPACING = 2
+ local PANEL_PADDING = 8
+
+ local findPanel = CreateFrame("Frame", "ModernMapMarkersFind_Panel", WorldMapFrame)
+ findPanel:SetFrameStrata(filterFrame:GetFrameStrata())
+ findPanel:SetFrameLevel(filterFrame:GetFrameLevel() + 20)
+ findPanel:SetWidth(PANEL_WIDTH)
+ findPanel:SetPoint("TOPRIGHT", findFrame, "BOTTOMRIGHT", -16, 0)
+ findPanel:SetBackdrop({
+ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 16,
+ insets = { left = 4, right = 4, top = 4, bottom = 4 },
+ })
+ findPanel:SetBackdropColor(0.1, 0.1, 0.1, 0.95)
+ findPanel:SetBackdropBorderColor(0.6, 0.6, 0.6, 1)
+ findPanel:Hide()
+
+ local activeContinent = 1
+ local activeType = "DUNGEON"
+
+ local function CreateSelectorButton(name, parent, width, text)
+ local btn = CreateFrame("Button", name, parent)
+ btn:SetWidth(width)
+ btn:SetHeight(BUTTON_HEIGHT)
+
+ btn:SetBackdrop({
+ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ tile = true, tileSize = 16, edgeSize = 12,
+ insets = { left = 2, right = 2, top = 2, bottom = 2 },
+ })
+
+ local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ label:SetPoint("CENTER", 0, 0)
+ label:SetText(text)
+ btn.label = label
+
+ btn:SetScript("OnEnter", function()
+ if not btn.isActive then
+ btn:SetBackdropColor(0.3, 0.3, 0.3, 1)
+ end
+ end)
+ btn:SetScript("OnLeave", function()
+ if not btn.isActive then
+ btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
+ end
+ end)
+
+ btn.isActive = false
+ btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
+ btn:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
+
+ return btn
+ end
+
+ local function SetButtonActive(btn, active)
+ btn.isActive = active
+ if active then
+ btn:SetBackdropColor(0.2, 0.4, 0.7, 1)
+ btn:SetBackdropBorderColor(0.4, 0.6, 1.0, 1)
+ btn.label:SetTextColor(1, 1, 1)
+ else
+ btn:SetBackdropColor(0.15, 0.15, 0.15, 1)
+ btn:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
+ btn.label:SetTextColor(0.8, 0.8, 0.8)
+ end
+ end
+
+ -- Continent buttons
+ local halfWidth = (PANEL_WIDTH - PANEL_PADDING * 2 - BUTTON_SPACING) / 2
+
+ local btnKalimdor = CreateSelectorButton("MMMFind_Kalimdor", findPanel, halfWidth, L:GetLocalizedString("Kalimdor"))
+ btnKalimdor:SetPoint("TOPLEFT", findPanel, "TOPLEFT", PANEL_PADDING, -PANEL_PADDING)
+
+ local btnEK = CreateSelectorButton("MMMFind_EK", findPanel, halfWidth, L:GetLocalizedString("Eastern Kingdoms"))
+ btnEK:SetPoint("TOPLEFT", btnKalimdor, "TOPRIGHT", BUTTON_SPACING, 0)
+
+ -- Type buttons
+ local thirdWidth = (PANEL_WIDTH - PANEL_PADDING * 2 - BUTTON_SPACING * 2) / 3
+
+ local btnDungeon = CreateSelectorButton("MMMFind_Dungeon", findPanel, thirdWidth, L:GetLocalizedString("Dungeons"))
+ btnDungeon:SetPoint("TOPLEFT", btnKalimdor, "BOTTOMLEFT", 0, -BUTTON_SPACING)
+
+ local btnRaid = CreateSelectorButton("MMMFind_Raid", findPanel, thirdWidth, L:GetLocalizedString("Raids"))
+ btnRaid:SetPoint("TOPLEFT", btnDungeon, "TOPRIGHT", BUTTON_SPACING, 0)
+
+ local btnWorldBoss = CreateSelectorButton("MMMFind_WorldBoss", findPanel, thirdWidth, L:GetLocalizedString("World Bosses"))
+ btnWorldBoss:SetPoint("TOPLEFT", btnRaid, "TOPRIGHT", BUTTON_SPACING, 0)
+
+ -- Scroll frame for the instance list
+ local listAreaTop = PANEL_PADDING + BUTTON_HEIGHT * 2 + BUTTON_SPACING * 2 + 4
+
+ local scrollFrame = CreateFrame("ScrollFrame", "MMMFind_Scroll", findPanel, "FauxScrollFrameTemplate")
+ scrollFrame:SetPoint("TOPLEFT", findPanel, "TOPLEFT", PANEL_PADDING, -listAreaTop)
+ scrollFrame:SetPoint("BOTTOMRIGHT", findPanel, "BOTTOMRIGHT", -PANEL_PADDING - 22, PANEL_PADDING)
+
+ -- Row buttons
+ local rowButtons = {}
+ for i = 1, MAX_VISIBLE_ROWS do
+ local row = CreateFrame("Button", "MMMFind_Row" .. i, findPanel)
+ row:SetHeight(ROW_HEIGHT)
+ row:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, -((i - 1) * ROW_HEIGHT))
+ row:SetPoint("RIGHT", scrollFrame, "RIGHT", 0, 0)
+
+ local highlight = row:CreateTexture(nil, "HIGHLIGHT")
+ highlight:SetAllPoints(row)
+ highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+ highlight:SetBlendMode("ADD")
+ highlight:SetAlpha(0.4)
+
+ local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ nameText:SetPoint("LEFT", row, "LEFT", 4, 0)
+ nameText:SetJustifyH("LEFT")
+ row.nameText = nameText
+
+ local lvlText = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
+ lvlText:SetPoint("RIGHT", row, "RIGHT", -4, 0)
+ lvlText:SetJustifyH("RIGHT")
+ row.lvlText = lvlText
+
+ row:SetScript("OnEnter", function()
+ if row.dataID then
+ WorldMapTooltip:SetOwner(row, "ANCHOR_LEFT")
+ WorldMapTooltip:AddLine(row.tooltipNameText, 1, 0.82, 0)
+ if row.tooltipLvlText then
+ WorldMapTooltip:AddLine(row.tooltipLvlText, 1, 1, 1)
+ end
+ WorldMapTooltip:Show()
+ end
+ end)
+ row:SetScript("OnLeave", function()
+ WorldMapTooltip:Hide()
+ end)
+
+ row:Hide()
+ rowButtons[i] = row
+ end
+
+ -- Current filtered + sorted list cache
+ local currentList = {}
+
+ -- Sort function: level then name
+ local function sortLvl(a, b)
+ local _, _, alvlStr = string.find(a.description or "", "^(%d+)")
+ local _, _, blvlStr = string.find(b.description or "", "^(%d+)")
+ local alvl = tonumber(alvlStr) or 0
+ local blvl = tonumber(blvlStr) or 0
+ if alvl == blvl then return (a.name or "") < (b.name or "") end
+ return alvl < blvl
+ end
+
+ -- Set text on a FontString and truncate with ellipsis if rendered width > maxWidth.
+ -- fontString : a valid FontString object
+ -- text : string to set
+ -- maxWidth : maximum allowed width in pixels (number)
+ local function SetFontStringTruncated(fontString, text, maxWidth)
+ if not fontString or type(text) ~= "string" or type(maxWidth) ~= "number" then return end
+ fontString:SetText(text)
+ if fontString:GetStringWidth() <= maxWidth then return end -- return if whole string isn't longer anyways
+
+ local ELLIPSIS = "…"
+ local lo = 0; local hi = string.len(text); local best = 0
+ while lo <= hi do -- binary search to determine the maximum string length including ellipsis
+ local mid = math.floor((lo + hi) / 2)
+ local substr = string.sub(text, 1, mid) .. ELLIPSIS
+ fontString:SetText(substr)
+ if fontString:GetStringWidth() <= maxWidth then
+ best = mid
+ lo = mid + 1
+ else
+ hi = mid - 1
+ end
+ end
+
+ if best > 0 then
+ fontString:SetText(string.sub(text, 1, best) .. ELLIPSIS)
+ else
+ fontString:SetText(ELLIPSIS) -- only possible when maxWidth is way too small
+ end
+ end
+
+ local function RefreshFindList()
+ local flatData = ModernMapMarkers_GetFlatData()
+ currentList = {}
+
+ for _, data in ipairs(flatData) do
+ if data.continent == activeContinent and data.type == activeType then
+ table.insert(currentList, data)
+ end
+ end
+ table.sort(currentList, sortLvl)
+
+ local totalRows = table.getn(currentList)
+ local displayRows = math.min(totalRows, MAX_VISIBLE_ROWS)
+
+ local panelHeight = listAreaTop + (displayRows * ROW_HEIGHT) + PANEL_PADDING + 4
+ if totalRows > MAX_VISIBLE_ROWS then
+ panelHeight = listAreaTop + (MAX_VISIBLE_ROWS * ROW_HEIGHT) + PANEL_PADDING + 4
+ end
+ findPanel:SetHeight(panelHeight)
+
+ FauxScrollFrame_Update(scrollFrame, totalRows, MAX_VISIBLE_ROWS, ROW_HEIGHT)
+
+ local offset = FauxScrollFrame_GetOffset(scrollFrame)
+ local lvlLabel = L:GetLocalizedString("Level") or "Level"
+
+ for i = 1, MAX_VISIBLE_ROWS do
+ local row = rowButtons[i]
+ local dataIndex = offset + i
+
+ if dataIndex <= totalRows then
+ local data = currentList[dataIndex]
+ local localizedName = L:GetLocalizedString(data.name, data.type)
+ row.nameText:SetText(localizedName)
+ row.tooltipNameText = localizedName -- store the full name text so it never gets truncated in the tooltip
+ row.tooltipLvlText = lvlLabel .. " " .. data.description
+
+ if data.description then
+ row.lvlText:SetText(data.description)
+ else
+ row.lvlText:SetText("")
+ end
+
+ SetFontStringTruncated(row.nameText, localizedName, row:GetWidth() - row.lvlText:GetStringWidth())
+ row.nameText:SetWidth(row:GetWidth() - row.lvlText:GetStringWidth())
+
+ row.dataID = data.id
+ row:SetScript("OnClick", function()
+ ModernMapMarkers_FindMarker(data.id)
+ end)
+ row:Show()
+ else
+ row.nameText:SetText("")
+ row.lvlText:SetText("")
+ row.dataID = nil
+ row:Hide()
+ end
+ end
+ end
+
+ scrollFrame:SetScript("OnVerticalScroll", function()
+ FauxScrollFrame_OnVerticalScroll(ROW_HEIGHT, RefreshFindList)
+ end)
+
+ -- Button click handlers
+ local function UpdateButtonStates()
+ SetButtonActive(btnKalimdor, activeContinent == 1)
+ SetButtonActive(btnEK, activeContinent == 2)
+ SetButtonActive(btnDungeon, activeType == "DUNGEON")
+ SetButtonActive(btnRaid, activeType == "RAID")
+ SetButtonActive(btnWorldBoss, activeType == "WORLDBOSS")
+ end
+
+ btnKalimdor:SetScript("OnClick", function()
+ activeContinent = 1
+ UpdateButtonStates()
+ RefreshFindList()
+ end)
+
+ btnEK:SetScript("OnClick", function()
+ activeContinent = 2
+ UpdateButtonStates()
+ RefreshFindList()
+ end)
+
+ btnDungeon:SetScript("OnClick", function()
+ activeType = "DUNGEON"
+ UpdateButtonStates()
+ RefreshFindList()
+ end)
+
+ btnRaid:SetScript("OnClick", function()
+ activeType = "RAID"
+ UpdateButtonStates()
+ RefreshFindList()
+ end)
+
+ btnWorldBoss:SetScript("OnClick", function()
+ activeType = "WORLDBOSS"
+ UpdateButtonStates()
+ RefreshFindList()
+ end)
+
+ function UpdateFindPanel()
+ UpdateButtonStates()
+ RefreshFindList()
+ end
+
+ -- Close panel when the world map closes
+ local origOnHide = WorldMapFrame:GetScript("OnHide")
+ WorldMapFrame:SetScript("OnHide", function()
+ if origOnHide then origOnHide() end
+ findPanel:Hide()
+ end)
+
+ UpdateButtonStates()
+
+ -- pfUI Skinning
+ if isPfUI and _G.pfUI and _G.pfUI.api then
+ if _G.pfUI.api.SkinDropDown then
+ _G.pfUI.api.SkinDropDown(filterFrame)
+ _G.pfUI.api.SkinDropDown(findFrame)
+ end
+ if _G.pfUI.api.SkinButton then
+ _G.pfUI.api.SkinButton(btnKalimdor)
+ _G.pfUI.api.SkinButton(btnEK)
+ _G.pfUI.api.SkinButton(btnDungeon)
+ _G.pfUI.api.SkinButton(btnRaid)
+ _G.pfUI.api.SkinButton(btnWorldBoss)
+ end
+ end
+
+ -- Apply saved visibility state
+ if ModernMapMarkersDB and ModernMapMarkersDB.hideDropdowns then
+ filterFrame:Hide()
+ findFrame:Hide()
+ end
+
+end
+
+-- Debug Utilities
+SLASH_MMMDEBUG1 = "/mmmzones"
+SlashCmdList["MMMDEBUG"] = function()
+ if not DEFAULT_CHAT_FRAME then return end
+ for contID = 1, 2 do
+ local contName = (contID == 1) and "Kalimdor" or "Eastern Kingdoms"
+ DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModernMapMarkers:|r Zone Indexes for " .. contName .. ":")
+ local zones = { GetMapZones(contID) }
+ for i, zoneName in ipairs(zones) do
+ DEFAULT_CHAT_FRAME:AddMessage(" " .. i .. ": " .. zoneName)
+ end
+ end
+end
+
+-- Slash command to toggle dropdown visibility
+SLASH_MMM1 = "/mmm"
+SlashCmdList["MMM"] = function()
+ if type(ModernMapMarkersDB) ~= "table" then
+ ModernMapMarkersDB = { filters = {} }
+ end
+ ModernMapMarkersDB.hideDropdowns = not ModernMapMarkersDB.hideDropdowns
+
+ local filterFrame = getglobal("ModernMapMarkersFilter_Blizz")
+ local findFrame = getglobal("ModernMapMarkersFind_Blizz")
+ local findPanel = getglobal("ModernMapMarkersFind_Panel")
+
+ if filterFrame and findFrame then
+ if ModernMapMarkersDB.hideDropdowns then
+ filterFrame:Hide()
+ findFrame:Hide()
+ if findPanel then findPanel:Hide() end
+ if DEFAULT_CHAT_FRAME then
+ DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModern|rMapMarkers: " .. L:GetLocalizedString("Dropdown menus hidden."))
+ end
+ else
+ filterFrame:Show()
+ findFrame:Show()
+ if DEFAULT_CHAT_FRAME then
+ DEFAULT_CHAT_FRAME:AddMessage("|cff7fff7fModern|rMapMarkers: " .. L:GetLocalizedString("Dropdown menus shown."))
+ end
+ end
+ end
+end
+
+local initFrame = CreateFrame("Frame")
+initFrame:RegisterEvent("VARIABLES_LOADED")
+initFrame:SetScript("OnEvent", function()
+ L:Initialize()
+ MMM_GUI.InitializeWorldMapControls()
+ this:UnregisterEvent("VARIABLES_LOADED")
+end)
+
_G.ModernMapMarkers_GUI = MMM_GUI
\ No newline at end of file
diff --git a/pfDB/MMM_pfDB.xml b/pfDB/MMM_pfDB.xml
new file mode 100644
index 0000000..6ffbd02
--- /dev/null
+++ b/pfDB/MMM_pfDB.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pfDB/MMM_pfDB_fixes.xml b/pfDB/MMM_pfDB_fixes.xml
new file mode 100644
index 0000000..27138fc
--- /dev/null
+++ b/pfDB/MMM_pfDB_fixes.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/pfDB/deDE/units_filtered.lua b/pfDB/deDE/units_filtered.lua
new file mode 100644
index 0000000..d74eb81
--- /dev/null
+++ b/pfDB/deDE/units_filtered.lua
@@ -0,0 +1,20 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "deDE" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["units"] = MMM_pfDB["units"] or {}
+MMM_pfDB["units"]["deDE"] = {
+ [6109] = "Azuregos",
+ [12397] = "Lord Kazzak",
+ [14887] = "Ysondre",
+ [14888] = "Lethon",
+ [14889] = "Smariss",
+ [14890] = "Taerar",
+ [16184] = "Nerubischer Aufseher",
+ [59963] = "Cla'ckora",
+ [62059] = "Vater Lycan",
+ [80935] = "Ostarius",
+ [80936] = "Dunkler Plünderer von Karazhan",
+ [92213] = "Concavius",
+}
diff --git a/pfDB/deDE/zones_filtered.lua b/pfDB/deDE/zones_filtered.lua
new file mode 100644
index 0000000..c2ec036
--- /dev/null
+++ b/pfDB/deDE/zones_filtered.lua
@@ -0,0 +1,96 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "deDE" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["zones"] = MMM_pfDB["zones"] or {}
+MMM_pfDB["zones"]["deDE"] = {
+ [1] = "Dun Morogh",
+ [3] = "Ödland",
+ [4] = "Verwüstete Lande",
+ [8] = "Sümpfe des Elends",
+ [10] = "Dämmerwald",
+ [11] = "Sumpfland",
+ [12] = "Wald von Elwynn",
+ [14] = "Durotar",
+ [15] = "Düstermarschen",
+ [16] = "Azshara",
+ [17] = "Brachland",
+ [19] = "Zul'Gurub",
+ [28] = "Westliche Pestländer",
+ [33] = "Schlingendorntal",
+ [35] = "Beutebucht",
+ [40] = "Westfall",
+ [41] = "Gebirgspass der Totenwinde",
+ [46] = "Brennende Steppe",
+ [47] = "Hinterland",
+ [51] = "Sengende Schlucht",
+ [85] = "Tirisfal",
+ [117] = "Basislager Grom'gol",
+ [130] = "Silberwald",
+ [133] = "Gnomeregan",
+ [139] = "Östliche Pestländer",
+ [141] = "Teldrassil",
+ [148] = "Dunkelküste",
+ [150] = "Hafen von Menethil",
+ [209] = "Burg Schattenfang",
+ [331] = "Ashenvale",
+ [340] = "Kargath",
+ [357] = "Feralas",
+ [392] = "Ratschet",
+ [400] = "Tausend Nadeln",
+ [405] = "Desolace",
+ [440] = "Tanaris",
+ [442] = "Auberdine",
+ [491] = "Kral der Klingenhauer",
+ [513] = "Die Insel Theramore",
+ [616] = "Hyjal",
+ [702] = "Rut'theran",
+ [717] = "Das Verlies",
+ [718] = "Höhlen des Wehklagens",
+ [719] = "Tiefschwarze Grotte",
+ [722] = "Hügel der Klingenhauer",
+ [796] = "Das Scharlachrote Kloster",
+ [978] = "Zul'Farrak",
+ [1108] = "Die Vergessene Küste",
+ [1120] = "Insel Sardor",
+ [1216] = "Holzschlundfeste",
+ [1337] = "Uldaman",
+ [1377] = "Silithus",
+ [1417] = "Versunkener Tempel",
+ [1497] = "Undercity",
+ [1519] = "Stormwind",
+ [1537] = "Ironforge",
+ [1637] = "Orgrimmar",
+ [1638] = "Thunder Bluff",
+ [2040] = "Alah'Thalas",
+ [2100] = "Maraudon",
+ [2159] = "Onyxias Hort",
+ [2279] = "Stratholme",
+ [2366] = "Der Schwarze Morast",
+ [2408] = "Schattenflucht",
+ [2437] = "Der Flammenschlund",
+ [2577] = "Düsterbruch",
+ [2677] = "Pechschwingenhort",
+ [2717] = "Geschmolzener Kern",
+ [3317] = "Dorf der Bruchhauer",
+ [3428] = "Ahn'Qiraj",
+ [3429] = "Ruinen von Ahn'Qiraj",
+ [3456] = "Naxxramas",
+ [3457] = "Turm von Karazhan",
+ [4021] = "Funkelwasserhafen ",
+ [5077] = "Mondsichelhain",
+ [5086] = "Karazhan Krypta",
+ [5087] = "Gewölbe von Sturmwind",
+ [5097] = "Smaragdsanktum",
+ [5098] = "Hassschmieden Steinbruch",
+ [5138] = "Die Todesminen",
+ [5139] = "Schwarzfelsspitze",
+ [5140] = "Schwarzfelstiefen",
+ [5142] = "Scholomance",
+ [5179] = "Gilneas",
+ [5180] = "Stadt von Gilneas",
+ [5561] = "Balor",
+ [5600] = "Drachenmal Zuflucht",
+ [5628] = "Ruine von Sturmschmied",
+}
diff --git a/pfDB/deDE/zones_filtered_fixes.lua b/pfDB/deDE/zones_filtered_fixes.lua
new file mode 100644
index 0000000..ff36b91
--- /dev/null
+++ b/pfDB/deDE/zones_filtered_fixes.lua
@@ -0,0 +1,7 @@
+if GetLocale() ~= "deDE" then return end
+
+-- modifications
+MMM_pfDB["zones"]["deDE"][15] = "Marschen von Dustwallow"
+
+-- additions
+MMM_pfDB["zones"]["deDE"][-5] = "Untere Hallen von Karazhan"
diff --git a/pfDB/enUS/units_filtered.lua b/pfDB/enUS/units_filtered.lua
new file mode 100644
index 0000000..87f626f
--- /dev/null
+++ b/pfDB/enUS/units_filtered.lua
@@ -0,0 +1,18 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["units"] = MMM_pfDB["units"] or {}
+MMM_pfDB["units"]["enUS"] = {
+ [6109] = "Azuregos",
+ [12397] = "Lord Kazzak",
+ [14887] = "Ysondre",
+ [14888] = "Lethon",
+ [14889] = "Emeriss",
+ [14890] = "Taerar",
+ [16184] = "Nerubian Overseer",
+ [59963] = "Cla'ckora",
+ [62059] = "Father Lycan",
+ [80935] = "Ostarius",
+ [80936] = "Dark Reaver of Karazhan",
+ [92213] = "Concavius",
+}
diff --git a/pfDB/enUS/zones_filtered.lua b/pfDB/enUS/zones_filtered.lua
new file mode 100644
index 0000000..345dd64
--- /dev/null
+++ b/pfDB/enUS/zones_filtered.lua
@@ -0,0 +1,94 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["zones"] = MMM_pfDB["zones"] or {}
+MMM_pfDB["zones"]["enUS"] = {
+ [1] = "Dun Morogh",
+ [3] = "Badlands",
+ [4] = "Blasted Lands",
+ [8] = "Swamp of Sorrows",
+ [10] = "Duskwood",
+ [11] = "Wetlands",
+ [12] = "Elwynn Forest",
+ [14] = "Durotar",
+ [15] = "Dustwallow Marsh",
+ [16] = "Azshara",
+ [17] = "The Barrens",
+ [19] = "Zul'Gurub",
+ [28] = "Western Plaguelands",
+ [33] = "Stranglethorn Vale",
+ [35] = "Booty Bay",
+ [40] = "Westfall",
+ [41] = "Deadwind Pass",
+ [46] = "Burning Steppes",
+ [47] = "The Hinterlands",
+ [51] = "Searing Gorge",
+ [85] = "Tirisfal Glades",
+ [117] = "Grom'gol Base Camp",
+ [130] = "Silverpine Forest",
+ [133] = "Gnomeregan",
+ [139] = "Eastern Plaguelands",
+ [141] = "Teldrassil",
+ [148] = "Darkshore",
+ [150] = "Menethil Harbor",
+ [209] = "Shadowfang Keep",
+ [331] = "Ashenvale",
+ [340] = "Kargath",
+ [357] = "Feralas",
+ [392] = "Ratchet",
+ [400] = "Thousand Needles",
+ [405] = "Desolace",
+ [440] = "Tanaris",
+ [442] = "Auberdine",
+ [491] = "Razorfen Kraul",
+ [513] = "Theramore Isle",
+ [616] = "Hyjal",
+ [702] = "Rut'theran Village",
+ [717] = "The Stockade",
+ [718] = "Wailing Caverns",
+ [719] = "Blackfathom Deeps",
+ [722] = "Razorfen Downs",
+ [796] = "Scarlet Monastery",
+ [978] = "Zul'Farrak",
+ [1108] = "The Forgotten Coast",
+ [1120] = "Sardor Isle",
+ [1216] = "Timbermaw Hold",
+ [1337] = "Uldaman",
+ [1377] = "Silithus",
+ [1417] = "Sunken Temple",
+ [1497] = "Undercity",
+ [1519] = "Stormwind City",
+ [1537] = "Ironforge",
+ [1637] = "Orgrimmar",
+ [1638] = "Thunder Bluff",
+ [2040] = "Alah'Thalas",
+ [2100] = "Maraudon",
+ [2159] = "Onyxia's Lair",
+ [2279] = "Stratholme",
+ [2366] = "The Black Morass",
+ [2408] = "Shadowprey Village",
+ [2437] = "Ragefire Chasm",
+ [2577] = "Dire Maul",
+ [2677] = "Blackwing Lair",
+ [2717] = "Molten Core",
+ [3317] = "Revantusk Village",
+ [3428] = "Ahn'Qiraj",
+ [3429] = "Ruins of Ahn'Qiraj",
+ [3456] = "Naxxramas",
+ [3457] = "Tower of Karazhan",
+ [4021] = "Sparkwater Port",
+ [5077] = "Crescent Grove",
+ [5086] = "Karazhan Crypt",
+ [5087] = "Stormwind Vault",
+ [5097] = "Emerald Sanctum",
+ [5098] = "Hateforge Quarry",
+ [5138] = "The Deadmines",
+ [5139] = "Blackrock Spire",
+ [5140] = "Blackrock Depths",
+ [5142] = "Scholomance",
+ [5179] = "Gilneas",
+ [5180] = "Gilneas City",
+ [5561] = "Balor",
+ [5600] = "Dragonmaw Retreat",
+ [5628] = "Stormwrought Ruins",
+}
diff --git a/pfDB/enUS/zones_filtered_fixes.lua b/pfDB/enUS/zones_filtered_fixes.lua
new file mode 100644
index 0000000..bf2f028
--- /dev/null
+++ b/pfDB/enUS/zones_filtered_fixes.lua
@@ -0,0 +1,8 @@
+-- modifications
+
+-- additions
+MMM_pfDB["zones"]["enUS"][-1] = "Moonwhisper Coast"
+MMM_pfDB["zones"]["enUS"][-2] = "Moonhoof Village"
+MMM_pfDB["zones"]["enUS"][-3] = "Windhorn Canyon"
+MMM_pfDB["zones"]["enUS"][-4] = "Frostmane Hollow"
+MMM_pfDB["zones"]["enUS"][-5] = "Lower Karazhan Halls"
diff --git a/pfDB/esES/units_filtered.lua b/pfDB/esES/units_filtered.lua
new file mode 100644
index 0000000..41b711a
--- /dev/null
+++ b/pfDB/esES/units_filtered.lua
@@ -0,0 +1,20 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "esES" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["units"] = MMM_pfDB["units"] or {}
+MMM_pfDB["units"]["esES"] = {
+ [6109] = "Azuregos",
+ [12397] = "Lord Kazzak",
+ [14887] = "Ysondre",
+ [14888] = "Lethon",
+ [14889] = "Emeriss",
+ [14890] = "Taerar",
+ [16184] = "Sobrestante Nerubiano",
+ [59963] = "Cla'ckora",
+ [62059] = "Padre Lycan",
+ [80935] = "Ostarius",
+ [80936] = "Segador Oscuro de Karazhan",
+ [92213] = "Concavius",
+}
diff --git a/pfDB/esES/zones_filtered.lua b/pfDB/esES/zones_filtered.lua
new file mode 100644
index 0000000..d34330f
--- /dev/null
+++ b/pfDB/esES/zones_filtered.lua
@@ -0,0 +1,96 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "esES" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["zones"] = MMM_pfDB["zones"] or {}
+MMM_pfDB["zones"]["esES"] = {
+ [1] = "Dun Morogh",
+ [3] = "Tierras Inhóspitas",
+ [4] = "Tierras Devastadas",
+ [8] = "Pantano de las Penas",
+ [10] = "Bosque del Ocaso",
+ [11] = "Los Humedales",
+ [12] = "Bosque de Elwynn",
+ [14] = "Durotar",
+ [15] = "Marjal Revolcafango",
+ [16] = "Azshara",
+ [17] = "Los Baldíos",
+ [19] = "Zul'Gurub",
+ [28] = "Tierras de la Peste del Oeste",
+ [33] = "Vega de Tuercespina",
+ [35] = "Bahía del Botín",
+ [40] = "Páramos del Poniente",
+ [41] = "Paso de la Muerte",
+ [46] = "Las Estepas Ardientes",
+ [47] = "Tierras del Interior",
+ [51] = "La Garganta de Fuego",
+ [85] = "Claros de Tirisfal",
+ [117] = "Campamento Base Grom'gol",
+ [130] = "Bosque de Argénteos",
+ [133] = "Gnomeregan",
+ [139] = "Tierras de la Peste del Este",
+ [141] = "Teldrassil",
+ [148] = "Costa Oscura",
+ [150] = "Puerto Menethil",
+ [209] = "Fortaleza Colmillo Oscuro",
+ [331] = "Vallefresno",
+ [340] = "Kargath",
+ [357] = "Feralas",
+ [392] = "Ratchett",
+ [400] = "Las Mil Agujas",
+ [405] = "Desolace",
+ [440] = "Tanaris",
+ [442] = "Auberdine",
+ [491] = "Madriguera de Rajacieno",
+ [513] = "Isla Theramore",
+ [616] = "Hyjal",
+ [702] = "Aldea Rut'theran",
+ [717] = "La Mazmorras",
+ [718] = "Cuevas de los Lamentos",
+ [719] = "Cavernas de Brazanegra",
+ [722] = "Zahúrda Rajacieno",
+ [796] = "Monasterio Escarlata",
+ [978] = "Zul'Farrak",
+ [1108] = "La Costa Olvidada",
+ [1120] = "Isla Sardor",
+ [1216] = "Bastión de los Timbermaw",
+ [1337] = "Uldaman",
+ [1377] = "Silithus",
+ [1417] = "Templo Sumergido",
+ [1497] = "Entrañas",
+ [1519] = "Ciudad de Ventormenta",
+ [1537] = "Forjaz",
+ [1637] = "Orgrimmar",
+ [1638] = "Cima del Trueno",
+ [2040] = "Alah'Thalas",
+ [2100] = "Maraudon",
+ [2159] = "Guarida de Onyxia",
+ [2279] = "Stratholme",
+ [2366] = "La Ciénaga Negra",
+ [2408] = "Poblado Presacaz",
+ [2437] = "Sima Ígnea",
+ [2577] = "La Masacre",
+ [2677] = "Guarida Alanegra",
+ [2717] = "Núcleo de Magma",
+ [3317] = "Aldea Revantusk",
+ [3428] = "Ahn'Qiraj",
+ [3429] = "Ruinas de Ahn'Qiraj",
+ [3456] = "Naxxramas",
+ [3457] = "Torre de Karazhan",
+ [4021] = "Puerto Aguachispa",
+ [5077] = "Claro de la Media Luna",
+ [5086] = "Cripta de Karazhan",
+ [5087] = "Bóveda de Ventormenta",
+ [5097] = "Sanctum Esmeralda",
+ [5098] = "Cantera Hateforge",
+ [5138] = "Las Minas de la Muerte",
+ [5139] = "Cima Roca Negra",
+ [5140] = "Profundidades Roca Negra",
+ [5142] = "Scholomance",
+ [5179] = "Gilneas",
+ [5180] = "Ciudad Gilneas",
+ [5561] = "Balor",
+ [5600] = "Refugio Faucedrago",
+ [5628] = "Ruinas Forjatormenta",
+}
diff --git a/pfDB/esES/zones_filtered_fixes.lua b/pfDB/esES/zones_filtered_fixes.lua
new file mode 100644
index 0000000..6a2e4aa
--- /dev/null
+++ b/pfDB/esES/zones_filtered_fixes.lua
@@ -0,0 +1,11 @@
+if GetLocale() ~= "esES" then return end
+
+-- modifications
+MMM_pfDB["zones"]["esES"][1216] = "Bastión Fauces de Madera"
+
+-- additions
+MMM_pfDB["zones"]["esES"][-1] = "Costa Susurrolunar"
+MMM_pfDB["zones"]["esES"][-2] = "Aldea Pezuña Lunar"
+MMM_pfDB["zones"]["esES"][-3] = "Cañón Cornaviento"
+MMM_pfDB["zones"]["esES"][-4] = "Hondonada Peloescarcha"
+MMM_pfDB["zones"]["esES"][-5] = "Salas Inferiores de Karazhan"
diff --git a/pfDB/pfDB.xml b/pfDB/pfDB.xml
new file mode 100644
index 0000000..2f56ed2
--- /dev/null
+++ b/pfDB/pfDB.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/pfDB/pfDBHelper.lua b/pfDB/pfDBHelper.lua
new file mode 100644
index 0000000..93d84f7
--- /dev/null
+++ b/pfDB/pfDBHelper.lua
@@ -0,0 +1,96 @@
+MMM_PfDBHelper = {}
+
+local by_name = { zones = {}, units = {} } -- by_name[kind][locale] = { ["Localized Name"] = id, ... }
+local initialized = { zones = {}, units = {} } -- initialized[kind][locale] = true
+
+local idCache = { zones = {}, units = {} } -- idCache[kind][locale][name] = id or false
+local nameCache = { zones = {}, units = {} } -- nameCache[kind][locale][id] = name or false
+
+local idError = { zones = {}, units = {} } -- idError[kind][key] = true
+local nameError = { zones = {}, units = {} } -- nameError[kind][key] = true
+
+function MMM_PfDBHelper:Print(msg)
+ DEFAULT_CHAT_FRAME:AddMessage("MMM_PfDBHelper: " .. tostring(msg))
+end
+
+local function ensureLookupTable(kind, locale)
+ if not kind or not locale then return end
+ if initialized[kind][locale] then return end
+
+ by_name[kind][locale] = by_name[kind][locale] or {}
+
+ local db = MMM_pfDB and MMM_pfDB[kind]
+ -- vanilla locale (e.g. "enUS" or "deDE")
+ if db[locale] then
+ by_name[kind][locale] = by_name[kind][locale] or {}
+ for id, name in pairs(db[locale]) do
+ if type(name) == "string" then
+ by_name[kind][locale][name] = id
+ end
+ end
+ end
+
+ initialized[kind][locale] = true
+end
+
+local function getId(kind, text, locale)
+ if not kind or not text or not locale then return nil end
+ ensureLookupTable(kind, locale)
+
+ idCache[kind][locale] = idCache[kind][locale] or {}
+ if idCache[kind][locale][text] then
+ return idCache[kind][locale][text] -- we have a cached value. return it
+ end
+
+ local found = nil
+ if by_name[kind][locale] and by_name[kind][locale][text] then
+ found = by_name[kind][locale][text]
+ end
+
+ if not found and idError[kind][text] == nil then
+ idError[kind][text] = true
+ --MMM_PfDBHelper:Print("ERROR getId - kind = " .. tostring(kind) .. ", text = '" .. tostring(text) .. "'")
+ end
+
+ idCache[kind][locale][text] = found or false
+ return found
+end
+
+local function getName(kind, id, locale)
+ if not kind or not id or not locale then return nil end
+
+ nameCache[kind][locale] = nameCache[kind][locale] or {}
+ if nameCache[kind][locale][id] then
+ return nameCache[kind][locale][id]
+ end
+
+ local found = nil
+ local db = MMM_pfDB and MMM_pfDB[kind]
+ if db[locale] and db[locale][id] then
+ found = db[locale][id]
+ end
+
+ if not found and nameError[kind][id] == nil then
+ nameError[kind][id] = true
+ --MMM_PfDBHelper:Print("ERROR getName - kind = " .. tostring(kind) .. ", id = " .. tostring(id))
+ end
+
+ nameCache[kind][locale][id] = found or false
+ return found
+end
+
+function MMM_PfDBHelper:GetZoneId(zoneText, locale)
+ return getId("zones", zoneText, locale)
+end
+
+function MMM_PfDBHelper:GetUnitId(unitText, locale)
+ return getId("units", unitText, locale)
+end
+
+function MMM_PfDBHelper:GetZoneName(zoneId, locale)
+ return getName("zones", zoneId, locale)
+end
+
+function MMM_PfDBHelper:GetUnitName(unitId, locale)
+ return getName("units", unitId, locale)
+end
\ No newline at end of file
diff --git a/pfDB/ptBR/units_filtered.lua b/pfDB/ptBR/units_filtered.lua
new file mode 100644
index 0000000..70eba7f
--- /dev/null
+++ b/pfDB/ptBR/units_filtered.lua
@@ -0,0 +1,20 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "ptBR" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["units"] = MMM_pfDB["units"] or {}
+MMM_pfDB["units"]["ptBR"] = {
+ [6109] = "Azuregos",
+ [12397] = "Lorde Kazzak",
+ [14887] = "Ysondra",
+ [14888] = "Lethon",
+ [14889] = "Emeriss",
+ [14890] = "Taerar",
+ [16184] = "Feitor Nerubiano",
+ [59963] = "Cla'ckora",
+ [62059] = "Padre Lycan",
+ [80935] = "Ostarius",
+ [80936] = "Aniquilador Sombrio de Karazhan",
+ [92213] = "Concávio",
+}
diff --git a/pfDB/ptBR/zones_filtered.lua b/pfDB/ptBR/zones_filtered.lua
new file mode 100644
index 0000000..6e32482
--- /dev/null
+++ b/pfDB/ptBR/zones_filtered.lua
@@ -0,0 +1,96 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "ptBR" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["zones"] = MMM_pfDB["zones"] or {}
+MMM_pfDB["zones"]["ptBR"] = {
+ [1] = "Dun Morogh",
+ [3] = "Ermos",
+ [4] = "Terras Devastadas",
+ [8] = "Pantano das Tristezas",
+ [10] = "Floresta do Crepúsculo",
+ [11] = "Pântanos",
+ [12] = "Floresta de Elwynn",
+ [14] = "Durotar",
+ [15] = "Pântano Vadeante",
+ [16] = "Azshara",
+ [17] = "Os Barrens",
+ [19] = "Zul'Gurub",
+ [28] = "Plaguelands Ocidentais",
+ [33] = "Vale Stranglethorn",
+ [35] = "Angra do Butim",
+ [40] = "Westfall",
+ [41] = "Desfiladeiro da Morte",
+ [46] = "Estepes Ardentes",
+ [47] = "As Terras Interiores",
+ [51] = "Desfiladeiro Searing",
+ [85] = "Os Bosques de Tirisfal",
+ [117] = "Acampamento Base de Grom'gol",
+ [130] = "Floresta Silverpine",
+ [133] = "Gnomeregan",
+ [139] = "Terras Pestilentas Orientais",
+ [141] = "Teldrassil",
+ [148] = "Costa Negra",
+ [150] = "Puerto Menethil",
+ [209] = "Fortaleza Shadowfang",
+ [331] = "Vale das Cinzas",
+ [340] = "Kargath",
+ [357] = "Feralas",
+ [392] = "Carroça",
+ [400] = "As Mil Agulhas",
+ [405] = "Desolação",
+ [440] = "Tanaris",
+ [442] = "Auberdine",
+ [491] = "Razorfen Kraul",
+ [513] = "A Ilha Theramore",
+ [616] = "Hyjal",
+ [702] = "Aldeia Rut'theran",
+ [717] = "A Prisão",
+ [718] = "Cavernas Uivantes",
+ [719] = "Profundezas Negras",
+ [722] = "Urzal dos Mortos",
+ [796] = "Monastério Scarlet",
+ [978] = "Zul'Farrak",
+ [1108] = "A Costa Esquecida",
+ [1120] = "Ilha Sardor",
+ [1216] = "O Refugio Timbermaw",
+ [1337] = "Uldaman",
+ [1377] = "Silithus",
+ [1417] = "Templo Submerso",
+ [1497] = "Cidade Subterrânea",
+ [1519] = "Cidade Stormwind",
+ [1537] = "Altaforja",
+ [1637] = "Orgrimmar",
+ [1638] = "Thunder Bluff",
+ [2040] = "Alah'Thalas",
+ [2100] = "Maraudon",
+ [2159] = "Caverna de Onyxia",
+ [2279] = "Stratholme",
+ [2366] = "O Morass Negro",
+ [2408] = "Aldeia Shadowprey",
+ [2437] = "Cavernas Ragefire",
+ [2577] = "Gládio Cruel",
+ [2677] = "Covil Asa Negra",
+ [2717] = "Núcleo Derretido",
+ [3317] = "Aldeia Revantusk",
+ [3428] = "Ahn'Qiraj",
+ [3429] = "Ruínas de Ahn'Qiraj",
+ [3456] = "Naxxramas",
+ [3457] = "Karazhan",
+ [4021] = "Porto Sparkwater",
+ [5077] = "Bosque Crescente",
+ [5086] = "Cripta Karazhan",
+ [5087] = "Cofre Stormwind",
+ [5097] = "Santuário Esmeralda",
+ [5098] = "Pedreira Forja do Ódio",
+ [5138] = "As Minas Mortas",
+ [5139] = "Pico Rocha Negra",
+ [5140] = "Abismo Rocha Negra",
+ [5142] = "Scholomance",
+ [5179] = "Gilneas",
+ [5180] = "Cidade de Gilneas",
+ [5561] = "Balor",
+ [5600] = "Refúgio Presa do Dragão",
+ [5628] = "Ruínas da Tempestade",
+}
diff --git a/pfDB/ptBR/zones_filtered_fixes.lua b/pfDB/ptBR/zones_filtered_fixes.lua
new file mode 100644
index 0000000..0c84029
--- /dev/null
+++ b/pfDB/ptBR/zones_filtered_fixes.lua
@@ -0,0 +1,8 @@
+if GetLocale() ~= "ptBR" then return end
+
+-- modifications
+MMM_pfDB["zones"]["ptBR"][2437] = "Sima Ígnea"
+MMM_pfDB["zones"]["ptBR"][491] = "Urzal dos Tuscos"
+
+-- additions
+MMM_pfDB["zones"]["ptBR"][-5] = "Salões Inferiores de Karazhan"
diff --git a/pfDB/zhCN/units_filtered.lua b/pfDB/zhCN/units_filtered.lua
new file mode 100644
index 0000000..a8418f6
--- /dev/null
+++ b/pfDB/zhCN/units_filtered.lua
@@ -0,0 +1,20 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "zhCN" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["units"] = MMM_pfDB["units"] or {}
+MMM_pfDB["units"]["zhCN"] = {
+ [6109] = "艾索雷葛斯",
+ [12397] = "卡扎克",
+ [14887] = "伊森德雷",
+ [14888] = "雷索",
+ [14889] = "艾莫莉丝",
+ [14890] = "泰拉尔",
+ [16184] = "蛛怪监工",
+ [59963] = "克拉科拉",
+ [62059] = "人狼神父",
+ [80935] = "奥兹塔里亚斯",
+ [80936] = "卡拉赞黑暗掠夺者",
+ [92213] = "空卡维斯",
+}
diff --git a/pfDB/zhCN/zones_filtered.lua b/pfDB/zhCN/zones_filtered.lua
new file mode 100644
index 0000000..2ee8a60
--- /dev/null
+++ b/pfDB/zhCN/zones_filtered.lua
@@ -0,0 +1,96 @@
+-- created with https://codeberg.org/hyperhumble/pfDB-Localization-Helper
+
+if GetLocale() ~= "zhCN" then return end
+
+MMM_pfDB = MMM_pfDB or {}
+MMM_pfDB["zones"] = MMM_pfDB["zones"] or {}
+MMM_pfDB["zones"]["zhCN"] = {
+ [1] = "丹莫罗",
+ [3] = "荒芜之地",
+ [4] = "诅咒之地",
+ [8] = "悲伤沼泽",
+ [10] = "暮色森林",
+ [11] = "湿地",
+ [12] = "艾尔文森林",
+ [14] = "杜隆塔尔",
+ [15] = "尘泥沼泽",
+ [16] = "艾萨拉",
+ [17] = "贫瘠之地",
+ [19] = "祖尔格拉布",
+ [28] = "西瘟疫之地",
+ [33] = "荆棘谷",
+ [35] = "藏宝海湾",
+ [40] = "西部荒野",
+ [41] = "逆风小径",
+ [46] = "燃烧平原",
+ [47] = "辛特兰",
+ [51] = "灼热峡谷",
+ [85] = "提瑞斯法林地",
+ [117] = "格罗姆高营地",
+ [130] = "银松森林",
+ [133] = "诺莫瑞根",
+ [139] = "东瘟疫之地",
+ [141] = "泰达希尔",
+ [148] = "黑海岸",
+ [150] = "米奈希尔港",
+ [209] = "影牙城堡",
+ [331] = "灰谷",
+ [340] = "卡加斯",
+ [357] = "菲拉斯",
+ [392] = "棘齿城",
+ [400] = "千针石林",
+ [405] = "凄凉之地",
+ [440] = "塔纳利斯",
+ [442] = "奥伯丁",
+ [491] = "剃刀沼泽",
+ [513] = "塞拉摩岛",
+ [616] = "海加尔山",
+ [702] = "鲁瑟兰村",
+ [717] = "监狱",
+ [718] = "哀嚎洞穴",
+ [719] = "黑暗深渊",
+ [722] = "剃刀高地",
+ [796] = "血色修道院",
+ [978] = "祖尔法拉克",
+ [1108] = "被遗忘的海岸",
+ [1120] = "萨尔多岛",
+ [1216] = "木喉要塞",
+ [1337] = "奥达曼",
+ [1377] = "希利苏斯",
+ [1417] = "沉没的神庙",
+ [1497] = "幽暗城",
+ [1519] = "暴风城",
+ [1537] = "铁炉堡",
+ [1637] = "奥格瑞玛",
+ [1638] = "雷霆崖",
+ [2040] = "阿尔萨拉斯",
+ [2100] = "玛拉顿",
+ [2159] = "奥妮克希亚的巢穴",
+ [2279] = "斯坦索姆",
+ [2366] = "黑色沼泽",
+ [2408] = "葬影村",
+ [2437] = "怒焰裂谷",
+ [2577] = "厄运之槌",
+ [2677] = "黑翼之巢",
+ [2717] = "熔火之心",
+ [3317] = "恶齿村",
+ [3428] = "安其拉",
+ [3429] = "安其拉废墟",
+ [3456] = "纳克萨玛斯",
+ [3457] = "卡拉赞",
+ [4021] = "怒水港",
+ [5077] = "新月林地",
+ [5086] = "卡拉赞墓穴",
+ [5087] = "暴风城地牢",
+ [5097] = "翡翠圣殿",
+ [5098] = "仇恨熔炉采石场",
+ [5138] = "死亡矿井",
+ [5139] = "黑石塔",
+ [5140] = "黑石深渊",
+ [5142] = "通灵学院",
+ [5179] = "吉尔尼斯",
+ [5180] = "吉尔尼斯城",
+ [5561] = "巴洛",
+ [5600] = "龙喉居所",
+ [5628] = "风暴废墟",
+}
diff --git a/pfDB/zhCN/zones_filtered_fixes.lua b/pfDB/zhCN/zones_filtered_fixes.lua
new file mode 100644
index 0000000..c894039
--- /dev/null
+++ b/pfDB/zhCN/zones_filtered_fixes.lua
@@ -0,0 +1,9 @@
+if GetLocale() ~= "zhCN" then return end
+-- modifications
+
+-- additions
+MMM_pfDB["zones"]["zhCN"][-1] = "月语海岸"
+MMM_pfDB["zones"]["zhCN"][-2] = "月蹄村"
+MMM_pfDB["zones"]["zhCN"][-3] = "风角峡谷"
+MMM_pfDB["zones"]["zhCN"][-4] = "霜鬃谷"
+MMM_pfDB["zones"]["zhCN"][-5] = "卡拉赞下层大厅"