-------------------------------------------------------------------------------- -- Voice Chat Channels -- Consists of a list of channels the user can join, and a history -- list of previous users heard for each channel. -------------------------------------------------------------------------------- local LIST_CHANNELS = 1 local LIST_HISTORY = 2 local function GuildRoomEntriesComparator(entry1, entry2) if entry1.guildId ~= entry2.guildId then return entry1.guildId < entry2.guildId end --We want the officer's room to always be placed at the bottom. if entry1.guildRoomNumber == VOICE_CHAT_OFFICERS_ROOM_NUMBER then return false elseif entry2.guildRoomNumber == VOICE_CHAT_OFFICERS_ROOM_NUMBER then return true end return entry1.guildRoomNumber < entry2.guildRoomNumber end ZO_VoiceChatChannelsGamepad = ZO_Gamepad_ParametricList_Screen:Subclass() function ZO_VoiceChatChannelsGamepad:Initialize(control) GAMEPAD_VOICECHAT_CHANNELS_SCENE = ZO_Scene:New("gamepad_voice_chat", SCENE_MANAGER) local ACTIVATE_ON_SHOW = true ZO_Gamepad_ParametricList_Screen.Initialize(self, control, ZO_GAMEPAD_HEADER_TABBAR_CREATE, ACTIVATE_ON_SHOW, GAMEPAD_VOICECHAT_CHANNELS_SCENE) self:SetListsUseTriggerKeybinds(true) GAMEPAD_VOICECHAT_CHANNELS_FRAGMENT = ZO_FadeSceneFragment:New(control) self:InitializeHeaders() self:InitializeEvents() end function ZO_VoiceChatChannelsGamepad:InitializeHeaders() local function OnTabChangedToChannels() if self:IsHidden() then return end GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_LEFT_TOOLTIP) KEYBIND_STRIP:RemoveKeybindButtonGroup(self.historyKeybinds) self.currentList = LIST_CHANNELS self:Update() KEYBIND_STRIP:AddKeybindButtonGroup(self.channelKeybinds) local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.list, NARRATE_HEADER) end local function OnTabChangedToHistory() if self:IsHidden() then return end GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_LEFT_TOOLTIP) KEYBIND_STRIP:RemoveKeybindButtonGroup(self.channelKeybinds) self.currentList = LIST_HISTORY self:Update() KEYBIND_STRIP:AddKeybindButtonGroup(self.historyKeybinds) local NARRATE_HEADER = true SCREEN_NARRATION_MANAGER:QueueParametricListEntry(self.list, NARRATE_HEADER) end local channelsHeader = { text = GetString(SI_GAMEPAD_VOICECHAT_CHANNELS_TITLE), callback = OnTabChangedToChannels, } local historyHeader = { text = GetString(SI_GAMEPAD_VOICECHAT_HISTORY_TITLE), callback = OnTabChangedToHistory, } self.headerData = { tabBarEntries = { channelsHeader, historyHeader, }, } ZO_GamepadGenericHeader_Refresh(self.header, self.headerData) end function ZO_VoiceChatChannelsGamepad:InitializeEvents() --Manager callbacks VOICE_CHAT_MANAGER:RegisterCallback("ChannelsUpdate", function() self:Update() end) VOICE_CHAT_MANAGER:RegisterCallback("ParticipantsUpdate", function() self:UpdateParticipantsPanel() self:UpdateKeybinds() end) VOICE_CHAT_MANAGER:RegisterCallback("MuteUpdate", function() self:Update() end) --Event callbacks for playing sounds specifically while on this scene local function OnVoiceChannelJoined() if not self:IsHidden() then PlaySound(SOUNDS.VOICE_CHAT_MENU_CHANNEL_JOINED) end end local function OnVoiceChannelLeft() if not self:IsHidden() then PlaySound(SOUNDS.VOICE_CHAT_MENU_CHANNEL_LEFT) end end local function OnVoiceTransmitChannelChanged() if not self:IsHidden() then PlaySound(SOUNDS.VOICE_CHAT_MENU_CHANNEL_MADE_ACTIVE) end end self.control:RegisterForEvent(EVENT_VOICE_CHANNEL_JOINED, function(eventCode, ...) OnVoiceChannelJoined(...) end) self.control:RegisterForEvent(EVENT_VOICE_CHANNEL_LEFT, function(eventCode, ...) OnVoiceChannelLeft(...) end) self.control:RegisterForEvent(EVENT_VOICE_TRANSMIT_CHANNEL_CHANGED, function(eventCode, ...) OnVoiceTransmitChannelChanged(...) end) end do local function AddChannelToList(list, channel, headerText) local icon = channel.isJoined and VOICE_CHAT_ICON_LISTENING_CHANNEL or nil local newEntry = ZO_GamepadEntryData:New(channel.name, icon) newEntry.channel = channel newEntry:SetChannelActive(channel.isTransmitting) newEntry.narrationText = function(entryData, entryControl) local narrations = {} ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(entryData.text)) ZO_AppendNarration(narrations, ZO_GetSharedGamepadEntrySubLabelNarrationText(entryData, entryControl)) if channel.isJoined then ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(GetString(SI_SCREEN_NARRATION_JOINED_CHANNEL_ICON_NARRATION))) end ZO_AppendNarration(narrations, ZO_GetSharedGamepadEntryStatusIndicatorNarrationText(entryData, entryControl)) return narrations end if headerText then newEntry:SetHeader(headerText) list:AddEntryWithHeader("ZO_VoiceChatChannelsEntryGamepad", newEntry) else list:AddEntry("ZO_VoiceChatChannelsEntryGamepad", newEntry) end end function ZO_VoiceChatChannelsGamepad:PopulateChannels() local areaData, groupData, guildData = VOICE_CHAT_MANAGER:GetChannelData() local mainHeaderAdded = false --Area if areaData then AddChannelToList(self.list, areaData, GetString(SI_GAMEPAD_VOICECHAT_CHANNEL_MAIN_HEADER)) mainHeaderAdded = true end --Group if groupData then local header = not mainHeaderAdded and GetString(SI_GAMEPAD_VOICECHAT_CHANNEL_MAIN_HEADER) AddChannelToList(self.list, groupData, header) end --Guild Channels local guildRooms = {} for guildId, guildChannelData in pairs(guildData) do for guildRoomNumber, roomData in pairs(guildChannelData.rooms) do table.insert(guildRooms, roomData) end end table.sort(guildRooms, GuildRoomEntriesComparator) local lastGuildHeader = nil for i, roomData in ipairs(guildRooms) do local header = guildData[roomData.guildId].header if header ~= lastGuildHeader then lastGuildHeader = header else header = nil end AddChannelToList(self.list, roomData, header) end end end do local function AddHistoryDataToList(list, historyData, channelName, channel) local dataList = historyData.list local shouldAddHeader = true -- only add the header to the first entry for i, userData in ZO_NumericallyIndexedTableReverseIterator(dataList) do --we want newer entries added first, and the list is in order from old to new local newEntry = ZO_GamepadEntryData:New(ZO_FormatUserFacingDisplayName(userData.displayName), userData.isMuted and VOICE_CHAT_ICON_MUTED_PLAYER) newEntry.historyData = userData newEntry.channelName = channelName newEntry.channel = channel if shouldAddHeader then newEntry:SetHeader(channelName) list:AddEntryWithHeader("ZO_VoiceChatHistoryEntryGamepad", newEntry) shouldAddHeader = false else list:AddEntry("ZO_VoiceChatHistoryEntryGamepad", newEntry) end end end function ZO_VoiceChatChannelsGamepad:PopulateHistory() local areaData, groupData, guildData = VOICE_CHAT_MANAGER:GetChannelData() if areaData then AddHistoryDataToList(self.list, areaData.historyData, areaData.name, areaData) end if groupData then AddHistoryDataToList(self.list, groupData.historyData, groupData.name, groupData) end -- Guild Channels local guildRooms = {} for guildId, guildChannelData in pairs(guildData) do for guildRoomNumber, roomData in pairs(guildChannelData.rooms) do table.insert(guildRooms, roomData) end end table.sort(guildRooms, GuildRoomEntriesComparator) for i, roomData in ipairs(guildRooms) do AddHistoryDataToList(self.list, roomData.historyData, roomData.fullName, roomData) end end end function ZO_VoiceChatChannelsGamepad:RefreshHeaderData() ZO_GamepadGenericHeader_RefreshData(self.header, self.headerData) end function ZO_VoiceChatChannelsGamepad:UpdateKeybinds() if not GAMEPAD_VOICECHAT_CHANNELS_SCENE:IsShowing() then return end if self.currentList == LIST_CHANNELS then KEYBIND_STRIP:UpdateKeybindButtonGroup(self.channelKeybinds) elseif self.currentList == LIST_HISTORY then KEYBIND_STRIP:UpdateKeybindButtonGroup(self.historyKeybinds) end end function ZO_VoiceChatChannelsGamepad:UpdateParticipantsPanel() if self:IsHidden() then return end if self.currentList ~= LIST_CHANNELS then return end local selectedData = self.list:GetTargetData() if selectedData then local channel = selectedData.channel if channel.isJoined then local participantDataList = VOICE_CHAT_MANAGER:GetParticipantDataList(channel) GAMEPAD_TOOLTIPS:ClearStatusLabel(GAMEPAD_LEFT_TOOLTIP) GAMEPAD_TOOLTIPS:SetTooltipResetScrollOnClear(GAMEPAD_LEFT_TOOLTIP, false) GAMEPAD_TOOLTIPS:LayoutVoiceChatParticipants(GAMEPAD_LEFT_TOOLTIP, channel, participantDataList) end end end function ZO_VoiceChatChannelsGamepad:IsHidden() return self.control:IsControlHidden() end --ZO_Gamepad_ParametricList_Screen overrides function ZO_VoiceChatChannelsGamepad:SetupList(list) self.list = list list:AddDataTemplate("ZO_VoiceChatChannelsEntryGamepad", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction) list:AddDataTemplateWithHeader("ZO_VoiceChatChannelsEntryGamepad", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate") list:AddDataTemplate("ZO_VoiceChatHistoryEntryGamepad", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction) list:AddDataTemplateWithHeader("ZO_VoiceChatHistoryEntryGamepad", ZO_SharedGamepadEntry_OnSetup, ZO_GamepadMenuEntryTemplateParametricListFunction, nil, "ZO_GamepadMenuEntryHeaderTemplate") self.currentList = LIST_CHANNELS end function ZO_VoiceChatChannelsGamepad:OnSelectionChanged(list, selectedData, oldSelectedData) if selectedData then GAMEPAD_TOOLTIPS:ClearStatusLabel(GAMEPAD_LEFT_TOOLTIP) if self.currentList == LIST_CHANNELS then local channel = selectedData.channel if channel.isJoined then local participantDataList = VOICE_CHAT_MANAGER:GetParticipantDataList(channel) GAMEPAD_TOOLTIPS:LayoutVoiceChatParticipants(GAMEPAD_LEFT_TOOLTIP, channel, participantDataList) else GAMEPAD_TOOLTIPS:LayoutVoiceChatChannel(GAMEPAD_LEFT_TOOLTIP, channel) end elseif self.currentList == LIST_HISTORY then local historyData = selectedData.historyData local displayName = historyData.displayName local channelName = selectedData.channelName local lastTimeSpoken = historyData.lastTimeSpoken GAMEPAD_TOOLTIPS:LayoutVoiceChatParticipantHistory(GAMEPAD_LEFT_TOOLTIP, displayName, channelName, lastTimeSpoken) VOICE_CHAT_SOCIAL_OPTIONS:SetupOptions({speakerData = historyData, channel = selectedData.channel}) else GAMEPAD_TOOLTIPS:ClearTooltip(GAMEPAD_LEFT_TOOLTIP) end self:UpdateKeybinds() end end function ZO_VoiceChatChannelsGamepad:InitializeKeybindStripDescriptors() local joinOrActivateChannelKeybind = { name = function() local channel = self.list:GetTargetData().channel if not channel.isJoined then return GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_JOIN_CHANNEL) elseif not channel.isTransmitting then return GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_ENABLE_MIC) else return GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_DISABLE_MIC) end end, keybind = "UI_SHORTCUT_PRIMARY", callback = function() local channel = self.list:GetTargetData().channel if not channel.isJoined then VOICE_CHAT_MANAGER:JoinChannel(channel) elseif not channel.isTransmitting then VOICE_CHAT_MANAGER:TransmitChannel(channel) else VOICE_CHAT_MANAGER:StopTransmitting() end end, visible = function() if self.currentList ~= LIST_CHANNELS then return false end local entry = self.list:GetTargetData() if not entry then return false end return true end, } local leaveChannelKeybind = { name = GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_LEAVE_CHANNEL), keybind = "UI_SHORTCUT_SECONDARY", callback = function() local channel = self.list:GetTargetData().channel VOICE_CHAT_MANAGER:LeaveChannel(channel) end, visible = function() if self.currentList ~= LIST_CHANNELS then return false end local entry = self.list:GetTargetData() if not entry then return false end local channel = entry.channel return channel.isJoined end, } local showParticipantsKeybind = { name = GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_PARTICIPANT_OPTIONS), keybind = "UI_SHORTCUT_TERTIARY", callback = function() local channel = self.list:GetTargetData().channel VOICE_CHAT_PARTICIPANTS_GAMEPAD:SetChannel(channel) SCENE_MANAGER:Push("gamepad_voice_chat_participants") end, visible = function() if self.currentList ~= LIST_CHANNELS then return false end local entry = self.list:GetTargetData() if not entry then return false end local channel = entry.channel if not channel.isJoined then return false end local participantDataList = VOICE_CHAT_MANAGER:GetParticipantDataList(channel) return #participantDataList > 1 --we don't show the local player, so we need at least 1 other player end, } local viewTranscriptKeybind = { name = GetString(SI_GAMEPAD_VOICECHAT_KEYBIND_VIEW_TRANSCRIPT), keybind = "UI_SHORTCUT_RIGHT_STICK", callback = function() SCENE_MANAGER:Push("gamepadVoiceChatTranscript") end, visible = function() return GetSetting_Bool(SETTING_TYPE_ACCESSIBILITY, ACCESSIBILITY_SETTING_VOICE_CHAT_ACCESSIBILITY) end, } self.channelKeybinds = { alignment = KEYBIND_STRIP_ALIGN_LEFT, --These are inserted as numerically indiced entries joinOrActivateChannelKeybind, leaveChannelKeybind, showParticipantsKeybind, viewTranscriptKeybind, } ZO_Gamepad_AddBackNavigationKeybindDescriptors(self.channelKeybinds, GAME_NAVIGATION_TYPE_BUTTON) self.historyKeybinds = {} --ZO_VoiceChatSocialOptions_Gamepad will add the social keybinds ZO_Gamepad_AddBackNavigationKeybindDescriptors(self.historyKeybinds, GAME_NAVIGATION_TYPE_BUTTON) --Save these off so we can register cooldown delays on them later self.joinOrActivateChannelKeybind = joinOrActivateChannelKeybind self.leaveChannelKeybind = leaveChannelKeybind end function ZO_VoiceChatChannelsGamepad:OnShowing() ZO_GamepadGenericHeader_Activate(self.header) self:PerformUpdate() if self.currentList == LIST_CHANNELS then KEYBIND_STRIP:AddKeybindButtonGroup(self.channelKeybinds) elseif self.currentList == LIST_HISTORY then KEYBIND_STRIP:AddKeybindButtonGroup(self.historyKeybinds) end end function ZO_VoiceChatChannelsGamepad:OnHiding() ZO_GamepadGenericHeader_Deactivate(self.header) KEYBIND_STRIP:RemoveKeybindButtonGroup(self.historyKeybinds) KEYBIND_STRIP:RemoveKeybindButtonGroup(self.channelKeybinds) GAMEPAD_TOOLTIPS:Reset(GAMEPAD_LEFT_TOOLTIP) ZO_Gamepad_ParametricList_Screen.OnHiding(self) end do local function ShowUnavailableMessage(self) self.headerData.messageText = zo_strformat(GetString(SI_GAMEPAD_VOICECHAT_UNAVAILABLE), GetString(SI_GAMEPAD_HELP_WEBSITE)) end local function HideUnavailableMessage(self) self.headerData.messageText = nil end function ZO_VoiceChatChannelsGamepad:PerformUpdate() self.dirty = false self.list:Clear() if VOICE_CHAT_MANAGER:HasChannelData() then HideUnavailableMessage(self) if self.currentList == LIST_CHANNELS then self:PopulateChannels() TriggerTutorial(TUTORIAL_TRIGGER_VOICE_CHAT_OPEN_CHANNELS) elseif self.currentList == LIST_HISTORY then self:PopulateHistory() TriggerTutorial(TUTORIAL_TRIGGER_VOICE_CHAT_OPEN_HISTORY) end else ShowUnavailableMessage(self) end self:RefreshHeaderData() self.list:Commit() end end