diff --git a/ConsumesManager.toc b/ConsumesManager.toc new file mode 100644 index 0000000..e8e867c --- /dev/null +++ b/ConsumesManager.toc @@ -0,0 +1,10 @@ +## Interface: 11200 +## Title: |cff00ff00Consumes Manager|r |cff808080[Horyoshi]|r +## Notes: Tracks and manages consumables across your inventory, bank and mail through multiple characters. Keeps track of how much consumes you have left. +## Author: Horyoshi +## Version: 1.5 +## SavedVariables: ConsumesManager_Settings, ConsumesManager_Data + +Main.lua +Itemlist.lua +minimap_icon.xml \ No newline at end of file diff --git a/ConsumesTracker.toc b/ConsumesTracker.toc deleted file mode 100644 index 4a094f2..0000000 --- a/ConsumesTracker.toc +++ /dev/null @@ -1,10 +0,0 @@ -## Interface: 11200 -## Title: |cff00ff00Consumes Tracker|r |cff808080[Horyoshi]|r -## Notes: Tracks your inventory, bank and mail for consumables. Works through multiple characters on the same account. -## Author: Horyoshi -## Version: 1.4.1 -## SavedVariables: ConsumeTracker_Settings, ConsumeTracker_Data - -ConsumesTracker.lua -Consumables.lua -minimap_icon.xml \ No newline at end of file diff --git a/Consumables.lua b/Itemlist.lua similarity index 99% rename from Consumables.lua rename to Itemlist.lua index fe9e8b5..26a8375 100644 --- a/Consumables.lua +++ b/Itemlist.lua @@ -1,112 +1,112 @@ - -consumablesCategories = { - ["Elixirs"] = { - { id = 13452, name = "Elixir of the Mongoose", texture = "Interface\\Icons\\INV_Potion_32", description = "Increases Agility by 25 and critical strike chance by 2% for 1 hour." }, - { id = 9187, name = "Elixir of Greater Agility", texture = "Interface\\Icons\\INV_Potion_94", description = "Increases Agility by 15 for 1 hour." }, - { id = 13453, name = "Elixir of Brute Force", texture = "Interface\\Icons\\INV_Potion_40", description = "Increases Strength and Stamina by 18 for 1 hour." }, - { id = 13447, name = "Elixir of the Sages", texture = "Interface\\Icons\\INV_Potion_29", description = "Increases Intellect and Spirit by 18 for 1 hour." }, - { id = 13454, name = "Greater Arcane Elixir", texture = "Interface\\Icons\\INV_Potion_25", description = "Increases spell damage by up to 35 for 1 hour." }, - { id = 13445, name = "Elixir of Superior Defense", texture = "Interface\\Icons\\INV_Potion_66", description = "Increases armor by 450 for 1 hour." }, - { id = 17708, name = "Elixir of Frost Power", texture = "Interface\\Icons\\INV_Potion_03", description = "Increases Frost spell damage by up to 15 for 1 hour." }, - { id = 22193, name = "Bloodkelp Elixir of Resistance", texture = "Interface\\Icons\\INV_Potion_21", description = "Increases all magical resistances by 15 for 30 minutes." }, - { id = 3825, name = "Elixir of Fortitude", texture = "Interface\\Icons\\INV_Potion_43", description = "Increases the player's maximum health by 120 for 1 hour." }, - { id = 9206, name = "Elixir of Giants", texture = "Interface\\Icons\\INV_Potion_61", description = "Increases your Strength by 25 for 1 hour." }, - { id = 9179, name = "Elixir of Greater Intellect", texture = "Interface\\Icons\\INV_Potion_10", description = "Increases Intellect by 25 for 1 hour." }, - { id = 50237, name = "Elixir of Greater Nature Power", texture = "Interface\\Icons\\INV_Potion_22", description = "Increases nature spell damage by up to 55 for 1 hour." }, - { id = 3386, name = "Elixir of Poison Resistance", texture = "Interface\\Icons\\INV_Potion_12", description = "Imbiber is cured of up to four poisons up to level 60." }, - { id = 9264, name = "Elixir of Poison Resistance", texture = "Interface\\Icons\\INV_Potion_46", description = "Increases spell shadow damage by up to 40 for 30 minutes." }, - { id = 61224, name = "Dreamshard Elixir", texture = "Interface\\Icons\\INV_Potion_12", description = " Grants 2% spell critical and 15 spell power for 1 hour." }, - }, - ["Flasks"] = { - { id = 13513, name = "Flask of Chromatic Resistance", texture = "Interface\\Icons\\INV_Potion_48", description = "Increases your resistance to all schools of magic by 25 for 2 hours." }, - { id = 13511, name = "Flask of Distilled Wisdom", texture = "Interface\\Icons\\INV_Potion_97", description = "Increases the player's maximum mana by 2000 for 2 hours." }, - { id = 13512, name = "Flask of Supreme Power", texture = "Interface\\Icons\\INV_Potion_41", description = " Increases damage done by magical spells and effects by up to 150 for 2 hours." }, - { id = 13510, name = "Flask of the Titans", texture = "Interface\\Icons\\INV_Potion_62", description = "Increases the player's maximum health by 1200 for 2 hours." }, - { id = 13506, name = "Flask of Petrification", texture = "Interface\\Icons\\INV_Potion_26", description = "You turn to stone, protecting you from all physical attacks and spells for 60 sec." }, - }, - ["Protection Potions"] = { - { id = 13457, name = "Greater Fire Protection Potion", texture = "Interface\\Icons\\INV_Potion_24", description = "Absorbs 1950 to 3250 Fire damage for 1 hour." }, - { id = 13456, name = "Greater Frost Protection Potion", texture = "Interface\\Icons\\INV_Potion_20", description = "Absorbs 1950 to 3250 Frost damage for 1 hour." }, - { id = 13458, name = "Greater Nature Protection Potion", texture = "Interface\\Icons\\INV_Potion_22", description = "Absorbs 1950 to 3250 Nature damage for 1 hour." }, - { id = 13459, name = "Greater Shadow Protection Potion", texture = "Interface\\Icons\\INV_Potion_23", description = "Absorbs 1950 to 3250 Shadow damage for 1 hour." }, - { id = 13461, name = "Greater Arcane Protection Potion", texture = "Interface\\Icons\\INV_Potion_83", description = "Absorbs 1950 to 3250 Arcane damage for 1 hour." }, - { id = 13460, name = "Greater Holy Protection Potion", texture = "Interface\\Icons\\INV_Potion_09", description = "Absorbs 1950 to 3251 holy damage for 1 hour." }, - }, - ["Weapon Enhancements"] = { - { id = 12404, name = "Dense Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_SharpeningStone_05", description = "Increases weapon damage by 8 for 30 minutes." }, - { id = 12645, name = "Thorium Shield Spike", texture = "Interface\\Icons\\INV_Misc_Armorkit_20", description = "Attaches a Thorium Spike to a shield, causing 20-30 damage when blocking." }, - { id = 20748, name = "Brilliant Mana Oil", texture = "Interface\\Icons\\INV_Potion_100", description = "Increases mana regeneration by 12 and spell damage by up to 25 for 30 minutes." }, - { id = 20749, name = "Brilliant Wizard Oil", texture = "Interface\\Icons\\INV_Potion_105", description = "Increases spell damage by up to 36 for 30 minutes." }, - { id = 23123, name = "Blessed Wizard Oil", texture = "Interface\\Icons\\INV_Potion_26", description = "Increases spell damage against undead by up to 60. Lasts for 1 hour." }, - { id = 3829, name = "Frost Oil", texture = "Interface\\Icons\\INV_Potion_20", description = "Adds a chance to inflict Frost damage on hit. Lasts 30 minutes." }, - { id = 23122, name = "Consecrated Sharpening Stone", texture = "Interface\\Icons\\inv_stone_sharpeningstone_02", description = "Increases melee attack power by 100 against undead for 30 minutes." }, - { id = 18262, name = "Elemental Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_02", description = "Increases critical strike chance by 2% for 30 minutes." }, - { id = 8928, name = "Instant Poison VI", texture = "Interface\\Icons\\Ability_Poisons", description = "20% chance to inflict 112 to 149 Nature damage." }, - { id = 3776, name = "Crippling Poison II", texture = "Interface\\Icons\\INV_Potion_19", description = "30% chance to inflict the enemie, slowing their movement speed by 60% for 12 sec." }, - { id = 20844, name = "Deadly Poison V", texture = "Interface\\Icons\\Ability_Rogue_DualWeild", description = "30% chance of poisoning the enemy for 136 Nature damage over 12 sec. Stacks up to 5 times on a single target." }, - { id = 5763, name = "Mind-numbing Poison III", texture = "Interface\\Icons\\Spell_Nature_NullifyDisease", description = "20% chance of poisoning the enemy, increasing their casting time by 60% for 14 sec." }, - { id = 10922, name = "Wound Poison IV", texture = "Interface\\Icons\\Ability_PoisonSting", description = " 30% chance of poisoning the enemy, reducing all healing effects used on them by 135 for 15 sec. Stacks up to 5 times on a single target." } - }, - ["Combat Potions"] = { - { id = 13455, name = "Greater Stoneshield Potion", texture = "Interface\\Icons\\INV_Potion_69", description = "Increases armor by 2000 for 2 minutes." }, - { id = 13442, name = "Mighty Rage Potion", texture = "Interface\\Icons\\INV_Potion_41", description = "Restores 45 to 75 rage and increases Strength by 60 for 20 seconds." }, - { id = 20007, name = "Mageblood Potion", texture = "Interface\\Icons\\INV_Potion_45", description = "Regenerates 12 mana every 5 seconds for 1 hour." }, - }, - ["Utility Items"] = { - { id = 20008, name = "Living Action Potion", texture = "Interface\\Icons\\INV_Potion_07", description = "Removes movement impairing effects and grants immunity to them for 5 seconds." }, - { id = 3387, name = "Limited Invulnerability Potion", texture = "Interface\\Icons\\INV_Potion_62", description = "Makes you immune to physical attacks for 6 seconds." }, - { id = 5634, name = "Free Action Potion", texture = "Interface\\Icons\\INV_Potion_04", description = "Grants immunity to stun and movement impairing effects for 30 seconds." }, - { id = 61225, name = "Lucidity Potion", texture = "Interface\\Icons\\INV_Potion_36", description = "Become immune to Sleep, Polymorph, and Charm effects for 30 sec." }, - { id = 9030, name = "Restorative Potion", texture = "Interface\\Icons\\INV_Potion_01", description = "Removes a magic, curse, poison, or disease effect every 5 seconds for 30 seconds." }, - { id = 4390, name = "Iron Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 132 to 219 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." }, - { id = 15993, name = "Thorium Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 300 to 501 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." }, - { id = 10646, name = "Goblin Sapper Charge", texture = "Interface\\Icons\\spell_fire_selfdestruct", description = "Deals 450-751 Fire damage to enemies within 10 yards, also damages you." }, - { id = 61675, name = "Nordanaar Herbal Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Restores 525 to 875 health and 810 to 1350 mana." }, - { id = 13462, name = "Purification Potion", texture = "Interface\\Icons\\INV_Potion_31", description = "Attempts to remove one Curse, one Disease and one Poison from the Imbiber." }, - { id = 13444, name = "Major Mana Potion", texture = "Interface\\Icons\\INV_Potion_76", description = "Restores 1350 to 2251 mana." }, - { id = 13446, name = "Major Healing Potion", texture = "Interface\\Icons\\INV_Potion_54", description = "Restores 1050 to 1751 health." }, - { id = 7676, name = "Thistle Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." }, - }, - ["Food Buffs"] = { - { id = 21023, name = "Dirge's Kickin' Chimaerok Chops", texture = "Interface\\Icons\\INV_Misc_Food_65", description = "Increases Strength by 25 for 1 hour." }, - { id = 18254, name = "Runn Tum Tuber Surprise", texture = "Interface\\Icons\\INV_Misc_Food_63", description = "Increases Intellect by 10 for 1 hour." }, - { id = 13931, name = "Nightfin Soup", texture = "Interface\\Icons\\INV_Drink_17", description = "Restores 8 mana every 5 seconds for 1 hour." }, - { id = 20452, name = "Smoked Desert Dumplings", texture = "Interface\\Icons\\INV_Misc_Food_64", description = "Increases Strength by 20 for 1 hour." }, - { id = 51711, name = "Sweet Mountain Berry (Agility)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Agility by 10 for 1 hour." }, - { id = 51714, name = "Sweet Mountain Berry (Stamina)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Stamina by 10 for 1 hour." }, - { id = 51717, name = "Hardened Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 25 Stamina for 15 minutes" }, - { id = 51720, name = "Power Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 20 Strength for 15 minutes" }, - { id = 21151, name = "Rumsey Rum Black Label", texture = "Interface\\Icons\\INV_Drink_04", description = "Increases Stamina by 15 for 15 minutes." }, - { id = 12218, name = "Monster Omelet", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Increases 12 Stamina and 12 Spirit for 15 minutes." }, - { id = 60977, name = "Danonzo's Tel'Abim Delight", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Gain 22 spell damage for 15 minutes." }, - { id = 60978, name = "Danonzo's Tel'Abim Medley", texture = "Interface\\Icons\\INV_Misc_Food_07", description = "Increase haste by 2% for 15 minutes." }, - { id = 60976, name = "Danonzo's Tel'Abim Surprise", texture = "Interface\\Icons\\INV_Misc_Food_09", description = "Gain 45 Ranged Attack Power for 15 minutes." }, - }, - ["Special Buffs"] = { - { id = 12451, name = "Juju Power", texture = "Interface\\Icons\\INV_Misc_MonsterScales_11", description = "Increases Strength by 30 for 30 minutes." }, - { id = 12460, name = "Juju Might", texture = "Interface\\Icons\\INV_Misc_MonsterScales_07", description = "Increases Attack Power by 40 for 30 minutes." }, - { id = 12455, name = "Juju Ember", texture = "Interface\\Icons\\INV_Misc_MonsterScales_15", description = "Increases Fire Resistance by 15 for 30 minutes." }, - { id = 12457, name = "Juju Chill", texture = "Interface\\Icons\\INV_Misc_MonsterScales_09", description = "Increase Frost resistance by 15 for 10 minutes." }, - { id = 12820, name = "Winterfall Firewater", texture = "Interface\\Icons\\INV_Potion_92", description = "Increases Attack Power by 35 for 20 minutes." }, - { id = 20079, name = "Spirit of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's Spirit by 50 and Stamina by 50 for 2 hours." }, - { id = 20081, name = "Swiftness of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's run speed by 20% for 2 hours." }, - }, -} - - - -consumablesList = {} -consumablesNameToID = {} -consumablesTexture = {} -consumablesDescription = {} - -for categoryName, consumables in pairs(consumablesCategories) do - for _, consumable in ipairs(consumables) do - consumablesList[consumable.id] = consumable.name - consumablesNameToID[consumable.name] = consumable.id - consumablesTexture[consumable.id] = consumable.texture - consumablesDescription[consumable.id] = consumable.description - end -end - - + +consumablesCategories = { + ["Elixirs"] = { + { id = 13452, name = "Elixir of the Mongoose", texture = "Interface\\Icons\\INV_Potion_32", description = "Increases Agility by 25 and critical strike chance by 2% for 1 hour." }, + { id = 9187, name = "Elixir of Greater Agility", texture = "Interface\\Icons\\INV_Potion_94", description = "Increases Agility by 15 for 1 hour." }, + { id = 13453, name = "Elixir of Brute Force", texture = "Interface\\Icons\\INV_Potion_40", description = "Increases Strength and Stamina by 18 for 1 hour." }, + { id = 13447, name = "Elixir of the Sages", texture = "Interface\\Icons\\INV_Potion_29", description = "Increases Intellect and Spirit by 18 for 1 hour." }, + { id = 13454, name = "Greater Arcane Elixir", texture = "Interface\\Icons\\INV_Potion_25", description = "Increases spell damage by up to 35 for 1 hour." }, + { id = 13445, name = "Elixir of Superior Defense", texture = "Interface\\Icons\\INV_Potion_66", description = "Increases armor by 450 for 1 hour." }, + { id = 17708, name = "Elixir of Frost Power", texture = "Interface\\Icons\\INV_Potion_03", description = "Increases Frost spell damage by up to 15 for 1 hour." }, + { id = 22193, name = "Bloodkelp Elixir of Resistance", texture = "Interface\\Icons\\INV_Potion_21", description = "Increases all magical resistances by 15 for 30 minutes." }, + { id = 3825, name = "Elixir of Fortitude", texture = "Interface\\Icons\\INV_Potion_43", description = "Increases the player's maximum health by 120 for 1 hour." }, + { id = 9206, name = "Elixir of Giants", texture = "Interface\\Icons\\INV_Potion_61", description = "Increases your Strength by 25 for 1 hour." }, + { id = 9179, name = "Elixir of Greater Intellect", texture = "Interface\\Icons\\INV_Potion_10", description = "Increases Intellect by 25 for 1 hour." }, + { id = 50237, name = "Elixir of Greater Nature Power", texture = "Interface\\Icons\\INV_Potion_22", description = "Increases nature spell damage by up to 55 for 1 hour." }, + { id = 3386, name = "Elixir of Poison Resistance", texture = "Interface\\Icons\\INV_Potion_12", description = "Imbiber is cured of up to four poisons up to level 60." }, + { id = 9264, name = "Elixir of Poison Resistance", texture = "Interface\\Icons\\INV_Potion_46", description = "Increases spell shadow damage by up to 40 for 30 minutes." }, + { id = 61224, name = "Dreamshard Elixir", texture = "Interface\\Icons\\INV_Potion_12", description = " Grants 2% spell critical and 15 spell power for 1 hour." }, + }, + ["Flasks"] = { + { id = 13513, name = "Flask of Chromatic Resistance", texture = "Interface\\Icons\\INV_Potion_48", description = "Increases your resistance to all schools of magic by 25 for 2 hours." }, + { id = 13511, name = "Flask of Distilled Wisdom", texture = "Interface\\Icons\\INV_Potion_97", description = "Increases the player's maximum mana by 2000 for 2 hours." }, + { id = 13512, name = "Flask of Supreme Power", texture = "Interface\\Icons\\INV_Potion_41", description = " Increases damage done by magical spells and effects by up to 150 for 2 hours." }, + { id = 13510, name = "Flask of the Titans", texture = "Interface\\Icons\\INV_Potion_62", description = "Increases the player's maximum health by 1200 for 2 hours." }, + { id = 13506, name = "Flask of Petrification", texture = "Interface\\Icons\\INV_Potion_26", description = "You turn to stone, protecting you from all physical attacks and spells for 60 sec." }, + }, + ["Protection Potions"] = { + { id = 13457, name = "Greater Fire Protection Potion", texture = "Interface\\Icons\\INV_Potion_24", description = "Absorbs 1950 to 3250 Fire damage for 1 hour." }, + { id = 13456, name = "Greater Frost Protection Potion", texture = "Interface\\Icons\\INV_Potion_20", description = "Absorbs 1950 to 3250 Frost damage for 1 hour." }, + { id = 13458, name = "Greater Nature Protection Potion", texture = "Interface\\Icons\\INV_Potion_22", description = "Absorbs 1950 to 3250 Nature damage for 1 hour." }, + { id = 13459, name = "Greater Shadow Protection Potion", texture = "Interface\\Icons\\INV_Potion_23", description = "Absorbs 1950 to 3250 Shadow damage for 1 hour." }, + { id = 13461, name = "Greater Arcane Protection Potion", texture = "Interface\\Icons\\INV_Potion_83", description = "Absorbs 1950 to 3250 Arcane damage for 1 hour." }, + { id = 13460, name = "Greater Holy Protection Potion", texture = "Interface\\Icons\\INV_Potion_09", description = "Absorbs 1950 to 3251 holy damage for 1 hour." }, + }, + ["Weapon Enhancements"] = { + { id = 12404, name = "Dense Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_SharpeningStone_05", description = "Increases weapon damage by 8 for 30 minutes." }, + { id = 12645, name = "Thorium Shield Spike", texture = "Interface\\Icons\\INV_Misc_Armorkit_20", description = "Attaches a Thorium Spike to a shield, causing 20-30 damage when blocking." }, + { id = 20748, name = "Brilliant Mana Oil", texture = "Interface\\Icons\\INV_Potion_100", description = "Increases mana regeneration by 12 and spell damage by up to 25 for 30 minutes." }, + { id = 20749, name = "Brilliant Wizard Oil", texture = "Interface\\Icons\\INV_Potion_105", description = "Increases spell damage by up to 36 for 30 minutes." }, + { id = 23123, name = "Blessed Wizard Oil", texture = "Interface\\Icons\\INV_Potion_26", description = "Increases spell damage against undead by up to 60. Lasts for 1 hour." }, + { id = 3829, name = "Frost Oil", texture = "Interface\\Icons\\INV_Potion_20", description = "Adds a chance to inflict Frost damage on hit. Lasts 30 minutes." }, + { id = 23122, name = "Consecrated Sharpening Stone", texture = "Interface\\Icons\\inv_stone_sharpeningstone_02", description = "Increases melee attack power by 100 against undead for 30 minutes." }, + { id = 18262, name = "Elemental Sharpening Stone", texture = "Interface\\Icons\\INV_Stone_02", description = "Increases critical strike chance by 2% for 30 minutes." }, + { id = 8928, name = "Instant Poison VI", texture = "Interface\\Icons\\Ability_Poisons", description = "20% chance to inflict 112 to 149 Nature damage." }, + { id = 3776, name = "Crippling Poison II", texture = "Interface\\Icons\\INV_Potion_19", description = "30% chance to inflict the enemie, slowing their movement speed by 60% for 12 sec." }, + { id = 20844, name = "Deadly Poison V", texture = "Interface\\Icons\\Ability_Rogue_DualWeild", description = "30% chance of poisoning the enemy for 136 Nature damage over 12 sec. Stacks up to 5 times on a single target." }, + { id = 5763, name = "Mind-numbing Poison III", texture = "Interface\\Icons\\Spell_Nature_NullifyDisease", description = "20% chance of poisoning the enemy, increasing their casting time by 60% for 14 sec." }, + { id = 10922, name = "Wound Poison IV", texture = "Interface\\Icons\\Ability_PoisonSting", description = " 30% chance of poisoning the enemy, reducing all healing effects used on them by 135 for 15 sec. Stacks up to 5 times on a single target." } + }, + ["Combat Potions"] = { + { id = 13455, name = "Greater Stoneshield Potion", texture = "Interface\\Icons\\INV_Potion_69", description = "Increases armor by 2000 for 2 minutes." }, + { id = 13442, name = "Mighty Rage Potion", texture = "Interface\\Icons\\INV_Potion_41", description = "Restores 45 to 75 rage and increases Strength by 60 for 20 seconds." }, + { id = 20007, name = "Mageblood Potion", texture = "Interface\\Icons\\INV_Potion_45", description = "Regenerates 12 mana every 5 seconds for 1 hour." }, + }, + ["Utility Items"] = { + { id = 20008, name = "Living Action Potion", texture = "Interface\\Icons\\INV_Potion_07", description = "Removes movement impairing effects and grants immunity to them for 5 seconds." }, + { id = 3387, name = "Limited Invulnerability Potion", texture = "Interface\\Icons\\INV_Potion_62", description = "Makes you immune to physical attacks for 6 seconds." }, + { id = 5634, name = "Free Action Potion", texture = "Interface\\Icons\\INV_Potion_04", description = "Grants immunity to stun and movement impairing effects for 30 seconds." }, + { id = 61225, name = "Lucidity Potion", texture = "Interface\\Icons\\INV_Potion_36", description = "Become immune to Sleep, Polymorph, and Charm effects for 30 sec." }, + { id = 9030, name = "Restorative Potion", texture = "Interface\\Icons\\INV_Potion_01", description = "Removes a magic, curse, poison, or disease effect every 5 seconds for 30 seconds." }, + { id = 4390, name = "Iron Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 132 to 219 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." }, + { id = 15993, name = "Thorium Grenade", texture = "Interface\\Icons\\INV_Misc_Bomb_08", description = "Inflicts 300 to 501 Fire damage and stuns enemies in a 3-yard radius for 3 seconds." }, + { id = 10646, name = "Goblin Sapper Charge", texture = "Interface\\Icons\\spell_fire_selfdestruct", description = "Deals 450-751 Fire damage to enemies within 10 yards, also damages you." }, + { id = 61675, name = "Nordanaar Herbal Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Restores 525 to 875 health and 810 to 1350 mana." }, + { id = 13462, name = "Purification Potion", texture = "Interface\\Icons\\INV_Potion_31", description = "Attempts to remove one Curse, one Disease and one Poison from the Imbiber." }, + { id = 13444, name = "Major Mana Potion", texture = "Interface\\Icons\\INV_Potion_76", description = "Restores 1350 to 2251 mana." }, + { id = 13446, name = "Major Healing Potion", texture = "Interface\\Icons\\INV_Potion_54", description = "Restores 1050 to 1751 health." }, + { id = 7676, name = "Thistle Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." }, + }, + ["Food Buffs"] = { + { id = 21023, name = "Dirge's Kickin' Chimaerok Chops", texture = "Interface\\Icons\\INV_Misc_Food_65", description = "Increases Strength by 25 for 1 hour." }, + { id = 18254, name = "Runn Tum Tuber Surprise", texture = "Interface\\Icons\\INV_Misc_Food_63", description = "Increases Intellect by 10 for 1 hour." }, + { id = 13931, name = "Nightfin Soup", texture = "Interface\\Icons\\INV_Drink_17", description = "Restores 8 mana every 5 seconds for 1 hour." }, + { id = 20452, name = "Smoked Desert Dumplings", texture = "Interface\\Icons\\INV_Misc_Food_64", description = "Increases Strength by 20 for 1 hour." }, + { id = 51711, name = "Sweet Mountain Berry (Agility)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Agility by 10 for 1 hour." }, + { id = 51714, name = "Sweet Mountain Berry (Stamina)", texture = "Interface\\Icons\\INV_Misc_Food_40", description = "Increases Stamina by 10 for 1 hour." }, + { id = 51717, name = "Hardened Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 25 Stamina for 15 minutes" }, + { id = 51720, name = "Power Mushroom", texture = "Interface\\Icons\\INV_Mushroom_11", description = "Increases 20 Strength for 15 minutes" }, + { id = 21151, name = "Rumsey Rum Black Label", texture = "Interface\\Icons\\INV_Drink_04", description = "Increases Stamina by 15 for 15 minutes." }, + { id = 12218, name = "Monster Omelet", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Increases 12 Stamina and 12 Spirit for 15 minutes." }, + { id = 60977, name = "Danonzo's Tel'Abim Delight", texture = "Interface\\Icons\\INV_Misc_Food_06", description = "Gain 22 spell damage for 15 minutes." }, + { id = 60978, name = "Danonzo's Tel'Abim Medley", texture = "Interface\\Icons\\INV_Misc_Food_07", description = "Increase haste by 2% for 15 minutes." }, + { id = 60976, name = "Danonzo's Tel'Abim Surprise", texture = "Interface\\Icons\\INV_Misc_Food_09", description = "Gain 45 Ranged Attack Power for 15 minutes." }, + }, + ["Special Buffs"] = { + { id = 12451, name = "Juju Power", texture = "Interface\\Icons\\INV_Misc_MonsterScales_11", description = "Increases Strength by 30 for 30 minutes." }, + { id = 12460, name = "Juju Might", texture = "Interface\\Icons\\INV_Misc_MonsterScales_07", description = "Increases Attack Power by 40 for 30 minutes." }, + { id = 12455, name = "Juju Ember", texture = "Interface\\Icons\\INV_Misc_MonsterScales_15", description = "Increases Fire Resistance by 15 for 30 minutes." }, + { id = 12457, name = "Juju Chill", texture = "Interface\\Icons\\INV_Misc_MonsterScales_09", description = "Increase Frost resistance by 15 for 10 minutes." }, + { id = 12820, name = "Winterfall Firewater", texture = "Interface\\Icons\\INV_Potion_92", description = "Increases Attack Power by 35 for 20 minutes." }, + { id = 20079, name = "Spirit of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's Spirit by 50 and Stamina by 50 for 2 hours." }, + { id = 20081, name = "Swiftness of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's run speed by 20% for 2 hours." }, + }, +} + + + +consumablesList = {} +consumablesNameToID = {} +consumablesTexture = {} +consumablesDescription = {} + +for categoryName, consumables in pairs(consumablesCategories) do + for _, consumable in ipairs(consumables) do + consumablesList[consumable.id] = consumable.name + consumablesNameToID[consumable.name] = consumable.id + consumablesTexture[consumable.id] = consumable.texture + consumablesDescription[consumable.id] = consumable.description + end +end + + diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f288702..0000000 --- a/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/ConsumesTracker.lua b/Main.lua similarity index 58% rename from ConsumesTracker.lua rename to Main.lua index 0dc6e9e..ef9593b 100644 --- a/ConsumesTracker.lua +++ b/Main.lua @@ -1,1551 +1,1639 @@ --- ConsumesTracker.lua - --- Metadata ------------------------------------------------------------------------------------------- -AddonName = "Consumes Tracker" -Version = "1.4.1" -VState = "multi character (beta)" -WindowWidth = 310 - --- Onload & Click/Drag Functionality ------------------------------------------------------------------ -function ConsumesTracker_OnLoad(self) - self:RegisterForDrag("LeftButton") - self:SetScript("OnDragStart", function() ConsumesTracker_OnDragStart(self) end) - self:SetScript("OnDragStop", function() ConsumesTracker_OnDragStop(self) end) - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00" .. AddonName .. " Loaded! (version " .. Version .. "-" .. VState .. ")|r |cff808080[Made by Horyoshi]|r") -end - --- Click Handling ------------------------------------------------------------------------------------- -function ConsumesTracker_HandleClick(self, button) - if not self then return end - if button == "LeftButton" and not IsShiftKeyDown() then - if ConsumeTracker_MainFrame and ConsumeTracker_MainFrame:IsShown() then - ConsumeTracker_MainFrame:Hide() - else - ConsumeTracker_ShowMainWindow() - end - end -end - --- Drag Functions ------------------------------------------------------------------------------------- -function ConsumesTracker_OnDragStart(self) - if IsShiftKeyDown() then - self:StartMoving() - self.isMoving = true - end -end - -function ConsumesTracker_OnDragStop(self) - if self and self.isMoving then - self:StopMovingOrSizing() - self.isMoving = false - end -end - --- Initialize SavedVariables ---------------------------------------------------------------------------- -if not ConsumeTracker_Settings then - ConsumeTracker_Settings = {} -end -if not ConsumeTracker_Data then - ConsumeTracker_Data = {} -end - --- Event frame for updating data ---------------------------------------------------------------------- -local ConsumeTracker_EventFrame = CreateFrame("Frame") -ConsumeTracker_EventFrame:RegisterEvent("PLAYER_LOGIN") -ConsumeTracker_EventFrame:RegisterEvent("BAG_UPDATE") -ConsumeTracker_EventFrame:RegisterEvent("BANKFRAME_OPENED") -ConsumeTracker_EventFrame:RegisterEvent("PLAYERBANKSLOTS_CHANGED") -ConsumeTracker_EventFrame:RegisterEvent("ITEM_LOCK_CHANGED") -ConsumeTracker_EventFrame:RegisterEvent("MAIL_SHOW") -ConsumeTracker_EventFrame:RegisterEvent("MAIL_INBOX_UPDATE") -ConsumeTracker_EventFrame:RegisterEvent("MAIL_CLOSED") - -ConsumeTracker_EventFrame:SetScript("OnEvent", function() - if event == "PLAYER_LOGIN" then - ConsumeTracker_ScanPlayerInventory() - ConsumeTracker_ScanPlayerBank() - ConsumeTracker_ScanPlayerMail() - elseif event == "BAG_UPDATE" then - ConsumeTracker_ScanPlayerInventory() - if BankFrame and BankFrame:IsShown() then - ConsumeTracker_ScanPlayerBank() - end - if MailFrame and MailFrame:IsShown() then - ConsumeTracker_ScanPlayerMail() - end - elseif event == "BANKFRAME_OPENED" then - ConsumeTracker_ScanPlayerBank() - elseif event == "PLAYERBANKSLOTS_CHANGED" then - ConsumeTracker_ScanPlayerBank() - ConsumeTracker_ScanPlayerInventory() - elseif event == "ITEM_LOCK_CHANGED" then - if BankFrame and BankFrame:IsShown() then - ConsumeTracker_ScanPlayerInventory() - ConsumeTracker_ScanPlayerBank() - end - if MailFrame and MailFrame:IsShown() then - ConsumeTracker_ScanPlayerInventory() - ConsumeTracker_ScanPlayerMail() - end - elseif event == "MAIL_SHOW" or event == "MAIL_INBOX_UPDATE" then - ConsumeTracker_ScanPlayerMail() - end -end) - - - -function ConsumeTracker_CreateMainWindow() - -- Main Frame - ConsumeTracker_MainFrame = CreateFrame("Frame", "ConsumeTracker_MainFrame", UIParent) - ConsumeTracker_MainFrame:SetWidth(WindowWidth) - ConsumeTracker_MainFrame:SetHeight(512) - ConsumeTracker_MainFrame:SetPoint("CENTER", UIParent, "CENTER") - ConsumeTracker_MainFrame:SetFrameStrata("DIALOG") - ConsumeTracker_MainFrame:SetMovable(true) - ConsumeTracker_MainFrame:EnableMouse(true) - ConsumeTracker_MainFrame:RegisterForDrag("LeftButton") - ConsumeTracker_MainFrame:SetScript("OnDragStart", function() - this:StartMoving() - end) - ConsumeTracker_MainFrame:SetScript("OnDragStop", function() - this:StopMovingOrSizing() - end) - - table.insert(UISpecialFrames, "ConsumeTracker_MainFrame") - - -- Background Texture - local background = ConsumeTracker_MainFrame:CreateTexture(nil, "BACKGROUND") - background:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Background") - background:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 12, -12) - background:SetPoint("BOTTOMRIGHT", ConsumeTracker_MainFrame, "BOTTOMRIGHT", -12, 12) - - -- Border - ConsumeTracker_MainFrame:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", - tile = true, tileSize = 32, edgeSize = 32, - insets = { left = 11, right = 12, top = 12, bottom = 11 } - }) - ConsumeTracker_MainFrame:SetBackdropColor(0.1, 0.1, 0.1, 1) - - -- Title Text - local titleText = ConsumeTracker_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - titleText:SetText(AddonName) - titleText:SetPoint("TOP", ConsumeTracker_MainFrame, "TOP", 0, -2) - - -- Calculate the width of the title text and adjust the title background accordingly - local titleWidth = titleText:GetStringWidth() + 160 - - -- Title Background - local titleBg = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") - titleBg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") - titleBg:SetWidth(titleWidth) - titleBg:SetHeight(64) - titleBg:SetPoint("TOP", ConsumeTracker_MainFrame, "TOP", 0, 12) - - - -- Close Button - local closeButton = CreateFrame("Button", nil, ConsumeTracker_MainFrame, "UIPanelCloseButton") - closeButton:SetWidth(32) - closeButton:SetHeight(32) - closeButton:SetPoint("TOPRIGHT", ConsumeTracker_MainFrame, "TOPRIGHT", -5, -5) - closeButton:SetScript("OnClick", function() - ConsumeTracker_MainFrame:Hide() - end) - - -- Create a custom tooltip frame - local tooltipFrame = CreateFrame("Frame", "ConsumeTrackerTooltip", UIParent) - tooltipFrame:SetWidth(150) - tooltipFrame:SetHeight(40) - tooltipFrame:SetFrameStrata("TOOLTIP") - tooltipFrame:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, tileSize = 16, edgeSize = 16, - insets = { left = 4, right = 4, top = 4, bottom = 4 } - }) - tooltipFrame:SetBackdropColor(0, 0, 0, 1) - tooltipFrame:Hide() - - -- Tooltip text - local tooltipText = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - tooltipText:SetPoint("CENTER", tooltipFrame, "CENTER", 0, 0) - tooltipFrame.text = tooltipText - - local function ShowTooltip(tab, text) - tooltipFrame.text:SetText(text) - tooltipFrame:SetPoint("BOTTOMLEFT", tab, "TOPLEFT", 0, 0) - tooltipFrame:Show() - end - - local function HideTooltip() - tooltipFrame:Hide() - end - - -- Tabs - local tabs = {} - local function CreateTab(name, texture, xOffset, tooltipText, tabIndex) - local tab = CreateFrame("Button", name, ConsumeTracker_MainFrame) - tab:SetWidth(36) - tab:SetHeight(36) - tab:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", xOffset, -30) - - -- Tab Icon - local icon = tab:CreateTexture(nil, "ARTWORK") - icon:SetTexture(texture) - icon:SetWidth(36) - icon:SetHeight(36) - icon:SetPoint("CENTER", tab, "CENTER", 0, 0) - - tab.icon = icon - - -- Hover Glow - local hoverTexture = tab:CreateTexture(nil, "HIGHLIGHT") - hoverTexture:SetTexture("Interface\\Buttons\\CheckButtonHilight") - hoverTexture:SetBlendMode("ADD") - hoverTexture:SetAllPoints(tab) - - -- Active Highlight - local activeHighlight = tab:CreateTexture(nil, "OVERLAY") - activeHighlight:SetTexture("Interface\\Buttons\\CheckButtonHilight") - activeHighlight:SetBlendMode("ADD") - activeHighlight:SetAllPoints(tab) - activeHighlight:SetWidth(36) - activeHighlight:SetHeight(36) - activeHighlight:Hide() - tab.activeHighlight = activeHighlight - - -- Custom Tooltip Handlers - tab:SetScript("OnEnter", function() - ShowTooltip(tab, tooltipText) - end) - tab:SetScript("OnLeave", HideTooltip) - - -- Store tab for later - tabs[tabIndex] = tab - - return tab - end - - -- Tracker Tab - local tab1 = CreateTab("ConsumeTracker_MainFrameTab1", "Interface\\Icons\\INV_Potion_93", 30, "Consumables Tracker", 1) - tab1:SetScript("OnClick", function() - ConsumeTracker_ShowTab(1) - end) - - -- Options Tab - local tab2 = CreateTab("ConsumeTracker_MainFrameTab2", "Interface\\Icons\\INV_Misc_Gear_01", 80, "Options", 2) - tab2:SetScript("OnClick", function() - ConsumeTracker_ShowTab(2) - end) - - -- Characters Tab - local tab3 = CreateTab("ConsumeTracker_MainFrameTab3", "Interface\\Icons\\Ability_Rogue_Disguise", 130, "Characters", 3) - tab3:SetScript("OnClick", function() - ConsumeTracker_ShowTab(3) - end) - - -- Add Grey Line Under Tabs - local tabsLine = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") - tabsLine:SetHeight(1) - tabsLine:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 12, -72) - tabsLine:SetPoint("TOPRIGHT", ConsumeTracker_MainFrame, "TOPRIGHT", -12, -72) - tabsLine:SetTexture("Interface\\Buttons\\WHITE8x8") - tabsLine:SetVertexColor(0.4, 0.4, 0.4, 1) - - -- Tab Content - ConsumeTracker_MainFrame.tabs = {} - local tab1Content = CreateFrame("Frame", nil, ConsumeTracker_MainFrame) - tab1Content:SetWidth(WindowWidth - 50) - tab1Content:SetHeight(380) - tab1Content:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 30, -80) - ConsumeTracker_MainFrame.tabs[1] = tab1Content - - local tab2Content = CreateFrame("Frame", nil, ConsumeTracker_MainFrame) - tab2Content:SetWidth(WindowWidth - 50) - tab2Content:SetHeight(380) - tab2Content:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 30, -80) - ConsumeTracker_MainFrame.tabs[2] = tab2Content - - local tab3Content = CreateFrame("Frame", nil, ConsumeTracker_MainFrame) - tab3Content:SetWidth(WindowWidth - 50) - tab3Content:SetHeight(380) - tab3Content:SetPoint("TOPLEFT", ConsumeTracker_MainFrame, "TOPLEFT", 30, -80) - ConsumeTracker_MainFrame.tabs[3] = tab3Content - - -- Footer Text - local footerText = ConsumeTracker_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - footerText:SetText("Made by Horyoshi (v" .. Version .. "-" .. VState .. ")") - footerText:SetTextColor(0.6, 0.6, 0.6) - footerText:SetPoint("BOTTOM", ConsumeTracker_MainFrame, "BOTTOM", 0, 15) - - -- Add Grey Line Above Footer - local footerLine = ConsumeTracker_MainFrame:CreateTexture(nil, "ARTWORK") - footerLine:SetHeight(1) - footerLine:SetPoint("BOTTOMLEFT", ConsumeTracker_MainFrame, "BOTTOMLEFT", 12, 30) - footerLine:SetPoint("BOTTOMRIGHT", ConsumeTracker_MainFrame, "BOTTOMRIGHT", -12, 30) - footerLine:SetTexture("Interface\\Buttons\\WHITE8x8") - footerLine:SetVertexColor(0.4, 0.4, 0.4, 1) - - -- Tab Switching Function - function ConsumeTracker_ShowTab(tabIndex) - for index, content in pairs(ConsumeTracker_MainFrame.tabs) do - if index == tabIndex then - content:Show() - tabs[index].activeHighlight:Show() - else - content:Hide() - tabs[index].activeHighlight:Hide() - end - end - end - - -- Set Default Tab - ConsumeTracker_ShowTab(1) - - -- Add Custom Content for Tabs - ConsumeTracker_CreateTrackerContent(tab1Content) - ConsumeTracker_CreateOptionsContent(tab2Content) - ConsumeTracker_CreateCharactersContent(tab3Content) -end - - - --- Function to show the main window -function ConsumeTracker_ShowMainWindow() - if not ConsumeTracker_MainFrame then - ConsumeTracker_CreateMainWindow() - end - ConsumeTracker_MainFrame:Show() - -- Scan inventory when the window is opened - ConsumeTracker_ScanPlayerInventory() - -- Only scan bank and mail if they are open - if BankFrame and BankFrame:IsShown() then - ConsumeTracker_ScanPlayerBank() - end - if MailFrame and MailFrame:IsShown() then - ConsumeTracker_ScanPlayerMail() - end - -- Update the tracker content - ConsumeTracker_UpdateTrackerContent() -end - --- Function to switch tabs -function ConsumeTracker_ShowTab(tabIndex) - if not ConsumeTracker_MainFrame or not ConsumeTracker_MainFrame.tabs then return end - for i, tabContent in pairs(ConsumeTracker_MainFrame.tabs) do - if i == tabIndex then - tabContent:Show() - -- Set the tab button to active - local tabButton = getglobal("ConsumeTracker_MainFrameTab" .. i) - tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-ActiveTab") - else - tabContent:Hide() - -- Set the tab button to inactive - local tabButton = getglobal("ConsumeTracker_MainFrameTab" .. i) - tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-InActiveTab") - end - end -end - --- Function to create the content of the Tracker tab -function ConsumeTracker_CreateTrackerContent(parentFrame) - -- Scroll Frame - local scrollFrame = CreateFrame("ScrollFrame", "ConsumeTracker_ConsumablesScrollFrame", parentFrame) - scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) - scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) - scrollFrame:EnableMouseWheel(true) - - -- Define the OnMouseWheelHandler method - function scrollFrame:OnMouseWheelHandler(delta) - local current = self:GetVerticalScroll() - local maxScroll = self.range or 0 - local newScroll = math.max(0, math.min(current - (delta * 10), maxScroll)) - self:SetVerticalScroll(newScroll) - parentFrame.scrollBar:SetValue(newScroll) - end - - -- Set the OnMouseWheel script - scrollFrame:SetScript("OnMouseWheel", function() - local delta = arg1 - this:OnMouseWheelHandler(delta) - end) - - -- Scroll Child Frame - local scrollChild = CreateFrame("Frame", nil, scrollFrame) - scrollChild:SetWidth(WindowWidth - 10) - scrollChild:SetHeight(1) -- Will adjust later - scrollFrame:SetScrollChild(scrollChild) - - -- Initialize tables to store category and consumable labels - parentFrame.categoryLabels = {} - parentFrame.labels = {} - - -- Sort categories by name for consistent order - local sortedCategories = {} - for categoryName, _ in pairs(consumablesCategories) do - table.insert(sortedCategories, categoryName) - end - table.sort(sortedCategories, function(a, b) return a < b end) - - local index = 0 -- Initialize index for positioning - - for _, categoryName in ipairs(sortedCategories) do - local consumables = consumablesCategories[categoryName] - - -- Create category label - local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - categoryLabel:SetText(categoryName) - categoryLabel:SetTextColor(1, 1, 1) - categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -index * 16) - categoryLabel:Hide() -- Initially hidden - parentFrame.categoryLabels[categoryName] = categoryLabel - index = index + 1 -- Reserve space for category label - - -- Sort consumables by name - table.sort(consumables, function(a, b) return a.name < b.name end) - - for _, consumable in ipairs(consumables) do - local itemID = consumable.id - local itemName = consumable.name - - -- Create consumable label - local label = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - label:SetText(itemName) - label:SetTextColor(1, 1, 1) -- White color - label:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, -index * 16) -- Indent consumables - label:Hide() -- Initially hidden - parentFrame.labels[itemID] = label - - -- Create a frame for tooltip - local tooltipFrame = CreateFrame("Frame", nil, scrollChild) - tooltipFrame:SetWidth(220) - tooltipFrame:SetHeight(16) - tooltipFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, -index * 16) - tooltipFrame:EnableMouse(true) - tooltipFrame:SetScript("OnEnter", function() - ConsumeTracker_ShowConsumableTooltip(itemID) - end) - tooltipFrame:SetScript("OnLeave", function() - if ConsumeTracker_CustomTooltip then - ConsumeTracker_CustomTooltip:Hide() - end - end) - - -- Enable mouse wheel and pass events to the scroll frame - tooltipFrame:EnableMouseWheel(true) - tooltipFrame:SetScript("OnMouseWheel", function() - local delta = arg1 - local scrollFrame = parentFrame.scrollFrame - if scrollFrame and scrollFrame:IsVisible() then - scrollFrame:OnMouseWheelHandler(delta) - end - end) - - -- Associate tooltip frame with the label - label.tooltipFrame = tooltipFrame - - index = index + 1 -- Increment index for next label - end - - -- Add spacing after each category - index = index + 1 - end - - -- Adjust the height of the scroll child - scrollChild.contentHeight = index * 16 - scrollChild:SetHeight(scrollChild.contentHeight) - - -- Message Label - local messageLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") - messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") - messageLabel:SetPoint("CENTER", ConsumeTracker_MainFrame, "CENTER", 0, 0) - messageLabel:Hide() -- Initially hidden - parentFrame.messageLabel = messageLabel - - -- Scroll Bar - local scrollBar = CreateFrame("Slider", "ConsumeTracker_TrackerScrollBar", parentFrame) - scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) - scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) - scrollBar:SetWidth(16) - scrollBar:SetOrientation('VERTICAL') - scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") - scrollBar:SetBackdrop({ - bgFile = "Interface\\Buttons\\UI-SliderBar-Background", - edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", - tile = true, tileSize = 8, edgeSize = 8, - insets = { left = 3, right = 3, top = 3, bottom = 3 } - }) - scrollBar:SetScript("OnValueChanged", function() - local value = this:GetValue() - parentFrame.scrollFrame:SetVerticalScroll(value) - end) - parentFrame.scrollBar = scrollBar - parentFrame.scrollFrame = scrollFrame - parentFrame.scrollChild = scrollChild - - -- Update the scrollbar - ConsumeTracker_UpdateTrackerScrollBar() -end - -function ConsumeTracker_CreateOptionsContent(parentFrame) - -- Create Search Input - local searchBox = CreateFrame("EditBox", "ConsumeTracker_SearchBox", parentFrame, "InputBoxTemplate") - searchBox:SetWidth(WindowWidth - 50) - searchBox:SetHeight(20) - searchBox:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -5) - searchBox:SetAutoFocus(false) - searchBox:SetText("Search...") - searchBox:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color - - searchBox:SetScript("OnEditFocusGained", function() - if this:GetText() == "Search..." then - this:SetText("") - this:SetTextColor(1, 1, 1) -- User input text color - end - end) - - searchBox:SetScript("OnEditFocusLost", function() - if this:GetText() == "" then - this:SetText("Search...") - this:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color - end - end) - - -- Function to update the filter - local function UpdateFilter() - local filterText = string.lower(searchBox:GetText()) - if filterText == "search..." then filterText = "" end - local index = 0 -- Position index - local lineHeight = 18 - - -- Iterate over categories - for _, categoryInfo in ipairs(parentFrame.categoryInfo) do - local categoryLabel = categoryInfo.label - local anyOptionVisible = false - - -- First, check if any options in the category match the filter - for _, optionInfo in ipairs(categoryInfo.options) do - local itemNameLower = string.lower(optionInfo.name) - - if filterText == "" or string.find(itemNameLower, filterText, 1, true) then - anyOptionVisible = true - break - end - end - - -- If any options are visible, show the category label - if anyOptionVisible then - categoryLabel:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - categoryLabel:Show() - index = index + 1 - - -- Now, position and show the matching options - for _, optionInfo in ipairs(categoryInfo.options) do - local optionFrame = optionInfo.frame - local itemNameLower = string.lower(optionInfo.name) - - if filterText == "" or string.find(itemNameLower, filterText, 1, true) then - -- Show the option - optionFrame:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - optionFrame:Show() - index = index + 1 - else - optionFrame:Hide() - end - end - - -- Add extra spacing after the category - index = index + 1 -- Add one extra line of spacing between categories - - else - categoryLabel:Hide() - -- Hide all options under this category - for _, optionInfo in ipairs(categoryInfo.options) do - optionInfo.frame:Hide() - end - end - end - - -- Adjust the scroll child height - parentFrame.scrollChild.contentHeight = index * lineHeight - parentFrame.scrollChild:SetHeight(parentFrame.scrollChild.contentHeight) - - -- Update the scrollbar - ConsumeTracker_UpdateOptionsScrollBar() - end - - searchBox:SetScript("OnTextChanged", function() - UpdateFilter() - end) - - -- Adjust the size of the scroll frame to make room for the search box and add extra spacing - local scrollFrame = CreateFrame("ScrollFrame", "ConsumeTracker_OptionsScrollFrame", parentFrame) - scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -40) -- Changed from -30 to -50 for extra spacing - scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) - scrollFrame:EnableMouseWheel(true) - scrollFrame:SetScript("OnMouseWheel", function() - local delta = arg1 - local current = this:GetVerticalScroll() - local maxScroll = this.maxScroll or 0 - local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) - this:SetVerticalScroll(newScroll) - parentFrame.scrollBar:SetValue(newScroll) - end) - - -- Scroll Child Frame - local scrollChild = CreateFrame("Frame", nil, scrollFrame) - scrollChild:SetWidth(WindowWidth - 10) - scrollChild:SetHeight(1) -- Will adjust later - scrollFrame:SetScrollChild(scrollChild) - parentFrame.scrollChild = scrollChild - parentFrame.scrollFrame = scrollFrame - - -- Sort categories alphabetically - local sortedCategories = {} - for categoryName, _ in pairs(consumablesCategories) do - table.insert(sortedCategories, categoryName) - end - table.sort(sortedCategories) - - -- Checkboxes - parentFrame.checkboxes = {} - parentFrame.categoryInfo = {} - local index = 0 -- Position index - local lineHeight = 18 - - -- Iterate over sorted categories - for _, categoryName in ipairs(sortedCategories) do - local consumables = consumablesCategories[categoryName] - - -- Create category label - local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - categoryLabel:SetText(categoryName) - categoryLabel:SetTextColor(1, 1, 1) - categoryLabel:Show() - - local categoryInfo = { name = categoryName, label = categoryLabel, options = {} } - - index = index + 1 -- Position for the category label - - local numOptionsInCategory = 0 -- Counter for options in this category - - -- Sort the consumables by name - table.sort(consumables, function(a, b) return a.name < b.name end) - - -- For each consumable in the category - for _, consumable in ipairs(consumables) do - local currentItemID = consumable.id - local itemName = consumable.name - - -- Create a frame that encompasses the checkbox and label - local optionFrame = CreateFrame("Frame", "ConsumeTracker_OptionsFrame" .. index, scrollChild) - optionFrame:SetWidth(WindowWidth - 10) - optionFrame:SetHeight(lineHeight) - optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - optionFrame:Show() - - -- Create the checkbox inside the optionFrame - local checkbox = CreateFrame("CheckButton", "ConsumeTracker_OptionsCheckbox" .. index, optionFrame) - checkbox:SetWidth(16) - checkbox:SetHeight(16) - checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) - - -- Create Textures for the checkbox - checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") - checkbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") - checkbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") - checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") - - -- Create FontString for label - local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) - label:SetText(itemName) - - -- Set up the checkbox OnClick handler - checkbox:SetScript("OnClick", function() - ConsumeTracker_Settings[currentItemID] = checkbox:GetChecked() - ConsumeTracker_UpdateTrackerContent() - end) - -- Load saved setting - if ConsumeTracker_Settings[currentItemID] then - checkbox:SetChecked(true) - end - parentFrame.checkboxes[currentItemID] = checkbox - - -- Make the optionFrame clickable - optionFrame:EnableMouse(true) - optionFrame:SetScript("OnMouseDown", function() - checkbox:Click() - end) - - -- Mouseover Tooltip - optionFrame:SetScript("OnEnter", function() - ConsumeTracker_ShowOptionsTooltip(currentItemID) - end) - optionFrame:SetScript("OnLeave", function() - if ConsumeTracker_OptionsTooltip then - ConsumeTracker_OptionsTooltip:Hide() - end - end) - - -- Store option info - table.insert(categoryInfo.options, { frame = optionFrame, name = itemName }) - - index = index + 1 -- Increment index after adding option - numOptionsInCategory = numOptionsInCategory + 1 -- Increment options count - end - - -- Position the category label above its options - categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numOptionsInCategory - 1) * lineHeight) - - -- Store category info - table.insert(parentFrame.categoryInfo, categoryInfo) - - -- Add extra spacing after the category - index = index + 1 -- Add one extra line of spacing between categories - end - - -- Adjust the scroll child height - scrollChild.contentHeight = (index - 1) * lineHeight - scrollChild:SetHeight(scrollChild.contentHeight) - - -- Scroll Bar - local scrollBar = CreateFrame("Slider", "ConsumeTracker_OptionsScrollBar", parentFrame) - scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -56) -- Adjusted for extra spacing - scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) - scrollBar:SetWidth(16) - scrollBar:SetOrientation('VERTICAL') - scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") - scrollBar:SetBackdrop({ - bgFile = "Interface\\Buttons\\UI-SliderBar-Background", - edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", - tile = true, tileSize = 8, edgeSize = 8, - insets = { left = 3, right = 3, top = 3, bottom = 3 } - }) - scrollBar:SetScript("OnValueChanged", function() - local value = this:GetValue() - parentFrame.scrollFrame:SetVerticalScroll(value) - end) - parentFrame.scrollBar = scrollBar - - -- Update the scrollbar - ConsumeTracker_UpdateOptionsScrollBar() -end - - -function ConsumeTracker_CreateCharactersContent(parentFrame) - -- Scroll Frame - local scrollFrame = CreateFrame("ScrollFrame", "ConsumeTracker_CharactersScrollFrame", parentFrame) - scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) - scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) - scrollFrame:EnableMouseWheel(true) - scrollFrame:SetScript("OnMouseWheel", function() - local delta = arg1 - local current = scrollFrame:GetVerticalScroll() - local maxScroll = scrollFrame.maxScroll or 0 - local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) - scrollFrame:SetVerticalScroll(newScroll) - if parentFrame.scrollBar then - parentFrame.scrollBar:SetValue(newScroll) - end - end) - - -- Scroll Child Frame - local scrollChild = CreateFrame("Frame", nil, scrollFrame) - scrollChild:SetWidth(WindowWidth - 10) - scrollChild:SetHeight(1) - scrollFrame:SetScrollChild(scrollChild) - - -- Initialize variables - parentFrame.checkboxes = {} - local index = 0 -- Position index - - -- Ensure settings table for characters exists - ConsumeTracker_Settings["Characters"] = ConsumeTracker_Settings["Characters"] or {} - - -- Get list of characters - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - local playerName = UnitName("player") - - local characterList = {} - - -- Ensure data structure exists - if ConsumeTracker_Data[realmName] and ConsumeTracker_Data[realmName][faction] then - for characterName, _ in pairs(ConsumeTracker_Data[realmName][faction]) do - table.insert(characterList, characterName) - end - end - - -- Ensure current character is included - local playerInList = false - for _, name in ipairs(characterList) do - if name == playerName then - playerInList = true - break - end - end - if not playerInList then - table.insert(characterList, playerName) - end - - -- Sort the character list - table.sort(characterList) - - -- Create FontString for title - local title = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") - title:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, 0) -- Offset from the top - title:SetText("Select Characters to Track") - title:SetTextColor(1, 1, 1) - - -- Offset index to start below the title - local startYOffset = 20 - - -- For each character - for _, characterName in ipairs(characterList) do - index = index + 1 - - -- Create a local copy of characterName for the closure - local currentCharacterName = characterName - - -- Create a frame that encompasses the checkbox and label - local optionFrame = CreateFrame("Frame", "ConsumeTracker_CharacterFrame" .. index, scrollChild) - optionFrame:SetWidth(WindowWidth - 10) - optionFrame:SetHeight(18) - optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -(startYOffset + (index - 1) * 20)) - - -- Create the checkbox inside the optionFrame - local checkbox = CreateFrame("CheckButton", "ConsumeTracker_CharacterCheckbox" .. index, optionFrame) - checkbox:SetWidth(16) - checkbox:SetHeight(16) - checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) - - -- Create Textures for the checkbox - checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") - checkbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") - checkbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") - checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") - - -- Create FontString for label - local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) - label:SetText(currentCharacterName) - - -- Set up the checkbox OnClick handler - checkbox:SetScript("OnClick", function() - if checkbox:GetChecked() then - ConsumeTracker_Settings["Characters"][currentCharacterName] = true - else - ConsumeTracker_Settings["Characters"][currentCharacterName] = false - end - ConsumeTracker_UpdateTrackerContent() - end) - -- Load saved setting - if ConsumeTracker_Settings["Characters"][currentCharacterName] == nil then - -- Default to checked - checkbox:SetChecked(true) - ConsumeTracker_Settings["Characters"][currentCharacterName] = true - else - checkbox:SetChecked(ConsumeTracker_Settings["Characters"][currentCharacterName]) - end - parentFrame.checkboxes[currentCharacterName] = checkbox - - -- Make the optionFrame clickable (so clicking on the label checks/unchecks the box) - optionFrame:EnableMouse(true) - optionFrame:SetScript("OnMouseDown", function() - checkbox:Click() - end) - end - - -- Adjust the scroll child height - scrollChild.contentHeight = startYOffset + index * 20 - scrollChild:SetHeight(scrollChild.contentHeight) - - -- Scroll Bar - local scrollBar = CreateFrame("Slider", "ConsumeTracker_CharactersScrollBar", parentFrame) - scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) - scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) - scrollBar:SetWidth(16) - scrollBar:SetOrientation('VERTICAL') - scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") - scrollBar:SetBackdrop({ - bgFile = "Interface\\Buttons\\UI-SliderBar-Background", - edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", - tile = true, tileSize = 8, edgeSize = 8, - insets = { left = 3, right = 3, top = 3, bottom = 3 } - }) - scrollBar:SetScript("OnValueChanged", function() - local value = scrollBar:GetValue() - scrollFrame:SetVerticalScroll(value) - end) - parentFrame.scrollBar = scrollBar - parentFrame.scrollFrame = scrollFrame - parentFrame.scrollChild = scrollChild - - -- Update the scrollbar - ConsumeTracker_UpdateCharactersScrollBar() -end - - -function ConsumeTracker_UpdateTrackerContent() - if not ConsumeTracker_MainFrame or not ConsumeTracker_MainFrame.tabs or not ConsumeTracker_MainFrame.tabs[1] then - return - end - - local trackerFrame = ConsumeTracker_MainFrame.tabs[1] - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - local playerName = UnitName("player") - - -- Ensure data structure exists - if not ConsumeTracker_Data[realmName] or not ConsumeTracker_Data[realmName][faction] then - return - end - local data = ConsumeTracker_Data[realmName][faction] - - -- Check if bank and mail have been scanned for current character - local currentCharData = data[playerName] - local bankScanned = currentCharData and currentCharData["bank"] ~= nil - local mailScanned = currentCharData and currentCharData["mail"] ~= nil - - -- If bank and mail have not been scanned, show message and hide item labels - if not bankScanned or not mailScanned then - -- Hide all labels - for _, label in pairs(trackerFrame.labels) do - label:Hide() - if label.tooltipFrame then - label.tooltipFrame:Hide() - end - end - -- Hide category labels - for _, categoryLabel in pairs(trackerFrame.categoryLabels) do - categoryLabel:Hide() - end - - -- Show message - trackerFrame.messageLabel:SetText("|cffff0000This character is not scanned yet|r\n\n|cffffffffOpen your |rBank|cffffffff and |rMail |cffffffffto get started|r") - trackerFrame.messageLabel:Show() - - -- Adjust scroll child height - trackerFrame.scrollChild:SetHeight(50) - - -- Update the tracker scrollbar - ConsumeTracker_UpdateTrackerScrollBar() - - return - end - - -- Proceed with normal content update - local index = 0 -- Positioning index for items - local totalCounts = {} -- Track total counts for each item - local hasAnyVisibleItems = false -- Track if any items are visible - - -- Sort categories alphabetically for consistent order - local sortedCategories = {} - for categoryName, _ in pairs(consumablesCategories) do - table.insert(sortedCategories, categoryName) - end - table.sort(sortedCategories) - - -- Hide all labels initially to ensure proper cleanup - for _, label in pairs(trackerFrame.labels) do - label:Hide() - if label.tooltipFrame then - label.tooltipFrame:Hide() - end - end - - -- Hide all category labels initially - for _, categoryLabel in pairs(trackerFrame.categoryLabels) do - categoryLabel:Hide() - end - - -- Ensure character settings exist - ConsumeTracker_Settings["Characters"] = ConsumeTracker_Settings["Characters"] or {} - - -- Iterate over categories - for _, categoryName in ipairs(sortedCategories) do - local consumables = consumablesCategories[categoryName] - local hasVisibleItems = false - local categoryLabel = trackerFrame.categoryLabels[categoryName] - - -- Ensure the category label exists - if not categoryLabel then - -- Create category label dynamically if missing - categoryLabel = trackerFrame.scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - categoryLabel:SetText(categoryName) - categoryLabel:SetTextColor(1, 1, 1) - trackerFrame.categoryLabels[categoryName] = categoryLabel - end - - -- Position category label (always above items) - categoryLabel:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 0, -index * 16) - categoryLabel:Hide() -- Initially hidden - - index = index + 1 -- Reserve space for the category label - - -- Calculate totals and show items if enabled - for _, consumable in ipairs(consumables) do - local itemID = consumable.id - if ConsumeTracker_Settings[itemID] then - hasVisibleItems = true - hasAnyVisibleItems = true -- Set to true when an item is displayed - totalCounts[itemID] = 0 -- Initialize count - - -- Sum counts across all selected characters - for character, charData in pairs(data) do - if ConsumeTracker_Settings["Characters"][character] == true then - -- Proceed with counting - local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 - local bank = charData["bank"] and charData["bank"][itemID] or 0 - local mail = charData["mail"] and charData["mail"][itemID] or 0 - totalCounts[itemID] = totalCounts[itemID] + inventory + bank + mail - end - end - - -- Show and position item label - local label = trackerFrame.labels[itemID] - if not label then - -- Create the label dynamically if missing - label = trackerFrame.scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - trackerFrame.labels[itemID] = label - -- Tooltip frame - local tooltipFrame = CreateFrame("Frame", nil, trackerFrame.scrollChild) - tooltipFrame:SetWidth(WindowWidth - 30) - tooltipFrame:SetHeight(16) - tooltipFrame:EnableMouse(true) - tooltipFrame:SetScript("OnEnter", function() ConsumeTracker_ShowConsumableTooltip(itemID) end) - tooltipFrame:SetScript("OnLeave", function() - if ConsumeTracker_CustomTooltip then - ConsumeTracker_CustomTooltip:Hide() - end - end) - label.tooltipFrame = tooltipFrame - end - - local totalCount = totalCounts[itemID] - label:SetText(consumable.name .. " (" .. totalCount .. ")") - label:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 20, -index * 16) - label:Show() - - -- Adjust label color based on count - if totalCount == 0 then - label:SetTextColor(1, 0, 0) -- Red - elseif totalCount < 10 then - label:SetTextColor(1, 0.4, 0) -- Orange - elseif totalCount <= 20 then - label:SetTextColor(1, 0.85, 0) -- Yellow - else - label:SetTextColor(0, 1, 0) -- Green - end - - -- Show tooltip frame - if label.tooltipFrame then - label.tooltipFrame:SetPoint("TOPLEFT", trackerFrame.scrollChild, "TOPLEFT", 20, -index * 16) - label.tooltipFrame:SetWidth(label:GetStringWidth()) - label.tooltipFrame:SetHeight(16) - label.tooltipFrame:Show() - end - - index = index + 1 - end - end - - -- Show the category label if it has visible items - if hasVisibleItems then - categoryLabel:Show() - else - index = index - 1 -- Remove extra space if no items - end - end - - -- Adjust scroll child height based on the total number of items - local contentHeight = index * 16 - if contentHeight < 50 then - contentHeight = 50 -- Minimum height to display the message label - end - trackerFrame.scrollChild:SetHeight(contentHeight) - - if not hasAnyVisibleItems then - -- Show message when no items are selected - trackerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") - trackerFrame.messageLabel:Show() - else - -- Hide the message label as we have content to display - trackerFrame.messageLabel:Hide() - end - - -- Update the tracker scrollbar - ConsumeTracker_UpdateTrackerScrollBar() -end - - - -function ConsumeTracker_UpdateTrackerScrollBar() - local trackerFrame = ConsumeTracker_MainFrame.tabs[1] - local scrollBar = trackerFrame.scrollBar - local scrollFrame = trackerFrame.scrollFrame - local scrollChild = trackerFrame.scrollChild - - local totalHeight = scrollChild:GetHeight() - local shownHeight = trackerFrame:GetHeight() - 20 -- Account for padding/margins - - if totalHeight > shownHeight then - local maxScroll = totalHeight - shownHeight - scrollFrame.range = maxScroll -- Set the scroll range - scrollBar:SetMinMaxValues(0, maxScroll) - scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) - scrollBar:Show() - else - scrollFrame.range = 0 -- Also handle this case - scrollBar:SetMinMaxValues(0, 0) - scrollBar:SetValue(0) - scrollBar:Hide() - end -end - - - -function ConsumeTracker_UpdateOptionsScrollBar() - local optionsFrame = ConsumeTracker_MainFrame.tabs[2] - local scrollBar = optionsFrame.scrollBar - local scrollFrame = optionsFrame.scrollFrame - local scrollChild = optionsFrame.scrollChild - - local totalHeight = scrollChild.contentHeight - local parentHeight = optionsFrame:GetHeight() - local searchBoxHeight = 36 -- Adjusted height including padding - local shownHeight = parentHeight - searchBoxHeight - 20 - - local maxScroll = math.max(0, totalHeight - shownHeight) - scrollFrame.maxScroll = maxScroll - - if totalHeight > shownHeight then - scrollBar:SetMinMaxValues(0, maxScroll) - scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) - scrollBar:Show() - else - scrollBar:SetMinMaxValues(0, 0) - scrollBar:SetValue(0) - scrollBar:Hide() - end -end - -function ConsumeTracker_UpdateCharactersScrollBar() - local charactersFrame = ConsumeTracker_MainFrame.tabs[3] - local scrollBar = charactersFrame.scrollBar - local scrollFrame = charactersFrame.scrollFrame - local scrollChild = charactersFrame.scrollChild - - local totalHeight = scrollChild.contentHeight - local shownHeight = 320 -- Adjust based on your UI - - local maxScroll = math.max(0, totalHeight - shownHeight) - scrollFrame.maxScroll = maxScroll - - if totalHeight > shownHeight then - scrollBar:SetMinMaxValues(0, maxScroll) - scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) - scrollBar:Show() - else - scrollBar:SetMinMaxValues(0, 0) - scrollBar:SetValue(0) - scrollBar:Hide() - end -end - - -function ConsumeTracker_ShowConsumableTooltip(itemID) - -- Ensure item is enabled in settings - if not ConsumeTracker_Settings[itemID] then - return - end - - -- Create or reuse custom tooltip frame - if not ConsumeTracker_CustomTooltip then - -- Create the frame - local tooltipFrame = CreateFrame("Frame", "ConsumeTracker_CustomTooltip", UIParent) - tooltipFrame:SetFrameStrata("TOOLTIP") - tooltipFrame:SetWidth(200) - tooltipFrame:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, tileSize = 16, edgeSize = 16, - insets = { left = 4, right = 4, top = 4, bottom = 4 } - }) - tooltipFrame:SetBackdropColor(0, 0, 0, 1) - - -- Item icon - local icon = tooltipFrame:CreateTexture(nil, "ARTWORK") - icon:SetWidth(32) - icon:SetHeight(32) - icon:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", 10, -10) - tooltipFrame.icon = icon - - -- Item name - local title = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -2) - title:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) - title:SetJustifyH("LEFT") - title:SetTextColor(1,1,1) - tooltipFrame.title = title - - -- Item Total - local total = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") - total:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -20) - total:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) - total:SetJustifyH("LEFT") - tooltipFrame.total = total - - -- Content text - local content = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - content:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) - content:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) - content:SetJustifyH("LEFT") - tooltipFrame.content = content - - ConsumeTracker_CustomTooltip = tooltipFrame - end - - local tooltipFrame = ConsumeTracker_CustomTooltip - - -- Get item info - local itemName = consumablesList[itemID] or "Unknown Item" - local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" - - -- Set icon and title - tooltipFrame.icon:SetTexture(itemTexture) - tooltipFrame.title:SetText(itemName) - - -- Prepare content text - local contentText = "" - - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - local playerName = UnitName("player") - - -- Ensure data structure exists - ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} - ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} - - local data = ConsumeTracker_Data[realmName][faction] - - -- Initialize totals - local totalInventory, totalBank, totalMail = 0, 0, 0 - local hasItems = false - local characterList = {} - - -- Ensure character settings exist - ConsumeTracker_Settings["Characters"] = ConsumeTracker_Settings["Characters"] or {} - - -- Collect data for each character - for character, charData in pairs(data) do - if ConsumeTracker_Settings["Characters"][character] == true then - local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 - local bank = charData["bank"] and charData["bank"][itemID] or 0 - local mail = charData["mail"] and charData["mail"][itemID] or 0 - local total = inventory + bank + mail - - if total > 0 then - hasItems = true - totalInventory = totalInventory + inventory - totalBank = totalBank + bank - totalMail = totalMail + mail - - table.insert(characterList, { - name = character, - inventory = inventory, - bank = bank, - mail = mail, - total = total, - isPlayer = (character == playerName) - }) - end - end - end - - local totalItems = totalInventory + totalBank + totalMail - -- Adjust label color based on count - if totalItems == 0 then - tooltipFrame.total:SetTextColor(1, 0, 0) -- Red - elseif totalItems < 10 then - tooltipFrame.total:SetTextColor(1, 0.4, 0) -- Orange - elseif totalItems <= 20 then - tooltipFrame.total:SetTextColor(1, 0.85, 0) -- Yellow - else - tooltipFrame.total:SetTextColor(0, 1, 0) -- Green - end - - tooltipFrame.total:SetText("Total: " .. totalItems) - - if not hasItems then - contentText = contentText .. "|cffff0000No items found for this consumable.|r" - else - - -- Sort characters alphabetically (optional) - table.sort(characterList, function(a, b) return a.name < b.name end) - - -- Display data for each character - for _, charInfo in ipairs(characterList) do - - local nameColor = charInfo.isPlayer and "|cff00ff00" or "|cffffffff" -- Green for player, grey for others - contentText = contentText .. nameColor .. charInfo.name .. " (" .. charInfo.total .. ")|r\n" - - local detailText = "" - if charInfo.inventory > 0 then - detailText = detailText .. "|cffffffffInventory:|r " .. charInfo.inventory .. " " - end - if charInfo.bank > 0 then - detailText = detailText .. "|cffffffffBank:|r " .. charInfo.bank .. " " - end - if charInfo.mail > 0 then - detailText = detailText .. "|cffffffffMail:|r " .. charInfo.mail .. " " - end - - contentText = contentText .. " " .. detailText .. "\n\n" - end - end - - tooltipFrame.content:SetText(contentText) - - -- Calculate the number of lines in contentText using string.gsub - local _, numLines = string.gsub(contentText, "\n", "") - numLines = numLines + 2 -- Add lines for title and padding - - -- Adjust tooltip height based on the number of lines - local lineHeight = 10 - local totalHeight = 50 + (numLines * lineHeight) - tooltipFrame:SetHeight(totalHeight) - - -- Set the width based on content - local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.content:GetStringWidth() +20) - tooltipFrame:SetWidth(maxWidth) - - -- Position the tooltip near the cursor - local x, y = GetCursorPosition() - local scale = UIParent:GetEffectiveScale() - tooltipFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / scale + 10, y / scale - 10) - - tooltipFrame:Show() -end - - -function ConsumeTracker_ShowOptionsTooltip(itemID) - -- Set the maximum width for the description text - local maxDescriptionWidth = 160 - - -- Create or reuse the tooltip frame - if not ConsumeTracker_OptionsTooltip then - -- Create the frame - local tooltipFrame = CreateFrame("Frame", "ConsumeTracker_OptionsTooltip", UIParent) - tooltipFrame:SetFrameStrata("TOOLTIP") - tooltipFrame:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, tileSize = 16, edgeSize = 16, - insets = { left = 4, right = 4, top = 4, bottom = 4 } - }) - tooltipFrame:SetBackdropColor(0, 0, 0, 1) - - -- Item icon - local icon = tooltipFrame:CreateTexture(nil, "ARTWORK") - icon:SetWidth(32) - icon:SetHeight(32) - icon:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", 10, -10) - tooltipFrame.icon = icon - - -- Item name - local title = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -2) - title:SetJustifyH("LEFT") - tooltipFrame.title = title - - -- Item description - local description = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - description:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -16) - description:SetWidth(maxDescriptionWidth) - description:SetJustifyH("LEFT") - description:SetTextColor(1, 1, 1) - tooltipFrame.description = description - - ConsumeTracker_OptionsTooltip = tooltipFrame - end - - local tooltipFrame = ConsumeTracker_OptionsTooltip - - -- Get item info - local itemName = consumablesList[itemID] or "Unknown Item" - local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" - local itemDescription = consumablesDescription[itemID] or "" - - -- Function to add line breaks manually - local function WrapText(text, maxWidth, fontObject) - local wrappedText = "" - local currentLine = "" - local space = " " - local testString = tooltipFrame:CreateFontString(nil, "OVERLAY", fontObject) - testString:SetWidth(0) - testString:SetHeight(0) - testString:Hide() -- We don't need to display this - - -- Replace \n with spaces to avoid unintended line breaks - text = string.gsub(text, "\n", " ") - - -- Split the text into words - for word in string.gfind(text, "%S+") do - local testLine = currentLine == "" and word or (currentLine .. space .. word) - testString:SetText(testLine) - if testString:GetStringWidth() > maxWidth then - wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) - currentLine = word - else - currentLine = testLine - end - end - wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) - return wrappedText - end - - -- Wrap the description text - local wrappedDescription = WrapText(itemDescription, maxDescriptionWidth, "GameFontNormalSmall") - - -- Set icon, title, and description - tooltipFrame.icon:SetTexture(itemTexture) - tooltipFrame.title:SetText(itemName) - tooltipFrame.description:SetText(wrappedDescription) - - -- Calculate the number of lines in the wrapped description - local _, lineCount = string.gsub(wrappedDescription, "\n", "") - lineCount = lineCount + 1 -- Add 1 for the last line - - -- Set the heights for the description and the tooltip frame - local lineHeight = 12 - tooltipFrame.description:SetHeight(lineCount * lineHeight) - - local totalHeight = 50 + (lineCount * lineHeight) - tooltipFrame:SetHeight(totalHeight) - - -- Set the width of the tooltip - local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.description:GetStringWidth() + 70) - tooltipFrame:SetWidth(maxWidth) - - -- Position the tooltip near the cursor - local x, y = GetCursorPosition() - local scale = UIParent:GetEffectiveScale() - tooltipFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / scale + 10, y / scale - 10) - - tooltipFrame:Show() -end - - - - - --- Function to scan player's inventory -function ConsumeTracker_ScanPlayerInventory() - local playerName = UnitName("player") - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - - ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} - ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} - ConsumeTracker_Data[realmName][faction][playerName] = ConsumeTracker_Data[realmName][faction][playerName] or {} - local data = ConsumeTracker_Data[realmName][faction][playerName] - data["inventory"] = {} - - for bag = 0, 4 do - local numSlots = GetContainerNumSlots(bag) - if numSlots then - for slot = 1, numSlots do - local link = GetContainerItemLink(bag, slot) - if link then - local _, _, itemID = string.find(link, "item:(%d+)") - if itemID then - itemID = tonumber(itemID) - if ConsumeTracker_Settings[itemID] then - local texture, itemCount = GetContainerItemInfo(bag, slot) - data["inventory"][itemID] = (data["inventory"][itemID] or 0) + itemCount - end - end - end - end - end - end - - ConsumeTracker_UpdateTrackerContent() -end - --- Function to scan player's bank -function ConsumeTracker_ScanPlayerBank() - - if not BankFrame or not BankFrame:IsShown() then - return - end - - local playerName = UnitName("player") - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - - local data = ConsumeTracker_Data[realmName][faction][playerName] - data["bank"] = {} - - -- Bank bags are -1 and 5 to 10 in WoW 1.12 - for bag = -1, 10 do - if bag == -1 or (bag >= 5 and bag <= 10) then - local numSlots = GetContainerNumSlots(bag) - if numSlots then - for slot = 1, numSlots do - local link = GetContainerItemLink(bag, slot) - if link then - local _, _, itemID = string.find(link, "item:(%d+)") - if itemID then - itemID = tonumber(itemID) - if ConsumeTracker_Settings[itemID] then - local texture, itemCount = GetContainerItemInfo(bag, slot) - data["bank"][itemID] = (data["bank"][itemID] or 0) + itemCount - end - end - end - end - end - end - end - - ConsumeTracker_UpdateTrackerContent() -end - --- Function to scan player's mail -function ConsumeTracker_ScanPlayerMail() - if not MailFrame or not MailFrame:IsShown() then - return - end - - local playerName = UnitName("player") - local realmName = GetRealmName() - local faction = UnitFactionGroup("player") - - -- Ensure data tables are initialized - ConsumeTracker_Data[realmName] = ConsumeTracker_Data[realmName] or {} - ConsumeTracker_Data[realmName][faction] = ConsumeTracker_Data[realmName][faction] or {} - ConsumeTracker_Data[realmName][faction][playerName] = ConsumeTracker_Data[realmName][faction][playerName] or {} - local data = ConsumeTracker_Data[realmName][faction][playerName] - data["mail"] = {} - - local numInboxItems = GetInboxNumItems() - if numInboxItems and numInboxItems > 0 then - for mailIndex = 1, numInboxItems do - local itemName, itemTexture, itemCount, itemQuality = GetInboxItem(mailIndex) - if itemName and itemCount and itemCount > 0 then - -- Since GetInboxItemLink is not available, use itemName to get itemID - local itemID = consumablesNameToID[itemName] - if itemID and ConsumeTracker_Settings[itemID] then - data["mail"][itemID] = (data["mail"][itemID] or 0) + itemCount - end - end - end - end - - ConsumeTracker_UpdateTrackerContent() -end - +-- Metadata --------------------------------------------------------------------------------------------- +AddonName = "Consumes Manager" +Version = "1.5" +VState = "beta" +WindowWidth = 350 + +-- Onload & Click Functionality ------------------------------------------------------------------------- +function ConsumesManager_OnLoad(self) + self:RegisterForDrag("LeftButton") + self:SetScript("OnDragStart", function() ConsumesManager_OnDragStart(self) end) + self:SetScript("OnDragStop", function() ConsumesManager_OnDragStop(self) end) + + -- Create a frame for the delay + local delayFrame = CreateFrame("Frame") + local elapsed = 0 + local delay = 1 + + delayFrame:SetScript("OnUpdate", function() + elapsed = elapsed + arg1 -- arg1 provides the time since the last frame + if elapsed >= delay then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00" .. AddonName .. "|r |cffaaaaaa(v" .. Version .. "-" .. VState .. ")|r |cffffffffLoaded!|r |cffaaaaaa- Made by Horyoshi|r") + delayFrame:SetScript("OnUpdate", nil) -- Stop the OnUpdate script + end + end) +end + +function ConsumesManager_HandleClick(self, button) + if not self then return end + if button == "LeftButton" and not IsShiftKeyDown() then + if ConsumesManager_MainFrame and ConsumesManager_MainFrame:IsShown() then + ConsumesManager_MainFrame:Hide() + else + ConsumesManager_ShowMainWindow() + end + end +end + +function ConsumesManager_OnDragStart(self) + if IsShiftKeyDown() then + self:StartMoving() + self.isMoving = true + end +end + +function ConsumesManager_OnDragStop(self) + if self and self.isMoving then + self:StopMovingOrSizing() + self.isMoving = false + end +end + +-- Initialize SavedVariables ---------------------------------------------------------------------------- + if not ConsumesManager_Settings then + ConsumesManager_Settings = {} + end + if not ConsumesManager_Data then + ConsumesManager_Data = {} + end + +-- Event frame for updating data ------------------------------------------------------------------------ + local ConsumesManager_EventFrame = CreateFrame("Frame") + ConsumesManager_EventFrame:RegisterEvent("PLAYER_LOGIN") + ConsumesManager_EventFrame:RegisterEvent("BAG_UPDATE") + ConsumesManager_EventFrame:RegisterEvent("BANKFRAME_OPENED") + ConsumesManager_EventFrame:RegisterEvent("PLAYERBANKSLOTS_CHANGED") + ConsumesManager_EventFrame:RegisterEvent("ITEM_LOCK_CHANGED") + ConsumesManager_EventFrame:RegisterEvent("MAIL_SHOW") + ConsumesManager_EventFrame:RegisterEvent("MAIL_INBOX_UPDATE") + ConsumesManager_EventFrame:RegisterEvent("MAIL_CLOSED") + + ConsumesManager_EventFrame:SetScript("OnEvent", function() + if event == "PLAYER_LOGIN" then + ConsumesManager_ScanPlayerInventory() + ConsumesManager_ScanPlayerBank() + ConsumesManager_ScanPlayerMail() + elseif event == "BAG_UPDATE" then + ConsumesManager_ScanPlayerInventory() + if BankFrame and BankFrame:IsShown() then + ConsumesManager_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumesManager_ScanPlayerMail() + end + elseif event == "BANKFRAME_OPENED" then + ConsumesManager_ScanPlayerBank() + elseif event == "PLAYERBANKSLOTS_CHANGED" then + ConsumesManager_ScanPlayerBank() + ConsumesManager_ScanPlayerInventory() + elseif event == "ITEM_LOCK_CHANGED" then + if BankFrame and BankFrame:IsShown() then + ConsumesManager_ScanPlayerInventory() + ConsumesManager_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumesManager_ScanPlayerInventory() + ConsumesManager_ScanPlayerMail() + end + elseif event == "MAIL_SHOW" or event == "MAIL_INBOX_UPDATE" then + ConsumesManager_ScanPlayerMail() + end + end) + + +-- Main Windows Setup ----------------------------------------------------------------------------------- +function ConsumesManager_CreateMainWindow() + -- Main Frame + ConsumesManager_MainFrame = CreateFrame("Frame", "ConsumesManager_MainFrame", UIParent) + ConsumesManager_MainFrame:SetWidth(WindowWidth) + ConsumesManager_MainFrame:SetHeight(512) + ConsumesManager_MainFrame:SetPoint("CENTER", UIParent, "CENTER") + ConsumesManager_MainFrame:SetFrameStrata("DIALOG") + ConsumesManager_MainFrame:SetMovable(true) + ConsumesManager_MainFrame:EnableMouse(true) + ConsumesManager_MainFrame:RegisterForDrag("LeftButton") + ConsumesManager_MainFrame:SetScript("OnDragStart", function() + this:StartMoving() + end) + ConsumesManager_MainFrame:SetScript("OnDragStop", function() + this:StopMovingOrSizing() + end) + + table.insert(UISpecialFrames, "ConsumesManager_MainFrame") + + -- Background Texture + local background = ConsumesManager_MainFrame:CreateTexture(nil, "BACKGROUND") + background:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Background") + background:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 12, -12) + background:SetPoint("BOTTOMRIGHT", ConsumesManager_MainFrame, "BOTTOMRIGHT", -12, 12) + + -- Border + ConsumesManager_MainFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", + tile = true, tileSize = 32, edgeSize = 32, + insets = { left = 11, right = 12, top = 12, bottom = 11 } + }) + ConsumesManager_MainFrame:SetBackdropColor(0.1, 0.1, 0.1, 1) + + -- Title Text + local titleText = ConsumesManager_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + titleText:SetText(AddonName) + titleText:SetPoint("TOP", ConsumesManager_MainFrame, "TOP", 0, 0) + + -- Calculate the width of the title text and adjust the title background accordingly + local titleWidth = titleText:GetStringWidth() + 200 + + -- Title Background + local titleBg = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") + titleBg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header") + titleBg:SetWidth(titleWidth) + titleBg:SetHeight(64) + titleBg:SetPoint("TOP", ConsumesManager_MainFrame, "TOP", 0, 12) + + + -- Close Button + local closeButton = CreateFrame("Button", nil, ConsumesManager_MainFrame, "UIPanelCloseButton") + closeButton:SetWidth(32) + closeButton:SetHeight(32) + closeButton:SetPoint("TOPRIGHT", ConsumesManager_MainFrame, "TOPRIGHT", -5, -5) + closeButton:SetScript("OnClick", function() + ConsumesManager_MainFrame:Hide() + end) + + -- Create a custom tooltip frame + local tooltipFrame = CreateFrame("Frame", "ConsumesManagerTooltip", UIParent) + tooltipFrame:SetWidth(150) + tooltipFrame:SetHeight(40) + tooltipFrame:SetFrameStrata("TOOLTIP") + tooltipFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tooltipFrame:SetBackdropColor(0, 0, 0, 1) + tooltipFrame:Hide() + + -- Tooltip text + local tooltipText = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + tooltipText:SetPoint("CENTER", tooltipFrame, "CENTER", 0, 0) + tooltipFrame.text = tooltipText + + local function ShowTooltip(tab, text) + tooltipFrame.text:SetText(text) + tooltipFrame:SetPoint("BOTTOMLEFT", tab, "TOPLEFT", 0, 0) + tooltipFrame:Show() + end + + local function HideTooltip() + tooltipFrame:Hide() + end + + -- Tabs + local tabs = {} + local function CreateTab(name, texture, xOffset, tooltipText, tabIndex) + local tab = CreateFrame("Button", name, ConsumesManager_MainFrame) + tab:SetWidth(36) + tab:SetHeight(36) + tab:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", xOffset, -30) + + -- Tab Icon + local icon = tab:CreateTexture(nil, "ARTWORK") + icon:SetTexture(texture) + icon:SetWidth(36) + icon:SetHeight(36) + icon:SetPoint("CENTER", tab, "CENTER", 0, 0) + + tab.icon = icon + + -- Hover Glow + local hoverTexture = tab:CreateTexture(nil, "HIGHLIGHT") + hoverTexture:SetTexture("Interface\\Buttons\\CheckButtonHilight") + hoverTexture:SetBlendMode("ADD") + hoverTexture:SetAllPoints(tab) + + -- Active Highlight + local activeHighlight = tab:CreateTexture(nil, "OVERLAY") + activeHighlight:SetTexture("Interface\\Buttons\\CheckButtonHilight") + activeHighlight:SetBlendMode("ADD") + activeHighlight:SetAllPoints(tab) + activeHighlight:SetWidth(36) + activeHighlight:SetHeight(36) + activeHighlight:Hide() + tab.activeHighlight = activeHighlight + + -- Custom Tooltip Handlers + tab:SetScript("OnEnter", function() + ShowTooltip(tab, tooltipText) + end) + tab:SetScript("OnLeave", HideTooltip) + + -- Store tab for later + tabs[tabIndex] = tab + + return tab + end + + -- Manager Tab + local tab1 = CreateTab("ConsumesManager_MainFrameTab1", "Interface\\Icons\\INV_Potion_93", 30, "Consumables Manager", 1) + tab1:SetScript("OnClick", function() + ConsumesManager_ShowTab(1) + end) + + -- Options Tab + local tab2 = CreateTab("ConsumesManager_MainFrameTab2", "Interface\\Icons\\INV_Misc_Gear_01", 80, "Options", 2) + tab2:SetScript("OnClick", function() + ConsumesManager_ShowTab(2) + end) + + -- Characters Tab + local tab3 = CreateTab("ConsumesManager_MainFrameTab3", "Interface\\Icons\\Ability_Rogue_Disguise", 130, "Characters", 3) + tab3:SetScript("OnClick", function() + ConsumesManager_ShowTab(3) + end) + + -- Add Grey Line Under Tabs + local tabsLine = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") + tabsLine:SetHeight(1) + tabsLine:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 12, -72) + tabsLine:SetPoint("TOPRIGHT", ConsumesManager_MainFrame, "TOPRIGHT", -12, -72) + tabsLine:SetTexture("Interface\\Buttons\\WHITE8x8") + tabsLine:SetVertexColor(0.4, 0.4, 0.4, 1) + + -- Tab Content + ConsumesManager_MainFrame.tabs = {} + local tab1Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) + tab1Content:SetWidth(WindowWidth - 50) + tab1Content:SetHeight(380) + tab1Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) + ConsumesManager_MainFrame.tabs[1] = tab1Content + + local tab2Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) + tab2Content:SetWidth(WindowWidth - 50) + tab2Content:SetHeight(380) + tab2Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) + ConsumesManager_MainFrame.tabs[2] = tab2Content + + local tab3Content = CreateFrame("Frame", nil, ConsumesManager_MainFrame) + tab3Content:SetWidth(WindowWidth - 50) + tab3Content:SetHeight(380) + tab3Content:SetPoint("TOPLEFT", ConsumesManager_MainFrame, "TOPLEFT", 30, -80) + ConsumesManager_MainFrame.tabs[3] = tab3Content + + -- Footer Text + local footerText = ConsumesManager_MainFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + footerText:SetText("Made by Horyoshi (v" .. Version .. "-" .. VState .. ")") + footerText:SetTextColor(0.6, 0.6, 0.6) + footerText:SetPoint("BOTTOM", ConsumesManager_MainFrame, "BOTTOM", 0, 15) + + -- Add Grey Line Above Footer + local footerLine = ConsumesManager_MainFrame:CreateTexture(nil, "ARTWORK") + footerLine:SetHeight(1) + footerLine:SetPoint("BOTTOMLEFT", ConsumesManager_MainFrame, "BOTTOMLEFT", 12, 30) + footerLine:SetPoint("BOTTOMRIGHT", ConsumesManager_MainFrame, "BOTTOMRIGHT", -12, 30) + footerLine:SetTexture("Interface\\Buttons\\WHITE8x8") + footerLine:SetVertexColor(0.4, 0.4, 0.4, 1) + + -- Tab Switching Function + function ConsumesManager_ShowTab(tabIndex) + for index, content in pairs(ConsumesManager_MainFrame.tabs) do + if index == tabIndex then + content:Show() + tabs[index].activeHighlight:Show() + else + content:Hide() + tabs[index].activeHighlight:Hide() + end + end + end + + -- Set Default Tab + ConsumesManager_ShowTab(1) + + -- Add Custom Content for Tabs + ConsumesManager_CreateManagerContent(tab1Content) + ConsumesManager_CreateOptionsContent(tab2Content) + ConsumesManager_CreateCharactersContent(tab3Content) +end + +function ConsumesManager_ShowMainWindow() + if not ConsumesManager_MainFrame then + ConsumesManager_CreateMainWindow() + end + ConsumesManager_MainFrame:Show() + -- Scan inventory when the window is opened + ConsumesManager_ScanPlayerInventory() + -- Only scan bank and mail if they are open + if BankFrame and BankFrame:IsShown() then + ConsumesManager_ScanPlayerBank() + end + if MailFrame and MailFrame:IsShown() then + ConsumesManager_ScanPlayerMail() + end + -- Update the Manager content + ConsumesManager_UpdateManagerContent() +end + +function ConsumesManager_ShowTab(tabIndex) + if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs then return end + for i, tabContent in pairs(ConsumesManager_MainFrame.tabs) do + if i == tabIndex then + tabContent:Show() + -- Set the tab button to active + local tabButton = getglobal("ConsumesManager_MainFrameTab" .. i) + tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-ActiveTab") + else + tabContent:Hide() + -- Set the tab button to inactive + local tabButton = getglobal("ConsumesManager_MainFrameTab" .. i) + tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-InActiveTab") + end + end +end + + + +-- Create Window Tabs ----------------------------------------------------------------------------------- +function ConsumesManager_CreateManagerContent(parentFrame) + -- Scroll Frame + local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_ManagerScrollFrame", parentFrame) + scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) + scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) + scrollFrame:EnableMouseWheel(true) + scrollFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + local current = this:GetVerticalScroll() + local maxScroll = this.range or 0 + local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) + this:SetVerticalScroll(newScroll) + parentFrame.scrollBar:SetValue(newScroll) + end) + + -- Scroll Child Frame + local scrollChild = CreateFrame("Frame", nil, scrollFrame) + scrollChild:SetWidth(WindowWidth - 10) + scrollChild:SetHeight(1) -- Will adjust later + scrollFrame:SetScrollChild(scrollChild) + parentFrame.scrollChild = scrollChild + parentFrame.scrollFrame = scrollFrame + + -- Initialize data structures + parentFrame.categoryInfo = {} + local index = 0 -- Position index + local lineHeight = 18 + + -- Sort categories alphabetically + local sortedCategories = {} + for categoryName, _ in pairs(consumablesCategories) do + table.insert(sortedCategories, categoryName) + end + table.sort(sortedCategories) + + -- Iterate over sorted categories + for _, categoryName in ipairs(sortedCategories) do + local consumables = consumablesCategories[categoryName] + + -- Create category label + local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + categoryLabel:SetText(categoryName) + categoryLabel:SetTextColor(1, 1, 1) + categoryLabel:Show() + + local categoryInfo = { name = categoryName, label = categoryLabel, options = {} } + + index = index + 1 -- Position for the category label + + local numOptionsInCategory = 0 -- Counter for options in this category + + -- Sort the consumables by name + table.sort(consumables, function(a, b) return a.name < b.name end) + + -- For each consumable in the category + for _, consumable in ipairs(consumables) do + local itemID = consumable.id + local itemName = consumable.name + + -- Create a frame that encompasses the button and label + local optionFrame = CreateFrame("Frame", "ConsumesManager_ManagerOptionFrame" .. index, scrollChild) + optionFrame:SetWidth(WindowWidth - 10) + optionFrame:SetHeight(lineHeight) + optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + optionFrame:Show() + optionFrame:EnableMouse(true) -- Enable mouse events for OnEnter and OnLeave + + -- Create the 'Use' button inside the optionFrame + local useButton = CreateFrame("Button", "ConsumesManager_UseButton" .. index, optionFrame, "UIPanelButtonTemplate") + useButton:SetWidth(40) + useButton:SetHeight(16) + useButton:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + useButton:SetText("Use") + + -- Create FontString for label + local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + label:SetPoint("LEFT", useButton, "RIGHT", 4, 0) + label:SetText(itemName) + + -- Set up the button OnClick handler + useButton:SetScript("OnClick", function() + local bag, slot = ConsumesManager_FindItemInBags(itemID) + if bag and slot then + UseContainerItem(bag, slot) + else + DEFAULT_CHAT_FRAME:AddMessage("Item not found in bags.") + end + end) + + -- Initialize button state + useButton:Disable() + + -- Mouseover Tooltip + optionFrame:SetScript("OnEnter", function() + ConsumesManager_ShowConsumableTooltip(itemID) + end) + optionFrame:SetScript("OnLeave", function() + if ConsumesManager_CustomTooltip then + ConsumesManager_CustomTooltip:Hide() + end + end) + + -- Store option info + table.insert(categoryInfo.options, { + frame = optionFrame, + label = label, + name = itemName, + itemID = itemID, + button = useButton + }) + + index = index + 1 -- Increment index after adding option + numOptionsInCategory = numOptionsInCategory + 1 -- Increment options count + end + + -- Position the category label above its options + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numOptionsInCategory - 1) * lineHeight) + + -- Store category info + table.insert(parentFrame.categoryInfo, categoryInfo) + + -- Add extra spacing after the category + index = index + 1 -- Add one extra line of spacing between categories + end + + -- Adjust the scroll child height + scrollChild.contentHeight = (index - 1) * lineHeight + scrollChild:SetHeight(scrollChild.contentHeight) + + -- Message Label (adjusted to be a child of parentFrame) + local messageLabel = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + messageLabel:SetPoint("CENTER", parentFrame, "CENTER", 0, 0) + messageLabel:Hide() -- Initially hidden + parentFrame.messageLabel = messageLabel + + -- Scroll Bar + local scrollBar = CreateFrame("Slider", "ConsumesManager_ManagerScrollBar", parentFrame) + scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) + scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) + scrollBar:SetWidth(16) + scrollBar:SetOrientation('VERTICAL') + scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scrollBar:SetBackdrop({ + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 3, bottom = 3 } + }) + scrollBar:SetScript("OnValueChanged", function() + local value = this:GetValue() + parentFrame.scrollFrame:SetVerticalScroll(value) + end) + parentFrame.scrollBar = scrollBar + + -- Update the scrollbar + ConsumesManager_UpdateManagerScrollBar() +end + +function ConsumesManager_CreateOptionsContent(parentFrame) + -- Create Search Input + local searchBox = CreateFrame("EditBox", "ConsumesManager_SearchBox", parentFrame, "InputBoxTemplate") + searchBox:SetWidth(WindowWidth - 50) + searchBox:SetHeight(25) + searchBox:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -5) + searchBox:SetAutoFocus(false) + searchBox:SetText("Search...") + searchBox:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color + + searchBox:SetScript("OnEditFocusGained", function() + if this:GetText() == "Search..." then + this:SetText("") + this:SetTextColor(1, 1, 1) -- User input text color + end + end) + + searchBox:SetScript("OnEditFocusLost", function() + if this:GetText() == "" then + this:SetText("Search...") + this:SetTextColor(0.5, 0.5, 0.5) -- Placeholder text color + end + end) + + -- Function to update the filter + local function UpdateFilter() + local filterText = string.lower(searchBox:GetText()) + if filterText == "search..." then filterText = "" end + local index = 0 -- Position index + local lineHeight = 18 + + -- Iterate over categories + for _, categoryInfo in ipairs(parentFrame.categoryInfo) do + local categoryLabel = categoryInfo.label + local anyOptionVisible = false + + -- First, check if any options in the category match the filter + for _, optionInfo in ipairs(categoryInfo.options) do + local itemNameLower = string.lower(optionInfo.name) + + if filterText == "" or string.find(itemNameLower, filterText, 1, true) then + anyOptionVisible = true + break + end + end + + -- If any options are visible, show the category label + if anyOptionVisible then + categoryLabel:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + categoryLabel:Show() + index = index + 1 + + -- Now, position and show the matching options + for _, optionInfo in ipairs(categoryInfo.options) do + local optionFrame = optionInfo.frame + local itemNameLower = string.lower(optionInfo.name) + + if filterText == "" or string.find(itemNameLower, filterText, 1, true) then + -- Show the option + optionFrame:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + optionFrame:Show() + index = index + 1 + else + optionFrame:Hide() + end + end + + -- Add extra spacing after the category + index = index + 1 -- Add one extra line of spacing between categories + + else + categoryLabel:Hide() + -- Hide all options under this category + for _, optionInfo in ipairs(categoryInfo.options) do + optionInfo.frame:Hide() + end + end + end + + -- Adjust the scroll child height + parentFrame.scrollChild.contentHeight = index * lineHeight + parentFrame.scrollChild:SetHeight(parentFrame.scrollChild.contentHeight) + + -- Update the scrollbar + ConsumesManager_UpdateOptionsScrollBar() + end + + searchBox:SetScript("OnTextChanged", function() + UpdateFilter() + end) + + -- Adjust the size of the scroll frame to make room for the search box and add extra spacing + local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_OptionsScrollFrame", parentFrame) + scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -40) -- Start below the search box + scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 60) -- Leave space for buttons + scrollFrame:EnableMouseWheel(true) + scrollFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + local current = this:GetVerticalScroll() + local maxScroll = this.maxScroll or 0 + local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) + this:SetVerticalScroll(newScroll) + parentFrame.scrollBar:SetValue(newScroll) + end) + + -- Scroll Child Frame + local scrollChild = CreateFrame("Frame", nil, scrollFrame) + scrollChild:SetWidth(WindowWidth - 10) + scrollChild:SetHeight(1) -- Will adjust later + scrollFrame:SetScrollChild(scrollChild) + parentFrame.scrollChild = scrollChild + parentFrame.scrollFrame = scrollFrame + + -- Sort categories alphabetically + local sortedCategories = {} + for categoryName, _ in pairs(consumablesCategories) do + table.insert(sortedCategories, categoryName) + end + table.sort(sortedCategories) + + -- Checkboxes + parentFrame.checkboxes = {} + parentFrame.categoryInfo = {} + local index = 0 -- Position index + local lineHeight = 18 + + -- Iterate over sorted categories + for _, categoryName in ipairs(sortedCategories) do + local consumables = consumablesCategories[categoryName] + + -- Create category label + local categoryLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + categoryLabel:SetText(categoryName) + categoryLabel:SetTextColor(1, 1, 1) + categoryLabel:Show() + + local categoryInfo = { name = categoryName, label = categoryLabel, options = {} } + + index = index + 1 -- Position for the category label + + local numOptionsInCategory = 0 -- Counter for options in this category + + -- Sort the consumables by name + table.sort(consumables, function(a, b) return a.name < b.name end) + + -- For each consumable in the category + for _, consumable in ipairs(consumables) do + local currentItemID = consumable.id + local itemName = consumable.name + + -- Create a frame that encompasses the checkbox and label + local optionFrame = CreateFrame("Frame", "ConsumesManager_OptionsFrame" .. index, scrollChild) + optionFrame:SetWidth(WindowWidth - 10) + optionFrame:SetHeight(lineHeight) + optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + optionFrame:Show() + + -- Create the checkbox inside the optionFrame + local checkbox = CreateFrame("CheckButton", "ConsumesManager_OptionsCheckbox" .. index, optionFrame) + checkbox:SetWidth(16) + checkbox:SetHeight(16) + checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + + -- Create Textures for the checkbox + checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") + checkbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") + checkbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") + checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") + + -- Create FontString for label + local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) + label:SetText(itemName) + + -- Set up the checkbox OnClick handler + checkbox:SetScript("OnClick", function() + ConsumesManager_Settings[currentItemID] = checkbox:GetChecked() + ConsumesManager_UpdateManagerContent() + end) + -- Load saved setting + if ConsumesManager_Settings[currentItemID] then + checkbox:SetChecked(true) + end + parentFrame.checkboxes[currentItemID] = checkbox + + -- Make the optionFrame clickable + optionFrame:EnableMouse(true) + optionFrame:SetScript("OnMouseDown", function() + checkbox:Click() + end) + + -- Mouseover Tooltip + optionFrame:SetScript("OnEnter", function() + ConsumesManager_ShowOptionsTooltip(currentItemID) + end) + optionFrame:SetScript("OnLeave", function() + if ConsumesManager_OptionsTooltip then + ConsumesManager_OptionsTooltip:Hide() + end + end) + + -- Store option info + table.insert(categoryInfo.options, { frame = optionFrame, name = itemName }) + + index = index + 1 -- Increment index after adding option + numOptionsInCategory = numOptionsInCategory + 1 -- Increment options count + end + + -- Position the category label above its options + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numOptionsInCategory - 1) * lineHeight) + + -- Store category info + table.insert(parentFrame.categoryInfo, categoryInfo) + + -- Add extra spacing after the category + index = index + 1 -- Add one extra line of spacing between categories + end + + -- Adjust the scroll child height + scrollChild.contentHeight = (index - 1) * lineHeight + scrollChild:SetHeight(scrollChild.contentHeight) + + -- Scroll Bar + local scrollBar = CreateFrame("Slider", "ConsumesManager_OptionsScrollBar", parentFrame) + -- Corrected Y-offsets to prevent overlapping + scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -40) -- Start below the search box + scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) -- End above the buttons + scrollBar:SetWidth(16) + scrollBar:SetOrientation('VERTICAL') + scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scrollBar:SetBackdrop({ + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 3, bottom = 3 } + }) + scrollBar:SetScript("OnValueChanged", function() + local value = this:GetValue() + parentFrame.scrollFrame:SetVerticalScroll(value) + end) + parentFrame.scrollBar = scrollBar + + -- Update the scrollbar + ConsumesManager_UpdateOptionsScrollBar() + + -- Create Select All Button + local selectAllButton = CreateFrame("Button", "ConsumesManager_SelectAllButton", parentFrame, "UIPanelButtonTemplate") + selectAllButton:SetWidth(100) + selectAllButton:SetHeight(24) + selectAllButton:SetText("Select All") + selectAllButton:SetPoint("BOTTOMLEFT", parentFrame, "BOTTOMLEFT", 20, 10) + selectAllButton:SetScript("OnClick", function() + for itemID, checkbox in pairs(parentFrame.checkboxes) do + checkbox:SetChecked(true) + ConsumesManager_Settings[itemID] = true + end + ConsumesManager_UpdateManagerContent() + end) + + -- Create Deselect All Button + local deselectAllButton = CreateFrame("Button", "ConsumesManager_DeselectAllButton", parentFrame, "UIPanelButtonTemplate") + deselectAllButton:SetWidth(100) + deselectAllButton:SetHeight(24) + deselectAllButton:SetText("Deselect All") + deselectAllButton:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -40, 10) + deselectAllButton:SetScript("OnClick", function() + for itemID, checkbox in pairs(parentFrame.checkboxes) do + checkbox:SetChecked(false) + ConsumesManager_Settings[itemID] = false + end + ConsumesManager_UpdateManagerContent() + end) +end + +function ConsumesManager_CreateCharactersContent(parentFrame) + -- Scroll Frame + local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_CharactersScrollFrame", parentFrame) + scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) + scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) + scrollFrame:EnableMouseWheel(true) + scrollFrame:SetScript("OnMouseWheel", function() + local delta = arg1 + local current = scrollFrame:GetVerticalScroll() + local maxScroll = scrollFrame.maxScroll or 0 + local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) + scrollFrame:SetVerticalScroll(newScroll) + if parentFrame.scrollBar then + parentFrame.scrollBar:SetValue(newScroll) + end + end) + + -- Scroll Child Frame + local scrollChild = CreateFrame("Frame", nil, scrollFrame) + scrollChild:SetWidth(WindowWidth - 10) + scrollChild:SetHeight(1) + scrollFrame:SetScrollChild(scrollChild) + + -- Initialize variables + parentFrame.checkboxes = {} + local index = 0 -- Position index + + -- Ensure settings table for characters exists + ConsumesManager_Settings["Characters"] = ConsumesManager_Settings["Characters"] or {} + + -- Get list of characters + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + local playerName = UnitName("player") + + local characterList = {} + + -- Ensure data structure exists + if ConsumesManager_Data[realmName] and ConsumesManager_Data[realmName][faction] then + for characterName, _ in pairs(ConsumesManager_Data[realmName][faction]) do + table.insert(characterList, characterName) + end + end + + -- Ensure current character is included + local playerInList = false + for _, name in ipairs(characterList) do + if name == playerName then + playerInList = true + break + end + end + if not playerInList then + table.insert(characterList, playerName) + end + + -- Sort the character list + table.sort(characterList) + + -- Create FontString for title + local title = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + title:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, 0) -- Offset from the top + title:SetText("Select Characters to Track") + title:SetTextColor(1, 1, 1) + + -- Offset index to start below the title + local startYOffset = 20 + + -- For each character + for _, characterName in ipairs(characterList) do + index = index + 1 + + -- Create a local copy of characterName for the closure + local currentCharacterName = characterName + + -- Create a frame that encompasses the checkbox and label + local optionFrame = CreateFrame("Frame", "ConsumesManager_CharacterFrame" .. index, scrollChild) + optionFrame:SetWidth(WindowWidth - 10) + optionFrame:SetHeight(18) + optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -(startYOffset + (index - 1) * 20)) + + -- Create the checkbox inside the optionFrame + local checkbox = CreateFrame("CheckButton", "ConsumesManager_CharacterCheckbox" .. index, optionFrame) + checkbox:SetWidth(16) + checkbox:SetHeight(16) + checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + + -- Create Textures for the checkbox + checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") + checkbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") + checkbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") + checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") + + -- Create FontString for label + local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) + label:SetText(currentCharacterName) + + -- Set up the checkbox OnClick handler + checkbox:SetScript("OnClick", function() + if checkbox:GetChecked() then + ConsumesManager_Settings["Characters"][currentCharacterName] = true + else + ConsumesManager_Settings["Characters"][currentCharacterName] = false + end + ConsumesManager_UpdateManagerContent() + end) + -- Load saved setting + if ConsumesManager_Settings["Characters"][currentCharacterName] == nil then + -- Default to checked + checkbox:SetChecked(true) + ConsumesManager_Settings["Characters"][currentCharacterName] = true + else + checkbox:SetChecked(ConsumesManager_Settings["Characters"][currentCharacterName]) + end + parentFrame.checkboxes[currentCharacterName] = checkbox + + -- Make the optionFrame clickable (so clicking on the label checks/unchecks the box) + optionFrame:EnableMouse(true) + optionFrame:SetScript("OnMouseDown", function() + checkbox:Click() + end) + end + + -- Adjust the scroll child height + scrollChild.contentHeight = startYOffset + index * 20 + scrollChild:SetHeight(scrollChild.contentHeight) + + -- Scroll Bar + local scrollBar = CreateFrame("Slider", "ConsumesManager_CharactersScrollBar", parentFrame) + scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) + scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) + scrollBar:SetWidth(16) + scrollBar:SetOrientation('VERTICAL') + scrollBar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob") + scrollBar:SetBackdrop({ + bgFile = "Interface\\Buttons\\UI-SliderBar-Background", + edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", + tile = true, tileSize = 8, edgeSize = 8, + insets = { left = 3, right = 3, top = 3, bottom = 3 } + }) + scrollBar:SetScript("OnValueChanged", function() + local value = scrollBar:GetValue() + scrollFrame:SetVerticalScroll(value) + end) + parentFrame.scrollBar = scrollBar + parentFrame.scrollFrame = scrollFrame + parentFrame.scrollChild = scrollChild + + -- Update the scrollbar + ConsumesManager_UpdateCharactersScrollBar() +end + + + +-- Update Window Tabs ----------------------------------------------------------------------------------- +function ConsumesManager_UpdateManagerContent() + if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then + return + end + + local ManagerFrame = ConsumesManager_MainFrame.tabs[1] + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + local playerName = UnitName("player") + + -- Ensure data structure exists + if not ConsumesManager_Data[realmName] or not ConsumesManager_Data[realmName][faction] then + return + end + local data = ConsumesManager_Data[realmName][faction] + + -- Check if bank and mail have been scanned for current character + local currentCharData = data[playerName] + local bankScanned = currentCharData and currentCharData["bank"] ~= nil + local mailScanned = currentCharData and currentCharData["mail"] ~= nil + + -- If bank and mail have not been scanned, show message and hide item labels + if not bankScanned or not mailScanned then + -- Hide all option frames + for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do + categoryInfo.label:Hide() + for _, optionInfo in ipairs(categoryInfo.options) do + optionInfo.frame:Hide() + end + end + + -- Show message + ManagerFrame.messageLabel:SetText("|cffff0000This character is not scanned yet|r\n\n|cffffffffOpen your |rBank|cffffffff and |rMail |cffffffffto get started|r") + ManagerFrame.messageLabel:Show() + + -- Update the Manager scrollbar + ConsumesManager_UpdateManagerScrollBar() + + return + end + + -- Proceed with normal content update + local index = 0 -- Positioning index for items + local hasAnyVisibleItems = false -- Track if any items are visible + local lineHeight = 18 + + -- Hide the message label + ManagerFrame.messageLabel:Hide() + + -- Iterate over categories + for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do + local anyOptionVisible = false + + -- First, check if any options in the category are enabled + for _, optionInfo in ipairs(categoryInfo.options) do + local itemID = optionInfo.itemID + if ConsumesManager_Settings[itemID] then + anyOptionVisible = true + break + end + end + + -- If any options are visible, show the category label + if anyOptionVisible then + categoryInfo.label:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + categoryInfo.label:Show() + index = index + 1 + + -- Now, position and show the enabled options + for _, optionInfo in ipairs(categoryInfo.options) do + local itemID = optionInfo.itemID + local itemName = optionInfo.name + local label = optionInfo.label + local button = optionInfo.button + local frame = optionInfo.frame + + if ConsumesManager_Settings[itemID] then + -- Sum counts across all selected characters + local totalCount = 0 + for character, charData in pairs(data) do + if ConsumesManager_Settings["Characters"][character] == true then + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + totalCount = totalCount + inventory + bank + mail + end + end + + -- Update label text with counts + label:SetText(itemName .. " (" .. totalCount .. ")") + + -- Adjust label color based on count + if totalCount == 0 then + label:SetTextColor(1, 0, 0) -- Red + elseif totalCount < 10 then + label:SetTextColor(1, 0.4, 0) -- Orange + elseif totalCount <= 20 then + label:SetTextColor(1, 0.85, 0) -- Yellow + else + label:SetTextColor(0, 1, 0) -- Green + end + + -- Enable or disable the 'Use' button based on whether the item is in the player's inventory + local playerInventory = data[playerName]["inventory"] or {} + local countInInventory = playerInventory[itemID] or 0 + if countInInventory > 0 then + button:Enable() + else + button:Disable() + end + + -- Show and position the option frame + frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + frame:Show() + index = index + 1 + hasAnyVisibleItems = true + else + frame:Hide() + end + end + + -- Add extra spacing after the category + index = index + 1 -- Add one extra line of spacing between categories + else + categoryInfo.label:Hide() + -- Hide all options under this category + for _, optionInfo in ipairs(categoryInfo.options) do + optionInfo.frame:Hide() + end + end + end + + -- Adjust the scroll child height + ManagerFrame.scrollChild.contentHeight = index * lineHeight + ManagerFrame.scrollChild:SetHeight(ManagerFrame.scrollChild.contentHeight) + + -- Update the scrollbar + ConsumesManager_UpdateManagerScrollBar() + + if not hasAnyVisibleItems then + -- Show message when no items are selected + ManagerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + ManagerFrame.messageLabel:Show() + else + -- Hide the message label as we have content to display + ManagerFrame.messageLabel:Hide() + end +end + + + + +-- Scrollbar Window Updates ----------------------------------------------------------------------------- +function ConsumesManager_UpdateManagerScrollBar() + local ManagerFrame = ConsumesManager_MainFrame.tabs[1] + local scrollBar = ManagerFrame.scrollBar + local scrollFrame = ManagerFrame.scrollFrame + local scrollChild = ManagerFrame.scrollChild + + local totalHeight = scrollChild:GetHeight() + local shownHeight = ManagerFrame:GetHeight() - 20 -- Account for padding/margins + + if totalHeight > shownHeight then + local maxScroll = totalHeight - shownHeight + scrollFrame.range = maxScroll -- Set the scroll range + scrollBar:SetMinMaxValues(0, maxScroll) + scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) + scrollBar:Show() + else + scrollFrame.range = 0 -- Also handle this case + scrollBar:SetMinMaxValues(0, 0) + scrollBar:SetValue(0) + scrollBar:Hide() + end +end + +function ConsumesManager_UpdateOptionsScrollBar() + local optionsFrame = ConsumesManager_MainFrame.tabs[2] + local scrollBar = optionsFrame.scrollBar + local scrollFrame = optionsFrame.scrollFrame + local scrollChild = optionsFrame.scrollChild + + local totalHeight = scrollChild.contentHeight + local parentHeight = optionsFrame:GetHeight() + local searchBoxHeight = 36 -- Adjusted height including padding + local buttonsHeight = 40 -- Space reserved for Select/Deselect buttons + local shownHeight = parentHeight - searchBoxHeight - buttonsHeight - 20 -- Additional padding + + local maxScroll = math.max(0, totalHeight - shownHeight) + scrollFrame.maxScroll = maxScroll + + if totalHeight > shownHeight then + scrollBar:SetMinMaxValues(0, maxScroll) + scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) + scrollBar:Show() + else + scrollBar:SetMinMaxValues(0, 0) + scrollBar:SetValue(0) + scrollBar:Hide() + end +end + + +function ConsumesManager_UpdateCharactersScrollBar() + local charactersFrame = ConsumesManager_MainFrame.tabs[3] + local scrollBar = charactersFrame.scrollBar + local scrollFrame = charactersFrame.scrollFrame + local scrollChild = charactersFrame.scrollChild + + local totalHeight = scrollChild.contentHeight + local shownHeight = 320 -- Adjust based on your UI + + local maxScroll = math.max(0, totalHeight - shownHeight) + scrollFrame.maxScroll = maxScroll + + if totalHeight > shownHeight then + scrollBar:SetMinMaxValues(0, maxScroll) + scrollBar:SetValue(math.min(scrollBar:GetValue(), maxScroll)) + scrollBar:Show() + else + scrollBar:SetMinMaxValues(0, 0) + scrollBar:SetValue(0) + scrollBar:Hide() + end +end + + + +-- Functions to USE an item ----------------------------------------------------------------------------- +function ConsumesManager_UpdateUseButtons() + if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then + return + end + + local ManagerFrame = ConsumesManager_MainFrame.tabs[1] + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + -- Ensure data structure exists + if not ConsumesManager_Data[realmName] or not ConsumesManager_Data[realmName][faction] then + return + end + local data = ConsumesManager_Data[realmName][faction] + local charData = data[playerName] + if not charData then return end + + local inventory = charData["inventory"] or {} + + -- Iterate over the options to update the buttons + for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do + for _, optionInfo in ipairs(categoryInfo.options) do + local itemID = optionInfo.itemID + local button = optionInfo.button + if ConsumesManager_Settings[itemID] then + local count = inventory[itemID] or 0 + if count > 0 then + button:Enable() + else + button:Disable() + end + else + button:Disable() + end + end + end +end + +function ConsumesManager_FindItemInBags(itemID) + for bag = 0, 4 do + local numSlots = GetContainerNumSlots(bag) + if numSlots then + for slot = 1, numSlots do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, linkItemID = string.find(link, "item:(%d+)") + if linkItemID then + linkItemID = tonumber(linkItemID) + if linkItemID == itemID then + return bag, slot + end + end + end + end + end + end + return nil, nil +end + + + +-- Tooltip Windows -------------------------------------------------------------------------------------- +function ConsumesManager_ShowConsumableTooltip(itemID) + -- Ensure item is enabled in settings + if not ConsumesManager_Settings[itemID] then + return + end + + -- Create or reuse custom tooltip frame + if not ConsumesManager_CustomTooltip then + -- Create the frame + local tooltipFrame = CreateFrame("Frame", "ConsumesManager_CustomTooltip", UIParent) + tooltipFrame:SetFrameStrata("TOOLTIP") + tooltipFrame:SetWidth(200) + tooltipFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tooltipFrame:SetBackdropColor(0, 0, 0, 1) + + -- Item icon + local icon = tooltipFrame:CreateTexture(nil, "ARTWORK") + icon:SetWidth(32) + icon:SetHeight(32) + icon:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", 10, -10) + tooltipFrame.icon = icon + + -- Item name + local title = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -2) + title:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) + title:SetJustifyH("LEFT") + title:SetTextColor(1,1,1) + tooltipFrame.title = title + + -- Item Total + local total = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + total:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -20) + total:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) + total:SetJustifyH("LEFT") + tooltipFrame.total = total + + -- Content text + local content = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + content:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) + content:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) + content:SetJustifyH("LEFT") + tooltipFrame.content = content + + ConsumesManager_CustomTooltip = tooltipFrame + end + + local tooltipFrame = ConsumesManager_CustomTooltip + + -- Get item info + local itemName = consumablesList[itemID] or "Unknown Item" + local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" + + -- Set icon and title + tooltipFrame.icon:SetTexture(itemTexture) + tooltipFrame.title:SetText(itemName) + + -- Prepare content text + local contentText = "" + + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + local playerName = UnitName("player") + + -- Ensure data structure exists + ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} + ConsumesManager_Data[realmName][faction] = ConsumesManager_Data[realmName][faction] or {} + + local data = ConsumesManager_Data[realmName][faction] + + -- Initialize totals + local totalInventory, totalBank, totalMail = 0, 0, 0 + local hasItems = false + local characterList = {} + + -- Ensure character settings exist + ConsumesManager_Settings["Characters"] = ConsumesManager_Settings["Characters"] or {} + + -- Collect data for each character + for character, charData in pairs(data) do + if ConsumesManager_Settings["Characters"][character] == true then + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + local total = inventory + bank + mail + + if total > 0 then + hasItems = true + totalInventory = totalInventory + inventory + totalBank = totalBank + bank + totalMail = totalMail + mail + + table.insert(characterList, { + name = character, + inventory = inventory, + bank = bank, + mail = mail, + total = total, + isPlayer = (character == playerName) + }) + end + end + end + + local totalItems = totalInventory + totalBank + totalMail + -- Adjust label color based on count + if totalItems == 0 then + tooltipFrame.total:SetTextColor(1, 0, 0) -- Red + elseif totalItems < 10 then + tooltipFrame.total:SetTextColor(1, 0.4, 0) -- Orange + elseif totalItems <= 20 then + tooltipFrame.total:SetTextColor(1, 0.85, 0) -- Yellow + else + tooltipFrame.total:SetTextColor(0, 1, 0) -- Green + end + + tooltipFrame.total:SetText("Total: " .. totalItems) + + if not hasItems then + contentText = contentText .. "|cffff0000No items found for this consumable.|r" + else + + -- Sort characters alphabetically (optional) + table.sort(characterList, function(a, b) return a.name < b.name end) + + -- Display data for each character + for _, charInfo in ipairs(characterList) do + + local nameColor = charInfo.isPlayer and "|cff00ff00" or "|cffffffff" -- Green for player, grey for others + contentText = contentText .. nameColor .. charInfo.name .. " (" .. charInfo.total .. ")|r\n" + + local detailText = "" + if charInfo.inventory > 0 then + detailText = detailText .. "|cffffffffInventory:|r " .. charInfo.inventory .. " " + end + if charInfo.bank > 0 then + detailText = detailText .. "|cffffffffBank:|r " .. charInfo.bank .. " " + end + if charInfo.mail > 0 then + detailText = detailText .. "|cffffffffMail:|r " .. charInfo.mail .. " " + end + + contentText = contentText .. " " .. detailText .. "\n\n" + end + end + + tooltipFrame.content:SetText(contentText) + + -- Calculate the number of lines in contentText using string.gsub + local _, numLines = string.gsub(contentText, "\n", "") + numLines = numLines + 2 -- Add lines for title and padding + + -- Adjust tooltip height based on the number of lines + local lineHeight = 10 + local totalHeight = 50 + (numLines * lineHeight) + tooltipFrame:SetHeight(totalHeight) + + -- Set the width based on content + local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.content:GetStringWidth() +20) + tooltipFrame:SetWidth(maxWidth) + + -- Position the tooltip near the cursor + local x, y = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + tooltipFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / scale + 10, y / scale - 10) + + tooltipFrame:Show() +end + +function ConsumesManager_ShowOptionsTooltip(itemID) + -- Set the maximum width for the description text + local maxDescriptionWidth = 160 + + -- Create or reuse the tooltip frame + if not ConsumesManager_OptionsTooltip then + -- Create the frame + local tooltipFrame = CreateFrame("Frame", "ConsumesManager_OptionsTooltip", UIParent) + tooltipFrame:SetFrameStrata("TOOLTIP") + tooltipFrame:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, tileSize = 16, edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + tooltipFrame:SetBackdropColor(0, 0, 0, 1) + + -- Item icon + local icon = tooltipFrame:CreateTexture(nil, "ARTWORK") + icon:SetWidth(32) + icon:SetHeight(32) + icon:SetPoint("TOPLEFT", tooltipFrame, "TOPLEFT", 10, -10) + tooltipFrame.icon = icon + + -- Item name + local title = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -2) + title:SetJustifyH("LEFT") + tooltipFrame.title = title + + -- Item description + local description = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + description:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -16) + description:SetWidth(maxDescriptionWidth) + description:SetJustifyH("LEFT") + description:SetTextColor(1, 1, 1) + tooltipFrame.description = description + + ConsumesManager_OptionsTooltip = tooltipFrame + end + + local tooltipFrame = ConsumesManager_OptionsTooltip + + -- Get item info + local itemName = consumablesList[itemID] or "Unknown Item" + local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" + local itemDescription = consumablesDescription[itemID] or "" + + -- Function to add line breaks manually + local function WrapText(text, maxWidth, fontObject) + local wrappedText = "" + local currentLine = "" + local space = " " + local testString = tooltipFrame:CreateFontString(nil, "OVERLAY", fontObject) + testString:SetWidth(0) + testString:SetHeight(0) + testString:Hide() -- We don't need to display this + + -- Replace \n with spaces to avoid unintended line breaks + text = string.gsub(text, "\n", " ") + + -- Split the text into words + for word in string.gfind(text, "%S+") do + local testLine = currentLine == "" and word or (currentLine .. space .. word) + testString:SetText(testLine) + if testString:GetStringWidth() > maxWidth then + wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) + currentLine = word + else + currentLine = testLine + end + end + wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) + return wrappedText + end + + -- Wrap the description text + local wrappedDescription = WrapText(itemDescription, maxDescriptionWidth, "GameFontNormal") + + -- Set icon, title, and description + tooltipFrame.icon:SetTexture(itemTexture) + tooltipFrame.title:SetText(itemName) + tooltipFrame.description:SetText(wrappedDescription) + + -- Calculate the number of lines in the wrapped description + local _, lineCount = string.gsub(wrappedDescription, "\n", "") + lineCount = lineCount + 1 -- Add 1 for the last line + + -- Set the heights for the description and the tooltip frame + local lineHeight = 12 + tooltipFrame.description:SetHeight(lineCount * lineHeight) + + local totalHeight = 50 + (lineCount * lineHeight) + tooltipFrame:SetHeight(totalHeight) + + -- Set the width of the tooltip + local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.description:GetStringWidth() + 70) + tooltipFrame:SetWidth(maxWidth) + + -- Position the tooltip near the cursor + local x, y = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + tooltipFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / scale + 10, y / scale - 10) + + tooltipFrame:Show() +end + + + +-- Scan Functions --------------------------------------------------------------------------------------- +function ConsumesManager_ScanPlayerInventory() + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + -- Initialize data structures + ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} + ConsumesManager_Data[realmName][faction] = ConsumesManager_Data[realmName][faction] or {} + ConsumesManager_Data[realmName][faction][playerName] = ConsumesManager_Data[realmName][faction][playerName] or {} + local data = ConsumesManager_Data[realmName][faction][playerName] + data["inventory"] = {} + + -- Scan all bags (0 to 4 represent backpack and additional bags) + for bag = 0, 4 do + local numSlots = GetContainerNumSlots(bag) + if numSlots then + for slot = 1, numSlots do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, itemID = string.find(link, "item:(%d+)") + if itemID then + itemID = tonumber(itemID) + -- Scan all items, regardless of selection + local texture, itemCount = GetContainerItemInfo(bag, slot) + if itemCount and itemCount > 0 then + data["inventory"][itemID] = (data["inventory"][itemID] or 0) + itemCount + end + end + end + end + end + end + + -- Update the Manager window and use buttons + ConsumesManager_UpdateUseButtons() + ConsumesManager_UpdateManagerContent() +end + +function ConsumesManager_ScanPlayerBank() + -- Ensure the BankFrame is open before scanning + if not BankFrame or not BankFrame:IsShown() then + return + end + + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + -- Initialize data structures + ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} + ConsumesManager_Data[realmName][faction] = ConsumesManager_Data[realmName][faction] or {} + ConsumesManager_Data[realmName][faction][playerName] = ConsumesManager_Data[realmName][faction][playerName] or {} + local data = ConsumesManager_Data[realmName][faction][playerName] + data["bank"] = {} + + -- In WoW 1.12, bank bags are -1 and 5 to 10 + for bag = -1, 10 do + if bag == -1 or (bag >= 5 and bag <= 10) then + local numSlots = GetContainerNumSlots(bag) + if numSlots then + for slot = 1, numSlots do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, itemID = string.find(link, "item:(%d+)") + if itemID then + itemID = tonumber(itemID) + -- Scan all items, regardless of selection + local texture, itemCount = GetContainerItemInfo(bag, slot) + if itemCount and itemCount > 0 then + data["bank"][itemID] = (data["bank"][itemID] or 0) + itemCount + end + end + end + end + end + end + end + + -- Update the Manager window + ConsumesManager_UpdateManagerContent() +end + +function ConsumesManager_ScanPlayerMail() + -- Ensure the MailFrame is open before scanning + if not MailFrame or not MailFrame:IsShown() then + return + end + + local playerName = UnitName("player") + local realmName = GetRealmName() + local faction = UnitFactionGroup("player") + + -- Initialize data structures + ConsumesManager_Data[realmName] = ConsumesManager_Data[realmName] or {} + ConsumesManager_Data[realmName][faction] = ConsumesManager_Data[realmName][faction] or {} + ConsumesManager_Data[realmName][faction][playerName] = ConsumesManager_Data[realmName][faction][playerName] or {} + local data = ConsumesManager_Data[realmName][faction][playerName] + data["mail"] = {} + + local numInboxItems = GetInboxNumItems() + if numInboxItems and numInboxItems > 0 then + for mailIndex = 1, numInboxItems do + local itemName, itemTexture, itemCount, itemQuality = GetInboxItem(mailIndex) + if itemName and itemCount and itemCount > 0 then + -- Since GetInboxItemLink is not available in 1.12, use itemName to get itemID + local itemID = consumablesNameToID[itemName] + if itemID then + -- Scan all items, regardless of selection + data["mail"][itemID] = (data["mail"][itemID] or 0) + itemCount + end + end + end + end + + -- Update the Manager window + ConsumesManager_UpdateManagerContent() +end diff --git a/README.md b/README.md deleted file mode 100644 index cd338c7..0000000 --- a/README.md +++ /dev/null @@ -1,40 +0,0 @@ -Currently in beta and needs testing. Report bugs here: https://github.com/Cinecom/ConsumesTracker/issues - -# ConsumesTracker -Created with ♥ by Horyoshi for World of Warcraft 1.12 Turtle WoW -Tracks your inventory, bank and mail for consumables. Works through multiple characters on the same account. - -## How To - -Unzip the file and place the 'ConsumesTracker' folder in your /Interface/AddOns folder. Remove '-master' from the folder name! - -Click the mini-map icon to open/close the Tracker -- Tracker: shows the consumables that you track (need innitial setup, just follow instructions) -- Options: enable / disable items you want to track -- Characters: exclude characters you don't want to track - -## Future Features - -- Syncing across multiple accounts on the same server (bank alts) -- View preset lists for your class for specific raids. (ie. shows list of consumes that a mage needs to bring to BWL) - -image - -## Changelog - -**##V1.4.1** -``` -- Can now be updated via the Turtle WoW launcher -``` -image - - -**V1.4** -``` -- Updated consumables list -- Fixed icons and ID's for all consumables -- Fixed an issue where the consumables counting could be a negative value -- Added a search filter in the options window -- Fixed general styling issues -- You can now close the addon window by pressing 'Esc' -``` diff --git a/minimap_icon.xml b/minimap_icon.xml index 7451ea7..ab5cb39 100644 --- a/minimap_icon.xml +++ b/minimap_icon.xml @@ -1,5 +1,5 @@ -