-- copyright 2006 by Rene Schneider (Slarti on EU-Blackhand), 2017 by laytya -- Modified by Alexander Shelokhnev (Dreamios on Tel'Abim (Turtle-WoW)) in 2022 local _G = getfenv(0) ATSW_MAX_TRADESKILL_TABS = 8 ATSW_TRADESKILL_HEIGHT = 18 ATSW_RECIPES_DISPLAYED = 23 ATSW_TASKS_DISPLAYED = 5 ATSW_TOOLS_DISPLAYED = 4 ATSW_NECESSARIES_DISPLAYED = 20 ATSW_AUCTION_ITEMS_DISPLAYED = 5 ATSW_TABLE_CREATION_SIZE = 10 ATSW_NECESSARY_REAGENT_ADD = 1 ATSW_NECESSARY_REAGENT_DELETE = 2 ATSW_POSSIBLE_BAGS = 1 ATSW_POSSIBLE_BANK = 2 ATSW_POSSIBLE_ALTS = 3 ATSW_POSSIBLE_MERCHANT = 4 ATSW_POSSIBLE_NOTASK = 5 ATSW_CAST = 1 ATSW_FLASH = 2 ATSW_FADEOUT = 3 ATSW_FLASH_TIME = 5/60 ATSW_FADEOUT_TIME = 10/60 ERR_LEARN_RECIPE_PATTERN = string.gsub(ERR_LEARN_RECIPE_S, '%%s', '(.+)' ) ERR_LEARN_SPELL_PATTERN = string.gsub(ERR_LEARN_SPELL_S, '%%s', '(.+)' ) ERR_LEARN_ABILITY_PATTERN = string.gsub(ERR_LEARN_ABILITY_S, '%%s', '(.+)' ) ERR_UNLEARN_RECIPE_PATTERN = string.gsub(ERR_SPELL_UNLEARNED_S, '%%s', '(.+)' ) ERR_SKILL_UP_PATTERN = string.gsub(string.gsub(ERR_SKILL_UP_SI,'%%d', '%%d+'),'%%s','(.+)') ERR_SKILL_GAINED_PATTERN = string.gsub(ERR_SKILL_GAINED_S, '%%s', '(.+)' ) LOOT_ITEM_CREATED_SELF_PATTERN = string.gsub(LOOT_ITEM_CREATED_SELF, '%%s', '(.+)' ) UIPanelWindows['ATSWFrame' ] = { area = 'left', pushable = 8 } UIPanelWindows['ATSWCSFrame' ] = { area = 'left', pushable = 8 } UIPanelWindows['ATSWConfigFrame' ] = { area = 'center', pushable = 1 } ATSWTypeColor = { ['optimal' ] = { R = 1.00, G = 0.50, B = 0.25 }, ['medium' ] = { R = 1.00, G = 1.00, B = 0.00 }, ['easy' ] = { R = 0.25, G = 0.75, B = 0.25 }, ['trivial' ] = { R = 0.50, G = 0.50, B = 0.50 }, ['used' ] = { R = 0.50, G = 0.50, B = 0.50 }, ['header' ] = { R = 1.00, G = 0.82, B = 0 }, ['none' ] = { R = 0.25, G = 0.75, B = 0.25 } } local QualityColor = { [1] = { R = 0.62, G = 0.62, B = 0.62 }, [2] = { R = 1, G = 1, B = 1 }, [3] = { R = 0.12, G = 1, B = 0 }, [4] = { R = 0, G = 0.44, B = 0.87 }, [5] = { R = 0.64, G = 0.21, B = 0.93 }, [6] = { R = 1, G = 0.5, B = 0 }, [7] = { R = 0.9, G = 0.8, B = 0.5 }, [8] = { R = 0, G = 0.8, B = 1 } } local ToolID = { ['Blacksmith Hammer' ] = 5956, ['Arclight Spanner' ] = 6219, ['Gyromatic Micro-Adjustor' ] = 10498, ['Philosopher\'s Stone' ] = 9149, ['Runed Copper Rod' ] = 6218, ['Runed Silver Rod' ] = 6339, ['Runed Golden Rod' ] = 11130, ['Runed Truesilver Rod' ] = 11145, ['Runed Arcanite Rod' ] = 16207 } local Professions = { 'Trade_Engraving', -- Enchanting 'Trade_Tailoring', -- Tailoring 'Trade_Engineering', -- Engineering 'Trade_BlackSmithing', -- Blacksmithing 'INV_Jewelry_Necklace_11', -- Jewelcrafting 'Spell_Fire_FlameBlades', -- Smelting 'Trade_Alchemy', -- Alchemy 'INV_Misc_ArmorKit_17', -- Leatherworking 'INV_Misc_Food_15', -- Cooking 'Spell_Holy_SealOfSacrifice', -- First Aid 'Trade_Survival', -- Survival 'Ability_Hunter_BeastCall02', -- Beast Training 'Trade_BrewPoison' -- Poisons } -- For AtlasLoot addon local ProfessionNamesForAtlasLoot = { ['Interface\\Icons\\Trade_Engraving'] = { -- Enchanting 'EnchantingApprentice1', 'EnchantingJourneyman1', 'EnchantingJourneyman2', 'EnchantingExpert1', 'EnchantingExpert2', 'EnchantingArtisan1', 'EnchantingArtisan2', 'EnchantingArtisan3' }, ['Interface\\Icons\\Trade_Tailoring'] = { -- Tailoring 'TailoringApprentice1', 'TailoringJourneyman1', 'TailoringJourneyman2', 'TailoringExpert1', 'TailoringExpert2', 'TailoringArtisan1', 'TailoringArtisan2', 'TailoringArtisan3', 'TailoringArtisan4' }, ['Interface\\Icons\\Trade_Engineering'] = { -- Engineering 'EngineeringApprentice1', 'EngineeringJourneyman1', 'EngineeringJourneyman2', 'EngineeringExpert1', 'EngineeringExpert2', 'EngineeringArtisan1', 'EngineeringArtisan2', 'Gnomish1', 'Goblin1' }, ['Interface\\Icons\\Trade_BlackSmithing'] ={ -- Blacksmithing 'SmithingApprentice1', 'SmithingJourneyman1', 'SmithingJourneyman2', 'SmithingExpert1', 'SmithingExpert2', 'SmithingArtisan1', 'SmithingArtisan2', 'SmithingArtisan3', 'Armorsmith1', 'Weaponsmith1', 'Axesmith1', 'Hammersmith1', 'Swordsmith1' }, ['Interface\\Icons\\INV_Jewelry_Necklace_11'] = { -- Jewelcrafting 'JewelcraftingApprentice1', 'JewelcraftingJourneyman1', 'JewelcraftingJourneyman2', 'JewelcraftingExpert1', 'JewelcraftingExpert2', 'JewelcraftingExpert3', 'JewelcraftingArtisan1', 'JewelcraftingArtisan2' }, ['Interface\\Icons\\Spell_Fire_FlameBlades'] = { -- Smelting 'Smelting1' }, ['Interface\\Icons\\Trade_Alchemy'] = { -- Alchemy 'AlchemyApprentice1', 'AlchemyJourneyman1', 'AlchemyExpert1', 'AlchemyArtisan1', 'AlchemyArtisan2' }, ['Interface\\Icons\\INV_Misc_ArmorKit_17'] = { -- Leatherworking 'LeatherApprentice1', 'LeatherJourneyman1', 'LeatherJourneyman2', 'LeatherExpert1', 'LeatherExpert2', 'LeatherArtisan1', 'LeatherArtisan2', 'LeatherArtisan3', 'Dragonscale1', 'Elemental1', 'Tribal1' }, ['Interface\\Icons\\INV_Misc_Food_15'] = { -- Cooking 'CookingApprentice1', 'CookingJourneyman1', 'CookingExpert1', 'CookingArtisan1' }, ['Interface\\Icons\\Spell_Holy_SealOfSacrifice'] = { -- First Aid 'FirstAid1' }, ['Interface\\Icons\\Trade_Survival'] = { -- Survival 'Survival1', 'Survival2' }, ['Interface\\Icons\\Ability_Hunter_BeastCall02'] = {''}, -- Beast Training ['Interface\\Icons\\Trade_BrewPoison'] = { -- Poisons 'Poisons1' } } ATSW_Specializations = { ['Interface\\Icons\\Trade_Engineering'] = { -- Engineering 'INV_Gizmo_02', -- Gnomish Engineer 'Spell_Fire_SelfDestruct' -- Goblin Engineer }, ['Interface\\Icons\\Trade_BlackSmithing'] ={ -- Blacksmithing 'INV_Chest_Plate04', -- Armorsmith 'INV_Sword_25', -- Weaponsmith 'INV_Axe_05', -- Master Axesmith 'INV_Hammer_23', -- Master Hammersmith 'INV_Sword_41' -- Master Swordsmith }, ['Interface\\Icons\\INV_Misc_ArmorKit_17'] = { -- Leatherworking 'INV_Misc_MonsterScales_03', -- Dragonscale Leatherworking 'Spell_Fire_Volcano', -- Elemental Leatherworking 'Spell_Nature_NullWard' -- Tribal Leatherworking } } ATSW_Background = { ['Interface\\Icons\\Trade_Engraving' ] = 'Enchanting', ['Interface\\Icons\\Trade_Tailoring' ] = 'Tailoring', ['Interface\\Icons\\Trade_Engineering' ] = 'Engineering', ['Interface\\Icons\\Trade_BlackSmithing' ] = 'Blacksmithing', ['Interface\\Icons\\INV_Jewelry_Necklace_11' ] = 'Jewelcrafting', ['Interface\\Icons\\Spell_Fire_FlameBlades' ] = 'Smelting', ['Interface\\Icons\\Trade_Alchemy' ] = 'Alchemy', ['Interface\\Icons\\INV_Misc_ArmorKit_17' ] = 'Leatherworking', ['Interface\\Icons\\INV_Misc_Food_15' ] = 'Cooking', ['Interface\\Icons\\Spell_Holy_SealOfSacrifice' ] = 'FirstAid', ['Interface\\Icons\\Trade_Survival' ] = 'Survival', ['Interface\\Icons\\Ability_Hunter_BeastCall02' ] = 'BeastTraining', ['Interface\\Icons\\Trade_BrewPoison' ] = 'Poisons' } -- Colors are stored for code readability local Color = { GREY = '|cff7f7f7f', LIGHTGREY= '|cff9F9F9F', RED = '|cffff0000', WHITE = '|cffFFFFFF', GREEN = '|cff00FF00', DARKGREEN = '|cff1f8f1f', PURPLE = '|cff9F3FFF', BLUE = '|cff0070dd', ORANGE = '|cffff7f3f', YELLOW = '|cffffff00', DEFAULT = '|cffFFd200' } local function GetUnitClass(Unit) local _, Class = UnitClass(Unit) if Class and string.len(Class) > 0 then local First = string.upper(string.sub(Class, 1, 1)) local Rest = string.upper(string.sub(Class, 2)) Class = First .. Rest end return Class end local realm = GetRealmName() local player = UnitName('player') local class = GetUnitClass('player') ATSW_player = player ATSW_realm = realm ATSW_Characters = {} ATSW_Characters[realm] = {} local TradeSkill function ATSW_TradeSkill() return TradeSkill end local InitializedOnShow = false local ReagentsDataIsComplete = true local RecipesDataIsComplete = -1 local RecipesDataTries = 0 local CooldownUpdateTime = 0 local LoadingProfessions = false local UpdateCount = 0 local LastCastName = nil local NoDropDownUpdate = false local BankFrameOpened = false local s = ERR_USE_LOCKED_WITH_ITEM_S -- 'Requires %s' local sRequires = string.sub(s, 1, string.find(s, '%s')) -- Delete %s ATSW_Debug = false ATSW_SwitchingFrames = false ATSW_SwitchingToEditor = false ATSW_SwitchingToMain = false -- Selected ATSW_Profession = {} ATSW_Profession[realm] = {} ATSW_Profession[realm][player] = nil ATSW_SelectedRecipe = {} ATSW_SelectedRecipe[realm] = {} ATSW_SelectedRecipe[realm][player] = {} -- Top control panel ATSW_SortBy = {} ATSW_SortBy[realm] = {} ATSW_SortBy[realm][player] = {} ATSW_SearchString = {} ATSW_SearchString[realm] = {} ATSW_SearchString[realm][player] = {} ATSW_BlockSearchTextChanged = false ATSW_SubClassFilter = {} ATSW_SubClassFilter[realm] = {} ATSW_SubClassFilter[realm][player] = {} ATSW_InvSlotFilter = {} ATSW_InvSlotFilter[realm] = {} ATSW_InvSlotFilter[realm][player] = {} -- Recipes auxiliary arrays ATSW_ContractedCategories = {} ATSW_ContractedCategories[realm] = {} ATSW_ContractedCategories[realm][player] = {} ATSW_ScrollOffset = {} ATSW_ScrollOffset[realm] = {} ATSW_ScrollOffset[realm][player] = {} ATSW_NeedToUpdate = {} ATSW_NeedToUpdate[realm] = {} ATSW_NeedToUpdate[realm][player] = {} -- Previous recipes ATSW_PreviousRecipes = {} ATSW_PreviousRecipes[realm] = {} ATSW_PreviousRecipes[realm][player] = {} -- Tasks ATSW_Tasks = {} ATSW_Tasks[realm] = {} ATSW_Tasks[realm][player] = {} ATSW_NecessaryReagents = {} ATSW_NecessaryReagents[realm] = {} ATSW_NecessaryReagents[realm][player] = {} ATSW_TimeCost = {} -- Bottom control panel ATSW_Amount = {} ATSW_Amount[realm] = {} ATSW_Amount[realm][player] = {} -- Processing local Processing = { Active = false, Profession = nil, Recipe = nil, Texture = nil, RecipeTime = 0, Amount = 0, Time = 0, Delay = 0, Complete = 0, Stop = false } -- Options ATSW_Options = { Unified = true, ConsiderBank = false, ConsiderAlts = false, ConsiderMerchants = false, RecipeTooltip = true, AutoBuy = false, DisplayShoppingList = true, } ATSW_Attributes = {} ATSW_Attributes[realm] = {} ATSW_Attributes[realm][player] = {} -- Transform old settings. Remove in the future, 2026 year maybe. if ATSW_Unified ~= nil then ATSW_Options.Unified = ATSW_Unified ATSW_Options.ConsiderBank = ATSW_ConsiderBank ATSW_Options.ConsiderAlts = ATSW_ConsiderAlts ATSW_Options.ConsiderMerchants = ATSW_ConsiderMerchants ATSW_Options.RecipeTooltip = ATSW_RecipeTooltip ATSW_Options.AutoBuy = ATSW_AutoBuy ATSW_Options.DisplayShoppingList = ATSW_DisplayShoppingList ATSW_Unified = nil ATSW_ConsiderBank = nil ATSW_ConsiderAlts = nil ATSW_ConsiderMerchants = nil ATSW_RecipeTooltip = nil ATSW_AutoBuy = nil ATSW_DisplayShoppingList = nil end -- Controllable tables instead of standard tables do not leave garbage if they change in the game ATSW = { Recipes = {}, RecipesSize = {}, RecipesSorted = {}, RecipesSortedSize = {}, } ATSW.Recipes[realm] = {} ATSW.Recipes[realm][player] = {} ATSW.RecipesSize[realm] = {} ATSW.RecipesSize[realm][player] = {} ATSW.RecipesSorted[realm] = {} ATSW.RecipesSorted[realm][player] = {} ATSW.RecipesSortedSize[realm] = {} ATSW.RecipesSortedSize[realm][player] = {} -- Utility functions local function SetVisible(Frame, State) if Frame then if State then Frame:Show() else Frame:Hide() end end end local function SetEnabled(Frame, State) if Frame then if State then Frame:Enable() else Frame:Disable() end end end local function SetSize(Object, Size, SizeY) Object:SetWidth(Size) Object:SetHeight(SizeY or Size) end local function FormatTime(Seconds, FourDigits) if tonumber(Seconds) then local Time local D, H, M, S = ChatFrame_TimeBreakDown(Seconds) if D > 0 then Time = string.format('%dd:%dh', D, H) elseif H > 0 then Time = string.format('%dh:%dm', H, M) else if FourDigits then Time = string.format( '%02d:%02d', M, S) else if M > 0 then Time = string.format('%d:%02d', M, S) elseif S > 0 then Time = string.format('%d', S) end end end return Time end end local function FormatCooldown(Cooldown) if Cooldown then local Time = math.floor(Cooldown) if Time > 0 then Time = SecondsToTime(Time, false) Time = string.gsub(Time, 'Day', 'day') Time = string.gsub(Time, 'Hr', 'hour') Time = string.gsub(Time, 'Min', 'minute') Time = string.gsub(Time, 'Sec', 'second') end return Time end end local function ShortFormatCooldown(Cooldown, FourDigits) if Cooldown and type(Cooldown) == 'number' and Cooldown >= 0 then return FormatTime(Cooldown, FourDigits) else return '' end end local function ConvertCooldown(Cooldown, Long) if Cooldown then local S = Cooldown - GetTime() if S >= 0 then if Long then return FormatCooldown(S) else return ' |cffE00000(' .. ShortFormatCooldown(S) .. ')|r' end end end end local function HexToDec(Hex) Hex = string.upper(Hex) local Total = 0 for I = 1, string.len(Hex) do local Char = string.byte(Hex, I) local Numeric if Char > 64 then Numeric = Char - 55 else Numeric = Char - 48 end Total = Total + Numeric * math.pow(16, string.len(Hex) - I) end return Total end local function Round(number, decimals) return math.floor((number * math.pow(10, decimals) + 0.5)) / math.pow(10, decimals) end local function LinkToName(Link) if Link then return string.gsub(Link,'^.*%[(.*)%].*$','%1') end end local function LinkToColor(Link) if Link then return string.gsub(Link, '^.*|cff(.-)|.*$', '%1') end end local function GetLinkColorRGB(Link) if Link then local Color = HexToDec(LinkToColor(Link)) local R = math.floor(Color / 65536) local G = math.floor((Color - R * 65536) / 256) local B = math.floor((Color - R * 65536 - G * 256)) return R/255, G/255, B/255 end end local function ColorToQuality(R, G, B) if R then for I = 1, table.getn(QualityColor) do local C = QualityColor[I] if C.R == Round(R, 2) and C.G == Round(G, 2) and C.B == Round(B, 2) then return I end end end return 0 end local function LinkToQuality(Link) local R, G, B = GetLinkColorRGB(Link) return ColorToQuality(R, G, B) end local function ATSW_LinkToID(Link) if Link then local _, _, LinkType, ID = string.find(Link, '^.*|H?([^:]*):?(%d+)') return tonumber(ID) end end local ITEM_MIN_LEVEL_PATTERN = string.gsub(ITEM_MIN_LEVEL,'%%d', '(%%d+)') local function GetItemMinLevel(ID, Reagent) local Stats = {GetTradeSkillItemStats(ID)} for I, V in pairs(Stats) do local _, _, Level = string.find(V, ITEM_MIN_LEVEL_PATTERN) if Level then return tonumber(Level) end end end local function GetItemTexture(ID) local _, _, _, _, _, _, _, _, Texture = GetItemInfo(ID) return Texture end local function RemoveEscapeCharacters(String) local Result = tostring(String) Result = gsub(Result, '|c........', '' ) -- Remove color start. Result = gsub(Result, '|r', '' ) -- Remove color end. Result = gsub(Result, '|H.-|h(.-)|h', '%1' ) -- Remove links. Result = gsub(Result, '|T.-|t', '' ) -- Remove textures. Result = gsub(Result, '{.-}', '' ) -- Remove raid target icons. return Result end local function GetCategoryTexture(Expanded) return 'Interface\\Buttons\\UI-' .. (Expanded and 'Min' or 'Pl') .. 'usButton-Up' end local function GetSpellNum(Name) local I, SName = 0 repeat I = I + 1 SName = GetSpellName(I, BOOKTYPE_SPELL) if SName and Name and string.find(SName, Name) then return I end until not SName end local function GetProfessionTexture(Name) local SpellNum = GetSpellNum(Name) if SpellNum then return GetSpellTexture(SpellNum, BOOKTYPE_SPELL) end end local function Icon(Name) return Name and ('Interface\\Icons\\' .. Name) end local function IsEnchant(Index) local I = Icon('Spell_Holy_GreaterHeal') if type(Index) == 'string' then return Index == I elseif type(Index) == 'number' then return ATSW_GetCraftTexture(Index) == I end end local function GetLatency() local _, _, lag = GetNetStats() return lag / 1000 end local function Debug(Message) if ATSW_Debug then ChatFrame1:AddMessage('ATSW Debug: ' .. (Message or 'nil')) end end local Key = { Ctrl = function() return IsControlKeyDown() end, Shift = function() return IsShiftKeyDown() end, Alt = function() return IsAltKeyDown() end } local Delayed = false ATSW_InTime = 0 -- Game information get functions local function GetCraftCaption() local Skill, Rank, MaxRank, Name if TradeSkill then Skill, Rank, MaxRank = GetTradeSkillLine() else Skill = GetCraftName() _, Rank, MaxRank = GetCraftDisplaySkillLine() end if ATSW_ProfessionExists(Skill) then Name = Skill else Name = LastCastName end return Name, Rank, MaxRank end local function GetCraftCount() if TradeSkill then return GetNumTradeSkills() else return GetNumCrafts() end end local function GetFirstCraft() if TradeSkill then return GetFirstTradeSkill() else local _, _, Type = GetCraftInfo(1) return 1 + (Type == 'header' and 1 or 0) end end local function GetCraftNameType (Index) local Name, Type, SubName if TradeSkill then Name, Type = GetTradeSkillInfo(Index) else Name, SubName, Type = GetCraftInfo(Index) end return Name, SubName, Type end local function GetCraftLink (Index) if TradeSkill then return GetTradeSkillItemLink(Index) else return GetCraftItemLink(Index) end end ATSW_AtlasIDCache = {} local function CraftIDtoAtlasID(ID, Enchants) if ATSW_AtlasIDCache[ID] then return ATSW_AtlasIDCache[ID] end local DB = GetSpellInfoVanillaDB or GetSpellInfoAtlasLootDB local Table if Enchants then Table = DB['enchants'] else Table = DB['craftspells'] end if Table then for I, Value in pairs(Table) do local Item = Value.craftItem or Value.item if Item and ID and Item == ID then ATSW_AtlasIDCache[ID] = I return I end end end end local function GetCraftTexture (Index) local Icon local DB = GetSpellInfoVanillaDB or GetSpellInfoAtlasLootDB if DB then local ID = ATSW_LinkToID(GetCraftLink(Index)) local AtlasID = CraftIDtoAtlasID(ID, not TradeSkill) local Info = DB['enchants'][AtlasID] or DB['craftspells'][AtlasID] Icon = Info and (Info['icon'] or GetItemTexture(Info['craftItem'])) end if not Icon then if TradeSkill then Icon = GetTradeSkillIcon(Index) else Icon = GetCraftIcon(Index) end end return Icon end function ATSW_GetCraftTexture (Index) return GetCraftTexture(Index) end local function GetCraftMinMax (Index) local Min, Max if TradeSkill then Min, Max = GetTradeSkillNumMade(Index) else Min, Max = 1, 1 end return Min, Max end local function GetCraftCooldown (Index) if TradeSkill then return GetTradeSkillCooldown(Index) end end local function GetReagentCount (Index) if TradeSkill then return GetTradeSkillNumReagents(Index) else return GetCraftNumReagents(Index) end end local function GetCraftInformation (Index) local Name, SubName, Type, Available, Expanded, Action, TrainingCost, RequiredLevel if TradeSkill then Name, Type, Available, Expanded, Action = GetTradeSkillInfo(Index) else Name, SubName, Type, Available, Expanded, TrainingCost, RequiredLevel = GetCraftInfo(Index) end return Name, SubName, Type, Available, Expanded, Action or TrainingCost, RequiredLevel end local function GetCraftData (Index) local Name, SubName, Type, Available, Expanded, ActionOrTrainingCost, RequiredLevel = GetCraftInformation(Index) local Min, Max = GetCraftMinMax(Index) return Name, SubName, Type, GetCraftTexture (Index), GetCraftLink (Index), Min, Max, GetCraftCooldown (Index), GetReagentCount (Index), ActionOrTrainingCost, RequiredLevel end local function GetReagentInformation (Index, ReagentIndex) if TradeSkill then return GetTradeSkillReagentInfo(Index, ReagentIndex) else return GetCraftReagentInfo(Index, ReagentIndex) end end local function GetReagentLink (Index, ReagentIndex) if TradeSkill then return GetTradeSkillReagentItemLink(Index, ReagentIndex) else return GetCraftReagentItemLink(Index, ReagentIndex) end end local function GetReagentData (Index, ReagentIndex) local Name, Texture, Amount, PlayerAmount = GetReagentInformation(Index, ReagentIndex) local Link = GetReagentLink(Index, ReagentIndex) return Name, Texture, Amount, PlayerAmount, Link end local function GetCraftRequirements (Index) if TradeSkill then return GetTradeSkillTools(Index) else return GetCraftSpellFocus(Index) end end local function SelectCraftItem (Index) if TradeSkill then return SelectTradeSkill(Index) else return SelectCraft(Index) end end local function Craft (Index, Amount) if TradeSkill then DoTradeSkill(Index, Amount) else DoCraft(Index) end end local function Profession() return ATSW_Profession[realm] and ATSW_Profession[realm][player] end local function RecipeSelected() return ATSW_SelectedRecipe[realm][player][Profession()] end function ATSW_RecipeSelected() return RecipeSelected() end local function Recipes(Prof) return ATSW.Recipes[realm][player][Prof or Profession()] end local function RecipesSize(Value) if tonumber(Value) and Profession() then ATSW.RecipesSize[realm][player][Profession()] = Value else return ATSW.RecipesSize[realm][player][Value or Profession()] or 0 end end local function Recipe(I, Prof) return Recipes(Prof or Profession() )[I] end function ATSW_Recipe(I) return Recipe(I) end local function AddRecipe(Index) local Size = RecipesSize() + 1 if Size > table.getn(Recipes()) then for I = 1, ATSW_TABLE_CREATION_SIZE do table.insert(Recipes(), { Name = nil, SubName = nil, Type = nil, Texture = nil, Position = nil, Link = nil, ProductMin = 0, ProductMax = 0, Cooldown = nil, Reagents = {}, ReagentsSize = 0, TrainingCost = 0, RequiredLevel = nil }) for RI = 1, MAX_TRADE_SKILL_REAGENTS do table.insert(Recipe(Size-1+I).Reagents, { Name = nil, Texture = nil, Amount = 0, PlayerAmount = 0, Link = nil }) end end end local R = Recipe(Size) R.Name, R.SubName, R.Type, R.Texture, R.Link, R.ProductMin, R.ProductMax, R.Cooldown, R.ReagentsSize, R.TrainingCost, R.RequiredLevel = GetCraftData(Index) R.Position = Index for I = 1, R.ReagentsSize do Rt = R.Reagents[I] Rt.Name, Rt.Texture, Rt.Amount, Rt.PlayerAmount, Rt.Link = GetReagentData(Index, I) end if R.Cooldown then R.Cooldown = GetTime() + R.Cooldown end RecipesSize(Size) end local function RecipesSorted() return ATSW.RecipesSorted[realm][player][Profession()] end local function RecipesSortedSize(Value) if Value and Profession() then ATSW.RecipesSortedSize[realm][player][Profession()] = Value end return ATSW.RecipesSortedSize[realm][player][Profession()] or 0 end local function RecipeSorted(I) return RecipesSorted()[I] end local function AddRecipeSorted(R) local Size = RecipesSortedSize() + 1 if Size > table.getn(RecipesSorted()) then for I = 1, ATSW_TABLE_CREATION_SIZE do table.insert(RecipesSorted(), nil ) end end RecipesSorted()[Size] = R RecipesSortedSize(Size) end -- Wrap functions with smaller names for easy access from code local function NeedToUpdate() return ATSW_NeedToUpdate[realm][player][Profession()] end local function SetNeedToUpdate(Prof, Value) if Value == nil then Value = true end if Prof then ATSW_NeedToUpdate[realm][player][Prof] = Value else for I in pairs(ATSW_NeedToUpdate[realm][player]) do ATSW_NeedToUpdate[realm][player][I] = Value end end end local function PreviousRecipes(Prof) return ATSW_PreviousRecipes[realm][player][Prof or Profession()] end local function PreviousRecipesSize(Prof) return table.getn(PreviousRecipes(Prof or Profession()) or {}) end local function PreviousRecipe(I) return PreviousRecipes()[I] end local function SortBy() return ATSW_SortBy[realm][player][Profession()] end local function SearchString() return ATSW_SearchString[realm][player][Profession()] end local function SetSearchString(Text) ATSW_SearchString[realm][player][Profession()] = Text end local function ScrollOffset() return ATSW_ScrollOffset[realm][player][Profession()] end local function Contracted() return ATSW_ContractedCategories[realm][player][Profession()][SortBy()] end function ATSW_Contracted() return Contracted() end local function SubClass() return ATSW_SubClassFilter[realm][player][Profession()] end local function InvSlot() return ATSW_InvSlotFilter[realm][player][Profession()] end local function SetSubClass(Value) if Profession() then ATSW_SubClassFilter[realm][player][Profession()] = Value end end local function SetInvSlot(Value) if Profession() then ATSW_InvSlotFilter[realm][player][Profession()] = Value end end local function Tasks(Prof) return ATSW_Tasks[realm][player][Prof or Profession()] end local function TasksSize(Prof) return table.getn(Tasks(Prof or Profession()) or {}) end local function Task(I, Prof) return Tasks(Prof or Profession())[I] end local function DoTaskExist(Name, Prof) for I, V in pairs(Tasks(Prof or Profession())) do if V.Name == Name then return I end end end local function NoTasks() if ATSW_Tasks and ATSW_Tasks[realm] and ATSW_Tasks[realm][player] then local TotalAmount = 0 for I in pairs(ATSW_Tasks[realm][player]) do TotalAmount = TotalAmount + TasksSize(I) end return TotalAmount == 0 else return true end end local function NecessaryReagents() return ATSW_NecessaryReagents[realm][player] end local function NecessaryReagentsSize() return table.getn(NecessaryReagents()) end local function NecessaryReagent(I) return NecessaryReagents()[I] end local function ResetNecessaries() for I = 1, NecessaryReagentsSize() do table.remove(NecessaryReagents(), NecessaryReagentsSize()+1-I) end end function ATSW_ShowAttributes(Value) if Value ~= nil and Profession() then ATSW_Attributes[realm][player][Profession()] = Value if SearchString() ~= '' then ATSW_GetRecipesSorted(true) end else return ATSW_Attributes[realm][player][Profession()] end end -- Sound (Open/Close) management functions and reading of last casted spell Original_PlaySound = PlaySound local function Stub() end local Original_CastSpellByName = CastSpellByName local function MyCastSpellByName(Name, OnSelf) local Storage = PlaySound LastCastName = Name PlaySound = Stub Original_CastSpellByName(Name, OnSelf) PlaySound = Storage end CastSpellByName = MyCastSpellByName local Original_CastSpell = CastSpell local function MyCastSpell(spellID, spellbookType) local Storage = PlaySound LastCastName = GetSpellName(spellID, spellbookType) PlaySound = Stub Original_CastSpell(spellID, spellbookType) PlaySound = Storage end CastSpell = MyCastSpell local function GetNameFromTooltip(Slot) ATSWActionNameTooltip:SetParent(UIParent) ATSWActionNameTooltip:SetOwner(UIParent, 'ANCHOR_NONE') ATSWActionNameTooltip:SetAction(Slot) return ATSWActionNameTooltipTextLeft1:GetText() end local Original_UseAction = UseAction local function MyUseAction(Slot, CheckCursor, OnSelf) local Storage = PlaySound LastCastName = GetNameFromTooltip(Slot) PlaySound = Stub Original_UseAction(Slot, CheckCursor, OnSelf) PlaySound = Storage end UseAction = MyUseAction -- Craft delay detection functions local Original_PickupContainerItem = PickupContainerItem local function MyPickupContainerItem(Bag, Slot) Processing.Delay = GetTime() Original_PickupContainerItem(Bag, Slot) end PickupContainerItem = MyPickupContainerItem local Original_BindEnchant = BindEnchant local function MyBindEnchant() Processing.Delay = GetTime() Original_BindEnchant() end BindEnchant = MyBindEnchant local Original_ReplaceEnchant = ReplaceEnchant local function MyReplaceEnchant() Processing.Delay = GetTime() Original_ReplaceEnchant() end ReplaceEnchant = MyReplaceEnchant local Original_ReplaceTradeEnchant = ReplaceTradeEnchant local function MyReplaceTradeEnchant() Processing.Delay = GetTime() Original_ReplaceTradeEnchant() end ReplaceTradeEnchant = MyReplaceTradeEnchant -- Getting information from craft list functions local function GetRecipeID(Name) if Name then return ATSW_LinkToID(Recipe(ATSW_GetRecipePosition(Name)).Link) end end local function GetTradeSkillsPosition(Name) -- OctoWoW can expose two different recipes with the same -- GetTradeSkillInfo() name while their output item links are different. -- Example observed in Blacksmithing: -- 175 -> name "Iron Belt Buckle", link [Iron Belt Buckle] -- 190 -> name "Iron Belt Buckle", link [Iron Buckle] -- ATSW may therefore use the output-item name to disambiguate its cached -- list. When that happens, find the live row by cached Position + item ID -- before falling back to the (broken/duplicated) game-provided name. if Name and RecipesSize() > 0 then for I = 1, RecipesSize() do local R = Recipe(I) if R and R.Type ~= 'header' and R.Name == Name and R.Position then local CachedID = R.Link and ATSW_LinkToID(R.Link) local LiveLink = GetCraftLink(R.Position) local LiveID = LiveLink and ATSW_LinkToID(LiveLink) if CachedID and LiveID and CachedID == LiveID then return R.Position end end end end for I = 1, GetCraftCount() do if GetCraftNameType(I) == Name then return I end end return false end local function IsInTradeSkills(Name) return GetTradeSkillsPosition(Name) ~= false end local function DropDownFilterPass(Name) return not (SubClass() > 0 or InvSlot() > 0) or IsInTradeSkills(Name) end local function SubNameToString(SubName) return SubName and SubName ~= '' and (' ' .. Color.GREY .. '(' .. SubName .. ')|r') or '' end function ATSW_SubNameToString(SubName) return SubNameToString(SubName) end local function SubNameToName(Name) if Name then local LName local _, _, SubName = string.find(Name, '%s%s%s%(+(.+)%)+') -- Triple space means SubName is in Name local TripleSpace = string.find(Name, '%s%s%s') if TripleSpace then LName = string.sub(Name, 1, TripleSpace - 1) else LName = Name end return LName, SubName end end local function RecipePosition(Name, List, ListSize) if Name and ListSize > 0 then local LName, SubName = SubNameToName(Name) if SubName == '' then SubName = nil end for I = 1, ListSize do local R = List[I] if R.Name == LName and (SubName and R.SubName == SubName or SubName == nil) then return R.Position end end end end local function GetRecipePosition(Name, Prof) if Prof == nil then Prof = Profession() end return RecipePosition(Name, Recipes(Prof), RecipesSize(Prof)) end function ATSW_GetRecipePosition(Name, Prof) return GetRecipePosition(Name, Prof) end local function GetRecipeSortedPosition(Name) return RecipePosition(Name, RecipesSorted(), RecipesSortedSize()) end local function GetPositionFromGame(Name) -- OctoWoW adds recipes with very similar names (for example -- "Iron Buckle" and "Iron Belt Buckle"). ATSW historically -- resolved a recipe only by its displayed name, which can select the -- wrong live tradeskill index when custom-server recipes reorder the list. -- Prefer the crafted item ID cached by ATSW and only fall back to the -- recipe name after the ENTIRE live list has been checked. local OriginalName = Name local CachedPos = GetRecipePosition(OriginalName) local CachedRecipe = CachedPos and Recipe(CachedPos) local CachedID = CachedRecipe and CachedRecipe.Link and ATSW_LinkToID(CachedRecipe.Link) local BaseName, SubName = SubNameToName(OriginalName) local NameFallback = nil local Count = GetCraftCount() for I = 1, Count do local GName, GSubName = GetCraftNameType(I) if GName then -- Exact output item identity is authoritative when available. if CachedID then local GameLink = GetCraftLink(I) local GameID = GameLink and ATSW_LinkToID(GameLink) if GameID and GameID == CachedID then return I end end -- Remember, but do not immediately return, a name match. This -- prevents a similarly named custom recipe from winning before -- the item-ID match later in the list is reached. if not NameFallback and GName == BaseName and (SubName and GSubName == SubName or SubName == nil) then NameFallback = I end end end return NameFallback or CachedPos end function ATSW_GetPositionFromGame(Name) return GetPositionFromGame(Name) end function ATSW_GetContractedPos(Name) for I = 1, table.getn(Contracted()) do if Contracted()[I] == Name then return I end end end function ATSW_IsContracted(Name) return ATSW_GetContractedPos(Name) end local function IsExpanded(Name) return not ATSW_IsContracted(Name) end function ATSW_GetProfessionTexture(Name) return GetProfessionTexture(Name) end local function IsBeastTraining() return GetProfessionTexture(Profession()) == Icon('Ability_Hunter_BeastCall02') end local function IsSmelting() return GetProfessionTexture(Profession()) == Icon('Spell_Fire_FlameBlades') end local function IsPoisons() return GetProfessionTexture(Profession()) == Icon('Trade_BrewPoison') end local function ClassColorize(Name) local Color = { ['Druid'] = '|cffff7c0a', ['Hunter'] = '|cffaad372', ['Mage'] = '|cff3fc7eb', ['Paladin'] = '|cfff48cba', ['Priest'] = '|cffffffff', ['Rogue'] = '|cfffff468', ['Shaman'] = '|cff0070dd', ['Warlock'] = '|cff8788ee', ['Warrior'] = '|cffc69b6d' } for I in ipairs(ATSW_Characters[realm]) do if ATSW_Characters[realm][I].Name == Name then return Color[ATSW_Characters[realm][I].Class] .. Name .. '|r' end end return Name end local function GetCraftingTime(Name, Amount) local ID = GetRecipeID(Name) local Cost = ATSW_TimeCost[ID] local DB = GetSpellInfoVanillaDB or GetSpellInfoAtlasLootDB if Cost then return Cost*(Amount or 0) elseif DB then local AtlasID = CraftIDtoAtlasID(ID) if AtlasID then local castTime = DB['craftspells'][AtlasID]['castTime'] return castTime end end end ATSW_TEST = 9000 function ATSW_TooltipSetSkill(Tooltip, Index) if ATSW_TradeSkill() then Tooltip:SetTradeSkillItem(Index) else Tooltip:SetCraftSpell(Index) end end -- Main code section local function SetSizeAndPoints(S, Width, Point, RelativePoint, RelativeTo, X, Y) S:SetJustifyH('LEFT') S:SetJustifyV('TOP') S:SetWidth(Width) S:SetPoint(Point, RelativePoint, RelativeTo, X, Y) end local HelpItems = 0 local function AddHelpItem(Parameter, Description, NoReplace) local C = HelpItems if not Parameter then Parameter = '' end if not Description then Description = '' end if not NoReplace then Parameter = string.gsub(Parameter, '|c', '|cffFFD100') Description = string.gsub(Description, '|c', '|cffFFD100') end local Frame = ATSWSearchHelpFrame local S = Frame:CreateFontString('ATSWHelp' .. C + 1, 'ARTWORK', 'GameFontHighlightSmall') if C == 0 then SetSizeAndPoints(S, 120, 'TOPLEFT', ATSWSearchHelpFrame, 'TOPLEFT', 20, -50) else SetSizeAndPoints(S, 120, 'TOPLEFT', 'ATSWHelp' .. C, 'BOTTOMLEFT', 0, -15) end S:SetText(Parameter) local D = Frame:CreateFontString('ATSWHelp' .. C + 1 .. 'Description', 'ARTWORK', 'GameFontHighlightSmall') SetSizeAndPoints(D, 265, 'TOPLEFT', S, 'TOPRIGHT') D:SetText(Description) HelpItems = C + 1 end function ATSW_CreateListButtons() -- Create recipe buttons for R = 1, ATSW_RECIPES_DISPLAYED do local B = CreateFrame('Button', 'ATSWRecipe' .. R, ATSWFrame, 'ATSWRecipeButtonTemplate') B.XOffset = 22 B.Position = nil B.Name = nil B.Type = nil B.Possible = nil B.Text = nil if R == 1 then B:SetPoint ('TOP', ATSWFrame, 'TOP', 0, -77) else B:SetPoint ('TOP', 'ATSWRecipe' .. R - 1, 'BOTTOM', 0, 0) end B:SetWidth (ATSWListScrollFrame:GetWidth()) B:SetHeight (ATSW_TRADESKILL_HEIGHT) B:SetPoint ('LEFT', ATSWFrame, 'LEFT', B.XOffset, 0) end -- Set highlight frame ATSWHighlight:SetHeight(ATSW_TRADESKILL_HEIGHT) ATSWHighlightMouseOver:SetHeight(ATSW_TRADESKILL_HEIGHT) -- Create tool buttons for T = 1, ATSW_TOOLS_DISPLAYED do local B = CreateFrame('Button', 'ATSWTool' .. T, ATSWFrame, 'ATSWToolTemplate') if T == 1 then B:SetPoint ('LEFT', ATSWToolsLabel, 'RIGHT', 4, 0) else B:SetPoint ('LEFT', 'ATSWTool' .. T - 1, 'RIGHT', 6, 0) end end -- Create reagent buttons local Columns = 4 local Row, Column for R = 1, MAX_TRADE_SKILL_REAGENTS do local B = CreateFrame('Button', 'ATSWReagent' .. R, ATSWFrame, 'ATSWReagentTemplate') Row = math.ceil(R/Columns) Column = R - ((Row - 1) * Columns) if Column == 1 then if Row == 1 then B:SetPoint ('TOPLEFT', ATSWReagentsLabel, 'BOTTOMLEFT', 11, -6) else B:SetPoint ('TOPLEFT', 'ATSWReagent' .. 1 + (Columns * (math.max(Row - 2, 0))) , 'BOTTOMLEFT', 0, -30) end else B:SetPoint ('LEFT', 'ATSWReagent' .. R - 1, 'RIGHT', 55, 0) end B:SetID(R) end -- Create task buttons for T = 1, ATSW_TASKS_DISPLAYED do local B = CreateFrame('Button', 'ATSWTask' .. T, ATSWFrame, 'ATSWTaskButtonTemplate') if T == 1 then B:SetPoint ('TOPLEFT', ATSWHorizontalBar1Left, 'TOPLEFT', 6, -14) else B:SetPoint ('TOPLEFT', 'ATSWTask' .. T - 1, 'BOTTOMLEFT', 0, -5) end B:SetWidth (314) B:SetHeight (ATSW_TRADESKILL_HEIGHT) local D = CreateFrame('Button', 'ATSWTask' .. T .. 'DeleteButton', B, 'UIPanelCloseButton') D:SetWidth (18) D:SetHeight (18) D:SetPoint ('RIGHT', ATSWTaskScrollFrame, 'RIGHT', -3, 0) D:SetPoint ('TOP', B, 'TOP', 0, 1) D:SetScript ('OnClick', ATSW_TaskDeleteOnClick) end -- Create search help strings AddHelpItem(ATSW_HELP_REAGENT, ATSW_HELP_REAGENT_DESC) AddHelpItem(ATSW_HELP_LEVEL, ATSW_HELP_LEVEL_DESC) AddHelpItem(ATSW_HELP_QUALITY, ATSW_HELP_QUALITY_DESC) AddHelpItem(ATSW_HELP_POSSIBLE, ATSW_HELP_POSSIBLE_DESC) AddHelpItem(ATSW_HELP_POSSIBLE_TOTAL, ATSW_HELP_POSSIBLE_TOTAL_DESC) AddHelpItem() AddHelpItem() AddHelpItem() AddHelpItem(ATSW_HELP_QNAMES, ATSW_HELP_QNAMES_DESC, true) AddHelpItem(ATSW_HELP_QCOLORS, ATSW_HELP_QCOLORS_DESC, true) end local function UnregisterEvents(Frame, ...) for _, Event in ipairs(arg) do Frame:UnregisterEvent(Event) end end local function RegisterEvents(Frame, ...) for _, Event in ipairs(arg) do Frame:RegisterEvent(Event) end end function ATSW_OnLoad() -- Unregister events from Blizzard professions frames EnableAddOn('Blizzard_TradeSkillUI') EnableAddOn('Blizzard_CraftUI') LoadAddOn('Blizzard_TradeSkillUI') LoadAddOn('Blizzard_CraftUI') UnregisterEvents(TradeSkillFrame, 'SKILL_LINES_CHANGED', 'PLAYER_ENTERING_WORLD', 'TRADE_SKILL_UPDATE', 'UNIT_PORTRAIT_UPDATE') UnregisterEvents(CraftFrame, 'SKILL_LINES_CHANGED', 'CRAFT_UPDATE', 'UNIT_PORTRAIT_UPDATE', 'SPELLS_CHANGED', 'UNIT_PET_TRAINING_POINTS') UnregisterEvents(UIParent, 'TRADE_SKILL_SHOW', 'TRADE_SKILL_CLOSE', 'CRAFT_SHOW', 'CRAFT_CLOSE') -- Register slash commands SLASH_ATSW1 = '/atsw' SLASH_ATSW2 = '/advancedtradeskillwindow' SlashCmdList['ATSW'] = ATSW_Command -- Register events RegisterEvents(ATSWFrame, 'TRADE_SKILL_SHOW', 'TRADE_SKILL_CLOSE', 'CRAFT_SHOW', 'CRAFT_CLOSE', 'UNIT_PET', 'UNIT_PET_TRAINING_POINTS', 'BAG_UPDATE', 'BANKFRAME_OPENED', 'BANKFRAME_CLOSED', 'MERCHANT_SHOW', 'MERCHANT_UPDATE', 'MERCHANT_CLOSED', 'AUCTION_HOUSE_CLOSED', 'AUCTION_HOUSE_SHOW', 'PLAYERBANKSLOTS_CHANGED', 'PLAYERBANKBAGSLOTS_CHANGED', 'PLAYER_ENTERING_WORLD', 'CHAT_MSG_SYSTEM', 'CHAT_MSG_SKILL', 'CHAT_MSG_LOOT', 'CVAR_UPDATE', 'UI_ERROR_MESSAGE') -- Attach name sort checkbox ATSWNameSortCheckbox:SetPoint('LEFT', ATSWDifficultySortCheckbox, 'RIGHT', ATSWDifficultySortCheckboxText2:GetWidth()+5, 0) -- Create side tabs for T = 1, ATSW_MAX_TRADESKILL_TABS do local CB = CreateFrame('CheckButton', 'ATSWFrameTab' .. T, ATSWFrameSideTabsFrame, 'ATSWTabTemplate') if T == 1 then CB:SetPoint('TOPLEFT', ATSWFrameSideTabsFrame, 'TOPRIGHT', 0, -36) else CB:SetPoint('TOPLEFT', 'ATSWFrameTab' .. T - 1, 'BOTTOMLEFT', 0, -17) end end -- Fix Blizzard GameTooltip edges insets (from 5 to 4) local R, G, B, A = GameTooltip:GetBackdropColor() GameTooltip:SetBackdrop({bgFile=[[Interface\Tooltips\UI-Tooltip-Background]], edgeFile=[[Interface\Tooltips\UI-Tooltip-Border]], tile = true, edgeSize = 16, tileSize = 16, insets = {left = 4, right = 4, top = 4, bottom = 4}}) GameTooltip:SetBackdropColor(R, G, B, A) -- Fix a bug which shows error 'skillOffset is nil' on line 171 of Blizzard_TradeSkillUI.lua (modified by Turtle-WoW team) FauxScrollFrame_SetOffset(TradeSkillListScrollFrame, 0) ATSWFrame:SetMovable(true) ATSWFrame:EnableMouse(true) ATSWFrame:RegisterForDrag('LeftButton') ATSWFrame:SetScript('OnDragStart', function() this:StartMoving() end) ATSWFrame:SetScript('OnDragStop', function() this:StopMovingOrSizing() end ) end local function SetDropDownFilter(SubClass, InvSlot) ATSWFrame:UnregisterEvent( 'TRADE_SKILL_UPDATE' ) local SubClasses = table.getn({GetTradeSkillSubClasses()}) local InvSlots = table.getn({GetTradeSkillInvSlots()}) if SubClasses < SubClass then SubClass = 0 end if InvSlots < InvSlot then InvSlot = 0 end SetTradeSkillSubClassFilter ( SubClass, 1, 1 ) SetTradeSkillInvSlotFilter ( InvSlot, 1, 1 ) if ATSWFrame.UpdateIsRegistered then ATSWFrame:RegisterEvent ( 'TRADE_SKILL_UPDATE' ) end end function ATSW_UpdateListScroll() FauxScrollFrame_Update ( ATSWListScrollFrame, RecipesSortedSize(), ATSW_RECIPES_DISPLAYED, ATSW_TRADESKILL_HEIGHT, nil, nil, nil, ATSWHighlight, 300, 300 ) end function ATSW_UpdateTaskListScroll() FauxScrollFrame_Update ( ATSWTaskScrollFrame, TasksSize(), ATSW_TASKS_DISPLAYED, ATSW_TRADESKILL_HEIGHT-1 ) end local function SetSortBy(Sort) if Profession() then ATSW_SortBy[realm][player][Profession()] = Sort ATSW_GetRecipesSorted(true) end end local OldCraftCount = 0 local function WhatReagentIsLackingData() for I = 1, MAX_TRADE_SKILL_REAGENTS do local Button = _G['ATSWReagent' .. I] local ButtonTexture = _G['ATSWReagent' .. I .. 'Texture'] if Button:IsVisible() then if not (ButtonTexture:GetTexture() and Button.Name and Button.Link) then return Button end end end return false end function ATSW_OnUpdate(TimePassed) -- Loading recipes if ATSWFrame:IsVisible() or ATSWCSFrame:IsVisible() then -- Update frame if trade skill count is changed if ATSWFrame.UpdateIsRegistered then if NeedToUpdate() then ATSW_UpdateFrame() end if UpdateCount > 0 then if IsBeastTraining() then ATSW_UpdateFrame() end ATSW_UpdateStatusBarRank() UpdateCount = 0 end else -- This check is necessary for fast initial loading while data is not in the game cache if OldCraftCount ~= GetCraftCount() then OldCraftCount = GetCraftCount() ATSW_UpdateFrame() end end -- Update missing recipes data if RecipesDataIsComplete > -1 then if RecipesDataTries > 0 then RecipesDataTries = RecipesDataTries - 1 else local Complete = false SetDropDownFilter(0, 0) -- It is needed for congruence of Recipe index and Game index if RecipesSize() ~= GetCraftCount() then Complete = true else for I = 1, RecipesSize() do local R = Recipe(I) if R.Type ~= 'header' then if not (R.Name and R.Texture) then local Name = GetCraftNameType(I) local Texture = GetCraftTexture(I) if Name and Texture then Complete = true end end end end end SetDropDownFilter(SubClass(), InvSlot()) if Complete then RecipesDataIsComplete = -1 ATSW_GetRecipes(true) ATSW_SelectRecipe(RecipeSelected()) else RecipesDataIsComplete = RecipesDataIsComplete + 2 RecipesDataTries = RecipesDataIsComplete end end end -- Update missing reagents data if not ReagentsDataIsComplete and RecipeSelected() then local B = WhatReagentIsLackingData() if B then local Name, Texture, _, _, Link = GetReagentData(B.RecipeIndex, B.ReagentIndex) ReagentsDataIsComplete = (Name and Texture and Link) ~= nil if ReagentsDataIsComplete then for I = 1, RecipesSize() do local R = Recipe(I) for J = 1, R.ReagentsSize do local Rt = R.Reagents[J] Rt.Name, Rt.Texture, Rt.Amount, Rt.PlayerAmount, Rt.Link = GetReagentData(I, J) end end ATSW_SelectRecipe(RecipeSelected()) end end end -- Update scroll offset if ScrollOffset() and ATSWListScrollFrameScrollBar:GetValue() ~= ScrollOffset() then ATSWListScrollFrameScrollBar:SetValue(ScrollOffset()) end -- Update cooldowns if GetTime() - 1 >= CooldownUpdateTime then -- Set cooldowns for recipe buttons for I = 1, ATSW_RECIPES_DISPLAYED do local Button = _G['ATSWRecipe' .. I] local ButtonSubText = _G['ATSWRecipe' .. I .. 'SubText'] if Button:IsVisible() and Button.Position and not IsBeastTraining() then ButtonSubText:SetText(ConvertCooldown(Recipe(Button.Position).Cooldown) or '') end end -- Set cooldowns for task buttons for I = 1, ATSW_TASKS_DISPLAYED do local Button = _G['ATSWTask' .. I] if Button:IsVisible() and Button.Position then Button:SetText(Button.Text .. (ConvertCooldown(Recipe(Button.Position).Cooldown) or '')) end end -- Set cooldown for schematic if ATSWRecipeCooldown.Cooldown then local CD = ConvertCooldown(ATSWRecipeCooldown.Cooldown, true) if CD then ATSWRecipeCooldown:SetText(COOLDOWN_REMAINING .. ' ' .. CD) else ATSWRecipeCooldown.Cooldown = nil ATSWRecipeCooldown:SetText('') end end -- If cooldowns in recipes are expired, then set them to nil and update Create button for I = 1, RecipesSize() do local R = Recipe(I) if R.Cooldown and R.Cooldown < GetTime() then R.Cooldown = nil if DoTaskExist(R.Name) or R.Name == RecipeSelected() then ATSW_UpdateCreateButton() end end end CooldownUpdateTime = GetTime() end ATSW_ArmorCraftCompatibility() end -- Progress bar if ATSWProgressBar.Working then local Bar = ATSWProgressBar local Border = ATSWProgressBarBorder local Spark = ATSWProgressBarSpark local Stop = ATSWProgressBarStop local Glow = ATSWProgressBarGlow local ItemFound for I = 1, ATSW_TASKS_DISPLAYED do local Button = _G['ATSWTask' .. I] if Button.Name == Processing.Recipe then ItemFound = true if not Bar.TaskItem or Bar.TaskItem ~= Button then Bar: SetParent(Button) Bar: SetPoint('TOPLEFT', Button:GetName(), 'TOPLEFT', -2, 0) Bar: SetFrameLevel(12) Border: SetFrameLevel(13) Spark: SetFrameLevel(14) Button: SetFrameLevel(15) Bar.TaskItem = Button end break end end if ItemFound then if not Bar:IsVisible() then Bar: Show() if Bar.Mode == ATSW_CAST then Spark: Show() Stop: SetParent(Bar) Stop: SetFrameLevel(15) Stop: Show() end end else Bar: Hide() Stop: Hide() end if Bar.Mode == ATSW_CAST then if GetTime() < Bar.EndTime then Bar: SetValue(GetTime()) Spark: SetPoint('CENTER', Bar, 'LEFT', (1 - (Bar.EndTime - GetTime())/(Bar.EndTime - Bar.StartTime)) * Bar:GetWidth(), -1) else if IsEnchant(Processing.Texture) then Bar.Mode = ATSW_FLASH end end local TimeLeft = Bar.EndTime - GetTime() ATSWProgressBarStopSubText:SetText(ShortFormatCooldown(TimeLeft+1, true)) elseif Bar.Mode == ATSW_FLASH then -- Glow appearing local Alpha = Glow:GetAlpha() + TimePassed / ATSW_FLASH_TIME Glow: SetAlpha(math.min(Alpha, 1)) if Spark:IsVisible() then local Min, Max = Bar:GetMinMaxValues() Bar: SetValue(Max) Stop: Hide() Spark: Hide() end if Alpha >= 1 then Bar.Mode = ATSW_FADEOUT end elseif Bar.Mode == ATSW_FADEOUT then -- Glow disappearing local Alpha = Glow:GetAlpha() - TimePassed / ATSW_FADEOUT_TIME if Alpha > 0 then Glow: SetAlpha(Alpha) Bar: SetAlpha(Alpha) if Bar:IsVisible() and Task(Bar.TaskItem.TaskIndex).Amount == 1 then Bar.TaskItem:SetAlpha(Alpha) end else ATSW_CompleteTask() Bar: Hide() end end end -- Delay functions if Delayed and GetTime() > ATSW_InTime then ATSW_Delayed() Delayed = false end end function ATSW_Delayed() end local function Delay(Function, Time) ATSW_Delayed = Function ATSW_InTime = GetTime() + Time Delayed = true end function ATSW_ProfessionExists(Prof) if Prof then local _, _, _, NumSpells = GetSpellTabInfo(1) for I in ipairs(Professions) do local ProfessionTexture = Icon(Professions[I]) for B = 1, NumSpells do local Texture = GetSpellTexture (B, 'BOOKTYPE_SPELL') local Name = GetSpellName (B, 'BOOKTYPE_SPELL') if Texture and Texture == ProfessionTexture and string.find(Name, Prof) then return true end end end end end local function GetFirstProfession() local _, _, _, NumSpells = GetSpellTabInfo(1) -- Search spell book by texture for I in ipairs(Professions) do local ProfessionTexture = Icon(Professions[I]) for B = 1, NumSpells do local Texture = GetSpellTexture (B, 'BOOKTYPE_SPELL') if Texture and Texture == ProfessionTexture then return GetSpellName(B, 'BOOKTYPE_SPELL') end end end end local function ResetTabs() for T = 1, ATSW_MAX_TRADESKILL_TABS do local Tab = _G['ATSWFrameTab'..T] if Tab then Tab.Name = nil local NormalTexture = Tab:GetNormalTexture() if NormalTexture then NormalTexture:SetTexture(nil) end end end end local function TabExists(Texture) for T = 1, ATSW_MAX_TRADESKILL_TABS do local Tab = _G['ATSWFrameTab'..T] if Tab then local NormalTexture = Tab:GetNormalTexture() if NormalTexture then local TabTexture = NormalTexture:GetTexture() if TabTexture and string.lower(TabTexture) == string.lower(Texture) then return true end end end end end local function ChangeSmeltingIcon(Texture) if Texture == 'Interface\\Icons\\Spell_Fire_FlameBlades' then return 'Interface\\AddOns\\AdvancedTradeSkillWindow2\\Textures\\Smelting' end return Texture end function ATSW_ConfigureSkillButtons(Exception) local _, _, _, NumSpells = GetSpellTabInfo(1) ResetTabs() -- Iterate tabs for T = 1, ATSW_MAX_TRADESKILL_TABS do local Tab = _G['ATSWFrameTab' .. T] local TabName, TabTexture -- Iterate professions for I in ipairs(Professions) do local ProfessionTexture = Icon(Professions[I]) ProfessionTexture = ChangeSmeltingIcon(ProfessionTexture) if not TabExists(ProfessionTexture) then -- Scan spell book for professions for B = 1, NumSpells do local Texture = GetSpellTexture (B, 'BOOKTYPE_SPELL') local Name = GetSpellName (B, 'BOOKTYPE_SPELL') Texture = ChangeSmeltingIcon(Texture) if Name ~= Exception and Texture and Texture == ProfessionTexture then TabName = GetSpellName(B, 'BOOKTYPE_SPELL') TabTexture = Texture break end end if TabTexture then break end end end SetVisible(Tab, TabTexture) Tab.Name = TabName Tab:SetNormalTexture(TabTexture) end if Profession() then ATSW_SelectTab(Profession()) end end function ATSW_SelectTab(Name) for T = 1, ATSW_MAX_TRADESKILL_TABS do local Tab = _G['ATSWFrameTab' .. T] Tab:SetChecked(Tab.Name == Name) end end function ATSW_AttachTabsTo(Frame) if ATSWFrameSideTabsFrame:GetParent() ~= Frame then ATSWFrameSideTabsFrame:SetParent(Frame) ATSWFrameSideTabsFrame:SetPoint('TOPLEFT', Frame, 'TOPRIGHT', -59, -28) ATSWFrameSideTabsFrame:Show() end end function ATSW_SwitchToFrame(Frame) local function GetFrame(F) if F == 1 then return ATSWFrame elseif F == 2 then return ATSWCSFrame end end ATSW_SwitchingFrames = true if ATSW_SwitchingToMain then ATSW_SwitchingToMain = false else if Frame and not (GetFrame(1):IsVisible() or GetFrame(2):IsVisible()) then Original_PlaySound('igCharacterInfoOpen') end end for I = 1, 2 do local F = GetFrame(I) if F:IsVisible() and F ~= Frame then if Frame == ATSWCSFrame then ATSW_SwitchingToEditor = true end HideUIPanel(F) end end for I = 1, 2 do local F = GetFrame(I) if not F:IsVisible() and F == Frame then ShowUIPanel (F) ATSW_AttachTabsTo (F) if not (BlizzMo and ATSWFrame.settings and ATSWFrame.settings.save) then if MerchantFrame:IsVisible() then local oldCenter = UIParent.center UIParent.center = nil SetCenterFrame(F) UIParent.center = oldCenter else local oldLeft = UIParent.left UIParent.left = nil SetLeftFrame(F) UIParent.left = oldLeft end end end end if not Frame and Processing.Active then Processing.Stop = true end ATSW_SwitchingFrames = false end local function LoadAllProfessionsOnce() if not LoadingProfessions then LoadingProfessions = true local CurrentProfession = GetCraftCaption() local LastName = CurrentProfession local _, _, _, NumSpells = GetSpellTabInfo(1) ATSWFrame: UnregisterEvent( 'TRADE_SKILL_SHOW' ) ATSWFrame: UnregisterEvent( 'TRADE_SKILL_CLOSE' ) ATSWFrame: UnregisterEvent( 'CRAFT_SHOW' ) ATSWFrame: UnregisterEvent( 'CRAFT_CLOSE' ) local OldSound = PlaySound PlaySound = Stub for I in ipairs(Professions) do local ProfessionTexture = Icon(Professions[I]) for B = 1, NumSpells do local Texture = GetSpellTexture (B, 'BOOKTYPE_SPELL') local Name = GetSpellName (B, 'BOOKTYPE_SPELL') if Name and not string.find(Name, CurrentProfession) and Texture == ProfessionTexture then CastSpellByName(Name) LastName = Name break end end end if LastName ~= CurrentProfession then CloseTradeSkill() CloseCraft() CastSpellByName(CurrentProfession) end PlaySound = OldSound ATSWFrame: RegisterEvent( 'TRADE_SKILL_SHOW' ) ATSWFrame: RegisterEvent( 'TRADE_SKILL_CLOSE' ) ATSWFrame: RegisterEvent( 'CRAFT_SHOW' ) ATSWFrame: RegisterEvent( 'CRAFT_CLOSE' ) end end local function ATSW_Show() LoadAllProfessionsOnce() -- This function will cause all learned professions recipe data to load into the game from the server at once. -- After the user chose another profession the recipe data is already in the game to be loaded into the frame. if not InitializedOnShow then ATSW_CreateListButtons() for I = 1, ATSW_TASKS_DISPLAYED do _G['ATSWTask' .. I .. 'DeleteButton']:GetNormalTexture():SetTexCoord(0.2, 0.8, 0.2, 0.8) _G['ATSWTask' .. I .. 'DeleteButton']:GetPushedTexture():SetTexCoord(0.2, 0.8, 0.2, 0.8) end ATSW_ConfigureSkillButtons() ATSW_AtlasLoot = ATSW_CheckForAtlasLootLoaded() InitializedOnShow = true end ATSW_InitializeFrame () ATSW_UpdateCaption () ATSW_ShowSort () ATSW_ShowSearch () ATSW_UpdateBackground () ATSW_SelectTab (Profession()) ATSW_ShowAmount () ATSW_ShowAmountMenu (TradeSkill) ATSW_ShowDropDownMenu (TradeSkill) ATSW_ShowReagentsButton (not IsBeastTraining()) ATSW_ShowTaskButton (not IsBeastTraining()) ATSW_ShowTrainingPointsMenu (IsBeastTraining()) ATSWAttributesButton:SetChecked(ATSW_ShowAttributes()) if RecipesSize() > 0 then ATSW_GetRecipes() ATSW_ShowSelection() ATSW_UpdateTasks() if SortBy() == 'Custom' then ATSWCS_FillAllRecipes() ATSWCS_Update() end end if NoTasks() then ResetNecessaries() end ATSW_SwitchToFrame(ATSWFrame) ATSWUpdaterFrame:Show() ATSW_UpdateListScroll() end function CloseATSW() if ATSW_TradeSkill() then CloseTradeSkill() else CloseCraft() end end function ATSW_Hide() if UIParent:IsVisible() then ATSW_SwitchToFrame() Original_PlaySound('igCharacterInfoClose') end if ATSW_SwitchingToEditor then ATSW_SwitchingToEditor = false else PlaySound('igCharacterInfoClose') end ATSWUpdaterFrame:Hide() end local function InitializeTable(Table, Value, Prof) if Prof == nil then Prof = Profession() end if Table == nil then Table = {} end if Table[realm] == nil then Table[realm] = {} end if Table[realm][player] == nil then Table[realm][player] = {} end if Table[realm][player][Prof] == nil then Table[realm][player][Prof] = Value end end local function InitializeTables() local function InitializeContractedCategories() local Table = ATSW_ContractedCategories InitializeTable(Table, {}) if Table[realm][player][Profession()]['Category'] == nil then Table[realm][player][Profession()]['Category'] = {} end if Table[realm][player][Profession()]['Custom'] == nil then Table[realm][player][Profession()]['Custom'] = {} end end InitializeTable(ATSW.Recipes, {} ) InitializeTable(ATSW.RecipesSize, 0 ) InitializeTable(ATSW.RecipesSorted, {} ) InitializeTable(ATSW.RecipesSortedSize, 0 ) InitializeTable(ATSW_SelectedRecipe, nil ) InitializeTable(ATSW_Attributes, false ) InitializeTable(ATSW_NeedToUpdate, true ) InitializeTable(ATSW_SortBy, 'Category' ) InitializeTable(ATSW_SearchString, '' ) InitializeTable(ATSW_SubClassFilter, 0 ) InitializeTable(ATSW_InvSlotFilter, 0 ) InitializeTable(ATSW_ScrollOffset, 0 ) InitializeTable(ATSW_Amount, 0 ) InitializeTable(ATSW_PreviousRecipes, {} ) InitializeTable(ATSW_Tasks, {} ) InitializeTable(ATSW_CustomCategories, {} ) InitializeTable(ATSW_CSOpenCategory, 0 ) InitializeTable(ATSW_CSSelected, '' ) InitializeContractedCategories() end local function InitializeProfession() local Table = ATSW_Profession if not Table then Table = {} end if not Table[realm] then Table[realm] = {} end if not Table[realm][player] then Table[realm][player] = nil end end local function GetProfession() InitializeProfession() local Skill = GetCraftCaption() if Skill and Skill ~= 'UNKNOWN' then ATSW_Profession[realm][player] = Skill end return Skill end function ATSW_UpdateStatusBarRank() local _, Rank, MaxRank = GetCraftCaption() SetVisible(ATSWRankFrame, Rank > 0) SetVisible(ATSWRankFrameRank, MaxRank > 0) SetVisible(ATSWRankFrameSpark, Rank ~= MaxRank) if ATSWRankFrame:IsVisible() then ATSWRankFrame: SetMinMaxValues(0, MaxRank) ATSWRankFrame: SetValue(Rank) ATSWRankFrameSpark: SetPoint('CENTER', ATSWRankFrame, 'LEFT', ATSWRankFrame:GetWidth() * Rank/MaxRank, 0) end if ATSWRankFrameRank:IsVisible() then ATSWRankFrameRank: SetText(Rank .. '/' .. MaxRank) end end function ATSW_InitializeFrame() local SameProfession = GetCraftCaption() == Profession() if not SameProfession or RecipesSize() == 0 then GetProfession () InitializeTables () ATSW_ShowDropDown () if RecipesSize() == 0 then ATSW_HideRecipes () ATSW_HideRecipe () ATSW_HideTasks () end end if IsBeastTraining() then -- This is an easy and quick fix of an issue, not a solid complete solution ATSW_HideRecipe() end end function ATSW_UpdateBackground() local BG = ATSW_Background[GetProfessionTexture(Profession())] if BG then ATSWBackground:SetTexture('Interface\\AddOns\\AdvancedTradeSkillWindow2\\Textures\\Background\\' .. BG) else ATSWBackground:SetTexture(nil) end end function ATSW_FindSpecialization() local I = 0 local STexture local Specs = ATSW_Specializations[GetProfessionTexture(Profession())] if Specs then repeat I = I + 1 STexture = GetSpellTexture(I, BOOKTYPE_SPELL) if STexture then for S = 1, table.getn(Specs) do if STexture == Icon(Specs[S]) then return Color.GREY .. ' (' .. GetSpellName(I, BOOKTYPE_SPELL) .. ')|r' end end end until not STexture or I == 36 end return '' end function ATSW_UpdateCaption() local Name = GetCraftCaption() -- Set status bar info ATSWFrameTitleText:SetText(format(TEXT(TRADE_SKILL_TITLE), Name) .. ATSW_FindSpecialization()) ATSW_UpdateStatusBarRank() -- Set skill portrait SetPortraitToTexture(ATSWFramePortrait, GetProfessionTexture(Name)) end function ATSW_UpdateFrame() ATSW_InitializeFrame() ATSW_GetRecipes(true) ATSW_ShowSelection() if NoDropDownUpdate then NoDropDownUpdate = false else ATSW_ShowDropDown() end if SortBy() == 'Custom' then ATSWCS_FillAllRecipes() ATSWCS_Update() end SetNeedToUpdate(Profession(), false) if not ATSWFrame.UpdateIsRegistered and RecipesSize() > 0 and RecipesSize() == GetCraftCount() then ATSWFrame:RegisterEvent( 'TRADE_SKILL_UPDATE' ) ATSWFrame:RegisterEvent( 'CRAFT_UPDATE' ) ATSWFrame.UpdateIsRegistered = true end ATSW_UpdateTasks() ATSW_ShowTrainingPoints() end function ATSW_OnEvent() if ATSW_Debug then local s = '' if arg1 then s = s .. ' (|cffB0B0B0arg1|r = ' .. '|cffFFD100' .. arg1 .. '|r' end if arg2 then s = s .. ', |cffB0B0B0arg2|r = ' .. '|cffFFD100' .. arg2 .. '|r' end if arg3 then s = s .. ', |cffB0B0B0arg3|r = ' .. '|cffFFD100' .. arg3 .. '|r' end if arg4 then s = s .. ', |cffB0B0B0arg4|r = ' .. '|cffFFD100' .. arg4 .. '|r' end if arg5 then s = s .. ', |cffB0B0B0arg5|r = ' .. '|cffFFD100' .. arg5 .. '|r' end if arg6 then s = s .. ', |cffB0B0B0arg6|r = ' .. '|cffFFD100' .. arg6 .. '|r' end if arg7 then s = s .. ', |cffB0B0B0arg7|r = ' .. '|cffFFD100' .. arg7 .. '|r' end if arg8 then s = s .. ', |cffB0B0B0arg8|r = ' .. '|cffFFD100' .. arg8 .. '|r' end if arg9 then s = s .. ', |cffB0B0B0arg9|r = ' .. '|cffFFD100' .. arg9 .. '|r' end if arg10 then s = s .. ', |cffB0B0B0arg10|r = ' .. '|cffFFD100' .. arg10 .. '|r' end if s ~= '' then s = s .. ')' end -- Filter NPCScan TargetUnit() spam if not (event == 'UI_ERROR_MESSAGE' and string.find(arg1, ERR_UNIT_NOT_FOUND)) then Debug('|cffB0B0B0event|r = ' .. '|cffFFD100' .. tostring(event) .. '|r' .. s) end end if event == 'UNIT_PET' then ATSW_ShowTrainingPoints() elseif event == 'UNIT_PET_TRAINING_POINTS' then ATSW_ShowTrainingPoints() elseif event == 'BANKFRAME_OPENED' or event == 'PLAYERBANKSLOTS_CHANGED' or event == 'PLAYERBANKBAGSLOTS_CHANGED' then BankFrameOpened = true ATSW_SaveBank() elseif event == 'BANKFRAME_CLOSED' then BankFrameOpened = false elseif event == 'MERCHANT_SHOW' then ATSW_UpdateMerchant() if ATSW_Options.AutoBuy then ATSW_BuyFromMerchant() else ATSW_SetBuyFrameVisible() end if ATSWFrame:IsVisible() and not (BlizzMo and ATSWFrame.settings and ATSWFrame.settings.save) then local oldCenter = UIParent.center UIParent.center = nil SetCenterFrame(ATSWFrame) UIParent.center = oldCenter end elseif event == 'MERCHANT_CLOSED' then ATSWBuyNecessaryFrame:Hide() if ATSWFrame:IsVisible() and not (BlizzMo and ATSWFrame.settings and ATSWFrame.settings.save) then local oldLeft = UIParent.left UIParent.left = nil SetLeftFrame(ATSWFrame) UIParent.left = oldLeft end elseif event == 'MERCHANT_UPDATE' then ATSW_UpdateMerchant() elseif event == 'AUCTION_HOUSE_SHOW' then ATSW_ShowAuctionShoppingList() elseif event == 'AUCTION_HOUSE_CLOSED' then ATSWShoppingListFrame:Hide() elseif event == 'BAG_UPDATE' then if BankFrameOpened then ATSW_SaveBank() end ATSW_SaveBag(arg1) if ATSWFrame:IsVisible() then -- Check if a selected tool is update for Slot = 1, GetContainerNumSlots(arg1) do local function ToolIsUpdating(BID) for I = 1, ATSW_TOOLS_DISPLAYED do local Button = _G['ATSWTool' .. I] if Button:IsVisible() then local ToolID = ToolID[Button.Name] if BID == ToolID then return true end end end return false end local BID = ATSW_LinkToID(GetContainerItemLink(arg1, Slot)) if ToolIsUpdating(BID) then ATSW_SelectRecipe(RecipeSelected()) end end end -- Update all frame sections if ATSWFrame: IsVisible() then ATSW_UpdateRecipes () ATSW_UpdateTasks () if RecipeSelected() then ATSW_SelectRecipe(RecipeSelected()) end end if ATSWCSFrame: IsVisible() then ATSWCS_UpdateATSWFrame () ATSWCS_Update () end ATSW_UpdateNecessaryReagents() if MerchantFrame: IsVisible() then ATSW_SetBuyFrameVisible () end if ATSWShoppingListFrame: IsVisible() then ATSW_UpdateAuctionList () end elseif event == 'TRADE_SKILL_UPDATE' or event == 'CRAFT_UPDATE' then UpdateCount = UpdateCount + 1 elseif event == 'SPELLCAST_START' then if arg1 == Processing.Recipe and not Processing.Active then local function ColorizeProgressBar() local R, G, B = 1, 0.82, 0 local Colors = { [2840] = {R = 0.72, G = 0.45, B = 0.20}, -- Copper [3576] = {R = 1.00, G = 0.89, B = 0.48}, -- Tin [2841] = {R = 0.80, G = 0.67, B = 0.36}, -- Bronze [2842] = {R = 0.75, G = 0.75, B = 0.78}, -- Silver [3575] = {R = 0.62, G = 0.63, B = 0.65}, -- Iron [3577] = {R = 1.00, G = 0.84, B = 0.00}, -- Gold [3859] = {R = 0.64, G = 0.72, B = 0.68}, -- Steel [3860] = {R = 0.74, G = 0.85, B = 0.67}, -- Mithril [6037] = {R = 0.85, G = 0.98 ,B = 0.98}, -- Truesilver [12359] = {R = 0.50, G = 0.75, B = 0.71}, -- Thorium [11371] = {R = 0.57, G = 0.56, B = 0.54}, -- Dark Iron [17771] = {R = 0.78, G = 0.73 ,B = 0.86}, -- Elementium } local function GetColor(ID) return Colors[ID].R, Colors[ID].G, Colors[ID].B end if IsSmelting() then R, G, B = GetColor(GetRecipeID(Processing.Recipe)) end ATSWProgressBarTexture:SetVertexColor(R, G, B) end Processing.RecipeTime = arg2 / 1000 local ID = GetRecipeID(arg1) if ID then ATSW_TimeCost[ID] = Processing.RecipeTime end local CastTime = Processing.RecipeTime if TradeSkill then CastTime = CastTime * Processing.Amount end ATSW_InitializeProgressBar(Processing.Delay, (Processing.Delay + CastTime)) ColorizeProgressBar() Processing.Delay = GetTime() - Processing.Delay ATSW_StartProcessing() end elseif event == 'SPELLCAST_INTERRUPTED' then ATSW_StopProcessing() elseif event == 'PLAYER_ENTERING_WORLD' then ATSW_SaveBag(0) ATSW_Hide() -- Initialize Table local Table = ATSW_NecessaryReagents if Table == nil then Table = {} end if Table[realm] == nil then Table[realm] = {} end if Table[realm][player] == nil then Table[realm][player] = {} end if ATSW_Characters then if ATSW_Characters[realm] then for I in ipairs(ATSW_Characters[realm]) do if ATSW_Characters[realm][I].Name == player then return end end else ATSW_Characters[realm] = {} end end table.insert(ATSW_Characters[realm], { Name = player, Class = class }) elseif event == 'CHAT_MSG_SYSTEM' then if string.find(arg1, ERR_LEARN_RECIPE_PATTERN) or string.find(arg1, ERR_LEARN_SPELL_PATTERN) then -- Learn new recipe event SetNeedToUpdate() end if string.find(arg1, ERR_UNLEARN_RECIPE_PATTERN) then -- Unlearn profession event local _, _, Skill = string.find(arg1, ERR_UNLEARN_RECIPE_PATTERN) if ATSW_ProfessionExists(Skill) then -- Reset previous recipes for I = 1, PreviousRecipesSize(Skill) do table.remove(PreviousRecipes(Skill), PreviousRecipesSize(Skill)-1+I) end ATSW_ConfigureSkillButtons(Skill) -- Reconfigure tabs except Skill -- Reset tasks for I = 1, TasksSize(Skill) do ATSW_DeleteTask(Task(I, Skill).Name, nil, Skill) end -- Reset necessary settings ATSW_SelectedRecipe [realm][player][Skill] = nil ATSW.RecipesSize [realm][player][Skill] = 0 ATSW.RecipesSortedSize [realm][player][Skill] = 0 ATSW_SubClassFilter [realm][player][Skill] = 0 ATSW_InvSlotFilter [realm][player][Skill] = 0 ATSW_SelectedRecipe [realm][player][Skill] = nil ATSW_ScrollOffset [realm][player][Skill] = 0 if Skill == Profession() then ATSW_Profession[realm][player] = nil ATSW_SwitchToFrame() -- Hide frame if Skill is selected end end end elseif event == 'CHAT_MSG_LOOT' then -- Task completion if Processing.Active then if string.find(arg1, LOOT_ITEM_CREATED_SELF_PATTERN) then -- React on 'You create: %s' pattern Bar = ATSWProgressBar if Processing.Amount > 1 then ATSW_CompleteTask() else if Processing.Stop then ATSW_CompleteTask() -- Complete immediately else Bar.Mode = ATSW_FLASH -- Start flash animation end end RecipesRemaining = Processing.Amount - Processing.Complete TimePassed = GetTime() - Bar.StartTime ERT = (Processing.RecipeTime + GetLatency()) * RecipesRemaining -- Estimated remaining time Bar.EndTime = GetTime() + TimePassed + ERT Bar:SetMinMaxValues(Bar.StartTime, Bar.EndTime) end end elseif event == 'CHAT_MSG_SKILL' then if string.find(arg1, ERR_SKILL_UP_PATTERN) then -- Increase profession rank event local _, _, Skill = string.find(arg1, ERR_SKILL_UP_PATTERN) NoDropDownUpdate = true if ATSW_ProfessionExists(Skill) then SetNeedToUpdate(Skill) else SetNeedToUpdate() end ATSW_UpdateStatusBarRank() elseif string.find(arg1, ERR_SKILL_GAINED_PATTERN) then -- Learn new profession event ATSW_ConfigureSkillButtons() end elseif event == 'CVAR_UPDATE' then if arg1 == 'USE_UISCALE' then ATSWRecipeTooltip:SetScale(GetCVar('uiscale')) elseif arg1 == 'WINDOWED_MODE' then -- Fix for BarGlow texture becomes white if toggling windowed mode ATSWProgressBarGlow:SetTexture(nil) ATSWProgressBarGlow:SetTexture('Interface\\AddOns\\AdvancedTradeSkillWindow2\\Textures\\ProgressBarFlash') ATSWOptionsMenuHeader:SetTexture(nil) ATSWOptionsMenuHeader:SetTexture('Interface\\AddOns\\AdvancedTradeSkillWindow2\\Textures\\UI-DialogBox-Header') end elseif event == 'UI_ERROR_MESSAGE' then if Processing.Active and (string.find(arg1, ERR_INV_FULL) or string.find(arg1, ERR_ITEM_MAX_COUNT) or string.find(arg1, sRequires)) then ATSW_StopProcessing() end elseif event == 'TRADE_SKILL_SHOW' then if UIParent:IsVisible() then if ATSWFrame:IsVisible() or ATSWCSFrame:IsVisible() then ATSWFrame: UnregisterEvent ('CRAFT_CLOSE' ) CloseCraft() ATSWFrame: RegisterEvent ('CRAFT_CLOSE' ) end TradeSkill = true ATSW_Show() else CloseTradeSkill() end elseif event == 'CRAFT_SHOW' then if UIParent:IsVisible() then if ATSWFrame:IsVisible() or ATSWCSFrame:IsVisible() then ATSWFrame: UnregisterEvent ('TRADE_SKILL_CLOSE') CloseTradeSkill() if TradeSkill then Processing.Stop = true end ATSWFrame: RegisterEvent ('TRADE_SKILL_CLOSE') end TradeSkill = false ATSW_Show() else CloseCraft() end elseif event == 'TRADE_SKILL_CLOSE' then ATSW_Hide() elseif event == 'CRAFT_CLOSE' then ATSW_Hide() end end -- Control local function SetSortCheckboxes(Sort) ATSWCategorySortCheckbox: SetChecked( Sort == 'Category' ) ATSWDifficultySortCheckbox: SetChecked( Sort == 'Difficulty' ) ATSWNameSortCheckbox: SetChecked( Sort == 'Name' ) ATSWCustomSortCheckbox: SetChecked( Sort == 'Custom' ) SetVisible(ATSWCustomEditorButton, Sort == 'Custom') end function ATSW_SortCheckbox_OnClick() SetSortBy(this.Sort) SetSortCheckboxes(this.Sort) end local function FilterDropDownButton_OnClick(This, Filter, SetFilter, FilterDropDown, SetTradeSkillFilter) local Value = This:GetID() - 1 if Value ~= Filter then SetFilter(Value) UIDropDownMenu_SetSelectedID(FilterDropDown, Value + 1) SetTradeSkillFilter(Value, 1, 1) ATSW_GetRecipesSorted(true) end end function ATSWSubClassDropDownButton_OnClick() FilterDropDownButton_OnClick(this, SubClass, SetSubClass, ATSWSubClassDropDown, SetTradeSkillSubClassFilter) end function ATSWInvSlotDropDownButton_OnClick() FilterDropDownButton_OnClick(this, InvSlot, SetInvSlot, ATSWInvSlotDropDown, SetTradeSkillInvSlotFilter) end local function InsertIntoAuction(Link) local _, _, Name = string.find(Link, '%[?([^%[%]]*)%]') if aux_frame and aux_frame:IsVisible() then local aux = require 'aux' local info = require 'aux.util.info' local _, _, ID = string.find(Link, 'item:(%d+)') local Item = string.format('item:%d', tonumber(ID)) local item_info = info.item(tonumber(aux.select(3, strfind(Item, '^item:(%d+)')))) if item_info and aux.get_tab().CLICK_LINK then aux.get_tab().CLICK_LINK(item_info) end elseif AuxBuySearchBox and AuxBuySearchBox:IsVisible() then AuxBuySearchBox:SetText(Name) AuxBuySearchButton_OnClick() elseif AuxBuyNameInputBox and AuxBuyNameInputBox:IsVisible() then AuxBuyNameInputBox:SetText(Name) Aux.buy.SearchButton_onclick() elseif CanSendAuctionQuery() and AuctionFrame and AuctionFrame:IsVisible() then BrowseName:SetText(Name) AuctionFrameBrowse_Search() BrowseNoResultsText:SetText(BROWSE_NO_RESULTS) end end function ATSWRecipeButton_OnClick(Button) local Link = this.Link if Button == 'LeftButton' then local Name = this.Name local Type = this.Type if not Key.Ctrl() and Key.Shift() and not Key.Alt() and not (Type == 'header') then if ChatFrameEditBox:IsVisible() or WIM_EditBoxInFocus then ATSW_SayReagents(this.Position) elseif not (IsBeastTraining() or Type == 'header') then local Possible = ATSW_HowManyItemsArePossibleToCreate(Name) ATSW_AddTask(Name, Possible > 0 and Possible or 1) end elseif Key.Ctrl() and not Key.Shift() and not Key.Alt() and Link then DressUpItemLink(Link) else -- Pass the exact cached recipe position from the clicked row. -- This avoids ambiguity between OctoWoW recipes with similar names. ATSW_SelectRecipe(Name, Type, this.Position) end elseif Button == 'RightButton' then InsertIntoAuction(Link) end end function ATSW_TaskDeleteOnClick() ATSW_DeleteTask(this:GetParent().Name) end function ATSWTool_OnClick(Button) if Button == 'LeftButton' then table.insert(PreviousRecipes(), RecipeSelected()) ATSW_SelectRecipe(this.Name) end end function ATSWReagentsButton_OnClick() if ATSWReagentsFrame:IsVisible() then ATSWReagentsFrame:Hide() else for R = 1, ATSW_NECESSARIES_DISPLAYED do -- Create reagent frame buttons local F = CreateFrame('Frame', 'ATSWRFReagent' .. R, ATSWReagentsFrame, 'ATSWRFReagentTemplate') if R == 1 then F:SetPoint('TOPLEFT', 26, -59) else F:SetPoint('TOPLEFT', 'ATSWRFReagent' .. R - 1, 'BOTTOMLEFT') end end ATSW_UpdateNecessaryReagents() ATSWReagentsFrame:Show() end end local function Crement(Edge, Step) local Amount = ATSWAmountBox:GetNumber() if not Key.Ctrl() and Key.Shift() and not Key.Alt() then Amount = Edge end if Amount ~= Edge then ATSWAmountBox:SetNumber(Amount + Step) else if Edge == 0 then ATSWAmountBox:SetText(ATSW_ALL) else ATSWAmountBox:SetText(Edge) end end end function ATSWFrameDecrement_OnClick() Crement(0, -1) end function ATSWFrameIncrement_OnClick() Crement(100, 1) end function ATSWTask_OnClick(arg1) if arg1 == 'LeftButton' then if not Key.Ctrl() and Key.Shift() and not Key.Alt() and not Processing.Active and not Recipe(this.Position).Cooldown then local QName = Task(this.TaskIndex).Name local QAmount = Task(this.TaskIndex).Amount local IncludeTasks = true local Possible = ATSW_HowManyItemsArePossibleToCreate(QName, ATSW_POSSIBLE_NOTASK) > 0 if Possible then ATSW_Craft(QName, QAmount) end else ATSWRecipeButton_OnClick(arg1) end end end function ATSWTaskButton_OnClick() local Amount = ATSWAmountBox:GetNumber() local Name = RecipeSelected() if Amount == 0 and TradeSkill then if ATSW_Options.ConsiderBank or ATSW_Options.ConsiderAlts or ATSW_Options.ConsiderMerchants then Amount = ATSW_HowManyItemsArePossibleToCreateWithConsidering(Name) else Amount = ATSW_HowManyItemsArePossibleToCreate(Name) end end if Amount == 0 then Amount = 1 end ATSW_AddTask(Name, Amount) end function ATSWCreateButton_OnClick() local Amount = ATSWAmountBox:GetNumber() local Name, QName, QAmount = RecipeSelected() local IncludeTasks = true if IsBeastTraining() then Craft(GetRecipePosition(Name)) elseif not Processing.Active then if Amount == 0 then if TradeSkill then Amount = ATSW_HowManyItemsArePossibleToCreate(Name) else Amount = 1 end end for I = 1, TasksSize() do if ATSW_HowManyItemsArePossibleToCreate(Task(I).Name, ATSW_POSSIBLE_NOTASK) > 0 then QName = Task(I).Name QAmount = Task(I).Amount break end end if TasksSize() == 0 or not (QName and QAmount) then ATSW_AddTask(Name, Amount) QName = Name QAmount = Amount end if QName and QAmount then ATSW_Craft(QName, QAmount) end end end function ATSW_ShowFrame() CastSpellByName(ATSW_ProfessionExists(Profession()) and Profession() or GetFirstProfession() or '') end function ATSW_Command(Command) local function Chat(msg) DEFAULT_CHAT_FRAME:AddMessage(msg) end if Command == 'options' or Command == 'configuration' or Command == 'config' then ATSW_ToggleOptionsFrame() elseif Command == 'debug' then ATSW_Debug = not ATSW_Debug else ATSW_ShowFrame() end end function ATSW_ToggleOptionsFrame() if ATSWConfigFrame:IsVisible() then HideUIPanel(ATSWConfigFrame) else ATSWOFUnifiedButton: SetChecked(ATSW_Options.Unified) ATSWOFSeparateButton: SetChecked(not ATSW_Options.Unified) ATSWOFIncludeBankButton: SetChecked(ATSW_Options.ConsiderBank) ATSWOFIncludeAltsButton: SetChecked(ATSW_Options.ConsiderAlts) ATSWOFIncludeMerchantsButton: SetChecked(ATSW_Options.ConsiderMerchants) ATSWOFAutoBuyButton: SetChecked(ATSW_Options.AutoBuy) ATSWOFTooltipButton: SetChecked(ATSW_Options.RecipeTooltip) ATSWOFShoppingListButton: SetChecked(ATSW_Options.DisplayShoppingList) ShowUIPanel(ATSWConfigFrame) end end function ATSWRecipe_OnClick() local Link = this.Link if arg1 == 'RightButton' then InsertIntoAuction(Link) elseif arg1 == 'LeftButton' then if Key.Ctrl() and not Key.Shift() and not Key.Alt() then DressUpItemLink(Link) elseif not Key.Ctrl() and Key.Shift() and not Key.Alt() then if WIM_EditBoxInFocus then WIM_EditBoxInFocus:Insert(Link) elseif ChatFrameEditBox:IsVisible() then ChatFrameEditBox:Insert(Link) else if Link then ATSWSearchBox:SetText(':r ' .. (LinkToName(Link) or '')) end end elseif string.find(this:GetName(), 'ATSWReagent') then local ReagentID = ATSW_LinkToID(GetReagentLink(GetPositionFromGame(RecipeSelected()), this:GetID())) for I = 1, RecipesSize() do if ReagentID == ATSW_LinkToID(Recipe(I).Link) then table.insert(PreviousRecipes(), RecipeSelected()) ATSW_SelectRecipe(Recipe(I).Name) end end end end end function ATSWPreviousItemButton_OnClick() ATSW_SelectRecipe(table.remove(PreviousRecipes())) end function ATSWSubClassDropDown_OnLoad() _G[this:GetName()..'Button']:SetHeight(26) UIDropDownMenu_Initialize(this, ATSWSubClassDropDown_Initialize) UIDropDownMenu_SetWidth(120) UIDropDownMenu_SetSelectedID(ATSWSubClassDropDown, 1) end function ATSWSubClassDropDown_OnShow() UIDropDownMenu_Initialize(this, ATSWSubClassDropDown_Initialize) UIDropDownMenu_SetWidth(120) end function ATSWSubClassDropDown_Initialize() ATSWFilterFrame_LoadSubClasses(GetTradeSkillSubClasses()) end local function SetDropDownText(frame, filter, t, DefaultText) local Text if filter > 0 then Text = t[filter] elseif table.getn(t) == 1 then Text = t[1] else Text = DefaultText end UIDropDownMenu_SetText(Text, frame) end function ATSWFilterFrame_LoadSubClasses(...) local info = {} local filter = SubClass() if not filter then filter = 0 end if filter > arg.n then filter = 0 end info.func = ATSWSubClassDropDownButton_OnClick if arg.n > 1 then info.text = ALL_SUBCLASSES info.checked = filter == 0 UIDropDownMenu_AddButton(info) if info.checked then UIDropDownMenu_SetSelectedID(ATSWSubClassDropDown, 1) end end for I = 1, arg.n do info.text = arg[I] info.checked = filter == I UIDropDownMenu_AddButton(info) if info.checked then UIDropDownMenu_SetSelectedID(ATSWSubClassDropDown, I+1) end end if filter == 0 then UIDropDownMenu_SetSelectedID(ATSWSubClassDropDown, 1) end SetDropDownText(ATSWSubClassDropDown, filter, arg, ALL_SUBCLASSES) end function ATSWInvSlotDropDown_OnLoad() _G[this:GetName()..'Button']:SetHeight(26) UIDropDownMenu_Initialize(this, ATSWInvSlotDropDown_Initialize) UIDropDownMenu_SetWidth(120) UIDropDownMenu_SetSelectedID(ATSWInvSlotDropDown, 1) end function ATSWInvSlotDropDown_OnShow() UIDropDownMenu_Initialize(this, ATSWInvSlotDropDown_Initialize) UIDropDownMenu_SetWidth(120) end function ATSWInvSlotDropDown_Initialize() ATSWFilterFrame_LoadInvSlots(GetTradeSkillInvSlots()) end function ATSWFilterFrame_LoadInvSlots(...) local info = {} local filter = InvSlot() if not filter then filter = 0 end if filter > arg.n then filter = 0 end info.func = ATSWInvSlotDropDownButton_OnClick if arg.n > 1 then info.text = ALL_INVENTORY_SLOTS info.checked = filter == 0 UIDropDownMenu_AddButton(info) if info.checked then UIDropDownMenu_SetSelectedID(ATSWInvSlotDropDown, 1) end end for I = 1, arg.n do info.text = arg[I] info.checked = filter == I UIDropDownMenu_AddButton(info) if info.checked then UIDropDownMenu_SetSelectedID(ATSWInvSlotDropDown, I+1) end end if filter == 0 then UIDropDownMenu_SetSelectedID(ATSWInvSlotDropDown, 1) end SetDropDownText(ATSWInvSlotDropDown, filter, arg, ALL_INVENTORY_SLOTS) end -- Get recipes from server local function ATSW_DisambiguateDuplicateRecipeNames() -- Some OctoWoW profession rows have a duplicated/mislabelled recipe name -- even though GetTradeSkillItemLink() identifies different output items. -- ATSW2 historically uses recipe names as keys, so the duplicate causes one -- recipe to select/display/craft the other. Only alter names when a name is -- actually duplicated, and use the output link's [item name] to distinguish -- the rows. Unique recipes (e.g. Smelting recipes whose spell name differs -- from the output item) are left untouched. local Counts = {} for I = 1, RecipesSize() do local R = Recipe(I) if R and R.Type ~= 'header' and R.Name then Counts[R.Name] = (Counts[R.Name] or 0) + 1 end end for I = 1, RecipesSize() do local R = Recipe(I) if R and R.Type ~= 'header' and R.Name and Counts[R.Name] and Counts[R.Name] > 1 and R.Link then local _, _, LinkName = string.find(R.Link, '%[(.-)%]') if LinkName and LinkName ~= '' then R.OriginalName = R.Name R.Name = LinkName end end end end function ATSW_GetRecipes(New) if RecipesSize() == 0 or New then SetDropDownFilter(0, 0) -- It is needed for full trade skill list initial loading RecipesSize(0) for I = 1, GetCraftCount() do AddRecipe(I) end -- OctoWoW compatibility: distinguish duplicate API recipe names by -- their actual crafted item link before ATSW sorts/caches by name. ATSW_DisambiguateDuplicateRecipeNames() end SetDropDownFilter(SubClass(), InvSlot()) ATSW_GetRecipesSorted(New) end function ATSW_GetRecipesSorted(New) if RecipesSortedSize() == 0 or New then if SortBy() == 'Category' then ATSW_GetRecipesSortedByCategory () elseif SortBy() == 'Difficulty' then ATSW_GetRecipesSortedByDifficulty () elseif SortBy() == 'Name' then ATSW_GetRecipesSortedByName () elseif SortBy() == 'Custom' then ATSW_GetRecipesSortedByCustom () end end ATSW_UpdateRecipes() end -- Sorting of recipes function ATSW_GetRecipesSortedByCategory() RecipesSortedSize(0) local Expanded, H = true for I = 1, RecipesSize() do local R = Recipe(I) if R.Type == 'header' then H = R else if ATSW_Filter(R) and DropDownFilterPass(R.Name) then if H then Expanded = IsExpanded(H.Name) AddRecipeSorted(H) H = nil end if Expanded then AddRecipeSorted(R) end end end end end local function GetRecipesSortedWithoutHeaders() RecipesSortedSize(0) for I = 1, RecipesSize() do local R = Recipe(I) if R.Type ~= 'header' and ATSW_Filter(R) and DropDownFilterPass(R.Name) then AddRecipeSorted(R) end end end function ATSW_TypeToNumber(Type) local Number = 0 if Type == 'trivial' then Number = 4 end if Type == 'easy' then Number = 3 end if Type == 'medium' then Number = 2 end if Type == 'optimal' then Number = 1 end if Type == 'none' then Number = 0 end return Number end local function GetReagentDataProfName(Name) return ProfessionNamesForReagentData[GetProfessionTexture(Name)] end function ATSW_CompareDifficulty(Left, Right) return ATSW_TypeToNumber(Left.Type) < ATSW_TypeToNumber(Right.Type) end function ATSW_GetRecipesSortedByDifficulty() GetRecipesSortedWithoutHeaders() ATSW_Sort(RecipesSorted(), RecipesSortedSize(), ATSW_CompareDifficulty) -- Compatibility with AtlasLoot if ATSW_AtlasLoot then ATSW_Sort(RecipesSorted(), RecipesSortedSize(), ATSW_CompareDifficultyUsingExternalData) end end function ATSW_GetRecipesSortedByName() local function CompareName(Left, Right) return string.lower(Left.Name) < string.lower(Right.Name) end GetRecipesSortedWithoutHeaders() ATSW_Sort(RecipesSorted(), RecipesSortedSize(), CompareName) end function ATSW_GetRecipesSortedByCustom() RecipesSortedSize(0) -- First add all custom categories and recipes in them local function Categories() return ATSW_CustomCategories[realm][player][Profession()] end local Size = table.getn(Categories()) local function Category(C) return Categories()[C] end local function CatRecipe(C, I) return Category(C).Items[I] end local Expanded, HeaderAdded for C = 1, Size do Expanded = IsExpanded(Category(C).Name) HeaderAdded = false for I = 1, table.getn(Category(C).Items) do local R = CatRecipe(C, I) if R.Type ~= 'header' and ATSW_Filter(R) and DropDownFilterPass(R.Name) and IsInTradeSkills(R.Name) then if not HeaderAdded then AddRecipeSorted(Category(C)) HeaderAdded = true end if Expanded then AddRecipeSorted(Recipe(GetRecipePosition(R.Name))) end end end end -- Then add uncategorized recipes into the category 'Uncategorized' HeaderAdded = false for I = 1, RecipesSize() do local R = Recipe(I) if R.Type ~= 'header' and not ATSWCS_IsCategorized(R.Name, R.SubName) and ATSW_Filter(R) and DropDownFilterPass(R.Name) then if not HeaderAdded then AddRecipeSorted({Name = ATSW_UNCATEGORIZED, Type = 'header'}) HeaderAdded = true end if IsExpanded(ATSW_UNCATEGORIZED) then AddRecipeSorted(R) end end end end -- Thank you kikito for the function(https://stackoverflow.com/users/312586/kikito) -- https://stackoverflow.com/questions/36820438/how-can-built-in-sorting-algorithm-be-so-fast function ATSW_Sort(Table, Size, SortingFunction) local function Sort(Table, Left, Right, SortingFunction) if Right - Left > 0 then local P = Left for I = Left + 1, Right do if SortingFunction(Table[I], Table[P]) then if I == P + 1 then Table[P], Table[P+1] = Table[P+1], Table[P] else Table[P], Table[P+1], Table[I] = Table[I], Table[P], Table[P+1] end P = P + 1 end end Sort(Table, Left, P - 1, SortingFunction) Sort(Table, P + 1, Right, SortingFunction) end end Sort(Table, 1, Size, SortingFunction) end local StatW = Color.WHITE .. '%d' .. Color.LIGHTGREY local StatWP = Color.WHITE .. '%d%%' .. Color.LIGHTGREY local StatWS = Color.WHITE .. '%s' .. Color.LIGHTGREY local StatG = Color.GREEN .. '%d' .. Color.LIGHTGREY local StatGS = Color.GREEN .. '%s' .. Color.LIGHTGREY local EnchantPatterns = { {Pattern = 'enchant[s]* a piece of (%a+) .+ increase[s]* the ([%a%s]+) of the wearer by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 1}, {Pattern = 'enchant[s]* a piece of ([%a%s]+) armor to [%a]+ [+]*(%d+%%*) [to%s]*([%a%s]+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* a piece of ([%a%s]+) armor so it has.+ (%d+%%*) chance per hit of giving.+ (%d+) points of ([%a%s]+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%4$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to [%a]+ [+]*(%d+%%*) ([%a%s]+) skill.', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to increase (%a+).* resistance by (%d+).', Return = Color.WHITE .. '%3$d' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_RESIST .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 4}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to [%a]+ [+]*(%d+%%*).* (%a+) resistance', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_RESIST .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 4}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to restore (%d+%%*) (%a+) every (.+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_EVERY .. Color.WHITE .. '%4$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to do (%d+%%*) additional[%s%a]* damage [%a]+ (%a+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_DAMAGE_TO .. Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to [%a]+ [+]*(%d+%%*)[%s%a]* ([%a%s]+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to [%a]+ [%a%s]*(%d+%%*) chance [%s%a]* ([%a%s]+).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to decrease (%a+) .+by (%d+%%*).', Return = Color.WHITE .. '-%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to [%a%s]+ [+]*(%d*%%*)([%a%s]+) bonus([%a%s]*).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s%4$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to [%a]+ a slight ([%a%s]+) increase.', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to often .+ the target reducing their ([%a%s]+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_TO_REDUCE .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to often strike.+ (%d+).+ (%a+) damage.', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_DAMAGE_ON_HIT .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s-]+) to have a chance of (%a+) and [%a%s]+ damage to (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%2$s' .. ATSW_AND_DAMAGING .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_ON_HIT_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) so .+ the ([%a%s]+) skill of the wearer is increased by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) so that it increases[%a%s]* resistance to (%a+) [%a%s]*by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_RESIST .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 2}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) so that it increases the ([%a%s]+) of .+ by (%d+).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 2}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) so that it increases ([%a%s]+) by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 2}, {Pattern = 'enchant[s]*[%s]*[a%s]*.* (%a+) so .+ the wearer\'s ([%a%s]+) by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to increase the ([%a%s]+) of the wearer by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 1}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to increase.* healing.+ by.* (%d+%%*).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_HEALING .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 1}, {Pattern = 'enchant[s]*[%a%s]* (%a+) to increase ([%a%s]+) by (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 1}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to [%a]+ ([%a%s]+) damage by[%a%s]* (%d+%%*).', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_SPELL_DAMAGE .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to [%a]+ .+(%d+%%*).+ (%a+) damage when casting.+spells.', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%3$s' .. ATSW_SPELL_DAMAGE .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to [%a%s]+ (%d+%%*) ([%a%s]*)damage to .*spells.', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. '%3$s' .. ATSW_SPELL_DAMAGE .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to do (%d+%%*) additional[%s%a]* damage.', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_DAMAGE .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'enchant[s]* [a%s]*([%a%s]+) to.* steal life.* and give it to.+', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. ATSW_VAMPIRISM .. Color.LIGHTGREY .. ATSW_ON_HIT_FOR .. Color.WHITE .. '%1$s'}, -- Lifestealing {Pattern = 'enchant[s]* [a%s]*([%a%s]+) so that often when attacking.* heals.* (%d+) to (%d+).* (%a+) by (%d+) for (.+)', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.GREEN .. '%2$d' .. Color.WHITE .. '-' .. Color.GREEN .. '%3$d' .. Color.LIGHTGREY .. ATSW_HEALTH_AND .. Color.WHITE .. '%5$d' .. Color.LIGHTGREY .. ' ' .. '%4$s' .. ATSW_FOR .. Color.WHITE .. '%6$s' .. Color.LIGHTGREY .. ATSW_ON_HIT_FOR .. Color.WHITE .. '%1$s'} -- Crusader } local PoisonPatterns = { {Pattern = '.+ (%d+)%% chance .+ (%d+) to (%d+) ([%a%s]*)damage', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_TO .. Color.WHITE .. '%2$d-%3$d ' .. Color.LIGHTGREY .. '%4$sdmg', Similar = 1}, {Pattern = '.+ threat.+', Return = Color.WHITE .. ATSW_PLUS_THREAT}, {Pattern = '.+ (%d+)%% chance .+ (%d+) (%a*[%s]*)damage', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_TO .. Color.WHITE .. '%2$d ' .. Color.LIGHTGREY .. '%3$sdmg', Similar = 1}, {Pattern = '.+ (%d+)%% chance .+ slowing their ([%a%s]+) by (%d+)%% for (%d+) (%a+)', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_TO .. Color.WHITE .. '-%3$d%% ' .. Color.LIGHTGREY .. '%2$s' .. ATSW_FOR .. Color.WHITE .. '%4$d %5$s', Similar = 1}, {Pattern = '.+ (%d+)%% chance .+ increasing their casting time by (%d+)%% for (%d+) (%a+)', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_TO .. Color.WHITE .. '-%2$d%% ' .. Color.LIGHTGREY .. ATSW_CAST_TIME .. ATSW_FOR .. Color.WHITE .. '%3$d %4$s', Similar = 1}, {Pattern = '.+ (%d+)%% chance .+ reducing all healing .+ by (%d+%%*) for (%d+) (%a+)', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_TO .. Color.WHITE .. '-%2$s ' .. Color.LIGHTGREY .. ATSW_INC_HEAL .. ATSW_FOR .. Color.WHITE .. '%3$d %4$s', Similar = 1}, {Pattern = '. Stacks up to (%d+) times.', Return = Color.LIGHTGREY .. ATSW_STACK .. Color.WHITE .. '%1$d'}, {Pattern = '(%d+) charges.', Return = Color.WHITE .. '%1$d' .. Color.LIGHTGREY .. ATSW_CHARGES}, } local AttributePatterns = { {Pattern = '(%d+) Armor', Return = StatW .. ATSW_ARMOR}, {Pattern = '[+-]+(%d+) [%a%s]*Strength', Return = StatW .. ATSW_STR}, {Pattern = '[+-]+(%d+) [%a%s]*Agility', Return = StatW .. ATSW_AGI}, {Pattern = '[+-]+(%d+) [%a%s]*Stamina', Return = StatW .. ATSW_STA}, {Pattern = '[+-]+(%d+) [%a%s]*Intellect', Return = StatW .. ATSW_INT}, {Pattern = '[+-]+(%d+) [%a%s]*Spirit', Return = StatW .. ATSW_SPI}, {Pattern = 'gain (%d+) [%a%s]*Strength', Return = StatW .. ATSW_STR}, {Pattern = 'gain (%d+) [%a%s]*Agility', Return = StatW .. ATSW_AGI}, {Pattern = 'gain (%d+) [%a%s]*Stamina', Return = StatW .. ATSW_STA}, {Pattern = 'gain (%d+) [%a%s]*Intellect', Return = StatW .. ATSW_INT}, {Pattern = 'gain (%d+) [%a%s]*Spirit', Return = StatW .. ATSW_SPI}, {Pattern = ' eat.+ increase.* (%a+) by (%d+)', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' ' .. '%1$s'}, {Pattern = 'gain (%d+)%% ([%a]+)', Return = StatWP .. ' ' .. '%2$s'}, {Pattern = 'gain .+ and (%d+) ([%a]+)', Return = StatW .. ' ' .. '%2$s'}, {Pattern = 'eat.+ [%a]+ (%d+) ([%a]+) every (%d+) (%a+)', Return = StatW .. ' ' .. '%2$s' .. ATSW_EVERY .. '%3$d %4$s'}, {Pattern = 'All Stats (.%d+)', Return = StatW .. ATSW_ALL_ATTRIBUTES}, {Pattern = '[+-]+(%d+) (%a+) Resistance[%.]*$', Return = Color.WHITE .. '%1$d' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_RESIST}, {Pattern = '[+-]+(%d+) All Resistances', Return = StatW .. ATSW_ALL_RESIST}, {Pattern = '(%d+) Slot([%a%s]*) (%a+)', Return = StatW .. ATSW_SLOTS .. '%2$s' .. ' ' .. '%3$s'}, {Pattern = 'Equip: (%a+) [+](%d+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' ' .. '%1$s'}, {Pattern = 'gain[s]* (.%d+) (%a+) damage', Return = Color.WHITE .. '%1$d ' .. Color.LIGHTGREY .. '%2$s' .. ATSW_DAMAGE}, {Pattern = 'Absorb[s]* (%d+) to (%d+) (%a+) damage.+Lasts (.+).', Return = Color.WHITE .. '%1$d-%2$d ' .. Color.LIGHTGREY .. '%3$s' .. ATSW_DAMAGE_ABSORB .. ATSW_FOR .. Color.WHITE .. '%4$s', Similar = 4}, {Pattern = 'Absorb[s]* (%d+) (%a+) damage.+Lasts (.+).', Return = Color.WHITE .. '%1$d ' .. Color.LIGHTGREY .. '%2$s' .. ATSW_DAMAGE_ABSORB .. ATSW_FOR .. Color.WHITE .. '%3$s', Similar = 4}, {Pattern = 'Reflect[s]* (%a+) .+ (%d+) sec.', Return = Color.LIGHTGREY .. '%1$s' .. ATSW_REFLECT .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%2$d sec.'}, {Pattern = 'Chance on hit: decrease the ([%a%s]+) by (%d+%%*) for ([%d%.]+) (%a+).+affected.+ cannot stealth or turn invisible', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ', ' .. ATSW_MINUS_STEALTH_INVIS .. ATSW_FOR .. Color.WHITE .. '%3$d %4$s', Similar = 13}, {Pattern = 'decrease the ([%a%s]+) by (%d+%%*) for ([%d%.]+) (%a+).', Return = Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_FOR .. Color.WHITE .. '%3$s %4$s', Similar = 13}, {Pattern = 'increases Fishing by (%d+) (.+)', Return = StatW .. ATSW_FISHING .. '%s', Similar = 1}, {Pattern = 'Increase[s]*[d]* Defense %+(%d+)', Return = StatW .. ATSW_DEFENSE}, {Pattern = 'Reduces([%s%a]+) spell damage taken by (%d+%%*).', Return = Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. '%1$s' .. ATSW_DAMAGE_TAKEN}, {Pattern = 'Reduc[%a]+ damage taken from critical hits and damage over time effects by (%d+)%%.', Return = Color.WHITE .. '-%1$d%%' .. Color.LIGHTGREY .. ATSW_DMG_TAKEN_CRITS_DOTS}, {Pattern = 'Increases damage and healing done by magical spells and effects(.*) by up to (%d+)', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ATSW_SPELL_POWER .. '%1$s', Similar = 1}, {Pattern = 'Increases (%a+) spell damage by up to (%d+) for (.+).', Return = Color.WHITE .. '%2$d ' .. Color.LIGHTGREY .. '%1$s' .. ATSW_DAMAGE_FOR .. Color.WHITE .. ' %3$s.'}, {Pattern = 'Increases spell (%a+) damage by up to (%d+) for (.+).', Return = Color.WHITE .. '%2$d ' .. Color.LIGHTGREY .. '%1$s' .. ATSW_DAMAGE_FOR .. Color.WHITE .. ' %3$s.'}, {Pattern = 'Increases (%a+) damage by up to (%d+) for (.+).', Return = Color.WHITE .. '%2$d ' .. Color.LIGHTGREY .. '%1$s' .. ATSW_DAMAGE_FOR .. Color.WHITE .. ' %3$s.'}, {Pattern = 'Increases damage done to (%a+) by magical spells and effects by up to (%d+)', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ATSW_SPELL_DAMAGE_TO .. '%1$s', Similar = 1}, {Pattern = 'Increases damage done by (%a+) spells and effects by up to (%d+)', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_DAMAGE, Similar = 1}, {Pattern = 'Increases healing done by spells and effects by up to (%d+)', Return = StatW .. ATSW_HEALING, Similar = 1}, {Pattern = '(%d+%.?%d*)%% of damage dealt is returned as healing', Return = StatWP .. ' ' .. ATSW_VAMPIRISM, Similar = 1}, {Pattern = 'Increases .+ chance to block attacks .+ (%d+)%%.', Return = StatWP .. ATSW_BLOCK, Similar = 1}, {Pattern = 'Increases .+ block value .+ (%d+).', Return = StatW .. ATSW_BLOCK, Similar = 1}, {Pattern = 'Increases your chance to parry an attack by (%d+).', Return = StatWP .. ATSW_PARRY, Similar = 1}, {Pattern = 'Decreases your chance to parry an attack by (%d+).', Return = Color.WHITE .. '-%1$d%%' .. Color.LIGHTGREY .. ATSW_PARRY, Similar = 1}, {Pattern = 'Increases your chance to dodge an attack by (%d+).', Return = StatWP .. ATSW_DODGE, Similar = 1}, {Pattern = 'Increases your attack and casting speed by (%d+).', Return = StatWP .. ATSW_ATTACK_AND_CASTING_SPEED, Similar = 1}, {Pattern = 'Equip: .*Increases ([%a%s]+) by (%d+)%%%.', Return = Color.WHITE .. '%2$d%%' .. Color.LIGHTGREY .. ' ' .. '%1$s', Similar = 1}, {Pattern = 'Equip: .*Increases [your]*[%s]*(.+) by ([%d]+[%%]*).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s', Similar = 1}, {Pattern = 'Equip: .*Increases [the]*[your]*[%s]*([%a%s%d]+).', Return = Color.WHITE .. '+ %s', Similar = 1}, {Pattern = 'Use: Slightly increases [the]*[your]*[%s]*([%a%s%d]+) for ([%a%s%d]+).', Return = Color.WHITE .. '+ %s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s', Similar = 1}, {Pattern = 'Equip: .*Increase[s]* [your]*[%s]*(.+) by (%d+)%%.', Return = Color.WHITE .. '+%2$d%%' .. Color.LIGHTGREY .. ' ' .. '%1$s', Similar = 1}, {Pattern = '.*Increase [your]*[%s]*([%a%s]+) for ([%a%s%d]+).', Return = Color.LIGHTGREY .. '+ %s' .. ATSW_FOR .. Color.WHITE .. '%s.', Similar = 1}, {Pattern = 'Use: Increases [%a%s]*resistance to (%a+)[%a%s]* by (%d+%%*) for ([%d%s%a]+).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_RESIST .. ATSW_FOR .. Color.WHITE .. '%3$s', Similar = 3}, {Pattern = 'Increases [%a%s]*resistance to (%a+)[%a%s]* by (%d+%%*).', Return = Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_RESIST, Similar = 3}, {Pattern = 'Increases (%a+) by (%d+) and .+ critical hit by (%d+)%% for (.+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_AND .. Color.WHITE .. '%3$d%%' .. Color.LIGHTGREY .. ATSW_CRIT .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%4$s'}, {Pattern = 'the attacker has a (%d+)%%+ chance of being inflicted with disease that increases their damage taken by (%d+) for ([%a%s%d%.]+). Lasts for ([%a%s%d%.]+).', Return = StatWP .. ATSW_OF .. Color.WHITE .. '+%d' .. Color.LIGHTGREY .. ' ' .. ATSW_DAMAGE_ATTACKER_FOR .. Color.WHITE .. '%s,' .. Color.LIGHTGREY .. ATSW_LASTS .. Color.WHITE .. '%s.'}, {Pattern = '(%d+%%*) chance of dealing (%d+)([%s%a]*) on a successful melee attack.', Return = StatWS .. ' ' .. '%1$d%2$s' .. ATSW_ON_MELEE}, {Pattern = 'Use: Imbiber is ([%a%s%d]+) for the next ([%a%s%d]+).', Return = StatWS .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Use: Increases your ([%a%s]+) by (%d+%%*) for ([%d%s%a%.]+).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_FOR .. Color.WHITE .. '%3$s', Similar = 3}, {Pattern = 'Use: Increases the player\'s ([%a%s]+) by (%d+%%*) for ([%d%s%a]+).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_FOR .. Color.WHITE .. '%3$s', Similar = 3}, {Pattern = 'Use: Increases ([%a%s]+) by (%d+%%*) for ([%d%s%a%.]+).', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_FOR .. Color.WHITE .. '%3$s', Similar = 3}, {Pattern = 'Use: attempts to remove one (%a+), one (%a+) and one (%a+) from.+', Return = Color.WHITE .. '-%s, -%s, -%s'}, {Pattern = 'Use: Increases ([%a%s%d]+) by (%d+) to (%d+) and increases (%a+) by (%d+) for ([%a%s%d%.]+).', Return = Color.WHITE .. '%2$d-%3$d ' .. Color.LIGHTGREY .. '%1$s, ' .. Color.WHITE .. '%5$d' .. Color.LIGHTGREY .. ' ' .. '%4$s' .. ATSW_FOR .. Color.WHITE .. '%6$s', Similar = 6}, {Pattern = 'Use: Increases (.+) by (%d+) to (%d+).', Return = Color.WHITE .. '%2$d-%3$d ' .. Color.LIGHTGREY .. '%1$s', Similar = 6}, {Pattern = 'Use: Your (.+) is increased and your (.+) goes up by (%d+%%*).+Lasts (.+).', Return = Color.WHITE .. '+%3$s ' .. Color.LIGHTGREY .. '%2$s' .. ATSW_AND .. '%1$s' .. ATSW_FOR .. Color.WHITE .. '%4$s'}, {Pattern = 'Use: [%a]+ is cured of up to ([%a%s%d]+) poison[s]* up to ([%a%s%d]+).', Return = Color.WHITE .. '-%s' .. Color.LIGHTGREY .. ATSW_POISONS_OF .. Color.WHITE .. '%s'}, {Pattern = 'Decreases the magical resistances of your spell targets by (%d+).', Return = Color.WHITE .. '-%d' .. Color.LIGHTGREY .. ATSW_TARGETS_ALL_RESIST}, {Pattern = ': Removes (%d+) magic, curse, poison.+ disease effect.+ every (%d+) seconds for ([%a%s%d]+).', Return = Color.WHITE .. '-%d ' .. Color.LIGHTGREY .. ATSW_NON_PHYSICAL_DEBUFF .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_SECONDS_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Use: Regenerate (.+) for (.+).', Return = StatWS .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Use: Makes you immune to (.+) for [the%snext]*[%s]*([%a%s%d]+).', Return = Color.LIGHTGREY .. ATSW_IMMUNITY_TO .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s', Similar = 5}, {Pattern = 'Use: Allows the imbiber to ([%a%s%d]+) for ([%a%s%d]+).', Return = Color.WHITE .. '+ %s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s', Similar = 2}, {Pattern = 'Use: [%a]+s the imbiber ([%a%s%d]+) for ([%a%s%d]+).', Return = Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s', Similar = 2}, {Pattern = 'Use: [%a]+s the imbiber ([%a%s%d]+) for ([%a%s%d]+).', Return = Color.WHITE .. '+ %s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s', Similar = 2}, {Pattern = 'Shows the location of all nearby (%a+) on.+ minimap for ([%a%s%d]+)', Return = Color.LIGHTGREY .. ATSW_SHOWS .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Use: When applied to a melee weapon it gives a (%d+%%+) chance of casting (.+) at the opponent.+Lasts ([%a%s%d]+).', Return = Color.WHITE .. '+%s' .. Color.LIGHTGREY .. ATSW_TO_CAST .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Equip: Allows (%d+%%*) of your (%a+) regeneration to [%a]+ while ([%a%s]+).', Return = Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_REGEN_IF .. Color.WHITE .. '%s', Similar = 2}, {Pattern = 'Equip: Allows ([%a%s%d]+).', Return = Color.WHITE .. '%s', Similar = 2}, {Pattern = '.*Allows [%a%s%d]+ to ([%a%s%d]+).', Return = Color.WHITE .. '%s', Similar = 2}, {Pattern = '.*Allows control of a mechanical target for a short time.', Return = Color.WHITE .. ATSW_CONTROL_MECHANICAL}, {Pattern = 'Shoots a firework .+ that (.+).', Return = Color.LIGHTGREY .. '%s'}, {Pattern = 'reducing its melee and spell damage by (%d+) and its movement rate by (%d+)%% for (%d+) (%a+).', Return = Color.WHITE .. '-%d' .. Color.LIGHTGREY .. ATSW_MELEE_SPELL_DAMAGE_AND .. Color.WHITE .. '-%d%%' .. Color.LIGHTGREY .. ATSW_MOVEMENT_FOR .. Color.WHITE .. '%d %s'}, {Pattern = 'Use: shrinks .+ reducing [%a]+ ([%a%s]+) by (%d+)', Return = Color.WHITE .. '-%2$d' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_OF_TARGET}, {Pattern = 'Causes all (%a+) .+ to run away for (%d+) (%a+).', Return = Color.LIGHTGREY .. ATSW_SCARES .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%d %s.'}, {Pattern = '.*Attaches [%a%s]+ to a (.+) that .+ (%a+) by (%d+%%*)', Return = Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ' ' .. '%2$s' .. ATSW_FOR .. Color.WHITE .. ' %1$s', Similar = 3}, {Pattern = 'Drops a target dummy .+ (%d+) seconds', Return = Color.LIGHTGREY .. ATSW_ATTRACTS_MONSTERS .. Color.WHITE .. '%d sec.', Similar = 3}, {Pattern = 'Use: .+ weapon.+ swing (%d+)%%.* for (%d+) (%a+).', Return = StatWP .. ATSW_ATTACK_SPEED_FOR .. Color.WHITE .. '%d %s.'}, {Pattern = 'Creates a battle chicken.', Return = Color.WHITE .. ATSW_WILL_FIGHT}, {Pattern = ': Gives you a ([%a%s%d]+) that lets you ([%a%s%d]+).', Return = StatWS .. ATSW_TO_OTHER .. Color.WHITE .. '%s', Similar = 3}, {Pattern = 'Use: Increases your (.+) by up to (%d+) and your (.+) by up to (%d+) for (.+).', Return = Color.WHITE .. '%2$d %1$s' .. Color.LIGHTGREY .. ATSW_AND .. Color.WHITE .. '%4$d %3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%5$s', Similar = 3}, {Pattern = 'Use: Improves your chance to hit by (%d+)%% for (.+).', Return = StatWP .. ATSW_HIT_FOR .. Color.WHITE .. '%s.', Similar = 3}, {Pattern = 'Use: Improves your chance to hit with spells by (%d+)%% for (.+).', Return = StatWP .. ATSW_SPELL_HIT_FOR .. Color.WHITE .. '%s.', Similar = 3}, {Pattern = 'Equip: Improves your chance to get a critical strike with ([%a%s]+) by (%d+)%%.', Return = Color.WHITE .. '%2$d%%' .. Color.LIGHTGREY .. ' ' .. '%1$s' .. ATSW_CRIT, Similar = 3}, {Pattern = 'Use: Improves your chance to get a critical strike by (%d+)%% for (.+).', Return = StatWP .. ATSW_CRIT_FOR .. Color.WHITE .. ' %s.', Similar = 3}, {Pattern = 'Use: Improves your chance to get a critical strike with([%s%a]*) spells by (%d+)%% for (.+).', Return = Color.WHITE .. '%2$d%% %1$s' .. ATSW_SPELL_CRIT_FOR .. Color.WHITE .. '%3$s.', Similar = 3}, {Pattern = 'Use: [%a]+s your [%a%d%s]+ to ([%a%s%d]+).', Return = Color.WHITE .. ATSW_WILL .. Color.WHITE .. '%s', Similar = 3}, {Pattern = '.+: Reduces your (.+)[%a%s%d]* for (%d+)', Return = Color.WHITE .. '-' .. Color.LIGHTGREY .. '%s' .. ATSW_FOR .. Color.WHITE .. '%d sec.', Similar = 3}, {Pattern = 'Chance on hit: Blasts up to (%d+) (%a+) for (%d+) to (%d+) (%a+) damage', Return = Color.LIGHTGREY .. ATSW_CHANCE_FOR .. Color.WHITE .. '%3$d-%4$d ' .. Color.LIGHTGREY .. '%5$s' .. ATSW_DAMAGE_TO .. ' %1$d %2$s', Similar = 4}, {Pattern = 'Chance on hit: Blasts .+ (%d+) (%a+) damage', Return = Color.LIGHTGREY .. ATSW_CHANCE_FOR .. Color.WHITE .. '%d ' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE, Similar = 4}, {Pattern = 'Chance on hit: Sends a .+ at the enemy.+ (%d+) to (%d+) (%a+) damage', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. ' %s' .. ATSW_DAMAGE, Similar = 4}, {Pattern = 'Use: Blasts ([%a%s]+).', Return = StatWS .. '', Similar = 14}, {Pattern = 'Use: Deals (%d+) (%a+) damage every (.+) to (.+) for (.+).', Return = StatWS .. Color.LIGHTGREY .. ' ' .. '%s' .. ATSW_DAMAGE_PER .. Color.WHITE .. '%s ' .. Color.LIGHTGREY .. ATSW_TO_OTHER .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Use: Does (%d+) (%a+) damage .+ yard .+ every (%d+) seconds for ([%a%s%d]+)', Return = Color.WHITE .. '%d' .. Color.LIGHTGREY .. ' ' .. '%s' .. ATSW_DAMAGE_PER .. Color.WHITE .. ' %d sec.' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s.'}, {Pattern = '.(%d+) attack power in ([%a%s%,]+) forms only', Return = StatW .. ATSW_ATTACK_POWER_IN .. Color.WHITE .. '%s form', Similar = 9}, {Pattern = '.(%d+) attack power', Return = StatW .. ATSW_ATTACK_POWER, Similar = 9}, {Pattern = '.(%d+) ranged Attack Power', Return = StatW .. ATSW_RANGED_POWER}, {Pattern = 'increases attack power by (%d+) against (%a+)', Return = StatW .. ATSW_ATTACK_POWER_AGAINST .. Color.WHITE .. '%s'}, {Pattern = 'Your attacks ignore (%d+) of the target\'s armor', Return = StatW .. ATSW_ARMOR_IGNORE}, {Pattern = '[+]+ (%d+) [-]+ (%d+) (%a+) damage', Return = Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. ' ' .. '%s' .. ATSW_DAMAGE}, {Pattern = '(%d+%.?%d*) damage per second', Return = StatWS .. ATSW_DPS}, {Pattern = 'Improves your chance to hit by (%d+)', Return = StatWP .. ATSW_HIT, Similar = 3}, {Pattern = 'Improves your chance to hit with spells by (%d+)', Return = StatWP .. ATSW_SPELL_HIT, Similar = 3}, {Pattern = 'Improves your chance to get a critical strike by (%d+)', Return = StatWP .. ATSW_CRIT, Similar = 3}, {Pattern = 'Improves your chance to get a critical strike with([%s%a]*) spells by (%d+)', Return = Color.WHITE .. '%2$d%%' .. Color.LIGHTGREY .. '%1$s' .. ATSW_SPELL_CRIT, Similar = 3}, {Pattern = 'Heals (%d+) damage over (.+)', Return = StatG .. ATSW_HEALTH_OVER .. Color.WHITE .. '%s'}, {Pattern = 'Heals (%d+) health and (%d+) mana', Return = StatG .. ATSW_HEALTH .. Color.WHITE .. ', ' .. StatW .. ATSW_MANA}, {Pattern = 'Dispels polymorph', Return = Color.LIGHTGREY .. ATSW_DISPEL .. ' ' .. Color.WHITE .. ATSW_POLYMORPH}, {Pattern = 'Restores (%d+) ([^t][%a%s%d][%a%s%d][%a%s%d]*)(%.?%s?)', Return = StatG .. ' %s'}, {Pattern = 'Restores (%d+) to (%d+) (.+).', Return = Color.GREEN .. '%d' .. Color.LIGHTGREY .. '-' .. Color.GREEN .. '%d' .. Color.LIGHTGREY .. ' ' .. '%s'}, {Pattern = 'Reduces the cooldown of your (.+) ability by (.+)', Return = StatWS .. ATSW_CD_REDUCED_BY .. Color.WHITE .. '%s'}, {Pattern = 'Reduces the mana cost of your (.+) by (.+)', Return = StatWS .. ATSW_COST_REDUCED_BY .. Color.WHITE .. '%s'}, {Pattern = 'Drains (%d+) to (%d+) mana .+ Silences .+ (%d+)', Return = Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. ATSW_MANA_BURN_SILENCE .. Color.WHITE .. '%d sec.'}, {Pattern = 'Equip: (%d+)%% chance.+ (%d+) to (%d+)([%a%s]*) damage on a successful melee attack', Return = Color.WHITE .. '%d%%' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE, Similar = 4}, {Pattern = 'chance to strike your ranged target.* (%d+) to (%d+)([%a%s]*) damage', Return = Color.LIGHTGREY .. ATSW_CHANCE_FOR .. Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE, Similar = 4}, {Pattern = 'damaging you for (%d+) to (%d+)([%s%a]*) damage', Return = Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE_TO_YOU}, {Pattern = '(%d+) to (%d+)([%s%a]*) damage to you', Return = Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE_TO_YOU}, {Pattern = '(%d+)%% chance to stun.*(%d+) sec.', Return = Color.LIGHTGREY .. ATSW_STUN .. Color.WHITE .. '%2$d sec,' .. Color.LIGHTGREY .. ATSW_CHANCE .. Color.WHITE .. '%1$d%%', Similar = 5}, {Pattern = 'Chance on hit: stun.*(%d+)%s*sec', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF_STUN .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_SECONDS, Similar = 5}, {Pattern = 'Use: Increase (%a+) by (%d+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' %1$s'}, {Pattern = 'Use: Increase ([%a%s]+) damage by (%d+) for (.+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ATSW_DAMAGE_FOR .. ' ' .. Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = ': ([%a]+) Damage received is reduced by (%d+%%*).', Return = Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_DAMAGE_RECEIVED}, {Pattern = 'When struck by a harmful spell, the caster of that spell has a (%d+)%% chance to.* ([%a]+)[d].* for (.+).', Return = StatWP .. ATSW_TO_OTHER .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_ATTACKER_OF_SPELL_FOR .. Color.WHITE .. '%s'}, {Pattern = 'When struck by a non%-periodic damage spell you have a (%d+)%% chance of getting a (.+) spell shield that absorbs (%d+) to (%d+) of that school of damage', Return = Color.WHITE .. '%1$d%%' .. Color.LIGHTGREY .. ATSW_IF_DAMAGED_BY_SPELL .. Color.WHITE .. '%3$d-%4$d' .. Color.LIGHTGREY .. ATSW_OF_SAME_SCHOOL_FOR .. Color.WHITE .. '%2$s', Similar = 4}, {Pattern = '(%d+) to (%d+)([%a%s]*) damage', Return = Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE, Similar = 4}, {Pattern = 'When struck.* has a (%d+)%% chance to heal you for (%d+) to (%d+).', Return = StatWP .. ATSW_TO_RESTORE .. Color.WHITE .. '%d-%d' .. Color.LIGHTGREY .. ATSW_HEALTH_IF_DAMAGED, Similar = 10}, {Pattern = 'Heal ([%a%s]+) for (%d+) to (%d+).', Return = Color.GREEN .. '%2$d-%3$d' .. Color.LIGHTGREY .. ATSW_HEALTH_TO .. Color.WHITE .. '%1$s', Similar = 10}, {Pattern = 'When struck.* has a (%d+)%% chance of stealing (%d+) life from the attacker over (.+).', Return = StatWP .. ATSW_CHANCE_OF .. ATSW_VAMPIRISM .. ': ' .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_OVER .. Color.WHITE .. '%s'}, {Pattern = 'When struck.* has a (%d+)%% chance to reduce damage taken by (%a+) .+ by (%d+%%*) for ([%a%s%d]+)', Return = StatWP .. ATSW_CHANCE_TO_REDUCE .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_DAMAGE_TAKEN_BY .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'When struck by.* (%a+) attacker, that attacker has.* (%d+)%% chance of.+ to (%a+) for (%d+) (%a+).+ level (%d+) and below', Return = Color.WHITE .. '%2$d%%' .. Color.LIGHTGREY .. ATSW_CHANCE_TO .. Color.WHITE .. '%3$s ' .. Color.LIGHTGREY .. '%1$s' .. Color.LIGHTGREY .. ATSW_ATTACKER_OF_LEVEL .. Color.WHITE .. '%6$d' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%4$d %5$s.'}, {Pattern = 'Adds [+](%d+) healing and damage from spells to an item worn on the ([%a%s%,]+).+ level (%d+) and above', Return = Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_SPELL_POWER_FOR .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_OF_LEVEL .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_AND_ABOVE}, {Pattern = 'Adds (%d+) (%a+) damage to your [%a]+ attack', Return = StatW .. ATSW_OTHER_DAMAGE}, {Pattern = 'Equip: Immune to Disarm', Return = Color.LIGHTGREY .. ATSW_IMMUNE_TO .. Color.WHITE .. ATSW_DISARM}, {Pattern = 'Chance on hit: Disarm .* for (%d+) sec.', Return = Color.LIGHTGREY .. ATSW_CHANCE_TO .. Color.WHITE .. ATSW_DISARM .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%d sec.'}, {Pattern = 'Chance on hit: protect[s]*.* with a (%a+) shield.', Return = Color.LIGHTGREY .. ATSW_CHANCE_FOR .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_SHIELD}, {Pattern = 'Chance on hit: reduce[s]*.* threat [%a%s\']+.', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. ATSW_MINUS_THREAT}, {Pattern = 'Chance on hit: reduce[s]*.* (%a+) by (%d+%%*) for (%d+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. ATSW_TARGETS_STAT .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%3$d %4$s.', Similar = 12}, {Pattern = 'Chance on hit: reduce[s]*.* (%a+) by (%d+%%*).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '-%2$s' .. Color.LIGHTGREY .. ATSW_TARGETS_STAT, Similar = 12}, {Pattern = '. Stacks up to (%d+) times.', Return = Color.LIGHTGREY .. ATSW_STACK .. Color.WHITE .. '%d'}, {Pattern = 'Chance on hit: dispels[%a%s\']+.', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. ATSW_DISPEL}, {Pattern = 'Chance on hit: .+ target for (%d+) (%a+) damage and an additional (%d+)([%s%a]*) damage over ([%d%.]+) (%a+)', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ' %s' .. ATSW_DAMAGE_AND .. Color.WHITE .. '%d%s' .. Color.LIGHTGREY .. ATSW_DAMAGE_OVER .. Color.WHITE .. '%s %s.', Similar = 11}, {Pattern = 'Chance on hit: .+ (%d+)([%s%a]*) damage over ([%d%.]+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE_OVER .. Color.WHITE .. '%s %s.', Similar = 11}, {Pattern = 'Chance on hit: Party .+ crit increased by (%d+%%+).+Lasts.* ([%d%.]+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '+%s' .. Color.LIGHTGREY .. ATSW_CRIT_FOR_PARTY .. Color.WHITE .. '%s %s.'}, {Pattern = 'Give[s] (%d+).* (%a+) to.* party members.', Return = Color.WHITE .. '%d' .. Color.LIGHTGREY .. ' %s' .. ATSW_FOR_PARTY}, {Pattern = 'Equip: Increase[s]*.* (%a+) of.* party members by (%d+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_FOR_PARTY}, {Pattern = 'Chance on landing a damaging spell to deal (%d+)([%s%a]*) damage while sacrificing (%d+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. '%s' .. ATSW_DAMAGE_AND .. Color.WHITE .. '-%d' .. Color.LIGHTGREY .. ATSW_HEALTH_ON_SPELL_HIT}, {Pattern = 'Chance on hit: Spell damage taken by target.* increased by (%d+%%*) for ([%d%.]+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '+%s' .. Color.LIGHTGREY .. ATSW_SPELL_DAMAGE_TAKEN_BY_TARGET}, {Pattern = 'Chance on hit: Increase[s]*.* critical strike chance of.* next attack made within (%d+) (%a+) by (%d+)%%', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '+%3$d' .. Color.LIGHTGREY .. ATSW_CRIT_OF_NEXT_ATTACK .. Color.WHITE .. '%1$d %2$s'}, {Pattern = 'and slowing (%a+) speed by (%d+)%% for (%d+) (%a+)', Return = Color.WHITE .. '-%2$d%%' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_FOR .. '%3$d %4$s.'}, {Pattern = 'Chance on hit: .+ movement slow.* by (%d+)%% and increasing the time between attacks by (%d+)%% for (%d+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '-%d%%' .. Color.LIGHTGREY .. ATSW_MOVEMENT_AND .. Color.WHITE .. '-%d%% ' .. Color.LIGHTGREY .. ATSW_ATTACK_SPEED_OF_TARGET .. Color.WHITE .. '%d %s'}, {Pattern = 'Chance on hit: Heal self for (%d+) to (%d+) and increases ([%a%s]+) by (%d+) for ([%d%.]+) (%a+).', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%1$d-%2$d' .. Color.LIGHTGREY .. ATSW_HEALTH_AND .. Color.WHITE .. '%4$d' .. Color.LIGHTGREY .. '%3$s' .. ATSW_FOR .. Color.WHITE .. '%5$s %6$s'}, {Pattern = 'On successful melee or ranged attack.+ (%d+) (%a+) and.+ drain (%d+) (%a+)', Return = Color.LIGHTGREY .. ATSW_CHANCE_OF .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. '%s' .. ATSW_AND_DRAIN .. Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_MANA_ON_HIT}, {Pattern = 'Protects the wearer from .+ Shadow Flame', Return = Color.LIGHTGREY .. ATSW_PROTECTION_FROM .. Color.WHITE .. 'Onyxia\'s Shadow Flame'}, {Pattern = '(%d+) yard[s]*', Return = Color.LIGHTGREY .. ATSW_RADIUS .. Color.WHITE .. ' %d'}, {Pattern = 'stuns [%a%s%d]+ for (%d+) (%a+)', Return = Color.LIGHTGREY .. ATSW_STUN .. Color.WHITE .. '%d %s', Similar = 5}, {Pattern = 'companion', Return = ATSW_COMPANION}, {Pattern = 'Adds a toy', Return = ATSW_TOY}, {Pattern = 'Goblin Jumper Cables XL', Return = Color.WHITE .. '50%%' .. Color.LIGHTGREY .. ATSW_CHANCE_TO_RESURRECT, Similar = 'cables'}, {Pattern = 'Goblin Jumper Cables', Return = Color.WHITE .. '33%%' .. Color.LIGHTGREY .. ATSW_CHANCE_TO_RESURRECT, Similar = 'cables'}, {Pattern = 'Portable Wormhole Generator: ([%a%s%d%-]+)', Return = Color.LIGHTGREY .. ATSW_PORTAL_TO .. Color.WHITE .. '%s'}, {Pattern = 'Use: .+ transport[s]* .+ to ([%a%s]+)', Return = Color.LIGHTGREY .. ATSW_TELEPORT_TO .. Color.WHITE .. '%s'}, {Pattern = 'control.* mind .+ for (%d+) (%a+)', Return = Color.LIGHTGREY .. ATSW_MIND_CONTROL_FOR .. Color.WHITE .. '%d %s'}, {Pattern = 'detects.* stealth.* invisible', Return = Color.WHITE .. ATSW_STEALTH_INVISIBILITY_DETECTION}, {Pattern = 'World Enlarger', Return = Color.LIGHTGREY .. ATSW_REDUCE_YOUR .. Color.WHITE .. ATSW_SIZE}, {Pattern = 'Charge an enemy, knocking it silly for (%d+) seconds.', Return = Color.LIGHTGREY .. ATSW_INCAPACITATE .. Color.WHITE .. '%d sec.'}, {Pattern = 'Use: .+ protect.* (%d+) damage.* over.* (%d+) (%a+).', Return = Color.WHITE .. '%d' .. Color.LIGHTGREY .. ATSW_DAMAGE_ABSORB_FOR .. Color.WHITE .. '%d %s.'}, {Pattern = 'Gordok Ogre Suit', Return = Color.LIGHTGREY .. ATSW_DISGUISE .. Color.WHITE .. ATSW_GORDOK_OGRE}, {Pattern = 'Savory Deviate Delight', Return = Color.LIGHTGREY .. ATSW_DISGUISE .. Color.WHITE .. ATSW_PIRATE_OR_NINJA}, {Pattern = 'Flask of Petrification', Return = Color.WHITE .. ATSW_FLASK_OF_PETRIFICATION}, {Pattern = 'Grants (%d+)%% spell critical and (%d+) ([%a%s]+) for (.+).', Return = Color.WHITE .. '%d%%' .. Color.LIGHTGREY .. ATSW_CRIT .. ATSW_AND .. Color.WHITE .. '%d %s' .. Color.LIGHTGREY .. ATSW_FOR .. '%s'}, {Pattern = 'Increase[s]*.+ damage of a ([%a%s-]+) by (%d+) for (.+).', Return = Color.WHITE .. '%2$d' .. Color.LIGHTGREY .. ATSW_DAMAGE_FOR .. ' ' .. Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'Attach.* .+ to your ([%a%s-]+) that increase[s]* your ([%a%s]+) by (%d+).', Return = Color.WHITE .. '%3$d' .. Color.LIGHTGREY .. ' %2$s' .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'Attach.* .+ to your ([%a%s-]+) that deal[s]* damage every time you block with it.', Return = Color.LIGHTGREY .. ATSW_DAMAGE_ON_BLOCK_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'Attach.* .+ to your ([%a%s-]+), making it impossible to ([%a%s]+).', Return = Color.LIGHTGREY .. ATSW_IMMUNITY_TO .. Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'Removes existing [%a%s]+ and makes you immune to ([%a%s]+) for (.+).', Return = Color.LIGHTGREY .. ATSW_IMMUNITY_TO .. Color.WHITE .. '%s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%s'}, {Pattern = 'Attach.* [%a]+ to your ([%a%s]+) that increase[s]* your ([%a%s]+) slightly.', Return = Color.WHITE .. '+%2$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s', Similar = 3}, {Pattern = 'Attach.* a counterweight to a (.+) making it (%d+)%% faster', Return = Color.WHITE .. '+%2$d%%' .. Color.LIGHTGREY .. ATSW_ATTACK_SPEED_FOR .. Color.WHITE .. '%1$s'}, {Pattern = 'Use: .*increase the (%a+) .+ worn on the ([%a%s%,]+) by (%d+).+ level (%d+) and above.', Return = Color.WHITE .. '%3$d' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_FOR .. Color.WHITE .. '%2$s' .. Color.LIGHTGREY .. ATSW_OF_LEVEL .. Color.WHITE .. '%4$d' .. Color.LIGHTGREY .. ATSW_AND_ABOVE, Similar = 15}, {Pattern = 'Use: .*increase the (%a+) .+ worn on the ([%a%s%,]+) by (%d+) and (%a+) by (%d+).', Return = Color.WHITE .. '%3$d' .. Color.LIGHTGREY .. ' %1$s' .. ATSW_AND .. Color.WHITE .. '%5$d' .. Color.LIGHTGREY .. ' %4$s' .. ATSW_FOR .. Color.WHITE .. '%2$s', Similar = 15}, {Pattern = 'Use: Increase critical chance on [a%s]*([%a%s]+) by (%d+)%% for (.+).', Return = Color.WHITE .. '%2$d%%' .. Color.LIGHTGREY .. ATSW_CRIT_FOR .. ' ' .. Color.WHITE .. '%3$s' .. Color.LIGHTGREY .. ATSW_FOR .. Color.WHITE .. '%1$s'}, {Pattern = ': [%a]+s [%a%s%d]+ that [%a]*[%s]*([%a%s%d]+).', Return = StatWS, LastResort = true}, {Pattern = 'Use: Increases ([%a%s%d]+) by (%d+)%.', Return = Color.WHITE .. '+%2$d ' .. Color.LIGHTGREY .. '%1$s.', LastResort = true}, } for _, Item in pairs(EnchantPatterns) do Item.Pattern = string.lower(Item.Pattern) end for _, Item in pairs(PoisonPatterns) do Item.Pattern = string.lower(Item.Pattern) end for _, Item in pairs(AttributePatterns) do Item.Pattern = string.lower(Item.Pattern) end local function ReplaceWords(w) local r = '' if w == 'one' then r = 1 elseif w == 'two' then r = 2 elseif w == 'three' then r = 3 elseif w == 'four' then r = 4 elseif w == 'five' then r = 5 elseif w == 'six' then r = 6 elseif w == 'seven' then r = 7 elseif w == 'eight' then r = 8 elseif w == 'nine' then r = 9 end return r end local function GetAttributes(Recipe) if Recipe.Type == 'header' then return '' end if Recipe.Attributes then return Recipe.Attributes end ATSWRecipeItemTooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT') ATSW_TooltipSetSkill(ATSWRecipeItemTooltip, GetPositionFromGame(Recipe.Name)) local I, Text, TextRight = 0 local Return = '' local IsCloth local IsPoison repeat I = I + 1 Text = _G['ATSWRecipeItemTooltipTextLeft' .. I] TextRight = _G['ATSWRecipeItemTooltipTextRight' .. I] Text = Text and Text:GetText() TextRight = TextRight and TextRight:GetText() local Similar = false if TextRight == 'Cloth' then IsCloth = true end if Text then Text = string.lower(Text) if I == 1 and string.find(Text, 'poison') then IsPoison = true end end local cR, cG, cB = _G['ATSWRecipeItemTooltipTextLeft' .. I]:GetTextColor() cR = math.floor(cR + 0.5) cG = math.floor(cG + 0.5) cB = math.floor(cB + 0.5) if Text and string.sub(Text, 1, 1) ~= '\"' and not string.find(Text, '%(%d+%) ') then local Patterns, PatternFound if string.find(string.sub(Text, 1, 26), 'enchant[s]* ') then Patterns = EnchantPatterns elseif IsPoison then Patterns = PoisonPatterns else Patterns = AttributePatterns end for _, Item in pairs(Patterns) do if Item.Similar ~= Similar and not (Item.LastResort and PatternFound) then local PStart, PEnd, A, B, C, D, E, F = string.find(Text, Item.Pattern) if PStart and not (Item.Pattern == '(%d+) armor' and IsCloth) then PatternFound = true if Return ~= '' then Return = Return .. Color.WHITE .. ',|r ' end if Item.Return then local Minus = '' if Item.ReplaceWords == 1then A = ReplaceWords(A) end if string.sub(Text, 1, 1) == '-' then Minus = Color.WHITE .. '-|r' end Return = Return .. Minus .. string.format(Item.Return, A, B, C, D, E, F) .. '|r' else Return = Return .. Color.LIGHTGREY .. Text .. '|r' end if Item.Similar then Similar = Item.Similar end if string.find(Return, 'str') and string.find(Return, 'agi') and string.find(Return, 'sta') and string.find(Return, 'int') and string.find(Return, 'spi') then local _, _, str = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' str') local _, _, agi = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' agi') local _, _, sta = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' sta') local _, _, int = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' int') local _, _, spi = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' spi') if str == agi and str == sta and str == int and str == spi then local Start = string.find(Return, '%d+' .. Color.LIGHTGREY .. ' str') local _, End, Amount = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' spi.') local StrLeft = string.sub(Return, 1, Start-1) local StrRight = string.sub(Return, End+1, string.len(Return)) Return = StrLeft .. Amount .. Color.LIGHTGREY .. ' all attributes|r' .. StrRight end end if string.find(Return, 'arcane resist') and string.find(Return, 'fire resist') and string.find(Return, 'nature resist') and string.find(Return, 'frost resist') and string.find(Return, 'shadow resist') then local Start = string.find(Return, '%d+' .. Color.LIGHTGREY .. ' arcane') local _, End, Amount = string.find(Return, '(%d+)' .. Color.LIGHTGREY .. ' shadow resist') local StrLeft = string.sub(Return, 1, Start-1) local StrRight = string.sub(Return, End+1, string.len(Return)) Return = StrLeft .. Amount .. Color.LIGHTGREY .. ' all resist' .. StrRight end if string.find(string.sub(Text, 1, 26), 'enchant[s]* ') then break end if (cR == 1 and cG == 1 and cB == 1) or string.len(Text) - (PEnd-PStart+1) < 11 then -- Optimization: detection of full pattern match break end if PStart == 1 and PEnd == string.len(Text) then break end end end end if Return == '' and I == ATSWRecipeItemTooltip:NumLines() and I > 1 and not string.find(Text, 'races: ') and not string.find(Text, 'classes: ') and not string.find(Text, 'tools: ') and not string.find(Text, 'reagents: ') then if not (cR == 1 and cG == 1 and cB == 1) then local Pos = string.find(Text, ': ') if not Pos then Pos = 0 else Pos = Pos + 2 end Text = string.sub(Text, Pos, string.len(Text)) if string.len(Text) > 47 then Text = string.sub(Text, 1, 47) .. '...' end Return = Color.LIGHTGREY .. Text .. '|r' end end end until not Text ATSWRecipeItemTooltip:SetOwner(this, '') Recipe.Attributes = Return return Return end local function RemoveColorTags(Text) local Result = Text while string.find(Result, '|c%x%x%x%x%x%x%x%x') do Result = string.gsub(Result, '|c%x%x%x%x%x%x%x%x', '') end Result = string.gsub(Result, '|r', '') return Result end function ATSW_UpdateRecipes() local ScrollOffset = ScrollOffset() / ATSW_TRADESKILL_HEIGHT local TextureWidth = ATSWRecipe1Texture:GetWidth() local _, _, _, TextureLeft = ATSWRecipe1Texture:GetPoint(1) local FrameWidth = ATSWListScrollFrame:GetWidth() for I = 1, ATSW_RECIPES_DISPLAYED do local Button = _G['ATSWRecipe' .. I] local Index = I + ScrollOffset local Exist = Index <= RecipesSortedSize() local R, Possible local Text, SubText = '' SetVisible(Button, false) -- Needed for tooltip update if scrolled SetVisible(Button, Exist) if Exist then R = RecipeSorted(Index) if R.Type == 'header' then R.Texture = GetCategoryTexture(IsExpanded(R.Name)) else if not (R.Name and R.Texture and (R.Link or IsBeastTraining())) then RecipesDataIsComplete = RecipesDataIsComplete + 2 RecipesDataTries = RecipesDataIsComplete end end local ButtonText = _G['ATSWRecipe' .. I .. 'Text'] local ButtonSubText = _G['ATSWRecipe' .. I .. 'SubText'] local ButtonTexture = _G['ATSWRecipe' .. I .. 'Texture'] local QualityTexture = _G['ATSWRecipe' .. I .. 'QualityTexture'] local ButtonHighlight = _G['ATSWRecipe' .. I .. 'Highlight'] local point, relativeTo, relativePoint, _, yOfs = Button:GetPoint(2) -- LEFT, ATSWFrame, LEFT local BorderSize = 0.071 local cR, cG, cB = GetLinkColorRGB(R.Link) local PossibleAmount = 0 local PossibleTotal = 0 local TypeColor = ATSWTypeColor[R.Type] local XOffset = 0 local HighlightSize = 0.2 local TP = R.TrainingCost if R.Type == 'header' then HighlightSize = 0 BorderSize = 0 else XOffset = 20 if not IsBeastTraining() then PossibleAmount = ATSW_HowManyItemsArePossibleToCreate(R.Name, ATSW_Options.ConsiderBank and ATSW_POSSIBLE_BANK, ATSW_Options.ConsiderAlts and ATSW_POSSIBLE_ALTS, ATSW_Options.ConsiderMerchants and ATSW_POSSIBLE_MERCHANT) PossibleTotal = ATSW_HowManyItemsArePossibleToCreateWithConsidering (R.Name) if ATSW_Options.Unified then Possible = PossibleAmount > 0 and PossibleAmount elseif PossibleTotal > 0 then Possible = PossibleAmount .. '/' .. PossibleTotal end if Possible then Possible = ' |cffB0B0B0[' .. Possible .. ']|r' end end end if ATSW_ShowAttributes() and R.Type ~= 'header' then Text = GetAttributes(R) Button:SetFont('Fonts\\FRIZQT__.ttf', 10) else Text = R.Name .. SubNameToString(R.SubName) Button:SetFont('Fonts\\FRIZQT__.ttf', 12) end Text = Text .. (Possible or '') if TP and TP > 0 then SubText = format(TRAINER_LIST_TP, TP) if R.Type == 'used' then SubText = Color.GREY .. SubText .. '|r' end else SubText = ConvertCooldown(R.Cooldown) or '' end Button:SetText(RemoveColorTags(Text)) local TextWidth = Button:GetTextWidth() Button: SetText (Text) ButtonSubText: SetText (SubText) Button: SetTextColor (TypeColor.R, TypeColor.G, TypeColor.B) Button: SetPoint (point, relativeTo:GetName(), relativePoint, Button.XOffset + XOffset, yOfs) Button: SetWidth (math.min(TextureLeft + TextureWidth + TextWidth, FrameWidth - XOffset) + 4) if TextWidth > FrameWidth - 40 then TextWidth = FrameWidth - 40 else TextWidth = 0 end ButtonText: SetWidth (TextWidth) ButtonTexture: SetTexture (R.Texture) ButtonTexture: SetTexCoord (0+BorderSize, 1-BorderSize, 0+BorderSize, 1-BorderSize) ButtonHighlight: SetTexCoord (0+HighlightSize, 1-HighlightSize, 0+HighlightSize, 1-HighlightSize) ButtonHighlight: SetDesaturated (R.Type ~= 'header') QualityTexture: SetVertexColor (cR, cG, cB) SetVisible(QualityTexture, cR and cG and cB and not (cR == 1 and cG == 1 and cB == 1)) if R.Type == 'header' then Button:RegisterForClicks('LeftButtonUp') else Button:RegisterForClicks('LeftButtonUp', 'RightButtonUp') end end Button.Name = R and (R.Name .. (R.SubName and R.SubName ~= '' and (' (' .. R.SubName .. ')') or '')) Button.SubName = R and R.SubName Button.Type = R and R.Type Button.Link = R and R.Link Button.Position = R and R.Position Button.Possible = Possible Button.Text = Text end ATSW_SetHighlight(RecipeSelected()) ATSW_UpdateListScroll() end function ATSW_HideRecipes() for I = 1, ATSW_RECIPES_DISPLAYED do _G['ATSWRecipe' .. I]:Hide() end end function ATSW_UpdateTasks() local Offset = FauxScrollFrame_GetOffset(ATSWTaskScrollFrame) for I = 1, ATSW_TASKS_DISPLAYED do local Button = _G['ATSWTask' .. I] local DeleteButton = _G['ATSWTask' .. I .. 'DeleteButton'] local Name, Type, Amount, Texture, Link, Cooldown, Pos, Text, TypeColor, Possible local Index = I + Offset local Exist = Task(Index) local tR, tG, tB = 1, 1, 1 SetVisible(Button, false) -- It is needed for tooltip update if button text is changed SetVisible(Button, Exist) SetVisible(DeleteButton, Exist) if Exist then Name = Exist.Name Amount = Exist.Amount Texture = Exist.Texture Time = GetCraftingTime(Name, Amount) Pos = GetRecipePosition(Name) if Pos then Type = Recipe(Pos).Type or 'trivial' Link = Recipe(Pos).Link Cooldown = Recipe(Pos).Cooldown TypeColor = ATSWTypeColor[Type] tR, tG, tB = TypeColor.R, TypeColor.G, TypeColor.B end local ItemTexture = _G['ATSWTask' .. I .. 'Texture'] local QualityTexture = _G['ATSWTask' .. I .. 'QualityTexture'] local TimeCost = _G['ATSWTask' .. I .. 'TimeCost'] local R, G, B = GetLinkColorRGB(Link) local t_Width = ItemTexture:GetWidth() local _, _, _, t_xOfs, _ = ItemTexture:GetPoint(0) local NameIsProcessing = Processing.Active and (Name == Processing.Recipe) local PossibleAmount = ATSW_HowManyItemsArePossibleToCreateWithConsidering(Name) if PossibleAmount > 0 then PossibleAmount = '|cff10C010' .. PossibleAmount .. '|r' end Text = Name .. ' |cffB0B0B0[' .. Amount .. '/' .. PossibleAmount .. '|cffB0B0B0]|r' Button.R = tR Button.G = tG Button.B = tB Button.TaskIndex = Index DeleteButton.TaskIndex = Index Button: SetText (Text .. (ConvertCooldown(Cooldown) or '')) Button: SetTextColor (tR, tG, tB) ItemTexture: SetTexture (Texture) Button: SetWidth (Button:GetTextWidth() + t_xOfs + t_Width + 4) QualityTexture: SetVertexColor(R, G, B) TimeCost: SetText (ShortFormatCooldown(Time, true)) SetVisible(DeleteButton, not NameIsProcessing) SetVisible(TimeCost, not NameIsProcessing) SetVisible(QualityTexture, R and G and B and not (R == 1 and G == 1 and B == 1)) Button:SetAlpha(1) end Button.Name = Name Button.Text = Text Button.Position = Pos Button.TaskIndex = Exist and Index end SetEnabled (ATSWReagentsButton, NecessaryReagentsSize() ~= 0) SetVisible (ATSWWeb, TasksSize() == 0) ATSW_UpdateCreateButton() ATSW_UpdateTaskListScroll() end function ATSW_HideTasks() for I = 1, ATSW_TASKS_DISPLAYED do _G['ATSWTask' .. I]:Hide() end end function ATSW_ShowSort() SetSortCheckboxes(SortBy()) end function ATSW_ShowSearch() ATSW_BlockSearchTextChanged = true ATSWSearchBox:SetText(SearchString()) SetVisible(ATSWSearchLabel, SearchString() == '') SetVisible(ATSWSearchIcon, SearchString() == '') if SearchString() == '' then ATSWSearchBox:ClearFocus() end ATSW_BlockSearchTextChanged = false end function ATSW_ShowDropDown() UIDropDownMenu_Initialize(ATSWSubClassDropDown, ATSWSubClassDropDown_Initialize) UIDropDownMenu_Initialize(ATSWInvSlotDropDown, ATSWInvSlotDropDown_Initialize) end function ATSW_ShowSelection() local function FirstCraft() local Name, SubName = GetCraftNameType(GetFirstCraft()) if SubName and SubName ~= '' then Name = Name .. ' (' .. SubName .. ')' end return Name end ATSW_SelectRecipe(RecipeSelected() or FirstCraft()) end function ATSW_ShowAmount() local Number = ATSW_Amount[realm][player][Profession()] if Number == 0 then ATSWAmountBox:SetText(ATSW_ALL) else ATSWAmountBox:SetNumber(Number) end end function ATSW_ShowTrainingPoints() if IsBeastTraining() then local Total, Spent = GetPetTrainingPoints() ATSWTrainingPointsText:SetText(math.max(Total - Spent, 0)) ATSWTrainingPointsFrame:SetWidth(ATSWTrainingPointsLabel:GetWidth()+ATSWTrainingPointsText:GetWidth()+20) end end function ATSW_ShowAmountMenu (Visible) SetVisible(ATSWAmountText, Visible) SetVisible(ATSWDecrementButton, Visible) SetVisible(ATSWAmountBox, Visible) SetVisible(ATSWIncrementButton, Visible) end function ATSW_ShowDropDownMenu (Visible) ATSWSubClassDropDown: Hide() -- Needed for Dropdown list disappearance if changing professions ATSWInvSlotDropDown: Hide() SetVisible(ATSWSubClassDropDown, Visible) SetVisible(ATSWInvSlotDropDown, Visible) end function ATSW_ShowReagentsButton (Visible) SetVisible(ATSWReagentsButton, Visible) if not Visible then ATSWReagentsFrame:Hide() end end function ATSW_ShowTaskButton (Visible) SetVisible(ATSWTaskButton, Visible) end function ATSW_ShowTrainingPointsMenu (Visible) SetVisible(ATSWTrainingPointsFrame, Visible) end function ATSW_SelectRecipe(Name, Type, ExactPosition) if Name then if Type == 'header' then ATSW_SwitchCategory(Name) else -- Store selected recipe name ATSW_SelectedRecipe[realm][player][Profession()] = Name ATSW_ShowRecipe(Name, ExactPosition) end else ATSW_SelectedRecipe[realm][player][Profession()] = nil ATSW_HideRecipe() end ATSW_UpdateCreateButton() end function ATSW_SwitchCategory(Name) if ATSW_IsContracted(Name) then table.remove(Contracted(), ATSW_GetContractedPos(Name)) else table.insert(Contracted(), Name) end ATSW_GetRecipesSorted(true) end function ATSW_ShowRecipe(Name, ExactPosition) local GamePosition -- When invoked directly from a recipe row, its Position identifies the -- exact cached recipe. Verify the output item against the current live -- tradeskill list before using it; otherwise use the robust resolver. if ExactPosition then local R = Recipe(ExactPosition) local ExpectedID = R and R.Link and ATSW_LinkToID(R.Link) local LiveLink = GetCraftLink(ExactPosition) local LiveID = LiveLink and ATSW_LinkToID(LiveLink) if ExpectedID and LiveID and ExpectedID == LiveID then GamePosition = ExactPosition end end GamePosition = GamePosition or GetPositionFromGame(Name) if GamePosition then SelectCraftItem(GamePosition) end ATSWRecipeName:Show() ATSWRecipeIcon:Show() local Recipe = Recipe(GetRecipePosition(Name)) -- Name ATSWRecipeName:SetText(Recipe.Name) -- Icon local Quality = LinkToQuality(Recipe.Link) SetVisible(ATSWRecipeIconQualityTexture, Quality > 2) if Recipe.Texture then ATSWRecipeIcon:SetNormalTexture(Recipe.Texture) ATSWRecipeIcon.Link = Recipe.Link else ATSWRecipeIcon:Hide() end -- Quality border if Quality > 2 then local RColor = QualityColor[Quality] ATSWRecipeIconQualityTexture:SetVertexColor(RColor.R, RColor.G, RColor.B) else ATSWRecipeIconQualityTexture:SetVertexColor(1, 1, 1) end -- Amount of production local Min = Recipe.ProductMin local Max = Recipe.ProductMax if Max > 1 then if Min == Max then ATSWRecipeIconAmount:SetText(Min) else ATSWRecipeIconAmount:SetText(Min .. '-' .. Max) end else ATSWRecipeIconAmount:SetText('') end -- Cooldown local Cooldown = ConvertCooldown(Recipe.Cooldown, true) SetVisible(ATSWRecipeCooldown, Cooldown) ATSWRecipeCooldown.Cooldown = Recipe.Cooldown ATSWRecipeCooldown:SetText((Cooldown and COOLDOWN_REMAINING .. ' ' .. Cooldown) or '') local function ToolStringToTable(...) local Table = {} for I = 1, arg.n, 2 do table.insert(Table, { Name = arg[I], Available = arg[I+1] }) end return Table end local Tools, Required local BeastTraining = IsBeastTraining() if BeastTraining then -- Requirements local Button = _G['ATSWTool' .. 1] local ButtonText = _G['ATSWTool' .. 1 .. 'Text'] local ButtonTexture = _G['ATSWTool' .. 1 .. 'Texture'] local ReqLevel = Recipe.RequiredLevel Required = BuildColoredListString(GetCraftRequirements(GamePosition)) SetVisible(ATSWToolsLabel, Required or ReqLevel and ReqLevel > 0) SetVisible(Button, Required or ReqLevel and ReqLevel > 0) ATSWToolsLabel:SetText(REQUIRES_LABEL .. ' ') if Required then Button: SetText(Required) else if ReqLevel and ReqLevel > 0 then if UnitLevel('pet') >= ReqLevel then Button:SetText(format(RemoveEscapeCharacters(TRAINER_PET_LEVEL), ReqLevel)) else Button:SetText(format(TRAINER_PET_LEVEL_RED, ReqLevel)) end else Button:SetText('') end end ButtonTexture: SetWidth(Texture and 16 or -1) Button: SetWidth(Button:GetTextWidth() + ButtonTexture:GetWidth()) ButtonText: SetTextColor(0, 0, 0) SetEnabled(Button, false) else -- Tools Tools = ToolStringToTable(GetCraftRequirements(GamePosition)) SetVisible(ATSWToolsLabel, table.getn(Tools) > 0) ATSWToolsLabel:SetText(ATSW_TOOLS) for I = 1, ATSW_TOOLS_DISPLAYED do local Button = _G['ATSWTool' .. I] local ButtonText = _G['ATSWTool' .. I .. 'Text'] local ButtonTexture = _G['ATSWTool' .. I .. 'Texture'] local Exist = table.getn(Tools) >= I local R = 0 local Texture, Name, Available local Position, Link SetVisible(Button, Exist) if Exist then Name = Tools[I].Name Available = Tools[I].Available local ID = ToolID[Name] if ID then Texture = GetItemTexture(ID) end R = (Available or not Texture) and 0 or 1 Position = GetRecipePosition(Name) Link = Position and GetCraftLink(Position) SetEnabled(Button, Position) ButtonTexture: SetWidth(Texture and 16 or -1) ButtonTexture: SetTexture(Texture) Button: SetText(Name .. (I < table.getn(Tools) and ',' or '')) ButtonText: SetTextColor(R, 0, 0) Button: SetWidth(Button:GetTextWidth() + ButtonTexture:GetWidth()) end Button.R = R Button.Name = Name Button.Link = Link Button.Position = Position end end SetVisible(ATSWCostText, BeastTraining) SetVisible(ATSWCraftDescription, BeastTraining) if BeastTraining then -- Training cost if Recipe.TrainingCost > 0 then local Total, Spent = GetPetTrainingPoints() local Usable = math.max(Total - Spent, 0) if Usable >= Recipe.TrainingCost then ATSWCostText:SetText(Recipe.TrainingCost .. ' ' .. TRAINING_POINTS_LABEL) else ATSWCostText:SetText(RED_FONT_COLOR_CODE .. Recipe.TrainingCost .. FONT_COLOR_CODE_CLOSE .. ' ' .. TRAINING_POINTS_LABEL) end end SetVisible(ATSWReagentsLabel, Recipe.TrainingCost > 0) SetVisible(ATSWCostText, Recipe.TrainingCost > 0) ATSWReagentsLabel:SetText(COSTS_LABEL .. ' ') -- Description local Description = GetCraftDescription(GamePosition) SetVisible(ATSWCraftDescription, Description) if Description then ATSWCraftDescription:SetText(Description) end else -- Reagents local NumReagents = Recipe.ReagentsSize SetVisible(ATSWReagentsLabel, NumReagents > 0) ATSWReagentsLabel:SetText(SPELL_REAGENTS) for I = 1, MAX_TRADE_SKILL_REAGENTS do local Button = _G['ATSWReagent' .. I] local ButtonQuality = _G['ATSWReagent' .. I .. 'QualityTexture'] local Exists = I <= NumReagents local RName, RTexture, RAmount, RPlayerAmount, RLink, RTotalAmount, Quality, RColor, R, G, B, A, Craftable, Position SetVisible(Button, false) -- It is needed for tooltip update if schematic is changed SetVisible(Button, Exists) if Exists then local ButtonTexture = _G['ATSWReagent' .. I .. 'Texture'] local ButtonAmount = _G['ATSWReagent' .. I .. 'Amount'] local ButtonTitle = _G['ATSWReagent' .. I .. 'Title'] local Pos = Recipe.Position RName, RTexture, RAmount, RPlayerAmount, RLink = GetReagentData(GamePosition, I) if not (RName and RTexture and RLink) then ReagentsDataIsComplete = false end if RLink then Quality = LinkToQuality(RLink) if Quality > 2 then RColor = QualityColor[Quality] R, G, B = RColor.R, RColor.G, RColor.B else R, G, B = 1, 1, 1 end SetVisible(ButtonQuality, Quality > 2) if RPlayerAmount and RAmount then A = 0.5 + (RPlayerAmount >= RAmount and 0.45 or 0) RTotalAmount = RPlayerAmount .. ' /' .. RAmount else A = 0.5 RTotalAmount = '' end -- ButtonTitle:SetTextColor(R, G, B, A) -- Colored title ButtonTitle:SetTextColor(0, 0, 0, 1) -- Black title end Position = GetRecipePosition(RName) ButtonTexture: SetTexture (RTexture) ButtonTexture: SetVertexColor (A, A, A, A) ButtonAmount: SetText (RTotalAmount) ButtonTitle: SetText (RName or '') ButtonQuality: SetVertexColor (R, G, B, 0.5) end Button.Name = RName Button.Link = RLink Button.R = R Button.G = G Button.B = B Button.Position = Position Button.RecipeIndex = Recipe.Position Button.ReagentIndex = I end end ATSW_SetHighlight(Name) -- Remove previous recipe if it is selected for I, V in pairs(PreviousRecipes()) do if V == Recipe.Name then table.remove(PreviousRecipes(), I) break end end -- Previous recipes button SetVisible(ATSWPreviousItemButton, PreviousRecipesSize() > 0) -- Task button ATSWTaskButton:Enable() -- Amount control ATSWIncrementButton:Enable() ATSWDecrementButton:Enable() end function ATSW_HideRecipe() local Visible = false ATSW_SetHighlight(nil) SetVisible(ATSWRecipeName, Visible) SetVisible(ATSWRecipeIcon, Visible) SetVisible(ATSWRecipeCooldown, Visible) SetVisible(ATSWToolsLabel, Visible) SetVisible(ATSWReagentsLabel, Visible) SetVisible(ATSWCraftDescription, Visible) for I = 1, ATSW_RECIPES_DISPLAYED do _G['ATSWRecipe' .. I]:UnlockHighlight() end for I = 1, ATSW_TOOLS_DISPLAYED do SetVisible(_G['ATSWTool' .. I], Visible) end for I = 1, MAX_TRADE_SKILL_REAGENTS do SetVisible(_G['ATSWReagent' .. I], Visible) end -- Task button ATSWTaskButton:Disable() -- Amount control ATSWIncrementButton:Disable() ATSWDecrementButton:Disable() end function ATSW_SetHighlight(Name) if HighlightButton then HighlightButton:UnlockHighlight() HighlightButton = nil end if Name then for I = 1, ATSW_RECIPES_DISPLAYED do local Button = _G['ATSWRecipe' .. I] if Button.Name == Name then local R, G, B = Button:GetTextColor() ATSWHighlight:SetPoint('TOP', Button) ATSWHighlightTexture:SetVertexColor(R, G, B, 0.8) HighlightButton = Button end end end if HighlightButton then HighlightButton:LockHighlight() end SetVisible(ATSWHighlight, HighlightButton) end function ATSW_UpdateCreateButton() local Amount = ATSWAmountBox:GetNumber() local TaskPossible, Possible = 0 local IncludeTasks = true local Position = GetPositionFromGame(RecipeSelected()) if RecipesSize() > 0 then Possible = not Processing.Active if not Processing.Active then local Type if Position then _, _, Type = GetCraftNameType(Position) end if IsBeastTraining() then if UnitName('pet') then local Total, Spent = GetPetTrainingPoints() local _, _, _, _, _, TrainingCost, ReqLevel = GetCraftInformation(Position) Possible = Type ~= 'used' and (math.max(Total - Spent, 0) >= TrainingCost or TrainingCost == 0) if ReqLevel and ReqLevel > 0 then if UnitLevel('pet') < ReqLevel then Possible = false end end else Possible = false end else if not Amount or Amount == 0 then Amount = 1 end Possible = (Position and not GetCraftCooldown(Position)) and (Type ~= 'used') and ATSW_HowManyItemsArePossibleToCreate(RecipeSelected()) >= Amount for I = 1, TasksSize() do Pos = GetPositionFromGame(Task(I).Name) if Pos and not GetCraftCooldown(Pos) then if ATSW_HowManyItemsArePossibleToCreate(Task(I).Name, ATSW_POSSIBLE_NOTASK) > 0 then Possible = not (Processing.Active and Processing.Recipe == Task(I).Name) break end end end end end end if not Processing.Active then SetEnabled(ATSWCreateButton, Possible) end local Name = _G[GetCraftButtonToken()] local Text if Name == 'Enchant' then if Position and IsEnchant(Position) then Text = ATSW_ENCHANT else Text = CREATE end elseif Name == 'Create' or Name == 'Use' then Text = CREATE else Text = Name end ATSWCreateButton:SetText(Text or Name or '') ATSWCreateButton:SetWidth(math.max(ATSWCreateButton:GetTextWidth() + 25, 82)) end function ATSW_Craft(Name, Amount) local Possible local Index = GetPositionFromGame(Name) if TradeSkill then Possible = ATSW_HowManyItemsArePossibleToCreate(Name, ATSW_POSSIBLE_NOTASK) else Possible = 1 end Processing.Profession = Profession() Processing.Recipe = Name Processing.Texture = GetCraftTexture(Index) Processing.Amount = math.min(Amount, Possible) Processing.Delay = GetTime() --Compatibility with otravi Cast Bar oCBIcon = Processing.Texture oCBName = Name ATSWFrame:RegisterEvent('SPELLCAST_START') ATSW_PushCreateButton() Delay(function () ATSW_PushCreateButton(Processing.Active) end, GetLatency()*4) Craft(Index, Amount) end local function GetReagents(Name, Amount, Link, Texture, Table) local Table = Table or {} local Amount = Amount or 1 local Pos = GetPositionFromGame(Name) if not Pos then for I = 1, table.getn(Table) do if Table[I].Name == Name then Table[I].Amount = Table[I].Amount + Amount return Table end end table.insert(Table, { Name = Name, Amount = Amount, Link = Link, Texture = Texture }) return Table end for I = 1, GetReagentCount(Pos) do local RName, RTexture, RAmount, _, RLink = GetReagentData(Pos, I) local Min = GetCraftMinMax(GetPositionFromGame(Name)) local Amount = math.ceil(Amount / Min) Table = GetReagents(RName, (RAmount * Amount), RLink, RTexture, Table) end return Table end local function GetSayTooltipString() if ChatFrameEditBox:IsVisible() or WIM_EditBoxInFocus then local ChatType, ChatNumber local SayTarget = '' if WIM_EditBoxInFocus ~= nil then ChatType = 'WHISPER' ChatNumber = WIM_EditBoxInFocus:GetParent().theUser SayTarget = ATSW_TOOLTIP_TO .. WIM_UserWithClassColor(ChatNumber) else ChatType, ChatNumber = ChatFrameEditBox.chatType if ChatType == 'WHISPER' then ChatNumber = ChatFrameEditBox.tellTarget elseif ChatType == 'CHANNEL' then ChatNumber = ChatFrameEditBox.channelTarget local ChatID, ChatName = GetChannelName(ChatNumber) SayTarget = ATSW_TOOLTIP_TO .. '[' .. ChatID .. '. ' .. ChatName ..']' elseif ChatType == 'YELL' then SayTarget = ATSW_TOOLTIP_TO .. ATSW_TOOLTIP_YELL elseif ChatType == 'PARTY' then SayTarget = ATSW_TOOLTIP_TO .. ATSW_TOOLTIP_PARTY elseif ChatType == 'GUILD' then SayTarget = ATSW_TOOLTIP_TO .. ATSW_TOOLTIP_GUILD elseif ChatType == 'RAID' then SayTarget = ATSW_TOOLTIP_TO .. ATSW_TOOLTIP_RAID end end return ATSW_TOOLTIP_SAYREAGENTS .. SayTarget, ChatType, ChatNumber else return '' end end local Lines = {} local Message = '' local TooltipString, ChatType, ChatNumber StaticPopupDialogs['SAYREAGENTS'] = { text = '', button1 = TEXT(YES), button2 = TEXT(NO), OnAccept = function() SendChatMessage(Message, ChatType, nil, ChatNumber) for L = 1, table.getn(Lines) do SendChatMessage(Lines[L], ChatType, nil, ChatNumber) end end, timeout = 0, preferredIndex = 2, showAlert = 1, hideOnEscape = 1 } function ATSW_SayReagents(Position) TooltipString, ChatType, ChatNumber = GetSayTooltipString() local LineStart local R = Recipe(Position) if R.Link then if IsEnchant(Position) then LineStart = '' else LineStart = ATSW_REAGENTLIST1 .. ' ' end local Reagents = GetReagents(R.Name) local S = string.format(TooltipString, '^.*|cffffffff?(%s*)') .. '|r?\n\n' .. LineStart .. R.Link .. ' ' .. ATSW_REAGENTLIST2 Lines = {} for L = 1, table.getn(Reagents) do local Line = Reagents[L].Name if Reagents[L].Amount > 1 then Line = Line .. ' (' .. Reagents[L].Amount .. ')' end S = S .. '\n' .. Line table.insert(Lines, Line) end StaticPopupDialogs['SAYREAGENTS'].text = S .. '\n' Message = LineStart .. R.Link .. ' ' .. ATSW_REAGENTLIST2 StaticPopup_Show('SAYREAGENTS') end end function ATSW_HowManyItemsArePossibleToCreateWithConsidering(Name) return ATSW_HowManyItemsArePossibleToCreate( Name, ATSW_Options.ConsiderBank and ATSW_POSSIBLE_BANK, ATSW_Options.ConsiderAlts and ATSW_POSSIBLE_ALTS, ATSW_Options.ConsiderMerchants and ATSW_POSSIBLE_MERCHANT, ATSW_POSSIBLE_NOTASK ) end function ATSW_HowManyItemsArePossibleToCreate(Name, ...) local Pos = GetPositionFromGame(Name) if Pos then local I = 0 local Possible, PossibleAmountTotal local MaxReagents = GetReagentCount(Pos) if MaxReagents > 0 then local function Param(P) for I = 1, arg.n do if arg[I] == P then return I end end return false end local function SetMinPossible(Amount) Amount = math.floor(Amount) if not PossibleAmountTotal or Amount < PossibleAmountTotal then PossibleAmountTotal = Amount end if PossibleAmountTotal < 0 then PossibleAmountTotal = 0 end end repeat I = I + 1 local RName, _, RAmount, RPlayerAmount = GetReagentData(Pos, I) local TotalAmount = RPlayerAmount if Param(ATSW_POSSIBLE_BANK) then TotalAmount = TotalAmount + ATSW_GetBankAmount(RName) end if Param(ATSW_POSSIBLE_ALTS) then TotalAmount = TotalAmount + ATSW_GetAltsAmount(RName) end if Param(ATSW_POSSIBLE_MERCHANT) then if ATSW_IsInMerchant(RName) and TotalAmount < RAmount then TotalAmount = math.max(PossibleAmountTotal and PossibleAmountTotal * RAmount or 0, RAmount) end end SetMinPossible(TotalAmount / RAmount) Possible = PossibleAmountTotal > 0 until I == MaxReagents or not Possible if Possible and not Param(ATSW_POSSIBLE_NOTASK) then local TaskExist = DoTaskExist(Name) if TaskExist then SetMinPossible(PossibleAmountTotal - Task(TaskExist).Amount) end end return PossibleAmountTotal end end return 0 end function ATSW_SetNecessary(Name, Amount, Link, Texture, NecessaryReagentMode) if Name and Amount and Amount > 0 then local NPos for I = 1, NecessaryReagentsSize() do if NecessaryReagent(I).Name == Name then NPos = I end end if NPos then if NecessaryReagentMode == ATSW_NECESSARY_REAGENT_DELETE then if NecessaryReagent(NPos).Amount > Amount then NecessaryReagent(NPos).Amount = NecessaryReagent(NPos).Amount - Amount else table.remove(NecessaryReagents(), NPos) end elseif (NecessaryReagentMode == ATSW_NECESSARY_REAGENT_ADD) or not NecessaryReagentMode then NecessaryReagent(NPos).Amount = NecessaryReagent(NPos).Amount + Amount end elseif (NecessaryReagentMode == ATSW_NECESSARY_REAGENT_ADD) or not NecessaryReagentMode then table.insert(NecessaryReagents(), { Name = Name, Texture = Texture, Amount = Amount, Link = Link }) end if ATSWReagentsFrame:IsVisible() then ATSW_UpdateNecessaryReagents() end if MerchantFrame:IsVisible() then ATSW_SetBuyFrameVisible() end if NecessaryReagentsSize() > 0 then if ATSWShoppingListFrame:IsVisible() then ATSW_UpdateAuctionList() else ATSW_ShowAuctionShoppingList() end else ATSWShoppingListFrame:Hide() end end end -- Task functions local function AmountInTasksIndirect(Name, Prof) local Amount = 0 for I = 1, TasksSize(Prof) do local Pos = GetRecipePosition(Task(I, Prof).Name, Prof) local TaskAmount = Task(I, Prof).Amount for J = 1, Recipe(Pos, Prof).ReagentsSize do local R = Recipe(Pos, Prof).Reagents[J] if R.Name == Name then Amount = Amount + R.Amount * TaskAmount break end end end return Amount end local function AmountInTasksDirect(Name, Prof) local Pos = DoTaskExist(Name, Prof) if Pos then return Task(Pos, Prof).Amount else return 0 end end local function AmountInTasks(Name, Prof) return AmountInTasksIndirect(Name, Prof) - AmountInTasksDirect(Name, Prof) end function ATSW_AddTask(Name, Amount, Link, Texture, Recursive, NecessaryReagentMode, Insert, Prof) if Name then if Prof == nil then Prof = Profession() end local Pos = GetRecipePosition(Name, Prof) local function Possession(Name) local Bags, Bank, OtherCharacters Bags = ATSW_GetBagsAmount(Name) Bank = ATSW_Options.ConsiderBank and ATSW_GetBankAmount(Name) or 0 OtherCharacters = ATSW_Options.ConsiderAlts and ATSW_GetAltsAmount(Name) or 0 return Bags + Bank + OtherCharacters end if Pos then if Amount and Amount > 0 then if not NecessaryReagentMode then local TPos = DoTaskExist(Name) if TPos then Task(TPos).Amount = Task(TPos).Amount + Amount else local InsertPos = Insert and DoTaskExist(Insert) or TasksSize() + 1 table.insert(Tasks(), InsertPos, { Name = Name, Amount = Amount, Texture = GetCraftTexture(Pos) }) end end for I = 1, Recipe(Pos, Prof).ReagentsSize do local R = Recipe(Pos, Prof).Reagents[I] local Merchant, Required, InsertBefore = 1, 0 Merchant = ATSW_Options.ConsiderMerchants and ATSW_Merchant[R.Name] and 0 or 1 if GetRecipePosition(R.Name, Prof) then local Min = GetCraftMinMax(GetRecipePosition(R.Name, Prof)) Required = math.ceil(((AmountInTasksIndirect(R.Name, Prof) - (AmountInTasksDirect(R.Name, Prof) * Min) - math.ceil(Possession(R.Name) / Min))) / Min) * Merchant if AmountInTasksDirect(Name, Prof) > 0 and AmountInTasksDirect(R.Name, Prof) == 0 then InsertBefore = Name end else Required = R.Amount * Amount end if Required < 0 then NecessaryReagentMode = ATSW_NECESSARY_REAGENT_ADD Required = Amount end ATSW_AddTask(R.Name, Required, R.Link, R.Texture, true, NecessaryReagentMode, InsertBefore or Insert , Prof) end if not Recursive then ATSW_UpdateRecipes() ATSW_UpdateTasks() end end else ATSW_SetNecessary(Name, Amount, Link, Texture, NecessaryReagentMode) end end end function ATSW_DeleteTask(Name, Amount, Prof) for I = 1, TasksSize(Prof) do T = Task(I, Prof) if T.Name == Name then -- Get removing amount local Removed = Amount or T.Amount -- Remove task if not Amount or T.Amount <= Amount then table.remove(Tasks(Prof), I) else T.Amount = T.Amount - Amount end -- Remove necessary item ATSW_AddTask(Name, Removed, nil, nil, nil, ATSW_NECESSARY_REAGENT_DELETE, nil, Prof) if NoTasks() then ResetNecessaries() end -- Update lists if not (Prof and Prof ~= Profession()) then ATSW_UpdateRecipes() ATSW_UpdateTasks() end break end end end -- Progress bar functions function ATSW_InitializeProgressBar(StartTime, EndTime) ATSWProgressBar:SetMinMaxValues(StartTime, EndTime) ATSWProgressBar: SetAlpha(1) ATSWProgressBarGlow:SetAlpha(0) ATSWProgressBar.StartTime = StartTime ATSWProgressBar.EndTime = EndTime ATSWProgressBar.Mode = ATSW_CAST ATSWProgressBar.Working = true end ATSW_Time = 0 function ATSW_StartProcessing() ATSWFrame:RegisterEvent('SPELLCAST_INTERRUPTED') Processing.Complete = 0 Processing.Stop = false Processing.Active = true ATSW_UpdateTasks() end function ATSW_PushCreateButton(State) if State or (State == nil) then ATSWCreateButton:SetButtonState('PUSHED', true) ATSWCreateButton:SetHighlightTexture('') ATSWCreateButton:SetTextColor(0.5, 0.5, 0.5) ATSWCreateButton:SetHighlightTextColor(0.5, 0.5, 0.5) elseif State == false then ATSWCreateButton:SetButtonState('NORMAL', false) ATSWCreateButton:SetHighlightTexture('Interface\\Buttons\\UI-Panel-Button-Highlight') ATSWCreateButton:SetTextColor(1, 0.82, 0) ATSWCreateButton:SetHighlightTextColor(1, 0.82, 0) end end function ATSW_StopProcessing() ATSWFrame:UnregisterEvent('SPELLCAST_START') ATSWFrame:UnregisterEvent('SPELLCAST_INTERRUPTED') Processing.Active = false Processing.Stop = false ATSWProgressBar.Working = false ATSWProgressBar.TaskItem = nil ATSWProgressBar:Hide() ATSW_PushCreateButton(false) ATSW_UpdateTasks() end function ATSW_CompleteTask() ATSW_DeleteTask(Processing.Recipe, 1, Processing.Profession) Processing.Amount = Processing.Amount - 1 Processing.Complete = Processing.Complete + 1 if (Processing.Profession == Profession()) and ATSWFrame:IsVisible() then local Cooldown = GetCraftCooldown(GetPositionFromGame(Processing.Recipe)) if Cooldown then ATSW_UpdateFrame() end end if Processing.Amount == 0 or Processing.Stop then ATSW_StopProcessing() end end -- Auction/Necessaries functions function ATSW_ShowAuctionShoppingList() if (AuctionFrame and AuctionFrame:IsVisible() or aux_frame and aux_frame:IsVisible()) and NecessaryReagentsSize() > 0 and ATSW_Options.DisplayShoppingList then for A = 1, ATSW_AUCTION_ITEMS_DISPLAYED do -- Create auction shopping frame buttons if not _G['ATSWSLFReagent' .. A] then local F = CreateFrame('Frame', 'ATSWSLFReagent' .. A, ATSWShoppingListFrame, 'ATSWSLFReagentTemplate') if A == 1 then F:SetPoint('TOPLEFT', 10, -44) else F:SetPoint('TOPLEFT', 'ATSWSLFReagent' .. A - 1, 'BOTTOMLEFT') end end end -- Attach (Compatible with aux) if not aux_frame and AuctionFrame then ATSWShoppingListFrame:SetPoint('TOPLEFT', 'AuctionFrame', 'TOPLEFT', 348, -435.5) else ATSWShoppingListFrame:ClearAllPoints() ATSWShoppingListFrame:SetPoint('TOPRIGHT', 'aux_frame', 'BOTTOMRIGHT', 30, 0) end ATSWShoppingListFrame:Show() ATSW_UpdateAuctionList() -- Update end end function ATSWAuction_SearchForRecipe() InsertIntoAuction(this:GetParent().Link) end local function UpdateReagentList(ButtonName, ButtonsMax, Offset) for I = 1, ButtonsMax do local Button = _G[ButtonName .. I ] local Item = _G[ButtonName .. I .. 'Item' ] local Bags = _G[ButtonName .. I .. 'Bags' ] local Bank = _G[ButtonName .. I .. 'Bank' ] local Alts = _G[ButtonName .. I .. 'Alt' ] local Merchant = _G[ButtonName .. I .. 'Merchant' ] local R = NecessaryReagent(I + Offset) SetVisible(Button, false) -- It is needed for tooltip update if button text is changed SetVisible(Button, R) if R and Button then local C = LinkToColor(R.Link) or 'ffffff' local AmountBags = ATSW_GetBagsAmount (R.Name) local AmountBank = ATSW_GetBankAmount (R.Name) local AmountAlts = ATSW_GetAltsAmount (R.Name) local IsInMerchant = ATSW_IsInMerchant (R.Name) Button.Link = R.Link Button.Name = R.Name SetEnabled(Bags, false) SetEnabled(Bank, false) SetEnabled(Merchant, false) if AmountBags == 0 then AmountBags = Color.GREY .. AmountBags .. '|r' end if AmountBank == 0 then AmountBank = Color.GREY .. AmountBank .. '|r' end if AmountAlts == 0 then AmountAlts = Color.GREY .. AmountAlts .. '|r' end Item: SetNormalTexture (R.Texture) Item: SetText ('|cff' .. C .. '[' .. R.Name .. ']|r ' .. Color.GREY .. '[' .. R.Amount .. ']|r') Bags: SetText (AmountBags) Bank: SetText (AmountBank) Alts: SetText (AmountAlts) SetVisible (Merchant, IsInMerchant) end end end function ATSW_UpdateAuctionList() local ButtonsMax = ATSW_AUCTION_ITEMS_DISPLAYED local Offset = FauxScrollFrame_GetOffset(ATSWSLScrollFrame) UpdateReagentList ('ATSWSLFReagent', ButtonsMax, Offset) FauxScrollFrame_Update(ATSWSLScrollFrame, NecessaryReagentsSize(), ButtonsMax, ATSW_TRADESKILL_HEIGHT) end function ATSW_UpdateNecessaryReagents() if NoTasks() then ResetNecessaries() end if NecessaryReagentsSize() == 0 then ATSWReagentsFrame:Hide() else UpdateReagentList('ATSWRFReagent', ATSW_NECESSARIES_DISPLAYED, 0) SetVisible(ATSWReagentsFrameAndMore, NecessaryReagentsSize() > ATSW_NECESSARIES_DISPLAYED) end end -- Search functions function ATSWSearchBox_OnTextChanged(Text) if not ATSW_BlockSearchTextChanged then SetSearchString(Text) if RecipesSize() > 0 then ATSW_GetRecipesSorted(true) end end end local function ReplaceMagicCharacters(String) local S = String -- Magic characters are ^$()%.[]*? and maybe + and - S = string.gsub(S, '%%', '%%%%') S = string.gsub(S, '%[', '%%[') S = string.gsub(S, '%^', '%%^') S = string.gsub(S, '%$', '%%$') S = string.gsub(S, '%(', '%%(') S = string.gsub(S, '%)', '%%)') S = string.gsub(S, '%.', '%%.') S = string.gsub(S, '%[', '%%[') S = string.gsub(S, '%]', '%%]') S = string.gsub(S, '%*', '%%*') S = string.gsub(S, '%?', '%%?') return S end local function RemoveSideSpaces(S) if S then _, _, S = string.find(S, '^%s*(.-)%s*$') end return S end local Parameters = {} function ATSW_Filter(R, Type) local Name = R.Name if not Name then return false elseif Name == '' then return true end ParameterString = string.lower(ReplaceMagicCharacters(SearchString())) for I in pairs(Parameters) do Parameters[I] = nil end local _, _, Search = string.find(ParameterString, '^([^:]*)') if Search then Parameters['name'] = RemoveSideSpaces(Search) end for w in string.gfind(ParameterString, ':[^:]+') do local _, _, Param_Name, Param_Value = string.find(w, ':(%a+)%s([^:]+)') RemoveSideSpaces(Param_Value) if Param_Name then Parameters[Param_Name] = Param_Value end end for PName, PValue in pairs(Parameters) do if ATSW_ShowAttributes() then Name = GetAttributes(R) end if PName == 'name' then if not string.find(string.lower(Name), PValue) then return false end end if PName == 'reagent' or PName == 'r' then local Index = GetRecipePosition(Name) if Index then local Found = false for J = 1, Recipe(Index).ReagentsSize do if string.find(string.lower(GetReagentData(Index, J) or ''), PValue) then Found = true end end if not Found then return false end else return false end end if PName == 'level' or PName == 'rarity' or PName == 'quality' or PName == 'q' or PName == 'possible' or PName == 'possibletotal' then local PMin, PDirection, PMax if PName == 'rarity' or PName == 'quality' or PName == 'q' then _, _, PMin, PDirection, PMax = string.find(PValue, '^(%a+)%s*([%+%-]?)%s*(%a-)$') local QualityNames = { ['grey' ] = 1, ['white' ] = 2, ['green' ] = 3, ['blue' ] = 4, ['purple' ] = 5, ['orange' ] = 6, ['gold' ] = 7, ['cyan' ] = 8, ['poor' ] = 1, ['common' ] = 2, ['uncommon'] = 3, ['rare' ] = 4, ['epic' ] = 5, ['legendary'] = 6, ['artifact' ] = 7, ['heirloom' ] = 8 } PMin = QualityNames[PMin] PMax = QualityNames[PMax] else _, _, PMin, PDirection, PMax = string.find(PValue, '^(%d+)%s*([%+%-]?)%s*(%d-)$') PMin = tonumber(PMin) PMax = tonumber(PMax) end local Param if PName == 'level' then Param = GetItemMinLevel(Recipe(GetRecipePosition(Name)).Position) or 0 elseif PName == 'rarity' or PName == 'quality' or PName == 'q' then Param = LinkToQuality(Recipe(GetRecipePosition(Name)).Link) elseif PName == 'possible' then Param = ATSW_HowManyItemsArePossibleToCreate(Name) elseif PName == 'possibletotal' then Param = ATSW_HowManyItemsArePossibleToCreateWithConsidering(Name) end if PMin and not PMax then if PDirection ~= '' then if PDirection == '+' then return Param >= PMin elseif PDirection == '-' then return Param <= PMin end else return Param == PMin end elseif PDirection == '-' then return Param > PMin and Param < PMax elseif PDirection == '+' then return Param == PMin + PMax end end end return true end -- Tooltip functions function ATSWItemButton_OnEnter() local Link = this:GetParent().Link Link = string.gsub(Link, '|c(%x+)|H(item:%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r', '%2') if Link then GameTooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT') GameTooltip:SetPoint('BOTTOMLEFT', this:GetName(), 'TOPLEFT') GameTooltip:SetHyperlink(Link) GameTooltip:Show() end end function ATSWItemButton_OnLeave() GameTooltip:Hide() end function CreateDynamicTooltip(TooltipName) local Name = TooltipName local Tooltip = CreateFrame('GameTooltip', Name, UIParent, 'GameTooltipTemplate') Tooltip: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 } }) Tooltip:SetBackdropColor(0.09, 0.09, 0.18, 1) Tooltip.NumLines = 0 Tooltip.MaxLineWidth = 0 Tooltip:SetScale(GetCVar('uiscale')) Tooltip:SetFrameStrata('TOOLTIP') function ClearFrameObject(Object, IsTexture) if Object then Object:ClearAllPoints() if IsTexture then Object:SetTexture(nil) else Object:SetText('') end Object:Hide() end end function IncrementLineCount() Tooltip.NumLines = Tooltip.NumLines + 1 return Tooltip.NumLines end function SetPointToPrevious(Object, LineIndex, PrevText) Object:ClearAllPoints() if LineIndex == 1 then Object:SetPoint('TOPLEFT', Tooltip, 'TOPLEFT', 10, -10) else Object:SetPoint('TOP', PrevText, 'BOTTOM', 0, -4) Object:SetPoint('LEFT', Tooltip, 'LEFT', 10, 0) end end function CreateTooltipLine(IconPath, LeftText, RightText, R, G, B) local LineIndex = IncrementLineCount() local Width = 0 local IsHeader = LineIndex == 1 if LineIndex == 1 then Tooltip:AddLine(' ') end local Texture = _G[Name .. 'TextureLeft' .. LineIndex] local LeftFontString = _G[Name .. 'TextLeft' .. LineIndex] local RightFontString = _G[Name .. 'TextRight' .. LineIndex] if IconPath then if not Texture then Texture = Tooltip:CreateTexture(Name .. 'TextureLeft' .. LineIndex) Texture:SetTexCoord(0.07, 0.93, 0.07, 0.93) end if IsHeader then SetSize(Texture, 20, 20) else SetSize(Texture, 16, 16) end SetPointToPrevious(Texture, LineIndex, _G[Name .. 'TextLeft' .. (LineIndex - 1)]) Texture:SetTexture(IconPath) Texture:Show() else if Texture then ClearFrameObject(Texture, true) end end if not LeftFontString then LeftFontString = Tooltip:CreateFontString(Name .. 'TextLeft' .. LineIndex, 'ARTWORK', IsHeader and 'GameTooltipHeaderText' or 'GameFontNormal') end if not (R and G and B) then R, G, B = 1, 0.82, 0 end LeftFontString:SetTextColor(R, G, B) if IconPath then LeftFontString:ClearAllPoints() LeftFontString:SetPoint('LEFT', Texture, 'RIGHT', 2, 0) else SetPointToPrevious(LeftFontString, LineIndex, _G[Name .. 'TextLeft' .. (LineIndex - 1)]) end if R and R > 1 then -- Set font LeftFontString:SetFont('Fonts\\FRIZQT__.ttf', R) elseif not IsHeader then LeftFontString:SetFont('Fonts\\FRIZQT__.ttf', 12) end LeftFontString:SetText(LeftText) LeftFontString:Show() if LeftText then Width = Width + string.len(LeftText) * 3.7 end if RightText then if not RightFontString then RightFontString = Tooltip:CreateFontString(Name .. 'TextRight' .. LineIndex, 'ARTWORK', IsHeader and 'GameTooltipHeaderText' or 'GameTooltipText') end RightFontString:SetTextColor(1, 0.82, 0) RightFontString:ClearAllPoints() RightFontString:SetPoint('TOP', LeftFontString, 'TOP') RightFontString:SetPoint('RIGHT', Tooltip, 'RIGHT', -10, 0) RightFontString:SetText(RightText) RightFontString:Show() Width = Width + string.len(RightText) * 3.5 else if RightFontString then ClearFrameObject(RightFontString, false) end end Tooltip.MaxLineWidth = math.max(math.max(Width, Tooltip.MaxLineWidth), 350) end function Tooltip:AddIconLine(IconPath, Text, R, G, B) CreateTooltipLine(IconPath, Text, nil, R, G, B) end function Tooltip:AddTextLine(Text, R, G, B) CreateTooltipLine(nil, Text, nil, R, G, B) end function Tooltip:AddDoubleLine(IconPath, LeftText, RightText, R, G, B) CreateTooltipLine(IconPath, LeftText, RightText, R, G, B) end function Tooltip:ClearLines() for I = Tooltip.NumLines, 1, -1 do ClearFrameObject(_G[Name .. 'TextureLeft' .. I], true) ClearFrameObject(_G[Name .. 'TextLeft' .. I], false) ClearFrameObject(_G[Name .. 'TextRight' .. I], false) end Tooltip.NumLines = 0 Tooltip.MaxLineWidth = 0 end return Tooltip end ATSWRecipeTooltip = CreateDynamicTooltip('ATSWRecipeTooltip') local function CreateGameTooltip(TooltipName) local Tooltip = CreateFrame('GameTooltip', TooltipName, UIParent, 'GameTooltipTemplate') Tooltip:SetBackdrop(nil) Tooltip:SetScale(GetCVar('uiscale')) Tooltip:Hide() return Tooltip end ATSWRecipeItemTooltip = CreateGameTooltip('ATSWRecipeItemTooltip') local function AddSpace(S) if string.len(S) == 0 then S = S .. ' ' elseif string.len(S) == 1 then S = S .. ' ' end return S end local function AddColor(S, Amount) if Amount == 0 then S = Color.GREY .. S .. '|r' else S = '|cffffffff' .. S .. '|r' end return S end local function WordWrap(Text, Max) if not Text then return '' end local Length = string.len(Text) if Length <= Max then return Text end local Result = '' local I = 1 while I <= Length do Result = Result .. string.sub(Text, I, I + Max - 1) I = I + Max if I <= Length then Result = Result .. '\n' end end return Result end function ATSW_ShowRecipeTooltip() if not ATSW_Options.RecipeTooltip then return end local R = Recipe(GetRecipeSortedPosition(this.Name)) if IsBeastTraining() then if R then GameTooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT') GameTooltip:SetCraftSpell(R.Position) end return end local Tooltip = ATSWRecipeTooltip Tooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT', -4, 5) if this.Type == 'header' then return end local LINE_HEIGHT = 18 Tooltip:ClearLines() -- Header local cR, cG, cB = 1, 1, 1 if R.Link then cR, cG, cB = GetLinkColorRGB(R.Link) end Tooltip:AddIconLine(R.Texture, R.Name, cR, cG, cB) -- Game tooltip information ATSWRecipeItemTooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT') ATSW_TooltipSetSkill(ATSWRecipeItemTooltip, this.Position) ATSWRecipeItemTooltipTextLeft1:SetText(' ') ATSWRecipeItemTooltip:Show() local Height = ATSWRecipeItemTooltip:GetHeight() if Height > LINE_HEIGHT * 2 then for I = 1, Height/LINE_HEIGHT do Tooltip:AddTextLine(' ') end end -- Skill ups local AtlasLoot, SU2, SU3, SU4 = ATSW_SkillUps(R.Name) if AtlasLoot then Tooltip:AddTextLine(ATSW_TOOLTIP_SKILLUPS .. Color.ORANGE .. (AtlasLoot or '?') ..' ' .. Color.YELLOW .. (SU2 or '?') ..' ' .. Color.GREEN .. (SU3 or '?') ..' ' .. Color.GREY .. (SU4 or '?'), 11) end -- Required reagents Tooltip:AddTextLine(' ') Tooltip:AddDoubleLine(nil, ATSW_TOOLTIP_NECESSARY, ATSW_TOOLTIP_INBAGS .. ' ' .. ATSW_TOOLTIP_INBANK .. ' ' .. ATSW_TOOLTIP_ONALTS) -- Reagents local Reagents = GetReagents(R.Name, 1) for I = 1, 19 do if I <= table.getn(Reagents) then local R = Reagents[I] local Bags = ATSW_GetBagsAmount (R.Name) local Bank = ATSW_GetBankAmount (R.Name) local Alts = ATSW_GetAltsAmount (R.Name) local Merchant = '' if ATSW_IsInMerchant(R.Name) then Merchant = ' ' .. Color.GREY .. ATSW_TOOLTIP_BUYABLE..'|r' end local Amountstring = '' if R.Amount > 1 then Amountstring = Color.GREY .. ' (' .. R.Amount .. ')|r' end if Bags == 0 then Bags = '' end if Bank == 0 then Bank = '' end if Alts == 0 then Alts = '' end local BagStr, BankStr, AltsStr = Bags, Bank, Alts BagStr = AddSpace (BagStr ) BankStr = AddSpace (BankStr ) AltsStr = AddSpace (AltsStr ) BagStr = AddColor (BagStr, Bags ) BankStr = AddColor (BankStr, Bank ) AltsStr = AddColor (AltsStr, Alts ) local cR, cG, cB = GetLinkColorRGB(R.Link) Tooltip:AddDoubleLine(R.Texture, (R.Name or '') .. Amountstring .. Merchant, BagStr .. ' ' .. BankStr .. ' ' .. AltsStr .. ' ', cR, cG, cB) end end local S if ChatFrameEditBox:IsVisible() or WIM_EditBoxInFocus then S = GetSayTooltipString() elseif ATSW_HowManyItemsArePossibleToCreate(R.Name) > 0 then S = ATSW_TOOLTIP_ADDITEM end Tooltip:Show() Tooltip:SetHeight(Tooltip.NumLines * LINE_HEIGHT + 12 - ATSWRecipeItemTooltip:NumLines() * 2.3) if S then Tooltip:AddTextLine(S) Tooltip:SetHeight(Tooltip:GetHeight() + 24) end Tooltip:SetWidth(Tooltip.MaxLineWidth) ATSWRecipeItemTooltip:ClearAllPoints() ATSWRecipeItemTooltip:SetPoint('TOPLEFT', Tooltip, 'TOPLEFT', 0, -8) end -- Inventory functions ATSW_Bags = {} function ATSW_SaveBag(Bag) if ATSW_Bags[realm] == nil then ATSW_Bags[realm] = {} end if ATSW_Bags[realm][player] == nil then ATSW_Bags[realm][player] = {} end if ATSW_Bags[realm][player][Bag] == nil then ATSW_Bags[realm][player][Bag] = {} end -- Clear for Slot in pairs(ATSW_Bags[realm][player][Bag]) do ATSW_Bags[realm][player][Bag][Slot] = nil end -- Memorize for Slot = 1, GetContainerNumSlots(Bag) do local Name = LinkToName(GetContainerItemLink(Bag, Slot)) if Name then local _, Amount = GetContainerItemInfo(Bag, Slot) if ATSW_Bags[realm][player][Bag][Slot] == nil then ATSW_Bags[realm][player][Bag][Slot] = {} end ATSW_Bags[realm][player][Bag][Slot][Name] = Amount end end end function ATSW_GetBagsAmount(Name) local Total = 0 if Name then for Bag = 0, 4 do for Slot = 1, GetContainerNumSlots(Bag) do local ItemName = LinkToName(GetContainerItemLink(Bag, Slot)) if ItemName and ItemName == Name then local _, Amount = GetContainerItemInfo(Bag, Slot) Total = Total + Amount end end end end return Total end -- Bank functions ATSW_Bank = {} function ATSW_SaveBank() if BankFrameOpened then if not ATSW_Bank[realm] then ATSW_Bank[realm] = {} end if not ATSW_Bank[realm][player] then ATSW_Bank[realm][player] = {} end -- Clear for I in pairs(ATSW_Bank[realm][player]) do ATSW_Bank[realm][player][I] = nil end -- Memorize item slots for Slot = 1, 24 do local Name = LinkToName(GetContainerItemLink(BANK_CONTAINER, Slot)) if Name then local _, Amount = GetContainerItemInfo(BANK_CONTAINER, Slot) ATSW_AddToBank(Name, Amount) end end -- Memorize bank bag slots for Container = 5, 10 do for Slot = 1, GetContainerNumSlots(Container) do local Name = LinkToName(GetContainerItemLink(Container, Slot)) if Name then local _, Amount = GetContainerItemInfo(Container, Slot) ATSW_AddToBank(Name, Amount) end end end if ATSWFrame: IsVisible() then ATSW_UpdateRecipes () end if ATSWReagentsFrame: IsVisible() then ATSW_UpdateNecessaryReagents () end if ATSWShoppingListFrame: IsVisible() then ATSW_UpdateAuctionList () end end end function ATSW_AddToBank(Name, Amount) if not ATSW_Bank[realm] then ATSW_Bank[realm] = {} ATSW_Bank[realm][player] = {} end local BankAmount = ATSW_Bank[realm][player][Name] or 0 ATSW_Bank[realm][player][Name] = BankAmount + Amount end function ATSW_GetBankAmount(Name) if Name then if ATSW_Bank[realm] then if ATSW_Bank[realm][player] then if ATSW_Bank[realm][player][Name] then return ATSW_Bank[realm][player][Name] end end end end return 0 end -- Alternative character functions local function GetAltsAmountInBags(Name, PlayerName) local Table = ATSW_Bags[realm] local Amount = 0 if Name and Table then for PName in pairs(Table) do if (PlayerName and PlayerName == PName) or (PlayerName == nil and PName ~= player) then for Bag in pairs(Table[PName]) do for Slot in pairs(Table[PName][Bag]) do for IName, IAmount in pairs(Table[PName][Bag][Slot]) do if IName == Name then Amount = Amount + IAmount end end end end end end end return Amount end local function GetAltsAmountInBank(Name, PlayerName) local Table = ATSW_Bank[realm] local Amount = 0 if Name and Table then for PName in pairs(Table) do if PName ~= player then for IName, IAmount in pairs(Table[PName]) do if IName == Name then Amount = Amount + IAmount end end end end end return Amount end function ATSW_GetAltsAmount(Name) return GetAltsAmountInBags(Name) + GetAltsAmountInBank(Name) end local function GetLocation(Table, In) if Name then for RName in pairs(Table) do if RName == realm then for PName in pairs(Table[RName]) do if PName ~= player then local Amount = 0 if Table == ATSW_Bags then Amount = Amount + GetAltsAmountInBags(Name, PName) elseif Table == ATSW_Bank then Amount = Amount + GetAltsAmountInBank(Name, PName) end if Amount > 0 then if not HeaderAdded then Tooltip:AddTextLine(ATSW_TOOLTIP_POSSESS .. ' ' .. this:GetParent().Link .. ':') HeaderAdded = true end Tooltip:AddTextLine(ClassColorize(PName) .. ': ' .. '|cffffffff' .. Amount .. '|r ' .. In) end end end end end end end function ATSW_GetAltsLocationIntoTooltip(Name) local HeaderAdded = false local Tooltip = ATSWRecipeTooltip Tooltip:ClearLines() Tooltip:SetOwner(this, 'ANCHOR_BOTTOMRIGHT', 0) GetLocation(ATSW_Bags, ATSW_ALTLIST1) GetLocation(ATSW_Bank, ATSW_ALTLIST2) Tooltip:Show() Tooltip:SetWidth(Tooltip.MaxLineWidth) Tooltip:SetHeight(18 + Tooltip.NumLines * 16) end -- Merchant functions ATSW_Merchant = {} function ATSW_UpdateMerchant() if MerchantFrame:IsVisible() then for I = 1, GetMerchantNumItems() do local Name, _, _, _, Available = GetMerchantItemInfo(I) if Name and Available == -1 then ATSW_Merchant[Name] = true end end end end function ATSW_IsInMerchant(Name) return Name and ATSW_Merchant[Name] end local function GetMerchantReagentsPrice(Buy) if not (MerchantFrame:IsVisible() and NecessaryReagentsSize() > 0) then return end local TotalPrice = 0 for N = 1, NecessaryReagentsSize() do -- Calculate total price local Item = NecessaryReagent(N) for M = 1, GetMerchantNumItems() do local MName, _, MPrice, MQuantity, MAvailable = GetMerchantItemInfo(M) if Item.Name == MName then local HaveQuantity = ATSW_GetBagsAmount(Item.Name) + (ATSW_ConsigerBank and ATSW_GetBankAmount(Item.Name) or 0) local NeedQuantity = Item.Amount - HaveQuantity local BuyQuantity = math.ceil(NeedQuantity / MQuantity) if BuyQuantity > MAvailable and MAvailable > 0 then BuyQuantity = MAvailable end if BuyQuantity < 0 then BuyQuantity = 0 end TotalPrice = TotalPrice + BuyQuantity * MPrice if Buy and BuyQuantity > 0 then BuyMerchantItem(M, BuyQuantity) end end end end return TotalPrice end function ATSW_SetBuyFrameVisible() local Price = GetMerchantReagentsPrice() if Price and Price > 0 then MoneyFrame_Update(ATSWBuyPrice:GetName(), Price) ATSWBuyButton:Enable() ATSWBuyNecessaryFrame:Show() else ATSWBuyNecessaryFrame:Hide() end end function ATSW_BuyFromMerchant() GetMerchantReagentsPrice(true) end -- Compatibility with other AddOns -- ArmorCraft function ATSW_ArmorCraftCompatibility() if AC_Craft and AC_Craft:GetParent() ~= ATSWFrame then AC_Craft: SetParent (ATSWFrame) AC_Craft: SetPoint ('TOPLEFT', 'ATSWFrame', 'TOPRIGHT', 0, 0) AC_Craft: SetFrameLevel (0) AC_ToggleButton: SetParent (ATSWFrame) AC_ToggleButton: SetFrameLevel (ATSWFrame:GetFrameLevel() + 3) AC_ToggleButton: ClearAllPoints() AC_ToggleButton: SetPoint ('RIGHT', 'ATSWFrameCloseButton', 'LEFT', - AC_ToggleButton:GetWidth() - 80) AC_UseButton: ClearAllPoints() AC_UseButton: SetFrameLevel (ATSWFrame:GetFrameLevel() + 3) AC_UseButton: SetPoint ('RIGHT', 'AC_ToggleButton', 'LEFT', -2, 0) AC_Craft: SetAlpha (1.0) AC_ToggleButton: SetAlpha (1.0) AC_UseButton: SetAlpha (1.0) AC_ToggleButton: Show() end end -- AtlasLoot ATSW_AtlasLoot = nil function ATSW_CheckForAtlasLootLoaded() return AtlasLoot_Data and AtlasLoot_Data['AtlasLootCrafting'] end ATSWSkillUpCache = {} function ATSW_SkillUps(Name) if not ATSW_AtlasLoot then return end if ATSWSkillUpCache[Name] then local C = ATSWSkillUpCache[Name] return C[1], C[2], C[3], C[4] end local Texture = ATSW_GetProfessionTexture(Profession()) if not (Texture and ProfessionNamesForAtlasLoot[Texture]) then return end local Found = false for _, Item in pairs(ProfessionNamesForAtlasLoot[Texture]) do if Item ~= '' and ATSW_AtlasLoot[Item] then for _, Info in pairs(ATSW_AtlasLoot[Item]) do for N, Param in pairs(Info) do if N == 3 and string.sub(Param, 5, -1) == Name then Found = true end if N == 4 and Found then -- AtlasLoot item example: -- { 's3924', 'inv_gizmo_pipe_02', '=q1=Copper Tube', '=ds=#sr# =so1=50 =so2=80 =so3=95 =so4=110' }, -- =so parameters contain difficulty of the skill local _, _, SU1, SU2, SU3, SU4 = string.find(Param, '=so1=(.+)%s*=so2=(.+)%s*=so3=(.+)%s*=so4=(.+)') SU1 = tonumber(SU1) SU2 = tonumber(SU2) SU3 = tonumber(SU3) SU4 = tonumber(SU4) ATSWSkillUpCache[Name] = ATSWSkillUpCache[Name] or {SU1, SU2, SU3, SU4} return SU1, SU2, SU3, SU4 end end end end end end function ATSW_CompareSkill(Name) local SU1, SU2, SU3, SU4 = ATSW_SkillUps(Name) SU1 = SU1 or 0 SU2 = SU2 or 0 SU3 = SU3 or 0 SU4 = SU4 or 0 if SU1 then return SU1*20+(SU2-SU1)+(SU3-SU2)+(SU4-SU3) -- Works somehow else return 0 end end function ATSW_CompareDifficultyUsingExternalData(Left, Right) return (ATSW_TypeToNumber(Left.Type) == ATSW_TypeToNumber(Right.Type)) and (ATSW_CompareSkill(Left.Name) > ATSW_CompareSkill(Right.Name)) end