diff --git a/TruckLib/ScsMap/FarModel.cs b/TruckLib/ScsMap/FarModel.cs index f97e377..f844e78 100644 --- a/TruckLib/ScsMap/FarModel.cs +++ b/TruckLib/ScsMap/FarModel.cs @@ -37,7 +37,7 @@ public class FarModel : MapItem, IItemReferences /// Models specific to Far Model items which are only visible inside the Far Model /// area. Used if UseMapItems is false. /// - public List Models { get; set; } + public FarModelDataList Models { get; set; } /// /// Map items for which the view distance limit is ignored. Used if UseMapItems is true. @@ -83,7 +83,7 @@ internal FarModel(bool initFields) : base(initFields) protected override void Init() { base.Init(); - Models = new List(); + Models = new FarModelDataList(this); Children = new List(); } diff --git a/TruckLib/ScsMap/FarModelData.cs b/TruckLib/ScsMap/FarModelData.cs index dd1b192..2c130ef 100644 --- a/TruckLib/ScsMap/FarModelData.cs +++ b/TruckLib/ScsMap/FarModelData.cs @@ -8,18 +8,25 @@ namespace TruckLib.ScsMap public struct FarModelData { /// - /// Unit name of the model, as defined in /def/world/far_model.sii. + /// The map node which defines the position and rotation of the model. /// - public Token Model; + public INode Node { get; set; } /// - /// Relative scale per axis. + /// Unit name of the model, as defined in /def/world/far_model.sii. /// - public Vector3 Scale; + public Token Model { get; set; } /// - /// This node defines the position and rotation of the model. + /// Relative scale per axis. /// - public INode Node; + public Vector3 Scale { get; set; } + + public FarModelData(INode node, Token model, Vector3 scale) + { + Node = node; + Model = model; + Scale = scale; + } } } diff --git a/TruckLib/ScsMap/FarModelDataList.cs b/TruckLib/ScsMap/FarModelDataList.cs new file mode 100644 index 0000000..40030a1 --- /dev/null +++ b/TruckLib/ScsMap/FarModelDataList.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace TruckLib.ScsMap +{ + /// + /// Reperesents a list of Far Item models. + /// + public class FarModelDataList : IList + { + /// + /// The Far Model item which parents these models. + /// + public FarModel Parent { get; init; } + + private readonly List list = new(); + + /// + /// Instantiates an empty list. + /// + /// The Far Model item which parents these models. + public FarModelDataList(FarModel parent) + { + Parent = parent; + } + + /// + public FarModelData this[int index] + { + get => list[index]; + set => list[index] = value; + } + + /// + public int Count => list.Count; + + /// + public bool IsReadOnly => false; + + /// + public void Add(FarModelData item) + { + list.Add(item); + } + + /// + /// Creates a map node at the specified position and adds a FarModelData object with + /// the given properties to the end of the list. + /// + /// The position of the node. + /// The unit name of the model. + /// The scale of the model. + public void Add(Vector3 position, Token model, Vector3 scale) + { + Add(new FarModelData(CreateNode(position), model, scale)); + } + + /// + public void Clear() + { + foreach (var item in list) + GetRidOfTheNode(item); + list.Clear(); + } + + /// + public bool Contains(FarModelData item) + { + return list.Contains(item); + } + + /// + public void CopyTo(FarModelData[] array, int arrayIndex) + { + list.CopyTo(array, arrayIndex); + } + + /// + public IEnumerator GetEnumerator() + { + return list.GetEnumerator(); + } + + /// + public int IndexOf(FarModelData item) + { + return list.IndexOf(item); + } + + /// + public void Insert(int index, FarModelData item) + { + list.Insert(index, item); + } + + /// + /// Creates a map node at the specified position and inserts a FarModelData object with + /// the given properties at the specified index. + /// + /// The index. + /// The position of the node. + /// The unit name of the model. + /// The scale of the model. + public void Insert(int index, Vector3 position, Token model, Vector3 scale) + { + Insert(index, new FarModelData(CreateNode(position), model, scale)); + } + + /// + /// Removes the first occurrence of the specified object from the list + /// and deletes its map node if it is not connected to anything else. + /// + /// + public bool Remove(FarModelData item) + { + var success = list.Remove(item); + if (success) + GetRidOfTheNode(item); + return success; + } + + /// + /// Removes the element at the specified index from the list + /// and deletes its map node if it is not connected to anything else. + /// + /// + public void RemoveAt(int index) + { + GetRidOfTheNode(list[index]); + list.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return list.GetEnumerator(); + } + + private Node CreateNode(Vector3 position) + { + return Parent.Node.Sectors[0].Map.AddNode(position, false, Parent); + } + + private static void GetRidOfTheNode(FarModelData item) + { + item.Node.ForwardItem = null; + if (item.Node.IsOrphaned()) + item.Node.Sectors[0].Map.Delete(item.Node); + } + } +} diff --git a/TruckLib/ScsMap/Serialization/FarModelSerializer.cs b/TruckLib/ScsMap/Serialization/FarModelSerializer.cs index 1309c58..de906c7 100644 --- a/TruckLib/ScsMap/Serialization/FarModelSerializer.cs +++ b/TruckLib/ScsMap/Serialization/FarModelSerializer.cs @@ -19,13 +19,15 @@ public override MapItem Deserialize(BinaryReader r) fm.Height = r.ReadSingle() * sizeFactor; // models - fm.Models = new List(); + fm.Models = new FarModelDataList(fm); var modelCount = r.ReadUInt32(); for (int i = 0; i < modelCount; i++) { - var model = new FarModelData(); - model.Model = r.ReadToken(); - model.Scale = r.ReadVector3(); + var model = new FarModelData + { + Model = r.ReadToken(), + Scale = r.ReadVector3() + }; fm.Models.Add(model); } diff --git a/TruckLibTests/TruckLib/ScsMap/FarModelDataListTest.cs b/TruckLibTests/TruckLib/ScsMap/FarModelDataListTest.cs new file mode 100644 index 0000000..edb163f --- /dev/null +++ b/TruckLibTests/TruckLib/ScsMap/FarModelDataListTest.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using TruckLib.ScsMap; + +namespace TruckLibTests.TruckLib.ScsMap +{ + public class FarModelDataListTest + { + [Fact] + public void Add() + { + var map = new Map("foo"); + var fm = FarModel.Add(map, new Vector3(50, 0, 50), 60, 50); + + fm.Models.Add(new Vector3(69, 42, 0), "bar", Vector3.One); + + Assert.Single(fm.Models); + + Assert.Equal("bar", fm.Models[0].Model); + Assert.Equal(Vector3.One, fm.Models[0].Scale); + + Assert.Equal(new Vector3(69, 42, 0), fm.Models[0].Node.Position); + Assert.True(map.Nodes.ContainsKey(fm.Models[0].Node.Uid)); + Assert.False(fm.Models[0].Node.IsRed); + Assert.Equal(fm, fm.Models[0].Node.ForwardItem); + } + + [Fact] + public void Insert() + { + var map = new Map("foo"); + var fm = FarModel.Add(map, new Vector3(50, 0, 50), 60, 50); + + fm.Models.Add(new Vector3(69, 42, 0), "bar", Vector3.One); + fm.Models.Insert(0, new Vector3(12, 34, 56), "baz", Vector3.One); + + Assert.Equal(2, fm.Models.Count); + Assert.Equal(new Vector3(12, 34, 56), fm.Models[0].Node.Position); + } + + [Fact] + public void RemoveAt() + { + var map = new Map("foo"); + var fm = FarModel.Add(map, new Vector3(50, 0, 50), 60, 50); + + fm.Models.Add(new Vector3(69, 42, 0), "bar", Vector3.One); + var fmData = fm.Models[0]; + fm.Models.RemoveAt(0); + + Assert.False(map.Nodes.ContainsKey(fmData.Node.Uid)); + } + + [Fact] + public void Clear() + { + var map = new Map("foo"); + var fm = FarModel.Add(map, new Vector3(50, 0, 50), 60, 50); + + fm.Models.Add(new Vector3(69, 42, 0), "bar", Vector3.One); + var fmData1 = fm.Models[0]; + fm.Models.Add(new Vector3(12, 34, 56), "baz", Vector3.One); + var fmData2 = fm.Models[1]; + fm.Models.Clear(); + + Assert.Empty(fm.Models); + Assert.False(map.Nodes.ContainsKey(fmData1.Node.Uid)); + Assert.False(map.Nodes.ContainsKey(fmData2.Node.Uid)); + } + } +} diff --git a/TruckLibTests/TruckLib/ScsMap/FarModelTest.cs b/TruckLibTests/TruckLib/ScsMap/FarModelTest.cs new file mode 100644 index 0000000..896c71f --- /dev/null +++ b/TruckLibTests/TruckLib/ScsMap/FarModelTest.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TruckLib.ScsMap; +using System.Numerics; +using System.Drawing; + +namespace TruckLibTests.TruckLib.ScsMap +{ + public class FarModelTest + { + [Fact] + public void Add() + { + var map = new Map("foo"); + var fm = FarModel.Add(map, new Vector3(50, 0, 50), 60, 50); + + Assert.Equal(new Vector3(50, 0, 50), fm.Node.Position); + Assert.True(fm.Node.IsRed); + Assert.Equal(fm, fm.Node.ForwardItem); + Assert.Null(fm.Node.BackwardItem); + Assert.True(fm.Node.Sectors.Length == 1); + Assert.Equal(0, fm.Node.Sectors[0].X); + Assert.Equal(0, fm.Node.Sectors[0].Z); + } + + } + +}