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;
}
}