Quantcast

Added database to store information across characters

Salvatore Lopiparo [07-23-16 - 23:52]
Added database to store information across characters
Filename
CanIMogIt.toc
code.lua
database.lua
options.lua
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;