-- ------------- -- -- Price Tracker -- -- ------------- -- PriceTracker = { queryDelay = 3000, isSearching = false, settingsVersion = 0.2, icons = { gold = "EsoUI/Art/currency/currency_gold.dds" } } local PriceTracker = PriceTracker -- Addon initialization function PriceTracker:OnLoad(eventCode, addOnName) if(addOnName ~= "PriceTracker") then return end EVENT_MANAGER:RegisterForEvent("OnSearchResultsReceived", EVENT_TRADING_HOUSE_SEARCH_RESULTS_RECEIVED, function(...) self:OnSearchResultsReceived(...) end) EVENT_MANAGER:RegisterForEvent("OnTradingHouseClosed", EVENT_CLOSE_TRADING_HOUSE, function(...) self:OnTradingHouseClosed(...) end) ZO_PreHookHandler(ItemTooltip, "OnUpdate", function() self:OnUpdateTooltip(moc()) end) ZO_PreHookHandler(ItemTooltip, "OnHide", function() self:OnHideTooltip() end) SLASH_COMMANDS["/pt"] = function(...) self:CommandHandler(...) end SLASH_COMMANDS["/pricetracker"] = function(...) self:CommandHandler(...) end local defaults = { itemList = {}, algorithm = self.menu.algorithmTable[1] } -- Load saved settings self.settings = ZO_SavedVars:NewAccountWide("PriceTrackerSettings", self.settingsVersion, nil, defaults) -- Create a button in the trading house window self.button = PriceTrackerControlButton self.button:SetParent(ZO_TradingHouseLeftPaneBrowseItemsCommon) self.button:SetWidth(ZO_TradingHouseLeftPaneBrowseItemsCommonQuality:GetWidth()) self.menu:InitAddonMenu(addOnName) end -- Handle slash commands function PriceTracker:CommandHandler(text) if not text or text == "" or text == "help" then self:ShowHelp() return end if text == "reset" or text == "clear" then self.settings.itemList = {} return end end function PriceTracker:ShowHelp() d("To scan all item prices in all guild stores, click the 'Scan Prices' button in the guild store window.") d(" ") d("/pt help - Show this help") d("/pt clear - Clear stored price values") end function PriceTracker:OnUpdateTooltip(item) if not item or not item.dataEntry or not item.dataEntry.data or self.selectedItem == item then return end self.selectedItem = item local matches = self:GetMatches(item.dataEntry.data.name) if not matches then return end local price = self:SuggestPrice(matches) if not price then return end ZO_Tooltip_AddDivider(ItemTooltip) ItemTooltip:AddLine("Price Tracker", "ZoFontHeader2") local r, g, b = ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB() local stackCount = item.dataEntry.data.stackCount or item.dataEntry.data.stack ItemTooltip:AddLine("Suggested Price: |r", "ZoFontGame", r, g, b, TOPLEFT, MODIFY_TEXT_TYPE_NONE, LEFT, false) ItemTooltip:AddLine(self:FormatTooltipLine("Item price: ", price, stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false) if stackCount > 1 then ItemTooltip:AddLine(self:FormatTooltipLine("Stack price: ", price * stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false) end if self.settings.showMinMax then ItemTooltip:AddLine(self:FormatTooltipLine("Min / Max: ", self.mathUtils:Min(matches) .. " / " .. self.mathUtils:Max(matches)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false) ItemTooltip:AddLine(self:FormatTooltipLine("Min / Max (stack): ", self.mathUtils:Min(matches) * stackCount .. " / " .. self.mathUtils:Max(matches) * stackCount), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false) end if self.settings.showSeen then ItemTooltip:AddLine("Seen " .. #matches .. " times", "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false) end end function PriceTracker:OnHideTooltip() self.selectedItem = nil end function PriceTracker:OnScanPrices() if self.isSearching then return end self.button:SetEnabled(false) self.isSearching = true self.numOfGuilds = GetNumTradingHouseGuilds() self.currentGuild = 0 while not CanSellOnTradingHouse(self.currentGuild) and self.currentGuild < self.numOfGuilds do self.currentGuild = self.currentGuild + 1 end zo_callLater(function() ExecuteTradingHouseSearch(0, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay) end function PriceTracker:OnSearchResultsReceived(eventId, guildId, numItemsOnPage, currentPage, hasMorePages) if not self.isSearching then return end for i = 1, numItemsOnPage do self:AddItem(GetTradingHouseSearchResultItemInfo(i)) end if hasMorePages then zo_callLater(function() ExecuteTradingHouseSearch(currentPage + 1, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay) else if self.currentGuild < self.numOfGuilds then self.currentGuild = self.currentGuild + 1 SelectTradingHouseGuildId(self.currentGuild) zo_callLater(function() ExecuteTradingHouseSearch(0, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay) else self:OnTradingHouseClosed() end end end function PriceTracker:OnTradingHouseClosed() self.isSearching = false self.button:SetEnabled(true) end function PriceTracker:AddItem(icon, itemName, quality, stackCount, sellerName, timeRemaining, purchasePrice) local item = {} item.expiry = timeRemaining + GetTimeStamp() item.icon = icon item.name = itemName item.normalizedName = self:NormalizeName(itemName) item.quality = quality item.stackCount = stackCount item.sellerName = sellerName item.purchasePrice = purchasePrice item.guildId = self.currentGuild item.guildName = GetGuildName(item.guildId) if not self.settings.itemList[item.normalizedName] then self.settings.itemList[item.normalizedName] = {} end -- Do not add items that are already in the database if not self.settings.itemList[item.normalizedName][item.expiry] then self.settings.itemList[item.normalizedName][item.expiry] = item end end function PriceTracker:GetMatches(itemName) local normalizedName = self:NormalizeName(itemName) if not self.settings.itemList or not self.settings.itemList[normalizedName] then return nil end local index = next(self.settings.itemList[normalizedName]) if index == nil then return nil end local matches = {} while index do table.insert(matches, self.settings.itemList[normalizedName][index]) index = next(self.settings.itemList[normalizedName], index) end self.matches = matches return matches end -- TODO: Base calculation on user preference function PriceTracker:SuggestPrice(matches) if self.settings.algorithm == self.menu.algorithmTable[1] then return self.mathUtils:WeightedAverage(matches) end if self.settings.algorithm == self.menu.algorithmTable[2] then return self.mathUtils:Median(matches) end if self.settings.algorithm == self.menu.algorithmTable[3] then return self.mathUtils:Mode(matches) end d("Error deciding how to calculate suggested price") return nil end function PriceTracker:FormatTooltipLine(title, price, stackCount) return string.format("%-30s %7s%s", title, price, zo_iconFormat(self.icons.gold, 16, 16)) end function PriceTracker:NormalizeName(name) return zo_strformat(SI_TOOLTIP_ITEM_NAME, name) end EVENT_MANAGER:RegisterForEvent("PriceTrackerLoaded", EVENT_ADD_ON_LOADED, function(...) PriceTracker:OnLoad(...) end)