lobby tweaking
This commit is contained in:
@@ -461,6 +461,7 @@ namespace MegaKoop.Steam
|
|||||||
public void OpenFriendsOverlay() { Debug.Log("[SteamLobbyService] OpenFriendsOverlay stub"); }
|
public void OpenFriendsOverlay() { Debug.Log("[SteamLobbyService] OpenFriendsOverlay stub"); }
|
||||||
public IReadOnlyList<(string steamId, string name, bool online)> GetFriends() => new List<(string, string, bool)>();
|
public IReadOnlyList<(string steamId, string name, bool online)> GetFriends() => new List<(string, string, bool)>();
|
||||||
public void InviteFriendBySteamId(string steamId) { Debug.Log("[SteamLobbyService] InviteFriendBySteamId stub"); }
|
public void InviteFriendBySteamId(string steamId) { Debug.Log("[SteamLobbyService] InviteFriendBySteamId stub"); }
|
||||||
|
public bool IsKickedLocal() => false;
|
||||||
public IReadOnlyList<(string steamId, string name, bool isReady, bool isHost)> GetMembers() => new List<(string,string,bool,bool)>();
|
public IReadOnlyList<(string steamId, string name, bool isReady, bool isHost)> GetMembers() => new List<(string,string,bool,bool)>();
|
||||||
public void JoinLobbyByCode(string code) { _isInLobby = true; IsHost = false; LobbyCode = code; OnLobbyEntered?.Invoke(); }
|
public void JoinLobbyByCode(string code) { _isInLobby = true; IsHost = false; LobbyCode = code; OnLobbyEntered?.Invoke(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,9 +64,17 @@ namespace MegaKoop.UI
|
|||||||
private Button btnLeaveLobby;
|
private Button btnLeaveLobby;
|
||||||
private Button btnBackFromLobby;
|
private Button btnBackFromLobby;
|
||||||
|
|
||||||
|
[Header("Player Ready Styling")]
|
||||||
|
[SerializeField] private Color readyBorderColor = new Color(0.2f, 0.82f, 0.35f, 1f);
|
||||||
|
[SerializeField] private Color notReadyBorderColor = new Color(0.85f, 0.25f, 0.25f, 1f);
|
||||||
|
[SerializeField] private float avatarBorderThickness = 12f;
|
||||||
|
|
||||||
// Selection
|
// Selection
|
||||||
private string selectedPlayerSteamId = string.Empty;
|
private string selectedPlayerSteamId = string.Empty;
|
||||||
|
|
||||||
|
// Cached readiness state per member
|
||||||
|
private readonly Dictionary<string, bool> memberReadyCache = new Dictionary<string, bool>();
|
||||||
|
|
||||||
// Steam service
|
// Steam service
|
||||||
private SteamLobbyService steam;
|
private SteamLobbyService steam;
|
||||||
|
|
||||||
@@ -364,6 +372,7 @@ namespace MegaKoop.UI
|
|||||||
private void OnLobbyCreated()
|
private void OnLobbyCreated()
|
||||||
{
|
{
|
||||||
selectedPlayerSteamId = string.Empty;
|
selectedPlayerSteamId = string.Empty;
|
||||||
|
memberReadyCache.Clear();
|
||||||
UpdateUIFromSteam();
|
UpdateUIFromSteam();
|
||||||
// Auto-open invite overlay for the host
|
// Auto-open invite overlay for the host
|
||||||
if (steam != null && steam.IsInLobby && steam.IsHost)
|
if (steam != null && steam.IsInLobby && steam.IsHost)
|
||||||
@@ -376,6 +385,7 @@ namespace MegaKoop.UI
|
|||||||
private void OnLobbyEntered()
|
private void OnLobbyEntered()
|
||||||
{
|
{
|
||||||
selectedPlayerSteamId = string.Empty;
|
selectedPlayerSteamId = string.Empty;
|
||||||
|
memberReadyCache.Clear();
|
||||||
UpdateUIFromSteam();
|
UpdateUIFromSteam();
|
||||||
// Auto-open invite overlay if we are the host entering our lobby
|
// Auto-open invite overlay if we are the host entering our lobby
|
||||||
if (steam != null && steam.IsInLobby && steam.IsHost)
|
if (steam != null && steam.IsInLobby && steam.IsHost)
|
||||||
@@ -388,6 +398,7 @@ namespace MegaKoop.UI
|
|||||||
private void OnLobbyLeft()
|
private void OnLobbyLeft()
|
||||||
{
|
{
|
||||||
selectedPlayerSteamId = string.Empty;
|
selectedPlayerSteamId = string.Empty;
|
||||||
|
memberReadyCache.Clear();
|
||||||
UpdateUIFromSteam();
|
UpdateUIFromSteam();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,9 +438,24 @@ namespace MegaKoop.UI
|
|||||||
if (av)
|
if (av)
|
||||||
{
|
{
|
||||||
var img = av.GetComponent<Image>();
|
var img = av.GetComponent<Image>();
|
||||||
if (img && steam.TryGetAvatarSprite(steamId, out var spr2, true))
|
if (img)
|
||||||
{
|
{
|
||||||
img.sprite = spr2; img.color = Color.white; img.preserveAspect = true;
|
if (steam.TryGetAvatarSprite(steamId, out var spr2, true))
|
||||||
|
{
|
||||||
|
img.sprite = spr2;
|
||||||
|
img.color = Color.white;
|
||||||
|
img.preserveAspect = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
img.sprite = null;
|
||||||
|
img.color = new Color(0.3f, 0.6f, 0.3f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetMemberReadyState(steamId, out var isReady))
|
||||||
|
{
|
||||||
|
ApplyReadyBorder(img, isReady);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -572,12 +598,19 @@ namespace MegaKoop.UI
|
|||||||
{
|
{
|
||||||
if (IsHost || !IsInLobby || steam == null) return;
|
if (IsHost || !IsInLobby || steam == null) return;
|
||||||
var members = GetSteamMembers();
|
var members = GetSteamMembers();
|
||||||
|
CacheMemberReadyStates(members);
|
||||||
var localId = steam.LocalSteamIdString;
|
var localId = steam.LocalSteamIdString;
|
||||||
bool current = false;
|
bool current = false;
|
||||||
var me = members.FirstOrDefault(m => m.steamId == localId);
|
var me = members.FirstOrDefault(m => m.steamId == localId);
|
||||||
// When tuple is default, steamId will be null; guard
|
// When tuple is default, steamId will be null; guard
|
||||||
if (me.steamId == localId) current = me.isReady;
|
if (me.steamId == localId) current = me.isReady;
|
||||||
steam.SetReady(!current);
|
bool newReadyState = !current;
|
||||||
|
steam.SetReady(newReadyState);
|
||||||
|
if (!string.IsNullOrEmpty(localId))
|
||||||
|
{
|
||||||
|
memberReadyCache[localId] = newReadyState;
|
||||||
|
UpdateAvatarUIForSteamId(localId);
|
||||||
|
}
|
||||||
UpdateUIFromSteam();
|
UpdateUIFromSteam();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,7 +697,15 @@ namespace MegaKoop.UI
|
|||||||
|
|
||||||
private void UpdateVisibility()
|
private void UpdateVisibility()
|
||||||
{
|
{
|
||||||
if (btnStartGame) btnStartGame.gameObject.SetActive(IsInLobby && IsHost);
|
if (btnStartGame)
|
||||||
|
{
|
||||||
|
bool canHostStart = IsInLobby && IsHost;
|
||||||
|
btnStartGame.gameObject.SetActive(canHostStart);
|
||||||
|
if (btnStartGame.gameObject.activeSelf)
|
||||||
|
{
|
||||||
|
btnStartGame.interactable = AreAllPlayersReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (btnLeaveLobby) btnLeaveLobby.gameObject.SetActive(IsInLobby);
|
if (btnLeaveLobby) btnLeaveLobby.gameObject.SetActive(IsInLobby);
|
||||||
if (btnToggleReady) btnToggleReady.gameObject.SetActive(IsInLobby && !IsHost);
|
if (btnToggleReady) btnToggleReady.gameObject.SetActive(IsInLobby && !IsHost);
|
||||||
if (btnInviteFriends) btnInviteFriends.gameObject.SetActive(IsInLobby && IsHost);
|
if (btnInviteFriends) btnInviteFriends.gameObject.SetActive(IsInLobby && IsHost);
|
||||||
@@ -681,6 +722,137 @@ namespace MegaKoop.UI
|
|||||||
return steam != null ? steam.GetMembers().ToList() : new List<(string, string, bool, bool)>();
|
return steam != null ? steam.GetMembers().ToList() : new List<(string, string, bool, bool)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CacheMemberReadyStates(IEnumerable<(string steamId, string name, bool isReady, bool isHost)> members)
|
||||||
|
{
|
||||||
|
memberReadyCache.Clear();
|
||||||
|
if (members == null) return;
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(member.steamId)) continue;
|
||||||
|
memberReadyCache[member.steamId] = member.isReady;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetMemberReadyState(string steamId, out bool isReady)
|
||||||
|
{
|
||||||
|
isReady = false;
|
||||||
|
if (string.IsNullOrEmpty(steamId)) return false;
|
||||||
|
|
||||||
|
if (memberReadyCache.TryGetValue(steamId, out var cachedReady))
|
||||||
|
{
|
||||||
|
isReady = cachedReady;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var members = GetSteamMembers();
|
||||||
|
CacheMemberReadyStates(members);
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (member.steamId == steamId)
|
||||||
|
{
|
||||||
|
isReady = member.isReady;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AreAllPlayersReady()
|
||||||
|
{
|
||||||
|
if (!IsInLobby) return false;
|
||||||
|
if (memberReadyCache.Count == 0)
|
||||||
|
{
|
||||||
|
CacheMemberReadyStates(GetSteamMembers());
|
||||||
|
}
|
||||||
|
if (memberReadyCache.Count == 0) return false;
|
||||||
|
foreach (var kvp in memberReadyCache)
|
||||||
|
{
|
||||||
|
if (!kvp.Value) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyReadyBorder(Image avatarImage, bool isReady)
|
||||||
|
{
|
||||||
|
if (avatarImage == null) return;
|
||||||
|
|
||||||
|
var legacyOutline = avatarImage.GetComponent<Outline>();
|
||||||
|
if (legacyOutline != null) legacyOutline.enabled = false;
|
||||||
|
|
||||||
|
var borderImage = EnsureAvatarBorderImage(avatarImage);
|
||||||
|
if (borderImage == null) return;
|
||||||
|
|
||||||
|
if (avatarBorderThickness <= 0f)
|
||||||
|
{
|
||||||
|
borderImage.enabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
borderImage.enabled = true;
|
||||||
|
borderImage.color = isReady ? readyBorderColor : notReadyBorderColor;
|
||||||
|
|
||||||
|
var avatarRect = avatarImage.rectTransform;
|
||||||
|
var borderRect = borderImage.rectTransform;
|
||||||
|
borderRect.anchorMin = avatarRect.anchorMin;
|
||||||
|
borderRect.anchorMax = avatarRect.anchorMax;
|
||||||
|
borderRect.pivot = avatarRect.pivot;
|
||||||
|
borderRect.anchoredPosition = avatarRect.anchoredPosition;
|
||||||
|
borderRect.localPosition = avatarRect.localPosition;
|
||||||
|
borderRect.localRotation = avatarRect.localRotation;
|
||||||
|
borderRect.localScale = avatarRect.localScale;
|
||||||
|
var padding = avatarBorderThickness * 2f;
|
||||||
|
borderRect.sizeDelta = avatarRect.sizeDelta + new Vector2(padding, padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image EnsureAvatarBorderImage(Image avatarImage)
|
||||||
|
{
|
||||||
|
if (avatarImage == null) return null;
|
||||||
|
var parent = avatarImage.transform.parent;
|
||||||
|
if (parent == null) return null;
|
||||||
|
|
||||||
|
const string borderName = "Image_AvatarBorder";
|
||||||
|
Transform borderTransform = null;
|
||||||
|
for (int i = 0; i < parent.childCount; i++)
|
||||||
|
{
|
||||||
|
var child = parent.GetChild(i);
|
||||||
|
if (child != null && child.name == borderName)
|
||||||
|
{
|
||||||
|
borderTransform = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (borderTransform == null)
|
||||||
|
{
|
||||||
|
var borderGO = new GameObject(borderName, typeof(RectTransform), typeof(Image));
|
||||||
|
borderTransform = borderGO.transform;
|
||||||
|
borderTransform.SetParent(parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var borderImage = borderTransform.GetComponent<Image>();
|
||||||
|
if (borderImage == null)
|
||||||
|
{
|
||||||
|
borderImage = borderTransform.gameObject.AddComponent<Image>();
|
||||||
|
}
|
||||||
|
|
||||||
|
borderImage.raycastTarget = false;
|
||||||
|
borderImage.preserveAspect = false;
|
||||||
|
borderImage.type = Image.Type.Simple;
|
||||||
|
|
||||||
|
var layoutElement = borderTransform.GetComponent<LayoutElement>();
|
||||||
|
if (layoutElement == null)
|
||||||
|
{
|
||||||
|
layoutElement = borderTransform.gameObject.AddComponent<LayoutElement>();
|
||||||
|
}
|
||||||
|
layoutElement.ignoreLayout = true;
|
||||||
|
|
||||||
|
var avatarIndex = avatarImage.transform.GetSiblingIndex();
|
||||||
|
borderTransform.SetSiblingIndex(avatarIndex);
|
||||||
|
avatarImage.transform.SetSiblingIndex(borderTransform.GetSiblingIndex() + 1);
|
||||||
|
|
||||||
|
return borderImage;
|
||||||
|
}
|
||||||
|
|
||||||
private void RebuildPlayers()
|
private void RebuildPlayers()
|
||||||
{
|
{
|
||||||
if (contentPlayers == null) return;
|
if (contentPlayers == null) return;
|
||||||
@@ -694,6 +866,7 @@ namespace MegaKoop.UI
|
|||||||
foreach (var go in toDestroy) DestroyImmediate(go);
|
foreach (var go in toDestroy) DestroyImmediate(go);
|
||||||
|
|
||||||
var members = GetSteamMembers();
|
var members = GetSteamMembers();
|
||||||
|
CacheMemberReadyStates(members);
|
||||||
if (textPlayerCount) textPlayerCount.text = $"{members.Count}/{(ddMaxPlayers ? int.Parse(ddMaxPlayers.options[ddMaxPlayers.value].text) : 4)}";
|
if (textPlayerCount) textPlayerCount.text = $"{members.Count}/{(ddMaxPlayers ? int.Parse(ddMaxPlayers.options[ddMaxPlayers.value].text) : 4)}";
|
||||||
|
|
||||||
if (members.Count == 0)
|
if (members.Count == 0)
|
||||||
@@ -735,6 +908,14 @@ namespace MegaKoop.UI
|
|||||||
var goLE = go.GetComponent<LayoutElement>(); if (!goLE) goLE = go.AddComponent<LayoutElement>();
|
var goLE = go.GetComponent<LayoutElement>(); if (!goLE) goLE = go.AddComponent<LayoutElement>();
|
||||||
goLE.minHeight = 100; goLE.flexibleWidth = 1f;
|
goLE.minHeight = 100; goLE.flexibleWidth = 1f;
|
||||||
|
|
||||||
|
var horizontalLayout = go.GetComponent<HorizontalLayoutGroup>();
|
||||||
|
if (horizontalLayout)
|
||||||
|
{
|
||||||
|
var paddingValue = Mathf.CeilToInt(avatarBorderThickness);
|
||||||
|
if (horizontalLayout.padding.left < paddingValue) horizontalLayout.padding.left = paddingValue;
|
||||||
|
if (horizontalLayout.padding.right < paddingValue) horizontalLayout.padding.right = paddingValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Children
|
// Children
|
||||||
// Find avatar inside this item (not globally)
|
// Find avatar inside this item (not globally)
|
||||||
Image avatarGO = null;
|
Image avatarGO = null;
|
||||||
@@ -786,7 +967,20 @@ namespace MegaKoop.UI
|
|||||||
avRT.anchoredPosition = Vector2.zero;
|
avRT.anchoredPosition = Vector2.zero;
|
||||||
avRT.sizeDelta = new Vector2(96, 96);
|
avRT.sizeDelta = new Vector2(96, 96);
|
||||||
var avLE = avatarGO.GetComponent<LayoutElement>() ?? avatarGO.gameObject.AddComponent<LayoutElement>();
|
var avLE = avatarGO.GetComponent<LayoutElement>() ?? avatarGO.gameObject.AddComponent<LayoutElement>();
|
||||||
avLE.minWidth = 96; avLE.minHeight = 96;
|
var avatarSize = Mathf.Max(avRT.sizeDelta.x, avRT.sizeDelta.y);
|
||||||
|
var paddedSize = avatarSize + avatarBorderThickness * 2f;
|
||||||
|
avLE.minWidth = paddedSize;
|
||||||
|
avLE.minHeight = paddedSize;
|
||||||
|
avLE.preferredWidth = paddedSize;
|
||||||
|
avLE.preferredHeight = paddedSize;
|
||||||
|
|
||||||
|
if (goLE != null)
|
||||||
|
{
|
||||||
|
goLE.minWidth = Mathf.Max(goLE.minWidth, paddedSize);
|
||||||
|
goLE.preferredWidth = Mathf.Max(goLE.preferredWidth, paddedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyReadyBorder(avatarGO, ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selection handler
|
// Selection handler
|
||||||
|
|||||||
Reference in New Issue
Block a user