local ALName, ALPrivate = ... local _G = _G local AtlasLoot = _G.AtlasLoot local ItemDB = {} AtlasLoot.ItemDB = ItemDB local AL = AtlasLoot.Locales local ContentPhase = AtlasLoot.Data.ContentPhase -- lua local assert, setmetatable, rawset = _G.assert, _G.setmetatable, _G.rawset local type = _G.type local select = _G.select local pairs, unpack = _G.pairs, _G.unpack local match, str_split, format = _G.string.match, _G.string.split, _G.string.format local STRING_TYPE = "string" local BOSS_LINK_FORMAT = "%s:%s:%s" local LEVEL_RANGE_FORMAT = " (|cffff0000%d|r: |cffff8040%d|r - |cff40bf40%d|r)"--" <|cffff0000%d|r |cffff8040%d|r |cffffff00%d|r |cff40bf40%d|r>" local LEVEL_RANGE_FORMAT2 = " (|cffff8040%d|r - |cff40bf40%d|r)" local CONTENT_PHASE_FORMAT = "|cff00FF96".."".."|r" local IsMapsModuleAviable = AtlasLoot.Loader.IsMapsModuleAviable -- Saves all the items ;) ItemDB.Storage = {} -- Functions for a item database ItemDB.Proto = {} ItemDB.ContentProto = {} -- List of content from addons local contentList = {} -- NpcListTable /run print(string.split("-", UnitGUID"target")) -- ItemDB.NpcList = {} -- the metatable ItemDB.contentMt = { __index = ItemDB.ContentProto } -- clean nil boss entrys from DB -- WARNING: This breaks Sources addon local function CleanNilBossEntrys(oldTab) local newItemTab = {} for i = 1, #oldTab.items do if oldTab.items[i] ~= nil then newItemTab[#newItemTab + 1] = oldTab.items[i] end end oldTab.items = newItemTab return oldTab end ItemDB.mt = { __newindex = function(t, k, v) t.__atlaslootdata.contentCount = t.__atlaslootdata.contentCount + 1 v = CleanNilBossEntrys(v) -- debug, re-added to test clearing invalid bosses in vanilla setmetatable(v, ItemDB.contentMt) contentList[t.__atlaslootdata.addonName][t.__atlaslootdata.contentCount] = k contentList[t.__atlaslootdata.addonName][k] = t.__atlaslootdata.contentCount v.__atlaslootdata = t.__atlaslootdata AtlasLoot.Data.AutoSelect:AddInstanceTable(t.__atlaslootdata.addonName, k, v) v.gameVersion = v.gameVersion or t.__atlaslootdata.__gameVersion if not t.__atlaslootdata.gameVersions[v.gameVersion] then t.__atlaslootdata.gameVersions[v.gameVersion] = true end rawset(t, k, v) end } local function GetContentPhaseFromTable(tab) if ALPrivate.IS_BC then return tab.ContentPhaseBC elseif ALPrivate.IS_CLASSIC then return tab.ContentPhase end end --- Adds/Gets the table for a item database -- @param addonName full name of the addon folder (eg "AtlasLoot_MistsofPandaria") -- @param tierID the tier id of the EJ function ItemDB:Add(addonName, tierID, gameVersion) gameVersion = gameVersion or 0 if not ItemDB.Storage[addonName] then ItemDB.Storage[addonName] = {} for k,v in pairs(ItemDB.Proto) do ItemDB.Storage[addonName][k] = v end ItemDB.Storage[addonName].__atlaslootdata = { addonName = addonName, contentCount = 0, tierID = tierID, gameVersions = {} } setmetatable(ItemDB.Storage[addonName], ItemDB.mt) contentList[addonName] = {} end ItemDB.Storage[addonName].__atlaslootdata.gameVersions[gameVersion] = true ItemDB.Storage[addonName].__atlaslootdata.__gameVersion = gameVersion return ItemDB.Storage[addonName] end function ItemDB:Get(addonName) --assert(ItemDB.Storage[addonName], addonName.." not found!") return ItemDB.Storage[addonName] end function ItemDB:GetBossTable(addonName, contentName, boss) if ItemDB.Storage[addonName] and ItemDB.Storage[addonName][contentName] and ItemDB.Storage[addonName][contentName].items[boss] then return ItemDB.Storage[addonName][contentName].items[boss] end end function ItemDB:GetDifficulty(addonName, contentName, boss, dif) dif = dif or 1 local diffs = ItemDB.Storage[addonName]:GetDifficultys() -- first try to get the next lower difficulty -- if nothing found get higher one or try again with lowest difficulty local count = dif local numDiffs = #diffs while true do if count == 0 then count = dif + 1 elseif count > numDiffs then --error(dif.." (dif) not found! -> "..boss) return ItemDB:GetDifficulty(addonName, contentName, boss, 1) end if ItemDB.Storage[addonName][contentName].items[boss][count] then return count end if count > dif then count = count + 1 else count = count - 1 end end end local function getItemTableType(addonName, contentName, boss, dif) local tab = ItemDB.Storage[addonName] local typ = nil if tab[contentName].items[boss][dif].__linkedInfo then local newData = tab[contentName].items[boss][dif].__linkedInfo addonName, contentName, boss, dif = newData[1], newData[2], newData[3], newData[4] tab = ItemDB.Storage[addonName] end if tab[contentName].items[boss][dif].TableType then typ = tab:GetItemTableType(tab[contentName].items[boss][dif].TableType) elseif tab[contentName].items[boss].TableType then typ = tab:GetItemTableType(tab[contentName].items[boss].TableType) elseif tab[contentName].items.TableType then typ = tab:GetItemTableType(tab[contentName].items.TableType) elseif tab[contentName].TableType then typ = tab:GetItemTableType(tab[contentName].TableType) else typ = tab:GetItemTableType(1) end typ.extra = ItemDB.Storage[addonName]:GetAllExtraItemTableType() return typ end local function getItemsFromDiff(curBossTab, iniTab) -- first cache all positions that allready set local tmp = {} for i = 1, #curBossTab do tmp[ curBossTab[i][1] ] = true end -- now copy all items from the other difficulty local bossTab = iniTab[ curBossTab.GetItemsFromDiff ] assert(bossTab, "Diff '"..curBossTab.GetItemsFromDiff.."' not found" ) if bossTab.GetItemsFromDiff then getItemsFromDiff(bossTab, iniTab) end for i = 1, #bossTab do if not tmp[ bossTab[i][1] ] then curBossTab[ #curBossTab+1 ] = bossTab[i] end end curBossTab.GetItemsFromDiff = nil end local currentModuleLoadingInfo = nil local function loadItemsFromOtherModule(moduleLoader, loadString, contentTable, curContentName, curBossID, curAddonName, curDiff) if loadString then currentModuleLoadingInfo = {loadString, contentTable, curContentName, curBossID, curAddonName, curDiff} elseif currentModuleLoadingInfo then loadString, contentTable, curContentName, curBossID, curAddonName, curDiff = currentModuleLoadingInfo[1], currentModuleLoadingInfo[2], currentModuleLoadingInfo[3], currentModuleLoadingInfo[4], currentModuleLoadingInfo[5], currentModuleLoadingInfo[6] else return end local addonName, contentName, bossID, shortDiff = str_split(":", loadString) if (moduleLoader and moduleLoader ~= addonName) then return end bossID = tonumber(bossID) local loadState = AtlasLoot.Loader:LoadModule(addonName, nil, "itemDB") if loadState == "InCombat" or loadState == "DISABLED" or loadState == "MISSING" then AtlasLoot.Loader:LoadModule(addonName, loadItemsFromOtherModule, "itemDB") return addonName, loadState elseif contentName and ItemDB.Storage[addonName] then -- get name of diff local newDif = ItemDB.Storage[curAddonName]:GetDifficultyUName(curDiff) -- get new diff ID if newDif or shortDiff then newDif = ItemDB.Storage[addonName]:GetDifficultyByName(shortDiff or newDif) else newDif = ItemDB.Storage[curAddonName]:GetDifficultyUName(curDiff) or ItemDB.Storage[curAddonName]:GetDifficultyName(curDiff) newDif = ItemDB.Storage[addonName]:GetDifficultyByName(newDif) end --contentTable[curDiff] = setmetatable({__linkedInfo = {addonName, contentName, bossID, newDif}}, { __index =ItemDB.Storage[addonName][contentName].items[bossID][newDif]}) contentTable[curContentName].items[curBossID][curDiff] = ItemDB.Storage[addonName][contentName].items[bossID][newDif] if not contentTable[curContentName].items[curBossID][curDiff] then error("Linked Loottable not found contentName:"..(curContentName or "nil").." bossID:"..(curBossID or "nil").." dif:"..(curDiff or "nil")) end contentTable[curContentName].items[curBossID][curDiff].__linkedInfo = {addonName, contentName, bossID, newDif} currentModuleLoadingInfo = nil elseif ItemDB.Storage[addonName] then -- getBossID by name local bossID = contentTable[curContentName]:GetNameForItemTable(curBossID) for i=1, #ItemDB.Storage[addonName][curContentName].items do --print(ItemDB.Storage[addonName][curContentName]:GetNameForItemTable(i), bossID) if ItemDB.Storage[addonName][curContentName]:GetNameForItemTable(i) == bossID then bossID = i break end end if type(bossID) ~= "number" then error("No boss found for ID:"..curBossID.." name:"..bossID.." module:"..addonName.." contentName:"..curContentName) end loadString = format(BOSS_LINK_FORMAT, addonName, curContentName, bossID) currentModuleLoadingInfo = { loadString, contentTable, curContentName, curBossID, curAddonName, curDiff } loadItemsFromOtherModule() end end function ItemDB:GetItemTable(addonName, contentName, boss, dif) assert(addonName and ItemDB.Storage[addonName], addonName.." (addonName) not found!") assert(contentName and ItemDB.Storage[addonName][contentName], contentName.." (contentName) not found!") assert(boss and ItemDB.Storage[addonName][contentName].items[boss], boss.." (boss) not found!") local addonNameRETVALUE, addon if ItemDB.Storage[addonName][contentName].items[boss].link then return ItemDB:GetItemTable(ItemDB.Storage[addonName][contentName].items[boss].link[1], ItemDB.Storage[addonName][contentName].items[boss].link[2], ItemDB.Storage[addonName][contentName].items[boss].link[3], dif) end if type(dif) == STRING_TYPE then dif = ItemDB.Storage[addonName]:GetDifficultyByName(dif) end if not ItemDB.Storage[addonName][contentName].items[boss][dif] then dif = ItemDB:GetDifficulty(addonName, contentName, boss, dif) end currentModuleLoadingInfo = nil if ItemDB.Storage[addonName][contentName].items[boss][dif] then local bossDiff = ItemDB.Storage[addonName][contentName].items[boss][dif] -- get the items table from a string if type(bossDiff) == STRING_TYPE then local notLoadedAddonName, reason = loadItemsFromOtherModule(nil, bossDiff, ItemDB.Storage[addonName], contentName, boss, addonName, dif) if notLoadedAddonName then return notLoadedAddonName, reason, ItemDB.Storage[addonName]:GetDifficultyData(dif) end -- get the items table from a other difficulty elseif type(bossDiff) == "number" then ItemDB.Storage[addonName][contentName].items[boss][dif] = ItemDB.Storage[addonName][contentName].items[boss][ bossDiff ] -- get items from another difficulty elseif bossDiff.GetItemsFromDiff then getItemsFromDiff(bossDiff, ItemDB.Storage[addonName][contentName].items[boss]) end end --assert(dif and ItemDB.Storage[addonName][contentName].items[boss][dif], dif.." (dif) not found!") return ItemDB.Storage[addonName][contentName].items[boss][dif], getItemTableType(addonName, contentName, boss, dif), ItemDB.Storage[addonName]:GetDifficultyData(dif) end function ItemDB:GetModuleList(addonName) assert(contentList[addonName], addonName.." not found.") return contentList[addonName] end -- iniName, bossName function ItemDB:GetNameData_UNSAFE(addonName, contentName, boss, diff) if not ItemDB.Storage[addonName] or not ItemDB.Storage[addonName][contentName] then return end return ItemDB.Storage[addonName][contentName]:GetName(true), ItemDB.Storage[addonName][contentName]:GetNameForItemTable(boss, true), ItemDB.Storage[addonName]:GetDifficultyName(diff) end function ItemDB:GetNpcID_UNSAFE(addonName, contentName, boss) if not ItemDB.Storage[addonName] or not ItemDB.Storage[addonName][contentName] or not ItemDB.Storage[addonName][contentName].items[boss] then return end return ItemDB.Storage[addonName][contentName].items[boss].npcID end function ItemDB:GetCorrespondingField(addonName, contentName, newGameVersion) if not addonName or not contentName or not newGameVersion then return end if ItemDB.Storage[addonName] and ItemDB.Storage[addonName][contentName] and ItemDB.Storage[addonName][contentName].CorrespondingFields then return ItemDB.Storage[addonName][contentName].CorrespondingFields[newGameVersion] end end -- ################################################## -- TableProto -- ################################################## --[[ like Heroic, Normal, 25Man ]] local difficultys = {} function ItemDB.Proto:AddDifficulty(dif, uniqueName, difficultyID, tierID, textIsHiddenInHeader) assert(dif, "No 'dif' given.") if dif and AtlasLoot.DIFFICULTY[dif] then local difTab = AtlasLoot.DIFFICULTY[dif] dif = difTab.loc uniqueName = difTab.short difficultyID = difTab.id end if not difficultys[self.__atlaslootdata.addonName] then difficultys[self.__atlaslootdata.addonName] = { counter = 0, names = {}, uniqueNames = {}, data = {} } end local diffTab = difficultys[self.__atlaslootdata.addonName] if not diffTab.uniqueNames[uniqueName] or not diffTab.names[dif] then diffTab.counter = diffTab.counter + 1 diffTab.names[dif] = diffTab.counter if uniqueName then diffTab.uniqueNames[uniqueName] = diffTab.counter end diffTab.data[diffTab.counter] = { name = dif, uniqueName = uniqueName, difficultyID = difficultyID, tierID = tierID or ItemDB.Storage[self.__atlaslootdata.addonName].__atlaslootdata.tierID, textIsHidden = textIsHiddenInHeader, } end return diffTab.uniqueNames[uniqueName] or diffTab.names[dif] end function ItemDB.Proto:GetTierID(dif) return (dif and difficultys[self.__atlaslootdata.addonName].data[dif]) and difficultys[self.__atlaslootdata.addonName].data[dif].tierID or nil end function ItemDB.Proto:GetDifficultyByName(dif) return dif and ( difficultys[self.__atlaslootdata.addonName].uniqueNames[dif] or difficultys[self.__atlaslootdata.addonName].names[dif] ) or nil end function ItemDB.Proto:GetDifficultyName(dif) return (dif and difficultys[self.__atlaslootdata.addonName].data[dif]) and difficultys[self.__atlaslootdata.addonName].data[dif].name or nil end function ItemDB.Proto:GetDifficultyByID(id) for i = 1, #difficultys[self.__atlaslootdata.addonName].data do if difficultys[self.__atlaslootdata.addonName].data[i].difficultyID == id then return i end end end function ItemDB.Proto:GetDifficultyUName(dif) return (dif and difficultys[self.__atlaslootdata.addonName].data[dif]) and difficultys[self.__atlaslootdata.addonName].data[dif].uniqueName or nil end function ItemDB.Proto:GetDifficultyID(dif) return (dif and difficultys[self.__atlaslootdata.addonName].data[dif]) and difficultys[self.__atlaslootdata.addonName].data[dif].difficultyID or nil end function ItemDB.Proto:GetDifficultyData(difID) return difficultys[self.__atlaslootdata.addonName].data[difID] end function ItemDB.Proto:GetDifficultys() return difficultys[self.__atlaslootdata.addonName].data end --[[ New box for the dropdown menus ]] local content_types = {} function ItemDB.Proto:AddContentType(typ, color) assert(typ, "No 'typ' given.") if not content_types[self.__atlaslootdata.addonName] then content_types[self.__atlaslootdata.addonName] = {} end if not content_types[self.__atlaslootdata.addonName][typ] then content_types[self.__atlaslootdata.addonName][ #content_types[self.__atlaslootdata.addonName] + 1 ] = {typ, color or ATLASLOOT_UNKNOWN_COLOR} --table.insert(content_types[self.__atlaslootdata.addonName], {typ, color or ATLASLOOT_UNKNOWN_COLOR}) content_types[self.__atlaslootdata.addonName][typ] = #content_types[self.__atlaslootdata.addonName] end return content_types[self.__atlaslootdata.addonName][typ] end function ItemDB.Proto:GetContentTypes() return content_types[self.__atlaslootdata.addonName] end local iTable_types = {} function ItemDB.Proto:AddItemTableType(...) local tab = AtlasLoot.Button:CreateFormatTable({...}) if not iTable_types[self.__atlaslootdata.addonName] then iTable_types[self.__atlaslootdata.addonName] = {} end iTable_types[self.__atlaslootdata.addonName][#iTable_types[self.__atlaslootdata.addonName]+1] = tab return #iTable_types[self.__atlaslootdata.addonName] end function ItemDB.Proto:GetItemTableType(index) return iTable_types[self.__atlaslootdata.addonName] and iTable_types[self.__atlaslootdata.addonName][index] or nil end local extra_iTable_types = {} function ItemDB.Proto:AddExtraItemTableType(typ) if not extra_iTable_types[self.__atlaslootdata.addonName] then extra_iTable_types[self.__atlaslootdata.addonName] = {} end for i = 1, #extra_iTable_types[self.__atlaslootdata.addonName] do if extra_iTable_types[self.__atlaslootdata.addonName][i] == type then return i + 100 end end extra_iTable_types[self.__atlaslootdata.addonName][#extra_iTable_types[self.__atlaslootdata.addonName]+1] = typ return #extra_iTable_types[self.__atlaslootdata.addonName] + 100 end function ItemDB.Proto:GetExtraItemTableType(index) return extra_iTable_types[self.__atlaslootdata.addonName] and ( extra_iTable_types[self.__atlaslootdata.addonName][index] or extra_iTable_types[self.__atlaslootdata.addonName][index+100] ) or nil end function ItemDB.Proto:GetAllExtraItemTableType(index) return extra_iTable_types[self.__atlaslootdata.addonName] end function ItemDB.Proto:CheckForLink(dataID, boss, load) assert(dataID, self[dataID], "dataID not found - "..dataID) assert(boss, self[dataID].items[boss], "boss not found - "..boss) if self[dataID].items[boss] and self[dataID].items[boss].link then local link = self[dataID].items[boss].link if AtlasLoot.Loader:IsModuleLoaded(link[1]) then assert(ItemDB.Storage[link[1]], "module "..link[1].." not found") assert(ItemDB.Storage[link[1]][link[2]], "dataID "..link[2].." not found in module "..link[1]) assert(ItemDB.Storage[link[1]][link[2]].items[link[3]], "boss "..link[3].." not found in dataID "..link[2]..", module "..link[1]) self[dataID].items[boss] = ItemDB.Storage[link[1]][link[2]].items[link[3]] elseif load then local combat = AtlasLoot.Loader:LoadModule(self[dataID].items[boss].link[1], function() self:CheckForLink(dataID, boss) end, true) if combat then print"InCombat :(" end end end end function ItemDB.Proto:GetDifficulty(dataID, boss, dif) return ItemDB:GetDifficulty(self.__atlaslootdata.addonName, dataID, boss, dif) end function ItemDB.Proto:IsGameVersionAviable(version) return self.__atlaslootdata.gameVersions[version] and true or false end function ItemDB.Proto:GetAviableGameVersion(version) if self.__atlaslootdata.gameVersions[version] then return version elseif self.__atlaslootdata.gameVersions[AtlasLoot:GetGameVersion()] then return AtlasLoot:GetGameVersion() else return self.__atlaslootdata.__gameVersion end end -- ################################################## -- ContentProto -- ################################################## local ATLAS_TEXTURE, PATH_TEXTURE = "|A:%s:0:0|a ","|T%s:0|t " local SpecialMobList = { rare = format(ATLAS_TEXTURE, "nameplates-icon-elite-silver"), elite = format(ATLAS_TEXTURE, "nameplates-icon-elite-gold"), quest = format(ATLAS_TEXTURE, "QuestNormal"), questTurnIn = format(ATLAS_TEXTURE, "QuestTurnin"), boss = format(PATH_TEXTURE, "Interface\\TargetingFrame\\UI-TargetingFrame-Skull"), vendor = format(ATLAS_TEXTURE, "auctioneer"), summon = format(ATLAS_TEXTURE, "poi-rift1"), scourgeInvasion = format(PATH_TEXTURE, "133446"), } --- Get the content Type -- @return ContentName, ContentIndex function ItemDB.ContentProto:GetContentType() if not self.ContentType then error("ContentType not set for <"..self.__atlaslootdata.addonName.." / "..self.name..">") return nil end assert(content_types[self.__atlaslootdata.addonName][self.ContentType], self.ContentType.." not found!") return content_types[self.__atlaslootdata.addonName][self.ContentType][1], self.ContentType, content_types[self.__atlaslootdata.addonName][self.ContentType][2] end function ItemDB.ContentProto:GetName(raw) if self.AreaID and not self.MapID then self.MapID = self.AreaID end local name local addEnd = "" if not raw then if AtlasLoot.db.showLvlRange and self.LevelRange then if AtlasLoot.db.showMinEnterLvl then addEnd = format(LEVEL_RANGE_FORMAT, self.LevelRange[1] or 0, self.LevelRange[2] or 0, self.LevelRange[3] or 0 ) else addEnd = format(LEVEL_RANGE_FORMAT2, self.LevelRange[2] or 0, self.LevelRange[3] or 0 ) end end if AtlasLoot.db.ContentPhase.enableOnLootTable and not ContentPhase:IsActive(GetContentPhaseFromTable(self), self.gameVersion) then addEnd = addEnd.." "..format(CONTENT_PHASE_FORMAT, GetContentPhaseFromTable(self)) end end if self.name then name = self.name..addEnd elseif self.MapID then name = C_Map.GetAreaInfo(self.MapID)..addEnd or "MapID:"..self.MapID elseif self.FactionID then name = AtlasLoot:Faction_GetFactionName(self.FactionID)..addEnd elseif self.AchievementID then name = select(2, GetAchievementInfo(self.AchievementID)) else name = UNKNOWN end if self.nameFormat then name = format(self.nameFormat, name) end if self.NameColor and not raw and AtlasLoot.db.enableColorsInNames then name = format(self.NameColor, name) end return name end function ItemDB.ContentProto:GetInfo() if self.info then return self.info elseif self.FactionID then return select(2, GetFactionInfoByID(self.FactionID)) end end function ItemDB.ContentProto:GetNameForItemTable(index, raw) assert(self.items, "items table not found.") if raw and not self.items[index] then return end assert(index and self.items[index], "index not found.") index = self.items[index] local name local addStart, addEnd = "", "" if not raw then if AtlasLoot.db.ContentPhase.enableOnLootTable and not ContentPhase:IsActive(GetContentPhaseFromTable(index), index.gameVersion or self.gameVersion) then addEnd = addEnd.." "..format(CONTENT_PHASE_FORMAT, GetContentPhaseFromTable(index)) end if IsMapsModuleAviable(self.AtlasModule or index.AtlasModule) and index.AtlasMapBossID then addStart = addStart.."|cffffffff"..index.AtlasMapBossID..")|r " end if AtlasLoot.db.enableBossLevel and index.Level then if type(index.Level) == "table" then addStart = addStart.."|cff808080<"..index.Level[1].." - "..index.Level[2]..">|r " elseif index.Level == 999 then addStart = addStart..SpecialMobList.boss else addStart = addStart.."|cff808080<"..(index.Level == 999 and SpecialMobList.boss or index.Level)..">|r " end end if index.specialType and SpecialMobList[index.specialType] then addStart = addStart..SpecialMobList[index.specialType] end end if index.name then name = addStart..index.name..addEnd elseif index.FactionID then name = addStart..GetFactionInfoByID(index.FactionID)..addEnd elseif index.MapID then name = C_Map.GetAreaInfo(index.MapID)..addEnd or "MapID:"..index.MapID elseif index.AchievementID then name = select(2, GetAchievementInfo(index.AchievementID)) else name = UNKNOWN end if index.nameFormat then name = format(index.nameFormat, name) end if index.NameColor and not raw and AtlasLoot.db.enableColorsInNames then name = format(index.NameColor, name) end return name end