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
+51
View File
@@ -0,0 +1,51 @@
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
-- SELECT entry FROM creature_template WHERE trainertype=0 AND trainerclass=X
-- 1: Warrior, 2: Paladin, 3: Hunter, 4: Rogue, 5: Priest, 6: Death Knight, 7: Shaman, 8: Mage, 9: Warlock, 11: Druid
---@type table<Classes, NpcId[]>
local classTrainers
if Questie.IsWotlk then
classTrainers = {
["WARRIOR"]={911,912,913,914,985,1229,1901,2119,2131,3041,3042,3043,3059,3063,3153,3169,3353,3354,3408,3593,3598,4087,4089,4593,4594,4595,5113,5114,5479,5480,7315,8141,16503,16771,17120,17480,17504,26332},
["PALADIN"]={925,926,927,928,1232,5147,5148,5149,5491,5492,8140,15280,16275,16501,16679,16680,16681,16761,17121,17483,17509,17844,20406,23128,26327,35281},
["HUNTER"]={895,987,1231,1404,3038,3039,3040,3061,3065,3154,3171,3352,3406,3407,3596,3601,3963,4138,4146,4205,5115,5116,5117,5501,5515,5516,5517,8308,10930,15513,16270,16499,16672,16673,16674,16738,17110,17122,17505,26325},
["ROGUE"]={915,916,917,918,1234,1411,2122,2130,3155,3170,3327,3328,3401,3594,3599,4163,4214,4215,4582,4583,4584,5165,5166,5167,6707,13283,15285,16279,16684,16685,16686,26329},
["PRIEST"]={375,376,377,837,1226,2123,2129,3044,3045,3046,3595,3600,3706,3707,4090,4091,4092,4606,4607,4608,5141,5142,5143,5484,5489,5994,6014,6018,11397,11401,11406,15284,16276,16502,16658,16659,16660,16756,17482,17510,17511,26328},
["DEATHKNIGHT"]={28471,28472,28474,29194,29195,29196,31084},
["SHAMAN"]={986,3030,3031,3032,3062,3066,3157,3173,3344,3403,13417,17089,17204,17212,17219,17519,17520,20407,23127,26330},
["MAGE"]={198,328,331,944,1228,2124,2128,2485,2489,2492,3047,3048,3049,4165,4566,4567,4568,5144,5145,5146,5497,5498,5880,5882,5883,5884,5885,5957,5958,7311,7312,15279,16269,16500,16651,16652,16653,16654,16749,16755,17105,17481,17513,17514,19340,20791,23103,26326,27703,27704,27705,28956,28958,29156},
["WARLOCK"]={459,460,461,906,988,2126,2127,3156,3172,3324,3325,3326,4563,4564,4565,5171,5172,5173,5495,5496,5612,6251,15283,16266,16646,16647,16648,23534,26331},
["UNKNOWN"]={},
["DRUID"]={3033,3034,3036,3060,3064,3597,3602,4217,4218,4219,5504,5505,5506,8142,9465,12042,16655,16721,26324},
}
elseif Questie.IsTBC then
classTrainers = {
["WARRIOR"] = {911,912,913,914,985,1229,1901,2119,2131,3041,3042,3043,3059,3063,3153,3169,3353,3354,3408,3593,3598,4087,4089,4593,4594,4595,4992,5113,5114,5479,5480,7315,8141,16387,16503,16771,17120,17480,17504,26332},
["PALADIN"] = {925,926,927,928,1232,4988,5147,5148,5149,5491,5492,8140,15280,16275,16501,16679,16680,16681,16761,17121,17483,17509,17844,20406,23128,26327},
["HUNTER"] = {895,987,1231,1404,3038,3039,3040,3061,3065,3154,3171,3352,3406,3407,3596,3601,3963,4138,4146,4205,4986,5115,5116,5117,5501,5515,5516,5517,8308,10930,15513,16270,16499,16672,16673,16674,16738,17110,17122,17505,26325},
["ROGUE"] = {915,916,917,918,1234,1411,2122,2130,3155,3170,3327,3328,3401,3594,3599,4163,4214,4215,4582,4583,4584,4990,5165,5166,5167,13283,15285,16279,16684,16685,16686,22005,26329},
["PRIEST"] = {375,376,377,837,1226,2123,2129,3044,3045,3046,3595,3600,3706,3707,4090,4091,4092,4606,4607,4608,4989,5141,5142,5143,5484,5489,5994,6014,6018,11397,11401,11406,15284,16276,16502,16658,16659,16660,16756,17482,17510,17511,26328},
["SHAMAN"] = {986,3030,3031,3032,3062,3066,3157,3173,3344,3403,4991,13417,17089,17204,17212,17219,17519,17520,20407,23127,26330},
["MAGE"] = {198,313,328,331,944,1228,2124,2128,3047,3048,3049,4566,4567,4568,4987,5144,5145,5146,5497,5498,5880,5882,5883,5884,5885,7311,7312,15279,16269,16500,16651,16652,16653,16749,17481,17513,17514,26326,27704},
["WARLOCK"] = {459,460,461,906,988,2126,2127,3156,3172,3324,3325,3326,4563,4564,4565,4993,5171,5172,5173,5495,5496,5612,6251,15283,16266,16646,16647,16648,26331},
["DRUID"] = {3033,3034,3036,3060,3064,3597,3602,4217,4218,4219,4985,5504,5505,5506,8142,9465,12042,16655,16721,26324}
}
else
classTrainers = {
["WARRIOR"] = {911,912,913,914,985,1229,1901,2119,2131,3041,3042,3043,3059,3063,3153,3169,3353,3354,3408,3593,3598,4087,4089,4593,4594,4595,5113,5114,5479,5480,7315,8141,16387},
["PALADIN"] = {925,926,927,928,1232,5147,5148,5149,5491,5492,8140},
["HUNTER"] = {895,987,1231,1404,3038,3039,3040,3061,3065,3154,3171,3352,3406,3407,3596,3601,3963,4138,4146,4205,5115,5116,5117,5501,5515,5516,5517,8308,10930},
["ROGUE"] = {915,916,917,918,1234,1411,2122,2130,3155,3170,3327,3328,3401,3594,3599,4163,4214,4215,4582,4583,4584,5165,5166,5167,13283},
["PRIEST"] = {375,376,377,837,1226,2123,2129,3044,3045,3046,3595,3600,3706,3707,4090,4091,4092,4606,4607,4608,5141,5142,5143,5484,5489,5994,6014,6018,11397,11401,11406},
["SHAMAN"] = {986,3030,3031,3032,3062,3066,3157,3173,3344,3403,13417},
["MAGE"] = {198,313,328,331,944,1228,2124,2128,3047,3048,3049,4566,4567,4568,5144,5145,5146,5497,5498,5880,5882,5883,5884,5885,7311,7312},
["WARLOCK"] = {459,460,461,906,988,2126,2127,3156,3172,3324,3325,3326,4563,4564,4565,5171,5172,5173,5495,5496,5612},
["DRUID"] = {3033,3034,3036,3060,3064,3597,3602,4217,4218,4219,5504,5505,5506,8142,9465,12042}
}
end
function Townsfolk.GetClassTrainers()
return classTrainers
end
+44
View File
@@ -0,0 +1,44 @@
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
local mailboxes
if Questie.IsWotlk then
mailboxes = {
32349,140908,142075,142089,142093,142094,142095,142102,142103,142109,142110,142111,142117,142119,143981,143982,
143983,143984,143985,143986,143987,143988,143989,143990,144011,144112,144125,144126,144127,144128,144129,144130,
144131,144179,144570,153578,153716,157637,163313,163645,164618,164840,171556,171699,171752,173047,173221,175668,
175864,176319,176324,176404,177044,178864,179895,179896,180451,181236,181380,181381,181639,181883,181980,182356,
182357,182359,182360,182361,182362,182363,182364,182365,182391,182567,182939,182946,182948,182949,182950,182955,
183037,183038,183039,183040,183042,183047,183167,183856,183857,183858,184085,184133,184134,184135,184136,184137,
184138,184139,184140,184147,184148,184490,184652,184944,185102,185471,185472,185473,185477,185965,186230,186435,
186506,186629,186687,187113,187260,187268,187316,187322,188123,188132,188241,188256,188355,188486,188531,188534,
188541,188604,188618,188682,188710,189328,189329,189969,190914,190915,191228,191521,191605,191832,191946,191947,
191948,191949,191950,191951,191952,191953,191954,191955,191956,191957,192952,193030,193043,193044,193045,193071,
193791,193972,194016,194027,194147,194492,194788,195218,195219,195467,195468,195528,195529,195530,195554,195555,
195556,195557,195558,195559,195560,195561,195562,195603,195604,195605,195606,195607,195608,195609,195610,195611,
195612,195613,195614,195615,195616,195617,195618,195619,195620,195624,195625,195626,195627,195628,195629
}
elseif Questie.IsTBC then
mailboxes = {
32349,140908,142075,142089,142093,142094,142095,142102,142109,142110,142111,142117,143981,143982,143983,143984,
143985,143987,143988,143989,143990,144011,144112,144125,144126,144127,144128,144129,144130,144131,144179,144570,
153578,153716,157637,163313,163645,164618,164840,171556,171699,171752,173047,173221,176324,176404,177044,178864,
179895,179896,180451,181236,181380,181381,181883,181980,182356,182357,182359,182360,182361,182362,182363,182364,
182365,182567,182939,182946,182948,182949,182950,182955,183037,183038,183039,183040,183042,183047,183167,183856,
183857,183858,184085,184133,184134,184135,184136,184137,184138,184139,184140,184147,184148,184490,184652,184944,
185102,185471,185472,185473,185477,142103,176319,142119,143986,175864,181639,185965,186230,186629,188132,187260,
187113,188123
}
else
mailboxes = {
32349,142075,142089,142093,142094,142095,142102,142103,142109,142110,142111,142117,142119,143981,143982,143983,
143984,143985,143986,143987,143988,143989,143990,144011,144112,144125,144126,144127,144128,144129,144131,153578,
153716,157637,163313,163645,164618,164840,171556,171699,171752,173047,173221,176319,176324,176404,177044,178864,
179895,179896,180451,181236,181639,187260,188123
}
end
function Townsfolk.GetMailboxes()
return mailboxes
end
+26
View File
@@ -0,0 +1,26 @@
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
local meetingStones
if Questie.IsWotlk then
meetingStones = {
178824,178825,178826,178827,178828,178829,178831,178832,178833,178834,178844,178845,
178884,179554,179555,179584,179585,179586,179587,179595,179596,179597,182558,182559,
182560,184455,184456,184458,184462,184463,185321,185322,185433,185550,186251,188171,
188172,188488,191227,191529,192017,192399,192557,192622,193166,193602,194097,195013,
195498,195695,202184
}
elseif Questie.IsTBC then
meetingStones = {
178824,178825,178826,178827,178828,178829,178831,178832,178833,178834,178844,178845,
178884,179554,179555,179584,179585,179586,179587,179595,179596,179597,182558,182559,
182560,184455,184456,184458,184462,184463,185321,185322,185433,185550,186251,188171,
188172
}
else
meetingStones = {} -- Meeting Stones can not be used for teleports in Classic
end
function Townsfolk.GetMeetingStones()
return meetingStones
end
@@ -0,0 +1,46 @@
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
-- SELECT Entry FROM creature_template WHERE NpcFlags & 16 = 16 AND TrainerType=2
---@type NpcId[]
local professionTrainers
if Questie.IsWotlk then
professionTrainers = {514,812,908,1103,1215,1218,1241,1292,1300,1317,1346,1355,1382,1385,1386,1430,1458,1470,1473,1632,1651,1676,1680,1681,1683,1699,1700,1701,1702,1703,2114,2132,2326,2327,2329,2367,2390,2391,2399,2627,2704,2798,2818,2834,2836,2837,2855,2856,2998,3001,3004,3007,3009,3011,3013,3026,3028,3067,3069,3087,3136,3137,3174,3175,3179,3181,3184,3185,3290,3332,3345,3347,3355,3357,3363,3365,3373,3399,3404,3478,3484,3494,3523,3549,3555,3557,3603,3604,3605,3606,3607,3703,3704,3964,3965,3967,4156,4159,4160,4193,4204,4210,4211,4212,4213,4254,4258,4552,4573,4576,4578,4588,4591,4596,4598,4611,4614,4616,4898,4900,5032,5037,5038,5040,5041,5127,5137,5150,5153,5157,5159,5161,5164,5174,5177,5392,5482,5493,5499,5502,5511,5513,5518,5564,5566,5690,5695,5759,5784,5938,5939,5941,5943,6094,6286,6287,6288,6289,6290,6291,6292,6295,6297,6299,6306,6387,7087,7088,7089,7230,7231,7232,7406,7866,7867,7868,7869,7870,7871,7944,7946,7948,7949,8126,8128,8144,8146,8153,8306,8736,8738,9584,10370,10993,11017,11025,11031,11037,11048,11050,11051,11052,11072,11073,11074,11097,11098,11146,11177,11178,11557,11865,11866,11867,11868,11869,11870,12025,12030,12032,12961,13084,14740,15400,15501,16000,16160,16161,16190,16253,16265,16272,16273,16277,16278,16366,16367,16583,16588,16621,16633,16639,16640,16642,16644,16662,16663,16667,16669,16676,16688,16692,16702,16719,16723,16724,16725,16726,16727,16728,16729,16731,16736,16744,16746,16752,16763,16773,16774,16780,16823,17005,17101,17214,17215,17222,17245,17246,17424,17434,17441,17442,17487,17488,17634,17637,17983,18018,18747,18748,18749,18751,18752,18753,18754,18755,18771,18772,18773,18774,18775,18776,18777,18779,18802,18804,18911,18987,18988,18990,18991,18993,19052,19063,19180,19184,19185,19186,19187,19251,19252,19341,19369,19478,19539,19540,19576,19774,19775,19777,19778,20124,20125,21087,22477,23734,24868,25099,25277,26564,26903,26904,26905,26906,26907,26909,26910,26911,26912,26913,26914,26915,26916,26951,26952,26953,26954,26955,26956,26957,26958,26959,26960,26961,26962,26963,26964,26969,26972,26974,26975,26976,26977,26980,26981,26982,26986,26987,26988,26989,26990,26991,26992,26993,26994,26995,26996,26997,26998,26999,27000,27001,27023,27029,27034,28693,28694,28696,28697,28698,28699,28700,28701,28702,28703,28704,28705,28706,28742,29233,29505,29506,29507,29508,29509,29513,29514,29631,29924,30706,30709,30710,30711,30713,30715,30716,30717,30721,30722,32474,33580,33581,33583,33586,33587,33588,33589,33590,33591,33603,33630,33631,33633,33634,33635,33636,33637,33638,33640,33641,33674,33675,33676,33677,33678,33679,33680,33681,33682,33683,33684,33996}
elseif Questie.IsTBC then
professionTrainers = {
514,812,908,1103,1215,1218,1241,1292,1300,1317,1346,1355,1382,1385,1386,1430,1458,1470,1473,1632,1651,1676,1680,1681,1683,
1699,1700,1701,1702,1703,2114,2132,2326,2327,2329,2367,2390,2391,2399,2627,2704,2798,2818,2834,2836,2837,2855,2856,2998,3001,
3004,3007,3009,3011,3013,3026,3028,3067,3069,3087,3136,3137,3174,3175,3179,3181,3184,3185,3290,3332,3345,3347,3355,3357,3363,
3365,3373,3399,3404,3478,3484,3494,3523,3549,3555,3557,3603,3604,3605,3606,3607,3703,3704,3964,3965,3967,4156,4159,4160,4193,
4204,4210,4211,4212,4213,4254,4258,4552,4573,4576,4578,4588,4591,4596,4598,4611,4614,4616,4898,4900,5127,5137,5150,5153,5157,
5159,5161,5164,5174,5177,5392,5482,5493,5499,5502,5511,5513,5518,5564,5566,5690,5695,5759,5784,5938,5939,5941,5943,6094,6286,
6287,6288,6289,6290,6291,6292,6295,6297,6299,6306,6387,7087,7088,7089,7230,7231,7232,7406,7866,7867,7868,7869,7870,7871,7944,
7946,7948,7949,8126,8128,8144,8146,8153,8306,8736,8738,9584,10370,10993,11017,11025,11031,11037,11048,11050,11051,11052,11072,
11073,11074,11097,11098,11146,11177,11178,11865,11866,11867,11868,11869,11870,12025,12030,12032,12920,12939,12961,13084,14401,14740,15400,
15501,16160,16161,16253,16272,16273,16277,16278,16366,16367,16583,16588,16621,16633,16639,16640,16642,16644,16662,16663,16667,
16669,16676,16688,16692,16702,16719,16723,16724,16725,16726,16727,16728,16729,16731,16736,16744,16746,16752,16763,16773,16774,
16780,16823,17005,17101,17214,17215,17222,17245,17246,17424,17434,17441,17442,17487,17488,17634,17637,17983,18018,18747,18748,
18749,18751,18752,18753,18754,18755,18771,18772,18773,18774,18775,18776,18777,18779,18802,18804,18987,18988,18990,18991,18993,
19052,19063,19180,19184,19185,19186,19187,19251,19252,19341,19369,19478,19539,19540,19576,19774,19775,19777,19778,20124,20125,
21087,22477,24868,25099
}
else
professionTrainers = {
223,514,812,908,957,1103,1215,1218,1241,1246,1292,1300,1317,1346,1355,1382,1383,1385,1386,1430,1458,1466,1470,1473,1632,1651,
1676,1680,1681,1683,1699,1700,1701,1702,1703,2114,2132,2326,2327,2329,2367,2390,2391,2399,2627,2704,2737,2798,2818,2834,2836,
2837,2855,2856,2857,2998,3001,3004,3007,3008,3009,3011,3013,3026,3028,3067,3069,3087,3136,3137,3174,3175,3179,3181,3184,3185,
3290,3332,3345,3347,3355,3357,3363,3365,3373,3399,3404,3412,3478,3484,3494,3523,3530,3531,3549,3555,3557,3603,3604,3605,3606,
3607,3703,3704,3964,3965,3967,4156,4159,4160,4193,4204,4210,4211,4212,4213,4254,4258,4552,4573,4576,4578,4586,4588,4591,4596,
4598,4605,4609,4611,4614,4616,4898,4900,5127,5137,5150,5153,5157,5159,5161,5164,5174,5177,5392,5482,5493,5499,5500,5502,5511,
5513,5518,5564,5566,5567,5690,5695,5759,5784,5811,5938,5939,5941,5943,6094,6286,6287,6288,6289,6290,6291,6292,6295,6297,6299,
6306,6387,7087,7088,7089,7230,7231,7232,7406,7866,7867,7868,7869,7870,7871,7944,7946,7948,7949,8126,8128,8144,8146,8153,8306,
8736,8738,9584,10266,10276,10277,10278,10993,11017,11025,11026,11028,11029,11031,11037,11041,11042,11044,11046,11047,11048,
11049,11050,11051,11052,11065,11066,11067,11068,11070,11071,11072,11073,11074,11081,11083,11084,11096,11097,11098,11146,
11177,11178,11865,11866,11867,11868,11869,11870,12025,12030,12032,12920,12939,12961,13084,14401,14740
}
end
function Townsfolk.GetProfessionTrainers()
return professionTrainers
end
+378
View File
@@ -0,0 +1,378 @@
---@class QuestieMenu
local QuestieMenu = QuestieLoader:CreateModule("QuestieMenu")
---@type QuestieLib
local QuestieLib = QuestieLoader:ImportModule("QuestieLib")
---@type QuestieOptions
local QuestieOptions = QuestieLoader:ImportModule("QuestieOptions")
---@type QuestieJourney
local QuestieJourney = QuestieLoader:ImportModule("QuestieJourney")
---@type QuestieMap
local QuestieMap = QuestieLoader:ImportModule("QuestieMap")
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
---@type MeetingStones
local MeetingStones = QuestieLoader:ImportModule("MeetingStones")
---@type QuestieProfessions
local QuestieProfessions = QuestieLoader:ImportModule("QuestieProfessions")
---@type QuestieQuest
local QuestieQuest = QuestieLoader:ImportModule("QuestieQuest")
---@type l10n
local l10n = QuestieLoader:ImportModule("l10n")
---@type QuestieCorrections
local QuestieCorrections = QuestieLoader:ImportModule("QuestieCorrections")
---@type QuestieCombatQueue
local QuestieCombatQueue = QuestieLoader:ImportModule("QuestieCombatQueue")
---@type AvailableQuests
local AvailableQuests = QuestieLoader:ImportModule("AvailableQuests")
---@type Townsfolk
local Townsfolk = QuestieLoader:ImportModule("Townsfolk")
--- COMPATIBILITY ---
local C_Timer = QuestieCompat.C_Timer
local LibDropDown = QuestieCompat.LibUIDropDownMenu or LibStub:GetLibrary("LibUIDropDownMenuQuestie-4.0")
local _, playerClass = UnitClass("player")
local tinsert = tinsert
local professionKeys = QuestieProfessions.professionKeys
local _townsfolk_texturemap = {
["Flight Master"] = "Interface\\Minimap\\tracking\\flightmaster",
["Meeting Stones"] = QuestieLib.AddonPath.."Icons\\mstone.blp",
["Class Trainer"] = "Interface\\Minimap\\tracking\\class",
["Stable Master"] = "Interface\\Minimap\\tracking\\stablemaster",
["Spirit Healer"] = "Interface\\raidframe\\raid-icon-rez",
["Weapon Master"] = QuestieLib.AddonPath.."Icons\\slay.blp",
["Profession Trainer"] = "Interface\\Minimap\\tracking\\profession",
["Ammo"] = 132382,--select(10, GetItemInfo(2515)) -- sharp arrow
["Bags"] = 133634,--select(10, GetItemInfo(4496)) -- small brown pouch
["Potions"] = 134831,--select(10, GetItemInfo(929)) -- Healing Potion
["Trade Goods"] = 132912,--select(10, GetItemInfo(2321)) -- thread
["Drink"] = 134712,--select(10, GetItemInfo(8766)) -- morning glory dew
["Food"] = 133964,--select(10, GetItemInfo(4540)) -- bread
["Pet Food"] = 132165,--select(3, GetSpellInfo(6991)) -- feed pet
["Portal Trainer"] = "Interface\\Minimap\\vehicle-alliancemageportal",
["Reagents"] = (function()
if playerClass == "ROGUE" then
return "Interface\\Minimap\\tracking\\poisons"
end
return "Interface\\Minimap\\tracking\\reagents"
end)(),
[professionKeys.FIRST_AID] = "Interface\\Icons\\spell_holy_sealofsacrifice",
[professionKeys.BLACKSMITHING] = "Interface\\Icons\\trade_blacksmithing",
[professionKeys.LEATHERWORKING] = "Interface\\Icons\\trade_leatherworking",
[professionKeys.ALCHEMY] = "Interface\\Icons\\trade_alchemy",
[professionKeys.HERBALISM] = "Interface\\Icons\\trade_herbalism",
[professionKeys.COOKING] = "Interface\\Icons\\inv_misc_food_15",
[professionKeys.MINING] = "Interface\\Icons\\trade_mining",
[professionKeys.TAILORING] = "Interface\\Icons\\trade_tailoring",
[professionKeys.ENGINEERING] = "Interface\\Icons\\trade_engineering",
[professionKeys.ENCHANTING] = "Interface\\Icons\\trade_engraving",
[professionKeys.FISHING] = "Interface\\Icons\\trade_fishing",
[professionKeys.SKINNING] = "Interface\\Icons\\inv_misc_pelt_wolf_01",
[professionKeys.JEWELCRAFTING] = "Interface\\Icons\\inv_misc_gem_01",
[professionKeys.INSCRIPTION] = "Interface\\Icons\\inv_inscription_tradeskill01",
}
QuestieMenu.private.townsfolk_texturemap = _townsfolk_texturemap
local _spawned = {} -- used to check if we have already spawned an icon for this npc
local function toggle(key, forceRemove) -- /run QuestieLoader:ImportModule("QuestieMap"):ShowNPC(525, nil, 1, "teaste", {}, true)
local ids = Questie.db.global.townsfolk[key] or
Questie.db.char.townsfolk[key] or
Questie.db.global.professionTrainers[key] or
Questie.db.char.vendorList[key]
if (not ids) then
Questie:Debug(Questie.DEBUG_INFO, "Invalid townsfolk key", tostring(key))
return
end
local icon = _townsfolk_texturemap[key] or ("Interface\\Minimap\\tracking\\" .. strlower(key))
if key == "Mailbox" or key == "Meeting Stones" then -- object type townsfolk
if Questie.db.profile.townsfolkConfig[key] and (not forceRemove) then
for _, id in pairs(ids) do
if key == "Meeting Stones" then
local dungeonName, levelRange = MeetingStones:GetLocalizedDungeonNameAndLevelRangeByObjectId(id)
if dungeonName and levelRange then
QuestieMap:ShowObject(id, icon, 1.2, Questie:Colorize(l10n("Meeting Stone"), "white") .. "|n" .. dungeonName .. " " .. levelRange, {}, true, key)
end
else
QuestieMap:ShowObject(id, icon, 1.2, Questie:Colorize(l10n(key), "white"), {}, true, key)
end
end
else
for _, id in pairs(ids) do
QuestieMap:UnloadManualFrames(id, key)
end
end
else
if Questie.db.profile.townsfolkConfig[key] and (not forceRemove) then
local faction = UnitFactionGroup("Player")
local timer
local e = 1
local max = (#ids)+1
timer = C_Timer.NewTicker(0.01, function()
local start = e
while e < max and e-start < 32 do
local id = ids[e]
if (not _spawned[id]) then
local friendly = QuestieDB.QueryNPCSingle(id, "friendlyToFaction")
if ((not friendly) or friendly == "AH" or (faction == "Alliance" and friendly == "A") or (faction == "Horde" and friendly == "H")) and (not QuestieCorrections.questNPCBlacklist[id]) then
local npcName = QuestieDB.QueryNPCSingle(id, "name") or ("Missing NPC name for " .. tostring(id))
local subName = l10n(QuestieDB.QueryNPCSingle(id, "subName") or tostring(key))
local npcTitle = Questie:Colorize(npcName, "white") .. " (" .. subName .. ")"
QuestieMap:ShowNPC(id, icon, 1.2, npcTitle, {}, true, key, true)
_spawned[id] = true
end
end
e = e + 1
end
if e == max then
timer:Cancel()
end
end)
else
for _, id in pairs(ids) do
QuestieMap:UnloadManualFrames(id, key)
_spawned[id] = nil
end
end
end
end
local function build(key)
local icon = _townsfolk_texturemap[key] or ("Interface\\Minimap\\tracking\\" .. strlower(key))
return {
text = l10n(tostring(key)),
func = function() Questie.db.profile.townsfolkConfig[key] = not Questie.db.profile.townsfolkConfig[key] toggle(key) end,
icon=icon,
notCheckable=false,
checked=Questie.db.profile.townsfolkConfig[key],
isNotRadio=true,
keepShownOnClick=true
}
end
local function buildLocalized(key, localizedText)
local icon = _townsfolk_texturemap[key] or ("Interface\\Minimap\\tracking\\" .. strlower(key))
return {
text = localizedText,
func = function() Questie.db.profile.townsfolkConfig[key] = not Questie.db.profile.townsfolkConfig[key] toggle(key) end,
icon=icon,
notCheckable=false,
checked=Questie.db.profile.townsfolkConfig[key],
isNotRadio=true,
keepShownOnClick=true
}
end
function QuestieMenu:OnLogin(forceRemove) -- toggle all icons
Townsfolk:UpdatePlayerVendors()
if (not Questie.db.profile.townsfolkConfig) then
Questie.db.profile.townsfolkConfig = {
["Flight Master"] = true,
["Mailbox"] = true,
["Meeting Stones"] = true
}
end
for key in pairs(Questie.db.profile.townsfolkConfig) do
if forceRemove then
toggle(key, forceRemove)
end
toggle(key)
end
end
local div = { -- from libEasyMenu code
hasArrow = false,
dist = 0,
isTitle = true,
isUninteractable = true,
notCheckable = true,
iconOnly = true,
isSeparator = true,
icon = "Interface\\Common\\UI-TooltipDivider-Transparent",
tCoordLeft = 0,
tCoordRight = 1,
tCoordTop = 0,
tCoordBottom = 1,
tSizeX = 0,
tSizeY = 8,
tFitDropDownSizeX = true,
text="",
iconInfo = {
tCoordLeft = 0,
tCoordRight = 1,
tCoordTop = 0,
tCoordBottom = 1,
tSizeX = 0,
tSizeY = 8,
tFitDropDownSizeX = true
},
}
local secondaryProfessions = {
[professionKeys.FIRST_AID] = true,
[professionKeys.COOKING] = true,
[professionKeys.FISHING] = true
}
function QuestieMenu.buildProfessionMenu()
local profMenu = {}
local profMenuSorted = {}
local secondaryProfMenuSorted = {}
local profMenuData = {}
for key, _ in pairs(Questie.db.global.professionTrainers) do
local localizedKey = l10n(QuestieProfessions:GetProfessionName(key))
profMenuData[localizedKey] = buildLocalized(key, localizedKey)
if secondaryProfessions[key] then
tinsert(secondaryProfMenuSorted, localizedKey)
else
tinsert(profMenuSorted, localizedKey)
end
end
table.sort(profMenuSorted)
table.sort(secondaryProfMenuSorted)
for _, key in pairs(profMenuSorted) do
tinsert(profMenu, profMenuData[key])
end
tinsert(profMenu, div)
for _, key in pairs(secondaryProfMenuSorted) do
tinsert(profMenu, profMenuData[key])
end
return profMenu
end
function QuestieMenu.buildVendorMenu()
local vendorMenu = {}
local vendorMenuSorted = {}
local vendorMenuData = {}
for key, _ in pairs(Questie.db.char.vendorList) do
local localizedKey = l10n(tostring(key))
vendorMenuData[localizedKey] = build(key)
tinsert(vendorMenuSorted, localizedKey)
end
table.sort(vendorMenuSorted)
for _, key in pairs(vendorMenuSorted) do
tinsert(vendorMenu, vendorMenuData[key])
end
return vendorMenu
end
function QuestieMenu.buildTownsfolkMenu()
local townsfolkMenu = {}
for key in pairs(Questie.db.global.townsfolk) do
tinsert(townsfolkMenu, build(key))
end
for key in pairs(Questie.db.char.townsfolk) do
tinsert(townsfolkMenu, build(key))
end
return townsfolkMenu
end
function QuestieMenu:Show(hideDelay)
if not Questie.db.profile.townsfolkConfig then
Questie.db.profile.townsfolkConfig = {}
end
if not QuestieMenu.menu then
QuestieMenu.menu = LibDropDown:Create_UIDropDownMenu("QuestieTownsfolkMenuFrame", UIParent)
end
local menuTable = QuestieMenu.buildTownsfolkMenu()
tinsert(menuTable, { text= l10n("Available Quest"), func = function()
local value = not Questie.db.profile.enableAvailable
Questie.db.profile.enableAvailable = value
QuestieQuest:ToggleNotes(value)
QuestieQuest:SmoothReset()
end, icon=QuestieLib.AddonPath.."Icons\\available.blp", notCheckable=false, checked=Questie.db.profile.enableAvailable, isNotRadio=true, keepShownOnClick=true})
tinsert(menuTable, { text= l10n("Trivial Quest"), func = function()
local value = Questie.db.profile.lowLevelStyle == Questie.LOWLEVEL_ALL
if value then
Questie.db.profile.lowLevelStyle = Questie.LOWLEVEL_NONE
else
Questie.db.profile.lowLevelStyle = Questie.LOWLEVEL_ALL
end
AvailableQuests.CalculateAndDrawAll()
end, icon=QuestieLib.AddonPath.."Icons\\available_gray.blp", notCheckable=false, checked=Questie.db.profile.lowLevelStyle==Questie.LOWLEVEL_ALL, isNotRadio=true, keepShownOnClick=true})
tinsert(menuTable, { text= l10n("Objective"), func = function()
local value = not Questie.db.profile.enableObjectives
Questie.db.profile.enableObjectives = value
QuestieQuest:ToggleNotes(value)
QuestieQuest:SmoothReset()
end, icon=QuestieLib.AddonPath.."Icons\\event.blp", notCheckable=false, checked=Questie.db.profile.enableObjectives, isNotRadio=true, keepShownOnClick=true})
tinsert(menuTable, {text= l10n("Profession Trainer"), func = function() end, keepShownOnClick=true, hasArrow=true, menuList=QuestieMenu.buildProfessionMenu(), notCheckable=true})
tinsert(menuTable, {text= l10n("Vendor"), func = function() end, keepShownOnClick=true, hasArrow=true, menuList=QuestieMenu.buildVendorMenu(), notCheckable=true})
tinsert(menuTable, div)
tinsert(menuTable, { text= l10n('Advanced Search'), func=function() QuestieOptions:HideFrame(); QuestieJourney.tabGroup:SelectTab("search"); QuestieJourney.ToggleJourneyWindow() end})
tinsert(menuTable, { text= l10n("Questie Options"), func=function()
QuestieCombatQueue:Queue(function()
QuestieOptions:OpenConfigWindow()
end)
end})
tinsert(menuTable, { text= l10n('My Journey'), func=function()
QuestieCombatQueue:Queue(function()
QuestieOptions:HideFrame();
QuestieJourney.tabGroup:SelectTab("journey");
QuestieJourney.ToggleJourneyWindow()
end)
end})
if Questie.db.profile.debugEnabled then -- add recompile db & reload buttons when debugging is enabled
tinsert(menuTable, { text= l10n('Recompile Database'), func=function()
if Questie.IsSoD then
Questie.db.global.sod.dbIsCompiled = false
else
Questie.db.global.dbIsCompiled = false
end
ReloadUI()
end})
tinsert(menuTable, { text= l10n('Reload UI'), func=function() ReloadUI() end})
end
tinsert(menuTable, {text= l10n('Cancel'), func=function() end})
LibDropDown:EasyMenu(menuTable, QuestieMenu.menu, "cursor", -80, -15, "MENU", hideDelay or 2)
end
function QuestieMenu:ShowTownsfolk(hideDelay)
if not Questie.db.profile.townsfolkConfig then
Questie.db.profile.townsfolkConfig = {}
end
if not QuestieMenu.menuTowns then
QuestieMenu.menuTowns = LibDropDown:Create_UIDropDownMenu("QuestieTownsfolkMenuFrameTownsfolk", UIParent)
end
local menuTable = QuestieMenu.buildTownsfolkMenu()
tinsert(menuTable, {text= l10n('Cancel'), func=function() end})
LibDropDown:EasyMenu(menuTable, QuestieMenu.menuTowns, "cursor", -80, -15, "MENU", hideDelay)
end
function QuestieMenu:ShowProfessions(hideDelay)
if not Questie.db.profile.townsfolkConfig then
Questie.db.profile.townsfolkConfig = {}
end
if not QuestieMenu.menuProfs then
QuestieMenu.menuProfs = LibDropDown:Create_UIDropDownMenu("QuestieTownsfolkMenuFrameProfs", UIParent)
end
local menuTable = QuestieMenu.buildProfessionMenu()
tinsert(menuTable, {text= l10n('Cancel'), func=function() end})
LibDropDown:EasyMenu(menuTable, QuestieMenu.menuProfs, "cursor", -75, -15, "MENU", hideDelay)
end
function QuestieMenu:ShowVendors(hideDelay)
if not Questie.db.profile.townsfolkConfig then
Questie.db.profile.townsfolkConfig = {}
end
if not QuestieMenu.menuVendors then
QuestieMenu.menuVendors = LibDropDown:Create_UIDropDownMenu("QuestieTownsfolkMenuFrameVendors", UIParent)
end
local menuTable = QuestieMenu.buildVendorMenu()
tinsert(menuTable, {text= l10n('Cancel'), func=function() end})
LibDropDown:EasyMenu(menuTable, QuestieMenu.menuVendors, "cursor", -60, -15, "MENU", hideDelay)
end
+407
View File
@@ -0,0 +1,407 @@
---@class Townsfolk
local Townsfolk = QuestieLoader:CreateModule("Townsfolk")
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
---@type QuestieProfessions
local QuestieProfessions = QuestieLoader:ImportModule("QuestieProfessions")
local _, playerClass = UnitClass("player")
local playerFaction = UnitFactionGroup("player")
local tinsert = tinsert
local sub, bitband, strlen = string.sub, bit.band, string.len
local professionKeys = QuestieProfessions.professionKeys
local function _reformatVendors(lst, existingTable)
local newList = existingTable or {}
for k in pairs(lst) do
tinsert(newList, k)
end
return newList
end
---Uses a table to fetch multiple townfolk types at the same time.
---@param folkTypes table<string, {mask: NpcFlags|integer, requireSubname: boolean, data: NpcId[]}>
local function _PopulateTownsfolkTypes(folkTypes) -- populate the table with all npc ids based on the given bitmask
local count = 0
for id, npcData in pairs(QuestieDB.npcData) do
local flags = npcData[QuestieDB.npcKeys.npcFlags]
for name, folkType in pairs(folkTypes) do
if flags and bitband(flags, folkType.mask) == folkType.mask then
local npcName = npcData[QuestieDB.npcKeys.name]
local subName = npcData[QuestieDB.npcKeys.subName]
if npcName and sub(npcName, 1, 5) ~= "[DND]" then
if (not folkType.requireSubname) or (subName and strlen(subName) > 1) then
folkType.data[#folkType.data+1] = id
end
end
end
end
if count > 700 then -- 700 seems like a good number
count = 0
coroutine.yield()
end
count = count + 1
end
return folkTypes
end
function Townsfolk.Initialize()
--? This datastructure is used in PopulateTownsfolkTypes to fetch multiple townfolk data in the same npc loop cycle
---@type table<string, {mask: NpcFlags|integer, requireSubname: boolean, data: NpcId[]}>
local townsfolkData = {
["Repair"] = {
mask = QuestieDB.npcFlags.REPAIR,
requireSubname = true,
data = {}
},
["Auctioneer"] = {
mask = QuestieDB.npcFlags.AUCTIONEER,
requireSubname = false,
data = {}
},
["Banker"] = {
mask = QuestieDB.npcFlags.BANKER,
requireSubname = true,
data = {}
},
["Battlemaster"] = {
mask = QuestieDB.npcFlags.BATTLEMASTER,
requireSubname = true,
data = {}
},
["Flight Master"] = {
mask = QuestieDB.npcFlags.FLIGHT_MASTER,
requireSubname = false,
data = {}
},
["Innkeeper"] = {
mask = QuestieDB.npcFlags.INNKEEPER,
requireSubname = true,
data = {}
},
["Stable Master"] = { -- Used further down by hunters.
mask = QuestieDB.npcFlags.STABLEMASTER,
requireSubname = false,
data = {}
},
["Spirit Healer"] = {-- Used further down
mask = QuestieDB.npcFlags.SPIRIT_HEALER,
requireSubname = false,
data = {}
}
}
_PopulateTownsfolkTypes(townsfolkData)
local townfolk = {
["Repair"] = townsfolkData["Repair"].data,
["Auctioneer"] = townsfolkData["Auctioneer"].data,
["Banker"] = townsfolkData["Banker"].data,
["Battlemaster"] = townsfolkData["Battlemaster"].data,
["Flight Master"] = townsfolkData["Flight Master"].data,
["Innkeeper"] = townsfolkData["Innkeeper"].data,
["Weapon Master"] = {}, -- populated below
}
---@type table<ProfessionEnum, NpcId[]>
local professionTrainers = {
[professionKeys.FIRST_AID] = {},
[professionKeys.BLACKSMITHING] = {},
[professionKeys.LEATHERWORKING] = {},
[professionKeys.ALCHEMY] = {},
[professionKeys.HERBALISM] = {},
[professionKeys.COOKING] = {
19186, -- Kylene <Barmaid> (this is an edge case)
},
[professionKeys.MINING] = {},
[professionKeys.TAILORING] = {},
[professionKeys.ENGINEERING] = {},
[professionKeys.ENCHANTING] = {},
[professionKeys.FISHING] = {},
[professionKeys.SKINNING] = {}
}
if Questie.IsTBC or Questie.IsWotlk then
professionTrainers[professionKeys.JEWELCRAFTING] = {}
end
if Questie.IsWotlk then
professionTrainers[professionKeys.INSCRIPTION] = {}
end
local count = 0
local validProfessionTrainers = Townsfolk.GetProfessionTrainers()
for i=1, #validProfessionTrainers do
local id = validProfessionTrainers[i]
if QuestieDB.npcData[id] then
local subName = QuestieDB.npcData[id][QuestieDB.npcKeys.subName]
if subName then
if townfolk[subName] then -- weapon master,
tinsert(townfolk[subName], id)
else
for k, professionId in pairs(QuestieProfessions.professionTable) do
if string.match(subName, k) then
tinsert(professionTrainers[professionId], id)
end
end
end
end
end
if count > 10 then -- Yield every 10 iterations, 10 is just a madeup number, is pretty fast.
count = 0
coroutine.yield()
end
count = count + 1
end
-- Fix NPC Gubber Blump (10216) can train fishing profession
tinsert(professionTrainers[professionKeys.FISHING], 10216)
-- Fix NPC Aresella (18991) can train first aid profession
if Questie.IsTBC or Questie.IsWotlk then
tinsert(professionTrainers[professionKeys.FIRST_AID], 18991)
end
if Questie.IsClassic then
-- Vendors selling "Expert First Aid - Under Wraps"
tinsert(professionTrainers[professionKeys.FIRST_AID], 2805)
tinsert(professionTrainers[professionKeys.FIRST_AID], 13476)
end
if Questie.IsWotlk or Questie.IsTBC then
local meetingStones = Townsfolk.GetMeetingStones()
townfolk["Meeting Stones"] = {}
for _, id in pairs(meetingStones) do
tinsert(townfolk["Meeting Stones"], id)
end
end
-- todo: specialized trainer types (leatherworkers, engineers, etc)
local classSpecificTownsfolk = {}
local factionSpecificTownsfolk = {["Horde"] = {}, ["Alliance"] = {}}
local classTrainers = Townsfolk.GetClassTrainers()
for class, trainers in pairs(classTrainers) do
local newTrainers = {}
for _, trainer in pairs(trainers) do
if QuestieDB.npcData[trainer] then
local subName = QuestieDB.npcData[trainer][QuestieDB.npcKeys.subName]
if subName and string.len(subName) > 0 then
tinsert(newTrainers, trainer)
end
end
end
classSpecificTownsfolk[class] = {}
classSpecificTownsfolk[class]["Class Trainer"] = newTrainers
end
-- These are filtered later, when the player class does not match
classSpecificTownsfolk["HUNTER"]["Stable Master"] = townsfolkData["Stable Master"].data
classSpecificTownsfolk["MAGE"]["Portal Trainer"] = {4165,2485,2489,5958,5957,2492,16654,16755,19340,20791,27703,27705}
factionSpecificTownsfolk["Horde"]["Spirit Healer"] = townsfolkData["Spirit Healer"].data
factionSpecificTownsfolk["Alliance"]["Spirit Healer"] = townsfolkData["Spirit Healer"].data
factionSpecificTownsfolk["Horde"]["Mailbox"] = {}
factionSpecificTownsfolk["Alliance"]["Mailbox"] = {}
local mailboxes = Townsfolk.GetMailboxes()
for i=1, #mailboxes do
local id = mailboxes[i]
if QuestieDB.objectData[id] then
local factionID = QuestieDB.objectData[id][QuestieDB.objectKeys.factionID]
if factionID == 0 then
tinsert(factionSpecificTownsfolk["Horde"]["Mailbox"], id)
tinsert(factionSpecificTownsfolk["Alliance"]["Mailbox"], id)
elseif QuestieDB.factionTemplate[factionID] and bitband(QuestieDB.factionTemplate[factionID], 12) == 0 and bitband(QuestieDB.factionTemplate[factionID], 10) == 0 then
tinsert(factionSpecificTownsfolk["Horde"]["Mailbox"], id)
tinsert(factionSpecificTownsfolk["Alliance"]["Mailbox"], id)
elseif QuestieDB.factionTemplate[factionID] and bitband(QuestieDB.factionTemplate[factionID], 12) == 0 then
tinsert(factionSpecificTownsfolk["Horde"]["Mailbox"], id)
elseif QuestieDB.factionTemplate[factionID] and bitband(QuestieDB.factionTemplate[factionID], 10) == 0 then
tinsert(factionSpecificTownsfolk["Alliance"]["Mailbox"], id)
else
tinsert(factionSpecificTownsfolk["Horde"]["Mailbox"], id)
tinsert(factionSpecificTownsfolk["Alliance"]["Mailbox"], id)
end
else
Questie:Debug(Questie.DEBUG_DEVELOP, "Missing mailbox:", tostring(id))
end
end
local petFoodVendorTypes = {["Meat"] = {},["Fish"]={},["Cheese"]={},["Bread"]={},["Fungus"]={},["Fruit"]={},["Raw Meat"]={},["Raw Fish"]={}}
local petFoodIndexes = {"Meat","Fish","Cheese","Bread","Fungus","Fruit","Raw Meat","Raw Fish"}
count = 0
for id, data in pairs(QuestieDB.itemData) do
local foodType = data[QuestieDB.itemKeys.foodType]
if foodType then
tinsert(petFoodVendorTypes[petFoodIndexes[foodType]], id)
end
if count > 300 then -- Yield every 300 iterations, 300 is just a madeup number, is pretty fast.
count = 0
coroutine.yield()
end
count = count + 1
end
coroutine.yield()
--- Set the globals
Questie.db.global.townsfolk = townfolk
Questie.db.global.townsfolkNeedsUpdatedGlobalVendors = true
Questie.db.global.professionTrainers = professionTrainers
Questie.db.global.classSpecificTownsfolk = classSpecificTownsfolk
Questie.db.global.factionSpecificTownsfolk = factionSpecificTownsfolk
Questie.db.global.petFoodVendorTypes = petFoodVendorTypes
end
function Townsfolk.PostBoot() -- post DB boot (use queries here)
if Questie.db.global.townsfolkNeedsUpdatedGlobalVendors then
Questie.db.global.townsfolkNeedsUpdatedGlobalVendors = nil
-- insert item-based profession vendors
_reformatVendors(Townsfolk:PopulateVendors({22012, 21992, 21993, 16084, 16112, 16113, 16085, 19442, 6454, 8547, 23689}), Questie.db.global.professionTrainers[professionKeys.FIRST_AID])
_reformatVendors(Townsfolk:PopulateVendors({27532, 16082, 16083}), Questie.db.global.professionTrainers[professionKeys.FISHING])
_reformatVendors(Townsfolk:PopulateVendors({27736, 16072, 16073}), Questie.db.global.professionTrainers[professionKeys.COOKING])
end
-- item ids for class-specific reagents
local reagents = {
["MAGE"] = {17031, 17032, 17020},
["SHAMAN"] = {17030},
["PRIEST"] = {17029,17028},
["PALADIN"] = {21177,17033},
["WARRIOR"] = {},
["HUNTER"] = {},
["DEATHKNIGHT"] = {37201},
["WARLOCK"] = {5565,16583},
["ROGUE"] = Questie.IsWotlk and {2892} -- All poison vendors sell all ranks of poison, so Rank 1 of one poison is enough here
or {5140,2928,8924,5173,2930,8923},
["DRUID"] = {17034,17026,17035,17021,17038,17036,17037}
}
reagents = reagents[playerClass]
if Questie.IsSoD then
table.insert(reagents, 212160) -- In SoD the Chronoboon Displacer is sold by reagent vendors
end
-- populate vendor IDs from db
if #reagents > 0 then
Questie.db.char.vendorList["Reagents"] = _reformatVendors(Townsfolk:PopulateVendors(reagents))
end
Questie.db.char.vendorList["Trade Goods"] = _reformatVendors(Townsfolk:PopulateVendors({ -- item ids from wowhead for trade goods (temporarily disabled)
14256,12810,13463,8845,8846,4234,3713,8170,14341,4389,3357,2453,13464,
3355,3356,3358,4371,4304,5060,2319,18256,8925,3857,10940,2321,785,4404,2692,
2605,3372,2320,6217,2449,4399,4364,10938,18567,4382,4289,765,3466,3371,2447,2880,
2928,4361,10647,10648,4291,4357,8924,8343,4363,2678,5173,4400,2930,4342,2325,4340,
6261,8923,2324,2604,6260,4378,10290,17194,4341
}))
Questie.db.char.vendorList["Bags"] = _reformatVendors(Townsfolk:PopulateVendors({4496, 4497, 4498, 4499, (Questie.IsTBC or Questie.IsWotlk) and 30744 or nil}))
Questie.db.char.vendorList["Potions"] = _reformatVendors(Townsfolk:PopulateVendors({
118, 858, 929, 1710, 3928, 13446, 18839, (Questie.IsTBC or Questie.IsWotlk) and 22829 or nil, (Questie.IsTBC or Questie.IsWotlk) and 32947 or nil, (Questie.IsWotlk) and 33447 or nil, -- Healing Potions
2455, 3385, 3827, 6149, 13443, 13444, 18841, (Questie.IsTBC or Questie.IsWotlk) and 22832 or nil, (Questie.IsTBC or Questie.IsWotlk) and 32948 or nil, (Questie.IsWotlk) and 33448 or nil, -- Mana Potions
}))
Townsfolk:UpdatePlayerVendors()
end
function Townsfolk:BuildCharacterTownsfolk()
Questie.db.char.townsfolk = {}
Questie.db.char.vendorList = {}
Questie.db.char.townsfolkClass = UnitClass("player")
for key, npcs in pairs(Questie.db.global.factionSpecificTownsfolk[playerFaction]) do
Questie.db.char.townsfolk[key] = npcs
end
for key, npcs in pairs(Questie.db.global.classSpecificTownsfolk[playerClass]) do
Questie.db.char.townsfolk[key] = npcs
end
end
local function _UpdatePetFood() -- call on change pet
Questie.db.char.vendorList["Pet Food"] = {}
-- detect petfood vendors for player's pet
for _, key in pairs({GetStablePetFoodTypes(0)}) do
if Questie.db.global.petFoodVendorTypes[key] then
Townsfolk:PopulateVendors(Questie.db.global.petFoodVendorTypes[key], Questie.db.char.vendorList["Pet Food"], true)
end
end
Questie.db.char.vendorList["Pet Food"] = _reformatVendors(Questie.db.char.vendorList["Pet Food"])
end
local function _UpdateAmmoVendors() -- call on change weapon
Questie.db.char.vendorList["Ammo"] = _reformatVendors(Townsfolk:PopulateVendors({11285,3030,19316,2515,2512,11284,19317,2519,2516,3033,28056,28053,28061,28060}, {}, true))
end
local function _UpdateFoodDrink()
local drink = {159,8766,1179,1708,1645,1205,17404,19300,19299,27860,28399,29395,29454,33042,32453,32455} -- water item ids
local food = { -- food item ids (from wowhead)
8932,4536,8952,19301,13724,8953,3927,11109,8957,4608,4599,4593,4592,117,3770,3771,4539,8950,8948,7228,
2287,4601,422,16166,4537,4602,4542,4594,1707,4540,414,4538,4607,17119,19225,2070,21552,787,4544,18632,16167,4606,16170,
4541,4605,17408,17406,11444,21033,22324,18635,21030,17407,19305,18633,4604,21031,16168,19306,16169,19304,17344,19224,19223,
27857,27854,20857,27858,27856,29448,27855,29451,30355,28486,29450,29393,29394,29449,29452
}
Questie.db.char.vendorList["Food"] = _reformatVendors(Townsfolk:PopulateVendors(food, {}, true))
Questie.db.char.vendorList["Drink"] = _reformatVendors(Townsfolk:PopulateVendors(drink, {}, true))
end
function Townsfolk:UpdatePlayerVendors() -- call on levelup
_UpdateFoodDrink()
if playerClass == "HUNTER" then
_UpdatePetFood()
_UpdateAmmoVendors()
elseif playerClass == "ROGUE" or playerClass == "WARRIOR" then
_UpdateAmmoVendors()
end
end
function Townsfolk:PopulateVendors(itemList, existingTable, restrictLevel)
local factionKey = playerFaction == "Alliance" and "A" or "H"
local tbl = existingTable or {}
-- Create a cache to minimize db calls
local factionCache = {}
local flagCache = {}
local playerLevel = restrictLevel and UnitLevel("Player") or 0
for _, id in pairs(itemList) do
--print(id)
local valid = true
if restrictLevel then
local requiredLevel = QuestieDB.QueryItemSingle(id, "requiredLevel")
valid = (not requiredLevel) or (requiredLevel and requiredLevel <= playerLevel and requiredLevel >= playerLevel - 20)
end
if valid then
local vendors = QuestieDB.QueryItemSingle(id, "vendors")
if vendors then
for i=1, #vendors do
local vendorId = vendors[i]
--print(vendorId)
local friendlyToFaction = factionCache[vendorId] or QuestieDB.QueryNPCSingle(vendorId, "friendlyToFaction")
if not factionCache[vendorId] then
factionCache[vendorId] = friendlyToFaction
end
if (not friendlyToFaction) or friendlyToFaction == "AH" or friendlyToFaction == factionKey then
local flags = flagCache[vendorId] or QuestieDB.QueryNPCSingle(vendorId, "npcFlags")
if not flagCache[vendorId] then
flagCache[vendorId] = flags
end
if bitband(flags, QuestieDB.npcFlags.VENDOR) == QuestieDB.npcFlags.VENDOR then
tbl[vendorId] = true
end
end
end
end
end
end
return tbl
end