diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 9aaf715..57cf30c 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -61,7 +61,10 @@ + + + diff --git a/Tests/TwoDimension/AreaTests.cs b/Tests/TwoDimension/AreaTests.cs index fbe9ca0..fa861bb 100644 --- a/Tests/TwoDimension/AreaTests.cs +++ b/Tests/TwoDimension/AreaTests.cs @@ -156,6 +156,8 @@ public void PositionSet() Assert.That(() => area.SetPosition(null, mockPosition.Object), Throws.ArgumentNullException); Assert.That(() => area.SetPosition(mockLevel.Object, null), Throws.ArgumentNullException); + // TODO: Issue 13 (https://github.com/Wizcorp/TileSystem/issues/13) + // Test Set Position Works Assert.That(() => area.SetPosition(mockLevel.Object, mockPosition.Object), Throws.Nothing); diff --git a/Tests/TwoDimension/CreationTests.cs b/Tests/TwoDimension/CreationTests.cs new file mode 100644 index 0000000..610ec95 --- /dev/null +++ b/Tests/TwoDimension/CreationTests.cs @@ -0,0 +1,138 @@ +using Moq; + +using NUnit.Framework; + +using TileSystem.Interfaces.Base; +using TileSystem.Interfaces.TwoDimension; +using TileSystem.Implementation.TwoDimension; +using TileSystem.Interfaces.Creation; + +namespace Tests.TwoDimension +{ + [TestFixture] + [Category("TwoDimension")] + public class CreationTests + { + [Test] + public void CreateArea() + { + // Create mock factories + var mockAreaFactory = new Mock(); + var mockTileFactory = new Mock(); + var mockEntityFactory = new Mock(); + + // Create a mock position that will be used with the area set position + var mockPosition2D = new Mock(); + + // Set up area factory so it will have a mock area to invoke without throwing in the create area + mockAreaFactory.Setup(factory => factory.CreateArea(null, null)).Returns(new Mock().Object); + + // Create level with mock factories + Level level = new Level(mockAreaFactory.Object, mockTileFactory.Object, mockEntityFactory.Object); + + bool createdCalled = false; + + // Register added event and make sure it is called + level.AreaCreated += (sender, args) => + { + createdCalled = true; + }; + + // Test Null + Assert.That(() => level.CreateArea(null, mockPosition2D.Object, null, null), Throws.ArgumentNullException); + + // Test Null + Assert.That(() => level.CreateArea(level, null, null, null), Throws.ArgumentNullException); + + // Assert add event was not called + Assert.IsFalse(createdCalled); + + // Test Create Works + Assert.That(() => level.CreateArea(level, mockPosition2D.Object, null, null), Throws.Nothing); + + // Assert add event was called + Assert.IsTrue(createdCalled); + } + + [Test] + public void CreateTile() + { + // Create mock factories + var mockAreaFactory = new Mock(); + var mockTileFactory = new Mock(); + var mockEntityFactory = new Mock(); + + // Create a mock position that will be used with the tile set position + var mockPosition2D = new Mock(); + // Create a mock area that will be used with tile set position + var mockArea = new Mock(); + + // Set up tile factory so it will have a mock tile to invoke without throwing in the create tile + mockTileFactory.Setup(factory => factory.CreateTile(null, null)).Returns(new Mock().Object); + + // Create level with mock factories + Level level = new Level(mockAreaFactory.Object, mockTileFactory.Object, mockEntityFactory.Object); + + bool createdCalled = false; + + // Register added event and make sure it is called + level.TileCreated += (sender, args) => + { + createdCalled = true; + }; + + // Test Null + Assert.That(() => level.CreateTile(null, mockPosition2D.Object, null, null), Throws.ArgumentNullException); + + // Test Null + Assert.That(() => level.CreateTile(mockArea.Object, null, null, null), Throws.ArgumentNullException); + + // Assert add event was not called + Assert.IsFalse(createdCalled); + + // Test Create Works + Assert.That(() => level.CreateTile(mockArea.Object, mockPosition2D.Object, null, null), Throws.Nothing); + + // Assert add event was called + Assert.IsTrue(createdCalled); + } + + [Test] + public void CreateEntity() + { + // Create mock factories + var mockAreaFactory = new Mock(); + var mockTileFactory = new Mock(); + var mockEntityFactory = new Mock(); + + // Create a mock tile that will be used with entity set parent + var mockTile = new Mock(); + + // Set up entity factory so it will have a mock entity to invoke without throwing in the create entity + mockEntityFactory.Setup(factory => factory.CreateEntity(null, null)).Returns(new Mock().Object); + + // Create level with mock factories + Level level = new Level(mockAreaFactory.Object, mockTileFactory.Object, mockEntityFactory.Object); + + bool createdCalled = false; + + // Register added event and make sure it is called + level.EntityCreated += (sender, args) => + { + createdCalled = true; + }; + + // Test Null + Assert.That(() => level.CreateEntity(null, null, null), Throws.ArgumentNullException); + + // Assert add event was not called + Assert.IsFalse(createdCalled); + + // Test Create Works + Assert.That(() => level.CreateEntity(mockTile.Object, null, null), Throws.Nothing); + + // Assert add event was called + Assert.IsTrue(createdCalled); + } + } +} diff --git a/Tests/TwoDimension/EntityTests.cs b/Tests/TwoDimension/EntityTests.cs index e94620a..2bfe120 100644 --- a/Tests/TwoDimension/EntityTests.cs +++ b/Tests/TwoDimension/EntityTests.cs @@ -60,7 +60,7 @@ public void EntityDestroyTileRemove() var mockTile = new Mock(); // Set Tile Parent - entity.SetParent(mockTile.Object); + entity.SetTile(mockTile.Object); // Destroy entity.Destroy(); @@ -70,15 +70,15 @@ public void EntityDestroyTileRemove() } [Test] - public void SetParent() + public void SetTile() { Entity entity = new Entity(); var mockTile = new Mock(); // Test Null - Assert.That(() => entity.SetParent(null), Throws.ArgumentNullException); + Assert.That(() => entity.SetTile(null), Throws.ArgumentNullException); // Test Set Parent Works - Assert.That(() => entity.SetParent(mockTile.Object), Throws.Nothing); + Assert.That(() => entity.SetTile(mockTile.Object), Throws.Nothing); Assert.AreSame(entity.Tile, mockTile.Object); } diff --git a/Tests/TwoDimension/LevelTests.cs b/Tests/TwoDimension/LevelTests.cs new file mode 100644 index 0000000..641e2c0 --- /dev/null +++ b/Tests/TwoDimension/LevelTests.cs @@ -0,0 +1,126 @@ +using Moq; + +using NUnit.Framework; + +using TileSystem.Interfaces.Base; +using TileSystem.Interfaces.TwoDimension; +using TileSystem.Implementation.TwoDimension; +using TileSystem.Interfaces.Creation; + +namespace Tests.TwoDimension +{ + [TestFixture] + [Category("TwoDimension")] + public class LevelTests + { + [Test] + public void LevelFactoryConstructor() + { + // Create mock factories + var mockAreaFactory = new Mock(); + var mockTileFactory = new Mock(); + var mockEntityFactory = new Mock(); + + // Test Area Null + Assert.That(() => new Level(null, mockTileFactory.Object, mockEntityFactory.Object), Throws.ArgumentNullException); + + // Test Tile Null + Assert.That(() => new Level(mockAreaFactory.Object, null, mockEntityFactory.Object), Throws.ArgumentNullException); + + // Test Entity Null + Assert.That(() => new Level(mockAreaFactory.Object, mockTileFactory.Object, null), Throws.ArgumentNullException); + + // Test Create Works + Assert.That(() => new Level(mockAreaFactory.Object, mockTileFactory.Object, mockEntityFactory.Object), Throws.Nothing); + } + + [Test] + public void AreaAdd() + { + Level level = new Level(); + var mockArea = new Mock(); + + bool addCalled = false; + + // Register added event and make sure it is called + level.AreaAdded += (sender, args) => + { + addCalled = true; + }; + + // Test Null + Assert.That(() => level.Add(null), Throws.ArgumentNullException); + + // Assert add event was not called + Assert.IsFalse(addCalled); + + // Test Add Works + Assert.That(() => level.Add(mockArea.Object), Throws.Nothing); + + // Assert add event was called + Assert.IsTrue(addCalled); + + // Reset before next test + addCalled = false; + + // Test duplicate fails + Assert.That(() => level.Add(mockArea.Object), Throws.ArgumentException); + + // Assert add event was not called + Assert.IsFalse(addCalled); + } + + [Test] + public void AreaRemove() + { + Level level = new Level(); + var mockArea = new Mock(); + + bool removeCalled = false; + + // Register removed event and make sure it is called + level.AreaRemoved += (sender, args) => + { + removeCalled = true; + }; + + // Add Area + level.Add(mockArea.Object); + + // Test Null + Assert.That(() => level.Remove(null), Throws.ArgumentNullException); + + // Assert remove event was not called + Assert.IsFalse(removeCalled); + + // Test Remove (true removing the object) + Assert.That(level.Remove(mockArea.Object), Is.True); + + // Assert remove event was called + Assert.IsTrue(removeCalled); + + // Reset before next test + removeCalled = false; + + // Test Remove (false not removing the object) + Assert.That(level.Remove(mockArea.Object), Is.False); + + // Assert remove event was not called + Assert.IsFalse(removeCalled); + } + + [Test] + public void PositionGet() + { + // TODO: Issue 15 (https://github.com/Wizcorp/TileSystem/issues/15) + Assert.Fail(); + } + + [Test] + public void PositionGetNeighbours() + { + // TODO: Issue 15 (https://github.com/Wizcorp/TileSystem/issues/15) + Assert.Fail(); + } + } +} diff --git a/Tests/TwoDimension/PositionTests.cs b/Tests/TwoDimension/PositionTests.cs new file mode 100644 index 0000000..76e107c --- /dev/null +++ b/Tests/TwoDimension/PositionTests.cs @@ -0,0 +1,34 @@ +using NUnit.Framework; + +using TileSystem.Interfaces.TwoDimension; +using TileSystem.Implementation.TwoDimension; + +namespace Tests.TwoDimension +{ + [TestFixture] + [Category("TwoDimension")] + public class PositionTests + { + [Test] + public void PositionConstructor() + { + int x = 0; + int y = 0; + + // Test Area Null + Assert.That(() => new Position2D(x, y), Throws.Nothing); + + // Create position and check x,y + IPosition2D pos = new Position2D(x, y); + Assert.AreEqual(x, pos.X); + Assert.AreEqual(y, pos.Y); + } + + [Test] + public void CompareTo() + { + // TODO: Issue 11 (https://github.com/Wizcorp/TileSystem/issues/11) + Assert.Fail(); + } + } +} diff --git a/TileSystem/Implementation/TwoDimension/Area.cs b/TileSystem/Implementation/TwoDimension/Area.cs index 14e227e..729705f 100644 --- a/TileSystem/Implementation/TwoDimension/Area.cs +++ b/TileSystem/Implementation/TwoDimension/Area.cs @@ -16,6 +16,10 @@ namespace TileSystem.Implementation.TwoDimension /// public class Area : IArea { + // Position in 2d + private IPosition2D position2d; + + // List of tiles this area contains private List tiles; // Destroyed event from IArea @@ -26,12 +30,15 @@ public class Area : IArea public event EventHandler TileRemoved; // Representation in the system - public string Type { get; private set; } - public string Variation { get; private set; } + public string Type { get; protected set; } + public string Variation { get; protected set; } // Location in the system public ILevel Level { get; private set; } - public IPosition2D Position { get; private set; } + public IPosition Position + { + get { return position2d; } + } /// /// Default constructor sets up a list of ITile @@ -58,7 +65,7 @@ public Area(string type, string variation) : this() /// /// Parent level /// position in 2d - public void SetPosition(ILevel level, IPosition2D position) + public void SetPosition(ILevel level, IPosition position) { if (level == null) { @@ -70,8 +77,15 @@ public void SetPosition(ILevel level, IPosition2D position) throw new ArgumentNullException("position", "Position can not be null"); } + IPosition2D pos = position as IPosition2D; + + if (pos == null) + { + throw new ArgumentException("position must be of type IPosition2D", "position"); + } + Level = level; - Position = position; + position2d = pos; } /// @@ -155,6 +169,11 @@ public virtual void Destroy(bool propagate = false) } } + if (Level != null) + { + Level.Remove(this); + } + if (Destroyed != null) { Destroyed.Invoke(this, new AreaDestroyedArgs()); @@ -167,7 +186,7 @@ public virtual void Destroy(bool propagate = false) /// Formatted string representation of the Area(X,Y, Tile Count) public override string ToString() { - return string.Format("[Area X:{0} Y:{1}, Tiles Count:{2}]", Position.X, Position.Y, tiles.Count); + return string.Format("[Area X:{0} Y:{1}, Tiles Count:{2}]", position2d.X, position2d.Y, tiles.Count); } } } diff --git a/TileSystem/Implementation/TwoDimension/Entity.cs b/TileSystem/Implementation/TwoDimension/Entity.cs index be05eec..c4d4477 100644 --- a/TileSystem/Implementation/TwoDimension/Entity.cs +++ b/TileSystem/Implementation/TwoDimension/Entity.cs @@ -45,7 +45,7 @@ public Entity(string type, string variation) : this() /// Set the parent tile for this entity /// /// Parent Tile - public void SetParent(ITile tile) + public void SetTile(ITile tile) { if (tile == null) { diff --git a/TileSystem/Implementation/TwoDimension/Level.cs b/TileSystem/Implementation/TwoDimension/Level.cs new file mode 100644 index 0000000..eccd3f3 --- /dev/null +++ b/TileSystem/Implementation/TwoDimension/Level.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; + +using TileSystem.Interfaces.Base; +using TileSystem.Interfaces.Creation; +using TileSystem.Interfaces.Management; + +namespace TileSystem.Implementation.TwoDimension +{ + /// + /// Level is the main container for our 2d space, it allows + /// for addition of areas and controls the creation of + /// areas, tiles and entities. + /// + /// Notes: + /// We provide a 2d implementation in this format but this does + /// not have to be used in the same way, all the ICreateX interfaces + /// are designed to be placed anywhere in the structure, however the + /// ILevel, IArea, ITile, IEntity hierarchy is fixed. + /// + public class Level : ILevel, ICreateAreas, ICreateTiles, ICreateEntities + { + // List of areas in the system + private List areas; + + // Area Events + public event EventHandler AreaAdded; + public event EventHandler AreaRemoved; + + // Creation Events + public event EventHandler AreaCreated; + public event EventHandler TileCreated; + public event EventHandler EntityCreated; + + // Factories + public IAreaFactory AreaFactory { get; protected set; } + public ITileFactory TileFactory { get; protected set; } + public IEntityFactory EntityFactory { get; protected set; } + + /// + /// Default constructor sets up a list of IAreas + /// + public Level() + { + areas = new List(); + } + + /// + /// Constructor to allow inject of factories that are used + /// with the creation of Areas, Tiles, and Entities + /// + /// Area factory instance + /// Tile factory instance + /// Entity factory instance + public Level(IAreaFactory areaFactory, ITileFactory tileFactory, IEntityFactory entityFactory) : this() + { + if (areaFactory == null) + { + throw new ArgumentNullException("areaFactory", "Area Factory can not be null"); + } + + if (tileFactory == null) + { + throw new ArgumentNullException("tileFactory", "Tile Factory can not be null"); + } + + if (entityFactory == null) + { + throw new ArgumentNullException("entityFactory", "Entity Factory can not be null"); + } + + AreaFactory = areaFactory; + TileFactory = tileFactory; + EntityFactory = entityFactory; + } + + #region Creation Methods + + /// + /// Create an area in the level specified, with the type and variation + /// + /// Level to add the area to + /// Position to add the area in + /// Type of the area + /// Variation of the type + /// Instantiation parameters + /// Reference to the area created + public IArea CreateArea(ILevel level, IPosition position, string type, string variation, params object[] properties) + { + if (level == null) + { + throw new ArgumentNullException("level", "level can not be null"); + } + + if (position == null) + { + throw new ArgumentNullException("position", "position can not be null"); + } + + IArea area = AreaFactory.CreateArea(type, variation, properties); + + area.SetPosition(level, position); + + level.Add(area); + + if (AreaCreated != null) + { + AreaCreated.Invoke(this, new AreaCreatedArgs(level, area)); + } + + return area; + } + + /// + /// Create a tile in the area specified, with the type and variatiion + /// + /// Area to add the tile to + /// Position to add the tile in + /// Type of the tile + /// Variation of the type + /// Instantiation parameters + /// Reference to the tile created + public ITile CreateTile(IArea area, IPosition position, string type, string variation, params object[] properties) + { + if (area == null) + { + throw new ArgumentNullException("area", "area can not be null"); + } + + if (position == null) + { + throw new ArgumentNullException("position", "position can not be null"); + } + + ITile tile = TileFactory.CreateTile(type, variation, properties); + + tile.SetPosition(area, position); + + area.Add(tile); + + if (TileCreated != null) + { + TileCreated.Invoke(this, new TileCreatedArgs(area, tile)); + } + + return tile; + } + + /// + /// Create an entity on the tile specified, with the type and variation + /// + /// Tile to create the entity on + /// Type of the entity + /// Variation of the type + /// Instantiation parameters + /// Reference to the entity created + public IEntity CreateEntity(ITile tile, string type, string variation, params object[] properties) + { + if (tile == null) + { + throw new ArgumentNullException("tile", "tile can not be null"); + } + + IEntity entity = EntityFactory.CreateEntity(type, variation, properties); + + entity.SetTile(tile); + + tile.Add(entity); + + if (EntityCreated != null) + { + EntityCreated.Invoke(this, new EntityCreatedArgs(tile, entity)); + } + + return entity; + } + + #endregion + + #region Area Methods + + /// + /// Add area to the current level + /// + /// Area you are adding + public void Add(IArea area) + { + if (area == null) + { + throw new ArgumentNullException("area", "Areas can not be null"); + } + + if (areas.Contains(area)) + { + throw new ArgumentException("Duplicate value", "area"); + } + + areas.Add(area); + + if (AreaAdded != null) + { + AreaAdded.Invoke(this, new AreaAddedArgs(area)); + } + } + + /// + /// Remove area from the current level + /// + /// Area to remove + /// true if an area was removed + public bool Remove(IArea area) + { + if (area == null) + { + throw new ArgumentNullException("area", "Areas can not be null"); + } + + bool removed = areas.Remove(area); + + if (removed && AreaRemoved != null) + { + AreaRemoved.Invoke(this, new AreaRemovedArgs(area)); + } + + return removed; + } + + /// + /// Get an area at the position defined + /// + /// Position to look + /// Reference to the area + public IArea Get(IPosition position) + { + // TODO: Issue 15 (https://github.com/Wizcorp/TileSystem/issues/15) + throw new NotImplementedException(); + } + + /// + /// Get a list of neighbours for the specified area + /// + /// Area to get the neighbours around + /// List of IArea which are next to the supplied area + public List GetNeighbours(IArea area) + { + // TODO: Issue 15 (https://github.com/Wizcorp/TileSystem/issues/15) + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/TileSystem/Implementation/TwoDimension/Tile.cs b/TileSystem/Implementation/TwoDimension/Tile.cs index faf4aed..54a5080 100644 --- a/TileSystem/Implementation/TwoDimension/Tile.cs +++ b/TileSystem/Implementation/TwoDimension/Tile.cs @@ -16,6 +16,10 @@ namespace TileSystem.Implementation.TwoDimension /// public class Tile : ITile { + // Position in 2d + private IPosition2D position2d; + + // List of entities this tile contains private List entities; // Destroyed event from ITile @@ -31,7 +35,10 @@ public class Tile : ITile // Location in the system public IArea Area { get; private set; } - public IPosition2D Position { get; private set; } + public IPosition Position + { + get { return position2d; } + } /// /// Default constructor sets up a list of IEntity @@ -57,8 +64,8 @@ public Tile(string type, string variation) : this() /// Set position in the area of the tile /// /// Parent area - /// position in 2d - public void SetPosition(IArea area, IPosition2D position) + /// position + public void SetPosition(IArea area, IPosition position) { if (area == null) { @@ -70,8 +77,15 @@ public void SetPosition(IArea area, IPosition2D position) throw new ArgumentNullException("position", "Position can not be null"); } + IPosition2D pos = position as IPosition2D; + + if (pos == null) + { + throw new ArgumentException("position must be of type IPosition2D", "position"); + } + Area = area; - Position = position; + position2d = pos; } /// @@ -133,6 +147,11 @@ public virtual void Destroy(bool propagate = false) } } + if (Area != null) + { + Area.Remove(this); + } + if (Destroyed != null) { Destroyed.Invoke(this, new TileDestroyedArgs()); @@ -145,7 +164,7 @@ public virtual void Destroy(bool propagate = false) /// Formatted string representation of the Tile(X,Y, Entity Count) public override string ToString() { - return string.Format("[Tile X:{0} Y:{1}, Entities Count:{2}]", Position.X, Position.Y, entities.Count); + return string.Format("[Tile X:{0} Y:{1}, Entities Count:{2}]", position2d.X, position2d.Y, entities.Count); } } } diff --git a/TileSystem/Interfaces/Base/IArea.cs b/TileSystem/Interfaces/Base/IArea.cs index ed243bf..8f6f429 100644 --- a/TileSystem/Interfaces/Base/IArea.cs +++ b/TileSystem/Interfaces/Base/IArea.cs @@ -30,6 +30,8 @@ public interface IArea : IManageTiles List GetNeighbours(ITile tile); ILevel Level { get; } + IPosition Position { get; } + void SetPosition(ILevel level, IPosition position); event EventHandler Destroyed; void Destroy(bool propagate = false); diff --git a/TileSystem/Interfaces/Base/IEntity.cs b/TileSystem/Interfaces/Base/IEntity.cs index c784a90..dd775a2 100644 --- a/TileSystem/Interfaces/Base/IEntity.cs +++ b/TileSystem/Interfaces/Base/IEntity.cs @@ -10,6 +10,8 @@ namespace TileSystem.Interfaces.Base /// /// The Tile is a reference to the containing parent for fast traversing /// + /// Set Parent sets the tile that the entity is attached to + /// /// The Destroyed, Destroy and CleanUp are management functions so you can cleanup /// without triggering the event, much like destroy immediate in Unity /// @@ -19,6 +21,7 @@ public interface IEntity string Variation { get; } ITile Tile { get; } + void SetTile(ITile tile); event EventHandler Destroyed; void Destroy(); diff --git a/TileSystem/Interfaces/Base/ITile.cs b/TileSystem/Interfaces/Base/ITile.cs index 4bcecc5..dacd4b0 100644 --- a/TileSystem/Interfaces/Base/ITile.cs +++ b/TileSystem/Interfaces/Base/ITile.cs @@ -24,6 +24,8 @@ public interface ITile : IManageEntities string Variation { get; } IArea Area { get; } + IPosition Position { get; } + void SetPosition(IArea area, IPosition position); event EventHandler Destroyed; void Destroy(bool propagate = false); diff --git a/TileSystem/Interfaces/Creation/ICreateAreas.cs b/TileSystem/Interfaces/Creation/ICreateAreas.cs index 3f08a1d..304d423 100644 --- a/TileSystem/Interfaces/Creation/ICreateAreas.cs +++ b/TileSystem/Interfaces/Creation/ICreateAreas.cs @@ -5,14 +5,14 @@ namespace TileSystem.Interfaces.Creation { /// - /// ICreateAreas is used by the level to create entities + /// ICreateAreas is used to create areas /// using a normal creational pattern /// /// The level and position will determine where the area is created /// public interface ICreateAreas { - event EventHandler EntityCreated; + event EventHandler AreaCreated; IArea CreateArea(ILevel level, IPosition position, string type, string variation, params object[] properties); IAreaFactory AreaFactory { get; } } diff --git a/TileSystem/TileSystem.csproj b/TileSystem/TileSystem.csproj index 410cc28..47650b2 100644 --- a/TileSystem/TileSystem.csproj +++ b/TileSystem/TileSystem.csproj @@ -36,6 +36,7 @@ +