Organize custom scripts under Assets/Baba_yaga and merge Opsive folders to Assets root
This commit is contained in:
37
Assets/Baba_yaga/Game/EloData.cs
Normal file
37
Assets/Baba_yaga/Game/EloData.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Fusion;
|
||||
|
||||
namespace Baba_yaga.Game
|
||||
{
|
||||
[System.Serializable]
|
||||
[UnityEngine.Scripting.APIUpdating.MovedFrom(true, sourceNamespace: "Hallucinate.Game", sourceAssembly: "Opsive.UltimateCharacterController")]
|
||||
public struct PlayerEloData
|
||||
{
|
||||
public int Rating;
|
||||
public int GamesPlayed;
|
||||
|
||||
public PlayerEloData(int rating, int gamesPlayed)
|
||||
{
|
||||
Rating = rating;
|
||||
GamesPlayed = gamesPlayed;
|
||||
}
|
||||
|
||||
public static PlayerEloData Default => new PlayerEloData(1000, 0);
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.APIUpdating.MovedFrom(true, sourceNamespace: "Hallucinate.Game", sourceAssembly: "Opsive.UltimateCharacterController")]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Baba_yaga/Game/EloData.cs.meta
Normal file
2
Assets/Baba_yaga/Game/EloData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7dc894771ad8df46831ee15ee34fe7d
|
||||
64
Assets/Baba_yaga/Game/EloSystem.cs
Normal file
64
Assets/Baba_yaga/Game/EloSystem.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Baba_yaga.Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure logic for Elo rating calculations.
|
||||
/// Follows the 1v1 competitive formula with dynamic K-factor.
|
||||
/// </summary>
|
||||
public static class EloSystem
|
||||
{
|
||||
public const int RATING_FLOOR = 100;
|
||||
public const int PLACEMENT_GAMES = 30;
|
||||
|
||||
public static EloResult Calculate(
|
||||
int ratingA, int ratingB,
|
||||
int gamesPlayedA, int gamesPlayedB,
|
||||
float resultA) // 1=win, 0=lose, 0.5=draw
|
||||
{
|
||||
// 1. Expected Scores
|
||||
float eA = 1f / (1f + Mathf.Pow(10f, (ratingB - ratingA) / 400f));
|
||||
float eB = 1f - eA;
|
||||
|
||||
// 2. K-Factors
|
||||
int kA = GetK(ratingA, gamesPlayedA);
|
||||
int kB = GetK(ratingB, gamesPlayedB);
|
||||
|
||||
// 3. New Ratings
|
||||
int nA = Mathf.Max(RATING_FLOOR, Mathf.RoundToInt(ratingA + kA * (resultA - eA)));
|
||||
int nB = Mathf.Max(RATING_FLOOR, Mathf.RoundToInt(ratingB + kB * ((1f - resultA) - eB)));
|
||||
|
||||
return new EloResult(nA, nB, nA - ratingA, nB - ratingB);
|
||||
}
|
||||
|
||||
private static int GetK(int rating, int gamesPlayed)
|
||||
{
|
||||
if (gamesPlayed < PLACEMENT_GAMES) return 40;
|
||||
if (rating < 1200) return 32;
|
||||
if (rating < 2000) return 24;
|
||||
return 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"; // Iron
|
||||
if (rating < 1000) return "#CD7F32"; // Bronze
|
||||
if (rating < 1200) return "#C0C0C0"; // Silver
|
||||
if (rating < 1500) return "#FFD700"; // Gold
|
||||
if (rating < 1800) return "#4DC8A0"; // Platinum
|
||||
if (rating < 2100) return "#7B6EE8"; // Diamond
|
||||
return "#E84D8A"; // Master
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Baba_yaga/Game/EloSystem.cs.meta
Normal file
2
Assets/Baba_yaga/Game/EloSystem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 366843378ad652a41aeb5972186ebe18
|
||||
114
Assets/Baba_yaga/Game/MatchEloManager.cs
Normal file
114
Assets/Baba_yaga/Game/MatchEloManager.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Fusion;
|
||||
using Baba_yaga.Game;
|
||||
using Baba_yaga.UI;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Baba_yaga.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Orchestrates the Elo calculation and persistence on the Host.
|
||||
/// Broadcasts results to all clients.
|
||||
/// </summary>
|
||||
[UnityEngine.Scripting.APIUpdating.MovedFrom(true, sourceNamespace: "Hallucinate.Network", sourceAssembly: "Opsive.UltimateCharacterController")]
|
||||
public class MatchEloManager : NetworkBehaviour
|
||||
{
|
||||
[Networked] public EloResult LastMatchResult { get; set; }
|
||||
[Networked] public bool IsCalculating { get; set; }
|
||||
|
||||
private Dictionary<PlayerRef, string> _playerUsernames = new Dictionary<PlayerRef, string>();
|
||||
|
||||
// Simulating the database locally instead of Firebase
|
||||
private static Dictionary<string, PlayerEloData> _localEloDatabase = new Dictionary<string, PlayerEloData>();
|
||||
|
||||
private Task<PlayerEloData> GetLocalPlayerData(string username)
|
||||
{
|
||||
if (!_localEloDatabase.TryGetValue(username, out var data))
|
||||
{
|
||||
data = PlayerEloData.Default;
|
||||
_localEloDatabase[username] = data;
|
||||
}
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
private Task SaveLocalPlayerData(string username, PlayerEloData data)
|
||||
{
|
||||
_localEloDatabase[username] = data;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override void Spawned()
|
||||
{
|
||||
if (Object.HasStateAuthority)
|
||||
{
|
||||
// In a real scenario, you'd collect usernames as players join.
|
||||
// For now, we assume they are provided or stored in PlayerRef custom data.
|
||||
// Placeholder: Use a mock or collect from Session properties.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a player's username when they join.
|
||||
/// </summary>
|
||||
[Rpc(RpcSources.All, RpcTargets.StateAuthority)]
|
||||
public void RPC_RegisterUsername(PlayerRef player, string username)
|
||||
{
|
||||
if (!_playerUsernames.ContainsKey(player))
|
||||
{
|
||||
_playerUsernames.Add(player, username);
|
||||
Debug.Log($"[EloManager] Registered {username} for {player}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by GameManager on Host when match ends.
|
||||
/// </summary>
|
||||
public async void ProcessMatchResult(PlayerRef winner, PlayerRef loser, bool isDraw = false)
|
||||
{
|
||||
if (!Object.HasStateAuthority) return;
|
||||
|
||||
IsCalculating = true;
|
||||
|
||||
string nameA = _playerUsernames.GetValueOrDefault(winner, "Unknown_A");
|
||||
string nameB = _playerUsernames.GetValueOrDefault(loser, "Unknown_B");
|
||||
|
||||
// 1. Fetch from local simulated database
|
||||
var dataA = await GetLocalPlayerData(nameA);
|
||||
var dataB = await GetLocalPlayerData(nameB);
|
||||
|
||||
// 2. Calculate
|
||||
float resultA = isDraw ? 0.5f : 1.0f;
|
||||
var result = EloSystem.Calculate(dataA.Rating, dataB.Rating, dataA.GamesPlayed, dataB.GamesPlayed, resultA);
|
||||
|
||||
// 3. Update Data Objects
|
||||
dataA.Rating = result.NewRatingA;
|
||||
dataA.GamesPlayed++;
|
||||
|
||||
dataB.Rating = result.NewRatingB;
|
||||
dataB.GamesPlayed++;
|
||||
|
||||
// 4. Save to local simulated database
|
||||
await Task.WhenAll(
|
||||
SaveLocalPlayerData(nameA, dataA),
|
||||
SaveLocalPlayerData(nameB, dataB)
|
||||
);
|
||||
|
||||
// 5. Broadcast
|
||||
LastMatchResult = result;
|
||||
IsCalculating = false;
|
||||
|
||||
Debug.Log($"[EloManager] Match Processed. Winner: {nameA} (+{result.DeltaA}), Loser: {nameB} ({result.DeltaB})");
|
||||
|
||||
// Send RPC to show UI
|
||||
RPC_NotifyClients(result);
|
||||
}
|
||||
|
||||
[Rpc(RpcSources.StateAuthority, RpcTargets.All)]
|
||||
private void RPC_NotifyClients(EloResult result)
|
||||
{
|
||||
// This is where you'd trigger the Post-Match Rive/UI
|
||||
Debug.Log($"[Client] Received Elo Update: {result.DeltaA} / {result.DeltaB}");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Baba_yaga/Game/MatchEloManager.cs.meta
Normal file
2
Assets/Baba_yaga/Game/MatchEloManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1509b216eb9b7249bc7bb184f418a6f
|
||||
Reference in New Issue
Block a user