Merge branch 'master' of github.com:thatcosmonaut/encompass-cs
						commit
						1195f03fb2
					
				|  | @ -43,4 +43,4 @@ workflows: | |||
|             branches: | ||||
|               ignore: /.*/ | ||||
|             tags: | ||||
|               only: /^\d+\.\d+\.\d+(-rc\d+)?$/ | ||||
|               only: /^\d+\.\d+\.\d+(-preview\d+)?$/ | ||||
|  |  | |||
							
								
								
									
										9
									
								
								TODO
								
								
								
								
							
							
						
						
									
										9
									
								
								TODO
								
								
								
								
							|  | @ -1,4 +1,11 @@ | |||
| - implement IImmutableComponent and ITimedComponent | ||||
| - immutable component system? | ||||
| 
 | ||||
| - method to remove all components of a type without destroying Entities | ||||
| - method to remove a component of a type without destroying entity | ||||
| 
 | ||||
| - auto destroy entities that no longer have components  | ||||
| 
 | ||||
| - fast lookup for messages that contain entity references instead of `Where` loop? | ||||
| 
 | ||||
| - look at test coverage | ||||
| - docs | ||||
|  |  | |||
|  | @ -1,15 +0,0 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     [AttributeUsage(AttributeTargets.Class)] | ||||
|     public class TimeDilationPriority : Attribute | ||||
|     { | ||||
|         public int timeDilationPriority; | ||||
| 
 | ||||
|         public TimeDilationPriority(int timeDilationPriority) | ||||
|         { | ||||
|             this.timeDilationPriority = timeDilationPriority; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -27,7 +27,6 @@ namespace Encompass | |||
|         /// <summary> | ||||
|         /// Used when activating time dilation. Lower priority overrides higher priority. | ||||
|         /// </summary> | ||||
|         internal int? timeDilationPriority = null; | ||||
| 
 | ||||
|         private EntityManager entityManager; | ||||
|         private MessageManager messageManager; | ||||
|  | @ -139,9 +138,9 @@ namespace Encompass | |||
|         /// <summary> | ||||
|         /// Returns true if an Entity with the specified ID exists. | ||||
|         /// </summary> | ||||
|         internal bool EntityExists(Guid entityID) | ||||
|         protected bool EntityExists(Entity entity) | ||||
|         { | ||||
|             return entityManager.EntityExists(entityID); | ||||
|             return entityManager.EntityExists(entity.ID); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|  | @ -689,15 +688,10 @@ namespace Encompass | |||
|             componentManager.MarkForRemoval(componentID); | ||||
|         } | ||||
| 
 | ||||
|         private void CheckTimeDilationPriorityExists() | ||||
|         { | ||||
|             if (!timeDilationPriority.HasValue) { throw new TimeDilationPriorityUndefinedException("Engines that activate time dilation must use the TimeDilationPriority attribute."); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Activates the Encompass time dilation system. | ||||
|         /// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown. | ||||
|         /// Engines that have the IgnoresTimeDilation property will ignore all time dilation. | ||||
|         /// If multiple time dilations are active they will be averaged. | ||||
|         /// </summary> | ||||
|         /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> | ||||
|         /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> | ||||
|  | @ -705,14 +699,13 @@ namespace Encompass | |||
|         /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param> | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime) | ||||
|         { | ||||
|             CheckTimeDilationPriorityExists(); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, timeDilationPriority.Value); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Activates the Encompass time dilation system. | ||||
|         /// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown. | ||||
|         /// Engines that have the IgnoresTimeDilation property will ignore all time dilation. | ||||
|         /// If multiple time dilations are active they will be averaged. | ||||
|         /// </summary> | ||||
|         /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> | ||||
|         /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> | ||||
|  | @ -721,14 +714,13 @@ namespace Encompass | |||
|         /// <param name="easeOutTime">The time that will elapse before time is fully undilated.</param> | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime) | ||||
|         { | ||||
|             CheckTimeDilationPriorityExists(); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, timeDilationPriority.Value); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Activates the Encompass time dilation system. | ||||
|         /// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown. | ||||
|         /// Engines that have the IgnoresTimeDilation property will ignore all time dilation. | ||||
|         /// If multiple time dilations are active they will be averaged. | ||||
|         /// </summary> | ||||
|         /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> | ||||
|         /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> | ||||
|  | @ -737,14 +729,13 @@ namespace Encompass | |||
|         /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param> | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) | ||||
|         { | ||||
|             CheckTimeDilationPriorityExists(); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction, timeDilationPriority.Value); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, activeTime, easeOutTime, easeOutFunction); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Activates the Encompass time dilation system. | ||||
|         /// If activating time dilation, make sure the TimeDilationPriority attribute is set or an exception will be thrown. | ||||
|         /// Engines that have the IgnoresTimeDilation property will ignore all time dilation. | ||||
|         /// If multiple time dilations are active they will be averaged. | ||||
|         /// </summary> | ||||
|         /// <param name="factor">The time dilation factor, which is multiplied by real delta time.</param> | ||||
|         /// <param name="easeInTime">The time that will elapse before time is fully dilated, in real time.</param> | ||||
|  | @ -754,8 +745,7 @@ namespace Encompass | |||
|         /// <param name="easeOutFunction">An easing function for the easing out of time dilation.</param> | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) | ||||
|         { | ||||
|             CheckTimeDilationPriorityExists(); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction, timeDilationPriority.Value); | ||||
|             timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +0,0 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class TimeDilationPriorityConflictException : Exception | ||||
|     { | ||||
|         public TimeDilationPriorityConflictException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|     } | ||||
| } | ||||
|  | @ -1,12 +0,0 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace Encompass.Exceptions | ||||
| { | ||||
|     public class TimeDilationPriorityUndefinedException : Exception | ||||
|     { | ||||
|         public TimeDilationPriorityUndefinedException( | ||||
|             string format, | ||||
|             params object[] args | ||||
|         ) : base(string.Format(format, args)) { } | ||||
|     } | ||||
| } | ||||
|  | @ -29,6 +29,16 @@ namespace Encompass | |||
|             return entityManager.GetEntity(entityID); | ||||
|         } | ||||
| 
 | ||||
|         protected IEnumerable<Entity> ReadEntities<TComponent>() where TComponent : struct, IComponent | ||||
|         { | ||||
|             return ReadComponentsIncludingEntity<TComponent>().Select(tuple => tuple.Item2); | ||||
|         } | ||||
| 
 | ||||
|         protected Entity ReadEntity<TComponent>() where TComponent : struct, IComponent | ||||
|         { | ||||
|             return ReadComponentIncludingEntity<TComponent>().Item2; | ||||
|         } | ||||
| 
 | ||||
|         protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent | ||||
|         { | ||||
|             return componentManager.GetComponentsByType<TComponent>().Select(tuple => tuple.Item2); | ||||
|  |  | |||
|  | @ -1,73 +1,76 @@ | |||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Encompass | ||||
| { | ||||
|     internal class TimeManager | ||||
|     { | ||||
|         private TimeDilationData timeDilationData = new TimeDilationData { factor = 1 }; | ||||
|         private bool newTimeDilationData = false; | ||||
|         private TimeDilationData nextFrameTimeDilationData = new TimeDilationData { factor = 1 }; | ||||
|         private List<TimeDilationData> timeDilationDatas = new List<TimeDilationData>(); | ||||
| 
 | ||||
|         private double Linear(double t, double b, double c, double d) | ||||
|         { | ||||
|             return c * t / d + b; | ||||
|         } | ||||
|         private int minPriority = int.MaxValue; | ||||
| 
 | ||||
|         public double TimeDilationFactor | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return timeDilationData.Factor; | ||||
|                 return timeDilationDatas.Count == 0 ? 1 : timeDilationDatas.Select(data => data.Factor).Average(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public bool TimeDilationActive | ||||
|         { | ||||
|             get => TimeDilationFactor != 1; | ||||
|             get => timeDilationDatas.Count != 0; | ||||
|         } | ||||
| 
 | ||||
|         public void Update(double dt) | ||||
|         { | ||||
|             if (newTimeDilationData) | ||||
|             for (var i = timeDilationDatas.Count - 1; i >= 0; i--) | ||||
|             { | ||||
|                 timeDilationData = nextFrameTimeDilationData; | ||||
|             } | ||||
|                 var data = timeDilationDatas[i]; | ||||
| 
 | ||||
|             timeDilationData.elapsedTime += dt; | ||||
|             newTimeDilationData = false; | ||||
|             minPriority = int.MaxValue; | ||||
|         } | ||||
|                 data.elapsedTime += dt; | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, int priority) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, Linear, priority); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, int priority) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, Linear, priority); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction, int priority) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, easeOutFunction, priority); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction, int priority) | ||||
|         { | ||||
|             if (priority <= minPriority) | ||||
|             { | ||||
|                 newTimeDilationData = true; | ||||
|                 nextFrameTimeDilationData = new TimeDilationData | ||||
|                 if (data.elapsedTime > data.easeInTime + data.activeTime + data.easeOutTime) | ||||
|                 { | ||||
|                     elapsedTime = 0, | ||||
|                     easeInTime = easeInTime, | ||||
|                     easeInFunction = easeInFunction, | ||||
|                     activeTime = activeTime, | ||||
|                     easeOutTime = easeOutTime, | ||||
|                     easeOutFunction = easeOutFunction, | ||||
|                     factor = factor | ||||
|                 }; | ||||
|                     timeDilationDatas.RemoveAt(i); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     timeDilationDatas[i] = data; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, Linear); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, Linear); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) | ||||
|         { | ||||
|             ActivateTimeDilation(factor, easeInTime, Linear, activeTime, easeOutTime, easeOutFunction); | ||||
|         } | ||||
| 
 | ||||
|         public void ActivateTimeDilation(double factor, double easeInTime, System.Func<double, double, double, double, double> easeInFunction, double activeTime, double easeOutTime, System.Func<double, double, double, double, double> easeOutFunction) | ||||
|         { | ||||
|             timeDilationDatas.Add(new TimeDilationData | ||||
|             { | ||||
|                 elapsedTime = 0, | ||||
|                 easeInTime = easeInTime, | ||||
|                 easeInFunction = easeInFunction, | ||||
|                 activeTime = activeTime, | ||||
|                 easeOutTime = easeOutTime, | ||||
|                 easeOutFunction = easeOutFunction, | ||||
|                 factor = factor | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -234,22 +234,8 @@ namespace Encompass | |||
|             var writePriorities = new Dictionary<Type, HashSet<int>>(); | ||||
|             var writeMessageToEngines = new Dictionary<Type, List<Engine>>(); | ||||
| 
 | ||||
|             var timeDilationPriorities = new Dictionary<int, HashSet<Engine>>(); | ||||
| 
 | ||||
|             foreach (var engine in engines) | ||||
|             { | ||||
|                 var timeDilationPriorityAttribute = engine.GetType().GetCustomAttribute<TimeDilationPriority>(); | ||||
| 
 | ||||
|                 if (timeDilationPriorityAttribute != null) | ||||
|                 { | ||||
|                     engine.timeDilationPriority = timeDilationPriorityAttribute.timeDilationPriority; | ||||
|                     if (!timeDilationPriorities.ContainsKey(timeDilationPriorityAttribute.timeDilationPriority)) | ||||
|                     { | ||||
|                         timeDilationPriorities.Add(timeDilationPriorityAttribute.timeDilationPriority, new HashSet<Engine>()); | ||||
|                     } | ||||
|                     timeDilationPriorities[timeDilationPriorityAttribute.timeDilationPriority].Add(engine); | ||||
|                 } | ||||
| 
 | ||||
|                 if (engine.GetType().GetCustomAttribute<IgnoresTimeDilation>() != null) | ||||
|                 { | ||||
|                     engine.usesTimeDilation = false; | ||||
|  | @ -344,18 +330,6 @@ namespace Encompass | |||
|                 throw new EngineWriteConflictException(errorString); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var timeDilationEngines in timeDilationPriorities) | ||||
|             { | ||||
|                 var priority = timeDilationEngines.Key; | ||||
|                 var engines = timeDilationEngines.Value; | ||||
|                 if (engines.Count > 1) | ||||
|                 { | ||||
|                     var errorString = "Multiple Engines have the same Time Dilation Priority value: "; | ||||
|                     errorString += string.Join(", ", engines); | ||||
|                     throw new TimeDilationPriorityConflictException(errorString); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             var engineOrder = new List<Engine>(); | ||||
|             foreach (var engine in engineGraph.TopologicalSort()) | ||||
|             { | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|     <TargetFramework>netstandard2.0</TargetFramework> | ||||
|     <RootNamespace>Encompass</RootNamespace> | ||||
|     <PackageId>EncompassECS.Framework</PackageId> | ||||
|     <Version>0.16.0</Version> | ||||
|     <Version>0.17.0-preview2</Version> | ||||
|     <Authors>Evan Hemsley</Authors> | ||||
|     <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
|     <Company>Moonside Games</Company> | ||||
|  | @ -26,4 +26,4 @@ | |||
|     <PackageReference Include="Collections.Pooled" Version="1.0.82"/> | ||||
|     <PackageReference Include="MoonTools.Core.Graph" Version="1.0.0"/> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| </Project> | ||||
|  |  | |||
|  | @ -960,7 +960,6 @@ namespace Tests | |||
| 
 | ||||
|         static double dilatedDeltaTime; | ||||
| 
 | ||||
|         [TimeDilationPriority(0)] | ||||
|         class ActivateTimeDilationEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|  | @ -1001,54 +1000,41 @@ namespace Tests | |||
|             dilatedDeltaTime.Should().BeApproximately(0.3, 0.01); | ||||
|         } | ||||
| 
 | ||||
|         class ActivateTimeDilationWithoutPriorityEngine : Engine | ||||
|         class ActivateTimeDilationLowerFactorEngine : Engine | ||||
|         { | ||||
|             private bool activated = false; | ||||
| 
 | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 ActivateTimeDilation(0.2, 1, 1, 1); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void ActivateTimeDilationWithoutPriorityThrows() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationWithoutPriorityEngine()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|             Assert.Throws<TimeDilationPriorityUndefinedException>(() => world.Update(0.01)); | ||||
|         } | ||||
| 
 | ||||
|         [TimeDilationPriority(0)] | ||||
|         class ActivateTimeDilationLowerPriorityEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 if (!TimeDilationActive) | ||||
|                 if (!activated) | ||||
|                 { | ||||
|                     ActivateTimeDilation(0.2, 1, 1, 1); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     dilatedDeltaTime = dt; | ||||
|                     activated = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [TimeDilationPriority(1)] | ||||
|         class ActivateTimeDilationHigherPriorityEngine : Engine | ||||
|         class ActivateTimeDilationHigherFactorEngine : Engine | ||||
|         { | ||||
|             private bool activated = false; | ||||
| 
 | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 if (!activated) | ||||
|                 { | ||||
|                     ActivateTimeDilation(0.5, 1, 1, 1); | ||||
|                     activated = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static bool timeDilationActive; | ||||
|         class ReadDilatedDeltaTimeEngine : Engine | ||||
|         { | ||||
|             public override void Update(double dt) | ||||
|             { | ||||
|                 if (!TimeDilationActive) | ||||
|                 { | ||||
|                     ActivateTimeDilation(0.5, 1, 1, 1); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     dilatedDeltaTime = dt; | ||||
|                 } | ||||
|                 dilatedDeltaTime = dt; | ||||
|                 timeDilationActive = TimeDilationActive; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -1056,26 +1042,23 @@ namespace Tests | |||
|         public void MultipleActivateTimeDilation() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationLowerPriorityEngine()); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationHigherPriorityEngine()); | ||||
|             worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine()); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine()); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine()); | ||||
| 
 | ||||
|             var world = worldBuilder.Build(); | ||||
| 
 | ||||
|             world.Update(0.01); // activate time dilation | ||||
| 
 | ||||
|             world.Update(0.5); | ||||
|             world.Update(0.5); // 0.3 and 0.375 | ||||
| 
 | ||||
|             dilatedDeltaTime.Should().BeApproximately(0.3, 0.01); | ||||
|         } | ||||
|             dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01); | ||||
|             timeDilationActive.Should().BeTrue(); | ||||
| 
 | ||||
|         [Test] | ||||
|         public void MultipleActivateTimeDilationWithDuplicatePriority() | ||||
|         { | ||||
|             var worldBuilder = new WorldBuilder(); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationEngine()); | ||||
|             worldBuilder.AddEngine(new ActivateTimeDilationLowerPriorityEngine()); | ||||
|             world.Update(5); | ||||
| 
 | ||||
|             Assert.Throws<TimeDilationPriorityConflictException>(() => worldBuilder.Build()); | ||||
|             dilatedDeltaTime.Should().BeApproximately(5, 0.01); | ||||
|             timeDilationActive.Should().BeFalse(); | ||||
|         } | ||||
| 
 | ||||
|         static double undilatedDeltaTime; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue