using Godot; namespace AceField.Scripts.Logic; public partial class World : Node2D { [Export] public Scoreboard Scoreboard; [Export] public PackedScene PlayerScene; [Export] public int RoundCount = 1; [Export] public int RoundTotalCount = 20; [Export] public double RoundDuration = 60; [Export] public double RoundProgress; [Export] public double RoundTimeLeft; private Timer _roundTimer; public void StartGame(string currentPlayerName = null) { if (!Multiplayer.IsServer()) return; _roundTimer = new Timer(); _roundTimer.WaitTime = RoundDuration; _roundTimer.Autostart = true; _roundTimer.Timeout += NewRound; _roundTimer.OneShot = true; GetParent().AddChild(_roundTimer); _roundTimer.Start(RoundDuration); // Add players into the game PutPlayers(currentPlayerName); } public override void _Ready() { if (!Multiplayer.IsServer()) return; // Handling player connect / disconnect after this client connected Multiplayer.PeerDisconnected += RemovePlayer_Adaptor; Multiplayer.PeerConnected += AddPlayer_Adaptor; // Handling player connected before this client foreach (var id in Multiplayer.GetPeers()) Scoreboard.AddPlayer(id); // Add this client as a player if client isn't a dedicated server if (!OS.HasFeature("dedicated_server")) Scoreboard.AddPlayer(1); } public override void _ExitTree() { if (!Multiplayer.IsServer()) return; Multiplayer.PeerDisconnected -= RemovePlayer_Adaptor; Multiplayer.PeerConnected -= AddPlayer_Adaptor; } public override void _Process(double delta) { if (Multiplayer.IsServer()) { RoundProgress = _roundTimer.TimeLeft / _roundTimer.WaitTime; RoundTimeLeft = _roundTimer.TimeLeft; } } private static string BuildPlayerName(int id) => $"Player@{id}"; private void AddPlayer_Adaptor(long id) => Scoreboard.AddPlayer((int)id); private void RemovePlayer_Adaptor(long id) => Scoreboard.RemovePlayer((int)id); private void SpawnPlayer(int id, string name = null) { var player = PlayerScene.Instantiate(); player.PlayerId = id; player.Position = new Vector2(GD.RandRange(-100, 100), GD.RandRange(-100, 100)); player.Name = BuildPlayerName(id); player.PlayerDied += (killerId) => { if (killerId == 0) return; Scoreboard.AddScore(killerId); if (CountPlayer() <= 2) _roundTimer.Start(3); }; AddChild(player, true); player.PlayerName = name; } private void PutPlayers(string currentPlayerName = null) { // Spawn clients foreach (var id in Multiplayer.GetPeers()) SpawnPlayer(id, Scoreboard.Players[id]?.Name); // Spawn host if (!OS.HasFeature("dedicated_server")) SpawnPlayer(1, currentPlayerName ?? Scoreboard.Players[1]?.Name); } private void NewRound() { if (RoundCount >= RoundTotalCount) { Rpc(nameof(EndGame)); return; } RoundCount++; Rpc(nameof(CleanWorld)); PutPlayers(); _roundTimer.Start(RoundDuration); } public Player GetCurrentPlayer() { foreach (var child in GetChildren()) { if (child is Player { IsCurrentPlayer: true } player) return player; } return null; } public int CountPlayer() { var count = 0; foreach (var child in GetChildren()) { if (child is Player) count++; } return count; } public T GetTileByPosition(Vector2 position) where T : Node2D { foreach (var item in GetChildren()) { if (item is T tile && tile.Position == position) return tile; } return null; } [Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = true)] private void CleanWorld() { foreach (var child in GetChildren()) { child.QueueFree(); } } [Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = true)] private void EndGame() { GetParent().GetNode("OverlayLayer/Hud").Hide(); GetParent().GetNode("OverlayLayer/GameOverScreen").Show(); GetTree().Paused = true; } }