Files
AdvancedTradeSkillWindow2/AdvancedTradeSkillWindow2.lua

6297 lines
192 KiB
Lua

-- 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)
if Colors[ID] then
return Colors[ID].R, Colors[ID].G, Colors[ID].B
end
return 1, 0.82, 0
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