local PetJournalEnhanced = LibStub("AceAddon-3.0"):GetAddon("PetJournalEnhanced") local Sorting = PetJournalEnhanced:NewModule("Sorting", "AceEvent-3.0") local BreedInfo = LibStub("LibPetBreedInfo-1.0") local BREED_HIGH_CONFIDENCE = 2 local MAX_BALANCED = 1 local MAX_SPEED = 2 local MAX_STAMINA = 3 local MAX_ATTACK = 4 local ASCENDING = 1 local DESCENDING = 2 local USER = nil -- GLOBALS: C_PetJournal PetJournal LE_PET_JOURNAL_FILTER_COLLECTED LE_PET_JOURNAL_FILTER_NOT_COLLECTED PetJournalSearchBox local petMapping = {} local SetPet, GetPet do local petStore = {} function SetPet(pet) if pet.isOwned then petStore[pet.petID] = pet else petStore[pet.speciesID] = pet end end function Sorting:GetPet(petID) return petStore[petID] end end local GetSortFunctions do local function GetCompareFunc(var,func,direction) return function(a,b) local order = Sorting.db.global.sorting.order local avar = a[var] local bvar = b[var] if type(avar) == "boolean" then avar = avar and 1 or 0 bvar = bvar and 1 or 0 end if avar == bvar and func then return func(a,b) end if direction then if direction == ASCENDING then return avar < bvar elseif direction == DESCENDING then return avar > bvar end elseif order == ASCENDING then return avar < bvar elseif order == DESCENDING then --if not avar or not bvar then print(avar,bvar,var) print(a.name,b.name) end return avar > bvar end end end local function ComposeSortFunction(...) local args = {...} local func = nil for i=#args,1,-2 do local name = args[i-1]; local direction = args[i] func = GetCompareFunc(name, func, direction) end return func end local sortFunctions = { ComposeSortFunction("isOwned", DESCENDING, "level", USER, "name", ASCENDING), ComposeSortFunction("isOwned", DESCENDING, "name", USER, "level", DESCENDING), ComposeSortFunction("isOwned", DESCENDING, "petType", USER, "level", DESCENDING, "name", ASCENDING), ComposeSortFunction("isOwned", DESCENDING, "rarity", USER, "level", DESCENDING, "name", ASCENDING), ComposeSortFunction("isOwned", DESCENDING, "maxStat", USER, "level", DESCENDING, "name", ASCENDING), ComposeSortFunction("isOwned", DESCENDING, "breed", USER, "level", DESCENDING, "name", ASCENDING), } local sortFunctionsFavoritesFirst = {} for i=1,#sortFunctions do sortFunctionsFavoritesFirst[i] = GetCompareFunc("favorite", sortFunctions[i], DESCENDING) end function GetSortFunctions() if Sorting.sorting.favoritesFirst then return sortFunctionsFavoritesFirst else return sortFunctions end end end function Sorting:OnInitialize() -- self.ZoneFiltering = PetJournalEnhanced:GetModule("ZoneFiltering") self.callbacks = LibStub("CallbackHandler-1.0"):New(self) local defaults = { global = { filtering = { rarity = { [1] = true, [2] = true, [3] = true, [4] = true }, breed = { [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true, [12] = true, }, specialization = { [1] = true, [2] = true, [3] = true, [4] = true }, level = { [1] = true, [2] = true, [3] = true, [4] = true, [5] = true }, quantity = { [1] = true, [2] = true, [3] = true }, currentZone = false, canBattle = true, cantBattle = true, unknownZone = true, cantTrade = true, canTrade = true, abilityType = { [1] = true, [2] = true, [3] = true, [3] = true, [4] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true }, hiddenSpecies = false, favroritesOnly = false, }, sorting = { selection = 1, order = ASCENDING, favoritesFirst = false, }, hiddenSpecies = {}, } } local db = LibStub("AceDB-3.0"):New("PetJournalEnhancedDB", {}, true) local name = self:GetName() self.db = db:RegisterNamespace(name, defaults) self.db.global.filtering.unknownZone = true --always reset to true self.filtering = self.db.global.filtering self.sorting = self.db.global.sorting self:RegisterEvent("ZONE_CHANGED_NEW_AREA") self:RegisterEvent("PET_JOURNAL_PET_DELETED") local LibPetJournal = LibStub("LibPetJournal-2.0") LibPetJournal.RegisterCallback(self, "PostPetListUpdated", "ScanPets") PetJournalSearchBox:HookScript("OnTextChanged", function() Sorting:SortPets() end) hooksecurefunc(C_PetJournal,"SetFavorite", function() Sorting:UpdatePets() end) end function Sorting:IsSpeciesHidden(speciesID) --print(speciesID) if self.db.global.hiddenSpecies[speciesID] then return true end end function Sorting:SetSpeciesHidden(speciesID, hidden) self.db.global.hiddenSpecies[speciesID] = hidden end function Sorting:ResetSpeciesHidden() self.db.global.hiddenSpecies = {} end function Sorting:GetPetByIndex(index) return petMapping[index] end function Sorting:GetNumPets() return #petMapping end function Sorting:Reset() C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_COLLECTED, true) C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_NOT_COLLECTED, true) C_PetJournal.SetAllPetTypesChecked(true) C_PetJournal.SetAllPetSourcesChecked(true) self.ZoneFiltering:SetAllFiltered(true) local filtering = self.db.global.filtering local sorting = self.db.global.sorting for i=1,#filtering.rarity do filtering.rarity[i] = true end for i=1,#filtering.abilityType do filtering.abilityType[i] = true end for i=1,#filtering.level do filtering.level[i] = true end for i=1,#filtering.specialization do filtering.specialization[i] = true end for i=1,#filtering.quantity do filtering.quantity[i] = true end for i=1,#filtering.breed do filtering.breed[i] = true end filtering.currentZone = false filtering.canBattle = true filtering.cantBattle = true filtering.unknownZone = true filtering.canTrade = true filtering.cantTrade = true filtering.favoritesOnly = false sorting.selection = 2 sorting.order = ASCENDING sorting.favoritesFirst = false self:SortPets() end function Sorting:ScanPets() self:SortPets() end function Sorting:ZONE_CHANGED_NEW_AREA() if self.filtering.currentZone then self:SortPets() end end function Sorting:PETJOURNAL_ENHANCED_OPTIONS_UPDATE() self:SortPets() end function Sorting:PET_JOURNAL_PET_DELETED() self:SortPets() end do --Return highest stat for a pet. Check constants for return values. local function GetMaxStat(maxHealth, attack, speed) maxHealth = tonumber(maxHealth) local stamina = tonumber(math.floor((maxHealth -100)/5)) attack = tonumber(attack) speed = tonumber(speed) if attack > speed and attack > stamina then return MAX_ATTACK elseif speed > attack and speed > stamina then return MAX_SPEED elseif stamina > attack and stamina > speed then return MAX_STAMINA end return MAX_BALANCED end local function CreatePet(petID, speciesID, isOwned, name, petType, canBattle, tradable, rarity, level, sourceText, maxHealth, attack, speed) local pet = {} pet.speciesID = speciesID pet.petID = petID pet.isOwned = isOwned pet.name = name pet.petType = petType pet.canBattle = canBattle pet.tradable = tradable pet.rarity = rarity or 0 pet.maxStat = 0 pet.abilityFilter = 0 pet.breed = 0 pet.breedConfidence = 0 pet.level = level or 0 pet.sourceText = sourceText if isOwned and canBattle and maxHealth and attack and speed then pet.maxStat = GetMaxStat(maxHealth, attack, speed) local breed, breedConfidence = BreedInfo:GetBreedByPetID(petID) if breed then pet.breed, pet.breedConfidence = breed, breedConfidence end end if canBattle then local abilityIDs = C_PetJournal.GetPetAbilityList(pet.speciesID); for j=1, #abilityIDs do local _, _, abilityType = C_PetJournal.GetPetAbilityInfo(abilityIDs[j]); pet.abilityFilter = bit.bor(pet.abilityFilter, 2^abilityType); end else pet.level = 0 pet.rarity = 0 pet.petType = 0 end return pet end local function UpdatePet(petID, pet, level, favorite, index, rarity) pet.owned = C_PetJournal.GetNumCollectedInfo(pet.speciesID) pet.favorite = favorite pet.index = index pet.petID = petID if pet.canBattle and pet.isOwned then if pet.breedConfidence < BREED_HIGH_CONFIDENCE and level > pet.level then local breed, breedConfidence = BreedInfo:GetBreedByPetID(petID) if breed then pet.breed, pet.breedConfidence = breed, breedConfidence end end pet.level = level pet.rarity = rarity end end function Sorting:SortPets() --local start = debugprofilestop() local numPets = C_PetJournal.GetNumPets(PetJournal.isWild) local ZoneFiltering = self.ZoneFiltering wipe(petMapping) local activeFilters = self:GetActiveFilters() for i=1,numPets do local petID, speciesID, isOwned, _, level, favorite, _, name, _, petType, _, sourceText, _, _, canBattle, tradable, _ = C_PetJournal.GetPetInfoByIndex(i, PetJournal.isWild) local maxHealth, attack, speed, rarity if petID then _, maxHealth, attack, speed, rarity = C_PetJournal.GetPetStats(petID) end local id = nil if isOwned then id = petID else id = speciesID end local pet = self:GetPet(id) if not pet then pet = CreatePet(petID, speciesID, isOwned, name, petType, canBattle, tradable, rarity, level, sourceText, maxHealth, attack, speed) SetPet(pet) end UpdatePet(petID, pet, level, favorite, i, rarity) --filter pet local exclude = false for i=1, #activeFilters do exclude = activeFilters[i]:exclude(pet) if exclude then break end end if not exclude then table.insert(petMapping,pet) end end local sortFuncs = GetSortFunctions() local sortFunc = sortFuncs[self.sorting.selection] table.sort(petMapping, sortFunc) self.callbacks:Fire("PETS_SORTED") --local stop = debugprofilestop() - start --print(stop) end Sorting.UpdatePets = Sorting.SortPets end do local function BitwiseAndTable(tbl) local truth = true; for k,v in pairs(tbl) do truth = truth and v end return truth end local function InitializeFilters() local filtering = Sorting.filtering local sorting = Sorting.sorting local ZoneFiltering = Sorting.ZoneFiltering Sorting.filters = { --[[ { initialize = function(self) end, enabled = function(self) return false end, exclude = function(self,pet) return false end }, ]] { name = "can battle", enabled = function() return not filtering.canBattle end, exclude = function(self, pet) return pet.canBattle end }, { name = "favorites only", enabled = function() return filtering.favoritesOnly end, exclude = function(self, pet) if pet.isOwned and not pet.favorite then return true end return false end }, { name = "cant battle", enabled = function() return not filtering.cantBattle end, exclude = function(self, pet) return not pet.canBattle end }, { name = "can trade", enabled = function() return not filtering.canTrade end, exclude = function(self, pet) return pet.tradable end }, { name = "cant trade", enabled = function() return not filtering.cantTrade end, exclude = function(self, pet) return not pet.tradable end }, { name = "rarity", enabled = function() return not BitwiseAndTable(filtering.rarity) end, exclude = function(self, pet) return pet.rarity and pet.rarity > 0 and not filtering.rarity[pet.rarity] end }, { name = "quantity", enabled = function() return not BitwiseAndTable(filtering.quantity) end, exclude = function(self, pet) return pet.owned > 0 and not filtering.quantity[pet.owned] end }, { name = "pet spec", enabled = function() return not BitwiseAndTable(filtering.specialization) end, exclude = function(self, pet) return pet.maxStat > 0 and not filtering.specialization[pet.maxStat] end }, -- { -- name = "current zone", -- initialize = function(self) self.currentZoneId = C_Map.GetBestMapForUnit("player") end, -- enabled = function() return filtering.currentZone end, -- exclude = function(self, pet) -- local zones = ZoneFiltering:GetZonesBySpeciesID(pet.speciesID) -- return (zones == nil or not zones[self.currentZoneId]) -- end -- }, -- { -- name = "unknown zone", -- enabled = function() return not filtering.unknownZone end, -- exclude = function(self, pet) return ZoneFiltering:GetZonesBySpeciesID(pet.speciesID) == nil end -- }, { name = "breed", enabled = function() return not BitwiseAndTable(filtering.breed) end, exclude = function(self, pet) return pet.breed > 0 and not filtering.breed[pet.breed] end }, --[[ { name = "level", enabled = function() return not BitwiseAndTable(filtering.level) end, exclude = function(self,pet) return pet.level and (not filtering.level[1] and pet.level == 1) or (not filtering.level[2] and pet.level <= 10 and pet.level >= 2) or (not filtering.level[3] and pet.level <= 20 and pet.level >= 11) or (not filtering.level[4] and pet.level <= 24 and pet.level >= 20) or (not filtering.level[5] and pet.level == 25) end }, ]] { name = "ability", abilityFilter = 0, initialize = function(self) self.abilityFilter = 0 for i=1,#filtering.abilityType do if filtering.abilityType[i] then self.abilityFilter = bit.bor(self.abilityFilter, 2^i); end end end, enabled = function() return not BitwiseAndTable(filtering.abilityType) end, exclude = function(self, pet) return pet.canBattle and bit.band(self.abilityFilter, pet.abilityFilter) == 0 end, }, -- { -- name = "zone selection", -- enabled = function() return not ZoneFiltering:IsEveryZoneEnabled() end, -- exclude = function(self, pet) -- local zones = ZoneFiltering:GetZonesBySpeciesID(pet.speciesID) -- if zones then -- local filtered = false -- for k,v in pairs(zones) do -- filtered = filtered or ZoneFiltering:GetFiltered(k) -- end -- return not filtered -- end -- return false -- end -- }, { name = "hidden species", enabled = function() return not filtering.hiddenSpecies end, exclude = function(self, pet) return Sorting:IsSpeciesHidden(pet.speciesID) end }, } local levelRanges = {{1,1}, {2,10}, {11,20}, {21,24}, {25,25}} for i=1,#levelRanges do local filter = { name = "level "..levelRanges[i][1].."-"..levelRanges[i][2], enabled = function() return not filtering.level[i] end, exclude = function(self, pet) return pet.level and pet.level >= levelRanges[i][1] and pet.level <= levelRanges[i][2] end } table.insert(Sorting.filters, filter) end end function Sorting:GetActiveFilters() if not self.filters then InitializeFilters() end local activeFilters = {} for i=1, #self.filters do if self.filters[i].initialize then self.filters[i]:initialize() end if self.filters[i].enabled() then --print(self.filters[i].name or "Error: no filter name") table.insert(activeFilters, self.filters[i]) end end return activeFilters end end