You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

791 lines
35 KiB

local _,rematch = ...
local L = rematch.localization
local C = rematch.constants
local settings = rematch.settings
--[[
Rematch refers to pets as a single number or string, depending on their context. This reference,
called a petID, can be one of nine idTypes:
idType example petID value description
---------- --------------------- -----------------------------------
"pet" "Battle-0-0000000etc" a player-owned pet in the journal
"species" 42 speciesID species number
"leveling" 0 leveling slot
"ignored" "ignored" ignored slot
"link" "battlepet:42:25:etc" linked pet
"battle" "battle:2:1" pet in battle (battle:owner:index)
"random" "random:10" random mechanical pet (random:0 for any pet type)
"unnotable" "unnotable:npcID" pet of a target not in targetData's notableTargets
"empty" nil or "blank" no pet
"unknown" (anything else) indecipherable/undefined pet
To simplify getting information about these different types of pets, to eliminate the scattered
code to independently call the various API through C_PetJournal and C_PetBattles, and to reduce
redundant API calls for information already retrieved (or information not used!), this module
encapsulates a "front end" for information about pets.
To use, fetch a petID to make it the pet of interest (which can be any of the above strings or numbers):
petInfo:Fetch(petID)
After a pet of interest is fetched, simply index the stat you want:
print(petInfo.name,"is level",petInfo.level,"and breed",petInfo.breedName)
The stat can be any of these:
petID: this is the pet reference Fetched (string, number, link, etc)
idType: "pet" "species" "leveling" "ignored" "link" "battle" "random" or "unknown" (string)
speciesID: numeric speciesID of the pet (integer)
customName: user-renamed pet name (string)
speciesName: name of the species (string)
name: customName if defined, speciesName otherwise (string)
level: whole level 1-25 (integer)
xp: amount of xp in current level (integer)
maxXp: total xp to reach next level (integer)
fullLevel: level+xp/maxXp (float)
displayID: id of the pet's skin (integer)
isFavorite: whether pet is favorited (bool)
icon: fileID of pet's icon or specific filename (integer or string)
petType: numeric type of pet 1-10 (integer)
creatureID: npcID of summoned pet (integer)
sourceText: formatted text about where pet is from (string)
loreText: "back of the card" lore (string)
isWild: whether the pet is found in the wild (bool)
canBattle: whether pet can battle (bool)
isTradable: whether pet can be caged (bool)
isUnique: whether only one of pet can be learned (bool)
isObtainable: whether this pet is in the journal (bool)
health: current health of the pet (integer)
maxHealth: maximum health of the pet (integer)
power: power stat of the pet (integer)
speed: speed stat of the pet (integer)
rarity: rarity 1-4 of pet (integer)
isDead: whether the pet is dead (bool)
isInjured: whether the pet has less than max health (bool)
isSummonable: whether the pet can be summoned (bool)
summonError: the error ID why a pet can't be summoned
summonErrorText: the error text why a pet can't be summoned
summonShortError: shortened text of why a pet can't be summoned (for pet card stat)
isRevoked: whether the pet is revoked (bool)
abilityList: table of pet's abilities (table)
levelList: table of pet's ability levels (table)
valid: whether the petID is valid and petID is not missing (bool)
owned: whether the petID is a valid pet owned by the player (bool)
count: number of pet the player owns (integer)
maxCount: maximum number of this pet the player can own (integer)
countColor: hex color code for pet count (white for 0, green for count<max, red for count=max)
hasBreed: whether pet can battle and there's a breed source (bool)
breedID: 3-12 for known breeds, 0 for unknown breed, nil for n/a (integer)
breedName: text version of breed like P/P or S/B (string)
possibleBreedIDs: list of breedIDs possible for the pet's species (table)
possibleBreedNames: list of breedNames possible for the pet's species (table)
numPossibleBreeds: number of known breeds for the pet (integer)
needsFanfare: whether a pet is wrapped (bool)
battleOwner: whether ally(1) or enemy(2) pet in battle (integer)
battleIndex: 1-3 index of pet in battle (integer)
isSlotted: whether pet is slotted (bool)
inTeams: whether pet is in any teams (pet and species idTypes only) (bool)
numTeams: number of teams the pet belongs to (pet and species only) (integer)
sourceID: the source index (1=Drop, 2=Quest, 3=Vendor, etc) of the pet (integer)
moveset: the exact moveset of the pet ("123,456,etc") (string)
speciesAt25: whether the pet has a version at level 25 (bool)
hasNotes: whether the pet has notes (bool)
notes: the text of the pet's notes (string)
isLeveling: whether the pet is in the queue (bool)
isSummoned: whether the pet is currently summoned (bool)
expansionID: the numeric index of the expansion the pet is from: 0=classic, 1=BC, 2=WotLK, etc. (integer)
expansionName: the name of the expansion the pet is from (string)
isSpecialType: whether the petid is a leveling, random or ignored (bool)
passive: the "racial" or passive text of the pet type (string)
shortHealthStatus: the numeric health at max health, or percent if injured, or DEAD if dead
longHealthStatus: a hp/maxHp (percent%) description of pet health
npcID: the npcID of the target for an unnotable petID
tint: either "red" for revoked/wrong-faction pets, "grey" for otherwise unsummonable, nil for no tint
strongVs: a table of [ability]=petType of attacks that do increased damage (table)
toughVs: the petType of attack that this pet takes reduced damage from (integer)
vulnerableVs: the petType of attack that this pet takes increased damage from (integeter)
formattedName: name of pet with color codes for its rarity
isStickied: whether the pet is temporarily stickied to top of pet list (wrapped) (bool)
If a separate petInfo is needed (such as doing a comparison of one pet against another), then you can
create a new petInfo with a :Create() from any other petInfo:
local myPetInfo = rematch.petInfo:Create()
At the end of this file, rematch.altInfo is created as an alternative to rematch.petInfo for this purpose.
How it works:
petInfo:Fetch(petID) will check if the petID is different from the last-fetched pet. If so,
it will wipe the existing information and store the petID and idType within the table, ready
for stats to be queried/indexed.
The created petInfo table has a __index metamethod to look up indexes that don't exist.
If a petInfo[stat] has no value, the __index metamethod will call the appropriate API
(depending on the idType of the pet) and fill in its value. Future references to petInfo[stat]
will have a value and not invoke a __index.
Also:
The script filter system has its own petInfo that's already fetched for each pet. Script filters
do not need to fetch a the current pet.
petInfo:Reset() will force a wipe of information.
When the petID is guaranteed to be from the journal (either a valid, owned petID string or
speciesID), Fetch(petID,true) will skip the test of its type to improve performance.
link format: "battlepet:<speciesID>:<level>:<rarity>:<health>:<power>:<speed>"
]]
-- if an alternate petInfo needs made, use: local altInfo = rematch.petInfo:Create()
local GetPetInfoByPetID = C_PetJournal.GetPetInfoByPetID
local GetPetInfoBySpeciesID = C_PetJournal.GetPetInfoBySpeciesID
-- local functions defined here
local fillInfoByPetID, fillInfoBySpeciesID, getIDType, fetch, reset, lookup, create
-- each stat comes from a function. this table says what function populates each stat
local stats = {
speciesID="General", customName="General", level="General", xp="General", maxXp="General",
displayID="General", isFavorite="General", speciesName="General", name="General", icon="General",
petType="General", creatureID="General", sourceText="General", loreText="General", isWild="General",
canBattle="General", isTradable="General", isUnique="General", isObtainable="General", npcID="General",
isValid="Valid", isOwned="Valid",
health="Stats",maxHealth="Stats", power="Stats", speed="Stats", rarity="Stats", color="Stats",
isDead="Dead", isInjured="Dead",
shortHealthStatus="Status",longHealthStatus="Status",
isRevoked="Other", needsFanfare="Other",
isSummonable="SummonInfo", summonError="SummonInfo", summonErrorText="SummonInfo",
isSummoned="IsSummoned",
abilityList="Abilities", levelList="Abilities",
usableAbilities="UsableAbilities",
strongVs="StrongVs",
toughVs="ToughVs", vulnerableVs="ToughVs",
count="Count", maxCount="Count", countColor="Count",
fullLevel="FullLevel",
suffix="Suffix", petTypeName="Suffix",
battleOwner="Battle", battleIndex="Battle",
isSlotted="Slotted",
breedID="Breed", breedName="Breed", hasBreed="Breed",
possibleBreedIDs="PossibleBreeds", possibleBreedNames="PossibleBreeds", numPossibleBreeds="PossibleBreeds",
inTeams="Teams", numTeams="Teams",
sourceID="Source",
moveset="Moveset",
isSpeciesAt25="SpeciesAt25",
isMovesetAt25="MovesetAt25",
notes="Notes", hasNotes="Notes",
isLeveling="IsLeveling",
expansionID="Expansion", expansionName="Expansion",
isSpecialType="Special",
passive="Passive",
marker="PetMarker",
tint="Tint",
formattedName="FormattedName",
isStickied="Stickied",
}
-- and each function is a member of this table, called during a lookup to populate the above stats
local funcs = {}
-- the General func has to deal with 7 idTypes separately, so it has subfuncs broken out into the following table
local generalSubFuncs
-- indexed by petInfo reference, this contains tables like abilityList and fetchedFuncs reused to reduce garbage
local reusedTables = {}
--[[ stat-pulling funcs ]]
-- the majority of information about a pet will come from the General function, which is broken up
-- by idTypes in generalSubFuncs to make lookups a little quicker
function funcs:General()
local idType = self.idType
if idType and generalSubFuncs[idType] then
generalSubFuncs[idType](self)
else
self.name = L["Unknown"]
self.icon = C.UNKNOWN_ICON
end
end
-- verifies the pet contains information (not a server-reassigned petID or bad speciesID) and if it's owned
function funcs:Valid()
local idType = self.idType
if idType=="pet" and self.speciesID then -- if speciesID read ok then "pet" petID is functional
self.isValid = true
self.isOwned = true -- owned is only true if regular petID is a "Battle-0-etc" petID
elseif (idType=="species" and self.name) or (idType=="leveling" or idType=="ignored" or idType=="random" or idType=="unnotable") or (idType=="link" and self.name) or (idType=="battle" and self.name) then
self.isValid = true
self.isOwned = false
else
self.isValid = false
self.isOwned = false
end
end
-- gets rarity, health, power and speed stats if possible
function funcs:Stats()
local idType = self.idType
local rarity,health,maxHealth,power,speed
if idType=="pet" then
health,maxHealth,power,speed,rarity = C_PetJournal.GetPetStats(self.petID)
elseif idType=="link" then
rarity,health,power,speed = self.petID:match("battlepet:%d+:%d+:(%d+):(%d+):(%d+):(%d+)")
if rarity then
rarity = tonumber(rarity)+1 -- links are 0-3 rarity intead of 1-4
health = tonumber(health)
maxHealth = tonumber(health)
power = tonumber(power)
speed = tonumber(speed)
end
elseif idType=="battle" then -- pets in a battle fetch live stats (but note values cache!)
local owner = self.battleOwner
local index = self.battleIndex
if C_PetBattles.GetPetSpeciesID(owner,index) then
rarity = C_PetBattles.GetBreedQuality(owner,index)
health = C_PetBattles.GetHealth(owner,index)
maxHealth = C_PetBattles.GetMaxHealth(owner,index)
power = C_PetBattles.GetPower(owner,index)
speed = C_PetBattles.GetSpeed(owner,index)
end
end
if not self.canBattle then -- for non-battle pets, hide all stats except rarity
health = nil
maxHealth = nil
power = nil
speed = nil
end
self.rarity = rarity
self.health = health
self.maxHealth = maxHealth
self.power = power
self.speed = speed
if rarity then
self.color = rematch.utils:GetRarityColor(rarity-1)
end
end
-- whether a pet isDead or isInjured
function funcs:Dead()
if self.maxHealth and self.maxHealth > 0 then
self.isDead = self.health==0
self.isInjured = self.health<self.maxHealth
end
end
-- text versions of pet status (health so far)
function funcs:Status()
local health,maxHealth = self.health,self.maxHealth
if self.isDead then -- if pet is dead, use 'Dead' text rather than 0%
self.shortHealthStatus = DEAD
self.longHealthStatus = format("%s%d/%d (%s)\124r",C.HEX_RED,health,maxHealth,DEAD)
elseif self.isInjured then -- if not at full life, display a percent instead of a number
self.shortHealthStatus = format("%d%%",health*100/maxHealth)
self.longHealthStatus = format("%s%d/%d (%d%%)\124r",C.HEX_RED,health,maxHealth,health*100/maxHealth)
else -- at full life, display actual health (same as maxHealth)
self.shortHealthStatus = tostring(maxHealth)
self.longHealthStatus = tostring(maxHealth)
end
end
-- intended for journal listing that probably only happens when updating display
function funcs:Other()
if self.idType=="pet" then
local petID = self.petID
self.isRevoked = C_PetJournal.PetIsRevoked(petID)
self.needsFanfare = C_PetJournal.PetNeedsFanfare(petID)
end
end
-- isSummonable moved to this for new GetPetSummonInfo
function funcs:SummonInfo()
if self.idType=="pet" then
local isSummonable, error, errorText = C_PetJournal.GetPetSummonInfo(self.petID)
if not isSummonable and error==Enum.PetJournalError.PetIsDead then
isSummonable = true -- treating summonable pets that are just dead as summonable
-- (if there's another reason it's unsummonable, then it's unlikely it could be dead too)
end
self.isSummonable = isSummonable
self.summonError = error
self.summonErrorText = errorText -- this text is a bit too long for pet card but usable in tooltips
self.summonShortError = error and C.SUMMON_SHORT_ERRORS[error] or L["Can't Summon"] -- usable for pet card
end
end
-- whether the pet is currently summoned
function funcs:IsSummoned()
self.isSummoned = C_PetJournal.GetSummonedPetGUID() == self.petID
end
-- fills petInfo.abilityList and petInfo.levelList tables
function funcs:Abilities()
if self.speciesID then
local abilityList = reusedTables[self].abilityList
local levelList = reusedTables[self].levelList
C_PetJournal.GetPetAbilityList(self.speciesID,abilityList,levelList)
self.abilityList = abilityList
self.levelList = levelList
end
end
-- fills petInfo.usableAbilities to be a lookup table of abilities the pet can use at their current level
function funcs:UsableAbilities()
local usableAbilities = reusedTables[self].usableAbilities
wipe(usableAbilities)
local level = self.level
if level and level > 0 then
local abilityList = self.abilityList
local levelList = self.levelList
for i=1,6 do
local listLevel = levelList[i]
if listLevel and listLevel <= level then
usableAbilities[abilityList[i]] = true
end
end
end
self.usableAbilities = usableAbilities
end
-- fills petInfo.strongVs table indexed by abilityID and the petType the ability is strong against
function funcs:StrongVs()
local strongVs = reusedTables[self].strongVs
local abilityList = self.abilityList
wipe(strongVs)
if abilityList then
for _,abilityID in ipairs(abilityList) do
local _,_,_,_,_,_,abilityType,noHints = C_PetBattles.GetAbilityInfoByID(abilityID)
if not noHints then -- skipping self heals and such that don't attack
strongVs[abilityID] = C.HINTS_OFFENSE[abilityType][1]
end
end
end
self.strongVs = strongVs
end
-- sets toughVs to the pet type the pet is tough against ()
function funcs:ToughVs()
local hint = C.HINTS_DEFENSE[self.petType]
self.toughVs = hint and hint[2]
self.vulnerableVs = hint and hint[1]
end
-- gets the current number of collected versions of the pet and the max allowed copies
function funcs:Count()
if self.speciesID then
local count,maxCount = C_PetJournal.GetNumCollectedInfo(self.speciesID)
self.count,self.maxCount = count,maxCount
if not count or count==0 then
self.countColor = C.HEX_WHITE
elseif count==maxCount then
self.countColor = C.HEX_RED
elseif count<maxCount then
self.countColor = C.HEX_GREEN
end
end
end
-- gets the pet's level plus their xp/maxXp, so a level 23 pet at 45% towards level 24 is fullLevel 23.45
function funcs:FullLevel()
local xp = self.xp
if xp then
self.fullLevel = self.level + (xp/self.maxXp)
else
self.fullLevel = self.level
end
end
-- suffix is not localized and used for icon names (Water in Pet_Type_Water), petTypeName is the localized name of the pet type (Aquatic)
function funcs:Suffix()
local petType = self.petType
if petType then
self.suffix = PET_TYPE_SUFFIX[petType]
self.petTypeName = _G["BATTLE_PET_NAME_"..petType]
end
end
-- pulls battleOwner and battleIndex from the "battle" petID
function funcs:Battle()
if self.idType=="battle" then
local owner,index = self.petID:match("battle:(%d):(%d)")
self.battleOwner = tonumber(owner)
self.battleIndex = tonumber(index)
end
end
-- whether the pet is slotted
function funcs:Slotted()
local idType = self.idType
self.isSlotted = (idType=="pet" and C_PetJournal.PetIsSlotted(self.petID)) or (idType=="battle" and self.battleOwner==1)
end
-- the breed of an owned pet in the journal, a link or in battle
function funcs:Breed()
local source = rematch.breedInfo:GetBreedSource()
local idType = self.idType
if source and self.isValid and self.canBattle and (idType=="pet" or idType=="link" or idType=="battle") then
local breedID,breedName
if source=="BattlePetBreedID" then
if idType=="pet" or idType=="link" then
breedID = BPBID_Internal.CalculateBreedID(self.speciesID,self.rarity,self.level,self.maxHealth,self.power,self.speed,false,false)
elseif idType=="battle" then
breedID = BPBID_Internal.breedCache[self.battleIndex + (self.battleOwner==2 and 3 or 0)]
end
elseif source=="PetTracker" then
if idType=="pet" then
breedID = PetTracker.Pet(self.petID):GetBreed()
elseif idType=="link" then
breedID = PetTracker.Predict:Breed(self.speciesID,self.level,self.rarity,self.maxHealth,self.power,self.speed)
elseif idType=="battle" and PetTracker.Battle then
breedID = PetTracker.Battle(self.battleOwner,self.battleIndex):GetBreed()
end
end
if type(breedID)~="number" then
breedID = 0
breedName = self.numPossibleBreeds==0 and "NEW" or "???"
end
self.breedID = breedID
self.breedName = breedName or rematch.breedInfo:GetBreedNameByID(breedID)
self.hasBreed = true
else
self.hasBreed = false
end
end
-- the possible breeds of the pet's species
function funcs:PossibleBreeds()
local possibleBreedIDs = reusedTables[self].possibleBreedIDs
local possibleBreedNames = reusedTables[self].possibleBreedNames
wipe(possibleBreedIDs)
wipe(possibleBreedNames)
local source = rematch.breedInfo:GetBreedSource()
local speciesID = self.speciesID
if source and type(speciesID)=="number" and self.canBattle then
local data -- table to contain possible breeds
if source=="BattlePetBreedID" then
if not BPBID_Arrays.BreedsPerSpecies then
BPBID_Arrays.InitializeArrays()
end
data = BPBID_Arrays.BreedsPerSpecies[speciesID]
elseif source=="PetTracker" then
data = PetTracker.SpecieBreeds[speciesID]
end
-- if there's a table of breeds, copy them to possibleBreeds
if data and type(data)=="table" then
for _,breed in ipairs(data) do
tinsert(possibleBreedIDs,breed)
tinsert(possibleBreedNames,rematch.breedInfo:GetBreedNameByID(breed))
end
end
self.possibleBreedIDs = possibleBreedIDs
self.possibleBreedNames = possibleBreedNames
self.numPossibleBreeds = #possibleBreedIDs
end
end
-- a moveset is the exact abilities (AND their order) of a pet
function funcs:Moveset()
if type(self.abilityList)=="table" then
self.moveset = table.concat(self.abilityList,",")
else
self.moveset = nil
end
end
-- isSpeciesAt25 is true if there's a version of this pet's species at level 25
function funcs:SpeciesAt25()
self.isSpeciesAt25 = rematch.collectionInfo:IsSpeciesAt25(self.speciesID)
end
-- isMovesetAt25 is true if there's a pet of any species with this moveset at level 25
function funcs:MovesetAt25()
self.isMovesetAt25 = rematch.collectionInfo:IsMovesetAt25(self.moveset)
end
function funcs:Notes()
local notes = settings.PetNotes[self.speciesID]
self.notes = notes
self.hasNotes = notes and true or false
end
-- sets expansionID and expansionName to describe the expansion the pet is from
function funcs:Expansion()
local expansionID = rematch.speciesInfo:GetExpansion(self.speciesID)
if expansionID then
self.expansionID = expansionID
self.expansionName = _G["EXPANSION_NAME"..expansionID]
end
end
-- sourceID is 1=Drop, 2=Quest, 3=Vendor, etc.
function funcs:Source()
self.sourceID = rematch.speciesInfo:GetSourceID(self.speciesID)
end
-- whether the petID is in a team and how many teams
function funcs:Teams()
local numTeams = rematch.savedTeams and rematch.savedTeams:GetNumTeamsWithPet(self.petID) or 0
self.inTeams = numTeams > 0
self.numTeams = numTeams
end
-- whether the pet is not an actual pet but a special non-pet type
function funcs:Special()
local idType = self.idType
self.isSpecialType = idType=="leveling" or idType=="random" or idType=="ignored" or idType=="unnotable"
end
-- the passive or racial text for the pet type
function funcs:Passive()
local petType = self.petType
if petType and not self.isSpecialType then
self.passive = select(5,C_PetBattles.GetAbilityInfoByID(PET_BATTLE_PET_TYPE_PASSIVES[petType])):match("^.-\r\n(.-)\r"):gsub("%[percentage.-%]%%","4%%")
end
end
-- the numeric 1-8 marker assigned to the speciesID
function funcs:PetMarker()
local speciesID = self.speciesID
if speciesID then
self.marker = settings.PetMarkers[speciesID]
end
end
-- tint is "red" if the pet is unsummonable and owned (revoked or wrong faction), or "grey" if
-- otherwise unsummonable (uncollected speciesID) or nil if no tint
function funcs:Tint()
if not self.isSummonable and not self.isSpecialType then
self.tint = self.isOwned and "red" or "grey"
end
end
-- true/false if pet is in the leveling queue
-- note: if queue is mid-process, this is unreliable; check the settings.LevelingQueue then
function funcs:IsLeveling()
self.isLeveling = rematch.queue:IsPetLeveling(self.petID)
end
-- returns the pet name with color codes
function funcs:FormattedName()
if settings.ColorPetNames then
local color = self.color
self.formattedName = format("%s%s\124r",color and color.hex or C.HEX_WHITE,self.name)
else
self.formattedName = self.name
end
end
function funcs:Stickied()
self.isStickied = rematch.sort:IsPetIDStickied(self.petID)
end
------------------------------------------------------------------------------------------------
--[[ helper funcs (these are declared local at the top here) ]]
-- lookup table of sub-functions to fill General stats depending on the pet's idType
generalSubFuncs = {
pet = function(self)
fillInfoByPetID(self,self.petID)
end,
species = function(self)
fillInfoBySpeciesID(self,self.petID) -- petID here is a number (speciesID)
end,
leveling = function(self)
self.name = L["Leveling Pet"]
self.icon = C.LEVELING_ICON
self.displayID = "Interface\\Buttons\\talktomequestion_ltblue.m2"
end,
ignored = function(self)
self.name = L["Ignored Pet"]
self.icon = C.IGNORED_ICON
self.displayID = "Interface\\Buttons\\talktomered.m2"
end,
link = function(self)
local speciesID,level = self.petID:match("battlepet:(%d+):(%d+):")
speciesID = tonumber(speciesID)
if speciesID then
fillInfoBySpeciesID(self,speciesID)
self.level = tonumber(level) -- rarity, health, power and speed comes from Stats
end
end,
battle = function(self)
local owner = self.battleOwner
local index = self.battleIndex
if owner==1 then -- for ally battle pets, just use the loaded pet
local petID = C_PetJournal.GetPetLoadOutInfo(index)
if petID then
fillInfoByPetID(self,petID)
end
elseif owner==2 then
local speciesID = C_PetBattles.GetPetSpeciesID(owner,index)
if speciesID then
fillInfoBySpeciesID(self,speciesID)
self.level = C_PetBattles.GetLevel(owner,index)
self.displayID = C_PetBattles.GetDisplayID(owner,index)
end
end
end,
random = function(self)
-- petType always defined as 0-10 for random pets
local petType = math.min(10,math.max(0,tonumber(self.petID:match("random:(%d+)")) or 0))
self.petType = petType
-- name is "Random Pet" or "Random Humanoid", "Random Dragonkin", etc
local suffix = PET_TYPE_SUFFIX[petType]
self.name = suffix and format(L["Random %s"],_G["BATTLE_PET_NAME_"..petType]) or L["Random Pet"]
self.icon = suffix and format("Interface\\Icons\\Icon_PetFamily_%s",suffix) or "Interface\\Icons\\INV_Misc_Dice_02"
self.displayID = "Interface\\Buttons\\talktomequestionmark.m2"
end,
empty = function(self)
self.name = L["Empty Pet Slot"]
self.icon = C.EMPTY_ICON
end,
unnotable = function(self)
self.name = L["Not Notable"]
self.icon = C.UNNOTABLE_ICON
self.npcID = tonumber(self.petID:match("unnotable:(%d+)"))
end,
}
-- used in General functions to gather info by petID (BattlePet-0-000etc)
function fillInfoByPetID(self,petID)
local speciesID, customName, speciesName -- prevent a __index lookup if petID is invalid or not renamed
local canBattle, level, xp, maxXp
speciesID,customName,level,xp,maxXp,self.displayID,
self.isFavorite,speciesName,self.icon,self.petType,self.creatureID,
self.sourceText,self.loreText,self.isWild,canBattle,self.isTradable,
self.isUnique,self.isObtainable = GetPetInfoByPetID(petID)
self.name = customName or speciesName
self.speciesID = speciesID
self.customName = customName
self.speciesName = speciesName
-- canBattle can be false for GetPetInfoByPetID when it can be true for GetPetInfoBySpeciesID (/sigh)
canBattle = rematch.speciesInfo:CanBattle(speciesID)
if canBattle then -- only define level and xp for pets that can battle
self.level = level
self.xp = xp
self.maxXp = maxXp
end
self.canBattle = canBattle
end
-- used in General functions to gather info by speciesID (42)
function fillInfoBySpeciesID(self,speciesID)
local speciesName,icon -- prevent a __index lookup if speciesID is invalid
speciesName,icon,self.petType,self.creatureID,self.sourceText,
self.loreText,self.isWild,self.canBattle,self.isTradable,self.isUnique,
self.isObtainable,self.displayID = GetPetInfoBySpeciesID(speciesID)
-- when speciesID is invalid, it returns the GetPetInfoBySpeciesID function now for some reason
if type(speciesName)=="function" then -- nil everything if
speciesName = nil
icon = nil
end
self.speciesName = speciesName
self.name = speciesName
self.speciesID = speciesID
self.icon = icon
if not self.icon then
self.icon = C.REMATCH_ICON
end
end
-- used by fetch, getIDType takes a petID and returns what type of id it is. one of:
-- "pet" "species" "leveling" "ignored" "link" "battle" "random" "ignored" or "unknown"
function getIDType(petID,forJournal)
local idType = type(petID)
if forJournal then
if idType=="string" then
return "pet"
elseif idType=="number" then
return "species"
end
elseif idType=="string" then
if petID:match("^BattlePet%-%x%-%x%x%x%x%x%x%x%x%x%x%x%x$") then
return "pet"
elseif petID:match("battlepet:%d+:%d+:%d+:%d+:%d+:%d+") then
return "link"
elseif petID:match("battle:%d:%d") then
return "battle"
elseif petID:match("random:%d+") then
return "random"
elseif petID=="ignored" then
return "ignored"
elseif petID:match("unnotable:%d+") then
return "unnotable"
elseif petID=="empty" then
return "empty"
end
elseif idType=="number" then
if petID>0 then
return "species"
elseif petID==0 then
return "leveling"
end
elseif idType=="nil" then
return "empty"
end
return "unknown" -- if we reached here, no idea what this petID is!
end
-- fetch makes a petID the "pet of interest." it can be a full petID, a speciesID, link, etc.
-- if fromJournal is true, then this petID is trusted to be an owned petID or a speciesID.
-- the same petID can be fetched multiple times and it will not duplicate the work to get info
function fetch(self,petID,fromJournal)
if petID~=self.petID or not petID then -- nils are okay (they're unknown; need 'not petID' if nil~=nil)
reset(self)
self.petID = petID -- note that .petID property is the one used in the fetch and never changes
self.idType = getIDType(petID,fromJournal)
end
return self -- initially, only the given petID and its idType are known
end
-- resets a petInfo, called when a new pet is fetched. it's also a good idea to call this at the start
-- of an update in case any pets changed attributes since the petInfo stat was last fetched. (by design,
-- API calls are not re-run for the same pet, it uses the cached value.)
function reset(self)
wipe(self)
for _,tbl in pairs(reusedTables[self]) do
wipe(tbl) -- wipes the reusable tables
end
self.Fetch = fetch -- re-establishing these since we wiped self
self.Reset = reset
self.Create = create
end
-- the __index metamethod that returns an already-cached result or fetches the result and returns it
function lookup(self,stat)
local func = stats[stat]
local fetchedFuncs = reusedTables[self].fetchedFuncs
if func and not fetchedFuncs[func] and funcs[func] then -- if function for the stat has not been called
fetchedFuncs[func] = true -- flag it being used
funcs[func](self) -- and run the func to populate the stat
end
return rawget(self,stat) -- and return the value now, either already cached or just-pulled value
end
-- creates a new petInfo if needed (rematch.petInfo is the primary one. rematch.petInfo (or any
-- petInfo can spawn a new one with petInfo:Create()))
function create()
local info = {}
reusedTables[info] = {
fetchedFuncs = {}, -- lookup of functions that have been run
abilityList = {}, -- cached result of C_PetJournal.GetPetAbilityList
levelList = {}, -- cached result of C_PetJouranl.GetPetAbilityList
usableAbilities = {}, -- list of abilities the pet can use at their current level
possibleBreedIDs = {}, -- table of breedIDs possible for a speciesID
possibleBreedNames = {}, -- table of breedNames possible for a speciesID
strongVs = {}, -- table of abilities and the petTypes they are strong against
}
info.Fetch = fetch -- member functions
info.Reset = reset
info.Create = create -- any petInfo can spawn new petInfos if needed
setmetatable(info,{__index=lookup})
return info
end
-- create the main petInfo used throughout the addon
rematch.petInfo = create()
-- creating an alternate one in case we need to fetch two concurrently (comparing others)
rematch.altInfo = rematch.petInfo:Create()