From 4be0847388ec33da0076d171cc3f5357b2b29029 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:03:20 +0100 Subject: [PATCH 01/10] Database.lua --- Core/Database.lua | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Core/Database.lua b/Core/Database.lua index e60c101..292160b 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -159,7 +159,7 @@ local function PopulateDefaultOptions() local spellID = option.auraID; local castSpellID = option.castAuraID; local untrackableByAura = option.itemTrackable or option.spellTrackable; - local type = option.type; + local optionType = option.type; local isPrism = (option.category == "PRISM") or false; local instantUpdate = not isPrism; local usesCharges = option.charges; @@ -174,7 +174,7 @@ local function PopulateDefaultOptions() aura = spellID, castAura = castSpellID, untrackableByAura = untrackableByAura, - type = type, + type = optionType, isPrism = isPrism, instantUpdate = instantUpdate, usesCharges = usesCharges, @@ -379,7 +379,7 @@ function SIPPYCUP.Database.GetGlobalSetting(key) if global[key] == nil then if type(def) == "table" then - global[key] = global[key] or {}; + global[key] = {}; local t = global[key]; for k, v in pairs(def) do t[k] = t[k] or v; @@ -397,7 +397,6 @@ end ---@param value any function SIPPYCUP.Database.SetGlobalSetting(key, value) local global = SIPPYCUP.global; - -- local def = SIPPYCUP.Database.defaults.global[key]; if type(value) == "table" then global[key] = global[key] or {}; @@ -679,9 +678,6 @@ function SIPPYCUP.Database.CopyProfile(sourceProfileName) DeepCopyDefaults(defaultProfile, sourceFull); DeepMerge(SIPPYCUP.db.profiles[sourceProfileName], sourceFull); - -- Clear current profile minimal data table - SIPPYCUP.db.profiles[currentProfileName] = {}; - -- Save only minimal differences from defaults into current profile local minimalCopy = GetMinimalTable(sourceFull, defaultProfile); SIPPYCUP.db.profiles[currentProfileName] = minimalCopy; @@ -714,21 +710,19 @@ function SIPPYCUP.Database.DeleteProfile(profileName) -- Delete the profile minimal data SIPPYCUP.db.profiles[profileName] = nil; + -- Check current profile via profileKeys mapping for current character + local charKey = SIPPYCUP.Database.GetUnitName(); + local currentProfile = SIPPYCUP.db.profileKeys[charKey] or "Default"; + -- Remove any profileKeys that point to this profile if SIPPYCUP.db.profileKeys then - for charKey, profName in pairs(SIPPYCUP.db.profileKeys) do + for key, profName in pairs(SIPPYCUP.db.profileKeys) do if profName == profileName then - SIPPYCUP.db.profileKeys[charKey] = "Default"; + SIPPYCUP.db.profileKeys[key] = "Default"; end end - else - SIPPYCUP.db.profileKeys = {}; end - -- Check current profile via profileKeys mapping for current character - local charKey = SIPPYCUP.Database.GetUnitName(); - local currentProfile = SIPPYCUP.db.profileKeys[charKey] or "Default"; - if profileName == currentProfile then -- Switch character's profile to Default SIPPYCUP.db.profileKeys[charKey] = "Default"; From a99e18f2742286b8c09a3cebbb33d4aaf0cd7c14 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:03:28 +0100 Subject: [PATCH 02/10] Items.lua --- Core/Items.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/Items.lua b/Core/Items.lua index a51134a..0b7d490 100644 --- a/Core/Items.lua +++ b/Core/Items.lua @@ -176,8 +176,7 @@ function SIPPYCUP.Items.CheckNoAuraSingleOption(profileOptionData, spellID, minS -- Unfortunately duration can be 5 seconds (GCD), so we pull from the spell's base cooldown associated with the item. local duration; - local trackBySpell = false; - local trackByItem = false; + local trackBySpell, trackByItem = SIPPYCUP.Options.ResolveTrackingMethod(optionData); -- Determine tracking method if optionData.type == SIPPYCUP.Options.Type.CONSUMABLE then From 015f817fab21575f48eb81ee98b28b10f7808e30 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:03:53 +0100 Subject: [PATCH 03/10] Options,lua --- Core/Options.lua | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Core/Options.lua b/Core/Options.lua index 5fa4933..a1534fd 100644 --- a/Core/Options.lua +++ b/Core/Options.lua @@ -15,7 +15,7 @@ SIPPYCUP.Options.Type = { }; ---@class SIPPYCUPOption: table ----@field type string Whether this option is a consumable (0) or toy (1). +---@field type number Whether this option is a consumable (0) or toy (1). ---@field auraID number The option's aura ID. ---@field castAuraID number The option's cast aura ID, if none is set then use auraID. ---@field itemID number|table The option's item ID(s) @@ -61,7 +61,7 @@ local function NewOption(params) spellTrackable = params.spellTrackable or false, delayedAura = params.delayedAura or false, cooldownMismatch = params.cooldownMismatch or false, - buildAdded = params.buildAdded or nil, + buildAdded = params.buildAdded, requiresGroup = params.requiresGroup or false, charges = params.charges or false, }; @@ -260,6 +260,28 @@ SIPPYCUP.Options.Data = { NewOption{ type = SIPPYCUP.Options.Type.TOY, auraID = 1254376, itemID = 252265, category = "APPEARANCE", cooldownMismatch = true, buildAdded = "0.7.4|120001" }, -- Hexed Potatoad Mucus }; +---ResolveTrackingMethod returns whether to track a given option by spell or item cooldown. +---@param optionData SIPPYCUPOption +---@return boolean trackBySpell +---@return boolean trackByItem +function SIPPYCUP.Options.ResolveTrackingMethod(optionData) + if optionData.type == SIPPYCUP.Options.Type.CONSUMABLE then + return optionData.spellTrackable, optionData.itemTrackable; + elseif optionData.type == SIPPYCUP.Options.Type.TOY then + if optionData.itemTrackable then + return false, true; + end + if optionData.spellTrackable then + if SIPPYCUP.global.UseToyCooldown then + return false, true; + else + return true, false; + end + end + end + return false, false; +end + local function NormalizeLocName(name) return name:upper():gsub("[^%w]+", "_"); end @@ -370,8 +392,7 @@ function SIPPYCUP.Options.RefreshStackSizes(checkAll, reset, preExpireOnly) -- Helper to check cooldown startTime for item or spell trackable local function GetCooldownStartTime(option) - local trackBySpell = option.spellTrackable or false; - local trackByItem = option.itemTrackable or false; + local trackBySpell, trackByItem = SIPPYCUP.Options.ResolveTrackingMethod(option); if trackByItem then for _, id in ipairs(option.itemID) do From 4ed3285edee4db4b87c9c7571b1d5f465594fb60 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:04:06 +0100 Subject: [PATCH 04/10] Popups.lua --- Modules/Popups/Popups.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Modules/Popups/Popups.lua b/Modules/Popups/Popups.lua index 00e5a5c..a85b72c 100644 --- a/Modules/Popups/Popups.lua +++ b/Modules/Popups/Popups.lua @@ -90,8 +90,6 @@ local deferredActions = SIPPYCUP.Popups.deferredActions; ---@param loc string The loc key identifier to remove. ---@return nil local function RemoveDeferredActionsByLoc(loc) - if not deferredActions then return; end - for i = #deferredActions, 1, -1 do if deferredActions[i].loc == loc then tremove(deferredActions, i); @@ -661,8 +659,7 @@ function SIPPYCUP.Popups.Toggle(itemName, auraID, enabled) local auraInfo = C_UnitAuras.GetPlayerAuraBySpellID(optionData.auraID); local active = false; local startTime = 0; - local trackBySpell = false; - local trackByItem = false; + local trackBySpell, trackByItem = SIPPYCUP.Options.ResolveTrackingMethod(optionData); if optionData.type == SIPPYCUP.Options.Type.CONSUMABLE then trackBySpell = optionData.spellTrackable; @@ -989,7 +986,7 @@ function SIPPYCUP.Popups.HandlePopupAction(data, caller) local auraInstanceID = auraInfo and auraInfo.auraInstanceID; -- First, let's grab the latest currentInstanceID (or have it be nil if none which is fine). - profileOptionData.currentInstanceID = (auraInfo and auraInfo.auraInstanceID) or auraInstanceID; + profileOptionData.currentInstanceID = auraInstanceID; if isConsumable then profileOptionData.currentStacks = SIPPYCUP.Auras.CalculateCurrentStacks(auraInfo, auraID, reason, active); From 04f5c2fa431ad563773ffa81c5c2570245a6fe5e Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:22:54 +0100 Subject: [PATCH 05/10] Auras.lua --- Core/Auras.lua | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Core/Auras.lua b/Core/Auras.lua index b052692..03bb8f6 100644 --- a/Core/Auras.lua +++ b/Core/Auras.lua @@ -3,7 +3,7 @@ SIPPYCUP.Auras = {}; ----CheckEnabledAuras displays data on the currently tracking enabled options (even if they are zero). +---DebugEnabledAuras displays data on the currently tracking enabled options (even if they are zero). ---@return nil function SIPPYCUP.Auras.DebugEnabledAuras() for _, profileOptionData in pairs(SIPPYCUP.Database.auraToProfile) do @@ -107,7 +107,7 @@ local function ParseAura(updateInfo) -- On aura application (spellId secret in combat). local added = updateInfo.addedAuras; if added then - for _, auraInfo in ipairs(updateInfo.addedAuras) do + for _, auraInfo in ipairs(added) do local profileOptionData = SIPPYCUP.Database.FindMatchingProfile(auraInfo.spellId); if profileOptionData and profileOptionData.enable then local skip = SkipDuplicatePrismUnitAura(profileOptionData, auraInfo.auraInstanceID); @@ -127,7 +127,7 @@ local function ParseAura(updateInfo) -- On aura update (auraInstanceID is not secret). local updated = updateInfo.updatedAuraInstanceIDs; if updated then - for _, auraInstanceID in ipairs(updateInfo.updatedAuraInstanceIDs) do + for _, auraInstanceID in ipairs(updated) do local profileOptionData = SIPPYCUP.Database.FindMatchingProfile(nil, auraInstanceID); if profileOptionData and profileOptionData.enable then local auraInfo = GetAuraDataByAuraInstanceID("player", auraInstanceID); @@ -151,7 +151,7 @@ local function ParseAura(updateInfo) -- On aura removal (auraInstanceID is not secret). local removed = updateInfo.removedAuraInstanceIDs; if removed then - for _, auraInstanceID in ipairs(updateInfo.removedAuraInstanceIDs) do + for _, auraInstanceID in ipairs(removed) do local profileOptionData = SIPPYCUP.Database.FindMatchingProfile(nil, auraInstanceID); if profileOptionData and profileOptionData.enable and profileOptionData.currentInstanceID then SIPPYCUP.Database.instanceToProfile[auraInstanceID] = nil; @@ -166,12 +166,20 @@ local function ParseAura(updateInfo) end end +---BuildAuraKey creates a key from an auraID and instanceID. +---@param auraID number The aura ID. +---@param instanceID number The aura instance ID. +---@return string key The key. +local function BuildAuraKey(auraID, instanceID) + return tostring(auraID) .. "-" .. tostring(instanceID); +end + SIPPYCUP.Auras.auraQueue = {}; SIPPYCUP.Auras.auraQueueScheduled = false; ----flushAuraQueue combines all the UNIT_AURA events in the same frame together, filtering them for weird exceptions. +---FlushAuraQueue combines all the UNIT_AURA events in the same frame together, filtering them for weird exceptions. ---@return nil -local function flushAuraQueue() +local function FlushAuraQueue() local queue = SIPPYCUP.Auras.auraQueue; SIPPYCUP.Auras.auraQueue = {}; SIPPYCUP.Auras.auraQueueScheduled = false; @@ -226,7 +234,7 @@ local function flushAuraQueue() for addID, auraInfo in pairs(seenAdd) do if canaccessvalue(auraInfo.spellId) and auraInfo.spellId == auraID then -- cancel any pre-expiration timer keyed by this spell+instance - local key = tostring(auraID) .. "-" .. tostring(removedID); + local key = BuildAuraKey(auraID, removedID); SIPPYCUP.Auras.CancelPreExpirationTimer(key); seenRemoval[removedID] = nil; @@ -247,7 +255,7 @@ local function flushAuraQueue() if seenAdd[updatedAuraInstanceIDs] then if canaccessvalue(seenAdd[updatedAuraInstanceIDs].spellId) then -- cancel any pre-expiration timer keyed by this spell+instance - local key = tostring(seenAdd[updatedAuraInstanceIDs].spellId) .. "-" .. tostring(updatedAuraInstanceIDs); + local key = BuildAuraKey(seenAdd[updatedAuraInstanceIDs].spellId, updatedAuraInstanceIDs); SIPPYCUP.Auras.CancelPreExpirationTimer(key); seenAdd[updatedAuraInstanceIDs] = nil; end @@ -338,7 +346,7 @@ function SIPPYCUP.Auras.Convert(source, data) -- flush on the next frame (which will run the batched UNIT_AURAs) if not SIPPYCUP.Auras.auraQueueScheduled then SIPPYCUP.Auras.auraQueueScheduled = true; - RunNextFrame(flushAuraQueue); + RunNextFrame(FlushAuraQueue); end end @@ -360,7 +368,7 @@ function SIPPYCUP.Auras.CheckAllActiveOptions() for _, profileOptionData in pairs(auraToProfile) do local currentInstanceID = profileOptionData.currentInstanceID; -- True if it's active (or was), false if it's not been active. - local canBeActive = (currentInstanceID or 0) ~= 0; + local canBeActive = currentInstanceID ~= nil; local auraInfo = GetPlayerAuraBySpellID(profileOptionData.aura); if not auraInfo then @@ -386,7 +394,7 @@ function SIPPYCUP.Auras.CheckAllActiveOptions() end else -- There is auraInfo data but the spell was not marked as active, this means it was added but we did not catch it. - Convert(SIPPYCUP.Auras.Sources.ADD_AURA, auraInfo); + Convert(SIPPYCUP.Auras.Sources.ADD_AURA, { auraInfo }); end end end @@ -433,10 +441,6 @@ end local scheduledPreExpirationAuraTimers = {}; -local function BuildAuraKey(auraID, instanceID) - return tostring(auraID) .. "-" .. tostring(instanceID) -end - ---CreatePreExpirationTimer schedules a pre-expiration popup for a specific aura. ---Either pass in `key` directly, or provide `auraID` and `auraInstanceID` to build it. ---@param fireIn number Seconds until the timer fires. @@ -616,8 +620,8 @@ function SIPPYCUP.Auras.CheckPreExpirationForSingleOption(profileOptionData, min -- Schedule for "preOffset" seconds before expiration local fireIn = remaining - preOffset; - if fireIn <= 0 and SIPPYCUP.global.PreExpirationChecks then - -- Less than 60s left and we want pre-expiration popup: fire immediately + if fireIn <= 0 then + -- Less than preOffset left: fire immediately preExpireFired = true; local data = { @@ -629,8 +633,8 @@ function SIPPYCUP.Auras.CheckPreExpirationForSingleOption(profileOptionData, min reason = SIPPYCUP.Popups.Reason.PRE_EXPIRATION, }; SIPPYCUP.Popups.QueuePopupAction(data, "CheckPreExpirationForSingleOption - pre-expiration"); - elseif SIPPYCUP.global.PreExpirationChecks then - -- Schedule our 1m before expiration reminder. + else + -- Schedule our pre-expiration reminder. SIPPYCUP.Auras.CreatePreExpirationTimer(fireIn, auraInfo, key, auraID); end end From fa8325de5eb46aa7b2ed93cda3975759bc7814a7 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:22:59 +0100 Subject: [PATCH 06/10] Options.lua --- Core/Options.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Options.lua b/Core/Options.lua index a1534fd..4eab184 100644 --- a/Core/Options.lua +++ b/Core/Options.lua @@ -45,10 +45,10 @@ local function NewOption(params) end return { - type = params.type or SIPPYCUP.Options.Type.CONSUMABLE, + type = params.type or SIPPYCUP.Options.Type.CONSUMABLE, auraID = params.auraID, castAuraID = params.castAuraID or params.auraID, - itemID = itemIDs; -- always store as a table internally + itemID = itemIDs, -- always store as a table internally loc = params.loc, category = params.category, profile = params.profile, From 19673715c81f5103ecf218e083476e0922f1fa2e Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:23:06 +0100 Subject: [PATCH 07/10] Popups.lua --- Modules/Popups/Popups.lua | 71 ++++++++++----------------------------- 1 file changed, 18 insertions(+), 53 deletions(-) diff --git a/Modules/Popups/Popups.lua b/Modules/Popups/Popups.lua index a85b72c..c8f8b50 100644 --- a/Modules/Popups/Popups.lua +++ b/Modules/Popups/Popups.lua @@ -213,7 +213,7 @@ local function CreatePopup(templateType) self:Enable(); elseif not UnitInParty("player") or GetNumSubgroupMembers() == 0 then popupData.isNotinGroupButRequired = true; - self:Disable() + self:Disable(); end end @@ -234,10 +234,10 @@ local function CreatePopup(templateType) local itemCount = 0; if type(optionData.itemID) == "table" then for _, id in ipairs(optionData.itemID) do - itemCount = itemCount + C_Item.GetItemCount(id) + itemCount = itemCount + C_Item.GetItemCount(id); end else - itemCount = C_Item.GetItemCount(optionData.itemID) + itemCount = C_Item.GetItemCount(optionData.itemID); end local maxCount = itemCount + profileOptionData.currentStacks; @@ -267,7 +267,7 @@ local function CreatePopup(templateType) local optionData = popupData.optionData; if optionData.requiresGroup and popupData.isNotinGroupButRequired and UnitInParty("player") and GetNumSubgroupMembers() > 0 then - self:Enable() + self:Enable(); end GameTooltip:Hide(); end); @@ -424,7 +424,7 @@ local function UpdatePopupVisuals(popup, data) local item = Item:CreateFromItemID(itemID); item:ContinueOnItemLoad(function() - local icon = item:GetItemIcon() + local icon = item:GetItemIcon(); -- If for some reason itemName or itemLink is still not valid by now, pull it again. if not itemName or not itemLink then itemName = item:GetItemName(); @@ -661,24 +661,6 @@ function SIPPYCUP.Popups.Toggle(itemName, auraID, enabled) local startTime = 0; local trackBySpell, trackByItem = SIPPYCUP.Options.ResolveTrackingMethod(optionData); - if optionData.type == SIPPYCUP.Options.Type.CONSUMABLE then - trackBySpell = optionData.spellTrackable; - trackByItem = optionData.itemTrackable; - elseif optionData.type == SIPPYCUP.Options.Type.TOY then - -- Always track by item if itemTrackable - if optionData.itemTrackable then - trackByItem = true; - end - - if optionData.spellTrackable then - if SIPPYCUP.global.UseToyCooldown then - trackByItem = true; - else - trackBySpell = true; - end - end - end - -- If item can only be tracked by the item cooldown (worst) if trackByItem then startTime = C_Item.GetItemCooldown(optionData.itemID); @@ -772,7 +754,7 @@ function SIPPYCUP.Popups.QueuePopupAction(data, caller) local d, c = unpack(entry.args); SIPPYCUP.Popups.HandlePopupAction(d, c); - end) + end); end ---HandlePopupAction executes the popup action for a option aura. @@ -815,17 +797,17 @@ function SIPPYCUP.Popups.HandlePopupAction(data, caller) -- Check for a dirty bag state, if so then defer until it is no longer. if isConsumable and data.needsBagCheck then if SIPPYCUP.Bags.bagGeneration < data.auraGeneration then - SIPPYCUP_OUTPUT.Debug("Reached HandlePopupAction, but bag state is dirty"); - - deferredActions[#deferredActions + 1] = { - data = data, - caller = caller, - blockedBy = { - bag = true, - }, - }; - return; - end + SIPPYCUP_OUTPUT.Debug("Reached HandlePopupAction, but bag state is dirty"); + + deferredActions[#deferredActions + 1] = { + data = data, + caller = caller, + blockedBy = { + bag = true, + }, + }; + return; + end SIPPYCUP_OUTPUT.Debug("Reached HandlePopupAction, bag state is fine so continue."); end @@ -927,24 +909,7 @@ function SIPPYCUP.Popups.HandlePopupAction(data, caller) active = true; end - local trackBySpell = false; - local trackByItem = false; - - if isConsumable then - trackBySpell = optionData.spellTrackable; - trackByItem = optionData.itemTrackable; - elseif isToy then - if optionData.itemTrackable then - trackByItem = true; - end - if optionData.spellTrackable then - if SIPPYCUP.global.UseToyCooldown then - trackByItem = true; - else - trackBySpell = true; - end - end - end + local trackBySpell, trackByItem = SIPPYCUP.Options.ResolveTrackingMethod(optionData); -- Extra check because toys have longer cooldowns than option tend to, so don't fire if cd is still up. if isToy and reason == SIPPYCUP.Popups.Reason.REMOVAL then From abeb3a98c0f0611aa7aeeaf09f24d435b76a8229 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:23:25 +0100 Subject: [PATCH 08/10] Database.lua --- Core/Database.lua | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Core/Database.lua b/Core/Database.lua index 292160b..1fad152 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -134,6 +134,17 @@ SIPPYCUP.Database.defaults = { local defaults = SIPPYCUP.Database.defaults; +---PersistCurrentProfile saves the current runtime profile as minimal differences to saved variables. +---@return nil +local function PersistCurrentProfile() + local currentProfileName = SIPPYCUP.Database.GetCurrentProfileName(); + if not currentProfileName or not SIPPYCUP.Profile then return; end + + local defaultProfile = defaults.profiles.Default or {}; + local minimal = GetMinimalTable(SIPPYCUP.Profile, defaultProfile); + SIPPYCUP.db.profiles[currentProfileName] = minimal; +end + ---Represents a single option's tracking settings within a user profile. ---@class SIPPYCUPProfileOption: table ---@field enable boolean Whether the option is enabled for tracking. @@ -549,11 +560,7 @@ function SIPPYCUP.Database.SetProfile(profileName) local defaultProfile = defaults.profiles.Default or {}; -- Persist current runtime profile before switching - local currentProfileName = SIPPYCUP.Database.GetCurrentProfileName(); - if currentProfileName and SIPPYCUP.Profile then - local minimal = GetMinimalTable(SIPPYCUP.Profile, defaultProfile); - db.profiles[currentProfileName] = minimal; - end + PersistCurrentProfile(); -- Create target profile if it does not exist if not db.profiles[profileName] then @@ -598,11 +605,7 @@ function SIPPYCUP.Database.CreateProfile(profileName) local defaultProfile = defaults.profiles.Default or {}; -- Persist current runtime profile before switching - local currentProfileName = SIPPYCUP.Database.GetCurrentProfileName(); - if currentProfileName and SIPPYCUP.Profile then - local minimal = GetMinimalTable(SIPPYCUP.Profile, defaultProfile); - db.profiles[currentProfileName] = minimal; - end + PersistCurrentProfile(); -- Create an empty minimal profile (no overrides) db.profiles[profileName] = {}; From 7709214c795a5524cf00d3eaa41775750a7e67fb Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:49:42 +0100 Subject: [PATCH 09/10] Update DeleteProfile --- Core/Database.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/Database.lua b/Core/Database.lua index 1fad152..2d70a97 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -730,10 +730,9 @@ function SIPPYCUP.Database.DeleteProfile(profileName) -- Switch character's profile to Default SIPPYCUP.db.profileKeys[charKey] = "Default"; - -- Ensure Default profile exists minimally + -- Ensure Default profile exists minimally (no overrides = empty table) if not SIPPYCUP.db.profiles["Default"] then SIPPYCUP.db.profiles["Default"] = {}; - DeepCopyDefaults(defaults.profiles.Default, SIPPYCUP.db.profiles["Default"]); end -- Update runtime shortcut with full Default profile data From b8514808d076d778afd2534f19efcc79d3a10316 Mon Sep 17 00:00:00 2001 From: Raenore <172234435+Raenore@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:19:36 +0100 Subject: [PATCH 10/10] Add changelog --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3d0a98..8ab378c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. +## [0.7.4] - 2026-03-25 +Minor patch adding select Midnight consumables. + +### Added +- Added [Pango Plating](https://www.wowhead.com/item=268717) (Appearance) ([#96](https://github.com/Raenore/Sippy-Cup/pull/96)). +- Added [Hexed Potatoad](https://www.wowhead.com/item=252265) Mucus (Appearance) and [Researcher's Shadowgraft](https://www.wowhead.com/item=250319) (Effect) ([#97](https://github.com/Raenore/Sippy-Cup/pull/97)). + ## [0.7.3] - 2026-03-05 Minor patch adding select Ruby Feast (Dragonflight) consumables and resolving user-submitted bugs. @@ -79,7 +86,8 @@ Given that Sippy Cup never officially supported combat situations, these restric ## Full Changelog The complete changelog, including older versions, can always be found on [Sippy Cup's GitHub Wiki](https://github.com/Raenore/Sippy-Cup/wiki/Full-Changelog). -[unreleased]: https://github.com/Raenore/Sippy-Cup/compare/0.7.3...HEAD +[unreleased]: https://github.com/Raenore/Sippy-Cup/compare/0.7.4...HEAD +[0.7.4]: https://github.com/Raenore/Sippy-Cup/compare/0.7.3...0.7.4 [0.7.3]: https://github.com/Raenore/Sippy-Cup/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/Raenore/Sippy-Cup/compare/0.7.1...0.7.2 [0.7.1]: https://github.com/Raenore/Sippy-Cup/compare/0.7.0...0.7.1