-- Definitions local ATTACH_GOLD_ICON = "EsoUI/Art/TradeWindow/Gamepad/gp_tradeAddGold.dds" local ADD_ATTACHMENT_ICON = "EsoUI/Art/TradeWindow/Gamepad/gp_tradeAddItem.dds" local TRADE_ITEM_ENTRY_TEMPLATE = "ZO_GamepadItemSubEntryTemplate" local ATTACH_GOLD_TEXT = GetString(SI_GAMEPAD_TRADE_ATTACH_GOLD) local ATTACH_ITEMS_TEXT = GetString(SI_GAMEPAD_TRADE_ATTACH_ITEMS) --These functions handle updating the UI as we go between confimation states. --An entry [A][B] holds the function that can move the UI from confirm state A to confirm state B. local ConfirmChangeFunctions = { [TRADE_ME] = { [TRADE_CONFIRM_EDIT] = { [TRADE_CONFIRM_ACCEPT] = function() GAMEPAD_TRADE:EnterConfirmation(TRADE_ME) end }, [TRADE_CONFIRM_ACCEPT] = { [TRADE_CONFIRM_EDIT] = function() GAMEPAD_TRADE:ExitConfirmation(TRADE_ME) end, } }, [TRADE_THEM] = { [TRADE_CONFIRM_EDIT] = { [TRADE_CONFIRM_ACCEPT] = function() GAMEPAD_TRADE:EnterConfirmation(TRADE_THEM) end }, [TRADE_CONFIRM_ACCEPT] = { [TRADE_CONFIRM_EDIT] = function() GAMEPAD_TRADE:ExitConfirmation(TRADE_THEM) end, } }, } -- Window has two views local VIEW_OFFER = 1 local VIEW_INVENTORY = 2 ----------------- -- Initialization ----------------- ZO_GamepadTradeWindow = ZO_Object.MultiSubclass(ZO_SharedTradeWindow, ZO_Gamepad_ParametricList_BagsSearch_Screen) function ZO_GamepadTradeWindow:New(control) local tradeWindow = ZO_Object.New(self) tradeWindow:Initialize(control) return tradeWindow end function ZO_GamepadTradeWindow:Initialize(control) self.tradeScene = ZO_Scene:New("gamepadTrade", SCENE_MANAGER) local DONT_ACTIVATE_ON_SHOW = false ZO_Gamepad_ParametricList_BagsSearch_Screen.Initialize(self, control, ZO_GAMEPAD_HEADER_TABBAR_DONT_CREATE, DONT_ACTIVATE_ON_SHOW, self.tradeScene) ZO_SharedTradeWindow.Initialize(self, self.control) self:SetTextSearchContext("tradeTextSearch") end function ZO_GamepadTradeWindow:OnDeferredInitialize() local function HandleInventoryChanged() if not self.control:IsHidden() then self:RefreshOfferList(TRADE_ME) end end self.control:RegisterForEvent(EVENT_INVENTORY_FULL_UPDATE, HandleInventoryChanged) self.control:RegisterForEvent(EVENT_INVENTORY_SINGLE_SLOT_UPDATE, HandleInventoryChanged) self.sceneName = "gamepadTrade" self.confirmChangeFunctions = ConfirmChangeFunctions self.theirControls = self.control:GetNamedChild("TheirControls") -- Gold Slider self.goldSliderControl = self.control:GetNamedChild("Mask"):GetNamedChild("GoldSliderBox") self.goldSlider = ZO_CurrencySelector_Gamepad:New(self.goldSliderControl:GetNamedChild("Selector")) self.goldSlider:SetCurrencyType(CURT_MONEY) self.goldSlider:SetClampValues(true) self:InitializeOfferLists() self:InitializeInventoryList() self:InitializeHeaders() self:InitializeNarration() end function ZO_GamepadTradeWindow:InitializeOfferLists() -- Offer Lists self.offerLists = {} self.listTradeItemCount = {} self.offeredMoney = {} self:SetupOfferList(TRADE_ME) self:SetupOfferList(TRADE_THEM) self.offerLists[TRADE_ME]:SetDirectionalInputEnabled(false) self.offerLists[TRADE_THEM]:SetDirectionalInputEnabled(false) self.listMovementController = ZO_MovementController:New(MOVEMENT_CONTROLLER_DIRECTION_VERTICAL) self.activeListType = TRADE_ME end function ZO_GamepadTradeWindow:InitializeNarration() --Unregister for narration so we can register it manually self:UnregisterForNarration() --Manually register each of the offer lists for narration for tradeType, list in pairs(self.offerLists) do local narrationInfo = { canNarrate = function() return self:IsShowing() and self.activeListType == tradeType end, headerNarrationFunction = function() return self:GetHeaderNarration() end, footerNarrationFunction = function() return self:GetFooterNarration() end, } SCREEN_NARRATION_MANAGER:RegisterParametricList(list, narrationInfo) end local inventoryListNarrationInfo = { canNarrate = function() return self:IsShowing() and self.view == VIEW_INVENTORY end, headerNarrationFunction = function() return self:GetHeaderNarration() end, footerNarrationFunction = function() return self:GetFooterNarration() end, } --self.inventoryList is not an actual list, so we need to register self.inventoryList.list instead SCREEN_NARRATION_MANAGER:RegisterParametricList(self.inventoryList.list, inventoryListNarrationInfo) end function ZO_GamepadTradeWindow:ActivateTextSearch() ZO_Gamepad_ParametricList_BagsSearch_Screen.ActivateTextSearch(self) self:SetTextSearchEntryHidden(false) end function ZO_GamepadTradeWindow:DeactivateTextSearch() ZO_Gamepad_ParametricList_BagsSearch_Screen.DeactivateTextSearch(self) self:SetTextSearchEntryHidden(true) end do local function SetupList(list) list:AddDataTemplate(TRADE_ITEM_ENTRY_TEMPLATE, ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction) end function ZO_GamepadTradeWindow:SetupOfferList(tradetype) local list if tradetype == TRADE_ME then list = self:AddList("MyOffer", SetupList) else -- Parametric list screen assumes that its lists operate in the nav 1 panel. -- In order to have another list be in a different panel, it can't be grouped with all the lists the -- base class keeps track of list = ZO_GamepadVerticalItemParametricScrollList:New(self.theirControls:GetNamedChild("Mask"):GetNamedChild("Container"):GetNamedChild("List")) SetupList(list) list:SetAlignToScreenCenter(true) list:SetOnSelectedDataChangedCallback(function(currentList, selectedData) self:OnSelectionChanged(currentList, selectedData) end) end list:SetNoItemText(GetString(SI_GAMEPAD_TRADE_NO_ITEMS_OFFERED)) list.tradetype = tradetype list:Activate() self.offerLists[tradetype] = list end end function ZO_GamepadTradeWindow:OnUpdatedSearchResults() local list = self:GetCurrentList() if list == self.inventoryList then list:RefreshList() end end do local function ItemFilter(itemData) return TRADE_WINDOW:CanTradeItem(itemData) and not ZO_IsItemCurrentlyOfferedForTrade(itemData.bagId, itemData.slotIndex) end function ZO_GamepadTradeWindow:InitializeInventoryList() local function OnSelectionChanged(_, selectedData) self:InventorySelectionChanged(selectedData) end local function InventorySetupFunction(entryData) entryData.isTradeItem = ZO_IsItemCurrentlyOfferedForTrade(entryData.bagId, entryData.slotIndex) end local function OnRefreshList(list) if list:GetNumItems() == 0 then self:RequestEnterHeader() list:SetNoItemText(GetString(SI_INVENTORY_ERROR_FILTER_EMPTY)) else self:RequestLeaveHeader() list:SetNoItemText(GetString(SI_GAMEPAD_INVENTORY_EMPTY)) end end self:SetTextSearchEntryHidden(true) local SETUP_LOCALLY = true self.inventoryList = self:AddList("Inventory", SETUP_LOCALLY, ZO_GamepadInventoryList, BAG_BACKPACK, SLOT_TYPE_ITEM, OnSelectionChanged, InventorySetupFunction) self.inventoryList:SetOnRefreshListCallback(OnRefreshList) self.inventoryList:SetSearchContext(self.searchContext) self.inventoryList:SetItemFilterFunction(ItemFilter) self.inventoryList:SetNoItemText(GetString(SI_GAMEPAD_INVENTORY_EMPTY)) end end function ZO_GamepadTradeWindow:AddOfferListEntry(list, text, icon, callback, modifyTextType) local newEntry = ZO_GamepadEntryData:New(text, icon) newEntry:SetFontScaleOnSelection(false) newEntry.actionFunction = callback newEntry:SetModifyTextType(modifyTextType or MODIFY_TEXT_TYPE_NONE) list:AddEntry(TRADE_ITEM_ENTRY_TEMPLATE, newEntry) return newEntry end function ZO_GamepadTradeWindow:InitializeHeaders() local theirHeader = self.theirControls:GetNamedChild("Mask"):GetNamedChild("HeaderContainer"):GetNamedChild("Header") theirHeader.dividerSimple = theirHeader:GetNamedChild("DividerSimple") theirHeader.dividerAccent = theirHeader:GetNamedChild("DividerAccent") ZO_GamepadGenericHeader_Initialize(theirHeader, ZO_GAMEPAD_HEADER_TABBAR_DONT_CREATE) local myHeader = self.header myHeader.dividerSimple = myHeader:GetNamedChild("DividerSimple") myHeader.dividerAccent = myHeader:GetNamedChild("DividerAccent") self.headers = { [TRADE_THEM] = theirHeader, [TRADE_ME] = myHeader, } self:UpdateHeaders() end --------- --Updates --------- function ZO_GamepadTradeWindow:UpdateDirectionalInput() local list = self.offerLists[self.activeListType] local result = self.listMovementController:CheckMovement() -- Pass the movement to the correct list if result == MOVEMENT_CONTROLLER_MOVE_NEXT then if self.activeListType == TRADE_ME then if self.headerFocus:IsActive() then self:RequestLeaveHeader() elseif self._currentList then self._currentList:MoveNext() end else if list then list:MoveNext() end end elseif result == MOVEMENT_CONTROLLER_MOVE_PREVIOUS then if self.activeListType == TRADE_ME then if self._currentList and self._currentList:GetSelectedIndex() ~= 1 then self._currentList:MovePrevious() else self:RequestEnterHeader() end else if list then list:MovePrevious() end end end end function ZO_GamepadTradeWindow:RefreshCanSwitchFocus() local canSwitch = self.view ~= VIEW_INVENTORY and self.listTradeItemCount[TRADE_THEM] and self.listTradeItemCount[TRADE_THEM] > 0 self.headers[TRADE_ME].dividerSimple:SetHidden(canSwitch) self.headers[TRADE_ME].dividerAccent:SetHidden(not canSwitch) self.headers[TRADE_THEM].dividerSimple:SetHidden(canSwitch) self.headers[TRADE_THEM].dividerAccent:SetHidden(not canSwitch) self.canSwitchFocus = canSwitch if not canSwitch then self:SetOfferFocus(TRADE_ME) end end function ZO_GamepadTradeWindow:SetOfferFocus(listType) if self.activeListType == listType or (not self.canSwitchFocus and listType == TRADE_THEM) then return end if listType == TRADE_ME then GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:ClearFocus() GAMEPAD_NAV_QUADRANT_1_BACKGROUND_FRAGMENT:TakeFocus() PlaySound(SOUNDS.GAMEPAD_PAGE_BACK) --Re-narrate when changing focus between offers local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[listType], NARRATE_HEADER) else if self.view == VIEW_INVENTORY then return end GAMEPAD_NAV_QUADRANT_1_BACKGROUND_FRAGMENT:ClearFocus() GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:TakeFocus() PlaySound(SOUNDS.GAMEPAD_PAGE_FORWARD) --Re-narrate when changing focus between offers local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[listType], NARRATE_HEADER) end self.activeListType = listType self:RefreshKeybind() self:RefreshTooltips() end do local function UpdatePlayerGold(control) ZO_CurrencyControl_SetSimpleCurrency(control, CURT_MONEY, GetCurrencyAmount(CURT_MONEY, CURRENCY_LOCATION_CHARACTER), ZO_GAMEPAD_CURRENCY_OPTIONS_LONG_FORMAT) return true end local function GetInventoryString() return zo_strformat(SI_GAMEPAD_TRADE_INVENTORY_SPACES, GetNumBagUsedSlots(BAG_BACKPACK), GetBagSize(BAG_BACKPACK)) end function ZO_GamepadTradeWindow:UpdateGoldOfferValue(control, tradeType) ZO_CurrencyControl_SetSimpleCurrency(control, CURT_MONEY, self.offeredMoney[tradeType] or 0, ZO_GAMEPAD_CURRENCY_OPTIONS_LONG_FORMAT) return true end function ZO_GamepadTradeWindow:GetGoldOfferValueNarration(tradeType) return ZO_Currency_FormatGamepad(CURT_MONEY, self.offeredMoney[tradeType] or 0, ZO_CURRENCY_FORMAT_AMOUNT_ICON) end function ZO_GamepadTradeWindow:UpdateHeaders() self.myName = ZO_GetPrimaryPlayerName(GetUnitDisplayName("player"), GetUnitName("player")) local myTitle if self.confirm[TRADE_ME] == TRADE_CONFIRM_EDIT then myTitle = zo_strformat(SI_GAMEPAD_TRADE_USERNAME, self.myName) else myTitle = GetString(SI_GAMEPAD_TRADE_READY) end --The other trader doesn't say not ready and doesn't say 0 gold offered local theirTitle, theirGoldHeader, theirGoldValue, theirGoldValueNarration if self.confirm[TRADE_THEM] == TRADE_CONFIRM_EDIT then theirTitle = zo_strformat(SI_GAMEPAD_TRADE_USERNAME, TRADE_WINDOW.target) else theirTitle = GetString(SI_GAMEPAD_TRADE_READY) end if self.offeredMoney[TRADE_THEM] and self.offeredMoney[TRADE_THEM] > 0 then theirGoldHeader = GetString(SI_GAMEPAD_TRADE_OFFERED_GOLD) theirGoldValue = function(control) return self:UpdateGoldOfferValue(control, TRADE_THEM) end theirGoldValueNarration = function() return self:GetGoldOfferValueNarration(TRADE_THEM) end end self.headerData = { [TRADE_ME] = { titleText = myTitle, data1HeaderText = GetString(SI_GAMEPAD_TRADE_INVENTORY), data1Text = GetInventoryString, data2HeaderText = GetString(SI_GAMEPAD_TRADE_PLAYER_GOLD), data2Text = UpdatePlayerGold, data2TextNarration = ZO_Currency_GetPlayerCarriedGoldNarration, data3HeaderText = GetString(SI_GAMEPAD_TRADE_OFFERED_GOLD), data3Text = function(control) return self:UpdateGoldOfferValue(control, TRADE_ME) end, data3TextNarration = function() return self:GetGoldOfferValueNarration(TRADE_ME) end, }, [TRADE_THEM] = { titleText = theirTitle, data1HeaderText = theirGoldHeader, data1Text = theirGoldValue, data1TextNarration = theirGoldValueNarration, }, } ZO_GamepadGenericHeader_Refresh(self.headers[TRADE_ME], self.headerData[TRADE_ME]) ZO_GamepadGenericHeader_Refresh(self.headers[TRADE_THEM], self.headerData[TRADE_THEM]) end end function ZO_GamepadTradeWindow:InventorySelectionChanged(inventoryData) if self.view == VIEW_OFFER then return end if inventoryData then self.bagId = inventoryData.bagId self.slotIndex = inventoryData.slotIndex GAMEPAD_TOOLTIPS:LayoutBagItem(GAMEPAD_LEFT_TOOLTIP, self.bagId, self.slotIndex) else self.bagId = nil self.slotIndex = nil GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_LEFT_TOOLTIP) end self:RefreshKeybind() end function ZO_GamepadTradeWindow:ShowGoldSliderControl(value, maxValue) PlaySound(SOUNDS.GAMEPAD_MENU_FORWARD) local currentGold = self.offerLists[TRADE_ME]:GetTargetControl() currentGold:SetHidden(true) self.goldSlider:SetMaxValue(maxValue) self.goldSlider:SetValue(value) self:SwitchToKeybind(self.keybindStripDescriptorGoldSlider) self.goldSlider:Activate() self.goldSliderControl:SetHidden(false) end function ZO_GamepadTradeWindow:HideGoldSliderControl() local currentGold = self.offerLists[TRADE_ME]:GetTargetControl() currentGold:SetHidden(false) self.goldSlider:Deactivate() self.goldSliderControl:SetHidden(true) self:SwitchToKeybind(self.keybindStripDescriptorOffer) --Re-narrate when closing the gold slider SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[TRADE_ME]) end function ZO_GamepadTradeWindow:OnSelectionChanged(list, item) local tradetype = list.tradetype local tooltip = tradetype == TRADE_ME and GAMEPAD_LEFT_TOOLTIP or GAMEPAD_QUAD3_TOOLTIP if self.activeListType == tradetype then if item and item.tradeIndex then GAMEPAD_TOOLTIPS:LayoutTradeItem(tooltip, tradetype, item.tradeIndex) else GAMEPAD_TOOLTIPS:ClearTooltip(tooltip) end end self:RefreshKeybind() end function ZO_GamepadTradeWindow:RefreshTooltips() local tradetype = self.activeListType if tradetype == TRADE_ME then GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_QUAD3_TOOLTIP) if self.view == VIEW_INVENTORY then self:InventorySelectionChanged(self.inventoryList:GetTargetData()) else self:OnSelectionChanged(self.offerLists[TRADE_ME], self.offerLists[TRADE_ME]:GetTargetData()) end else GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_LEFT_TOOLTIP) self:OnSelectionChanged(self.offerLists[TRADE_THEM], self.offerLists[TRADE_THEM]:GetTargetData()) end end function ZO_GamepadTradeWindow:RefreshOfferList(tradetype, list) list = list or self.offerLists[tradetype] local isMyEdit = tradetype == TRADE_ME and self.confirm[tradetype] == TRADE_CONFIRM_EDIT local lastSelectedIndex = list.selectedIndex or 1 if list.Clear then list:Clear() else for _, entry in ipairs(list.data) do entry.control:SetHidden(true) end end if isMyEdit then local actionFunction = function() if IsUnitDead("player") then ZO_AlertEvent(EVENT_UI_ERROR, SI_CANNOT_DO_THAT_WHILE_DEAD) else self:ShowGoldSliderControl(self.offeredMoney[TRADE_ME] or 0, GetCurrencyAmount(CURT_MONEY, CURRENCY_LOCATION_CHARACTER)) end end self:AddOfferListEntry(list, ATTACH_GOLD_TEXT, ATTACH_GOLD_ICON, actionFunction, MODIFY_TEXT_TYPE_UPPERCASE) end local function SwitchToInventory(tradeIndex) PlaySound(SOUNDS.GAMEPAD_MENU_FORWARD) self:SwitchView(VIEW_INVENTORY) self.desiredTradeIndex = tradeIndex end self.listTradeItemCount[tradetype] = 0 for i = 1, TRADE_NUM_SLOTS do local itemLink = GetTradeItemLink(tradetype, i, LINK_STYLE_DEFAULT) if itemLink and itemLink ~= "" then local text, icon, stackCount, displayQuality, creator, sellPrice, meetsUsageRequirement, equipType, itemStyle = GetTradeItemInfo(tradetype, i) text = zo_strformat(SI_TOOLTIP_ITEM_NAME_WITH_QUANTITY, text, stackCount) local itemData = { text = text, icon = icon, displayQuality = displayQuality, -- quality is deprecated, included here for addon backwards compatibility quality = displayQuality, stackCount = stackCount, sellPrice = sellPrice, meetsUsageRequirement = meetsUsageRequirement, equipType = equipType, itemStyle = itemStyle, creator = creator, itemLink = itemLink, tradeIndex = i, } local entry = self:AddOfferListEntry(list, text, nil, function() TradeRemoveItem(i) end) entry:InitializeInventoryVisualData(itemData) self.listTradeItemCount[tradetype] = self.listTradeItemCount[tradetype] + 1 elseif isMyEdit then local text = ATTACH_ITEMS_TEXT local icon = ADD_ATTACHMENT_ICON local action = function() if IsUnitDead("player") then ZO_AlertEvent(EVENT_UI_ERROR, SI_CANNOT_DO_THAT_WHILE_DEAD) else SwitchToInventory(i) end end self:AddOfferListEntry(list, text, icon, action, MODIFY_TEXT_TYPE_UPPERCASE) end end if list.Commit then list:Commit() end self:UpdateHeaders() self:RefreshCanSwitchFocus() --- Preserve selection local ALLOW_EVEN_IF_DISABLED = true local FORCE_ANIMATION = false list:SetSelectedIndex(lastSelectedIndex, ALLOW_EVEN_IF_DISABLED, FORCE_ANIMATION) return list end function ZO_GamepadTradeWindow:EnterConfirmation(tradetype) local tradeComplete = self.confirm[TRADE_ME] == TRADE_CONFIRM_ACCEPT and self.confirm[TRADE_THEM] == TRADE_CONFIRM_ACCEPT ZO_Trade_GamepadWaiting:SetHidden(tradeComplete) local name if tradetype == TRADE_ME then self:RefreshKeybind() GAMEPAD_NAV_QUADRANT_1_BACKGROUND_FRAGMENT:SetHighlightHidden(false) name = zo_strformat(SI_GAMEPAD_TRADE_USERNAME, TRADE_WINDOW.target) --Re-narrate if the local player pressed submit SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[self.activeListType]) else GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:SetHighlightHidden(false) name = self.myName end if not tradeComplete then ZO_Trade_GamepadWaiting.name:SetText(name) end self.waitingName = name self:RefreshOfferList(tradetype) PlaySound(SOUNDS.TRADE_PARTICIPANT_READY) end function ZO_GamepadTradeWindow:ExitConfirmation(tradetype) ZO_Trade_GamepadWaiting:SetHidden(true) if tradetype == TRADE_ME then self.offerLists[TRADE_ME]:RefreshVisible() self:RefreshKeybind() GAMEPAD_NAV_QUADRANT_1_BACKGROUND_FRAGMENT:ClearHighlight() --Re-narrate if the local player cancelled their current offer SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[self.activeListType]) else GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:ClearHighlight() end self:RefreshOfferList(tradetype) self:SetConfirmationDelay(TRADE_DELAY_TIME) PlaySound(SOUNDS.TRADE_PARTICIPANT_RECONSIDER) end function ZO_GamepadTradeWindow:PrepareWindowForNewTrade() -- Do nothing, must implement as it's called from shared class end function ZO_GamepadTradeWindow:BeginTrade() local TRIGGER_CALLBACK = true self.inventoryList:RefreshList(TRIGGER_CALLBACK) self:RefreshOfferList(TRADE_ME) self:RefreshOfferList(TRADE_THEM) self:SwitchView(VIEW_OFFER) end function ZO_GamepadTradeWindow:SwitchView(view) self.view = view if view == VIEW_INVENTORY then self:ActivateTextSearch() self:SwitchToKeybind(self.keybindStripDescriptorInventory) -- inventoryControl adds conflicting keybinds so this must be done first self:SetCurrentList(self.inventoryList) if self.inventoryList.list:IsEmpty() then self:RequestEnterHeader() end --Re-narrate when switching to the inventory view local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.inventoryList.list, NARRATE_HEADER) else --- VIEW_OFFER self:DeactivateTextSearch() if self:IsHeaderActive() then self:ExitHeader() end self:SetCurrentList(self.offerLists[TRADE_ME]) self.offerLists[TRADE_ME]:RefreshVisible() self:SwitchToKeybind(self.keybindStripDescriptorOffer) -- inventoryControl removes conflicting keybinds so this must be done last --Re-narrate when leaving inventory view local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[TRADE_ME], NARRATE_HEADER) end self:RefreshTooltips() self:RefreshCanSwitchFocus() end ---------- --Keybinds ---------- function ZO_GamepadTradeWindow:SwitchToKeybind(keybindStripDescriptor) if self.keybindStripDescriptor then KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor) end self.keybindStripDescriptor = keybindStripDescriptor if keybindStripDescriptor then KEYBIND_STRIP:RemoveDefaultExit() KEYBIND_STRIP:AddKeybindButtonGroup(keybindStripDescriptor) end end function ZO_GamepadTradeWindow:RefreshKeybind() if self.keybindStripDescriptor then KEYBIND_STRIP:UpdateKeybindButtonGroup(self.keybindStripDescriptor) end end function ZO_GamepadTradeWindow:GetActiveItemSlot() local targetData = self.inventoryList.list:GetTargetData() if targetData then ZO_InventorySlot_SetType(targetData, SLOT_TYPE_GAMEPAD_INVENTORY_ITEM) end return targetData end function ZO_GamepadTradeWindow:InitializeKeybindDescriptor() self.keybindStripDescriptorOffer = { alignment = KEYBIND_STRIP_ALIGN_LEFT, -- Review My Offer { --Even though this is an ethereal keybind, the name will still be read during screen narration name = GetString(SI_GAMEPAD_TRADE_VIEW_MY_OFFER_KEYBIND), keybind = "UI_SHORTCUT_LEFT_TRIGGER", callback = function() self:SetOfferFocus(TRADE_ME) end, ethereal = true, narrateEthereal = function() return self.canSwitchFocus end, etherealNarrationOrder = 1, }, -- Edit Trade { keybind = "UI_SHORTCUT_PRIMARY", name = function() local selectedData = self.offerLists[TRADE_ME]:GetTargetData() if selectedData and selectedData.tradeIndex then return GetString(SI_GAMEPAD_TRADE_REMOVE) else return GetString(SI_GAMEPAD_TRADE_ADD) end end, callback = function() local selectedData = self.offerLists[TRADE_ME]:GetTargetData() if selectedData and selectedData.actionFunction then selectedData:actionFunction() end end, visible = function() return self.activeListType == TRADE_ME and self.confirm[TRADE_ME] == TRADE_CONFIRM_EDIT end, }, -- Cancel Trade { keybind = "UI_SHORTCUT_NEGATIVE", name = function() if self.confirm[TRADE_ME] == TRADE_CONFIRM_ACCEPT then return GetString(SI_GAMEPAD_TRADE_CANCEL_OFFER) else return GetString(SI_GAMEPAD_TRADE_CANCEL_TRADE) end end, callback = function() if self.confirm[TRADE_ME] == TRADE_CONFIRM_ACCEPT then TradeEdit() elseif self.listTradeItemCount[TRADE_ME] ~= 0 or (self.offeredMoney[TRADE_ME] and self.offeredMoney[TRADE_ME] ~= 0) then ZO_Dialogs_ShowGamepadDialog("TRADE_CANCEL_TRADE") else SCENE_MANAGER:HideCurrentScene() end end, sound = SOUNDS.DIALOG_DECLINE, }, -- Confirm Trade { keybind = "UI_SHORTCUT_SECONDARY", name = GetString(SI_GAMEPAD_TRADE_SUBMIT), visible = function() return self.confirm[TRADE_ME] == TRADE_CONFIRM_EDIT and self:IsModifyConfirmationLevelEnabled() and not IsUnitDead("player") end, callback = TradeAccept, sound = SOUNDS.DIALOG_ACCEPT, }, -- Review Their Offer { --Even though this is an ethereal keybind, the name will still be read during screen narration name = GetString(SI_GAMEPAD_TRADE_VIEW_THEIR_OFFER_KEYBIND), keybind = "UI_SHORTCUT_RIGHT_TRIGGER", callback = function() self:SetOfferFocus(TRADE_THEM) end, ethereal = true, narrateEthereal = function() return self.canSwitchFocus end, etherealNarrationOrder = 2, } } local function LeaveInventory() PlaySound(SOUNDS.GAMEPAD_MENU_BACK) self:SwitchView(VIEW_OFFER) self.desiredTradeIndex = nil end self.keybindStripDescriptorInventory = { alignment = KEYBIND_STRIP_ALIGN_LEFT, -- Edit Trade { keybind = "UI_SHORTCUT_PRIMARY", name = function() return GetString(SI_GAMEPAD_TRADE_ADD) end, callback = function() TradeAddItem(self.bagId, self.slotIndex, self.desiredTradeIndex) LeaveInventory() end, enabled = function() return self.inventoryList.list:GetNumItems() > 0 end, }, -- Switch back to Offer view { keybind = "UI_SHORTCUT_NEGATIVE", name = GetString(SI_GAMEPAD_BACK_OPTION), callback = function() LeaveInventory() end, }, -- Split Stack { name = GetString(SI_ITEM_ACTION_SPLIT_STACK), keybind = "UI_SHORTCUT_TERTIARY", callback = function() local itemSlot = self:GetActiveItemSlot() if itemSlot then ZO_InventorySlot_TrySplitStack(itemSlot) self.inventoryList:RefreshList() self:RefreshKeybind() end end, visible = function() local itemSlot = self:GetActiveItemSlot() return itemSlot and ZO_InventorySlot_CanSplitItemStack(itemSlot) end, }, } -- Gold Slider Edit self.keybindStripDescriptorGoldSlider = { alignment = KEYBIND_STRIP_ALIGN_LEFT, -- Accept { name = GetString(SI_GAMEPAD_TRADE_ACCEPT_MONEY), keybind = "UI_SHORTCUT_PRIMARY", callback = function() TradeSetMoney(self.goldSlider:GetValue()) PlaySound(SOUNDS.GAMEPAD_MENU_BACK) self:HideGoldSliderControl() end, }, -- Cancel { keybind = "UI_SHORTCUT_NEGATIVE", name = GetString(SI_GAMEPAD_BACK_OPTION), callback = function() PlaySound(SOUNDS.GAMEPAD_MENU_BACK) self:HideGoldSliderControl() end, }, } self.keybindStripDescriptor = self.keybindStripDescriptorOffer end function ZO_GamepadTradeWindow:OnBackButtonClicked() self:RequestLeaveHeader() PlaySound(SOUNDS.GAMEPAD_MENU_BACK) self:SwitchView(VIEW_OFFER) self.desiredTradeIndex = nil end function ZO_GamepadTradeWindow:OnShowing() ZO_Gamepad_ParametricList_BagsSearch_Screen.OnShowing(self) KEYBIND_STRIP:RemoveDefaultExit() self.activeListType = TRADE_ME self:SetCurrentList(self.offerLists[TRADE_ME]) GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:ClearFocus() self:BeginTrade() end function ZO_GamepadTradeWindow:OnHide() ZO_Gamepad_ParametricList_BagsSearch_Screen.OnHide(self) self.goldSlider:Deactivate() self.goldSliderControl:SetHidden(true) KEYBIND_STRIP:RestoreDefaultExit() TradeCancel() TradeSetMoney(0) self.offeredMoney = {} GAMEPAD_NAV_QUADRANT_1_BACKGROUND_FRAGMENT:ClearHighlight() GAMEPAD_NAV_QUADRANT_4_BACKGROUND_FRAGMENT:ClearHighlight() ZO_Trade_GamepadWaiting:SetHidden(true) end --Overridden from base function ZO_GamepadTradeWindow:GetHeaderNarration() --Normally we'd override ZO_Gamepad_ParametricList_Screen:GetHeaderData instead, but because we have two header controls, that isn't sufficient here local headerData = self.headerData[self.activeListType] local header = self.headers[self.activeListType] if header and headerData then return ZO_GamepadGenericHeader_GetNarrationText(header, headerData) end end --Overridden from base function ZO_GamepadTradeWindow:GetFooterNarration() local narrations = {} if not ZO_Trade_GamepadWaiting:IsHidden() then ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(GetString(SI_GAMEPAD_TRADE_WAITING_MESSAGE))) ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(self.waitingName)) end return narrations end ---------------------------- -- Slot management functions ---------------------------- --Either player added an item to the trade function ZO_GamepadTradeWindow:OnTradeWindowItemAdded(eventCode, who, tradeSlot, itemSoundCategory) self:RefreshOfferList(who) if who == TRADE_ME then self.inventoryList:RefreshList() self:RefreshKeybind() end PlayItemSound(itemSoundCategory, ITEM_SOUND_ACTION_SLOT) end --Either player removed an item from the trade function ZO_GamepadTradeWindow:OnTradeWindowItemRemoved(eventCode, who, tradeSlot, itemSoundCategory) self:RefreshOfferList(who) if who == TRADE_ME then self.inventoryList:RefreshList() self:RefreshKeybind() end PlayItemSound(itemSoundCategory, ITEM_SOUND_ACTION_PICKUP) end --region promotion of money change event function ZO_GamepadTradeWindow:OnTradeWindowMoneyChanged(eventCode, who, money) self.offeredMoney[who] = money self:RefreshOfferList(who) PlaySound(SOUNDS.ITEM_MONEY_CHANGED) --Re-narrate and include the header if the gold change was for the currently active list if who == self.activeListType then local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.offerLists[who], NARRATE_HEADER) end end -- --XML handlers -- function ZO_Trade_Gamepad_OnInitialize(control) GAMEPAD_TRADE = ZO_GamepadTradeWindow:New(control) SYSTEMS:RegisterGamepadObject("trade", GAMEPAD_TRADE) end