diff --git a/AdvancedTradeSkillWindow2.lua b/AdvancedTradeSkillWindow2.lua new file mode 100644 index 0000000..d55b599 --- /dev/null +++ b/AdvancedTradeSkillWindow2.lua @@ -0,0 +1,6292 @@ +-- 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 \ No newline at end of file