v9.7.2: Ebonhold Database integration and core logic refinements

This commit is contained in:
Xurkon
2026-02-14 21:13:44 -06:00
parent 0a2cc15641
commit 6ef85d4e2d
573 changed files with 1751730 additions and 2 deletions
@@ -0,0 +1,10 @@
---@type table
local EbonholdDB = QuestieLoader:CreateModule("EbonholdDB")
EbonholdDB.itemData = EbonholdDB.itemData or {
-- Ebonhold Custom Items would go here
-- [ItemID] = {Name, ...}
}
EbonholdItemDB = EbonholdItemDB or {}
EbonholdItemDB.itemData = EbonholdDB.itemData
@@ -0,0 +1,26 @@
---@type table
local EbonholdDB = QuestieLoader:CreateModule("EbonholdDB")
EbonholdDB.npcData = EbonholdDB.npcData or {
-- Quest 50030 Corrections: Full Spawn Data for Borean Tundra Elementals
-- Overrides existing sparse data in WotLK DB
[24601] = {'Steam Rager',9291,9291,70,71,0,{[3537] = {{67,25.4},{67.4,22},{67.4,26.4},{67.4,26.8},{68,26},{68,27},{68.2,23},{68.2,23.6},{68.4,22.4},{68.4,25},{68.4,28.2},{68.8,24.8},{68.8,31.2},{69.2,23.8},{69.2,26.8},{69.2,27.8},{69.4,22.2},{69.4,22.8},{69.4,26.4},{69.4,29},{69.4,30},{69.6,22.4},{69.6,24.4},{69.6,28.2},{69.8,22.8},{69.8,32},{70,29.4},{70.2,36.6},{70.4,25.4},{70.4,25.6},{70.4,26.6},{70.4,29.8},{70.4,30.8},{70.4,36},{70.6,29.4},{70.8,36.4},{71,22.2},{71,22.6},{71,24.4},{71,25.8},{71,28.2},{71,36.6},{71.2,38.4},{71.4,25.4},{71.4,27.2},{71.4,30.4},{71.4,31.2},{71.4,32.4},{71.4,33.2},{71.4,34},{71.4,35},{71.4,39.4},{71.4,39.6},{71.6,22},{71.6,29.6},{71.6,31},{71.6,39.4},{71.8,25.8},{71.8,27.2},{71.8,33.6},{71.8,42.2},{72,24.6},{72,28.6},{72,32.6},{72,37.6},{72.2,24.2},{72.2,28.4},{72.2,32.4},{72.2,35.2},{72.2,35.8},{72.2,36.6},{72.4,21},{72.4,23.4},{72.4,40.4},{72.4,41.2},{72.6,24.4},{72.6,27.2},{72.6,30.2},{72.6,31.4},{72.6,32.4},{72.6,33.4},{72.6,33.6},{72.6,35.4},{72.6,36.4},{72.6,36.6},{72.8,25.4},{72.8,41.6},{73,23.4},{73,26.2},{73,28.2},{73.2,38.2},{73.2,39.4},{73.2,40.2},{73.2,40.6},{73.4,28.6},{73.6,24.4},{73.6,26},{73.6,27.2},{73.6,40.4},{73.6,40.6},{73.8,25.2},{73.8,28},{73.8,29.2},{73.8,38.6},{74.2,30.2},{74.2,30.6},{74.2,37.4},{74.2,37.8},{74.4,31.6},{74.4,33.4},{74.4,34.2},{74.4,35},{74.4,35.8},{74.6,29.4},{74.6,30},{74.6,31.8},{74.6,36.8},{74.6,38.4},{74.6,39.4},{74.6,39.8},{74.8,30.8},{74.8,33.6},{74.8,34.6},{74.8,35.6},{75.4,33.4},{75.6,29},{75.6,33.4},{75.6,33.8},{75.6,34.6},{75.6,35.8},{75.8,38.2},{76.4,32.4},{76.4,37.4},{76.6,32},{76.8,36},{77,37.8},{77.4,37},{77.6,37}}},nil,3537,nil,nil,91,nil,"",0},
[25226] = {'Scalder',9291,9291,71,71,0,{[3537] = {{42.4,15.4},{42.4,15.6},{42.6,15.4},{42.6,16.4},{42.6,16.6},{42.8,17.6}}},nil,3537,nil,nil,14,nil,"",0},
[25376] = {'Imperean',9291,9291,72,72,0,{[3537] = {{46.4,9.4},{46.6,9.4}}},nil,3537,nil,nil,190,nil,"",0},
[25415] = {'Enraged Tempest',9291,9291,70,71,0,{[3537] = {{42,8},{42.2,9.4},{42.2,13.4},{42.4,10.2},{42.4,10.6},{42.6,10.4},{42.8,9},{42.8,10.6},{43,7.4},{43.2,8},{43.4,13.8},{43.6,7.8},{43.6,9},{43.6,13.4},{43.8,7.4},{43.8,10.6},{44,13.6},{44.2,11.6},{44.4,10},{44.6,7.4},{44.6,7.6},{44.6,11.6},{44.8,10.8},{45,9.2},{45.4,6.4},{45.4,9.8},{45.6,6.4},{45.6,9.8},{45.8,9.4},{45.8,12.4},{46,7.4},{46,7.6},{46,10.6},{46,13.2},{46.6,7.8},{46.6,9.2},{46.6,10.4},{46.6,11.2},{47.2,11.6},{48.2,7.4}}},nil,3537,nil,nil,834,nil,"",0},
[25416] = {'Simmer',9291,9291,72,72,0,{[3537] = {{50.4,15.2},{50.8,15.2}}},nil,3537,nil,nil,1983,nil,"",0},
[25417] = {'Raging Boiler',9291,9291,69,70,0,{[3537] = {{46,14.6},{46.2,13.2},{46.2,13.8},{46.8,14.4},{47,9.8},{47,12.4},{47,12.8},{47.2,14.6},{47.2,16.8},{47.4,11.2},{47.4,15.6},{47.8,12.4},{47.8,15.4},{48,11.4},{48,15.8},{48,21.2},{48.2,12.8},{48.2,16.6},{48.2,19.8},{48.4,14.2},{48.4,18.2},{48.4,19.4},{48.6,12.4},{48.6,16.2},{48.6,19.4},{48.8,13.4},{48.8,13.6},{49,16.8},{49.2,17.6},{49.4,10.2},{49.4,10.6},{49.4,15},{49.6,10.8},{49.6,13.4},{49.6,14.4},{49.6,17},{50.2,15},{50.4,15.6},{50.4,17.8},{50.6,15.6},{50.8,15.2}}},nil,3537,nil,nil,1983,nil,"",0},
[25418] = {'Churn',9291,9291,71,71,0,{[3537] = {{45.4,12.4},{45.8,12.2},{45.8,12.8},{45.8,13.6}}},nil,3537,nil,nil,1984,nil,"",0},
[25419] = {'Boiling Spirit',9291,9291,69,70,0,{[3537] = {{43.4,13.8},{44.4,9},{44.6,9.8},{44.6,15.6},{45.2,12.4},{45.2,12.6},{45.2,16.8},{45.4,11.4},{45.4,15.4},{45.8,9.8},{45.8,12.2},{45.8,13},{45.8,13.6},{46,14.8},{46,16.8},{46,19},{46.2,11.4},{46.2,15.6},{46.4,9.4},{46.6,9.6},{46.8,9.2},{46.8,15},{46.8,16.8},{47,11.2},{47,12.6},{47.2,12.4},{47.2,16},{47.2,18.4},{47.4,14.2},{47.6,11.4},{47.6,12.4},{47.6,15.4},{47.8,16.8},{47.8,17.8},{48,15.8},{48.2,13.4},{48.2,14},{48.6,16},{48.8,12.2},{48.8,13.4},{49,16.8},{49.2,14.8},{49.4,10.6},{49.4,13.8},{50,10.8},{50.2,15.2},{51,15.2}}},nil,3537,nil,nil,1984,nil,"",0},
[25514] = {'Rocknar',9291,9291,71,71,0,{[3537] = {{81.4,42.4},{81.4,42.6},{81.6,42.6}}},nil,3537,nil,nil,14,nil,"",0},
[25707] = {'Magic-bound Ancient',9291,9291,71,72,0,{[3537] = {{22.4,23.4},{22.4,24},{22.8,24},{23,25.4},{23.4,30.2},{23.4,33.6},{23.6,27.8},{23.8,25.8},{23.8,34.2},{24,29.4},{24,29.8},{24.2,21.4},{24.4,22.4},{24.4,22.6},{24.4,23.6},{24.4,30.8},{24.4,31.6},{24.4,35},{24.6,22.4},{24.6,23},{24.6,23.6},{24.6,28.8},{24.6,30.4},{25,31.4},{25.2,20.4},{25.2,25},{25.2,31.6},{25.4,28.4},{25.4,34.4},{25.4,34.8},{25.4,35.6},{25.4,36.6},{25.6,34.6},{25.6,37},{25.8,21.4},{25.8,34.2},{25.8,35.6},{26,21.8},{26,23.4},{26,29.4},{26,31.4},{26,31.8},{26.4,32.6},{26.6,35},{27,20.4},{27,32.6},{27,33.8},{27.2,31},{27.4,20.6},{27.4,21.8},{27.4,30.4},{27.4,32},{27.6,33.6},{27.8,32.2},{27.8,32.6},{28.2,28.8},{28.2,29.8},{28.2,31.4},{28.2,35.8},{28.4,20.4},{28.4,21.4},{28.4,21.8},{28.4,28},{28.6,32.6},{28.6,34.2},{28.8,21.4},{28.8,21.6},{28.8,28.4},{28.8,32.4},{29,20.4},{29.2,27.4},{29.4,29},{29.4,30},{29.4,30.6},{29.6,20.2},{29.6,20.6},{29.6,22.8},{29.6,30.4},{29.8,22},{29.8,24.4},{29.8,25.8},{29.8,30.8},{29.8,31.8},{30,29.4},{30,33.2},{30.4,24.6},{30.6,29.2},{30.6,29.8},{30.8,31.2},{31,22.4},{31,26.6},{31.2,23.4},{31.2,25.6},{31.4,20.2},{31.4,21.2},{31.4,24.4},{31.4,24.8},{31.4,27.8},{31.6,20.4},{31.6,22.4},{31.6,24.2},{31.6,24.8},{31.6,25.6},{31.6,26.6},{31.6,30.8},{31.8,20.6},{31.8,23.4},{32,30},{32.4,27.6},{32.4,28.6},{32.4,33.8},{32.6,27.8},{32.6,30.6},{32.8,23.4},{33.2,24.6},{33.2,34.4},{33.4,24.2},{33.4,25.6},{33.4,29.4},{33.6,23.4},{33.6,24.4},{33.6,24.6},{33.8,25.6},{33.8,26.8},{34.4,28.4},{34.4,29.2},{34.4,29.8},{34.6,25.6},{34.8,29},{35,27},{35,29.6},{35.2,28.4},{35.4,30.6},{35.6,28},{35.6,29},{35.6,29.6},{35.6,30.6}}},nil,3537,nil,nil,16,nil,"",0},
[25709] = {'Glacial Ancient',9291,9291,71,72,0,{[3537] = {{20.4,27},{20.4,28.2},{20.8,28.2},{21,25.8},{21,29.2},{21.2,29.6},{21.4,27},{21.4,31.4},{21.4,31.6},{21.4,32.6},{21.6,29.2},{21.6,30.4},{21.6,31},{21.6,32},{21.8,27.6},{22.2,33.2},{22.4,26.4},{22.4,27.2},{22.4,33.6},{22.4,34.6},{22.6,33.4},{22.8,27.2},{22.8,28.8},{23,32.4},{23,33.6},{23.2,27.8},{23.2,34.6},{23.4,25.2},{23.4,26},{23.4,35.6},{23.4,36.6},{23.6,25},{23.6,28.6},{23.6,33.4},{23.6,34.8},{23.6,35.8},{23.8,26.4},{23.8,27.8},{23.8,32.4},{23.8,36.6},{24,26.6},{24.2,34.2},{24.6,26},{24.6,27.8},{24.6,36.8},{24.8,27.2},{25,34.2},{25.2,19.4},{25.4,33.4},{25.4,34.6},{25.4,35.6},{25.6,33.4},{25.6,35},{25.6,35.6},{25.6,36.6},{25.8,34.2},{26.4,17.4},{26.4,18.2},{26.4,19.2},{26.4,20},{26.4,20.6},{26.6,18.4},{26.6,18.6},{26.6,19.6},{26.6,20.6},{27.2,33.8},{27.8,18},{29.4,30.2},{29.4,32.2},{29.4,32.8},{30.4,23.4},{30.4,23.8},{30.4,24.8},{30.4,29},{30.4,30.4},{30.4,31.4},{30.4,32.4},{30.4,32.6},{30.6,29.2},{30.6,30.4},{30.6,30.6},{30.6,32.2},{30.6,32.6},{31,28.4},{31.2,25.6},{31.4,22},{31.4,22.8},{31.4,23.6},{31.4,24.8},{31.6,22.4},{31.6,23.6},{31.6,24.6},{31.6,25.8},{31.6,31},{31.8,23.2},{32.6,23},{32.6,23.8},{32.6,26.4},{34.4,25.4},{34.4,26.4},{34.4,26.8},{34.4,28.2},{34.4,28.6},{34.8,26.4},{34.8,26.6},{35,28.6},{35.2,27.8},{35.4,25.4},{35.6,26.4},{35.6,27.2},{35.6,27.6},{35.6,28.6}}},nil,3537,nil,nil,7,nil,"",0},
[25715] = {'Frozen Elemental',9291,9291,70,71,0,{[65]={{11.28,62.69},{11.05,61.25},{9.43,61.9},{10.2,60.22},{10.21,62.04},{9.29,60.28},{8.68,62.04},{9.07,59.02},{7.78,62.99},{8.79,54.22},{9.63,56.48},{11.56,54.92},{11.53,57.32},{11.05,56.14},{8.94,55.86},{10.33,55.94},{10.23,57.3},{7.8,61.25},{13.09,60.42},{8.24,60.06},{12.21,58.61},{11.52,59.08},{11.71,53.34},{7.88,58.82},{8.55,58.35},{6.81,62.17},{11.85,60.86}},[3537] = {{82.8,48.8},{83.2,45.6},{83.4,44.4},{83.4,45},{83.4,47.2},{83.4,47.6},{83.6,44.4},{83.6,47.2},{83.8,44.8},{84.2,46.4},{84.2,47.6},{84.6,47.2},{85,46.4},{85,48.8},{85.2,48},{85.4,43.8},{85.4,44.8},{85.6,43.4},{85.8,44.2},{85.8,47.4},{85.8,47.6},{86,45.8},{86.2,45.4},{86.8,47.6},{87.2,43.4},{87.2,43.6},{87.2,45},{87.4,42.4},{87.4,46.4},{87.4,46.6},{87.6,45.4},{87.6,45.8},{87.8,42.8},{88.4,42.4},{88.4,44.2},{88.4,46.8},{88.6,44.2},{88.6,45.8},{88.6,46.8},{88.8,41.4},{88.8,42.2},{88.8,45.4},{89,42.6},{89.8,42.6},{90.2,40.8},{90.2,41.8},{91,43},{91.2,40.4},{91.2,41.6},{91.4,41.4},{91.6,41.2},{91.6,41.6},{92,42.6},{92.4,37.8},{92.4,39.4},{92.4,39.6},{92.6,39.2},{92.6,39.6},{92.8,43.6},{93,36.4},{93,37.4},{93,38},{93.4,35.4}}},nil,3537,nil,nil,14,nil,"",0},
[25742] = {'Alluvius',9291,9291,71,71,0,{[3537] = {{70.2,36.2},{75.4,35.6},{75.6,35.4},{75.6,35.8}}},nil,3537,nil,nil,35,nil,"",0},
[26045] = {'Storm Tempest',9291,9291,71,71,0,{[3537] = {{77,38.6}}},nil,3537,nil,nil,834,nil,"",0},
[32357] = {'Old Crystalbark',9291,9291,71,72,0,{[3537] = {{20.6,27.8},{20.8,27},{21.2,28.6},{21.2,29.8},{21.6,30.8},{21.8,27},{21.8,34.6},{22,33.8},{22.2,35.8},{22.4,33.4},{22.6,33},{23,33.8},{23.6,35.8},{24.4,34.4},{24.4,34.6},{25,34.2},{25,34.8},{25.6,34.6},{27,35.6},{29.6,33.2},{30,32},{30.8,30.6},{31.2,30.2},{31.6,30.2},{32,31.4},{33,31.8},{33.2,24.4},{33.2,34},{33.4,31},{33.6,31.2},{34.2,23.6},{34.4,25.4},{34.6,27.4},{35.2,28.4},{35.4,30},{35.6,30.8},{35.6,31.6},{36,28.2},{36.2,29.2},{36.2,30.2},{37.2,29}}},nil,3537,nil,nil,14,nil,"",0},
}
-- Backward compatibility
EbonholdNpcDB = EbonholdNpcDB or {}
EbonholdNpcDB.npcData = EbonholdDB.npcData
@@ -0,0 +1,17 @@
---@type table
local EbonholdDB = QuestieLoader:CreateModule("EbonholdDB")
EbonholdDB.objectData = EbonholdDB.objectData or {
-- Object 600600: Elemental Shrine (starts quest 50026)
[600600] = {
[1] = "Elemental Shrine", -- Name
[2] = nil, -- Type
[3] = nil, -- Zones
[4] = { [3703] = { { 54.29, 45.69 } } }, -- Spawns: Shattrath City
[5] = 12, -- Faction
[6] = nil, -- Unknown
},
}
EbonholdObjectDB = EbonholdObjectDB or {}
EbonholdObjectDB.objectData = EbonholdDB.objectData
@@ -0,0 +1,335 @@
---@type table
local EbonholdDB = QuestieLoader:CreateModule("EbonholdDB")
EbonholdDB.questData = EbonholdDB.questData or {
-- Quest 50026: Elemental Balance
-- Started by object 600600, kills of NPCs 17156 or 17157 count (both are elementals)
[50026] = {
[1] = "Elemental Balance", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 67, -- Quest level
[5] = 64, -- Min level
[6] = 3518, -- Zone (Nagrand)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 30 Elementals in Nagrand"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
{ 17153, 17154, 17155, 17156, 17157, 17159, 17160, 18062, 22309, 22310, 22311, 22313 }, -- IdList: all elemental types
17157, -- RootId: representative NPC
"Shattered Rumbler slain" -- Text
}
}
},
[17] = nil, -- XP reward
[30] = 60, -- Group size
},
-- Quest 50085: Savage Heights
-- Kill 75 beasts in Blade's Edge Mountains
[50085] = {
[1] = "Savage Heights", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 70, -- Quest level
[5] = 67, -- Min level
[6] = 3522, -- Zone (Blade's Edge Mountains)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 75 Beasts in Blade's Edge Mountains"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 47 beast-type NPCs in Blade's Edge Mountains
{ 10204, 20058, 20109, 20327, 20330, 20714, 20728, 20729, 20747, 20748, 20749, 20751, 20924, 20925, 20987, 20998, 21022, 21033, 21042, 21123, 21124, 21373, 21423, 21470, 21796, 21839, 21952, 21956, 22044, 22052, 22110, 22114, 22123, 22132, 22135, 22136, 22141, 22142, 22181, 22268, 22490, 22492, 22498, 22987, 23036, 23343, 23380 },
20728, -- RootId: Bladespire Raptor (representative beast)
"Beast slain"
}
}
},
[17] = nil, -- XP reward
[30] = 75, -- Kill count
},
-- Quest 50086: Unstable Fauna
-- Kill 75 beasts in Netherstorm
[50086] = {
[1] = "Unstable Fauna", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 70, -- Quest level
[5] = 67, -- Min level
[6] = 3523, -- Zone (Netherstorm)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 75 Beasts in Netherstorm"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 18 beast-type NPCs in Netherstorm
{ 18879, 18880, 18883, 18884, 20415, 20607, 20610, 20611, 20618, 20634, 20671, 20673, 20773, 20775, 20777, 20931, 20932, 21005 },
18879, -- RootId: Phase Hunter (representative beast)
"Beast slain"
}
}
},
[17] = nil, -- XP reward
[30] = 75, -- Kill count
},
-- Quest 50201: Dragonblight Trophy
-- Kill 1 rare in Dragonblight
[50201] = {
[1] = "Dragonblight Trophy", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 75, -- Quest level
[5] = 71, -- Min level
[6] = 65, -- Zone (Dragonblight)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 1 Rare in Dragonblight"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- 3 rare NPCs in Dragonblight (already in WotLK DB with spawns)
{ 32417, 32409, 32400 }, -- Scarlet Highlord Daion, Crazed Indu'le Survivor, Tukemuth
32417, -- RootId: Scarlet Highlord Daion (representative rare)
"Rare slain"
}
}
},
[17] = nil, -- XP reward
[30] = 1, -- Kill count (only 1 rare needed)
},
-- Quest 50160: Redridge Trophy
-- Kill 1 rare in Redridge Mountains
[50160] = {
[1] = "Redridge Trophy", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 17, -- Required level (Min level)
[5] = 25, -- Quest level
[17] = 44, -- Zone (Redridge Mountains)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 1 Rare in Redridge Mountains"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- Redridge Rares
{ 14271, 616, 14270, 584, 14272, 14273, 947, 14269 },
14271, -- RootId: Ribchaser (representative rare)
"Rare slain"
}
}
},
[30] = 1, -- Kill count
},
-- Quest 50192: Zangarmarsh Trophy
-- Kill 1 rare in Zangarmarsh
[50192] = {
[1] = "Zangarmarsh Trophy", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 60, -- Required level (Min level)
[5] = 63, -- Quest level
[17] = 3521, -- Zone (Zangarmarsh)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 1 Rare in Zangarmarsh"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- Zangarmarsh Rares
{ 18682, 18680, 18681 }, -- Bog Lurker, Marticar, Coilfang Emissary
18682, -- RootId: Bog Lurker (representative rare)
"Rare slain"
}
}
},
[30] = 1, -- Kill count
},
-- Quest 50083: Forest Stalkers
-- Kill 75 beasts in Terokkar Forest
[50083] = {
[1] = "Forest Stalkers", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 65, -- Quest level
[5] = 62, -- Min level
[6] = 3519, -- Zone (Terokkar Forest)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 75 Beasts in Terokkar Forest"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 53 beast-type NPCs in Terokkar Forest
{ 16932, 16933, 18467, 18464, 18466, 18670, 18465, 21723, 18461, 18648, 18750, 23219, 23051, 18463, 22424, 20682, 21515, 21854, 23163, 21816, 18476, 18477, 32956, 18437, 21634, 22337, 24922, 21804, 18470, 18438, 18647, 19607, 18468, 23338, 14866, 14868, 22100, 23206, 22972, 22105, 19659, 22326, 18707, 22807, 18469, 23167, 21635, 18492, 19616, 23162, 14869, 22339, 22987 },
16932, -- RootId: Razorfang Hatchling (representative beast)
"Beast slain"
}
}
},
[17] = nil, -- XP reward
[30] = 75, -- Kill count
},
-- Quest 50060: Skies of Blade's Edge
-- Kill Dragonkin in Blade's Edge Mountains
[50060] = {
[1] = "Skies of Blade's Edge", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 67, -- Required level (Min level)
[5] = 70, -- Quest level
[17] = 3522, -- Zone (Blade's Edge Mountains)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Slay Dragonkin in Blade's Edge Mountains"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 17 dragonkin-type NPCs in Blade's Edge Mountains
{ 18692, 21032, 20021, 20713, 21004, 23281, 23261, 21387, 22108, 21492, 23061, 21811, 21497, 23282, 21389, 22130, 23364 },
18692, -- RootId: Hemathion (representative dragonkin)
"Dragonkin slain"
}
}
},
},
-- Quest 50030: Tundra Turbulence
-- Kill Elementals in Borean Tundra
[50030] = {
[1] = "Tundra Turbulence", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 68, -- Required level (Min level)
[5] = 71, -- Quest level
[6] = 3537, -- Zone (Borean Tundra)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill Elementals in Borean Tundra"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 15 elemental-type NPCs in Borean Tundra
{ 25417, 32357, 25715, 25514, 25419, 24601, 25415, 25226, 25742, 25707, 25709, 26045, 25376, 25418, 25416 },
25417, -- RootId: Raging Boiler (representative elemental)
"Elementals slain"
}
}
},
[17] = nil, -- XP reward
[30] = 75, -- Kill count (Assumed generic high count for zone farming quests, user said "amount varies", so we don't hardcode it in logic but usually DB needs a number. 75 puts it in line with others.)
},
-- Quest 50087: Shadowed Beasts
-- Kill 75 beasts in Shadowmoon Valley
[50087] = {
[1] = "Shadowed Beasts", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 67, -- Quest level
[5] = 65, -- Min level
[6] = 3520, -- Zone (Shadowmoon Valley)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 75 Beasts in Shadowmoon Valley"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 25 beast-type NPCs in Shadowmoon Valley (excluding mounts)
{ 19379, 19784, 20502, 20503, 21102, 21108, 21307, 21340, 21408, 21462, 21627, 21723, 21802, 21864, 21879, 21897, 21901, 22265, 23020, 23169, 23264, 23267, 23269, 23326, 23501 },
21723, -- RootId: Blackwind Sabercat (representative beast)
"Beast slain"
}
}
},
[17] = nil, -- XP reward
[30] = 75, -- Kill count
},
-- Quest 50064: Heart of the Dragonflights
-- Kill 30 dragonkin in Dragonblight
[50064] = {
[1] = "Heart of the Dragonflights", -- Quest name
[2] = { nil, { 600600 } }, -- startedBy: {{npcIds}, {objectIds}, {itemIds}}
[4] = 73, -- Quest level
[5] = 71, -- Min level
[6] = 65, -- Zone (Dragonblight)
[8] = {
"This is a custom objective. Upon completion, you will automatically receive the assigned reward. If you die, the quest will be removed from your quest log. This quest is automatically rewarded upon completion.",
"Kill 30 Dragonkin in Dragonblight"
},
[10] = {
nil, -- [1] creatureObjective (not used)
nil, -- [2] objectObjective (not used)
nil, -- [3] itemObjective (not used)
nil, -- [4] reputationObjective (not used)
{ -- [5] killCreditObjective
{
-- All 49 dragonkin NPCs in Dragonblight (excluding 32572 which is an object)
{ 26276, 26277, 26322, 26349, 26443, 26593, 26917, 26925, 26933, 26949, 26983, 27255, 27506, 27530, 27542, 27575, 27608, 27629, 27682, 27725, 27763, 27765, 27785, 27789, 27856, 27896, 27897, 27898, 27900, 27925, 27935, 27938, 27940, 27943, 27948, 27950, 27953, 27990, 27996, 30058, 31333, 31334, 31393, 32180, 32185, 32186, 32352, 32533, 38017 },
26322, -- RootId: Arcane Wyrm (representative dragonkin)
"Dragonkin slain"
}
}
},
[17] = nil, -- XP reward
[30] = 30, -- Kill count
},
}
-- Backward compatibility
EbonholdQuestDB = EbonholdQuestDB or {}
EbonholdQuestDB.questData = EbonholdDB.questData
+323
View File
@@ -0,0 +1,323 @@
---@class EbonholdLoader
---@type table
local EbonholdLoader = QuestieLoader:CreateModule("EbonholdLoader")
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
---@type table
local EbonholdDB = QuestieLoader:ImportModule("EbonholdDB")
_G.EbonholdDB = _G.EbonholdDB or EbonholdDB
local _overridesInjected = false
local _zonesApplied = false
local function _LoadIfString(data, label)
if not data then return nil end
if type(data) == "string" then
local fn, err = loadstring(data)
if not fn then
if Questie and Questie.Debug then
Questie:Debug(Questie.DEBUG_CRITICAL,
"[EbonholdLoader] loadstring failed for " .. tostring(label) .. ": " .. tostring(err))
end
return nil
end
local ok, tbl = pcall(fn)
if not ok then
if Questie and Questie.Debug then
Questie:Debug(Questie.DEBUG_CRITICAL,
"[EbonholdLoader] executing chunk failed for " .. tostring(label) .. ": " .. tostring(tbl))
end
return nil
end
return tbl
end
return data
end
local function _MergeInto(dst, src)
if type(dst) ~= "table" or type(src) ~= "table" then return end
for id, entry in pairs(src) do
dst[id] = entry
end
end
-- Apply Ebonhold custom uiMapId->areaId mappings into ZoneDB (used by map/Journey)
function EbonholdLoader:ApplyZoneTables()
if _zonesApplied then return end
if not EbonholdZoneTables or not EbonholdZoneTables.uiMapIdToAreaId then return end
local ZoneDB = QuestieLoader:ImportModule("ZoneDB")
if not ZoneDB then return end
ZoneDB.private = ZoneDB.private or {}
ZoneDB.private.uiMapIdToAreaId = ZoneDB.private.uiMapIdToAreaId or {}
ZoneDB.private.areaIdToUiMapId = ZoneDB.private.areaIdToUiMapId or {}
ZoneDB.private.subZoneToParentZone = ZoneDB.private.subZoneToParentZone or {}
ZoneDB.private.dungeons = ZoneDB.private.dungeons or {}
ZoneDB.private.dungeonLocations = ZoneDB.private.dungeonLocations or {}
ZoneDB.private.dungeonParentZones = ZoneDB.private.dungeonParentZones or {}
for uiMapId, areaId in pairs(EbonholdZoneTables.uiMapIdToAreaId) do
if uiMapId and areaId then
-- Always set these mappings (remove the nil check to ensure they're set)
ZoneDB.private.uiMapIdToAreaId[uiMapId] = areaId
-- Ebonhold uses custom map ids for spawns/waypoints (e.g. 1238 for Northshire Valley).
-- QuestieMap draws by converting AreaId->UiMapId, so register these ids as self-mapping.
ZoneDB.private.areaIdToUiMapId[uiMapId] = uiMapId
-- If this uiMapId represents a *dungeon* map, prefer it for that dungeon's AreaId.
-- This keeps normal zones intact (e.g. Elwynn stays 1429), while letting Ebonhold
-- provide real instance maps (e.g. mapID 691 for The Stockade).
if type(ZoneDB.private.dungeons) == "table" and ZoneDB.private.dungeons[areaId] then
ZoneDB.private.areaIdToUiMapId[areaId] = uiMapId
end
-- Register subzone to parent zone mapping so GetParentZoneId works with Ebonhold zones
if uiMapId ~= areaId then
ZoneDB.private.subZoneToParentZone[uiMapId] = areaId
end
end
end
-- Optional: allow Ebonhold to define custom dungeon zones/entrances without touching core tables.
if type(EbonholdZoneTables.dungeons) == "table" then
for areaId, data in pairs(EbonholdZoneTables.dungeons) do
if areaId and data then
ZoneDB.private.dungeons[areaId] = data
end
end
end
if type(EbonholdZoneTables.dungeonLocations) == "table" then
for areaId, data in pairs(EbonholdZoneTables.dungeonLocations) do
if areaId and data then
ZoneDB.private.dungeonLocations[areaId] = data
end
end
end
if type(EbonholdZoneTables.dungeonParentZones) == "table" then
for subZoneId, parentZoneId in pairs(EbonholdZoneTables.dungeonParentZones) do
if subZoneId and parentZoneId then
ZoneDB.private.dungeonParentZones[subZoneId] = parentZoneId
end
end
end
-- Also map parentMapIDs (e.g. 10138) to a usable uiMapId to avoid fallback errors.
if EbonholdUiMapData and EbonholdUiMapData.uiMapData then
for uiMapId, data in pairs(EbonholdUiMapData.uiMapData) do
if uiMapId and ZoneDB.private.areaIdToUiMapId[uiMapId] == nil then
ZoneDB.private.areaIdToUiMapId[uiMapId] = uiMapId
end
if data and type(data.parentMapID) == "number" and ZoneDB.private.areaIdToUiMapId[data.parentMapID] == nil then
ZoneDB.private.areaIdToUiMapId[data.parentMapID] = uiMapId
end
end
end
-- Register zone sort names for custom zones
if EbonholdZoneTables.zoneSort then
ZoneDB.private.zoneSort = ZoneDB.private.zoneSort or {}
for zoneId, zoneName in pairs(EbonholdZoneTables.zoneSort) do
ZoneDB.private.zoneSort[zoneId] = zoneName
end
end
-- Clear cached zone lookups after adding custom zones
if QuestieDB and QuestieDB.private and QuestieDB.private.zoneCache then
QuestieDB.private.zoneCache = {}
end
-- Clear auto-blacklist since zone validation might have changed
if QuestieDB and QuestieDB.autoBlacklist then
QuestieDB.autoBlacklist = {}
end
_zonesApplied = true
end
-- Inject Ebonhold tables into QuestieDB *Overrides* so QueryQuestSingle/QueryNPCSingle can see them
function EbonholdLoader:InjectOverrides()
if _overridesInjected then return end
_overridesInjected = true
-- Apply zone tables FIRST, before any ZoneDB functions use the local variables
EbonholdLoader:ApplyZoneTables()
QuestieDB.npcDataOverrides = QuestieDB.npcDataOverrides or {}
QuestieDB.objectDataOverrides = QuestieDB.objectDataOverrides or {}
QuestieDB.itemDataOverrides = QuestieDB.itemDataOverrides or {}
QuestieDB.questDataOverrides = QuestieDB.questDataOverrides or {}
local npcData = _LoadIfString(EbonholdDB and EbonholdDB.npcData, "EbonholdDB.npcData")
local objectData = _LoadIfString(EbonholdDB and EbonholdDB.objectData, "EbonholdDB.objectData")
local itemData = _LoadIfString(EbonholdDB and EbonholdDB.itemData, "EbonholdDB.itemData")
local questData = _LoadIfString(EbonholdDB and EbonholdDB.questData, "EbonholdDB.questData")
_MergeInto(QuestieDB.npcDataOverrides, npcData)
_MergeInto(QuestieDB.objectDataOverrides, objectData)
_MergeInto(QuestieDB.itemDataOverrides, itemData)
_MergeInto(QuestieDB.questDataOverrides, questData)
-- Keep a lightweight list of custom quest ids for search/UI (DO NOT touch QuestPointers; those are numeric stream pointers)
if type(questData) == "table" then
QuestieDB.ebonholdQuestIds = QuestieDB.ebonholdQuestIds or {}
for questId, _ in pairs(questData) do
if type(questId) == "number" then
QuestieDB.ebonholdQuestIds[questId] = true
end
end
end
-- Keep a lightweight list of custom NPC ids for search/UI
if type(npcData) == "table" then
QuestieDB.ebonholdNpcIds = QuestieDB.ebonholdNpcIds or {}
for npcId, _ in pairs(npcData) do
if type(npcId) == "number" then
QuestieDB.ebonholdNpcIds[npcId] = true
end
end
end
-- Keep a lightweight list of custom object ids for search/UI
if type(objectData) == "table" then
QuestieDB.ebonholdObjectIds = QuestieDB.ebonholdObjectIds or {}
for objectId, _ in pairs(objectData) do
if type(objectId) == "number" then
QuestieDB.ebonholdObjectIds[objectId] = true
end
end
end
-- Keep a lightweight list of custom item ids for search/UI
if type(itemData) == "table" then
QuestieDB.ebonholdItemIds = QuestieDB.ebonholdItemIds or {}
for itemId, _ in pairs(itemData) do
if type(itemId) == "number" then
QuestieDB.ebonholdItemIds[itemId] = true
end
end
end
end
-- Hook ZoneDB.Initialize to inject custom zones BEFORE standard zones are processed
do
local ZoneDB = QuestieLoader:ImportModule("ZoneDB")
if ZoneDB then
local originalZoneInitialize = ZoneDB.Initialize
if type(originalZoneInitialize) == "function" then
function ZoneDB:Initialize(...)
-- Apply custom zones FIRST, before standard zone processing
EbonholdLoader:ApplyZoneTables()
-- Then initialize standard zones
return originalZoneInitialize(self, ...)
end
end
end
end
-- Hook QuestieDB.Initialize to guarantee timing for overrides
do
local originalInitialize = QuestieDB and QuestieDB.Initialize
if type(originalInitialize) == "function" then
function QuestieDB:Initialize(...)
-- 1) Inject overrides before the DB handles are created
EbonholdLoader:InjectOverrides()
local ret = originalInitialize(self, ...)
-- 2) Refresh Journey caches (zoneMap/zones) so "Quests by Zone" sees the new data
local ZoneDB = QuestieLoader:ImportModule("ZoneDB")
local QuestieJourney = QuestieLoader:ImportModule("QuestieJourney")
if ZoneDB and QuestieJourney then
QuestieJourney.zoneMap = ZoneDB:GetZonesWithQuests(true)
QuestieJourney.zones = ZoneDB:GetRelevantZones()
-- Clear the UI cache so the tree gets rebuilt
if QuestieJourney.private then
QuestieJourney.private.treeCache = nil
QuestieJourney.private.lastZoneSelection = {}
end
-- If the Journey window is open, rebuild the current tab
if QuestieJourneyFrame and QuestieJourneyFrame:IsShown() and QuestieJourney.tabGroup then
local p = QuestieJourney.private
if p and p.containerCache and p.lastOpenWindow then
p:HandleTabChange(p.containerCache, p.lastOpenWindow)
end
end
end
return ret
end
end
end
-- >>> NEW: Make QuestieDB.GetQuest work for Ebonhold override quests (Journey needs this)
do
local originalGetQuest = QuestieDB.GetQuest
if type(originalGetQuest) == "function" then
function QuestieDB.GetQuest(questId)
local q = originalGetQuest(questId)
if q then return q end
-- Fallback: build a minimal quest object from QueryQuestSingle (works with overrides)
local name = QuestieDB.QueryQuestSingle(questId, "name")
if not name then return nil end
local level = QuestieDB.QueryQuestSingle(questId, "level") or
QuestieDB.QueryQuestSingle(questId, "questLevel")
local reqLevel = QuestieDB.QueryQuestSingle(questId, "requiredLevel") or
QuestieDB.QueryQuestSingle(questId, "minLevel")
local desc = QuestieDB.QueryQuestSingle(questId, "objectiveText") or
QuestieDB.QueryQuestSingle(questId, "description") or QuestieDB.QueryQuestSingle(questId, "details")
-- Build the Starts field from startedBy (CRITICAL for available quests to show icons!)
local startedBy = QuestieDB.QueryQuestSingle(questId, "startedBy")
local starts = {
NPC = startedBy and startedBy[1] or {},
GameObject = startedBy and startedBy[2] or {},
Item = startedBy and startedBy[3] or {},
}
-- Also get other fields needed for quest display
local finishedBy = QuestieDB.QueryQuestSingle(questId, "finishedBy")
local specialFlags = QuestieDB.QueryQuestSingle(questId, "specialFlags")
local isRepeatable = specialFlags and (bit.band(specialFlags, 1) ~= 0) or false
-- Questie modules expect these common fields
return {
Id = questId,
id = questId,
name = name,
level = level or reqLevel or 0,
requiredLevel = reqLevel or 0,
Description = desc, -- Journey tooltip reads quest.Description
Starts = starts, -- CRITICAL: AvailableQuests needs this!
finishedBy = finishedBy,
IsRepeatable = isRepeatable,
}
end
end
end
-- Ensure zones also get applied when Questie finishes loading (covers edge cases)
do
local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:SetScript("OnEvent", function(_, _, name)
if name ~= "Questie-335" then return end
if C_Timer and C_Timer.After then
C_Timer.After(0, function() EbonholdLoader:ApplyZoneTables() end)
else
EbonholdLoader:ApplyZoneTables()
end
end)
end
return EbonholdLoader
@@ -0,0 +1,36 @@
---@class EbonholdUiMapData
EbonholdUiMapData = EbonholdUiMapData or {}
EbonholdUiMapData.uiMapData = {
-- Ebonhold custom dungeon maps
-- Example from Ascension (Reference only for structure):
-- [691] = {
-- [1] = 375.0,
-- [2] = 251.0,
-- [3] = 189.0,
-- [4] = 220.0,
-- instance = 34,
-- mapID = 691.1,
-- name = "The Stockade",
-- mapType = 3,
-- parentMapID = 717,
-- },
}
local function ApplyUiMapData()
if not QuestieCompat then return end
if not EbonholdUiMapData or not EbonholdUiMapData.uiMapData then return end
QuestieCompat.UiMapData = QuestieCompat.UiMapData or {}
for uiMapId, data in pairs(EbonholdUiMapData.uiMapData) do
if QuestieCompat.UiMapData[uiMapId] == nil then
QuestieCompat.UiMapData[uiMapId] = data
end
end
end
if QuestieCompat and QuestieCompat.LoadUiMapData then
hooksecurefunc(QuestieCompat, "LoadUiMapData", ApplyUiMapData)
end
ApplyUiMapData()
@@ -0,0 +1,17 @@
---@class EbonholdZoneTables
EbonholdZoneTables = EbonholdZoneTables or {}
EbonholdZoneTables.uiMapIdToAreaId = EbonholdZoneTables.uiMapIdToAreaId or {
-- Example mappings:
-- [1238] = 12, -- Custom Northshire Valley Map -> Elwynn Forest AreaID
}
-- Register zone sort names for custom zones so they can be used in quest zoneOrSort field
EbonholdZoneTables.zoneSort = EbonholdZoneTables.zoneSort or {
-- [1238] = "Northshire Valley",
}
-- Optional: custom dungeon registration (merged into ZoneDB by EbonholdLoader).
EbonholdZoneTables.dungeons = EbonholdZoneTables.dungeons or {}
EbonholdZoneTables.dungeonLocations = EbonholdZoneTables.dungeonLocations or {}
EbonholdZoneTables.dungeonParentZones = EbonholdZoneTables.dungeonParentZones or {}