--Show a list of Dreamseed when approaching Emerald Bounty Soil. --Show checkmark if the Plant's achievement criteria is complete --10 yd range: Plant Seed --Mechanism Explained: -- Fire "VIGNETTE_MINIMAP_UPDATED" when an Emerald Bounty (growing or not growing) enters/leaves the minimap -- Fire "UPDATE_UI_WIDGET" after planting a seed (Can be triggered by seeds planted across the entire map) -- Cast hidden spell "Dreamseed (425856)" when you get to 6 yd, where you get dismounted and your cursor becomes "Investigate" -- Taking off on Dragon with "Skyward Ascent (372610)" doesn't trigger "PLAYER_STARTED_MOVING", so we need to watch "UNIT_SPELLCAST_SUCCEEDED" local _, addon = ... if not addon.IsGame_10_2_0 then return end local API = addon.API; local GetPlayerMapCoord = API.GetPlayerMapCoord; local GetCreatureIDFromGUID = API.GetCreatureIDFromGUID; local QuickSlot = addon.QuickSlot; local MAPID_EMRALD_DREAM = 2200; local VIGID_BOUNTY = 5971; local RANGE_PLANT_SEED = 10; local FORMAT_ITEM_COUNT_ICON = "%s|T%s:0:0:0:0:64:64:0:64:0:64|t"; local SEED_ITEM_IDS = {208047, 208067, 208066}; --Gigantic, Plump, Small Dreamseed local SEED_SPELL_IDS = {417508, 417645, 417642}; local QUICKSLOT_NAME = "dreamseed"; local math = math; local sqrt = math.sqrt; local format = string.format; local pairs = pairs; local ipairs = ipairs; local C_VignetteInfo = C_VignetteInfo; local GetVignetteInfo = C_VignetteInfo.GetVignetteInfo; local GetVignettes = C_VignetteInfo.GetVignettes local GetStatusBarWidgetVisualizationInfo = C_UIWidgetManager.GetStatusBarWidgetVisualizationInfo; local GetItemDisplayVisualizationInfo = C_UIWidgetManager.GetItemDisplayVisualizationInfo; local GetAllWidgetsBySetID = C_UIWidgetManager.GetAllWidgetsBySetID; local IsFlying = IsFlying; local IsMounted = IsMounted; local GetAchievementCriteriaInfoByID = GetAchievementCriteriaInfoByID; local UIParent = UIParent; local GetItemCount = GetItemCount; local GetItemIconByID = C_Item.GetItemIconByID; local GetBestMapForUnit = C_Map.GetBestMapForUnit; local time = time; local GetTime = GetTime; local UnitChannelInfo = UnitChannelInfo; local IS_SEED_SPELL = {}; do for _, spellID in ipairs(SEED_SPELL_IDS) do IS_SEED_SPELL[spellID] = true; end end local function GetVisibleEmeraldBountyGUID() local vignetteGUIDs = GetVignettes(); local info; for i, vignetteGUID in ipairs(vignetteGUIDs) do info = GetVignetteInfo(vignetteGUID); if info and info.vignetteID == VIGID_BOUNTY then if info.onMinimap then return vignetteGUID, info.objectGUID end end end end local DataProvider = {}; local EL = CreateFrame("Frame", nil, UIParent); function EL:IsTrackedPlantGrowing() return self.trackedObjectGUID and DataProvider:IsPlantGrowing(self.trackedObjectGUID); end function EL:AttemptShowUI() if self:IsTrackedPlantGrowing() then return end self:RegisterEvent("UPDATE_UI_WIDGET"); self:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_START", "player"); self:RegisterUnitEvent("UNIT_SPELLCAST_CHANNEL_STOP", "player"); self.isChanneling = nil; QuickSlot:SetButtonData(SEED_ITEM_IDS, SEED_SPELL_IDS, QUICKSLOT_NAME); QuickSlot:ShowUI(); if self.trackedObjectGUID then local plantName, criteriaComplete = DataProvider:GetPlantNameAndProgress(self.trackedObjectGUID); if plantName then --plantName = "|cff808080"..plantName.."|r"; --DISABLED_FONT_COLOR if criteriaComplete then plantName = "|TInterface/AddOns/Plumber/Art/Button/Checkmark-Green-Shadow:16:16:-4:-2|t"..plantName; --"|A:common-icon-checkmark:0:0:-4:-2|a" |TInterface/AddOns/Plumber/Art/Button/Checkmark-Green:0:0:-4:-2|t end QuickSlot:SetHeaderText(plantName, true); QuickSlot:SetDefaultHeaderText(plantName); end else QuickSlot:SetDefaultHeaderText(nil); end return true end function EL:CloseUI() self:UnregisterEvent("UPDATE_UI_WIDGET"); self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START"); self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_STOP"); QuickSlot:RequestCloseUI(QUICKSLOT_NAME); end function EL:GetMapPointsDistance(x1, y1, x2, y2) local x = self.mapWidth * (x1 - x2); local y = self.mapHeight * (y1 - y2); return sqrt(x*x + y*y) end function EL:OnEvent(event, ...) if event == "VIGNETTE_MINIMAP_UPDATED" then local vignetteGUID, onMinimap = ... if vignetteGUID == self.trackedVignetteGUID then self:StopTrackingPosition(); elseif onMinimap then local info = GetVignetteInfo(vignetteGUID); if info and info.vignetteID == VIGID_BOUNTY then self.trackedObjectGUID = info.objectGUID; self:UpdateTargetLocation(vignetteGUID); end end elseif event == "PLAYER_STARTED_MOVING" then --Fires like crazy when channeling seed (Repeat START/STOP Moving) --Doesn't fire when taking off on a dragon self.isPlayerMoving = true; elseif event == "PLAYER_STOPPED_MOVING" then self.isPlayerMoving = false; if not self.isChanneling then self:CalculatePlayerToTargetDistance(); end elseif event == "PLAYER_MOUNT_DISPLAY_CHANGED" or event == "UNIT_SPELLCAST_SUCCEEDED" then self:CalculatePlayerToTargetDistance(); if IsMounted() then self.isPlayerMoving = true; end if event == "UNIT_SPELLCAST_SUCCEEDED" then local _, _, spellID = ... if spellID and IS_SEED_SPELL[spellID] then --This event fires when start Channeling (and we don't have a SPELLCAST_CHANNEL equivalent, it doesn't make sense) --UNIT_SPELLCAST_CHANNEL_START fires before this event --UNIT_SPELLCAST_CHANNEL_STOP fires regardless of finishing cast or not --So we check if the the channel stops after endTime local name, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo("player"); self.channelEndTime = endTime; else self.channelEndTime = nil; end end elseif event == "UPDATE_UI_WIDGET" then local widgetInfo = ... if DataProvider:IsValuableWidget(widgetInfo.widgetID) then if self:IsTrackedPlantGrowing() then self:StopTrackingPosition(); end end elseif event == "UNIT_SPELLCAST_CHANNEL_START" then self.isChanneling = true; elseif event == "UNIT_SPELLCAST_CHANNEL_STOP" then self.isChanneling = nil; if self.channelEndTime then local t = GetTime(); t = t * 1000; local diff = t - self.channelEndTime; if diff < 200 and diff > -200 then --Natural Complete C_Timer.After(0.2, function() DataProvider:MarkNearestPlantContributed(); end); else --Interrupted end end end end EL:SetScript("OnEvent", EL.OnEvent); function EL:UpdateTargetLocation(vignetteGUID) local position, facing = C_VignetteInfo.GetVignettePosition(vignetteGUID, MAPID_EMRALD_DREAM); self.trackedVignetteGUID = vignetteGUID; if position and not self:IsTrackedPlantGrowing() then self.targetX, self.targetY = position.x, position.y; self:StartTrackingPosition(); else self:StopTrackingPosition(); end end function EL:UpdateTrackedVignetteInfo() local vignetteGUID, objectGUID = GetVisibleEmeraldBountyGUID(); self.trackedObjectGUID = objectGUID; if vignetteGUID then if vignetteGUID ~= self.trackedVignetteGUID then self:UpdateTargetLocation(vignetteGUID); end else self:StopTrackingPosition(); end end function EL:OnUpdate(elapsed) self.t = self.t + elapsed; if self.t > self.t0 then self.t = 0; if self.isPlayerMoving then self:CalculatePlayerToTargetDistance(); end end end function EL:StartTrackingPosition() self:RegisterEvent("PLAYER_STARTED_MOVING"); self:RegisterEvent("PLAYER_STOPPED_MOVING"); self:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED"); --In case player landing right on the soil self:RegisterUnitEvent("UNIT_SPELLCAST_SUCCEEDED", "player"); self.t = 0; self:CalculatePlayerToTargetDistance(); self:SetScript("OnUpdate", self.OnUpdate); end function EL:StopTrackingPosition() if self.trackedVignetteGUID then self.trackedVignetteGUID = nil; self.trackedObjectGUID = nil; self.isPlayerMoving = nil; self.isChanneling = nil; self.isInRange = nil; self:UnregisterEvent("PLAYER_STARTED_MOVING"); self:UnregisterEvent("PLAYER_STOPPED_MOVING"); self:UnregisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED"); self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED"); self:SetScript("OnUpdate", nil); self:OnLeavingSoil(); end end function EL:UpdateMap() self.mapWidth, self.mapHeight = C_Map.GetMapWorldSize(MAPID_EMRALD_DREAM); end EL.mapWidth = 7477.1201171875; EL.mapHeight = 4983.330078125; function EL:CalculatePlayerToTargetDistance() self.playerX, self.playerY = GetPlayerMapCoord(MAPID_EMRALD_DREAM); if self.playerX and self.playerY then local d = self:GetMapPointsDistance(self.playerX, self.playerY, self.targetX, self.targetY); --print(format("Distance: %.1f yd", d)); --Change update frequency dynamically if d <= 10 then self.t0 = 0.2; elseif d < 50 then self.t0 = 0.5; else self.t0 = 1; end if d <= RANGE_PLANT_SEED and not IsFlying() then if not self.isInRange then self.isInRange = true; self:OnApproachingSoil(); end elseif self.isInRange then self.isInRange = false; self:OnLeavingSoil(); end end end function EL:OnApproachingSoil() local success = self:AttemptShowUI(); --Frame not shown if Growth Cycle has already begun end function EL:OnLeavingSoil() self:CloseUI(); end local ZoneTriggerModule; local function EnableModule(state) if state then if not ZoneTriggerModule then local module = API.CreateZoneTriggeredModule("quickslotseed"); ZoneTriggerModule = module; module:SetValidZones(MAPID_EMRALD_DREAM); EL:UpdateMap(); local function OnEnterZoneCallback() EL:RegisterEvent("VIGNETTE_MINIMAP_UPDATED"); EL:UpdateTrackedVignetteInfo(); end local function OnLeaveZoneCallback() EL:UnregisterEvent("VIGNETTE_MINIMAP_UPDATED"); EL:StopTrackingPosition(); end module:SetEnterZoneCallback(OnEnterZoneCallback); module:SetLeaveZoneCallback(OnLeaveZoneCallback); end ZoneTriggerModule:SetEnabled(true); ZoneTriggerModule:Update(); else if ZoneTriggerModule then ZoneTriggerModule:SetEnabled(false); end EL:UnregisterEvent("VIGNETTE_MINIMAP_UPDATED"); EL:StopTrackingPosition(); EL:CloseUI(); end end do local moduleData = { name = addon.L["ModuleName EmeraldBountySeedList"], dbKey = "EmeraldBountySeedList", description = addon.L["ModuleDescription EmeraldBountySeedList"], toggleFunc = EnableModule, categoryID = 10020000, uiOrder = 1, }; addon.ControlCenter:AddModule(moduleData); end local PLANT_DATA = { --17 types of Plant + 1 Tutorial --Kudos to @patf0rd on Twitter! --from VigInfo.ObjectGUID --/dump C_UIWidgetManager.GetStatusBarWidgetVisualizationInfo(5136) (bag max 180(3 min)) --C_UIWidgetManager.GetItemDisplayVisualizationInfo() --C_UIWidgetManager.GetTextWithStateWidgetVisualizationInfo() --shownState = 1, itemInfo = {showAsEarned = true} --AchievementID = 19013; --I Dream of Seeds --https://warcraft.wiki.gg/wiki/UPDATE_UI_WIDGET --C_UIWidgetManager.GetAllWidgetsBySetID --DBC: Vignette - VisibleTrackingQuestID to find 3 chest for each plant --[creatureID] = {criteriaID, widgetID(Growth Cycle), 3 Nurture Widgets, 6 Reward Widgets(There are actually 12 item widgets but only need 6)(Small, Plump, Gigantic Bounty, Small, Medium, Large Bloom(Shared by 3 rarities)), 3 DreamseedChest[VigID] (based on quality)} --Purple/Blue/Green, 3 widgetSetIDs -- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [208443] = {62028, 5084, 4994, 5087, 5088, 5183, 5181, 5182, 5184, 5245, 5247, 5769, 5856, 5857, 869, 918, 919}, --"Ysera's Clover" [208511] = {62029, 5122, 4995, 5089, 5090, 5186, 5188, 5187, 5185, 5248, 5249, 5772, 5854, 5855, 870, 920, 921}, --"Chiming Foxglove" [208556] = {62030, 5123, 4996, 5091, 5092, 5179, 5172, 5180, 5173, 5250, 5251, 5773, 5853, 5853, 871, 922, 923}, --"Dragon's Daffodil" [208563] = {62031, 5125, 5000, 5095, 5096, 5194, 5195, 5196, 5193, 5254, 5255, 5775, 5848, 5849, 873, 926, 927}, --"Singing Weedling" [208605] = {62032, 5126, 5001, 5097, 5098, 5198, 5200, 5199, 5197, 5256, 5257, 5776, 5846, 5847, 879, 928, 929}, --"Fuzzy Licorice" [208606] = {62039, 5127, 5002, 5099, 5100, 5202, 5204, 5203, 5201, 5258, 5259, 5777, 5844, 5845, 875, 930, 931}, --"Lofty Lupin" [208607] = {62038, 5128, 5003, 5101, 5102, 5206, 5208, 5207, 5205, 5260, 5261, 5778, 5842, 5843, 876, 932, 933}, --"Ringing Rose" (ok) [208615] = {62037, 5129, 5004, 5103, 5104, 5210, 5212, 5211, 5209, 5262, 5263, 5779, 5840, 5841, 877, 934, 935}, --"Dreamer's Daisy" (ok) [208616] = {62035, 5130, 5005, 5105, 5106, 5214, 5216, 5215, 5213, 5264, 5265, 5780, 5838, 5839, 878, 936, 937}, --"Viridescent Sprout" [208617] = {62041, 5124, 4999, 5093, 5094, 5191, 5189, 5190, 5192, 5252, 5253, 5774, 5850, 5851, 872, 924, 925}, --"Belligerent Begonias" --Sometimes not visible due to phasing? [209583] = {62027, 5131, 5075, 5108, 5109, 5218, 5220, 5219, 5217, 5266, 5267, 5782, 5783, 5784, 897, 938, 939}, --"Lavatouched Lilies" (ok) [209599] = {62040, 5132, 5076, 5107, 5110, 5222, 5224, 5223, 5221, 5268, 5269, 5787, 5788, 5789, 941, 898, 940}, --"Lullaby Lavender" (ok) [209880] = {62036, 5133, 5077, 5111, 5112, 5226, 5228, 5227, 5225, 5270, 5271, 5790, 5791, 5792, 899, 942, 943}, --"Glade Goldenrod" (ok) [210723] = {62185, 5134, 5113, 5114, 5115, 5230, 5232, 5231, 5229, 5272, 5273, 5793, 5862, 5863, 944, 946, 945}, --"Comfy Chamomile" [210724] = {62186, 5135, 5116, 5117, 5118, 5234, 5236, 5235, 5233, 5274, 5275, 5864, 5865, 5866, 947, 948, 949}, --"Moon Tulip" [210725] = {62189, 5136, 5119, 5120, 5121, 5238, 5240, 5239, 5237, 5276, 5277, 5867, 5868, 5869, 950, 951, 952}, --"Flourishing Scurfpea" [211059] = {62397, 5149, 5146, 5147, 5148, 5242, 5244, 5243, 5241, 5278, 5279, 5876, 5877, 5878, 970, 971, 972}, --"Whisperbloom Sapling" ! (not spawning due to Superbloom phasing) --Ageless Blossom (criteriaID: 62396) }; function DataProvider:IsValuableWidget(widgetID) if not self.valuableWidgets then self.valuableWidgets = {}; for _, data in pairs(PLANT_DATA) do if data[2] then self.valuableWidgets[ data[2] ] = true end end end return widgetID and self.valuableWidgets[widgetID] end function DataProvider:GetGrowthTimesByCreatureID(creatureID) if creatureID and PLANT_DATA[creatureID] then local widgetID = PLANT_DATA[creatureID][2]; local info = widgetID and GetStatusBarWidgetVisualizationInfo(widgetID); if info then return info.barValue, info.barMax end end end function DataProvider:GetGrowthTimes(objectGUID) local creatureID = GetCreatureIDFromGUID(objectGUID); return self:GetGrowthTimesByCreatureID(creatureID) end function DataProvider:IsPlantGrowingByCreatureID(creatureID) local remainingTime = self:GetGrowthTimesByCreatureID(creatureID) return remainingTime and remainingTime > 0 end function DataProvider:IsPlantGrowing(objectGUID) local remainingTime = self:GetGrowthTimes(objectGUID); return remainingTime and remainingTime > 0 end function DataProvider:GetPlantNameByCreatureID(creatureID) if creatureID and PLANT_DATA[creatureID] then local criteriaString = GetAchievementCriteriaInfoByID(19013, PLANT_DATA[creatureID][1]); return criteriaString end end function DataProvider:GetPlantNameAndProgress(objectGUID, isCreatureID) local id; if isCreatureID then id = objectGUID; else id = GetCreatureIDFromGUID(objectGUID); end if id and PLANT_DATA[id] then local criteriaString, criteriaType, completed = GetAchievementCriteriaInfoByID(19013, PLANT_DATA[id][1]); return criteriaString, completed end end function DataProvider.GetActiveDreamseedGrowthTimes() --This function shares between modules. (additional "Growth Cycle Timer" on PlayerChoiceFrame) --If this module is disabled "trackedVignetteGUID" will be nil and we to obtain it local vignetteGUID = EL.trackedVignetteGUID or DataProvider.lastVignetteGUID; if not vignetteGUID then vignetteGUID = GetVisibleEmeraldBountyGUID(); DataProvider.lastVignetteGUID = vignetteGUID; end if vignetteGUID then local info = GetVignetteInfo(vignetteGUID); if info and info.vignetteID == VIGID_BOUNTY then if info.onMinimap then return DataProvider:GetGrowthTimes(info.objectGUID) end end end end function DataProvider:GetNurtureProgress(objectGUID, convertToString) local creatureID = GetCreatureIDFromGUID(objectGUID); if creatureID and PLANT_DATA[creatureID] then local widgetID = PLANT_DATA[creatureID][3]; local info = widgetID and GetStatusBarWidgetVisualizationInfo(widgetID); if info and info.barValue and info.barMax then local barValue, barMax = info.barValue, info.barMax; if not (barValue and barMax) then return end; if barValue > barMax then barValue = barMax; end if convertToString then local str = info.text; if str then str = str..": ".. barValue .. "/" ..barMax else str = barValue .. "/" ..barMax end return str else return barValue, barMax end end end end function DataProvider:GetRewardTierByCreatureID(creatureID) local seedTier, bloomTier = 0, 0; if creatureID and PLANT_DATA[creatureID] then local info, widgetID, itemID; for i = 6, 8 do widgetID = PLANT_DATA[creatureID][i]; info = widgetID and GetItemDisplayVisualizationInfo(widgetID); if info and info.shownState == 1 and info.itemInfo and info.itemInfo.showAsEarned then itemID = info.itemInfo.itemID; if itemID == 210217 then seedTier = 1; --Small Dreamy Bounty elseif itemID == 210218 then seedTier = 2; --Plump Dreamy Bounty elseif itemID == 210219 then seedTier = 3; --Gigantic Dreamy Bounty end break end end for i = 9, 11 do widgetID = PLANT_DATA[creatureID][i]; info = widgetID and GetItemDisplayVisualizationInfo(widgetID); if info and info.shownState == 1 and info.itemInfo and info.itemInfo.showAsEarned then itemID = info.itemInfo.itemID; if itemID == 210224 then bloomTier = 1; --Small <50 elseif itemID == 210225 then bloomTier = 2; --Medium <100 elseif itemID == 210226 then bloomTier = 3; --Large =100 end break end end end return seedTier, bloomTier end function DataProvider:GetRewardTier(objectGUID) local creatureID = GetCreatureIDFromGUID(objectGUID); return self:GetRewardTierByCreatureID(creatureID) end function DataProvider:GetGrowthStateChanged(creatureID, growthRemainingSeconds) if not self.growthEndTimes then self.growthEndTimes = {}; end if growthRemainingSeconds <= 0 then if self.growthEndTimes[creatureID] then return true else return false end end local isChanged = false; local currentTime = time(); if self.growthEndTimes[creatureID] then if currentTime > self.growthEndTimes[creatureID] then isChanged = true; end else isChanged = true; end self.growthEndTimes[creatureID] = currentTime + growthRemainingSeconds; return isChanged end function DataProvider:Debug_ConstructWidgetID() --3 Bounty, 3 Blooms local REWARD_ITEMS = {210217, 210218, 210219, 210224, 210225, 210226}; for creatureID in pairs(PLANT_DATA) do local info, widgetSetID, setWidgets; local itemIDxWidgetID = {}; for i = 15, 17 do widgetSetID = PLANT_DATA[creatureID][i]; setWidgets = GetAllWidgetsBySetID(widgetSetID); if setWidgets then local numItems = 0; for _, widgetInfo in ipairs(setWidgets) do if widgetInfo.widgetType == 27 then numItems = numItems + 1; local widgetID = widgetInfo.widgetID; info = GetItemDisplayVisualizationInfo(widgetID); local itemID = info.itemInfo.itemID; if not itemIDxWidgetID[itemID] then itemIDxWidgetID[itemID] = widgetID; end end end end end local output; for i, itemID in ipairs(REWARD_ITEMS) do local widgetID = itemIDxWidgetID[itemID]; if output then output = output..", "..widgetID; else output = widgetID; end end API.SaveDataUnderKey(creatureID, output); end end --[[ function DataProvider:HasAnyRewardByCreatureID(creatureID) --We now use a more RAM friendly (not getting all the widgets in a wigetSet), but less robust approach if creatureID and PLANT_DATA[creatureID] then if self:IsDreamseedChestAvailableByCreatureID(creatureID) then return true, true end local creatureData = PLANT_DATA[creatureID]; local info; for i = 6, 11 do info = GetItemDisplayVisualizationInfo(creatureData[i]); if info and info.shownState == 1 and info.itemInfo and info.itemInfo.showAsEarned then return true, false end end end return false, false end --]] function DataProvider:HasAnyReward(objectGUID) local creatureID = GetCreatureIDFromGUID(objectGUID); return self:HasAnyRewardByCreatureID(creatureID); end function DataProvider:GetResourcesText() --208066, 208067, 208047 local info = C_CurrencyInfo.GetCurrencyInfo(2650); --Emerald Dewdrop local anyNonZero = false; local text; if info then local quantity = info.quantity; if quantity > 0 then anyNonZero = true; if quantity > 9999 then quantity = "9999+"; end else quantity = "|cff8080800|r"; end text = format(FORMAT_ITEM_COUNT_ICON, quantity, info.iconFileID).." "; else return end local count, icon; for _, itemID in ipairs(SEED_ITEM_IDS) do count = GetItemCount(itemID); icon = GetItemIconByID(itemID); if count == 0 then count = "|cff8080800|r"; else anyNonZero = true; end text = text.." "..format(FORMAT_ITEM_COUNT_ICON, count, icon); end if anyNonZero then return text else --we don't show this text if player has none of the required resources end end function DataProvider:GetChestOwnerCreatureIDs() local tbl = {}; local vignetteID; for creatureID, data in pairs(PLANT_DATA) do for i = 12, 14 do vignetteID = data[i]; tbl[vignetteID] = creatureID; end end return tbl end function DataProvider:SetChestStateByCreatureID(creatureID, state, objectGUID, x, y) if not creatureID then return end; if not self.dreamseedChestStates then self.dreamseedChestStates = {}; end --local plantName = self:GetPlantNameAndProgress(creatureID, true); if state and not self.dreamseedChestStates[creatureID] then self.dreamseedChestStates[creatureID] = {objectGUID, x, y}; if self.BackupCreaturePositions[creatureID] then self.BackupCreaturePositions[creatureID] = {x, y}; --overwrite our database in case Blizzard moves things end else self.dreamseedChestStates[creatureID] = nil; end if not state then --Player looted the chest self:SetPlantContributedByCreatureID(creatureID, nil); end end function DataProvider:SetChestState(objectGUID, state, x, y) local creatureID = GetCreatureIDFromGUID(objectGUID); self:SetChestStateByCreatureID(creatureID, state, objectGUID, x, y) end function DataProvider:TryGetChestInfoByCreatureID(creatureID) if self.dreamseedChestStates then return self.dreamseedChestStates[creatureID] end end function DataProvider:GetPlantCreatureIDs() local tbl = {}; for creatureID in pairs(PLANT_DATA) do tbl[creatureID] = false; end return tbl end function DataProvider:GetBackupLocation(creatureID) return creatureID and self.BackupCreaturePositions[creatureID] end function DataProvider:EnumerateSpawnLocations() return pairs(self.BackupCreaturePositions) end function DataProvider:GetNearbyPlantInfo() local uiMapID = GetBestMapForUnit("player"); if uiMapID ~= MAPID_EMRALD_DREAM then return end; local x, y = GetPlayerMapCoord(MAPID_EMRALD_DREAM); if x and y then EL:UpdateMap(); local distance; for creatureID, position in self:EnumerateSpawnLocations() do distance = EL:GetMapPointsDistance(x, y, position[1], position[2]); if distance <= 15 then return creatureID, position[1], position[2] end end end end function DataProvider:BuildPlantIndexTable() local function SortByCriteriaID(a, b) return PLANT_DATA[a] < PLANT_DATA[b] end local plants = {}; local n = 0; for creatureID, data in pairs(PLANT_DATA) do n = n + 1; plants[n] = creatureID; end table.sort(plants, SortByCriteriaID); local plantXIndex = {}; for index, creatureID in ipairs(plants) do plantXIndex[creatureID] = index; end self.indexXPlant = plants; self.plantXIndex = plantXIndex; end function DataProvider:GetPlantIndexByCreatureID(creatureID) if not self.plantXIndex then self:BuildPlantIndexTable(); end return self.plantXIndex[creatureID] end function DataProvider:GetPlantCreatureIDByIndex(index) if not self.indexXPlant then self:BuildPlantIndexTable(); end return self.indexXPlant[index] end function DataProvider:SetPlantContributedByCreatureID(creatureID, chestFlag) --chestFlag: --nil (no chest) --1 Potential chest (data from WidgetInfo) --2 Chest (Player made contribution during current game session) if not self.plantContributed then self.plantContributed = {}; end self.plantContributed[creatureID] = chestFlag end function DataProvider:HasAnyRewardByCreatureID(creatureID) --Alternative approach due to inconsistent data between actual spawn and widgetInfo --Check if player has successfully cast Dreamseed / made contributions using PlayerChoiceUI if creatureID and PLANT_DATA[creatureID] then if not self.plantContributed then self.plantContributed = {}; end return self.plantContributed[creatureID] ~= nil end return false, false end function DataProvider:MarkNearestPlantContributed() local creatureID = self:GetNearbyPlantInfo(); if creatureID then self:SetPlantContributedByCreatureID(creatureID, 2); end end function DataProvider:GetPlayerDistanceToTarget(playerX, playerY, targetX, targetY) return EL:GetMapPointsDistance(playerX, playerY, targetX, targetY); end ---- Workaround for inconsistent Chest flag: ---- 1. When player logs in, query UIWidgetManager to get chests from previous game session ---- 2. We check if the chests actually exist when player gets to their approximate locations function DataProvider:HasAnyPotentialRewardByCreatureID(creatureID) --We now use a more RAM friendly (not getting all the widgets in a wigetSet), but less robust approach if creatureID and PLANT_DATA[creatureID] then local creatureData = PLANT_DATA[creatureID]; local info; for i = 6, 11 do info = GetItemDisplayVisualizationInfo(creatureData[i]); if info and info.shownState == 1 and info.itemInfo and info.itemInfo.showAsEarned then return true end end end return false end function DataProvider:GetChestOwnerCreatureID(vignetteID) if not self.chestOwnerCreatureIDs then self.chestOwnerCreatureIDs = self:GetChestOwnerCreatureIDs(); end return vignetteID and self.chestOwnerCreatureIDs[vignetteID] end function DataProvider:IsDreamseedChest(vignetteID) return self:GetChestOwnerCreatureID(vignetteID) ~= nil end function DataProvider:RequestScanChests() if self.scanComplete then return end; if self.scanner then self.scanner.t = 0; self.scanner:Show(); return end local anyPotentionReward; local targetList = {}; local chestFlagAuto = 1; local numTargets = 0; for creatureID in pairs(PLANT_DATA) do if self:HasAnyPotentialRewardByCreatureID(creatureID) then anyPotentionReward = true; if self.BackupCreaturePositions[creatureID] then targetList[creatureID] = true; self:SetPlantContributedByCreatureID(creatureID, chestFlagAuto); numTargets = numTargets + 1; end end end --debugprint("numTargets", numTargets) --[[ --Disable AB Testing (Dreamseed Chests Icon can still be stuck on the map, so we have to enable on-set scanning every game session) if PlumberDB then if PlumberDB.DreamseedChestABTesting == nil then PlumberDB.DreamseedChestABTesting = math.random(100) >= 50; end if not PlumberDB.DreamseedChestABTesting then self.scanComplete = true; return end end --]] if anyPotentionReward then if not self.scanner then self.scanner = CreateFrame("Frame"); self.scanner.t = 0; self.scanner:SetScript("OnUpdate", function(f, elapsed) f.t = f.t + elapsed; if f.t > 3 then f.t = 0; local playerX, playerY = GetPlayerMapCoord(MAPID_EMRALD_DREAM); local distance; if playerX and playerY then local anyTarget, nearbyCreatureID, location; for creatureID in pairs(targetList) do anyTarget = true; location = self.BackupCreaturePositions[creatureID]; if location then distance = self:GetPlayerDistanceToTarget(playerX, playerY, location[1], location[2]); if distance <= 200 then nearbyCreatureID = creatureID; break end end end if not anyTarget then f:SetScript("OnUpdate", nil); f.t = nil; self.scanComplete = true; --debugprint("Scan Complete"); end if nearbyCreatureID then local vignetteGUIDs = GetVignettes(); local info, ownerCreatureID; for i, vignetteGUID in ipairs(vignetteGUIDs) do info = GetVignetteInfo(vignetteGUID); if info then ownerCreatureID = self:GetChestOwnerCreatureID(info.vignetteID); if ownerCreatureID then --debugprint(ownerCreatureID, nearbyCreatureID) end if ownerCreatureID == nearbyCreatureID then targetList[ownerCreatureID] = nil; self:SetPlantContributedByCreatureID(nearbyCreatureID, 2); --debugprint("Found One"); break end end end if self.plantContributed[nearbyCreatureID] == chestFlagAuto then if DataProvider:IsPlantGrowingByCreatureID(nearbyCreatureID) then --debugprint("Still Growing"); else self:SetPlantContributedByCreatureID(nearbyCreatureID, nil); --debugprint("Doesn't Exist"); end end end end end end); end else self.scanComplete = true; end end function DataProvider:PauseScanner() if self.scanComplete then return end; if self.scanner then self.scanner:Hide(); end end API.GetActiveDreamseedGrowthTimes = DataProvider.GetActiveDreamseedGrowthTimes; API.DreamseedUtil = DataProvider; DataProvider.BackupCreaturePositions = { [208605] = { 0.634965717792511, 0.4709928631782532, }, [209880] = { 0.4074325561523438, 0.4348400235176086, }, [210725] = { 0.4873448014259338, 0.8045594692230225, }, [208563] = { 0.6302892565727234, 0.5284437537193298, }, [208443] = { 0.5924164056777954, 0.5876181125640869, }, [208606] = { 0.5665966272354126, 0.4488694071769714, }, [211059] = { 0.5114631652832031, 0.5863984823226929, }, [208556] = { 0.6395775675773621, 0.6483616232872009, }, [209583] = { 0.4067537784576416, 0.2478460669517517, }, [209599] = { 0.5651239156723022, 0.3766665458679199, }, [208615] = { 0.4638132452964783, 0.4048779606819153, }, [208511] = { 0.5459136962890625, 0.6763055324554443, }, [208617] = { 0.4990068674087524, 0.3544299006462097, }, [208616] = { 0.400239109992981, 0.5268844366073608, }, [210724] = { 0.4264156222343445, 0.740414023399353, }, [208607] = { 0.4916412830352783, 0.4806915521621704, }, [210723] = { 0.3845012784004211, 0.5920345783233643, }, }; ---- Dev Tool --[[ do function YeetWidget_StatusBar() local GetStatusBarWidgetVisualizationInfo = C_UIWidgetManager.GetStatusBarWidgetVisualizationInfo; local info; local n = 0; for widgetID = 5000, 5200 do info = GetStatusBarWidgetVisualizationInfo(widgetID); if info and info.barMax ~= 180 and info.barValue > 0 then n = n + 1; print("#"..n, widgetID, info.text); end end end function YeetWidgetInfo() for widgetID, widgetType in pairs(EL.widgetData) do print("ID:", widgetID, " Type:", widgetType) end end function YeetPOI() local uiMapID = C_Map.GetBestMapForUnit("player"); local areaPoiIDs = C_AreaPoiInfo.GetAreaPOIForMap(uiMapID); local info; for i, areaPoiID in ipairs(areaPoiIDs) do info = C_AreaPoiInfo.GetAreaPOIInfo(uiMapID, areaPoiID); print(i, info.name); end end function YeetVignette() local vignetteGUIDs = C_VignetteInfo.GetVignettes(); local info, position; local vignettesGUIDs = {}; local total = 0; for i, guid in ipairs(vignetteGUIDs) do info = C_VignetteInfo.GetVignetteInfo(guid); if info and info.name then total = total + 1; vignettesGUIDs[total] = info.vignetteGUID; if info.name == "Dreamseed Chest" and true then print(total, format("#%s type:%s %s WorldMap %s Minimap %s Unique %s %s", info.vignetteID, info.type, info.name, tostring(info.onWorldMap), tostring(info.onMinimap), tostring(info.isUnique), info.vignetteGUID)); end end end local bestUniqueVignetteIndex = C_VignetteInfo.FindBestUniqueVignette(vignettesGUIDs); print("Show ",bestUniqueVignetteIndex); if bestUniqueVignetteIndex then info = C_VignetteInfo.GetVignetteInfo( vignettesGUIDs[bestUniqueVignetteIndex] ); print(info.atlasName); end end function YeetDistance() local uiMapID = C_Map.GetBestMapForUnit("player"); local trueDistance = C_Navigation.GetDistance(); local waypoint = C_Map.GetUserWaypoint(); local x0, y0 = waypoint.position.x, waypoint.position.y; local playerPosition = C_Map.GetPlayerMapPosition(uiMapID, "player"); local x1, y1 = playerPosition:GetXY(); local width, height = C_Map.GetMapWorldSize(uiMapID); local distance = math.sqrt( ((x1 -x0)*width)^2 + ((y1 -y0)*height)^2 ); print(trueDistance, distance); end function TestStatusBar() if not PlumberTestStatusBar then PlumberTestStatusBar = addon.CreateTimerFrame(UIParent); PlumberTestStatusBar:SetPoint("CENTER", UIParent, "CENTER", 0, 0); PlumberTestStatusBar:SetStyle(2); PlumberTestStatusBar:SetWidth(192); PlumberTestStatusBar:UpdateMaxBarFillWidth(); PlumberTestStatusBar:SetReverse(true); PlumberTestStatusBar:SetContinuous(false); end PlumberTestStatusBar:SetDuration(180); end function TestTinyBar() if not PlumberTestStatusBar then PlumberTestStatusBar = addon.CreateTinyStatusBar(UIParent); PlumberTestStatusBar:SetPoint("CENTER", UIParent, "CENTER", 0, 0); PlumberTestStatusBar:UpdateMaxBarFillWidth(); PlumberTestStatusBar:SetReverse(true); end PlumberTestStatusBar:SetDuration(10); end end --]] --[[ local UiWidgets_Reward_Bounty = { --{Small, Plump, Gigantic Bounty, Small, Medium, Large Bloom} {5179, 5172, 5180, 5173, 5245, 5247}, {5183, 5181, 5182, 5184, 5248, 5249}, {5186, 5188, 5187, 5185, 5250, 5251}, {5191, 5189, 5190, 5192, 5252, 5253}, {5194, 5195, 5196, 5193, 5254, 5255}, {5198, 5200, 5199, 5197, 5256, 5257}, {5202, 5204, 5203, 5201, 5258, 5259}, {5206, 5208, 5207, 5205, 5260, 5261}, {5210, 5212, 5211, 5209, 5262, 5263}, {5214, 5216, 5215, 5213, 5264, 5265}, {5218, 5220, 5219, 5217, 5266, 5267}, {5222, 5224, 5223, 5221, 5268, 5269}, {5226, 5228, 5227, 5225, 5270, 5271}, {5230, 5232, 5231, 5229, 5272, 5273}, {5234, 5236, 5235, 5233, 5274, 5275}, {5238, 5240, 5239, 5237, 5276, 5277}, {5242, 5244, 5243, 5241, 5278, 5279}, }; --]]