using System; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Debug; using Syntriax.Engine.Integration.MonoGame; using Syntriax.Engine.Network; namespace Pong.Behaviours; public class PongManager : Behaviour, INetworkEntity, IFirstFrameUpdate, ILoadContent, IPacketListenerClient, IPacketListenerClient { public Action? OnReset { get; set; } = null; public Action? OnFinished { get; set; } = null; public Action? OnScoreUpdated { get; set; } = null; private Random random = new(); private INetworkCommunicatorServer? networkServer = null; private ILogger? logger = null; private SoundEffectInstance? scoreSoundEffect = null; private SoundEffectInstance? gameEndSoundEffect = 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 Ball Ball { get; private set; } = null!; public bool IsGameInProgress { get; private set; } = false; public PongManager() => WinScore = 5; public PongManager(int winScore) => WinScore = winScore; public void FirstActiveFrame() { Ball = Universe.FindRequiredBehaviour(); networkServer = Universe.FindBehaviour(); logger = Universe.FindBehaviour(); } public void LoadContent(ContentManager content) { gameEndSoundEffect = content.Load("Audio/Win").CreateInstance(); scoreSoundEffect = content.Load("Audio/Score").CreateInstance(); } public void ScoreToLeft() { ScoreLeft++; PostScoreUpdate(); } public void ScoreToRight() { ScoreRight++; PostScoreUpdate(); } public bool Start() { if (networkServer is null) return false; if (Ball.RigidBody.Velocity.MagnitudeSquared > 0.01f) return false; Reset(); IsGameInProgress = true; PostScoreUpdate(); networkServer.SendToAll(new StartPacket()); logger?.Log(this, $"Game started"); return true; } public void Reset() { ScoreLeft = ScoreRight = 0; IsGameInProgress = false; Ball.ResetBall(); OnReset?.Invoke(this); } private void PostScoreUpdate() { OnScoreUpdated?.Invoke(this); Ball.ResetBall(); if (networkServer is not null) { networkServer.SendToAll(new ScorePacket(this)); logger?.Log(this, $"Sending score update packet to all: {ScoreLeft} - {ScoreRight}"); } int halfwayScore = (int)(WinScore * .5f); if (ScoreLeft > halfwayScore || ScoreRight > halfwayScore) { IsGameInProgress = false; gameEndSoundEffect?.Play(); OnFinished?.Invoke(this); logger?.Log(this, $"Game finished: {ScoreLeft} - {ScoreRight}"); return; } Ball.LaunchBall(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); } void IPacketListenerClient.OnClientPacketArrived(IConnection sender, ScorePacket packet) { ScoreLeft = packet.Left; ScoreRight = packet.Right; PostScoreUpdate(); if (IsGameInProgress && (ScoreLeft != 0 || ScoreRight != 0)) scoreSoundEffect?.Play(); logger?.Log(this, $"Client score update packet arrived: {packet.Left} - {packet.Right}"); } void IPacketListenerClient.OnClientPacketArrived(IConnection sender, StartPacket packet) { IsGameInProgress = true; logger?.Log(this, $"Game started"); } private class StartPacket : INetworkPacket; private class ScorePacket : INetworkPacket { public int Left { get; set; } public int Right { get; set; } public ScorePacket() { } public ScorePacket(PongManager pongManagerBehaviour) { Left = pongManagerBehaviour.ScoreLeft; Right = pongManagerBehaviour.ScoreRight; } } }