local _,rematch = ... local settings = rematch.settings rematch.roster = {} --[[ In past versions, rematch.roster was a starting point for any type of changes to the pet list, from adding new pets to a pet changing rarity or gaining a level, to even the pet list changing filters. This new roster is treated as a data source only. It will watch for pets being added and removed and keep a list of all current pets and species, but that's it. The main list of pets is a list of either "BattlePet-0-etc" petID for owned pets, or a numeric speciesID for uncollected pets. Another list is of distinct speciesIDs. These lists should be treated as a data source and are not intended to be altered outside this module. The lists can be read by one of three iterator functions: for petID in rematch.roster:AllPets() do -- do something -- end -- loop over all petIDs (and unowned speciesIDs) for petID in rematch.roster:AllOwnedPets() do -- do something -- end -- loop over all owned petIDs for petID in rematch.roster:AllSpecies() do -- do something -- end -- loop over all distinct speciesIDs The number of pets can be read by one of the following: rematch.roster:GetNumPets() -- returns the total number of pets, both owned (including duplicates) and uncollected rematch.roster:GetNumSpecies() -- returns the number of distinct speciesIDs in the journal, owned or not rematch.roster:GetNumOwned() -- returns the number of pets owned by the player, including duplicates rematch.roster:GetNumUniqueOwned() -- returns the number of distinct pets owned by the player (ignoring duplicates) ]] -- master list of pets, either a petID ("BattlePet-0-etc") for owned pets, or a speciesID (42) for uncollected pets local allPets = {} -- list of all speciesIDs in the journal local allSpecies = {} -- indexed by speciesID, an ordered list of owned petIDs for the speciesID local speciesPetIDs = {} -- place to backup journal settings when we update the roster local journalBackup = { search="", collected=nil, notCollected=nil, types={}, sources={} } local isUpdatingRoster -- true while roster is updating (while expanding/collapsing journal and clears a frame after) local uniqueOwnedCount = 0 -- number of distinct pets owned by the player local waitingForFirstUpdate = true -- becomes nil after first update, to fire REMATCH_PETS_LOADED rematch.events:Register(rematch.roster,"PLAYER_LOGIN",function(self) rematch.events:Register(rematch.roster,"NEW_PET_ADDED",rematch.roster.NEW_PET_ADDED) rematch.events:Register(rematch.roster,"PET_JOURNAL_PET_DELETED",rematch.roster.PET_JOURNAL_PET_DELETED) rematch.events:Register(rematch.roster,"UPDATE_SUMMONPETS_ACTION",rematch.roster.UPDATE_SUMMONPETS_ACTION) -- releasing a pet doesn't trigger any event except a PET_JOURNAL_LIST_UPDATE, so firing a fake PET_JOURNAL_PET_DELETD when it happens hooksecurefunc(C_PetJournal,"ReleasePetByID",function(petID) rematch.events:Fire("PET_JOURNAL_PET_DELETED",petID) end) end) -- experimenting: is this a reliable event that fires on login after pets are ready? function rematch.roster:UPDATE_SUMMONPETS_ACTION(...) rematch.timer:Start(0,rematch.roster.Update) -- update allPets and allSpecies rematch.events:Unregister(rematch.roster,"UPDATE_SUMMONPETS_ACTION") -- it served its purpose, now rely on add/delete end -- fires when a new pet is added to the journal function rematch.roster:NEW_PET_ADDED(...) if settings.StickyNewPets then rematch.sort:AddStickiedPetID(...) end rematch.timer:Start(0,rematch.roster.Update) -- update allPets and allSpecies end -- fires when a pet is removed from the journal function rematch.roster:PET_JOURNAL_PET_DELETED(...) rematch.timer:Start(0,rematch.roster.Update) -- update allPets and allSpecies end function rematch.roster:PET_JOURNAL_LIST_UPDATE(...) rematch.events:Unregister(rematch.roster,"PET_JOURNAL_LIST_UPDATE") -- this should've been a one-off call for releasing a pet rematch.timer:Start(0,rematch.roster.Update) -- update allPets and allSpecies end -- called one frame after the number of owned pets changes; expands the journal (clears filters) if they're not already, -- gathers into allPets/allSpecies, and then restores the journal filters to their previous state (if they changed) function rematch.roster:Update() if isUpdatingRoster or not rematch.main:IsPlayerInWorld() then return -- already doing an Update or player is in a loading screen, leave end rematch.roster:StartUpdatingRoster() local isAnyFilterUsed = rematch.roster:IsAnyFilterUsed() if isAnyFilterUsed then -- before expanding filters, confirm any are being used first (can drop from 122ms to 2ms to skip this) rematch.roster:ExpandJournal() end uniqueOwnedCount = 0 wipe(allPets) wipe(allSpecies) for speciesID,petIDs in pairs(speciesPetIDs) do wipe(petIDs) end for i=1,C_PetJournal.GetNumPets() do local petID,speciesID = C_PetJournal.GetPetInfoByIndex(i) tinsert(allPets,petID or speciesID) if not speciesPetIDs[speciesID] then speciesPetIDs[speciesID] = {} end if petID then if #speciesPetIDs[speciesID]==0 then uniqueOwnedCount = uniqueOwnedCount + 1 end tinsert(speciesPetIDs[speciesID],petID) end end -- now rebuild allSpecies for speciesID in pairs(speciesPetIDs) do tinsert(allSpecies,speciesPetIDs) end if isAnyFilterUsed then rematch.roster:RestoreJournal() end rematch.filters:ForceUpdate() -- set dirty flag on filter so list updates rematch.timer:Start(0,rematch.roster.FinishUpdatingRoster) end -- external so speciesInfo can also use the Expand/RestoreJournal function rematch.roster:StartUpdatingRoster() isUpdatingRoster = true end -- delayed a frame in case a NEW_PET_ADDED and UPDATE_SUMMONPETS_ACTION fires in pairs; clears flag that says we're updating -- and if this is the first update, then fire off a REMATCH_PETS_LOADED for anything waiting for pets to load on login function rematch.roster:FinishUpdatingRoster() isUpdatingRoster = nil --rematch.savedTeams:ValidateAllTeams() -- if pets leaving/adding, adjust teams that might be impacted if waitingForFirstUpdate then waitingForFirstUpdate = nil rematch.events:Fire("REMATCH_PETS_LOADED") end rematch.events:Fire("REMATCH_PETS_CHANGED") end --[[ counts ]] -- returns the total number of pets, both owned (including duplicates) and uncollected function rematch.roster:GetNumPets() return #allPets end -- returns the number of distinct speciesIDs in the journal, owned or not function rematch.roster:GetNumSpecies() return #allSpecies end -- returns the number of pets owned by the player, including duplicates function rematch.roster:GetNumOwned() return select(2,C_PetJournal.GetNumPets()) end -- returns the number of unique pets owned by the player (specifically, the number of different speciesIDs the player owns) function rematch.roster:GetNumUniqueOwned() return uniqueOwnedCount end -- returns a small ordered list of petIDs that are owned for the given speciesID (honor system here, nothing should update this return) function rematch.roster:GetSpeciesPetIDs(speciesID) return speciesID and speciesPetIDs[speciesID] end --[[ journal filter shenanigans ]] -- since there is no C_PetJournal.GetSearchFilter, we need to watch for it changing hooksecurefunc(C_PetJournal,"SetSearchFilter",function(search) if not isUpdatingRoster then journalBackup.search = search or "" end end) hooksecurefunc(C_PetJournal,"ClearSearchFilter",function() if not isUpdatingRoster then journalBackup.search = "" end end) -- clears all filters in the pet journal so all pets can be captured in roster:Update() function rematch.roster:ExpandJournal() journalBackup.collected = C_PetJournal.IsFilterChecked(LE_PET_JOURNAL_FILTER_COLLECTED) journalBackup.notCollected = C_PetJournal.IsFilterChecked(LE_PET_JOURNAL_FILTER_NOT_COLLECTED) for i=1,C_PetJournal.GetNumPetTypes() do journalBackup.types[i] = C_PetJournal.IsPetTypeChecked(i) end for i=1,C_PetJournal.GetNumPetSources() do journalBackup.sources[i] = C_PetJournal.IsPetSourceChecked(i) end C_PetJournal.ClearSearchFilter() C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_COLLECTED,true) C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_NOT_COLLECTED,true) C_PetJournal.SetAllPetSourcesChecked(true) C_PetJournal.SetAllPetTypesChecked(true) end -- restores all filters that were cleared in roster:ExpandJournal() function rematch.roster:RestoreJournal() C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_COLLECTED,journalBackup.collected) C_PetJournal.SetFilterChecked(LE_PET_JOURNAL_FILTER_NOT_COLLECTED,journalBackup.notCollected) for i=1,C_PetJournal.GetNumPetSources() do C_PetJournal.SetPetSourceChecked(i,journalBackup.sources[i]) end for i=1,C_PetJournal.GetNumPetTypes() do C_PetJournal.SetPetTypeFilter(i,journalBackup.types[i]) end C_PetJournal.SetSearchFilter(journalBackup.search) end -- returns true if any journal filters are used, since there's no need to expand/restore if it's already expanded -- using this to skip the expand/restore drops an expand/capture/restore from 122ms to 2ms (!!!) function rematch.roster:IsAnyFilterUsed() if journalBackup.search~="" then return true -- search is used end if not C_PetJournal.IsFilterChecked(LE_PET_JOURNAL_FILTER_COLLECTED) then return true -- collected is unchecked end if not C_PetJournal.IsFilterChecked(LE_PET_JOURNAL_FILTER_NOT_COLLECTED) then return true -- not collected is unchecked end for i=1,C_PetJournal.GetNumPetTypes() do if not C_PetJournal.IsPetTypeChecked(i) then return true -- a pet type is unchecked end end for i=1,C_PetJournal.GetNumPetSources() do if not C_PetJournal.IsPetSourceChecked(i) then return true -- a pet source is unchecked end end return false end --[[ iterator functions ]] -- loops over allPets, which is a list of owned petIDs ("BattlePet-0-etc") and unowned speciesIDs (42) function rematch.roster:AllPets() local i = 0 return function() i = i + 1 if i <= #allPets then return allPets[i] end end end -- loops over all owned petIDs function rematch.roster:AllOwnedPets() local i = 0 return function() i = i + 1 if i <= #allPets then if type(allPets[i])=="string" then return allPets[i] end end end end -- loops over allSpecies, or all distinct speciesIDs function rematch.roster:AllSpecies() local i = 0 return function() i = i + 1 if i <= #allSpecies then return allSpecies[i] end end end -- loops over all petIDs owned for the given speciesID function rematch.roster:AllSpeciesPetIDs(speciesID) local i = 0 return function() i = i + 1 if speciesPetIDs[speciesID] and i<=#speciesPetIDs[speciesID] then return speciesPetIDs[speciesID][i] end end end