using System; using Microsoft.Xna.Framework.Input; using Syntriax.Engine.Core; using Syntriax.Engine.Network; using Syntriax.Engine.Systems.Input; namespace Pong.Behaviours; public class PongManagerBehaviour : Behaviour, IPacketListenerServer, IPacketListenerServer, IPacketListenerClient, IPacketListenerClient, IPacketListenerClient { public Action? OnReset { get; set; } = null; public Action? OnFinished { get; set; } = null; public Action? OnScored { get; set; } = null; private readonly Random random = new(); private BallBehaviour ball = null!; private INetworkCommunicatorClient? networkClient = null!; private INetworkCommunicatorServer? networkServer = null; public int ScoreLeft { get; private set; } = 0; public int ScoreRight { get; private set; } = 0; public int ScoreSum => ScoreLeft + ScoreRight; public int WinScore { get; } = 5; public PongManagerBehaviour() => WinScore = 5; public PongManagerBehaviour(int winScore) => WinScore = winScore; protected override void OnFirstActiveFrame() { IButtonInputs buttonInputs = Universe.FindRequired>(); buttonInputs.RegisterOnRelease(Keys.Space, (_, _1) => networkClient?.SendToServer(new PongResetPacket())); ball = Universe.FindRequiredBehaviour(); networkClient = Universe.Find(); networkServer = Universe.Find(); } public void ScoreToLeft() { if (networkServer is not null) { ScoreLeft++; OnScored?.InvokeSafe(this); } CheckFinish(); } public void ScoreToRight() { if (networkServer is not null) { ScoreRight++; OnScored?.InvokeSafe(this); } CheckFinish(); } public void Reset() { ScoreLeft = ScoreRight = 0; OnReset?.InvokeSafe(this); } private void CheckFinish() { int halfwayScore = (int)(WinScore * .5f); ball.ResetBall(); networkServer?.SendToClient("*", new PongScoreUpdatePacket(this)); if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore) OnFinished?.InvokeSafe(this); else networkServer?.SendToClient("*", new PongStartPacket() { BallVelocity = GetBallLaunchDirection() }); } private Vector2D GetBallLaunchDirection() { const float AllowedRadians = 45f * Syntriax.Engine.Core.Math.DegreeToRadian; float rotation = (float)random.NextDouble() * 2f * AllowedRadians - AllowedRadians; bool isBackwards = (random.Next() % 2) == 1; return Vector2D.Right.Rotate(isBackwards ? rotation + Syntriax.Engine.Core.Math.PI : rotation); } public void OnClientPacketArrived(PongStartPacket packet) => ball.LaunchBall(packet.BallVelocity); public void OnClientPacketArrived(PongResetPacket packet) => Reset(); public void OnClientPacketArrived(PongScoreUpdatePacket packet) { ScoreLeft = packet.Left; ScoreRight = packet.Right; OnScored?.InvokeSafe(this); } public void OnServerPacketArrived(PongResetPacket packet, string from) { Reset(); Vector2D ballVelocity = GetBallLaunchDirection(); ball.LaunchBall(ballVelocity); networkServer?.SendToClient("*", new PongResetPacket()); networkServer?.SendToClient("*", new PongStartPacket() { BallVelocity = ballVelocity }); } public void OnServerPacketArrived(PongStartPacket packet, string from) { packet = new() { BallVelocity = GetBallLaunchDirection() }; ball.LaunchBall(packet.BallVelocity); networkServer?.SendToClient("*", packet); } public class PongStartPacket : INetworkPacket { public Vector2D BallVelocity { get; set; } = Vector2D.Zero; } public class PongResetPacket : INetworkPacket; public class PongScoreUpdatePacket : INetworkPacket { public int Left { get; set; } = 0; public int Right { get; set; } = 0; public PongScoreUpdatePacket() { } public PongScoreUpdatePacket(PongManagerBehaviour pongManagerBehaviour) { Left = pongManagerBehaviour.ScoreLeft; Right = pongManagerBehaviour.ScoreRight; } } }