diff --git a/CanIMogIt.toc b/CanIMogIt.toc
index 4aa5df1..fbb39d6 100644
--- a/CanIMogIt.toc
+++ b/CanIMogIt.toc
@@ -5,6 +5,7 @@
## Notes: Adds tooltips to items showing if you have learned a transmog appearance.
## X-Category: Transmogrify
## X-Website: http://mods.curse.com/addons/wow/can-i-mog-it
-## SavedVariables: CanIMogItOptions
+## SavedVariables: CanIMogItOptions, CanIMogItDatabase
code.lua
options.lua
+database.lua
diff --git a/code.lua b/code.lua
index 7382734..d0bd8bd 100644
--- a/code.lua
+++ b/code.lua
@@ -2,7 +2,7 @@
CanIMogIt = {}
-local dressUpModel = CreateFrame('DressUpModel')
+CanIMogIt.DressUpModel = CreateFrame('DressUpModel')
-----------------------------
@@ -152,10 +152,10 @@ CanIMogIt.KNOWN = KNOWN_ICON .. BLUE .. "Learned."
CanIMogIt.KNOWN_FROM_ANOTHER_ITEM = KNOWN_ICON .. BLUE .. "Learned from another item."
CanIMogIt.KNOWN_BY_ANOTHER_CHARACTER = KNOWN_BUT_ICON .. BLUE .. "Learned for a different class."
CanIMogIt.KNOWN_BUT_TOO_LOW_LEVEL = KNOWN_BUT_ICON .. BLUE .. "Learned but cannot transmog yet."
+CanIMogIt.KNOWN_FROM_ANOTHER_ITEM_AND_CHARACTER = KNOWN_BUT_ICON .. BLUE .. "Learned for a different class and item."
CanIMogIt.UNKNOWN = UNKNOWN_ICON .. ORANGE .. "Not learned."
CanIMogIt.UNKNOWABLE_BY_CHARACTER = UNKNOWABLE_BY_CHARACTER_ICON .. YELLOW .. "Another class can learn this item."
CanIMogIt.NOT_TRANSMOGABLE = NOT_TRANSMOGABLE_ICON .. GRAY .. "Cannot be learned."
-CanIMogIt.KNOWN_FROM_ANOTHER_ITEM_AND_CHARACTER = QUESTIONABLE_ICON .. YELLOW .. "Cannot determine status on other characters."
local knownTexts = {
@@ -254,16 +254,21 @@ local function printDebug(tooltip, itemLink)
local appearanceID = CanIMogIt:GetAppearanceID(itemLink)
addDoubleLine(tooltip, "GetAppearanceID:", tostring(appearanceID))
- if appearanceID then
- addDoubleLine(tooltip, "PlayerHasAppearance:", tostring(CanIMogIt:PlayerHasAppearance(appearanceID)))
- end
+
+ addLine(tooltip, '--------')
addDoubleLine(tooltip, "IsTransmogable:", tostring(CanIMogIt:IsTransmogable(itemLink)))
addDoubleLine(tooltip, "PlayerKnowsTransmogFromItem:", tostring(CanIMogIt:PlayerKnowsTransmogFromItem(itemLink)))
- addDoubleLine(tooltip, "IsValidAppearanceForPlayer:", tostring(CanIMogIt:IsValidAppearanceForPlayer(itemLink)))
- addDoubleLine(tooltip, "PlayerIsTooLowLevelForItem:", tostring(CanIMogIt:PlayerIsTooLowLevelForItem(itemLink)))
addDoubleLine(tooltip, "PlayerKnowsTransmog:", tostring(CanIMogIt:PlayerKnowsTransmog(itemLink)))
- addDoubleLine(tooltip, "PlayerCanLearnTransmog:", tostring(CanIMogIt:PlayerCanLearnTransmog(itemLink)))
+ addDoubleLine(tooltip, "CharacterCanLearnTransmog:", tostring(CanIMogIt:CharacterCanLearnTransmog(itemLink)))
+ addDoubleLine(tooltip, "IsValidAppearanceForCharacter:", tostring(CanIMogIt:IsValidAppearanceForCharacter(itemLink)))
+ addDoubleLine(tooltip, "CharacterIsTooLowLevelForItem:", tostring(CanIMogIt:CharacterIsTooLowLevelForItem(itemLink)))
+
+ addLine(tooltip, '--------')
+
+ addDoubleLine(tooltip, "Database GetItem:", tostring(CanIMogIt.Database:GetItem(itemLink)))
+ addDoubleLine(tooltip, "Database GetAppearanceTable:", tostring(CanIMogIt.Database:GetAppearanceTable(itemLink)))
+
end
@@ -283,6 +288,22 @@ CanIMogIt.cachedTooltipText = nil;
-----------------------------
+function CanIMogIt:GetAppearances()
+ -- Gets a table of all the appearances known to a character.
+ C_TransmogCollection.ClearSearch()
+ appearances = {}
+ for categoryID=1,28 do
+ categoryAppearances = C_TransmogCollection.GetCategoryAppearances(categoryID)
+ for i, categoryAppearance in pairs(categoryAppearances) do
+ if categoryAppearance.isCollected then
+ appearances[categoryAppearance.visualID] = categoryAppearance
+ end
+ end
+ end
+ return appearances
+end
+
+
function CanIMogIt:GetPlayerArmorTypeName()
local playerArmorTypeID = classArmorTypeMap[select(2, UnitClass("player"))]
return select(1, GetItemSubClassInfo(4, playerArmorTypeID))
@@ -299,6 +320,11 @@ function CanIMogIt:IsArmorSubClass(subClass, itemLink)
end
+function CanIMogIt:IsArmorSubClassIdentical(itemLinkA, itemLinkB)
+ return select(7, GetItemInfo(itemLinkA)) == select(7, GetItemInfo(itemLinkB))
+end
+
+
function CanIMogIt:IsArmorAppropriateForPlayer(itemLink)
local playerArmorTypeID = CanIMogIt:GetPlayerArmorTypeName()
if armorTypeSlots[CanIMogIt:GetSlotName(itemLink)] and not CanIMogIt:IsArmorSubClass(COSMETIC, itemLink) then
@@ -314,8 +340,19 @@ function CanIMogIt:GetSlotName(itemLink)
end
-function CanIMogIt:IsValidAppearanceForPlayer(itemLink)
- if IsEquippableItem(itemLink) then
+function CanIMogIt:CharacterCanEquipItem(itemLink)
+ local itemID = CanIMogIt:GetItemID(itemLink)
+ for i=1,28 do
+ if C_TransmogCollection.IsCategoryValidForItem(i, itemID) then
+ return true
+ end
+ end
+ return false
+end
+
+
+function CanIMogIt:IsValidAppearanceForCharacter(itemLink)
+ if CanIMogIt:CharacterCanEquipItem(itemLink) then
if CanIMogIt:IsItemArmor(itemLink) then
return CanIMogIt:IsArmorAppropriateForPlayer(itemLink)
else
@@ -327,7 +364,7 @@ function CanIMogIt:IsValidAppearanceForPlayer(itemLink)
end
-function CanIMogIt:PlayerIsTooLowLevelForItem(itemLink)
+function CanIMogIt:CharacterIsTooLowLevelForItem(itemLink)
local minLevel = select(5, GetItemInfo(itemLink))
return UnitLevel("player") < minLevel
end
@@ -356,16 +393,16 @@ function CanIMogIt:GetSource(itemLink)
local slots = inventorySlotsMap[slotName]
if not slots or not IsDressableItem(itemLink) then return end
- dressUpModel:SetUnit('player')
- dressUpModel:Undress()
+ CanIMogIt.DressUpModel:SetUnit('player')
+ CanIMogIt.DressUpModel:Undress()
for i, slot in pairs(slots) do
- dressUpModel:TryOn(itemLink, slot)
- local source = dressUpModel:GetSlotTransmogSources(slot)
+ CanIMogIt.DressUpModel:TryOn(itemLink, slot)
+ local source = CanIMogIt.DressUpModel:GetSlotTransmogSources(slot)
if source ~= 0 then return source end
end
end
-
+
function CanIMogIt:GetAppearanceID(itemLink)
-- Gets the appearanceID of the given itemID.
local source = CanIMogIt:GetSource(itemLink)
@@ -376,25 +413,20 @@ function CanIMogIt:GetAppearanceID(itemLink)
end
-function CanIMogIt:PlayerHasAppearance(appearanceID)
- -- Returns whether the player has the given appearanceID.
- local sources = C_TransmogCollection.GetAppearanceSources(appearanceID)
- if sources then
- for i, source in pairs(sources) do
- if source.isCollected then
- return true
- end
- end
- end
- return false
-end
-
-
function CanIMogIt:PlayerKnowsTransmog(itemLink)
-- Returns whether this item's appearance is already known by the player.
- local appearanceID = CanIMogIt:GetAppearanceID(itemLink)
- if appearanceID then
- return CanIMogIt:PlayerHasAppearance(appearanceID)
+ appearanceTable = self.Database:GetAppearanceTable(itemLink)
+ if not appearanceTable then return false end
+ if CanIMogIt:IsItemArmor(itemLink) then
+ for knownItemLink, bool in pairs(appearanceTable) do
+ -- if itemLink armor type is the same as one of the knownItemLink armor types
+ if CanIMogIt:IsArmorSubClassIdentical(itemLink, knownItemLink) then
+ return true
+ end
+ end
+ else
+ -- Is not armor, don't worry about same appearance for different types
+ return true
end
return false
end
@@ -403,11 +435,13 @@ end
function CanIMogIt:PlayerKnowsTransmogFromItem(itemLink)
-- Returns whether the transmog is known from this item specifically.
local itemID = CanIMogIt:GetItemID(itemLink)
- return C_TransmogCollection.PlayerHasTransmog(itemID)
+ local hasTransmog = C_TransmogCollection.PlayerHasTransmog(itemID)
+ CanIMogIt.Database:UpdateItem(itemLink, hasTransmog)
+ return hasTransmog
end
-function CanIMogIt:PlayerCanLearnTransmog(itemLink)
+function CanIMogIt:CharacterCanLearnTransmog(itemLink)
-- Returns whether the player can learn the item or not.
if CanIMogIt:GetSlotName(itemLink) == TABARD then return true end
local source = CanIMogIt:GetSource(itemLink)
@@ -489,8 +523,8 @@ function CanIMogIt:GetTooltipText(itemLink)
if CanIMogIt:IsTransmogable(itemLink) then
if CanIMogIt:PlayerKnowsTransmogFromItem(itemLink) then
- if CanIMogIt:IsValidAppearanceForPlayer(itemLink) then
- if CanIMogIt:PlayerIsTooLowLevelForItem(itemLink) then
+ if CanIMogIt:IsValidAppearanceForCharacter(itemLink) then
+ if CanIMogIt:CharacterIsTooLowLevelForItem(itemLink) then
text = CanIMogIt.KNOWN_BUT_TOO_LOW_LEVEL
else
text = CanIMogIt.KNOWN
@@ -499,8 +533,8 @@ function CanIMogIt:GetTooltipText(itemLink)
text = CanIMogIt.KNOWN_BY_ANOTHER_CHARACTER
end
elseif CanIMogIt:PlayerKnowsTransmog(itemLink) then
- if CanIMogIt:IsValidAppearanceForPlayer(itemLink) then
- if CanIMogIt:PlayerIsTooLowLevelForItem(itemLink) then
+ if CanIMogIt:IsValidAppearanceForCharacter(itemLink) then
+ if CanIMogIt:CharacterIsTooLowLevelForItem(itemLink) then
text = CanIMogIt.KNOWN_BUT_TOO_LOW_LEVEL
else
text = CanIMogIt.KNOWN_FROM_ANOTHER_ITEM
@@ -509,7 +543,7 @@ function CanIMogIt:GetTooltipText(itemLink)
text = CanIMogIt.KNOWN_FROM_ANOTHER_ITEM_AND_CHARACTER
end
else
- if CanIMogIt:PlayerCanLearnTransmog(itemLink) then
+ if CanIMogIt:CharacterCanLearnTransmog(itemLink) then
-- Set text to UNKNOWN
text = CanIMogIt.UNKNOWN
else
@@ -532,6 +566,11 @@ end
local function addToTooltip(tooltip, itemLink)
-- Does the calculations for determining what text to
-- display on the tooltip.
+
+ -- TODO: caching doesn't work when the compare tooltip is visible.
+ -- if CanIMogIt.cachedItemLink ~= itemLink then
+ -- print("itemLink changed! " .. itemLink)
+ -- end
local itemInfo = GetItemInfo(itemLink)
if not itemInfo then
CanIMogIt.cachedItemLink = nil
diff --git a/database.lua b/database.lua
new file mode 100644
index 0000000..898a4fd
--- /dev/null
+++ b/database.lua
@@ -0,0 +1,193 @@
+--[[
+ The database has the following structure:
+ {
+ [appearanceID] = {
+ [itemLink] = true,
+ },
+ }
+
+ It is updated at the following events:
+ Character is logged on.
+ The Transmog collection is updated.
+]]
+
+
+local Database = {}
+CanIMogIt.Database = Database
+
+CanIMogIt.sourceIDQueue = {}
+local sourceIDQueueLength = 0
+local getItemInfoReceivedCount = 0
+
+
+function Database:AddItem(itemLink, appearanceID)
+ --[[
+ Adds the given itemLink to the database. Returns whether it was added or not.
+ Will use the given appearanceID if passed, instead of recalculating.
+ ]]
+ appearanceID = appearanceID or CanIMogIt:GetAppearanceID(itemLink)
+ if not appearanceID then return false end
+ local appearanceTable = CanIMogItDatabase[appearanceID]
+ if not appearanceTable then
+ appearanceTable = self:AddAppearance(appearanceID)
+ end
+ if not appearanceTable[itemLink] then
+ appearanceTable[itemLink] = true
+ return true
+ end
+ return false
+end
+
+
+function Database:AddAppearance(appearanceID)
+ --[[
+ Adds the appearanceID to the database if it's not there already.
+ Returns the appearanceTable if it was created, false otherwise.
+ ]]
+ if not appearanceID then return false end
+ if not CanIMogItDatabase[appearanceID] then
+ local appearanceTable = {}
+ CanIMogItDatabase[appearanceID] = appearanceTable
+ return appearanceTable
+ end
+ return false
+end
+
+
+function Database:RemoveItem(itemLink)
+ -- Removes the item from the database.
+ local appearanceID = CanIMogIt:GetAppearanceID(itemLink)
+ if not appearanceID then return false end
+ local appearanceTable = CanIMogItDatabase[appearanceID]
+ if appearanceTable and appearanceTable[itemLink] then
+ appearanceTable[itemLink] = nil
+ return true
+ end
+ return false
+end
+
+
+function Database:RemoveAppearance(appearanceID)
+ --[[
+ Removes the appearanceID from the database. Returns whether
+ it was removed or not.
+ ]]
+ if CanIMogItDatabase[appearanceID] then
+ CanIMogItDatabase[appearanceID] = nil
+ return true
+ end
+ return false
+end
+
+
+function Database:GetAppearanceTable(itemLink)
+ -- Returns the appearance table for the item in the database.
+ local appearanceID = CanIMogIt:GetAppearanceID(itemLink)
+ return CanIMogItDatabase[appearanceID]
+end
+
+
+function Database:GetItem(itemLink)
+ -- Returns whether the item is in the database.
+ local appearanceTable = Database:GetAppearanceTable(itemLink)
+ if appearanceTable then
+ return appearanceTable[itemLink] or false
+ end
+ return false
+end
+
+
+function Database:UpdateAppearances()
+ --[[
+ Updates the database with the current appearances,
+ adding or removing as needed.
+ ]]
+ appearances = CanIMogIt:GetAppearances()
+ for appearanceID, appearance in pairs(appearances) do
+ if appearance.isCollected then
+ self:AddAppearance(appearanceID)
+ self:AddAppearanceSources(appearanceID)
+ else
+ self:RemoveAppearance(appearanceID)
+ end
+ end
+end
+
+
+function Database:AddItemBySourceID(sourceID, appearanceID)
+ local itemLink = select(6, C_TransmogCollection.GetAppearanceSourceInfo(sourceID))
+ if string.find(itemLink, '|h%[%]|h') then -- still cooking
+ -- Call GetItemInfo here so that we can capture the event when it's done cooking.
+ GetItemInfo(itemLink)
+ CanIMogIt.sourceIDQueue[sourceID] = appearanceID
+ sourceIDQueueLength = sourceIDQueueLength + 1
+ return
+ end
+ self:AddItem(itemLink, appearanceID)
+end
+
+
+function Database:AddAppearanceSources(appearanceID)
+ -- Adds the sources (the items) of the appearance to the database.
+ local sources = C_TransmogCollection.GetAppearanceSources(appearanceID)
+ for i, source in pairs(sources) do
+ if source.isCollected then
+ self:AddItemBySourceID(source.sourceID, appearanceID)
+ end
+ end
+end
+
+
+function Database:UpdateItem(itemLink, hasTransmogFromItem)
+ -- Updates the status of the item in the database.
+ if hasTransmogFromItem then
+ self:AddItem(itemLink)
+ else
+ self:RemoveItem(itemLink)
+ --[[ Need to update appearances in case the last item was
+ removed from an appearanceTable]]
+ local appearanceID = CanIMogIt:GetAppearanceID(itemLink)
+ if CanIMogItDatabase[appearanceID] and next(CanIMogItDatabase[appearanceID]) == nil then
+ -- if it's the last item in the appearanceTable, then remove the table.
+ CanIMogItDatabase[appearanceID] = nil
+ end
+ end
+end
+
+
+function CanIMogIt.frame:PlayerLogin(event, ...)
+ if event == "PLAYER_LOGIN" then
+ -- add all known appearanceID's to the database
+ Database:UpdateAppearances()
+ end
+end
+
+
+function CanIMogIt.frame:TransmogCollectionUpdated(event, ...)
+ if event == "TRANSMOG_COLLECTION_UPDATED" then
+ -- add the equipment slot that was changed to the database
+ Database:UpdateAppearances()
+ end
+end
+
+
+function CanIMogIt.frame:GetItemInfoReceived(event, ...)
+ if event ~= "GET_ITEM_INFO_RECEIVED" then return end
+ if next(CanIMogIt.sourceIDQueue) == nil then return end
+ -- Update the database with any items that were still cooking.
+ getItemInfoReceivedCount = getItemInfoReceivedCount + 1
+ if sourceIDQueueLength <= getItemInfoReceivedCount then
+ done = {}
+ for sourceID, appearanceID in pairs(CanIMogIt.sourceIDQueue) do
+ local itemLink = select(6, C_TransmogCollection.GetAppearanceSourceInfo(sourceID))
+ if not string.find(itemLink, '|h%[%]|h') then
+ -- Done cooking!
+ Database:AddItem(itemLink, appearanceID)
+ done[sourceID] = true
+ end
+ end
+ for sourceID, bool in pairs(done) do
+ CanIMogIt.sourceIDQueue[sourceID] = nil
+ end
+ end
+end
diff --git a/options.lua b/options.lua
index 5f67392..724b414 100644
--- a/options.lua
+++ b/options.lua
@@ -40,13 +40,34 @@ CanIMogItOptions_DisplayData = {
CanIMogIt.frame = CreateFrame("Frame", "CanIMogItOptionsFrame", UIParent);
CanIMogIt.frame.name = "Can I Mog It?";
InterfaceOptions_AddCategory(CanIMogIt.frame);
-CanIMogIt.frame:RegisterEvent("ADDON_LOADED")
-CanIMogIt.frame:SetScript("OnEvent", function(self, event, addonName)
+
+local EVENTS = {
+ "ADDON_LOADED",
+ "TRANSMOG_COLLECTION_UPDATED",
+ "PLAYER_LOGIN",
+ "GET_ITEM_INFO_RECEIVED",
+}
+
+for i, event in pairs(EVENTS) do
+ CanIMogIt.frame:RegisterEvent(event);
+end
+
+
+CanIMogIt.frame:SetScript("OnEvent", function(self, event, ...)
+ -- Add functions you want to catch events here
+ self:AddonLoaded(event, ...)
+ self:PlayerLogin(event, ...)
+ self:TransmogCollectionUpdated(event, ...)
+ self:GetItemInfoReceived(event, ...)
+end)
+
+
+function CanIMogIt.frame:AddonLoaded(event, addonName)
if event == "ADDON_LOADED" and addonName == "CanIMogIt" then
CanIMogIt.frame.Loaded()
end
-end)
+end
local function checkboxOnClick(self)
@@ -102,6 +123,9 @@ function CanIMogIt.frame.Loaded()
CanIMogItOptions = CanIMogItOptions_Defaults.options
print("CanIMogItOptions not found, loading defaults!")
end
+ if (not CanIMogItDatabase) then
+ CanIMogItDatabase = {}
+ end
-- Set missing options from the defaults if the version is out of date.
if (CanIMogItOptions["version"] < CanIMogIt_OptionsVersion) then
CanIMogItOptions_temp = CanIMogItOptions_Defaults.options;