using System; using System.Collections.Generic; using Chernobyl.Collections.Generic.Event; using Chernobyl.Dependency; using Chernobyl.Graphics; using Chernobyl.Graphics.Drawing; using Chernobyl.Graphics.Material.Shader.Parameters; using Chernobyl.Graphics.Texture; using Chernobyl.Mathematics; using Chernobyl.Mathematics.Collision; using Chernobyl.Mathematics.Geometry; using Chernobyl.Mathematics.Mechanics; using Chernobyl.Mathematics.Movement; using Chernobyl.Mathematics.Vectors; using Chernobyl.Readiness; using Chernobyl.Update; namespace Chernobyl.Interface.Tool { /// /// The default implementation of the ICursor. Represents an on screen /// cursor that is driven by some input device like a mouse or gamepad. Do /// NOT handle the type because not all cursors derive /// from . Instead, handle the type . /// public class Cursor : Sprite, ICursor { /// /// Constructs a Cursor instance that uses a sprite for its drawing and /// transformations. /// /// The service holder instance that takes /// services and gives them out. /// The path to the texture file or /// the name of the texture resource to use as the /// instance's image. /// The that drives this /// . public Cursor(IEventCollection services, string textureFilePathOrName, ITransform control) : this(services, new Texture2D(services, textureFilePathOrName), control, null) { } /// /// Constructs a Cursor instance that uses a sprite for its drawing and /// transformations. /// /// The service holder instance that takes /// services and gives them out. /// The texture to use for the image of the /// . /// The that drives this /// . public Cursor(IEventCollection services, ITexture texture, ITransform control) : this(services, texture, control, null) { } /// /// Constructs a Cursor instance that uses a sprite for its drawing and /// transformations. /// /// The service holder instance that takes /// services and gives them out. /// The texture to use for the image of the /// . /// The that drives this /// . /// The amount to scale the /// by in the both the X and Y axes or null /// if the default scale amount specified by /// is to be used. public Cursor(IEventCollection services, ITexture texture, ITransform control, Vector2? collisionGroupScaleAmount) : base(services, texture) { Control = control; // create the Point2D at the upper left corner of the sprite. Note // that, this only works if the sprite is using a mesh element // square that is 1x1 (which is what it uses if you specify a simple // texture). Point = new Point2D(-.5f, .5f); PreviousCollidables = new LinkedList>(); if (collisionGroupScaleAmount.HasValue == true) _collisionCollectionScaleAmount = collisionGroupScaleAmount.Value; _services = services; _services.Inject(this); } /// /// Updates the cursor by performing a collision check against the /// collidableCollection held by this cursor and calling the /// ICollidable.HandleCollision() method on all the collided with objects /// including this cursor. /// /// The change in time since last update. public void Update(TimeSpan deltaTime) { if(CollidableCollection != null) { // get our collision point and update our collidable with this new // point IEnumerable> collidables = CollidableCollection.GetCollidables(Point); // inform both sides of the collision. LinkedList> currentCollidables = new LinkedList>(); foreach (IExtendedCollidable coll in collidables) { if (PreviousCollidables.Remove(coll) == false) { // we have never hit this collidable; call the start of the // collision handling and add it to our previous collidables // list HandleCollisionStarted(coll); coll.HandleCollisionStarted(this); } // add this to our current collidables list currentCollidables.AddLast(coll); } // Any collidables left were not hit this time around so we handle // the collision ending and remove them from our previous collidables // list foreach (ICollidable2D coll in PreviousCollidables) { HandleCollisionEnded(coll); coll.HandleCollisionEnded(this); } // now update our previous collidable list with our current collidables PreviousCollidables = currentCollidables; } } /// /// The first button on the cursor. This button is used to click on objects, /// select those objects, choose from lists, etc. This button is often /// the left mouse button. /// public IButton FirstButton { // TODO: get rid of this property. Do the same for ICursor. get { throw new NotSupportedException("Cursor.FirstButton has " + "been deprecated."); } set { throw new NotSupportedException("Cursor.FirstButton has " + "been deprecated."); } } /// /// The second button on the cursor. This button is used to click on /// objects, select those objects, choose from lists, etc. This button /// is often the right mouse button. /// public IButton SecondButton { // TODO: get rid of this property. Do the same for ICursor. get { throw new NotSupportedException("Cursor.SecondButton has " + "been deprecated."); } set { throw new NotSupportedException("Cursor.SecondButton has " + "been deprecated."); } } /// /// The third button on the cursor. This button is used to click on /// objects, select those objects, choose from lists, etc. This button /// is often the middle mouse button. /// public IButton ThirdButton { // TODO: get rid of this property. Do the same for ICursor. get { throw new NotSupportedException("Cursor.ThirdButton has " + "been deprecated."); } set { throw new NotSupportedException("Cursor.ThirdButton has " + "been deprecated."); } } /// /// The parent of this IUpdateable or null if this IUpdateable does not /// have a parent. /// public IUpdateable UpdateableParent { get; set; } /// /// The updateable children of this cursor. /// public ICollection UpdateableChildren { get; private set; } /// /// The collision group whose collidables are checked for collisions. /// public ICollidableCollection, Point2D> CollidableCollection { get; private set; } /// /// Sets the that will become the /// of this cursor. /// [Inject(Type = typeof(IRootUpdateable))] public IUpdateable UpdateableService { set { Updateable.MakeParentChild(value, this); } } /// /// The instance that gives information about the back buffer viewport. /// [Inject] public IBackBufferViewport BackBufferViewport { set { IBackBufferViewport previousBackBufferViewport = _backBufferViewport; _backBufferViewport = value; // Unconfigure from the old IBackBufferViewport if(previousBackBufferViewport != null) Transform.SeperateParentChild(previousBackBufferViewport.ViewArea, _viewArea); // Configure with the new IBackBufferViewport if (_backBufferViewport != null) { // Create the collidable collection for this cursor, this // collidable collection will cover the entire plus the scale // amount specified by _collisionCollectionScaleAmount. _viewArea = new Rectangle(-_collisionCollectionScaleAmount.X * .5f, -_collisionCollectionScaleAmount.Y * .5f, _collisionCollectionScaleAmount.X, _collisionCollectionScaleAmount.Y); Transform.MakeParentChild(_backBufferViewport.ViewArea, _viewArea); ICollidableCollection, Point2D> previousCollidableCollection = CollidableCollection; CollidableCollection = new QuadTree, Point2D>(_viewArea); // If we had a previous CollidableCollection than we need // to add the items from it to the new CollidableCollection. if (previousCollidableCollection != null) { foreach (IExtendedCollidable extendedCollidable in previousCollidableCollection) CollidableCollection.Add(extendedCollidable); } // Attach together the controller and the IRender's parent. // The Render.TransformParent is not set until // Sprite.TextureParameterValueBecameReady(object, EventArgs) // has been invoked. So we will not parent the Render.TransformParent // if it is not ready. That will be done in the // Cursor.TextureParameterValueBecameReady(object, EventArgs) // method. if (Render.TransformParent != null) Transform.MakeParentChild(Control, Render.TransformParent); // attach together this cursor and the point Transform.MakeParentChild(this, Point); } } } /// /// The instance that controls the position of interface elements on /// the screen. This will be used by to make sure /// it is placed at the top of the screen. /// [Inject] public Interface.LayerBucket Layers { set { if (_layerTransform == null) { _layerTransform = new MatrixTransform(); _layerTransform.TransformDirtied += LayerTransformDirtied; } Interface.LayerBucket previousValue = _layers; _layers = value; if(previousValue != null) Transform.SeperateParentChild(previousValue.TopLayerTransform, _layerTransform); if(_layers != null) Transform.MakeParentChild(_layers.TopLayerTransform, _layerTransform); } } /// /// An event that is invoked by the /// event of the instance. This method is used to /// ensure this maintains its Z axis position /// closer to the screen. /// /// The instance that generated the event. /// The instance containing the event data. void LayerTransformDirtied(object sender, EventArgs e) { float zPosition = _layerTransform.WorldMatrix.Translation.Z; Matrix4 local = LocalMatrix; local.Row3 = new Vector4(local.Row3.X, local.Row3.Y, zPosition, local.Row3.W); SetTransformation(ref local); } /// /// The default amount to scale the by in /// the X and Y axes. /// public static readonly Vector2 DefaultCollisionGroupScaleAmount = new Vector2(2, 2); /// /// An event handler that is invoked when the /// instance's has become ready (i.e., /// is raised). This method /// ensures the is the parent of the /// instances' . /// The instances' /// is not set until /// has been called. /// /// The instance that generated the event. /// The instance containing /// the event data. protected override void TextureParameterValueBecameReady(object sender, EventArgs e) { base.TextureParameterValueBecameReady(sender, e); // Make sure we've set the Render's parent. if(Render.TransformParent.TransformParent != Control) Transform.MakeParentChild(Control, Render.TransformParent); } /// /// The that drives this . /// ITransform Control { get; set; } /// /// Collidables that were hit previously and may need to have their /// HandleCollisionEnded() method called when they have stopped colliding. /// LinkedList> PreviousCollidables { get; set; } /// /// The point of this cursor that can be collided against. /// Point2D Point { get; set; } /// /// The collidable that implements the collision of this object. /// protected override ICollidable2D CollidableImplementer { get { return Point; } } /// /// The start of the transform chain of this entity. If there is only /// one transform in the chain, then the TransformStart will equal the /// TransformEnd. /// protected override ITransform TransformStart { get { return Control; } } /// /// The amount to scale the by in the /// both the X and Y axes. /// Vector2 _collisionCollectionScaleAmount = DefaultCollisionGroupScaleAmount; /// /// The representing the view area after it has /// been scaled by . /// Rectangle _viewArea; /// /// The instance received by this /// in its constructor. /// readonly IEventCollection _services; /// /// The instance that is added to the /// of /// . This instance controls the Z position of this /// and is responsible for ensuring this /// stays at the top of the interface. /// ITransform _layerTransform; /// /// The backing field to . /// IBackBufferViewport _backBufferViewport; /// /// The backing field to . /// Interface.LayerBucket _layers; } }