Sboku/Arena/RoundManager.cs
using Sandbox.Events;
using SWB.Demo;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Sandbox.Sboku.Arena;
[Group("Sboku Arena")]
public sealed class RoundManager : Component,
IGameEventHandler<RoundManager.UpgradeScreenClosed>,
IGameEventHandler<RoundManager.GameOver>
{
[Property]
public bool AutoStartRounds { get; set; } = true;
[Property]
public int EnemiesPerSpawner { get; set; } = 3;
[Property]
public int StartCountdown { get; set; } = 5;
[Property]
public int EndCountdown { get; set; } = 5;
[Property]
public int BotCap { get; set; } = 3;
[Property]
public int TotalRounds { get; set; } = 10;
[Sync]
public int RoundNumber { get; set; } = 1;
[Sync]
public int BotsNumber { get; private set; }
[Sync]
public int Timer { get; private set; }
[Property]
public bool EnableUpgrades { get; set; } = true;
private TimeSince timer = new();
private Action callback;
private bool isRoundStarted = false;
private Spawner spawner;
[Rpc.Host]
protected override void OnAwake()
{
spawner = Scene.GetComponentInChildren<Spawner>();
}
protected override void OnStart()
{
base.OnStart();
if (AutoStartRounds && !isRoundStarted)
{
Log.Info("[Round] Auto-starting first round");
SetCallback(StartCountdown, StartRound);
}
}
[Rpc.Host]
protected override void OnFixedUpdate()
{
BotsNumber = Scene.GetAllComponents<SbokuBase>().Count();
if (isRoundStarted && BotsNumber == 0 && AreSpawnersDone())
{
isRoundStarted = false;
SetCallback(EndCountdown, FinishRound);
}
if (timer > 1f)
{
OnTimer();
timer = 0f;
}
}
private void OnTimer()
{
if (Timer > 0)
{
Timer--;
if (Timer == 0)
{
OnTimerExpire();
}
}
}
private void OnTimerExpire()
{
if (callback != null)
{
callback();
}
else
{
Log.Error("Callback is null!");
}
}
private bool AreSpawnersDone()
{
if (!spawner.IsDone())
return false;
return true;
}
private void SetCallback(int time, Action callback)
{
Timer = time;
this.callback = callback;
}
private bool navMeshGenerated = false;
private void StartRound()
{
Log.Info($"[RoundManager] StartRound called | Host={Networking.IsHost}");
if (isRoundStarted)
return;
if (!navMeshGenerated)
{
if (Scene.NavMesh != null)
{
Scene.NavMesh.Generate(Scene.PhysicsWorld);
navMeshGenerated = true;
Log.Info("[NavMesh] Generated before first round");
}
}
spawner.RequestSpawn();
isRoundStarted = true;
}
[Rpc.Host]
public void RemoveEntities()
{
foreach (var ad in Scene.GetComponentsInChildren<BotAdapter>(includeDisabled: true))
ad.GameObject.Destroy();
}
[Rpc.Host]
private void FinishRound()
{
isRoundStarted = false;
RoundNumber++;
RemoveEntities();
foreach (var ply in Scene.GetAllComponents<DemoPlayer>())
{
ply.Respawn();
if (ply.UsesUpgrades)
{
ply.GetComponent<UpgradeHolder>().FreePoints++;
}
}
if (RoundNumber <= TotalRounds)
{
if (EnableUpgrades)
{
Scene.Dispatch<OpenUpgradeScreen>(new());
}
else
{
SetCallback(StartCountdown, StartRound);
}
}
else
{
Scene.Dispatch<Victory>(new());
}
}
public void OnGameEvent(UpgradeScreenClosed eventArgs)
{
if (!isRoundStarted)
SetCallback(StartCountdown, StartRoundHost);
}
[Rpc.Host]
public void OnGameEvent(GameOver eventArgs)
{
isRoundStarted = false;
//spawners.ForEach(x => x.ClearQueue());
RemoveEntities();
foreach (var pl in Scene.GetComponentsInChildren<DemoPlayer>())
{
pl.Respawn();
}
if (AutoStartRounds)
{
// 🔁 Aloita peli uudelleen alusta
RoundNumber = 1;
Log.Info("[Round] GameOver → restarting automatically");
SetCallback(StartCountdown, StartRound);
}
else
{
// Arena / FPS -moodi
foreach (var pl in Scene.GetComponentsInChildren<DemoPlayer>())
{
pl.DestroyAllUIAsync(); // 🔑 OIKEA AIKOITUS(); // 🔑 TÄRKEIN RIVI
//pl.CreateFailScreen();
}
}
}
private void StartRoundHost()
{
if (!Networking.IsHost)
return;
StartRound();
}
public record OpenUpgradeScreen() : IGameEvent;
public record UpgradeScreenClosed() : IGameEvent;
public record GameOver() : IGameEvent;
public record Victory() : IGameEvent;
}