diff --git a/.idea/.idea.HALLUCINATE/.idea/workspace.xml b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
index 4698e1a3..61e14203 100644
--- a/.idea/.idea.HALLUCINATE/.idea/workspace.xml
+++ b/.idea/.idea.HALLUCINATE/.idea/workspace.xml
@@ -6,12 +6,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -149,7 +162,7 @@
-
+
diff --git a/Assets/Scripts/Duy/PlayerInputData.cs b/Assets/Scripts/Duy/PlayerInputData.cs
index c95009d6..b1396da0 100644
--- a/Assets/Scripts/Duy/PlayerInputData.cs
+++ b/Assets/Scripts/Duy/PlayerInputData.cs
@@ -1,6 +1,12 @@
using Fusion;
+using UnityEngine;
-public partial struct _PlayerInputData : INetworkInput
+namespace OnlyScove.Scripts
{
- public float direction;
-}
\ No newline at end of file
+ public struct PlayerInputData : INetworkInput
+ {
+ public Vector2 Direction;
+ public NetworkBool sprint;
+ public Quaternion rot;
+ }
+}
diff --git a/Assets/Scripts/Duy/_BasicSpawner.cs b/Assets/Scripts/Duy/_BasicSpawner.cs
index 2f9534e2..00af20bf 100644
--- a/Assets/Scripts/Duy/_BasicSpawner.cs
+++ b/Assets/Scripts/Duy/_BasicSpawner.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Fusion;
using Fusion.Sockets;
using UnityEngine;
+using OnlyScove.Scripts;
namespace Hallucinate.UI
{
@@ -42,7 +43,6 @@ namespace Hallucinate.UI
{
OnJoinStartedEvent?.Invoke();
- // Sử dụng SessionProperties để lưu mật khẩu
var customProps = new Dictionary();
if (!string.IsNullOrEmpty(password))
{
@@ -88,13 +88,6 @@ namespace Hallucinate.UI
public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
{
- if (runner.IsServer)
- {
- Vector2 spawnPosition = (player == runner.LocalPlayer) ? new Vector2(-8, 0) : new Vector2(8, 0);
- var networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
- _spawnedCharacters.Add(player, networkPlayerObject);
- }
-
if (player == runner.LocalPlayer)
{
var pdm = FindFirstObjectByType<_PlayerDataManager>();
@@ -104,12 +97,21 @@ namespace Hallucinate.UI
{
Name = LocalPlayerProfile.Name,
Role = LocalPlayerProfile.Role,
+ IsReady = false
};
pdm.RPC_UpdatePlayerMetaData(player, metaData);
}
}
}
+ public void StartGame()
+ {
+ if (_runner.IsServer)
+ {
+ _runner.LoadScene("Main Scene");
+ }
+ }
+
public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
{
if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
@@ -132,8 +134,27 @@ namespace Hallucinate.UI
public void OnInput(NetworkRunner runner, NetworkInput input)
{
- var data = new _PlayerInputData();
- data.direction = Input.GetAxis("Vertical");
+ var data = new PlayerInputData();
+
+ // Try to get input from the local player's InputReader
+ if (PlayerStateMachine.Local != null && PlayerStateMachine.Local.Input != null)
+ {
+ var inputReader = PlayerStateMachine.Local.Input;
+ data.Direction = inputReader.MoveInput;
+ data.sprint = inputReader.IsSprintHeld;
+
+ if (PlayerStateMachine.Local.Cam != null)
+ {
+ data.rot = PlayerStateMachine.Local.Cam.PlanarRotation;
+ }
+ }
+ else
+ {
+ // Fallback to basic input if player not spawned or InputReader missing
+ data.Direction = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
+ data.sprint = Input.GetKey(KeyCode.LeftShift);
+ }
+
input.Set(data);
}
@@ -149,7 +170,31 @@ namespace Hallucinate.UI
public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary data) { }
public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
- public void OnSceneLoadDone(NetworkRunner runner) { }
+
+ public void OnSceneLoadDone(NetworkRunner runner)
+ {
+ string currentSceneName = UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
+
+ if (runner.IsServer && currentSceneName == "Main Scene")
+ {
+ foreach (var player in runner.ActivePlayers)
+ {
+ Vector2 spawnPosition = (player == runner.LocalPlayer) ? new Vector2(-8, 0) : new Vector2(8, 0);
+ var networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
+ _spawnedCharacters.Add(player, networkPlayerObject);
+ }
+ }
+
+ if (currentSceneName == "Main Scene")
+ {
+ if (UIManager.Instance != null) UIManager.Instance.OnGameStarted();
+ }
+ else if (currentSceneName == "Lobby" || currentSceneName == "Menu")
+ {
+ if (UIManager.Instance != null) UIManager.Instance.OnBackToMenu();
+ }
+ }
+
public void OnSceneLoadStart(NetworkRunner runner) { }
}
}
diff --git a/Assets/Scripts/Duy/_PlayerDataManager.cs b/Assets/Scripts/Duy/_PlayerDataManager.cs
index 5762967d..10efcd46 100644
--- a/Assets/Scripts/Duy/_PlayerDataManager.cs
+++ b/Assets/Scripts/Duy/_PlayerDataManager.cs
@@ -6,23 +6,29 @@ public struct _PlayerMetaData : INetworkStruct
{
public NetworkString<_16> Name;
public _Role Role;
+ public NetworkBool IsReady;
}
public class _PlayerDataManager : NetworkBehaviour
{
- // biến này của Fusion sẽ tự động đồng bộ giữa các client và host,
- // khi có thay đổi sẽ tự động cập nhật ở tất cả các bên
[Networked]
public NetworkDictionary Players => default;
- // RPC: phương thức này sẽ được gọi từ client hoặc
- // host để cập nhật thông tin player, sau đó sẽ được gửi
- // đến state authority (host) để xử lý và đồng bộ lại cho tất cả các client
[Rpc(RpcSources.All, RpcTargets.StateAuthority)]
public void RPC_UpdatePlayerMetaData(PlayerRef playerRef, _PlayerMetaData metaData)
{
Players.Set(playerRef, metaData);
}
+
+ [Rpc(RpcSources.All, RpcTargets.StateAuthority)]
+ public void RPC_SetReady(PlayerRef playerRef, bool ready)
+ {
+ if (Players.TryGet(playerRef, out var data))
+ {
+ data.IsReady = ready;
+ Players.Set(playerRef, data);
+ }
+ }
public bool TryGetPlayerMetaData(PlayerRef playerRef, out _PlayerMetaData metaData)
{
diff --git a/Assets/Scripts/Fusion/BasicSpawner.cs b/Assets/Scripts/Fusion/BasicSpawner.cs
deleted file mode 100644
index 7f9488a9..00000000
--- a/Assets/Scripts/Fusion/BasicSpawner.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Linq;
-using Fusion;
-using Fusion.Sockets;
-using UnityEngine;
-using UnityEngine.SceneManagement;
-using Random = UnityEngine.Random;
-
-// Struct input đồng bộ giữa Spawner, Movement và StateMachine
-public struct PlayerInputData : INetworkInput
-{
- public Vector2 Direction;
- public Quaternion rot;
- public bool sprint;
-}
-
-public class BasicSpawner : MonoBehaviour, INetworkRunnerCallbacks
-{
- private NetworkRunner _runner;
- private string _roomName = "Room1";
-
- public LobbyManager LobbyManager;
- [SerializeField] private NetworkPrefabRef _playerPrefab;
- private Dictionary _spawnedCharacters = new Dictionary();
-
- // Thông tin profile local
- public PlayerProfile LocalPlayerProfile { get; private set; }
- public void SetLocalPlayerProfile(PlayerProfile profile)
- {
- LocalPlayerProfile = profile;
- }
-
- private void Awake()
- {
- if (_runner == null) _runner = gameObject.AddComponent();
- DontDestroyOnLoad(gameObject);
- }
-
- // Khởi tạo Game (Host/Client)
- public async Task StartGame(GameMode mode, string sessionName = "TestRoom")
- {
- Debug.Log($"Fusion: Đang khởi tạo kết nối với Mode: {mode} | Phòng: {sessionName}");
-
- if (_runner == null) _runner = gameObject.AddComponent();
- _runner.ProvideInput = true;
-
- var sceneManager = gameObject.GetComponent();
- if (sceneManager == null) sceneManager = gameObject.AddComponent();
-
- var result = await _runner.StartGame(new StartGameArgs()
- {
- GameMode = mode,
- SessionName = sessionName,
- Scene = SceneRef.FromIndex(1),
- SceneManager = sceneManager
- });
-
- if (result.Ok)
- {
- Debug.Log($"Fusion thành công: Đã vào phòng {sessionName}");
- }
- else
- {
- Debug.LogError($"Fusion thất bại: Lý do: {result.ShutdownReason}");
- if (_runner != null)
- {
- Destroy(_runner);
- _runner = null;
- }
- }
- }
-
- private void OnGUI()
- {
- if (_runner == null || !_runner.IsRunning)
- {
- float width = 400;
- float height = 300;
- float x = (Screen.width - width) / 2f;
- float y = (Screen.height - height) / 2f;
-
- GUI.Box(new Rect(x, y, width, height), "FUSION MULTIPLAYER");
-
- float innerX = x + 20;
- float innerY = y + 40;
- float contentWidth = width - 40;
-
- GUI.Label(new Rect(innerX, innerY, 100, 30), "Tên phòng:");
- _roomName = GUI.TextField(new Rect(innerX + 100, innerY, contentWidth - 100, 30), _roomName);
-
- if (GUI.Button(new Rect(innerX, innerY + 50, contentWidth, 60), "VÀO PHÒNG\n(Tự động Host/Client)"))
- {
- _ = StartGame(GameMode.AutoHostOrClient, _roomName);
- }
-
- if (GUI.Button(new Rect(innerX, innerY + 120, (contentWidth / 2) - 5, 50), "Tạo phòng\n(Host)"))
- {
- _ = StartGame(GameMode.Host, _roomName);
- }
-
- if (GUI.Button(new Rect(innerX + (contentWidth / 2) + 5, innerY + 120, (contentWidth / 2) - 5, 50), "Tham gia\n(Client)"))
- {
- _ = StartGame(GameMode.Client, _roomName);
- }
-
- GUI.Label(new Rect(innerX, innerY + 180, contentWidth, 50), "Gợi ý: Nhập cùng tên phòng để chơi chung.\nNếu phòng chưa có, máy sẽ tự tạo mới.");
- }
- else
- {
- string region = (_runner.SessionInfo != null && _runner.SessionInfo.IsValid) ? _runner.SessionInfo.Region : "Connecting...";
- int playerCount = 0;
- foreach (var p in _runner.ActivePlayers) playerCount++;
-
- string info = $"Mode: {_runner.GameMode} | Region: {region} | Players: {playerCount}";
- GUI.Box(new Rect(10, 10, 400, 30), info);
-
- if (GUI.Button(new Rect(10, 50, 100, 30), "Thoát"))
- {
- _runner.Shutdown();
- }
- }
- }
-
- public void OnPlayerJoined(NetworkRunner runner, PlayerRef player)
- {
- if (runner.IsServer)
- {
- Vector3 spawnPosition = new Vector3(Random.Range(-10f, 10f), 2f, Random.Range(-10f, 10f));
- spawnPosition += new Vector3(player.RawEncoded % 3, 0, player.RawEncoded % 3);
-
- var networkPlayerObject = runner.Spawn(_playerPrefab, spawnPosition, Quaternion.identity, player);
- runner.SetPlayerObject(player, networkPlayerObject);
- _spawnedCharacters.Add(player, networkPlayerObject);
- }
- }
-
- public void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
- {
- if (_spawnedCharacters.TryGetValue(player, out NetworkObject networkObject))
- {
- if (networkObject != null) runner.Despawn(networkObject);
- _spawnedCharacters.Remove(player);
- }
- }
-
- public void OnInput(NetworkRunner runner, NetworkInput input)
- {
- var data = new PlayerInputData();
-
- // ĐỌC TRỰC TIẾP: Không dùng Buffer để tránh bị trôi phím
- data.Direction = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
- data.sprint = Input.GetKey(KeyCode.LeftShift);
-
- if (OnlyScove.Scripts.PlayerStateMachine.Local != null)
- {
- var sm = OnlyScove.Scripts.PlayerStateMachine.Local;
- if (sm.Cam != null) data.rot = sm.Cam.PlanarRotation;
- else data.rot = sm.NetworkedCameraRotation;
- }
-
- input.Set(data);
- }
-
- public void OnSessionListUpdated(NetworkRunner runner, List sessionList)
- {
- if (LobbyManager != null) LobbyManager.DisplayRoomList(sessionList);
- // UI.UIEventBus.TriggerRoomListUpdate();
- }
-
- public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { }
- public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { }
- public void OnConnectedToServer(NetworkRunner runner) { }
- public void OnDisconnectedFromServer(NetworkRunner runner, NetDisconnectReason reason) { }
- public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { }
- public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { }
- public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { }
- public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary data) { }
- public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { }
- public void OnSceneLoadDone(NetworkRunner runner) { }
- public void OnSceneLoadStart(NetworkRunner runner) { }
- public void OnObjectExitAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
- public void OnObjectEnterAOI(NetworkRunner runner, NetworkObject obj, PlayerRef player) { }
- public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ReliableKey key, ArraySegment data) { }
- public void OnReliableDataProgress(NetworkRunner runner, PlayerRef player, ReliableKey key, float progress) { }
-}
\ No newline at end of file
diff --git a/Assets/Scripts/Fusion/BasicSpawner.cs.meta b/Assets/Scripts/Fusion/BasicSpawner.cs.meta
deleted file mode 100644
index cf96aa18..00000000
--- a/Assets/Scripts/Fusion/BasicSpawner.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 44bfaa339c82069418e72a14479a0212
\ No newline at end of file
diff --git a/Assets/Scripts/Fusion/LobbyHelper.cs b/Assets/Scripts/Fusion/LobbyHelper.cs
deleted file mode 100644
index e129507a..00000000
--- a/Assets/Scripts/Fusion/LobbyHelper.cs
+++ /dev/null
@@ -1 +0,0 @@
-// File này hiện tại không còn nội dung, có thể xóa đi hoặc để trống.
diff --git a/Assets/Scripts/Fusion/LobbyHelper.cs.meta b/Assets/Scripts/Fusion/LobbyHelper.cs.meta
deleted file mode 100644
index ce238440..00000000
--- a/Assets/Scripts/Fusion/LobbyHelper.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 2bf2c7f159565794b87d6f3ca2eb2976
\ No newline at end of file
diff --git a/Assets/Scripts/Fusion/LobbyManager.cs b/Assets/Scripts/Fusion/LobbyManager.cs
deleted file mode 100644
index 996e94ec..00000000
--- a/Assets/Scripts/Fusion/LobbyManager.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Collections.Generic;
-using Fusion;
-using UnityEngine;
-
-public class LobbyManager : MonoBehaviour
-{
- [Header("UI References")]
- public Transform roomListContent; // Ô chứa danh sách phòng (nếu có)
-
- public void DisplayRoomList(List sessionList)
- {
- Debug.Log($"Lobby Update: Đang tìm thấy {sessionList.Count} phòng.");
-
- // Xóa danh sách cũ (nếu bạn làm UI)
- /*
- foreach (Transform child in roomListContent) {
- Destroy(child.gameObject);
- }
- */
-
- // Hiển thị danh sách mới
- foreach (var session in sessionList)
- {
- Debug.Log($"- Phòng: {session.Name} | Người chơi: {session.PlayerCount}/{session.MaxPlayers}");
- // Ở đây bạn sẽ Instantiate các Button đại diện cho mỗi phòng
- }
- }
-}
diff --git a/Assets/Scripts/Fusion/LobbyManager.cs.meta b/Assets/Scripts/Fusion/LobbyManager.cs.meta
deleted file mode 100644
index a269a8c8..00000000
--- a/Assets/Scripts/Fusion/LobbyManager.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: b5aeb4670d7bf41499d3aaf409820260
\ No newline at end of file
diff --git a/Assets/Scripts/Fusion/PlayerProfile.cs b/Assets/Scripts/Fusion/PlayerProfile.cs
deleted file mode 100644
index 14fb3da9..00000000
--- a/Assets/Scripts/Fusion/PlayerProfile.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using UnityEngine;
-
-// Enum các loại nhân vật
-public enum CharacterClass
-{
- Warrior,
- Mage,
- Archer
-}
-
-// Lớp quản lý thông tin nhân vật local
-[System.Serializable]
-public class PlayerProfile
-{
- public string Name = "Player";
- public CharacterClass Class = CharacterClass.Warrior;
-}
diff --git a/Assets/Scripts/Fusion/PlayerProfile.cs.meta b/Assets/Scripts/Fusion/PlayerProfile.cs.meta
deleted file mode 100644
index 2b9acb62..00000000
--- a/Assets/Scripts/Fusion/PlayerProfile.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8a1452ae101af8e43b94c2c778a70fe0
\ No newline at end of file
diff --git a/Assets/Scripts/Fusion.meta b/Assets/Scripts/Game.meta
similarity index 77%
rename from Assets/Scripts/Fusion.meta
rename to Assets/Scripts/Game.meta
index 13389f28..1aacaf90 100644
--- a/Assets/Scripts/Fusion.meta
+++ b/Assets/Scripts/Game.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 6aa851b2c7ee553439ee0065f77665cb
+guid: 1a38893b1d8574b45bce269c39824bd6
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/Scripts/Game/EloSystem.cs b/Assets/Scripts/Game/EloSystem.cs
new file mode 100644
index 00000000..276d16ca
--- /dev/null
+++ b/Assets/Scripts/Game/EloSystem.cs
@@ -0,0 +1,64 @@
+using UnityEngine;
+using Fusion;
+
+namespace Hallucinate.Game
+{
+ public static class EloSystem
+ {
+ public static EloResult Calculate(
+ int ratingA, int ratingB,
+ int gamesPlayedA, int gamesPlayedB,
+ float resultA) // 1=win, 0=lose, 0.5=draw
+ {
+ float eA = 1f / (1f + Mathf.Pow(10f, (ratingB - ratingA) / 400f));
+ int kA = GetK(ratingA, gamesPlayedA);
+ int kB = GetK(ratingB, gamesPlayedB);
+
+ int nA = Mathf.Max(100, Mathf.RoundToInt(ratingA + kA * (resultA - eA)));
+ int nB = Mathf.Max(100, Mathf.RoundToInt(ratingB + kB * ((1 - resultA) - (1 - eA))));
+
+ return new EloResult(nA, nB, nA - ratingA, nB - ratingB);
+ }
+
+ private static int GetK(int r, int g) =>
+ g < 30 ? 40 : r < 1200 ? 32 : r < 2000 ? 24 : 16;
+
+ public static string GetRank(int rating)
+ {
+ if (rating < 800) return "Iron";
+ if (rating < 1000) return "Bronze";
+ if (rating < 1200) return "Silver";
+ if (rating < 1500) return "Gold";
+ if (rating < 1800) return "Platinum";
+ if (rating < 2100) return "Diamond";
+ return "Master";
+ }
+
+ public static string GetRankColor(int rating)
+ {
+ if (rating < 800) return "#8A8A8A";
+ if (rating < 1000) return "#CD7F32";
+ if (rating < 1200) return "#C0C0C0";
+ if (rating < 1500) return "#FFD700";
+ if (rating < 1800) return "#4DC8A0";
+ if (rating < 2100) return "#7B6EE8";
+ return "#E84D8A";
+ }
+ }
+
+ public struct EloResult : INetworkStruct
+ {
+ public int NewRatingA;
+ public int NewRatingB;
+ public int DeltaA;
+ public int DeltaB;
+
+ public EloResult(int nA, int nB, int dA, int dB)
+ {
+ NewRatingA = nA;
+ NewRatingB = nB;
+ DeltaA = dA;
+ DeltaB = dB;
+ }
+ }
+}
diff --git a/Assets/Scripts/Game/EloSystem.cs.meta b/Assets/Scripts/Game/EloSystem.cs.meta
new file mode 100644
index 00000000..9c8dcfc1
--- /dev/null
+++ b/Assets/Scripts/Game/EloSystem.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 366843378ad652a41aeb5972186ebe18
\ No newline at end of file
diff --git a/Assets/Scripts/Network.meta b/Assets/Scripts/Network.meta
new file mode 100644
index 00000000..2654083c
--- /dev/null
+++ b/Assets/Scripts/Network.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f96029db35b52ba4182888a7f14d2ea7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Network/MatchResultManager.cs b/Assets/Scripts/Network/MatchResultManager.cs
new file mode 100644
index 00000000..d36207d2
--- /dev/null
+++ b/Assets/Scripts/Network/MatchResultManager.cs
@@ -0,0 +1,77 @@
+using Fusion;
+using UnityEngine;
+using Hallucinate.Game;
+using Hallucinate.UI;
+using System.Linq;
+
+namespace Hallucinate.Network
+{
+ public class MatchResultManager : NetworkBehaviour
+ {
+ public static MatchResultManager Instance { get; private set; }
+
+ public override void Spawned()
+ {
+ if (Object.HasStateAuthority) Instance = this;
+ }
+
+ [Rpc(RpcSources.StateAuthority, RpcTargets.All)]
+ public void RPC_BroadcastResult(PlayerRef winner, EloResult eloResult)
+ {
+ Debug.Log($"Game Over! Winner: {winner}. Elo updated.");
+
+ // Update local Elo display and show Result UI
+ if (Runner.LocalPlayer == winner)
+ {
+ ShowResultUI(true, eloResult.DeltaA, eloResult.NewRatingA);
+ }
+ else
+ {
+ ShowResultUI(false, eloResult.DeltaB, eloResult.NewRatingB);
+ }
+ }
+
+ private void ShowResultUI(bool isWin, int delta, int newRating)
+ {
+ var hud = FindFirstObjectByType();
+ if (hud != null)
+ {
+ // In a real scenario, we might push a new Result screen
+ // For now, let's assume HUD has a result panel
+ Debug.Log($"RESULT: {(isWin ? "WIN" : "LOSS")} | Delta: {delta} | New Rating: {newRating}");
+
+ // Save to PlayerPrefs as a dummy "Server" persistence
+ PlayerPrefs.SetInt("EloRating", newRating);
+ int gamesPlayed = PlayerPrefs.GetInt("GamesPlayed", 0);
+ PlayerPrefs.SetInt("GamesPlayed", gamesPlayed + 1);
+ PlayerPrefs.Save();
+ }
+ }
+
+ public void ProcessMatchEnd(PlayerRef winner)
+ {
+ if (!Object.HasStateAuthority) return;
+
+ // Get ratings for both players
+ // In a real game, these would come from the server/metadata
+ int ratingA = PlayerPrefs.GetInt("EloRating", 1000);
+ int ratingB = 1000; // Placeholder for opponent
+ int gamesA = PlayerPrefs.GetInt("GamesPlayed", 0);
+ int gamesB = 0; // Placeholder
+
+ float resultA = (Runner.LocalPlayer == winner) ? 1.0f : 0.0f;
+
+ EloResult elo = EloSystem.Calculate(ratingA, ratingB, gamesA, gamesB, resultA);
+
+ RPC_BroadcastResult(winner, elo);
+
+ // Shut down runner after some delay
+ Invoke(nameof(ShutdownRunner), 5.0f);
+ }
+
+ private void ShutdownRunner()
+ {
+ Runner.Shutdown();
+ }
+ }
+}
diff --git a/Assets/Scripts/Network/MatchResultManager.cs.meta b/Assets/Scripts/Network/MatchResultManager.cs.meta
new file mode 100644
index 00000000..848e604e
--- /dev/null
+++ b/Assets/Scripts/Network/MatchResultManager.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 9eac0255d30a2bb40a43ff12cdcdf960
\ No newline at end of file
diff --git a/Assets/Scripts/Player Controller/PlayerStateMachine.cs b/Assets/Scripts/Player Controller/PlayerStateMachine.cs
index 128e4b6d..8bbed301 100644
--- a/Assets/Scripts/Player Controller/PlayerStateMachine.cs
+++ b/Assets/Scripts/Player Controller/PlayerStateMachine.cs
@@ -146,14 +146,6 @@ namespace OnlyScove.Scripts
bool isRunning = Runner != null && Runner.IsRunning;
if (Object == null && isRunning) return;
- // Proxy Sync
- if (isRunning && Movement.NetworkedPosition != Vector3.zero && !Object.HasInputAuthority)
- {
- Controller.enabled = false;
- transform.position = Movement.NetworkedPosition;
- Controller.enabled = true;
- }
-
if (GetInput(out PlayerInputData data))
{
MoveInput = data.Direction;
@@ -175,10 +167,24 @@ namespace OnlyScove.Scripts
currentState?.Tick(isRunning ? Runner.DeltaTime : Time.fixedDeltaTime);
}
}
- else
+ }
+
+ public override void Render()
+ {
+ bool isRunning = Runner != null && Runner.IsRunning;
+ if (isRunning && !Object.HasInputAuthority)
{
+ // Smooth interpolation for proxies
+ if (Movement.NetworkedPosition != Vector3.zero)
+ {
+ transform.position = Vector3.Lerp(transform.position, Movement.NetworkedPosition, Runner.DeltaTime * 15f);
+ }
UpdateAnimator(Runner.DeltaTime);
}
+ else if (!isRunning)
+ {
+ UpdateAnimator(Time.deltaTime);
+ }
}
private void Update()
diff --git a/Assets/Scripts/Player Controller/PlayerStats.cs b/Assets/Scripts/Player Controller/PlayerStats.cs
index ae81db9b..627c8999 100644
--- a/Assets/Scripts/Player Controller/PlayerStats.cs
+++ b/Assets/Scripts/Player Controller/PlayerStats.cs
@@ -1,6 +1,7 @@
using UnityEngine;
using Fusion;
using System;
+using Hallucinate.Network;
namespace OnlyScove.Scripts
{
@@ -24,10 +25,23 @@ namespace OnlyScove.Scripts
void OnHealthChangedRender()
{
OnHealthChanged?.Invoke(Health);
+
+ if (Health <= 0 && Object.HasStateAuthority)
+ {
+ // Find the other player as winner
+ foreach (var player in Runner.ActivePlayers)
+ {
+ if (player != Object.InputAuthority)
+ {
+ MatchResultManager.Instance?.ProcessMatchEnd(player);
+ break;
+ }
+ }
+ }
+
if (Object.HasInputAuthority)
{
// UI Placeholder: Trigger Health UI Change
- // Example: UI.UIEventBus.TriggerHealthChange(Health / 100f);
}
}
@@ -37,7 +51,6 @@ namespace OnlyScove.Scripts
if (Object.HasInputAuthority)
{
// UI Placeholder: Trigger Stamina UI Change
- // Example: UI.UIEventBus.TriggerStaminaChange(Stamina / 100f);
}
}
diff --git a/Assets/Scripts/UI/BaseUIController.cs b/Assets/Scripts/UI/BaseUIController.cs
index ea9e766f..11b1cdd4 100644
--- a/Assets/Scripts/UI/BaseUIController.cs
+++ b/Assets/Scripts/UI/BaseUIController.cs
@@ -45,6 +45,8 @@ namespace Hallucinate.UI
return key;
}
+ public virtual void Update() { }
+
public virtual async Task PlayTransitionIn()
{
if (root == null) return;
diff --git a/Assets/Scripts/UI/HUDController.cs b/Assets/Scripts/UI/HUDController.cs
index 1dffb2e4..7ab9e928 100644
--- a/Assets/Scripts/UI/HUDController.cs
+++ b/Assets/Scripts/UI/HUDController.cs
@@ -53,7 +53,7 @@ namespace Hallucinate.UI
}
}
- public void Update()
+ public override void Update()
{
if (!_isFaded && Time.time - _lastActionTime > FADE_TIMEOUT)
{
diff --git a/Assets/Scripts/UI/LobbyController.cs b/Assets/Scripts/UI/LobbyController.cs
index 979222b6..b6a033a5 100644
--- a/Assets/Scripts/UI/LobbyController.cs
+++ b/Assets/Scripts/UI/LobbyController.cs
@@ -3,6 +3,7 @@ using UnityEngine.UIElements;
using System.Collections.Generic;
using System.Threading.Tasks;
using Fusion;
+using System.Linq;
namespace Hallucinate.UI
{
@@ -10,6 +11,7 @@ namespace Hallucinate.UI
{
private VisualTreeAsset _roomItemTemplate;
private _BasicSpawner _spawner;
+ private _PlayerDataManager _playerDataManager;
// Containers
private VisualElement _joinContainer, _createContainer, _loungeContainer, _passOverlay;
@@ -24,6 +26,11 @@ namespace Hallucinate.UI
private Label _joinPassError;
private SessionInfo _selectedSession;
+ // Lounge Elements
+ private VisualElement _playerListContainer;
+ private Button _readyBtn, _startBtn;
+ private Label _loungeRoomName;
+
public override void Initialize(VisualElement uxmlRoot, UIManager manager)
{
base.Initialize(uxmlRoot, manager);
@@ -44,27 +51,63 @@ namespace Hallucinate.UI
_joinPassInput = root.Q("JoinPassInput");
_joinPassError = root.Q