--Data for hotbar categories that are supported by the assignable utility wheel. If a new category or action type is added that we want to support, this table needs to be updated internalassert(HOTBAR_CATEGORY_MAX_VALUE == 14, "Update category data.") internalassert(ACTION_TYPE_MAX_VALUE == 10, "Update category data.") local SUPPORTED_HOTBAR_CATEGORY_DATA = { [HOTBAR_CATEGORY_QUICKSLOT_WHEEL] = { [ACTION_TYPE_ITEM] = true, [ACTION_TYPE_COLLECTIBLE] = true, [ACTION_TYPE_QUEST_ITEM] = true, [ACTION_TYPE_EMOTE] = true, [ACTION_TYPE_QUICK_CHAT] = true, }, [HOTBAR_CATEGORY_EMOTE_WHEEL] = { [ACTION_TYPE_EMOTE] = true, [ACTION_TYPE_QUICK_CHAT] = true, }, [HOTBAR_CATEGORY_MEMENTO_WHEEL] = { [ACTION_TYPE_COLLECTIBLE] = true, }, [HOTBAR_CATEGORY_ALLY_WHEEL] = { [ACTION_TYPE_COLLECTIBLE] = true, }, [HOTBAR_CATEGORY_TOOL_WHEEL] = { [ACTION_TYPE_COLLECTIBLE] = true, }, } --Fake hotbar category used to denote that the wheel is currently hidden ZO_UTILITY_WHEEL_HOTBAR_CATEGORY_HIDDEN = -1 ZO_UTILITY_SLOT_EMPTY_STRING = GetString(SI_QUICKSLOTS_EMPTY) ZO_UTILITY_SLOT_EMPTY_TEXTURE = "EsoUI/Art/Quickslots/quickslot_emptySlot.dds" ZO_AssignableUtilityWheel_Shared = ZO_InitializingObject:Subclass() --[[ The data table can support the following fields: -hotbarCategories: Which hotbars this wheel represents. The order of the categories displayed is based off of the order of the categories here. -numSlots: The number of slots that are in this wheel. Should match up with the size of the bar that the slots are stored in. -startSlotIndex: The index that this wheel starts at on the hotbar(s). Example: startSlotIndex = ACTION_BAR_FIRST_UTILITY_BAR_SLOT -overrideShowNameLabels: Whether or not to show the names of each slot. This overrides the default behavior for all hotbar categories used in this wheel. -By default, the emote wheel category will show name labels and the rest will not. -showKeybinds: Whether or not to show the accessibility keybinds underneath each slot. Can be a boolean or a function that returns a boolean -If this field is not set, we will not show the keybinds -If this is set to true, we will not display name labels, regardless of what overrideShowNameLabels is set to -showPendingIcon: Whether or not to show the icon of the item being slotted in the center of the wheel. Currently only supported for Gamepad -showCategoryLabel: Whether nor not to show the name of the wheel currently being displayed -includeHiddenState: Set this to true if we want one of the "Cycle Wheel" options to hide the wheel entirely -onSelectionChangedCallback: Function called when the selected entry on the wheel changes. Currently only supported for Gamepad -onHotbarCategoryChangedCallback: Function called when the current hotbar category on the wheel changes. -overrideGamepadTooltip: Overrides the tooltip used when an entry is selected. Currently only supported for Gamepad. -If this field is not set, GAMEPAD_QUAD1_TOOLTIP will be used -overrideTooltipScrollEnabled: Can be set to a boolean to indicate whether or not tooltip scrolling is enabled for this wheel. Currently only supported for Gamepad. -If this field is not set, whether or not tooltip scrolling is enabled will be controlled via a keybind that only appears if this field has not been set. -overrideActivateOnShow: Can be set to a boolean to indicate whether or not the radial menu for this wheel should activate on showing. Currently only supported for Gamepad. -If this field is not set, we assume true -customNarrationObjectName: The unique name to use when registering the wheel for narration. Currently only supported for Gamepad. -This field is required to be set for gamepad wheels in order for narration to function -headerNarrationFunction: Function used to determine the header narration for this wheel. Currently only supported for Gamepad. -If this field is not set, no header narration will be included -If customNarrationObjectName is not set, this will do nothing ]] function ZO_AssignableUtilityWheel_Shared:Initialize(control, data) self.control = control self.categoryLabel = control:GetNamedChild("Category") if self.categoryLabel then self.categoryLabel:SetHidden(not data.showCategoryLabel) end self.data = data self.slots = {} self:SetupHotbarCategories(self.data.hotbarCategories) self:InitializeSlots() self:UpdateAllSlots() self:InitializeKeybindStripDescriptors() self:RegisterForEvents() end function ZO_AssignableUtilityWheel_Shared:RegisterForEvents() local function OnSlotUpdated(eventCode, physicalSlot, hotbarCategory) if self:GetHotbarCategory() == hotbarCategory then local PLAY_ANIMATION = true self:DoSlotUpdate(physicalSlot, PLAY_ANIMATION) end end self.control:RegisterForEvent(EVENT_HOTBAR_SLOT_UPDATED, OnSlotUpdated) self.control:RegisterForEvent(EVENT_PERSONALITY_CHANGED, function() --This event is only relevant if this wheel supports emotes if self:IsActionTypeSupported(ACTION_TYPE_EMOTE) then self:UpdateAllSlots() end end) end function ZO_AssignableUtilityWheel_Shared:GetNumSlotted() local numSlots = self.data.numSlots local actionBarOffset = self.data.startSlotIndex or 0 local numSlotted = 0 local hotbarCategory = self:GetHotbarCategory() for i = actionBarOffset + 1, actionBarOffset + numSlots do if GetSlotType(i, hotbarCategory) ~= ACTION_TYPE_NOTHING then numSlotted = numSlotted + 1 end end return numSlotted end function ZO_AssignableUtilityWheel_Shared:GetHotbarCategory() return self.hotbarCategories[self.currentHotbarCategoryIndex] end function ZO_AssignableUtilityWheel_Shared:UpdateAllSlots() if self:GetHotbarCategory() ~= ZO_UTILITY_WHEEL_HOTBAR_CATEGORY_HIDDEN then for physicalSlot in pairs(self.slots) do self:DoSlotUpdate(physicalSlot) end end end function ZO_AssignableUtilityWheel_Shared:CycleHotbarCategory() local nextHotbarCategoryIndex = self.currentHotbarCategoryIndex % self.numHotbars + 1 if nextHotbarCategoryIndex ~= self.currentHotbarCategoryIndex then self.currentHotbarCategoryIndex = nextHotbarCategoryIndex self:RefreshHotbarCategory() if self.data.onHotbarCategoryChangedCallback then self.data:onHotbarCategoryChangedCallback(nextHotbarCategoryIndex) end end end function ZO_AssignableUtilityWheel_Shared:RefreshHotbarCategory() local hotbarCategory = self:GetHotbarCategory() if hotbarCategory == ZO_UTILITY_WHEEL_HOTBAR_CATEGORY_HIDDEN then self.control:SetHidden(true) if self.categoryLabel then self.categoryLabel:SetText("") end else self.control:SetHidden(false) self:UpdateAllSlots() if self.categoryLabel then local categoryName = GetString("SI_HOTBARCATEGORY", hotbarCategory) self.categoryLabel:SetText(categoryName) end end end function ZO_AssignableUtilityWheel_Shared:SetupHotbarCategories(categoryList) local validCategories = {} local numValidCategories = 0 for _, hotbarCategory in ipairs(categoryList) do if SUPPORTED_HOTBAR_CATEGORY_DATA[hotbarCategory] ~= nil then table.insert(validCategories, hotbarCategory) numValidCategories = numValidCategories + 1 end end if numValidCategories > 0 then if self.data.includeHiddenState then table.insert(validCategories, ZO_UTILITY_WHEEL_HOTBAR_CATEGORY_HIDDEN) numValidCategories = numValidCategories + 1 end self.numHotbars = numValidCategories self.hotbarCategories = validCategories self.currentHotbarCategoryIndex = 1 return true else internalassert(false, "No valid hotbar categories found") return false end end function ZO_AssignableUtilityWheel_Shared:SetHotbarCategories(hotbarCategories) -- We use self.data.hotbarCategories instead of self.hotbarCategories for the comparison because self.hotbarCategories includes the hidden state if not ZO_AreNumericallyIndexedTablesEqual(hotbarCategories, self.data.hotbarCategories) then if self:SetupHotbarCategories(hotbarCategories) then ClearCursor() self.data.hotbarCategories = hotbarCategories self:RefreshHotbarCategory() KEYBIND_STRIP:UpdateKeybindButtonGroup(self.keybindStripDescriptor) --If we are setting the hotbar categories this wheel supports to something new, assume the hotbar category changed if self.data.onHotbarCategoryChangedCallback then self.data:onHotbarCategoryChangedCallback(self.currentHotbarCategoryIndex) end end end end function ZO_AssignableUtilityWheel_Shared:IsActionTypeSupported(actionType) local categoryData = SUPPORTED_HOTBAR_CATEGORY_DATA[self:GetHotbarCategory()] if categoryData and categoryData[actionType] then return true else return false end end function ZO_AssignableUtilityWheel_Shared:Activate() if self.categoryLabel then local hotbarCategory = self:GetHotbarCategory() if hotbarCategory ~= ZO_UTILITY_WHEEL_HOTBAR_CATEGORY_HIDDEN then local categoryName = GetString("SI_HOTBARCATEGORY", hotbarCategory) self.categoryLabel:SetText(categoryName) else self.categoryLabel:SetText("") end end self:UpdateAllSlots() KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor) end function ZO_AssignableUtilityWheel_Shared:Deactivate() KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor) end function ZO_AssignableUtilityWheel_Shared:ShouldShowKeybinds() if type(self.data.showKeybinds) == "function" then return self.data.showKeybinds() else return self.data.showKeybinds end end function ZO_AssignableUtilityWheel_Shared:CreateSlots() --To be overridden end function ZO_AssignableUtilityWheel_Shared:DoSlotUpdate(physicalSlot, playAnimation) --To be overridden end function ZO_AssignableUtilityWheel_Shared:InitializeKeybindStripDescriptors() local function AlignLeftOnGamepadCenterOnKeyboard() if IsInGamepadPreferredMode() then return KEYBIND_STRIP_ALIGN_LEFT else return KEYBIND_STRIP_ALIGN_CENTER end end self.keybindStripDescriptor = { alignment = AlignLeftOnGamepadCenterOnKeyboard, -- Cycle { name = GetString(SI_UTILITY_WHEEL_CYCLE_WHEEL), keybind = "UI_SHORTCUT_QUATERNARY", visible = function() return self.numHotbars > 1 and not IsCurrentlyPreviewing() end, callback = function() ClearCursor() self:CycleHotbarCategory() end, }, } end