diff --git a/README.md b/README.md index 70bf589..834d27f 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,14 @@ A game of Snake built using C# in the MonoGame framework, themed around everyone - [ ] Caden: Map generation - [ ] Caden: Spice generation when we spawn - [ ] Caden: Periodic spice generation throughout the game -- [ ] Max: Keyboard support for left, right, up, down, diagonal up left and diagonal up right. Also add these to the wormMovement system. - [ ] Menu Screen to let player name themselves (probably similar to how control selection screen will work) - Satchel - +- [ ] Max: Collision detection. Know whether we hit spice or another sandworm ## Items to Develop + - [ ] Fix ability to exit the game and come back in. Dean talked about what we need to do in Teams. - [ ] 3 different animated sprites for the spice - [ ] Sound effects on death of worm and when food is eaten - Satchel -- [ ] Collision detection. Know whether we hit spice or another sandworm - [ ] On collision, sandworm breaks apart and is available as food for other snakes - [ ] Record players score, kills and highest position. Probably can be added to the `GameScores` object. - [ ] Game over screen with score, kills, and highest position achieved. This is an overlay on the multiplayer game behind it. @@ -30,6 +29,8 @@ A game of Snake built using C# in the MonoGame framework, themed around everyone - [ ] Add a player status in leaderboard to show whether the player is currently invincible. ## Done + +- [x] Max: Keyboard support for left, right, up, down, diagonal up left and diagonal up right. Also add these to the wormMovement system. - [x] Max: Snake Movement with the queue system - [x] Max: Upgrade our movement system to reduce the lag in rotation - [x] Max: Add name support for the player diff --git a/src/Client/Content/Content.mgcb b/src/Client/Content/Content.mgcb index 964a796..188a6c0 100644 --- a/src/Client/Content/Content.mgcb +++ b/src/Client/Content/Content.mgcb @@ -159,3 +159,15 @@ /processorParam:TextureFormat=Color /build:Textures/tail.png + +#begin Textures/wall.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Textures/wall.png diff --git a/src/Client/Content/Textures/wall.png b/src/Client/Content/Textures/wall.png new file mode 100644 index 0000000..50764e4 Binary files /dev/null and b/src/Client/Content/Textures/wall.png differ diff --git a/src/Client/GameModel.cs b/src/Client/GameModel.cs index 97e3364..b630a44 100644 --- a/src/Client/GameModel.cs +++ b/src/Client/GameModel.cs @@ -23,6 +23,7 @@ public class GameModel private Systems.Interpolation m_systemInterpolation; private Systems.Renderer m_renderer; private Shared.Systems.WormMovement m_systemWormMovement; + private Shared.Systems.CollisionHandler m_systemCollisionHandler; private Controls m_controls; private GraphicsDeviceManager m_graphics; private SpriteFont m_font; @@ -35,6 +36,7 @@ public void update(TimeSpan elapsedTime) { m_systemNetwork.update(elapsedTime, MessageQueueClient.instance.getMessages()); m_systemKeyboardInput.update(elapsedTime); + m_systemCollisionHandler.update(elapsedTime); m_systemWormMovement.update(elapsedTime); m_systemInterpolation.update(elapsedTime); m_systemCamera.update(elapsedTime); @@ -65,6 +67,7 @@ public bool initialize(ContentManager contentManager, Controls controls, Graphic m_systemCamera = new Systems.Camera(new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight)); m_renderer = new Systems.Renderer(m_systemCamera, graphics, m_font, m_sand); + m_systemCollisionHandler = new Shared.Systems.CollisionHandler(); m_systemWormMovement = new Shared.Systems.WormMovement(); m_systemNetwork = new Systems.Network(); @@ -120,6 +123,11 @@ private Entity createEntity(Shared.Messages.NewEntity message) { entity.add(new Collision()); } + + if (message.hasWall) + { + entity.add(new Shared.Components.Wall()); + } // Worm parts @@ -171,6 +179,7 @@ private void addEntity(Entity entity) // NOTE: Update the systems we use here m_entities[entity.id] = entity; m_systemKeyboardInput.add(entity); + m_systemCollisionHandler.add(entity); m_systemWormMovement.add(entity); m_renderer.add(entity); m_systemNetwork.add(entity); @@ -187,6 +196,7 @@ private void removeEntity(uint id) // NOTE: Update the systems we use here m_entities.Remove(id); m_systemKeyboardInput.remove(id); + m_systemCollisionHandler.remove(id); m_systemWormMovement.remove(id); m_systemNetwork.remove(id); m_renderer.remove(id); diff --git a/src/Client/Menu/ChooseNameView.cs b/src/Client/Menu/ChooseNameView.cs index bb748b9..2b47209 100644 --- a/src/Client/Menu/ChooseNameView.cs +++ b/src/Client/Menu/ChooseNameView.cs @@ -82,7 +82,7 @@ public override void render(GameTime gameTime) (m_graphics.PreferredBackBufferHeight - textSize.Y) / 2); // Draw "Enter Your Name" text - m_spriteBatch.DrawString(font, enterNameText, textPosition, Color.PaleGoldenrod); + m_spriteBatch.DrawString(font, enterNameText, textPosition, Colors.displayColor); // Draw "Press Enter to proceed" below the name text if a name has been entered if (playerName.Length > 0) @@ -93,7 +93,7 @@ public override void render(GameTime gameTime) (m_graphics.PreferredBackBufferWidth - proceedTextSize.X) / 2, textPosition.Y + textSize.Y + 20); // 20 pixels below the name text - m_spriteBatch.DrawString(font, proceedText, proceedTextPosition, Color.PaleGoldenrod); + m_spriteBatch.DrawString(font, proceedText, proceedTextPosition, Colors.displayColor); } m_spriteBatch.End(); diff --git a/src/Client/Menu/HowToPlay.cs b/src/Client/Menu/HowToPlay.cs index c5ca20e..c934b18 100644 --- a/src/Client/Menu/HowToPlay.cs +++ b/src/Client/Menu/HowToPlay.cs @@ -65,7 +65,7 @@ public override void render(GameTime gameTime) // Title Vector2 titlePosition = new Vector2(m_graphics.PreferredBackBufferWidth / 2, m_graphics.PreferredBackBufferHeight / 4); Vector2 titleOrigin = font.MeasureString(titleMessage) / 2; - m_spriteBatch.DrawString(font, titleMessage, titlePosition - (titleOrigin * textScale), Color.PaleGoldenrod, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); + m_spriteBatch.DrawString(font, titleMessage, titlePosition - (titleOrigin * textScale), Colors.displayColor, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); // How to Play Instructions Vector2 instructionsPosition = new Vector2(m_graphics.PreferredBackBufferWidth / 2, m_graphics.PreferredBackBufferHeight / 2.5f); @@ -73,14 +73,14 @@ public override void render(GameTime gameTime) foreach (string line in lines) { Vector2 lineSize = font.MeasureString(line) * textScale; - m_spriteBatch.DrawString(font, line, instructionsPosition - new Vector2(lineSize.X / 2, 0), Color.PaleGoldenrod, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); + m_spriteBatch.DrawString(font, line, instructionsPosition - new Vector2(lineSize.X / 2, 0), Colors.displayColor, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); instructionsPosition.Y += lineSize.Y + 5; // Adjust spacing between lines if necessary, taking scale into account } // Continue Prompt Vector2 continuePosition = new Vector2(m_graphics.PreferredBackBufferWidth / 2, (m_graphics.PreferredBackBufferHeight / 4) * 3); Vector2 continueOrigin = font.MeasureString(continueMessage) / 2; - m_spriteBatch.DrawString(font, continueMessage, continuePosition - (continueOrigin * textScale), Color.PaleGoldenrod, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); + m_spriteBatch.DrawString(font, continueMessage, continuePosition - (continueOrigin * textScale), Colors.displayColor, 0f, Vector2.Zero, textScale, SpriteEffects.None, 0f); m_spriteBatch.End(); } diff --git a/src/Client/Systems/CollisionDetection.cs b/src/Client/Systems/CollisionDetection.cs deleted file mode 100644 index d645076..0000000 --- a/src/Client/Systems/CollisionDetection.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Client.Systems; - -public class CollisionDetection : Shared.Systems.System -{ - public override void update(TimeSpan elapsedTime) - { - } -} \ No newline at end of file diff --git a/src/Client/Systems/Network.cs b/src/Client/Systems/Network.cs index 5ed31e3..2ccf11e 100644 --- a/src/Client/Systems/Network.cs +++ b/src/Client/Systems/Network.cs @@ -52,6 +52,7 @@ public Network() : { m_removeEntityHandler((RemoveEntity)message); }); + } // Have to implement this because it is abstract in the base class @@ -187,6 +188,10 @@ private void handleUpdateEntity(TimeSpan elapsedTime, UpdateEntity message) m_updatedEntities.Add(entity.id); } + if (entity.contains() && message.hasSpicePower) + { + entity.get().setPower(message.spicePower); + } } } } diff --git a/src/Server/GameModel.cs b/src/Server/GameModel.cs index e44ead1..8763ffd 100644 --- a/src/Server/GameModel.cs +++ b/src/Server/GameModel.cs @@ -1,5 +1,6 @@  using Microsoft.Xna.Framework; +using Server.Systems; using Shared.Components; using Shared.Components.Appearance; using Shared.Entities; @@ -14,7 +15,10 @@ public class GameModel private Dictionary m_entities = new Dictionary(); private Dictionary m_clientToEntityId = new Dictionary(); private WormMovement m_systemWormMovement = new WormMovement(); + private CollisionDetection m_systemCollisionDetection = new CollisionDetection(); + private CollisionHandler m_systemCollisionHandler = new CollisionHandler(); Systems.Network m_systemNetwork = new Server.Systems.Network(); + private int mapSize = 5000; /// /// This is where the server-side simulation takes place. Messages @@ -24,6 +28,8 @@ public class GameModel public void update(TimeSpan elapsedTime) { m_systemNetwork.update(elapsedTime, MessageQueueServer.instance.getMessages()); + m_systemCollisionDetection.update(elapsedTime); + m_systemCollisionHandler.update(elapsedTime); m_systemWormMovement.update(elapsedTime); } @@ -32,14 +38,16 @@ public void update(TimeSpan elapsedTime) /// public bool initialize() { + generateWalls(); m_systemNetwork.registerJoinHandler(handleJoin); m_systemNetwork.registerDisconnectHandler(handleDisconnect); - MessageQueueServer.instance.registerConnectHandler(handleConnect); - + m_systemCollisionDetection.registerRemoveEntity(removeEntity); return true; } + + /// /// Give everything a chance to gracefully shutdown. /// @@ -68,13 +76,11 @@ private void handleConnect(int clientId) private void handleDisconnect(int clientId) { m_clients.Remove(clientId); - Message message = new Shared.Messages.RemoveEntity(m_clientToEntityId[clientId]); MessageQueueServer.instance.broadcastMessage(message); - removeEntity(m_clientToEntityId[clientId]); - m_clientToEntityId.Remove(clientId); + } /// @@ -91,6 +97,8 @@ private void addEntity(Entity entity) m_entities[entity.id] = entity; m_systemNetwork.add(entity); + m_systemCollisionDetection.add(entity); + m_systemCollisionHandler.add(entity); m_systemWormMovement.add(entity); } @@ -102,7 +110,10 @@ private void removeEntity(uint id) { m_entities.Remove(id); m_systemNetwork.remove(id); + m_systemCollisionDetection.remove(id); + m_systemCollisionHandler.remove(id); m_systemWormMovement.remove(id); + } /// @@ -134,6 +145,33 @@ private void handleJoin(int clientId, Shared.Messages.Message message) // to the newly joined client createNewWorm(clientId, name); } + + private void generateWalls() + { + // We want to create wall entities around the entire map. 5000x5000 is the size of the map + // We'll create a wall every 100 units + var wallSize = 100; + for (int i = 0; i < mapSize/100; i++) + { + // Top wall + Entity wall = Shared.Entities.Wall.create(new Vector2(i * wallSize, 0), wallSize); + addEntity(wall); + // MessageQueueServer.instance.broadcastMessage(new NewEntity(wall)); + // Bottom wall + wall = Shared.Entities.Wall.create(new Vector2(i * wallSize, mapSize-wallSize), wallSize); + addEntity(wall); + // MessageQueueServer.instance.broadcastMessage(new NewEntity(wall)); + // Left wall + wall = Shared.Entities.Wall.create(new Vector2(0, i * wallSize), wallSize); + addEntity(wall); + // MessageQueueServer.instance.broadcastMessage(new NewEntity(wall)); + // Right wall + wall = Shared.Entities.Wall.create(new Vector2(mapSize-wallSize, i * wallSize), wallSize); + addEntity(wall); + // MessageQueueServer.instance.broadcastMessage(new NewEntity(wall)); + } + + } private void createNewWorm(int clientId, string name) { @@ -201,7 +239,9 @@ private Vector2 getLeastDenseStartLocation() // We want to start the player in the least dense area of the screen // For now, we'll just start them randomly generated location Random random = new Random(); - return new Vector2(random.Next(0, 800), random.Next(0, 600)); + var lowerBound = (int)(.1 * mapSize); + var upperBound = (int)(.9 * mapSize); + return new Vector2(random.Next(lowerBound, upperBound), random.Next(lowerBound, upperBound)); } } } diff --git a/src/Server/MessageQueueServer.cs b/src/Server/MessageQueueServer.cs index 7c8b177..771818e 100644 --- a/src/Server/MessageQueueServer.cs +++ b/src/Server/MessageQueueServer.cs @@ -161,7 +161,6 @@ public void broadcastMessageWithLastId(Message message) public void registerConnectHandler(ConnectHandler handler) { m_connectHandler = handler; - } /// diff --git a/src/Server/Systems/CollisionDetection.cs b/src/Server/Systems/CollisionDetection.cs index 5acdb91..b98447d 100644 --- a/src/Server/Systems/CollisionDetection.cs +++ b/src/Server/Systems/CollisionDetection.cs @@ -1,24 +1,99 @@ using Microsoft.Xna.Framework; +using Shared.Components; +using Shared.Entities; +using Shared.Messages; +using Shared.Systems; namespace Server.Systems; public class CollisionDetection : Shared.Systems.System { + private System.Action m_removeEntity; + public CollisionDetection() : base( - typeof(Shared.Components.Collision) + typeof(Shared.Components.Collision), typeof(Shared.Components.Position), typeof(Shared.Components.Size) ) { } - - + public override void update(TimeSpan elapsedTime) { - throw new NotImplementedException(); + // Get the heads of the worms + List heads = new List(); + foreach (var entity in m_entities.Values) + { + if (entity.contains()) + { + heads.Add(entity); + } + } + + // Check each head against everything else + foreach (var head in heads) + { + var worm = WormMovement.getWormFromHead(head, m_entities); + // Make this worm into a hashset + HashSet wormSet = new HashSet(); + foreach (var entity in worm) + { + wormSet.Add(entity.id); + } + foreach (var entity in m_entities.Values) + { + // Ignore elements of the worm against everyone else + if (wormSet.Contains(entity.id)) + { + continue; + } + + if (entity.contains() ) + { + if (CircleCircleIntersect( + head.get().position, + head.get().size.X, + entity.get().position, + entity.get().size.X + )) + { + handleWormAteSpice(head, entity, elapsedTime); + } + } + else if (entity.contains()) + { + if (CircleCircleIntersect( + head.get().position, + head.get().size.X, + entity.get().position, + entity.get().size.X + )) + { + handleWormAteWorm(worm, entity); + } + } + else if (entity.contains()) + { + if (CircleLineIntersect( + entity.get().position, + entity.get().position + new Vector2(entity.get().size.X, 0), + head.get().size.X, + head.get().position + ) || CircleLineIntersect( + entity.get().position, + entity.get().position + new Vector2(0, entity.get().size.Y), + head.get().size.X, + head.get().position + )) + { + handleWormHitWall(worm); + } + } + } + } } // Reference: https://stackoverflow.com/questions/37224912/circle-line-segment-collision - private static bool CircleLineIntersect(Point pt1, Point pt2, float circleRadius, Vector2 circlePosition) + private static bool CircleLineIntersect(Vector2 pt1, Vector2 pt2, float circleRadius, Vector2 circlePosition) { Vector2 v1 = new Vector2((float)(pt2.X - pt1.X), (float)(pt2.X - pt1.X)); Vector2 v2 = new Vector2((float) pt1.X - circlePosition.X, (float)(pt1.X - circlePosition.Y)); @@ -42,6 +117,66 @@ private static bool CircleLineIntersect(Point pt1, Point pt2, float circleRadius } return false; } + + private static bool CircleCircleIntersect(Vector2 position1, float radius1, Vector2 position2, float radius2) + { + return Vector2.Distance(position1, position2) < radius1 + radius2; + } + + private void handleWormAteSpice(Entity head, Entity spice, TimeSpan elapsedTime) + { + // Remove the spice + MessageQueueServer.instance.broadcastMessage(new RemoveEntity(spice.id)); + // Add power to the worm head + var headPower = head.get(); + var spicePower = spice.get(); + headPower.addPower(spicePower.power); + MessageQueueServer.instance.broadcastMessage(new UpdateEntity(head, elapsedTime)); + } + + private void handleWormAteWorm(List worm, Entity otherHead) + { + // Check if we hit head on + if (otherHead.contains()) + { + // We need to compare the sizes of the two worms to see who dies + List otherWorm = WormMovement.getWormFromHead(otherHead, m_entities); + if (worm.Count > otherWorm.Count) + { + removeWorm(otherWorm); + } + else + { + removeWorm(worm); + } + } + else // We hit the side of the worm + { + // If the worm hit the body, then the worm dies + removeWorm(worm); + } + } + + private void handleWormHitWall(List worm) + { + removeWorm(worm); + } + + private void removeWorm(List worm) + { + foreach (var entity in worm) + { + MessageQueueServer.instance.broadcastMessage(new RemoveEntity(entity.id)); + m_removeEntity(entity.id); + } + // TODO: Add new entities to the world where the body was + + } + + public void registerRemoveEntity(Action removeEntity) + { + m_removeEntity = removeEntity; + } } diff --git a/src/Server/Systems/Network.cs b/src/Server/Systems/Network.cs index 8ad83e0..a5d2794 100644 --- a/src/Server/Systems/Network.cs +++ b/src/Server/Systems/Network.cs @@ -16,6 +16,8 @@ public class Network : Shared.Systems.System private DisconnectHandler m_disconnectHandler; private HashSet m_reportThese = new HashSet(); + private TimeSpan m_lastGlobalUpdateTime = new TimeSpan(m_globalUpdateFrequency); + private static int m_globalUpdateFrequency = 300; /// /// Primary activity in the constructor is to setup the command map @@ -53,7 +55,22 @@ public Network() : } // Have to implement this because it is abstract in the base class - public override void update(TimeSpan elapsedTime) { } + public override void update(TimeSpan elapsedTime) + { + m_lastGlobalUpdateTime -= elapsedTime; + if (m_lastGlobalUpdateTime.TotalMilliseconds < 0) + { + Console.WriteLine("Global Worm Location Update"); + m_lastGlobalUpdateTime = new TimeSpan(m_globalUpdateFrequency); + foreach (var entity in m_entities.Values) + { + if (entity.contains()) + { + m_reportThese.Add(entity.id); + } + } + } + } /// /// Have our own version of update, because we need a list of messages to work with, and @@ -101,44 +118,52 @@ private void handleInput(Shared.Messages.Input message) { var entity = m_entities[message.entityId]; var worm = WormMovement.getWormFromHead(entity, m_entities); + var update = false; foreach (var input in message.inputs) { switch (input) { case Shared.Components.Input.Type.PointLeft: Shared.Systems.WormMovement.left(worm, message.elapsedTime); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointRight: Shared.Systems.WormMovement.right(worm, message.elapsedTime); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointUp: Shared.Systems.WormMovement.up(worm); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointDown: Shared.Systems.WormMovement.down(worm); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointUpLeft: Shared.Systems.WormMovement.upLeft(worm); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointUpRight: Shared.Systems.WormMovement.upRight(worm); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointDownLeft: Shared.Systems.WormMovement.downLeft(worm); - m_reportThese.Add(entity.id); + update = true; break; case Shared.Components.Input.Type.PointDownRight: Shared.Systems.WormMovement.downRight(worm); - m_reportThese.Add(entity.id); + update = true; break; } } + if (update) + { + foreach (var e in worm) + { + m_reportThese.Add(e.id); + } + } } /// diff --git a/src/Shared/Components/SpicePower.cs b/src/Shared/Components/SpicePower.cs index 321e95f..3e109af 100644 --- a/src/Shared/Components/SpicePower.cs +++ b/src/Shared/Components/SpicePower.cs @@ -17,4 +17,9 @@ public void removePower(int power) { this.power -= power; } + + public void setPower(int power) + { + this.power = power; + } } \ No newline at end of file diff --git a/src/Shared/Components/Wall.cs b/src/Shared/Components/Wall.cs new file mode 100644 index 0000000..5c0bb95 --- /dev/null +++ b/src/Shared/Components/Wall.cs @@ -0,0 +1,6 @@ +namespace Shared.Components; + +public class Wall : Component +{ + +} \ No newline at end of file diff --git a/src/Shared/Entities/Wall.cs b/src/Shared/Entities/Wall.cs new file mode 100644 index 0000000..ffd5d61 --- /dev/null +++ b/src/Shared/Entities/Wall.cs @@ -0,0 +1,19 @@ +using Microsoft.Xna.Framework; +using Shared.Components; +using Shared.Components.Appearance; + +namespace Shared.Entities; + +public class Wall +{ + public static Entity create(Vector2 position, int size) + { + Entity entity = new Entity(); + entity.add(new Appearance("Textures/wall")); + entity.add(new Position(position)); + entity.add(new Size(new Vector2(size, size))); + entity.add(new Collision()); + entity.add(new Components.Wall()); + return entity; + } +} \ No newline at end of file diff --git a/src/Shared/Messages/Collision.cs b/src/Shared/Messages/Collision.cs new file mode 100644 index 0000000..caa0636 --- /dev/null +++ b/src/Shared/Messages/Collision.cs @@ -0,0 +1,19 @@ +namespace Shared.Messages; + +public class Collision : Message +{ + public uint entity1Id { get; private set; } + public uint entity2Id { get; private set; } + + public Collision(uint id, uint id2) : base(Type.Collision) + { + entity1Id = id; + entity2Id = id2; + } + + public Collision() : base(Type.Collision) + { + } + + +} \ No newline at end of file diff --git a/src/Shared/Messages/MessageTypes.cs b/src/Shared/Messages/MessageTypes.cs index 1e1bfb1..84a11b2 100644 --- a/src/Shared/Messages/MessageTypes.cs +++ b/src/Shared/Messages/MessageTypes.cs @@ -7,6 +7,7 @@ public enum Type : UInt16 NewEntity, // Server to client UpdateEntity, // Server to client RemoveEntity, // Server to client + Collision, // Server to client Join, // Client to server Input, // Client to server Disconnect // Client to server diff --git a/src/Shared/Messages/NewEntity.cs b/src/Shared/Messages/NewEntity.cs index 4f507e3..748070d 100644 --- a/src/Shared/Messages/NewEntity.cs +++ b/src/Shared/Messages/NewEntity.cs @@ -3,11 +3,13 @@ using Shared.Entities; using System.Text; using Shared.Components.Appearance; +using Wall = Shared.Entities.Wall; namespace Shared.Messages { public class NewEntity : Message { + // TODO: Add a wall component check public NewEntity(Entity entity) : base(Type.NewEntity) { this.id = entity.id; @@ -75,7 +77,7 @@ public NewEntity(Entity entity) : base(Type.NewEntity) this.childId = entity.get().id; } - if (entity.contains()) + if (entity.contains()) { this.hasCollision = true; } @@ -90,6 +92,11 @@ public NewEntity(Entity entity) : base(Type.NewEntity) this.hasName = true; this.name = entity.get().name; } + + if (entity.contains()) + { + this.hasWall = true; + } } public NewEntity() : base(Type.NewEntity) { @@ -101,8 +108,8 @@ public NewEntity() : base(Type.NewEntity) // Appearance public bool hasAppearance { get; private set; } = false; public string texture { get; private set; } - public bool hasCollision { get; private set; } = false; + public bool hasWall { get; private set; } = false; // Worm parts public bool hasHead { get; private set; } = false; @@ -147,6 +154,7 @@ public override byte[] serialize() serializeMovement(data); serializeInput(data); data.AddRange(BitConverter.GetBytes(hasCollision)); + data.AddRange(BitConverter.GetBytes(hasWall)); // Worm entities data.AddRange(BitConverter.GetBytes(hasHead)); @@ -159,9 +167,7 @@ public override byte[] serialize() return data.ToArray(); } - - - + public override int parse(byte[] data) { // NOTE: Add parser for the components on the WormHead, WormSegment, and WormTail entities @@ -176,6 +182,7 @@ public override int parse(byte[] data) offset = parseMovement(data, offset); offset = parseInput(data, offset); offset = parseCollision(data, offset); + offset = parseWall(data, offset); // Worm Entities offset = parseHead(data, offset); @@ -193,6 +200,13 @@ private int parseWorm(byte[] data, int offset) offset += sizeof(bool); return offset; } + + private int parseWall(byte[] data, int offset) + { + this.hasWall = BitConverter.ToBoolean(data, offset); + offset += sizeof(bool); + return offset; + } private int parseId(byte[] data, int offset) { diff --git a/src/Shared/Messages/UpdateEntity.cs b/src/Shared/Messages/UpdateEntity.cs index e5d8f48..18c23c8 100644 --- a/src/Shared/Messages/UpdateEntity.cs +++ b/src/Shared/Messages/UpdateEntity.cs @@ -18,6 +18,11 @@ public UpdateEntity(Entity entity, TimeSpan updateWindow) : base(Type.UpdateEnti this.orientation = entity.get().orientation; } + if (entity.contains()) + { + this.spicePower = entity.get().power; + } + this.updateWindow = updateWindow; } @@ -31,6 +36,10 @@ public UpdateEntity(): base(Type.UpdateEntity) public bool hasPosition { get; private set; } = false; public Vector2 position { get; private set; } public float orientation { get; private set; } + + // SpicePower + public bool hasSpicePower { get; private set; } = false; + public int spicePower { get; private set; } = 0; // Only the milliseconds are used/serialized public TimeSpan updateWindow { get; private set; } = TimeSpan.Zero; @@ -49,6 +58,12 @@ public override byte[] serialize() data.AddRange(BitConverter.GetBytes(position.Y)); data.AddRange(BitConverter.GetBytes(orientation)); } + + data.AddRange(BitConverter.GetBytes(hasSpicePower)); + if (hasSpicePower) + { + data.AddRange(BitConverter.GetBytes(spicePower)); + } data.AddRange(BitConverter.GetBytes((float)updateWindow.TotalMilliseconds)); @@ -74,6 +89,14 @@ public override int parse(byte[] data) this.orientation = BitConverter.ToSingle(data, offset); offset += sizeof(Single); } + + this.hasSpicePower = BitConverter.ToBoolean(data, offset); + offset += sizeof(bool); + if (hasSpicePower) + { + this.spicePower = BitConverter.ToInt32(data, offset); + offset += sizeof(int); + } this.updateWindow = TimeSpan.FromMilliseconds(BitConverter.ToSingle(data, offset)); offset += sizeof(Single); diff --git a/src/Shared/Systems/CollisionHandler.cs b/src/Shared/Systems/CollisionHandler.cs new file mode 100644 index 0000000..d2cda33 --- /dev/null +++ b/src/Shared/Systems/CollisionHandler.cs @@ -0,0 +1,13 @@ +using Shared.Components; +using Shared.Entities; + +namespace Shared.Systems; + +public class CollisionHandler : Shared.Systems.System +{ + public override void update(TimeSpan elapsedTime) + { + // Basically we look at each worm head and see how big it is. If it is above a certain threshold, then we update its size and remove the spice power. + } + +} \ No newline at end of file diff --git a/src/Shared/Systems/WormMovement.cs b/src/Shared/Systems/WormMovement.cs index 1d5de04..c60c4da 100644 --- a/src/Shared/Systems/WormMovement.cs +++ b/src/Shared/Systems/WormMovement.cs @@ -89,7 +89,7 @@ private void applyThrust(Entity wormHead, TimeSpan elapsedTime) var frameTotalMovement = movement.moveRate * (float)elapsedTime.TotalMilliseconds; var orientation = headPosition.orientation; float LOCATION_THRESHOLD = 2f; - const float MIN_SEGMENT_SPACING = 20f; + const float MIN_SEGMENT_SPACING = 30f; const float IDEAL_SEGMENT_SPACING = 40f; // Check how close the head is to its child @@ -129,7 +129,7 @@ private void applyThrust(Entity wormHead, TimeSpan elapsedTime) // Move towards that target var distanceToTarget = Vector2.Distance(currentPosition.position, target.position); var distanceToParent = Vector2.Distance(currentPosition.position, parentPosition.position); - if (distanceToTarget > MIN_SEGMENT_SPACING || distanceToParent > IDEAL_SEGMENT_SPACING) + if (distanceToTarget >= MIN_SEGMENT_SPACING || distanceToParent >= IDEAL_SEGMENT_SPACING) { var directionToTarget = target.position - currentPosition.position; directionToTarget.Normalize(); @@ -179,8 +179,6 @@ private static void changeDirection(List worm, float radians) } } - - private static Entity getHead(Entity entity, Dictionary entities) {