1295 lines
47 KiB
Lua
1295 lines
47 KiB
Lua
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
local _, TSM = ...
|
|
local Crafting = TSM.UI.CraftingUI:NewPackage("Crafting")
|
|
local L = TSM.Include("Locale").GetTable()
|
|
local Delay = TSM.Include("Util.Delay")
|
|
local FSM = TSM.Include("Util.FSM")
|
|
local TempTable = TSM.Include("Util.TempTable")
|
|
local Money = TSM.Include("Util.Money")
|
|
local String = TSM.Include("Util.String")
|
|
local Log = TSM.Include("Util.Log")
|
|
local Wow = TSM.Include("Util.Wow")
|
|
local Theme = TSM.Include("Util.Theme")
|
|
local ItemString = TSM.Include("Util.ItemString")
|
|
local BagTracking = TSM.Include("Service.BagTracking")
|
|
local ItemInfo = TSM.Include("Service.ItemInfo")
|
|
local Settings = TSM.Include("Service.Settings")
|
|
local ItemLinked = TSM.Include("Service.ItemLinked")
|
|
local UIElements = TSM.Include("UI.UIElements")
|
|
local private = {
|
|
settings = nil,
|
|
fsm = nil,
|
|
professions = {},
|
|
professionsKeys = {},
|
|
groupSearch = "",
|
|
showDelayFrame = 0,
|
|
filterText = "",
|
|
haveSkillUp = false,
|
|
haveMaterials = false,
|
|
professionFrame = nil,
|
|
}
|
|
local SHOW_DELAY_FRAMES = 2
|
|
local KEY_SEP = "\001"
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
function Crafting.OnInitialize()
|
|
private.settings = Settings.NewView()
|
|
:AddKey("global", "craftingUIContext", "professionScrollingTable")
|
|
:AddKey("global", "craftingUIContext", "professionDividedContainer")
|
|
:AddKey("char", "craftingUIContext", "groupTree")
|
|
:AddKey("factionrealm", "internalData", "isCraftFavorite")
|
|
ItemLinked.RegisterCallback(private.ItemLinkedCallback)
|
|
TSM.UI.CraftingUI.RegisterTopLevelPage(L["Crafting"], private.GetCraftingFrame)
|
|
private.FSMCreate()
|
|
end
|
|
|
|
function Crafting.GatherCraftNext(spellId, quantity)
|
|
private.fsm:ProcessEvent("EV_CRAFT_NEXT_BUTTON_CLICKED", spellId, quantity)
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Crafting UI
|
|
-- ============================================================================
|
|
|
|
function private.GetCraftingFrame()
|
|
TSM.UI.AnalyticsRecordPathChange("crafting", "crafting")
|
|
return UIElements.New("DividedContainer", "crafting")
|
|
:SetMinWidth(400, 200)
|
|
:SetBackgroundColor("PRIMARY_BG")
|
|
:SetSettingsContext(private.settings, "professionDividedContainer")
|
|
:SetLeftChild(UIElements.New("Frame", "left")
|
|
:SetLayout("VERTICAL")
|
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
|
:AddChild(UIElements.New("ViewContainer", "viewContainer")
|
|
:SetNavCallback(private.GetCraftingMainScreen)
|
|
:AddPath("main", true)
|
|
)
|
|
)
|
|
:SetRightChild(UIElements.New("Frame", "queue")
|
|
:SetLayout("VERTICAL")
|
|
:SetBackgroundColor("PRIMARY_BG")
|
|
:AddChild(UIElements.New("Text", "title")
|
|
:SetHeight(24)
|
|
:SetMargin(8, 8, 6, 0)
|
|
:SetFont("BODY_BODY1_BOLD")
|
|
:SetText(format(L["Crafting Queue (%d)"], 0))
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line")
|
|
:SetHeight(2)
|
|
:SetMargin(0, 0, 4, 4)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("CraftingQueueList", "queueList")
|
|
:SetQuery(TSM.Crafting.CreateCraftsQuery()
|
|
:IsNotNil("num")
|
|
:GreaterThan("num", 0)
|
|
)
|
|
:SetScript("OnRowMouseDown", private.QueueOnRowMouseDown)
|
|
:SetScript("OnRowClick", private.QueueOnRowClick)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line")
|
|
:SetHeight(2)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Frame", "footer")
|
|
:SetLayout("VERTICAL")
|
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
|
:AddChild(UIElements.New("Frame", "queueTime")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(8, 8, 8, 4)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetMargin(0, 4, 0, 0)
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Time to Craft:"])
|
|
)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "queueCost")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(8, 8, 0, 4)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetMargin(0, 4, 0, 0)
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Estimated Cost:"])
|
|
)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "queueProfit")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(8, 8, 0, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetMargin(0, 4, 0, 0)
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Estimated Profit:"])
|
|
)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetFont("TABLE_TABLE1")
|
|
:SetJustifyH("RIGHT")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "craft")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(24)
|
|
:SetMargin(8)
|
|
:AddChild(UIElements.NewNamed("ActionButton", "craftNextBtn", "TSMCraftingBtn")
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetText(L["Craft Next"])
|
|
:SetScript("OnClick", private.CraftNextOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "clearBtn")
|
|
:SetWidth(70)
|
|
:SetFont("BODY_BODY3_MEDIUM")
|
|
:SetText(L["Clear All"])
|
|
:SetScript("OnClick", private.ClearOnClick)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
:SetScript("OnUpdate", private.FrameOnUpdate)
|
|
:SetScript("OnHide", private.FrameOnHide)
|
|
end
|
|
|
|
function private.GetCraftingMainScreen(self, button)
|
|
if button == "main" then
|
|
return UIElements.New("Frame", "main")
|
|
:SetLayout("VERTICAL")
|
|
:SetPadding(0, 0, 6, 0)
|
|
:AddChild(UIElements.New("TabGroup", "content")
|
|
:SetNavCallback(private.GetCraftingElements)
|
|
:AddPath(L["Crafting List"], true)
|
|
:AddPath(L["TSM Groups"])
|
|
)
|
|
end
|
|
end
|
|
|
|
function private.GetCraftingElements(self, button)
|
|
if button == L["Crafting List"] then
|
|
private.filterText = ""
|
|
private.professionFrame = UIElements.New("Frame", "profession")
|
|
:SetLayout("VERTICAL")
|
|
:AddChild(UIElements.New("Frame", "header")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(24)
|
|
:SetMargin(8)
|
|
:AddChild(UIElements.New("SelectionDropdown", "professionDropdown")
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetHintText(L["No Profession Opened"])
|
|
:SetScript("OnSelectionChanged", private.ProfessionDropdownOnSelectionChanged)
|
|
)
|
|
:AddChild(UIElements.New("Input", "filterInput")
|
|
:SetHeight(24)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetIconTexture("iconPack.18x18/Search")
|
|
:SetClearButtonEnabled(true)
|
|
:SetHintText(L["Search Patterns"])
|
|
:SetScript("OnValueChanged", private.FilterInputOnValueChanged)
|
|
)
|
|
:AddChild(UIElements.New("ActionButton", "filterBtn", "FILTER")
|
|
:SetSize(24, 24)
|
|
:SetIcon("iconPack.18x18/Filter")
|
|
:SetDefaultNoBackground()
|
|
:DisableClickCooldown()
|
|
:SetHighlightLocked(private.haveSkillUp or private.haveMaterials, "INDICATOR")
|
|
:SetScript("OnClick", private.FilterButtonOnClick)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "recipeContent")
|
|
:SetLayout("VERTICAL")
|
|
:SetMargin(0, 0, 0, 1)
|
|
:AddChild(UIElements.New("ProfessionScrollingTable", "recipeList")
|
|
:SetFavoritesContext(private.settings.isCraftFavorite)
|
|
:SetSettingsContext(private.settings, "professionScrollingTable")
|
|
:SetScript("OnSelectionChanged", private.RecipeListOnSelectionChanged)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line")
|
|
:SetHeight(2)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("Frame", "details")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(120)
|
|
:SetPadding(8, 0, 8, 8)
|
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
|
:AddChild(UIElements.New("Frame", "left")
|
|
:SetLayout("VERTICAL")
|
|
:SetWidth(234)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:AddChild(UIElements.New("Frame", "content")
|
|
:SetLayout("HORIZONTAL")
|
|
:AddChild(UIElements.New("Button", "icon")
|
|
:SetSize(20, 20)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetScript("OnClick", private.ItemOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Button", "name")
|
|
:SetHeight(24)
|
|
:SetFont("ITEM_BODY1")
|
|
:SetJustifyH("LEFT")
|
|
:SetScript("OnClick", private.ItemOnClick)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "cost")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:SetMargin(0, 0, 8, 0)
|
|
:AddChild(UIElements.New("Text", "label")
|
|
:SetWidth("AUTO")
|
|
:SetMargin(0, 4, 0, 0)
|
|
:SetFont("BODY_BODY3")
|
|
:SetText(L["Crafting Cost"]..":")
|
|
)
|
|
:AddChild(UIElements.New("Text", "text")
|
|
:SetFont("TABLE_TABLE1")
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "craft")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(20)
|
|
:AddChild(UIElements.New("Text", "num")
|
|
:SetWidth("AUTO")
|
|
:SetFont("BODY_BODY3")
|
|
)
|
|
:AddChild(UIElements.New("Text", "error")
|
|
:SetFont("BODY_BODY3")
|
|
:SetTextColor(Theme.GetFeedbackColor("RED"))
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Spacer", "spacer"))
|
|
)
|
|
:AddChild(UIElements.New("Texture", "line")
|
|
:SetWidth(2)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("CraftingMatList", "matList")
|
|
:SetBackgroundColor("PRIMARY_BG_ALT")
|
|
)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Frame", "buttons")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(40)
|
|
:SetPadding(8)
|
|
:AddChild(UIElements.New("ActionButton", "craftAllBtn")
|
|
:SetWidth(155)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetText(L["Craft All"])
|
|
:SetScript("OnMouseDown", private.CraftAllBtnOnMouseDown)
|
|
:SetScript("OnClick", private.CraftAllBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("Input", "craftInput")
|
|
:SetWidth(75)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetBackgroundColor("ACTIVE_BG")
|
|
:SetJustifyH("CENTER")
|
|
:SetSubAddEnabled(true)
|
|
:SetValidateFunc("NUMBER", "1:9999")
|
|
:SetValue(1)
|
|
)
|
|
:AddChild(UIElements.New("ActionButton", "craftBtn")
|
|
:SetHeight(24)
|
|
:SetMargin(0, 8, 0, 0)
|
|
:SetText(L["Craft"])
|
|
:SetScript("OnMouseDown", private.CraftBtnOnMouseDown)
|
|
:SetScript("OnClick", private.CraftBtnOnClick)
|
|
)
|
|
:AddChild(UIElements.New("ActionButton", "queueBtn")
|
|
:SetHeight(24)
|
|
:SetText(L["Queue"])
|
|
:SetScript("OnClick", private.QueueBtnOnClick)
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Text", "recipeListLoadingText")
|
|
:SetJustifyH("CENTER")
|
|
:SetFont("HEADING_H5")
|
|
:SetText(L["Loading..."])
|
|
)
|
|
:SetScript("OnUpdate", private.ProfessionFrameOnUpdate)
|
|
:SetScript("OnHide", private.ProfessionFrameOnHide)
|
|
private.professionFrame:GetElement("recipeContent"):Hide()
|
|
private.professionFrame:GetElement("buttons"):Hide()
|
|
return private.professionFrame
|
|
elseif button == L["TSM Groups"] then
|
|
local frame = UIElements.New("Frame", "group")
|
|
:SetLayout("VERTICAL")
|
|
:AddChild(UIElements.New("Frame", "search")
|
|
:SetLayout("HORIZONTAL")
|
|
:SetHeight(24)
|
|
:SetMargin(8)
|
|
:AddChild(UIElements.New("Input", "input")
|
|
:SetIconTexture("iconPack.18x18/Search")
|
|
:AllowItemInsert(true)
|
|
:SetClearButtonEnabled(true)
|
|
:SetValue(private.groupSearch)
|
|
:SetHintText(L["Search Groups"])
|
|
:SetScript("OnValueChanged", private.GroupSearchOnValueChanged)
|
|
)
|
|
:AddChild(UIElements.New("Button", "expandAllBtn")
|
|
:SetSize(24, 24)
|
|
:SetMargin(8, 4, 0, 0)
|
|
:SetBackground("iconPack.18x18/Expand All")
|
|
:SetScript("OnClick", private.ExpandAllGroupsOnClick)
|
|
:SetTooltip(L["Expand / Collapse All Groups"])
|
|
)
|
|
:AddChild(UIElements.New("Button", "selectAllBtn")
|
|
:SetSize(24, 24)
|
|
:SetMargin(0, 4, 0, 0)
|
|
:SetBackground("iconPack.18x18/Select All")
|
|
:SetScript("OnClick", private.SelectAllGroupsOnClick)
|
|
:SetTooltip(L["Select / Deselect All Groups"])
|
|
)
|
|
:AddChild(UIElements.New("Button", "groupsBtn")
|
|
:SetSize(24, 24)
|
|
:SetBackground("iconPack.18x18/Groups")
|
|
:SetScript("OnClick", private.CreateProfessionBtnOnClick)
|
|
:SetTooltip(L["Create Profession Groups"])
|
|
)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "lineTop")
|
|
:SetHeight(2)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("ApplicationGroupTree", "groupTree")
|
|
:SetSettingsContext(private.settings, "groupTree")
|
|
:SetQuery(TSM.Groups.CreateQuery(), "Crafting")
|
|
:SetSearchString(private.groupSearch)
|
|
:SetScript("OnGroupSelectionChanged", private.GroupTreeOnGroupSelectionChanged)
|
|
)
|
|
:AddChild(UIElements.New("Texture", "lineBottom")
|
|
:SetHeight(2)
|
|
:SetTexture("ACTIVE_BG")
|
|
)
|
|
:AddChild(UIElements.New("ActionButton", "addBtn")
|
|
:SetHeight(24)
|
|
:SetMargin(6, 6, 8, 8)
|
|
:SetText(L["Restock Selected Groups"])
|
|
:SetScript("OnClick", private.QueueAddBtnOnClick)
|
|
)
|
|
:SetScript("OnUpdate", private.GroupFrameOnUpdate)
|
|
frame:GetElement("addBtn"):SetDisabled(frame:GetElement("groupTree"):IsSelectionCleared())
|
|
return frame
|
|
else
|
|
error("Unexpected button: "..tostring(button))
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Local Script Handlers
|
|
-- ============================================================================
|
|
|
|
function private.FrameOnUpdate(frame)
|
|
-- delay the FSM event by a few frames to give textures a chance to load
|
|
if private.showDelayFrame == SHOW_DELAY_FRAMES then
|
|
frame:SetScript("OnUpdate", nil)
|
|
private.fsm:ProcessEvent("EV_FRAME_SHOW", frame)
|
|
else
|
|
private.showDelayFrame = private.showDelayFrame + 1
|
|
end
|
|
end
|
|
|
|
function private.FrameOnHide()
|
|
private.showDelayFrame = 0
|
|
private.fsm:ProcessEvent("EV_FRAME_HIDE")
|
|
end
|
|
|
|
function private.ProfessionFrameOnUpdate(frame)
|
|
frame:SetScript("OnUpdate", nil)
|
|
TSM.UI.AnalyticsRecordPathChange("crafting", "crafting", "profession")
|
|
private.fsm:ProcessEvent("EV_PAGE_CHANGED", "profession")
|
|
private.fsm:ProcessEvent("EV_RECIPE_FILTER_CHANGED", private.filterText)
|
|
end
|
|
|
|
function private.ProfessionFrameOnHide(frame)
|
|
assert(private.professionFrame == frame)
|
|
private.professionFrame = nil
|
|
end
|
|
|
|
function private.GroupFrameOnUpdate(frame)
|
|
frame:SetScript("OnUpdate", nil)
|
|
TSM.UI.AnalyticsRecordPathChange("crafting", "crafting", "group")
|
|
private.fsm:ProcessEvent("EV_PAGE_CHANGED", "group")
|
|
end
|
|
|
|
function private.GroupSearchOnValueChanged(input)
|
|
private.groupSearch = strlower(input:GetValue())
|
|
input:GetElement("__parent.__parent.groupTree")
|
|
:SetSearchString(private.groupSearch)
|
|
:Draw()
|
|
end
|
|
|
|
function private.ExpandAllGroupsOnClick(button)
|
|
button:GetElement("__parent.__parent.groupTree")
|
|
:ToggleExpandAll()
|
|
end
|
|
|
|
function private.SelectAllGroupsOnClick(button)
|
|
button:GetElement("__parent.__parent.groupTree")
|
|
:ToggleSelectAll()
|
|
end
|
|
|
|
function private.CreateProfessionBtnOnClick(button)
|
|
local baseFrame = button:GetBaseElement()
|
|
local profName = TSM.Crafting.ProfessionState.GetCurrentProfession()
|
|
if not profName then
|
|
Log.PrintUser(L["There is currently no profession open, so cannot create profession groups."])
|
|
return
|
|
end
|
|
local items = profName..TSM.CONST.GROUP_SEP..L["Items"]
|
|
local mats = profName..TSM.CONST.GROUP_SEP..L["Materials"]
|
|
if TSM.Groups.Exists(profName) then
|
|
if not TSM.Groups.Exists(items) then
|
|
TSM.Groups.Create(items)
|
|
end
|
|
if not TSM.Groups.Exists(mats) then
|
|
TSM.Groups.Create(mats)
|
|
end
|
|
else
|
|
TSM.Groups.Create(profName)
|
|
TSM.Groups.Create(items)
|
|
TSM.Groups.Create(mats)
|
|
end
|
|
|
|
local numMats, numItems = 0, 0
|
|
local query = TSM.Crafting.CreateRawMatItemQuery()
|
|
:Matches("professions", profName)
|
|
:Select("itemString")
|
|
|
|
for _, itemString in query:IteratorAndRelease() do
|
|
local classId = ItemInfo.GetClassId(itemString)
|
|
if itemString and not TSM.Groups.IsItemInGroup(itemString) and not ItemInfo.IsSoulbound(itemString) and classId ~= LE_ITEM_CLASS_WEAPON and classId ~= LE_ITEM_CLASS_ARMOR then
|
|
TSM.Groups.SetItemGroup(itemString, mats)
|
|
numMats = numMats + 1
|
|
end
|
|
end
|
|
|
|
query = TSM.Crafting.ProfessionScanner.CreateQuery()
|
|
:Select("spellId")
|
|
|
|
for _, spellId in query:IteratorAndRelease() do
|
|
local itemString = TSM.Crafting.GetItemString(spellId)
|
|
if itemString and not TSM.Groups.IsItemInGroup(itemString) and not ItemInfo.IsSoulbound(itemString) then
|
|
TSM.Groups.SetItemGroup(itemString, items)
|
|
numItems = numItems + 1
|
|
end
|
|
end
|
|
|
|
if numMats > 0 or numItems > 0 then
|
|
Log.PrintfUser(L["%s group updated with %d items and %d materials."], profName, numItems, numMats)
|
|
else
|
|
Log.PrintfUser(L["%s group is already up to date."], profName)
|
|
end
|
|
|
|
baseFrame:GetElement("content.crafting.left.viewContainer.main.content.group.groupTree"):UpdateData(true)
|
|
baseFrame:HideDialog()
|
|
end
|
|
|
|
function private.GroupTreeOnGroupSelectionChanged(groupTree)
|
|
local addBtn = groupTree:GetElement("__parent.addBtn")
|
|
addBtn:SetDisabled(groupTree:IsSelectionCleared())
|
|
addBtn:Draw()
|
|
end
|
|
|
|
function private.ProfessionDropdownOnSelectionChanged(dropdown)
|
|
local key = dropdown:GetSelectedItemKey()
|
|
if not key then
|
|
-- nothing selected
|
|
return
|
|
end
|
|
local player, profession, skillId = strsplit(KEY_SEP, key)
|
|
if not profession then
|
|
-- the current linked / guild / NPC profession was re-selected, so just ignore this change
|
|
return
|
|
end
|
|
-- TODO: support showing of other player's professions?
|
|
assert(player == UnitName("player"))
|
|
TSM.Crafting.ProfessionUtil.OpenProfession(profession, skillId)
|
|
end
|
|
|
|
function private.FilterInputOnValueChanged(input)
|
|
local text = input:GetValue()
|
|
if text == private.filterText then
|
|
return
|
|
end
|
|
private.filterText = text
|
|
|
|
private.fsm:ProcessEvent("EV_RECIPE_FILTER_CHANGED", private.filterText)
|
|
end
|
|
|
|
function private.RecipeListOnSelectionChanged(list)
|
|
local selection = list:GetSelection()
|
|
if selection and CraftFrame_SetSelection and TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
|
CraftFrame_SetSelection(TSM.Crafting.ProfessionScanner.GetIndexBySpellId(selection))
|
|
end
|
|
|
|
private.fsm:ProcessEvent("EV_RECIPE_SELECTION_CHANGED")
|
|
if selection and IsShiftKeyDown() then
|
|
local item = TSM.Crafting.ProfessionUtil.GetRecipeInfo(selection)
|
|
ChatEdit_InsertLink(item)
|
|
end
|
|
end
|
|
|
|
function private.QueueBtnOnClick(button)
|
|
local value = max(tonumber(button:GetElement("__parent.craftInput"):GetValue()), 1)
|
|
private.fsm:ProcessEvent("EV_QUEUE_BUTTON_CLICKED", value)
|
|
end
|
|
|
|
function private.ItemOnClick(text)
|
|
local spellId = tonumber(text:GetElement("__parent.name"):GetContext())
|
|
if spellId then
|
|
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
|
if IsShiftKeyDown() and ChatEdit_GetActiveWindow() then
|
|
ChatEdit_InsertLink(GetCraftItemLink(TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId)))
|
|
end
|
|
else
|
|
if IsShiftKeyDown() and ChatEdit_GetActiveWindow() then
|
|
if TSM.IsWowClassic() then
|
|
ChatEdit_InsertLink(GetTradeSkillItemLink(TSM.Crafting.ProfessionScanner.GetIndexBySpellId(spellId)))
|
|
else
|
|
ChatEdit_InsertLink(C_TradeSkillUI.GetRecipeItemLink(spellId))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
Wow.SafeItemRef(ItemInfo.GetLink(text:GetElement("__parent.name"):GetContext()))
|
|
end
|
|
end
|
|
|
|
function private.CraftBtnOnMouseDown(button)
|
|
local quantity = max(tonumber(button:GetElement("__parent.craftInput"):GetValue()), 1)
|
|
private.fsm:ProcessEvent("EV_CRAFT_BUTTON_MOUSE_DOWN", quantity)
|
|
end
|
|
|
|
function private.CraftBtnOnClick(button)
|
|
button:SetPressed(true)
|
|
button:Draw()
|
|
local quantity = max(tonumber(button:GetElement("__parent.craftInput"):GetValue()), 1)
|
|
private.fsm:ProcessEvent("EV_CRAFT_BUTTON_CLICKED", quantity)
|
|
end
|
|
|
|
function private.CraftAllBtnOnMouseDown(button)
|
|
private.fsm:ProcessEvent("EV_CRAFT_BUTTON_MOUSE_DOWN", math.huge)
|
|
end
|
|
|
|
function private.CraftAllBtnOnClick(button)
|
|
button:SetPressed(true)
|
|
button:Draw()
|
|
private.fsm:ProcessEvent("EV_CRAFT_BUTTON_CLICKED", math.huge)
|
|
end
|
|
|
|
function private.QueueOnRowMouseDown(button, data, mouseButton)
|
|
if not private.IsPlayerProfession() or mouseButton ~= "LeftButton" then
|
|
return
|
|
end
|
|
local spellId = data:GetField("spellId")
|
|
if TSM.Crafting.ProfessionScanner.HasSpellId(spellId) then
|
|
TSM.Crafting.ProfessionUtil.PrepareToCraft(spellId, TSM.Crafting.Queue.GetNum(spellId))
|
|
end
|
|
end
|
|
|
|
function private.QueueOnRowClick(button, data, mouseButton)
|
|
if not private.IsPlayerProfession() then
|
|
return
|
|
end
|
|
local spellId = data:GetField("spellId")
|
|
if mouseButton == "RightButton" then
|
|
private.fsm:ProcessEvent("EV_QUEUE_RIGHT_CLICKED", spellId)
|
|
elseif TSM.Crafting.ProfessionScanner.HasSpellId(spellId) then
|
|
private.fsm:ProcessEvent("EV_CRAFT_NEXT_BUTTON_CLICKED", spellId, TSM.Crafting.Queue.GetNum(spellId))
|
|
end
|
|
end
|
|
|
|
function private.CraftNextOnClick(button)
|
|
button:SetPressed(true)
|
|
button:Draw()
|
|
local spellId = button:GetElement("__parent.__parent.__parent.queueList"):GetFirstData():GetField("spellId")
|
|
private.fsm:ProcessEvent("EV_CRAFT_NEXT_BUTTON_CLICKED", spellId, TSM.Crafting.Queue.GetNum(spellId))
|
|
end
|
|
|
|
function private.ClearOnClick(button)
|
|
TSM.Crafting.Queue.Clear()
|
|
button:GetElement("__parent.__parent.__parent.title")
|
|
:SetText(format(L["Crafting Queue (%d)"], 0))
|
|
:Draw()
|
|
button:GetElement("__parent.__parent.queueTime.text")
|
|
:SetText("")
|
|
:Draw()
|
|
button:GetElement("__parent.__parent.queueCost.text")
|
|
:SetText("")
|
|
:Draw()
|
|
button:GetElement("__parent.__parent.queueProfit.text")
|
|
:SetText("")
|
|
:Draw()
|
|
button:GetElement("__parent.craftNextBtn")
|
|
:SetDisabled(true)
|
|
:Draw()
|
|
end
|
|
|
|
function private.QueueAddBtnOnClick(button)
|
|
local groups = TempTable.Acquire()
|
|
for _, groupPath in button:GetElement("__parent.groupTree"):SelectedGroupsIterator() do
|
|
tinsert(groups, groupPath)
|
|
end
|
|
TSM.Crafting.Queue.RestockGroups(groups)
|
|
TempTable.Release(groups)
|
|
end
|
|
|
|
function private.FilterButtonOnClick(button)
|
|
button:GetBaseElement():ShowMenuDialog(button._frame, private.FilterDialogIterator, button:GetBaseElement(), private.FilterDialogButtonOnClick, true)
|
|
end
|
|
|
|
function private.FilterDialogButtonOnClick(button, self, index1, index2, extra)
|
|
assert(not extra and index1)
|
|
if index1 == "SKILLUP" then
|
|
private.haveSkillUp = not private.haveSkillUp
|
|
button:SetText(Theme.GetColor(private.haveSkillUp and "TEXT" or "TEXT+DISABLED"):ColorText(L["Have Skill Ups"]))
|
|
:Draw()
|
|
private.fsm:ProcessEvent("EV_RECIPE_FILTER_CHANGED", private.filterText)
|
|
elseif index1 == "MATS" then
|
|
private.haveMaterials = not private.haveMaterials
|
|
button:SetText(Theme.GetColor(private.haveMaterials and "TEXT" or "TEXT+DISABLED"):ColorText(L["Have Mats"]))
|
|
:Draw()
|
|
private.fsm:ProcessEvent("EV_RECIPE_FILTER_CHANGED", private.filterText)
|
|
elseif index1 == "RESET" then
|
|
self:GetBaseElement():HideDialog()
|
|
private.haveSkillUp = false
|
|
private.haveMaterials = false
|
|
private.fsm:ProcessEvent("EV_RECIPE_FILTER_CHANGED", private.filterText)
|
|
else
|
|
error("Unexpected index1: "..tostring(index1))
|
|
end
|
|
end
|
|
|
|
function private.FilterDialogIterator(self, prevIndex)
|
|
if prevIndex == nil then
|
|
return "SKILLUP", Theme.GetColor(private.haveSkillUp and "TEXT" or "TEXT+DISABLED"):ColorText(L["Have Skill Ups"])
|
|
elseif prevIndex == "SKILLUP" then
|
|
return "MATS", Theme.GetColor(private.haveMaterials and "TEXT" or "TEXT+DISABLED"):ColorText(L["Have Mats"])
|
|
elseif prevIndex == "MATS" then
|
|
return "RESET", L["Reset Filters"]
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- FSM
|
|
-- ============================================================================
|
|
|
|
function private.FSMCreate()
|
|
local fsmContext = {
|
|
frame = nil,
|
|
recipeQuery = nil,
|
|
professionQuery = nil,
|
|
page = "profession",
|
|
selectedRecipeSpellId = nil,
|
|
queueQuery = nil,
|
|
craftingSpellId = nil,
|
|
craftingType = nil,
|
|
craftingQuantity = nil,
|
|
}
|
|
|
|
TSM.Crafting.ProfessionState.RegisterUpdateCallback(function()
|
|
private.fsm:ProcessEvent("EV_PROFESSION_STATE_UPDATE")
|
|
end)
|
|
TSM.Crafting.ProfessionScanner.RegisterHasScannedCallback(function()
|
|
private.fsm:ProcessEvent("EV_PROFESSION_STATE_UPDATE")
|
|
end)
|
|
|
|
local fsmPrivate = {
|
|
success = nil,
|
|
isDone = nil,
|
|
}
|
|
local function BagTrackingCallback()
|
|
private.fsm:ProcessEvent("EV_BAG_UPDATE_DELAYED")
|
|
end
|
|
BagTracking.RegisterCallback(BagTrackingCallback)
|
|
local function CraftCallback()
|
|
private.fsm:ProcessEvent("EV_SPELLCAST_COMPLETE", fsmPrivate.success, fsmPrivate.isDone)
|
|
fsmPrivate.success = nil
|
|
fsmPrivate.isDone = nil
|
|
end
|
|
function fsmPrivate.CraftCallback(success, isDone)
|
|
fsmPrivate.success = success
|
|
fsmPrivate.isDone = isDone
|
|
Delay.AfterFrame(1, CraftCallback)
|
|
end
|
|
function fsmPrivate.QueueUpdateCallback()
|
|
private.fsm:ProcessEvent("EV_QUEUE_UPDATE")
|
|
end
|
|
function fsmPrivate.SkillUpdateCallback()
|
|
private.fsm:ProcessEvent("EV_SKILL_UPDATE")
|
|
end
|
|
function fsmPrivate.UpdateMaterials(context)
|
|
if context.page == "profession" then
|
|
context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.recipeList"):UpdateData(true)
|
|
context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.details.matList"):UpdateData(true)
|
|
end
|
|
fsmPrivate.UpdateCraftButtons(context)
|
|
end
|
|
function fsmPrivate.UpdateProfessionsDropdown(context)
|
|
-- update the professions dropdown info
|
|
local dropdownSelection = nil
|
|
local currentProfession = TSM.Crafting.ProfessionState.GetCurrentProfession()
|
|
local isCurrentProfessionPlayer = private.IsPlayerProfession()
|
|
wipe(private.professions)
|
|
wipe(private.professionsKeys)
|
|
if currentProfession and not isCurrentProfessionPlayer then
|
|
assert(not TSM.IsWowClassic())
|
|
local playerName = nil
|
|
local linked, linkedName = TSM.Crafting.ProfessionUtil.IsLinkedProfession()
|
|
if linked then
|
|
playerName = linkedName or "?"
|
|
elseif TSM.Crafting.ProfessionUtil.IsNPCProfession() then
|
|
playerName = L["NPC"]
|
|
elseif TSM.Crafting.ProfessionUtil.IsGuildProfession() then
|
|
playerName = L["Guild"]
|
|
end
|
|
assert(playerName)
|
|
tinsert(private.professions, currentProfession)
|
|
local key = currentProfession
|
|
tinsert(private.professionsKeys, key)
|
|
dropdownSelection = key
|
|
end
|
|
|
|
for _, player, profession, skillId, level, maxLevel in TSM.Crafting.PlayerProfessions.Iterator() do
|
|
if player == UnitName("player") then
|
|
tinsert(private.professions, format("%s (%d/%d)", profession, level, maxLevel))
|
|
local key = player..KEY_SEP..profession..KEY_SEP..skillId
|
|
tinsert(private.professionsKeys, key)
|
|
if isCurrentProfessionPlayer and profession == currentProfession then
|
|
assert(not dropdownSelection)
|
|
dropdownSelection = key
|
|
end
|
|
end
|
|
end
|
|
|
|
context.frame:GetElement("left.viewContainer.main.content.profession.header.professionDropdown")
|
|
:SetItems(private.professions, private.professionsKeys)
|
|
:SetSelectedItemByKey(dropdownSelection, true)
|
|
:Draw()
|
|
end
|
|
function fsmPrivate.UpdateSkills(context)
|
|
if context.page ~= "profession" then
|
|
return
|
|
end
|
|
|
|
fsmPrivate.UpdateProfessionsDropdown(context)
|
|
end
|
|
function fsmPrivate.UpdateFilter(context)
|
|
if context.frame:GetElement("left.viewContainer.main.content"):GetPath() ~= L["Crafting List"] then
|
|
return
|
|
end
|
|
context.frame:GetElement("left.viewContainer.main.content.profession.header.filterBtn"):SetHighlightLocked(private.haveSkillUp or private.haveMaterials, "INDICATOR")
|
|
:Draw()
|
|
end
|
|
function fsmPrivate.UpdateContentPage(context)
|
|
if context.page ~= "profession" and context.page ~= "filters" then
|
|
-- nothing to update
|
|
return
|
|
end
|
|
|
|
fsmPrivate.UpdateProfessionsDropdown(context)
|
|
|
|
local craftingContentFrame = context.frame:GetElement("left.viewContainer.main.content.profession")
|
|
if not private.IsProfessionLoaded() then
|
|
local text = nil
|
|
if private.IsProfessionClosed() then
|
|
text = L["No Profession Selected"]
|
|
elseif private.IsProfessionLoadedNoSkills() then
|
|
text = L["No Crafts"]
|
|
else
|
|
text = L["Loading..."]
|
|
end
|
|
craftingContentFrame:GetElement("recipeContent"):Hide()
|
|
craftingContentFrame:GetElement("buttons"):Hide()
|
|
craftingContentFrame:GetElement("recipeListLoadingText")
|
|
:SetText(text)
|
|
:Show()
|
|
craftingContentFrame:Draw()
|
|
return
|
|
end
|
|
|
|
local recipeContent = craftingContentFrame:GetElement("recipeContent")
|
|
craftingContentFrame:GetElement("buttons"):Show()
|
|
local recipeList = recipeContent:GetElement("recipeList")
|
|
recipeContent:Show()
|
|
craftingContentFrame:GetElement("recipeListLoadingText"):Hide()
|
|
|
|
recipeList:SetQuery(fsmContext.recipeQuery)
|
|
context.selectedRecipeSpellId = recipeList:GetSelection()
|
|
local buttonFrame = recipeContent:GetElement("__parent.buttons")
|
|
local detailsFrame = recipeContent:GetElement("details")
|
|
if not context.selectedRecipeSpellId then
|
|
buttonFrame:GetElement("craftBtn")
|
|
:SetDisabled(true)
|
|
buttonFrame:GetElement("queueBtn")
|
|
:SetDisabled(true)
|
|
buttonFrame:GetElement("craftInput")
|
|
:Hide()
|
|
buttonFrame:Draw()
|
|
detailsFrame:GetElement("left.content.icon")
|
|
:Hide()
|
|
detailsFrame:GetElement("left.content.name")
|
|
:SetText("")
|
|
:SetContext(nil)
|
|
:SetTooltip(nil)
|
|
detailsFrame:GetElement("left.cost.text")
|
|
:SetText("")
|
|
detailsFrame:GetElement("left.cost.label")
|
|
:Hide()
|
|
detailsFrame:GetElement("left.craft.num")
|
|
:SetText("")
|
|
detailsFrame:GetElement("left.craft.error")
|
|
:SetText(L["No receipe selected"])
|
|
buttonFrame:GetElement("craftAllBtn")
|
|
:SetDisabled(true)
|
|
detailsFrame:GetElement("matList")
|
|
:SetRecipe(nil)
|
|
:SetContext(nil)
|
|
craftingContentFrame:Draw()
|
|
return
|
|
end
|
|
local resultName, resultItemString, resultTexture = TSM.Crafting.ProfessionUtil.GetResultInfo(context.selectedRecipeSpellId)
|
|
-- engineer tinkers can't be crafted, multi-crafted or queued
|
|
local currentProfession = TSM.Crafting.ProfessionState.GetCurrentProfession()
|
|
if not resultItemString then
|
|
buttonFrame:GetElement("craftBtn")
|
|
:SetText(currentProfession == GetSpellInfo(7411) and L["Enchant"] or L["Tinker"])
|
|
buttonFrame:GetElement("queueBtn")
|
|
:SetDisabled(true)
|
|
buttonFrame:GetElement("craftInput")
|
|
:Hide()
|
|
else
|
|
buttonFrame:GetElement("craftBtn")
|
|
:SetText(L["Craft"])
|
|
buttonFrame:GetElement("queueBtn")
|
|
:SetDisabled(false)
|
|
buttonFrame:GetElement("craftInput")
|
|
:Show()
|
|
end
|
|
local nameTooltip = resultItemString
|
|
if not nameTooltip then
|
|
if TSM.Crafting.ProfessionState.IsClassicCrafting() then
|
|
nameTooltip = "craft:"..(TSM.Crafting.ProfessionScanner.GetIndexBySpellId(context.selectedRecipeSpellId) or context.selectedRecipeSpellId)
|
|
else
|
|
nameTooltip = "enchant:"..context.selectedRecipeSpellId
|
|
end
|
|
end
|
|
detailsFrame:GetElement("left.content.icon")
|
|
:SetBackground(resultTexture)
|
|
:SetTooltip(nameTooltip)
|
|
:Show()
|
|
detailsFrame:GetElement("left.content.name")
|
|
:SetText(resultName or "?")
|
|
:SetContext(resultItemString or tostring(context.selectedRecipeSpellId))
|
|
:SetTooltip(nameTooltip)
|
|
local craftingCost = TSM.Crafting.Cost.GetCostsBySpellId(context.selectedRecipeSpellId)
|
|
detailsFrame:GetElement("left.cost.label")
|
|
:Show()
|
|
detailsFrame:GetElement("left.cost.text")
|
|
:SetText(Money.ToString(craftingCost) or "")
|
|
detailsFrame:GetElement("left.craft.num")
|
|
:SetFormattedText(L["Crafts %d"], TSM.Crafting.GetNumResult(context.selectedRecipeSpellId))
|
|
local _, _, _, toolsStr, hasTools = TSM.Crafting.ProfessionUtil.GetRecipeInfo(context.selectedRecipeSpellId)
|
|
local errorText = detailsFrame:GetElement("left.craft.error")
|
|
local canCraft, errStr = false, nil
|
|
if toolsStr and not hasTools then
|
|
errStr = REQUIRES_LABEL.." "..toolsStr
|
|
elseif TSM.Crafting.ProfessionUtil.GetRemainingCooldown(context.selectedRecipeSpellId) then
|
|
errStr = L["On Cooldown"]
|
|
elseif TSM.Crafting.ProfessionUtil.GetNumCraftable(context.selectedRecipeSpellId) == 0 then
|
|
errStr = L["Missing Materials"]
|
|
else
|
|
canCraft = true
|
|
end
|
|
errorText:SetText(errStr and "("..errStr..")" or "")
|
|
local isEnchant = TSM.Crafting.ProfessionUtil.IsEnchant(context.selectedRecipeSpellId)
|
|
buttonFrame:GetElement("craftBtn")
|
|
:SetDisabled(not canCraft or context.craftingSpellId)
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "craft")
|
|
buttonFrame:GetElement("craftAllBtn")
|
|
:SetText(isEnchant and L["Enchant Vellum"] or L["Craft All"])
|
|
:SetDisabled(not resultItemString or not canCraft or context.craftingSpellId)
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "all")
|
|
detailsFrame:GetElement("matList")
|
|
:SetRecipe(context.selectedRecipeSpellId)
|
|
:SetContext(context.selectedRecipeSpellId)
|
|
craftingContentFrame:Draw()
|
|
if TSM.Crafting.ProfessionState.IsClassicCrafting() and CraftCreateButton then
|
|
CraftCreateButton:SetParent(buttonFrame:GetElement("craftBtn"):_GetBaseFrame())
|
|
CraftCreateButton:ClearAllPoints()
|
|
CraftCreateButton:SetAllPoints(buttonFrame:GetElement("craftBtn"):_GetBaseFrame())
|
|
CraftCreateButton:SetFrameLevel(200)
|
|
CraftCreateButton:DisableDrawLayer("BACKGROUND")
|
|
CraftCreateButton:DisableDrawLayer("ARTWORK")
|
|
CraftCreateButton:SetHighlightTexture(nil)
|
|
if canCraft then
|
|
CraftCreateButton:Enable()
|
|
else
|
|
CraftCreateButton:Disable()
|
|
end
|
|
end
|
|
end
|
|
function fsmPrivate.UpdateQueueFrame(context, noDataUpdate)
|
|
local queueFrame = context.frame:GetElement("queue")
|
|
local totalCost, totalProfit, totalCastTime, totalNumQueued = TSM.Crafting.Queue.GetTotals()
|
|
queueFrame:GetElement("title"):SetText(format(L["Crafting Queue (%d)"], totalNumQueued))
|
|
queueFrame:GetElement("footer.queueTime.text"):SetText(totalCastTime and SecondsToTime(totalCastTime) or "")
|
|
queueFrame:GetElement("footer.queueCost.text"):SetText(totalCost and Money.ToString(totalCost) or "")
|
|
local color = totalProfit and (totalProfit >= 0 and Theme.GetFeedbackColor("GREEN") or Theme.GetFeedbackColor("RED")) or nil
|
|
local totalProfitText = totalProfit and Money.ToString(totalProfit, color:GetTextColorPrefix()) or ""
|
|
queueFrame:GetElement("footer.queueProfit.text"):SetText(totalProfitText)
|
|
if not noDataUpdate then
|
|
queueFrame:GetElement("queueList"):UpdateData()
|
|
end
|
|
|
|
local professionLoaded = private.IsProfessionLoaded()
|
|
local nextCraftRecord = queueFrame:GetElement("queueList"):GetFirstData()
|
|
local nextCraftSpellId = nextCraftRecord and nextCraftRecord:GetField("spellId")
|
|
if nextCraftRecord and (not professionLoaded or not TSM.Crafting.ProfessionScanner.HasSpellId(nextCraftSpellId) or TSM.Crafting.ProfessionUtil.GetNumCraftable(nextCraftSpellId) == 0) then
|
|
nextCraftRecord = nil
|
|
end
|
|
local canCraftFromQueue = professionLoaded and private.IsPlayerProfession()
|
|
queueFrame:GetElement("footer.craft.craftNextBtn")
|
|
:SetDisabled(not canCraftFromQueue or not nextCraftRecord or context.craftingSpellId)
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "queue")
|
|
if nextCraftRecord and canCraftFromQueue then
|
|
TSM.Crafting.ProfessionUtil.PrepareToCraft(nextCraftSpellId, nextCraftRecord:GetField("num"))
|
|
end
|
|
queueFrame:Draw()
|
|
end
|
|
function fsmPrivate.UpdateCraftButtons(context)
|
|
if context.page == "profession" and private.IsProfessionLoaded() and context.selectedRecipeSpellId then
|
|
local _, _, _, toolsStr, hasTools = TSM.Crafting.ProfessionUtil.GetRecipeInfo(context.selectedRecipeSpellId)
|
|
local detailsFrame = context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.details")
|
|
local errorText = detailsFrame:GetElement("left.craft.error")
|
|
local canCraft, errStr = false, nil
|
|
if toolsStr and not hasTools then
|
|
errStr = REQUIRES_LABEL.." "..toolsStr
|
|
elseif TSM.Crafting.ProfessionUtil.GetRemainingCooldown(context.selectedRecipeSpellId) then
|
|
errStr = L["On Cooldown"]
|
|
elseif TSM.Crafting.ProfessionUtil.GetNumCraftable(context.selectedRecipeSpellId) == 0 then
|
|
errStr = L["Missing Materials"]
|
|
else
|
|
canCraft = true
|
|
end
|
|
errorText:SetText(errStr and "("..errStr..")" or "")
|
|
:Draw()
|
|
local isEnchant = TSM.Crafting.ProfessionUtil.IsEnchant(context.selectedRecipeSpellId)
|
|
local _, resultItemString = TSM.Crafting.ProfessionUtil.GetResultInfo(context.selectedRecipeSpellId)
|
|
detailsFrame:GetElement("__parent.__parent.buttons.craftBtn")
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "craft")
|
|
:SetDisabled(not canCraft or context.craftingSpellId)
|
|
:Draw()
|
|
detailsFrame:GetElement("__parent.__parent.buttons.craftAllBtn")
|
|
:SetText(isEnchant and L["Enchant Vellum"] or L["Craft All"])
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "all")
|
|
:SetDisabled(not resultItemString or not canCraft or context.craftingSpellId)
|
|
:Draw()
|
|
if context.craftingQuantity and context.craftingSpellId == context.selectedRecipeSpellId then
|
|
detailsFrame:GetElement("__parent.__parent.buttons.craftInput")
|
|
:SetValue(context.craftingQuantity)
|
|
:Draw()
|
|
end
|
|
if TSM.IsWowClassic() and CraftCreateButton then
|
|
if canCraft then
|
|
CraftCreateButton:Enable()
|
|
else
|
|
CraftCreateButton:Disable()
|
|
end
|
|
end
|
|
end
|
|
|
|
local nextCraftRecord = context.frame:GetElement("queue.queueList"):GetFirstData()
|
|
if nextCraftRecord and (TSM.Crafting.GetProfession(nextCraftRecord:GetField("spellId")) ~= TSM.Crafting.ProfessionState.GetCurrentProfession() or TSM.Crafting.ProfessionUtil.GetNumCraftable(nextCraftRecord:GetField("spellId")) == 0) then
|
|
nextCraftRecord = nil
|
|
end
|
|
local canCraftFromQueue = private.IsProfessionLoaded() and private.IsPlayerProfession()
|
|
context.frame:GetElement("queue.footer.craft.craftNextBtn")
|
|
:SetPressed(context.craftingSpellId and context.craftingType == "queue")
|
|
:SetDisabled(not canCraftFromQueue or not nextCraftRecord or context.craftingSpellId)
|
|
:Draw()
|
|
end
|
|
function fsmPrivate.StartCraft(context, spellId, quantity)
|
|
local numCrafted = TSM.Crafting.ProfessionUtil.Craft(spellId, quantity, context.craftingType ~= "craft", fsmPrivate.CraftCallback)
|
|
Log.Info("Crafting %d (requested %s) of %d", numCrafted, quantity == math.huge and "all" or quantity, spellId)
|
|
if numCrafted == 0 then
|
|
return
|
|
end
|
|
context.craftingSpellId = spellId
|
|
context.craftingQuantity = numCrafted
|
|
fsmPrivate.UpdateCraftButtons(context)
|
|
end
|
|
|
|
private.fsm = FSM.New("CRAFTING_UI_CRAFTING")
|
|
:AddState(FSM.NewState("ST_FRAME_CLOSED")
|
|
:SetOnEnter(function(context)
|
|
context.page = "profession"
|
|
context.frame = nil
|
|
context.craftingSpellId = nil
|
|
context.craftingQuantity = nil
|
|
context.craftingType = nil
|
|
end)
|
|
:AddTransition("ST_FRAME_CLOSED")
|
|
:AddTransition("ST_FRAME_OPEN_NO_PROFESSION")
|
|
:AddTransition("ST_FRAME_OPEN_WITH_PROFESSION")
|
|
:AddEvent("EV_FRAME_SHOW", function(context, frame)
|
|
context.frame = frame
|
|
if private.IsProfessionLoaded() then
|
|
return "ST_FRAME_OPEN_WITH_PROFESSION"
|
|
else
|
|
return "ST_FRAME_OPEN_NO_PROFESSION"
|
|
end
|
|
end)
|
|
)
|
|
:AddState(FSM.NewState("ST_FRAME_OPEN_NO_PROFESSION")
|
|
:SetOnEnter(function(context)
|
|
context.craftingSpellId = nil
|
|
context.craftingQuantity = nil
|
|
context.craftingType = nil
|
|
if not context.queueQuery then
|
|
context.queueQuery = TSM.Crafting.Queue.CreateQuery()
|
|
context.queueQuery:SetUpdateCallback(fsmPrivate.QueueUpdateCallback)
|
|
end
|
|
fsmPrivate.UpdateContentPage(context)
|
|
fsmPrivate.UpdateQueueFrame(context)
|
|
end)
|
|
:AddTransition("ST_FRAME_OPEN_NO_PROFESSION")
|
|
:AddTransition("ST_FRAME_OPEN_WITH_PROFESSION")
|
|
:AddTransition("ST_FRAME_CLOSED")
|
|
:AddEvent("EV_PROFESSION_STATE_UPDATE", function(context)
|
|
if private.IsProfessionLoaded() then
|
|
return "ST_FRAME_OPEN_WITH_PROFESSION"
|
|
end
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_PAGE_CHANGED", function(context, page)
|
|
context.page = page
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_QUEUE_UPDATE", function(context)
|
|
fsmPrivate.UpdateQueueFrame(context, true)
|
|
end)
|
|
)
|
|
:AddState(FSM.NewState("ST_FRAME_OPEN_WITH_PROFESSION")
|
|
:SetOnEnter(function(context)
|
|
context.recipeQuery = TSM.Crafting.ProfessionScanner.CreateQuery()
|
|
:Select("spellId", "categoryId")
|
|
:OrderBy("index", true)
|
|
:VirtualField("matNames", "string", TSM.Crafting.GetMatNames, "spellId")
|
|
context.professionQuery = TSM.Crafting.PlayerProfessions.CreateQuery()
|
|
context.professionQuery:SetUpdateCallback(fsmPrivate.SkillUpdateCallback)
|
|
if context.page == "profession" then
|
|
context.frame:GetElement("left.viewContainer.main.content.profession.header.filterInput")
|
|
:SetValue("")
|
|
:Draw()
|
|
private.filterText = ""
|
|
end
|
|
if context.selectedRecipeSpellId and context.page == "profession" then
|
|
local recipeList = context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.recipeList")
|
|
recipeList:SetQuery(context.recipeQuery)
|
|
if TSM.Crafting.ProfessionScanner.GetIndexBySpellId(context.selectedRecipeSpellId) then
|
|
recipeList:SetSelection(context.selectedRecipeSpellId)
|
|
end
|
|
end
|
|
fsmPrivate.UpdateContentPage(context)
|
|
fsmPrivate.UpdateFilter(context)
|
|
fsmPrivate.UpdateQueueFrame(context)
|
|
if not context.queueQuery then
|
|
context.queueQuery = TSM.Crafting.Queue.CreateQuery()
|
|
context.queueQuery:SetUpdateCallback(fsmPrivate.QueueUpdateCallback)
|
|
end
|
|
end)
|
|
:SetOnExit(function(context)
|
|
private.haveSkillUp = false
|
|
private.haveMaterials = false
|
|
context.recipeQuery:Release()
|
|
context.recipeQuery = nil
|
|
context.professionQuery:Release()
|
|
context.professionQuery = nil
|
|
end)
|
|
:AddTransition("ST_FRAME_OPEN_NO_PROFESSION")
|
|
:AddTransition("ST_FRAME_CLOSED")
|
|
:AddEvent("EV_PROFESSION_STATE_UPDATE", function(context)
|
|
if not private.IsProfessionLoaded() then
|
|
return "ST_FRAME_OPEN_NO_PROFESSION"
|
|
end
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_RECIPE_FILTER_CHANGED", function(context, filter)
|
|
local recipeList = context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.recipeList")
|
|
local prevSelection = recipeList:GetSelection()
|
|
context.recipeQuery:Reset()
|
|
:Select("spellId", "categoryId")
|
|
:OrderBy("index", true)
|
|
:VirtualField("matNames", "string", TSM.Crafting.GetMatNames, "spellId")
|
|
if filter ~= "" then
|
|
filter = String.Escape(filter)
|
|
context.recipeQuery
|
|
:Or()
|
|
:Matches("name", filter)
|
|
:Matches("matNames", filter)
|
|
:End()
|
|
end
|
|
if private.haveSkillUp then
|
|
context.recipeQuery:NotEqual("difficulty", "trivial")
|
|
end
|
|
if private.haveMaterials then
|
|
context.recipeQuery:Custom(private.HaveMaterialsFilterHelper)
|
|
end
|
|
recipeList:UpdateData(true)
|
|
fsmPrivate.UpdateFilter(context)
|
|
if recipeList:GetSelection() ~= prevSelection then
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end
|
|
end)
|
|
:AddEvent("EV_PAGE_CHANGED", function(context, page)
|
|
context.recipeQuery:ResetFilters()
|
|
context.page = page
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_QUEUE_BUTTON_CLICKED", function(context, quantity)
|
|
assert(context.selectedRecipeSpellId)
|
|
TSM.Crafting.Queue.Add(context.selectedRecipeSpellId, quantity)
|
|
fsmPrivate.UpdateQueueFrame(context, true)
|
|
end)
|
|
:AddEvent("EV_QUEUE_RIGHT_CLICKED", function(context, spellId)
|
|
if context.page ~= "profession" or TSM.Crafting.GetProfession(spellId) ~= TSM.Crafting.ProfessionState.GetCurrentProfession() then
|
|
return
|
|
end
|
|
local recipeList = context.frame:GetElement("left.viewContainer.main.content.profession.recipeContent.recipeList")
|
|
if not recipeList:IsSpellIdVisible(spellId) then
|
|
return
|
|
end
|
|
recipeList:SetSelection(spellId)
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_RECIPE_SELECTION_CHANGED", function(context)
|
|
fsmPrivate.UpdateContentPage(context)
|
|
end)
|
|
:AddEvent("EV_BAG_UPDATE_DELAYED", function(context)
|
|
fsmPrivate.UpdateMaterials(context)
|
|
fsmPrivate.UpdateQueueFrame(context)
|
|
local professionLoaded = private.IsProfessionLoaded()
|
|
local nextCraftRecord = context.frame:GetElement("queue.queueList"):GetFirstData()
|
|
local nextCraftSpellId = nextCraftRecord and nextCraftRecord:GetField("spellId")
|
|
if nextCraftRecord and professionLoaded and TSM.Crafting.ProfessionScanner.HasSpellId(nextCraftSpellId) and TSM.Crafting.ProfessionUtil.GetNumCraftable(nextCraftSpellId) > 0 and private.IsPlayerProfession() then
|
|
TSM.Crafting.ProfessionUtil.PrepareToCraft(nextCraftSpellId, nextCraftRecord:GetField("num"))
|
|
end
|
|
end)
|
|
:AddEvent("EV_QUEUE_UPDATE", function(context)
|
|
fsmPrivate.UpdateQueueFrame(context, true)
|
|
end)
|
|
:AddEvent("EV_SKILL_UPDATE", function(context)
|
|
fsmPrivate.UpdateSkills(context)
|
|
end)
|
|
:AddEvent("EV_CRAFT_BUTTON_MOUSE_DOWN", function(context, quantity)
|
|
context.craftingType = quantity == math.huge and "all" or "craft"
|
|
TSM.Crafting.ProfessionUtil.PrepareToCraft(context.selectedRecipeSpellId, quantity)
|
|
end)
|
|
:AddEvent("EV_CRAFT_BUTTON_CLICKED", function(context, quantity)
|
|
context.craftingType = quantity == math.huge and "all" or "craft"
|
|
fsmPrivate.StartCraft(context, context.selectedRecipeSpellId, quantity)
|
|
end)
|
|
:AddEvent("EV_CRAFT_NEXT_BUTTON_CLICKED", function(context, spellId, quantity)
|
|
if context.craftingSpellId then
|
|
-- already crafting something
|
|
return
|
|
end
|
|
local _, _, _, toolsStr, hasTools = TSM.Crafting.ProfessionUtil.GetRecipeInfo(spellId)
|
|
if (toolsStr and not hasTools) or TSM.Crafting.ProfessionUtil.GetNumCraftable(spellId) == 0 or TSM.Crafting.ProfessionUtil.GetRemainingCooldown(spellId) then
|
|
-- can't craft this
|
|
return
|
|
end
|
|
context.craftingType = "queue"
|
|
fsmPrivate.StartCraft(context, spellId, quantity)
|
|
end)
|
|
:AddEvent("EV_SPELLCAST_COMPLETE", function(context, success, isDone)
|
|
if success and context.craftingSpellId then
|
|
Log.Info("Crafted %d", context.craftingSpellId)
|
|
TSM.Crafting.Queue.Remove(context.craftingSpellId, 1)
|
|
context.craftingQuantity = context.craftingQuantity - 1
|
|
assert(context.craftingQuantity >= 0)
|
|
if context.craftingQuantity == 0 then
|
|
assert(isDone)
|
|
context.craftingSpellId = nil
|
|
context.craftingQuantity = nil
|
|
context.craftingType = nil
|
|
end
|
|
else
|
|
context.craftingSpellId = nil
|
|
context.craftingQuantity = nil
|
|
context.craftingType = nil
|
|
end
|
|
fsmPrivate.UpdateCraftButtons(context)
|
|
fsmPrivate.UpdateQueueFrame(context, true)
|
|
end)
|
|
)
|
|
:AddDefaultEventTransition("EV_FRAME_HIDE", "ST_FRAME_CLOSED")
|
|
:Init("ST_FRAME_CLOSED", fsmContext)
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Private Helper Functions
|
|
-- ============================================================================
|
|
|
|
function private.IsProfessionClosed()
|
|
return TSM.Crafting.ProfessionState.GetIsClosed()
|
|
end
|
|
|
|
function private.IsProfessionLoadedNoSkills()
|
|
return not private.IsProfessionClosed() and TSM.Crafting.ProfessionState.GetCurrentProfession() and TSM.Crafting.ProfessionScanner.HasScanned() and not TSM.Crafting.ProfessionScanner.HasSkills()
|
|
end
|
|
|
|
function private.IsProfessionLoaded()
|
|
return not private.IsProfessionClosed() and TSM.Crafting.ProfessionState.GetCurrentProfession() and TSM.Crafting.ProfessionScanner.HasScanned() and TSM.Crafting.ProfessionScanner.HasSkills()
|
|
end
|
|
|
|
function private.IsPlayerProfession()
|
|
return not (TSM.Crafting.ProfessionUtil.IsNPCProfession() or TSM.Crafting.ProfessionUtil.IsLinkedProfession() or TSM.Crafting.ProfessionUtil.IsGuildProfession())
|
|
end
|
|
|
|
function private.HaveMaterialsFilterHelper(row)
|
|
return TSM.Crafting.ProfessionUtil.IsCraftable(row:GetField("spellId"))
|
|
end
|
|
|
|
function private.ItemLinkedCallback(name, itemLink)
|
|
if not private.professionFrame then
|
|
return
|
|
end
|
|
local input = private.professionFrame:GetElement("header.filterInput")
|
|
input:SetValue(ItemInfo.GetName(ItemString.GetBase(itemLink)))
|
|
:SetFocused(false)
|
|
:Draw()
|
|
|
|
private.FilterInputOnValueChanged(input)
|
|
return true
|
|
end
|