From 8e1ee035dedd6f8543ded279593f633eeafb44d2 Mon Sep 17 00:00:00 2001 From: HungDK <> Date: Mon, 2 Feb 2026 09:19:24 +0700 Subject: [PATCH] Add team, teamate manager dlg --- .../Scripts/UI/Dialogs/DlgArrangeTeam.cs | 396 ++++++++++++++++++ .../Scripts/UI/Dialogs/DlgArrangeTeam.cs.meta | 2 + 2 files changed, 398 insertions(+) create mode 100644 Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs create mode 100644 Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs.meta diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs new file mode 100644 index 0000000000..047472c181 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs @@ -0,0 +1,396 @@ +// Port of C++ DlgArrangeTeam - Arrange Team dialog (invite, kick, leave, list teammates, nearby players, pickup mode, make leader). +// Uses Unity UI: content RectTransforms + item prefab with DlgArrangeTeamListItem (text component, optional Button). + +using System.Collections.Generic; +using BrewMonster; +using BrewMonster.Managers; +using BrewMonster.Network; +using BrewMonster.Scripts; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace BrewMonster.UI +{ + public class DlgArrangeTeam : AUIDialog + { + #region UI References + + [Header("Teammates list (Unity UI)")] + [SerializeField] private RectTransform Content_Teammates; + [SerializeField] private GameObject TeammateItemPrefab; + + [Header("Nearby players list (Unity UI)")] + [SerializeField] private RectTransform Content_Nearby; + [SerializeField] private GameObject NearbyItemPrefab; + + [Header("Buttons")] + [SerializeField] private Button m_pBtn_Invite; + [SerializeField] private Button m_pBtn_Kick; + [SerializeField] private Button m_pBtn_Leave; + [SerializeField] private Button m_pBtn_Disband; + [SerializeField] private Button m_pBtn_MakeLeader; + [SerializeField] private Button m_pBtn_Refresh; + + [Header("Invite")] + [SerializeField] private TMP_InputField m_pInput_Name; + + [Header("Pickup Mode")] + [SerializeField] private Toggle m_pChk_Random; + [SerializeField] private Toggle m_pChk_Free; + + [Header("Profession filter (dropdown: trigger opens panel with multi-select toggles)")] + [SerializeField] private TMP_Dropdown m_pDropdown_Prof; + + #endregion + + private readonly List m_TeammateItems = new List(); + private int m_selectedTeammateId; + + public override void Awake() + { + base.Awake(); + SetName("Win_ArrangeTeam"); +#if UNITY_EDITOR + if (m_pBtn_Leave == null) Debug.LogWarning("[DlgArrangeTeam] m_pBtn_Leave is not assigned in Inspector. Assign the Leave button."); + if (m_pBtn_Disband == null) Debug.LogWarning("[DlgArrangeTeam] m_pBtn_Disband is not assigned in Inspector. Assign the Disband button."); +#endif + } + + private void Start() + { + BindButtonListeners(); + } + + public override void OnEnable() + { + base.OnEnable(); + BindButtonListeners(); + } + + public override void OnDisable() + { + UnbindButtonListeners(); + base.OnDisable(); + } + + private void BindButtonListeners() + { + if (m_pBtn_Invite != null) { m_pBtn_Invite.onClick.RemoveListener(OnInvite); m_pBtn_Invite.onClick.AddListener(OnInvite); } + if (m_pBtn_Kick != null) { m_pBtn_Kick.onClick.RemoveListener(OnKick); m_pBtn_Kick.onClick.AddListener(OnKick); } + if (m_pBtn_Leave != null) { m_pBtn_Leave.onClick.RemoveListener(OnLeave); m_pBtn_Leave.onClick.AddListener(OnLeave); } + if (m_pBtn_Disband != null) { m_pBtn_Disband.onClick.RemoveListener(OnDisband); m_pBtn_Disband.onClick.AddListener(OnDisband); } + if (m_pBtn_MakeLeader != null) { m_pBtn_MakeLeader.onClick.RemoveListener(OnMakeLeader); m_pBtn_MakeLeader.onClick.AddListener(OnMakeLeader); } + if (m_pBtn_Refresh != null) { m_pBtn_Refresh.onClick.RemoveListener(OnRefresh); m_pBtn_Refresh.onClick.AddListener(OnRefresh); } + if (m_pChk_Random != null) m_pChk_Random.onValueChanged.AddListener(OnRandomToggled); + if (m_pChk_Free != null) m_pChk_Free.onValueChanged.AddListener(OnFreeToggled); + BindProfDropdown(); + } + + private void UnbindButtonListeners() + { + if (m_pBtn_Invite != null) m_pBtn_Invite.onClick.RemoveListener(OnInvite); + if (m_pBtn_Kick != null) m_pBtn_Kick.onClick.RemoveListener(OnKick); + if (m_pBtn_Leave != null) m_pBtn_Leave.onClick.RemoveListener(OnLeave); + if (m_pBtn_Disband != null) m_pBtn_Disband.onClick.RemoveListener(OnDisband); + if (m_pBtn_MakeLeader != null) m_pBtn_MakeLeader.onClick.RemoveListener(OnMakeLeader); + if (m_pBtn_Refresh != null) m_pBtn_Refresh.onClick.RemoveListener(OnRefresh); + if (m_pChk_Random != null) m_pChk_Random.onValueChanged.RemoveListener(OnRandomToggled); + if (m_pChk_Free != null) m_pChk_Free.onValueChanged.RemoveListener(OnFreeToggled); + UnbindProfDropdown(); + CloseProfFilterPanel(); + } + + private void OnRandomToggled(bool on) { if (on) OnPickupModeChanged(1); } + private void OnFreeToggled(bool on) { if (on) OnPickupModeChanged(0); } + + private void BindProfDropdown() + { + if (m_pDropdown_Prof != null) m_pDropdown_Prof.interactable = false; + } + + private void UnbindProfDropdown() { } + + private void CloseProfFilterPanel() { } + + private void UpdateProfDropdownCaption() + { + if (m_pDropdown_Prof == null) return; + var set = GetSelectedProfessionSet(); + string caption; + if (set == null) caption = "All"; + else if (set.Count >= (int)Profession.NUM_PROFESSION) caption = "All"; + else + { + var gameRun = EC_Game.GetGameRun(); + var names = new List(); + foreach (int i in set) + { + string name = gameRun?.GetProfName(i); + names.Add(string.IsNullOrEmpty(name) ? $"Prof {i}" : name); + } + caption = string.Join(", ", names); + } + var label = m_pDropdown_Prof.captionText; + if (label != null) label.text = caption; + } + + + public override void OnShowDialogue() + { + EnsureProfDropdownHasCaption(); + UpdateProfDropdownCaption(); + CloseProfFilterPanel(); + UpdateTeam(true); + } + + private void EnsureProfDropdownHasCaption() + { + if (m_pDropdown_Prof == null) return; + if (m_pDropdown_Prof.options.Count == 0) + m_pDropdown_Prof.AddOptions(new List { "Profession" }); + } + + /// Selected profession filter: null = show all. + private HashSet GetSelectedProfessionSet() + { + return null; + } + + /// Refresh teammate list, optional nearby list, and button states. + public bool UpdateTeam(bool bUpdateNear = false) + { + CECHostPlayer host = GetHostPlayer(); + if (host == null) return true; + + var team = host.GetTeam(); + bool hasTeam = team != null; + int idHost = host.GetCharacterID(); + bool isLeader = hasTeam && team.GetLeaderID() == idHost; + + // Teammates list: clear and rebuild from prefab. Sort by each member's single profession, then by name. + ClearList(Content_Teammates, m_TeammateItems); + m_selectedTeammateId = 0; + if (Content_Teammates != null && TeammateItemPrefab != null && hasTeam) + { + var members = new List(); + int n = team.GetMemberNum(); + for (int i = 0; i < n; i++) + { + var m = team.GetMemberByIndex(i); + if (m != null) members.Add(m); + } + members.Sort((a, b) => { int c = a.GetProfession().CompareTo(b.GetProfession()); if (c != 0) return c; return string.CompareOrdinal(a.GetName() ?? "", b.GetName() ?? ""); }); + foreach (var m in members) + { + string mateName = m.GetName(); + int cid = m.GetCharacterID(); + if (!string.IsNullOrEmpty(mateName)) + Debug.Log($"[DlgArrangeTeam] Teammate: {mateName} (id {cid})"); + var item = CreateListItem(Content_Teammates, TeammateItemPrefab, mateName, cid, true, out var listItem); + if (listItem != null) + m_TeammateItems.Add(listItem); + } + } + + // Nearby players list: filter by selected professions (multi-select); sort by each player's single profession, then by name + if (bUpdateNear && Content_Nearby != null) + { + var prefab = NearbyItemPrefab != null ? NearbyItemPrefab : TeammateItemPrefab; + ClearList(Content_Nearby, null); + var manPlayer = EC_ManMessageMono.Instance?.GetECManPlayer; + if (manPlayer != null && prefab != null) + { + HashSet filterSet = GetSelectedProfessionSet(); + var nearby = new List<(string name, int cid, int prof)>(); + var otherIds = manPlayer.GetOtherPlayerCharacterIds(); + foreach (int cid in otherIds) + { + if (hasTeam && team.GetMemberByID(cid) != null) continue; + var p = manPlayer.GetPlayer(cid, 0); + string name = p != null ? (p.GetName() ?? "") : ""; + if (string.IsNullOrEmpty(name) && p?.gameObject != null && !string.IsNullOrEmpty(p.gameObject.name)) name = p.gameObject.name; + if (string.IsNullOrEmpty(name)) name = "Player_" + cid; + int prof = (p as CECPlayer) != null ? (p as CECPlayer).GetProfession() : 0; + if (filterSet != null && !filterSet.Contains(prof)) continue; + nearby.Add((name, cid, prof)); + } + nearby.Sort((a, b) => { int c = a.prof.CompareTo(b.prof); if (c != 0) return c; return string.CompareOrdinal(a.name, b.name); }); + foreach (var t in nearby) + { + Debug.Log($"[DlgArrangeTeam] Nearby: {t.name} (id {t.cid})"); + CreateListItem(Content_Nearby, prefab, t.name, t.cid, false, out _); + } + } + } + + // Button states + if (m_pBtn_Leave != null) m_pBtn_Leave.interactable = hasTeam; + if (m_pBtn_Disband != null) m_pBtn_Disband.interactable = hasTeam && isLeader; + if (m_pBtn_Kick != null) m_pBtn_Kick.interactable = hasTeam && isLeader; + if (m_pBtn_Invite != null) m_pBtn_Invite.interactable = isLeader; + if (m_pBtn_MakeLeader != null) m_pBtn_MakeLeader.interactable = hasTeam && isLeader; + if (m_pBtn_Refresh != null) m_pBtn_Refresh.interactable = true; + + if (m_pChk_Random != null) m_pChk_Random.interactable = hasTeam && isLeader; + if (m_pChk_Free != null) m_pChk_Free.interactable = hasTeam && isLeader; + if (hasTeam && isLeader) + { + ushort pick = team.GetPickupFlag(); + if (m_pChk_Random != null) m_pChk_Random.SetIsOnWithoutNotify(pick != 0); + if (m_pChk_Free != null) m_pChk_Free.SetIsOnWithoutNotify(pick == 0); + } + + return true; + } + + private static void ClearList(RectTransform content, List items) + { + if (content == null) return; + if (items != null) items.Clear(); + for (int i = content.childCount - 1; i >= 0; i--) + { + var child = content.GetChild(i); + if (child != null && child.gameObject != null) + Destroy(child.gameObject); + } + } + + private GameObject CreateListItem(RectTransform content, GameObject prefab, string displayName, int characterId, bool isTeammate, out DlgArrangeTeamListItem listItem) + { + listItem = null; + var go = Instantiate(prefab, content); + go.SetActive(true); + var comp = go.GetComponent(); + if (comp != null) + { + comp.Set(displayName, characterId); + listItem = comp; + if (isTeammate) + comp.SetClickCallback(OnTeammateItemSelected); + else + { + comp.SetClickCallback(OnNearbyItemSelected); + comp.SetInviteCallback(OnNearbyItemInvite); + } + } + return go; + } + + private void OnTeammateItemSelected(DlgArrangeTeamListItem item) + { + m_selectedTeammateId = item.CharacterId; + foreach (var i in m_TeammateItems) + i.SetSelected(i == item); + } + + private void OnNearbyItemSelected(DlgArrangeTeamListItem item) + { + if (m_pInput_Name != null) + m_pInput_Name.text = !string.IsNullOrEmpty(item.DisplayName) ? item.DisplayName : item.CharacterId.ToString(); + } + + private void OnNearbyItemInvite(DlgArrangeTeamListItem item) + { + if (item?.CharacterId > 0) + UnityGameSession.c2s_CmdTeamInvite(item.CharacterId); + } + + /// Invite by name/ID in input field. Public so you can assign to Button.onClick in Inspector. + public void OnInvite() + { + CECHostPlayer host = GetHostPlayer(); + if (host == null) return; + + int idPlayer = ResolveInviteTarget(); + if (idPlayer <= 0) return; + UnityGameSession.c2s_CmdTeamInvite(idPlayer); + } + + private int ResolveInviteTarget() + { + if (m_pInput_Name == null || string.IsNullOrWhiteSpace(m_pInput_Name.text)) return 0; + string raw = m_pInput_Name.text.Trim(); + if (int.TryParse(raw, out int id) && id > 0) return id; + var manPlayer = EC_ManMessageMono.Instance?.GetECManPlayer; + if (manPlayer == null) return 0; + var otherIds = manPlayer.GetOtherPlayerCharacterIds(); + foreach (int cid in otherIds) + { + var p = manPlayer.GetPlayer(cid, 0); + string name = p?.GetName(); + if (!string.IsNullOrEmpty(name) && string.Equals(name, raw, System.StringComparison.OrdinalIgnoreCase)) + return cid; + } + return 0; + } + + /// Kick selected teammate. Public so you can assign to Button.onClick in Inspector. + public void OnKick() + { + int idMember = GetSelectedTeammateId(); + if (idMember <= 0) + { + Debug.Log("[DlgArrangeTeam] Kick: select a teammate first (click a row in the list)."); + return; + } + Debug.Log($"[DlgArrangeTeam] Kick member id={idMember}"); + UnityGameSession.c2s_CmdTeamKickMember(idMember); + } + + /// Leave team. Public so you can assign to Button.onClick in Inspector. + public void OnLeave() + { + if (UnityGameSession.Instance == null) + { + Debug.LogWarning("[DlgArrangeTeam] OnLeave: UnityGameSession.Instance is null. Cannot send leave command."); + return; + } + Debug.Log("[DlgArrangeTeam] Leave team"); + UnityGameSession.c2s_CmdTeamLeaveParty(); + Show(false); + } + + /// Disband team (leader). Uses C2S TEAM_DISMISS_PARTY, not leave. + public void OnDisband() + { + if (UnityGameSession.Instance == null) + { + Debug.LogWarning("[DlgArrangeTeam] OnDisband: UnityGameSession.Instance is null. Cannot send disband command."); + return; + } + Debug.Log("[DlgArrangeTeam] Disband team"); + UnityGameSession.c2s_CmdTeamDismissParty(); + Show(false); + } + + /// Make selected member leader. Public so you can assign to Button.onClick in Inspector. + public void OnMakeLeader() + { + int idMember = GetSelectedTeammateId(); + if (idMember <= 0) + { + Debug.Log("[DlgArrangeTeam] Make leader: select a teammate first (click a row in the list)."); + return; + } + Debug.Log($"[DlgArrangeTeam] Make leader id={idMember}"); + UnityGameSession.c2s_CmdTeamChangeLeader(idMember); + } + + /// Refresh teammate and nearby lists. Public so you can assign to Button.onClick in Inspector. + public void OnRefresh() + { + UpdateTeam(true); + } + + private void OnPickupModeChanged(short mode) + { + UnityGameSession.c2s_CmdTeamSetPickupFlag(mode); + } + + private int GetSelectedTeammateId() + { + return m_selectedTeammateId; + } + } +} diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs.meta b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs.meta new file mode 100644 index 0000000000..e6c94767fb --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgArrangeTeam.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 52c15f7b28624ef4697b1f7eb3a0d17c \ No newline at end of file