2017-09-03 03:35:32 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Drawing ;
using System.Drawing.Imaging ;
namespace SpriteLibrary
{
/// <summary>
/// An EventArgs that contains information about Sprites. Most of the Sprite events use
/// this SpriteEventArgs.
/// </summary>
public class SpriteEventArgs : EventArgs
{
/// <summary>
/// If another Sprite is involved in the event (Collision), than that Sprite is included here.
/// It will be null if no other Sprite is involved.
/// </summary>
public Sprite TargetSprite = null ;
/// <summary>
/// The CollisionMethod used in the event. Currently, only rectangle collisions are used
/// </summary>
public SpriteCollisionMethod CollisionMethod = SpriteCollisionMethod . rectangle ;
/// <summary>
/// For the CheckBeforeMove event, newlocation will be the location the sprite is trying
/// to move to. You can adjust the point (move it left, right, up, down) and it will affect
/// the placement of the sprite.
/// </summary>
public Point NewLocation = new Point ( - 1 , - 1 ) ;
/// <summary>
/// Used primarily in the CheckBeforeMove event. If you set cancel to true, then the move fails.
/// You can use this to keep a Sprite from going places where it ought not to go.
/// </summary>
public bool Cancel = false ;
}
/// <summary>
/// A Sprite is an animated image that has a size, position, rotation, and possible vector
/// It tracks where in the animation sequence it is, can report colisions, etc. This SpriteController
/// draws, moves, and deals with most graphical aspects of the sprites for you.
/// </summary>
public class Sprite
{
int _ID = - 1 ;
/// <summary>
/// The Sprite ID as specified by the sprite controller.
/// </summary>
public int ID { get { return _ID ; } private set { _ID = value ; } }
private bool SpriteHasInitialized = false ;
/// <summary>
/// The name of the sprite. Use SetSpriteName(Name) to change this name. Most Named sprites
/// are used to define what a sprite is. Once you have created a named sprite, you usually use
/// <see cref="SpriteController.DuplicateSprite(String)"/> to clone the sprite for use. The basic rule of thumb is
/// to load your sprites from images once, and name the initial sprites. Then, when you go to use
/// those sprites, get duplicates of them. The reason for this is because it takes more processing time to initially
/// create the sprites than it takes to duplicate them.
/// </summary>
public string SpriteName { get ; private set ; }
/// <summary>
/// Set the opacity of the sprite. The value should be between 0 and 1. 1 is solid, 0 is transparent.
/// Sometimes you want to drag a sprite around the map, or show a sprite that "could be there." Setting
/// the sprite opacity is usually how you do that. One warning, however. The opacity value takes effect the
/// next time it is drawn. If the sprite is animating rapidly, it will take effect nearly emmediately. If
/// it is not animating, not moving, or just sitting there, then it may not take effect for quite some time.
/// </summary>
public float Opacity { get { return _opacity ; } set { if ( value < = 1 & & value > = 0 ) _opacity = value ; } }
private float _opacity = 1 ;
/// <summary>
/// Return the name of the sprite that this was duplicated from. A duplicated sprite will have
/// no name, but will have a SpriteOriginName.
/// </summary>
public string SpriteOriginName { get ; private set ; }
SmartImage MyImage ;
private double SpeedAdjust = 1 ;
DateTime LastResetImage = DateTime . UtcNow ;
int _FrameIndex = - 1 ;
/// <summary>
/// This is the frame of the current animation sequence. You can use this if you need to figure out what frame index
/// to resume something at, or something like that.
/// </summary>
public int FrameIndex
{
get { return _FrameIndex ; }
private set { _FrameIndex = value ; }
} //We start out with -1 so we know we need to draw from the begining.
int _AnimationIndex = 0 ;
int AnimationNumberCount = 0 ;
/// <summary>
/// The final frame is the one that gets displayed once the animation has finished.
/// </summary>
private int _FinalFrameAfterAnimation = - 1 ;
private bool SetToAnimateOnce = false ;
private bool _AnimationDone = false ;
/// <summary>
/// Report whether or not the animation has been completed. When you tell a Sprite to AnimateOnce,
/// this will report "false" until the animation sequence has been finished. At that time, the value
/// will be "True." The tricky bit is that this is a boolean. If you have not told a sprite to
/// animate once, it will always return "false." If a sprite is paused, this returns "false." The only
/// time this returns "true" is when you tell a sprite to animate once, or animate a few times, and those
/// times have completed. At that time, this will report "True". If you have a sprite with only one frame,
/// it may not look like it is "animating", but it is. It is simply animating that one frame over and over.
/// So, AnimationDone reports false, unless you have told it to animate_once.
/// </summary>
public bool AnimationDone { get { return _AnimationDone ; } private set { _AnimationDone = value ; } }
private bool ForceRedraw = false ;
private bool NeedsDrawingAtEndOfTick = false ; //We use this to say that we need to draw at end
private System . Windows . Vector SpriteVector ;
/// <summary>
/// The movement speed of the sprite. To make a Sprite move, you need to set the MovementSpeed,
/// the direction (using
/// <see cref="SpriteLibrary.Sprite.SetSpriteDirection(System.Windows.Vector)"/>,
/// <see cref="SpriteLibrary.Sprite.SetSpriteDirectionToPoint(Point)"/>,
/// <see cref="SpriteLibrary.Sprite.SetSpriteDirectionRadians(double)"/>,
/// or <see cref="SpriteLibrary.Sprite.SetSpriteDirectionDegrees(double)"/>), and the
/// <see cref="SpriteLibrary.Sprite.AutomaticallyMoves"/> property.
/// The speed is calculated in pixels per amount of time. A higher number is faster than a lower number.
/// </summary>
public int MovementSpeed = 0 ; //Used in calculating when to move next.
private DateTime LastMovement = DateTime . UtcNow ;
//For tracking if we are moving along a set of points
private List < Point > MovementDestinations = new List < Point > ( ) ;
/// <summary>
/// Tells us if we are in the process of doing a MoveTo operation. This boolean should be the
/// opposite of SpriteReachedEndpoint, but that boolean is poorly named. This is usually the easier
/// one to use.
/// </summary>
public bool MovingToPoint { get { return _MovingToPoint ; } private set { _MovingToPoint = value ; } }
private bool _MovingToPoint = false ;
/// <summary>
/// If we are trying to collide with a sprite, we store that sprite here.
/// </summary>
private Sprite MovingToSprite = null ;
private bool _AutomaticallyMoves = false ; //Does the sprite move in the direction it has been set?
/// <summary>
/// Determine if the sprite automatically moves (you need to give it a direction [using one of the
/// SetSpriteDirection functions] and speed [MovementSpeed = X] also)
/// </summary>
public bool AutomaticallyMoves
{
get { return _AutomaticallyMoves ; }
set
{
_AutomaticallyMoves = value ;
LastMovement = DateTime . UtcNow ;
}
}
private int _Zvalue = 50 ;
/// <summary>
/// A number from 0 to 100. Default = 50. Higher numbers print on top of lower numbers. If you want a sprite to
/// always be drawn on top of other sprites, give it a number higher than 50. If you want a sprite to go under
/// other sprites, make its number lower than 50.
/// </summary>
public int Zvalue
{
get { return _Zvalue ; }
set { _Zvalue = value ; if ( _Zvalue < 0 ) _Zvalue = 0 ; if ( _Zvalue > 100 ) _Zvalue = 100 ; MySpriteController . SortSprites ( ) ; }
}
private bool PausedAnimation = false ;
private bool PausedMovement = false ;
private bool PausedEvents = false ;
private DateTime PausedAnimationTime = DateTime . UtcNow ;
private DateTime PausedMovementTime = DateTime . UtcNow ;
/// <summary>
/// Determine if the sprite will automatically move outside the box. If not, it will hit the side of the box and stick
/// </summary>
public bool CannotMoveOutsideBox = false ; //If set to true, it will not automatically move outside the picture box.
/// <summary>
/// Get or set the animation nimber. It is best to change the animation using ChangeAnimation.
/// It is safer.
/// </summary>
public int AnimationIndex
{
get { return _AnimationIndex ; }
set { if ( value > - 1 & & value < MyImage . AnimationCount ) _AnimationIndex = value ; }
}
/// <summary>
/// The number of animations this sprite has
/// </summary>
public int AnimationCount
{
get { return MyImage . AnimationCount ; }
}
//The location and size of the sprite
int xPositionOnImage = - 1 ;
int yPositionOnImage = - 1 ;
int xPositionOnPictureBox = - 1 ;
int yPositionOnPictureBox = - 1 ;
double xOnVector = - 1 ;
double yOnVector = - 1 ;
int Width = - 1 ;
int Height = - 1 ;
Rectangle MyRectangle { get { return new Rectangle ( xPositionOnImage , yPositionOnImage , Width , Height ) ; } }
bool _HasBeenDrawn = false ;
/// <summary>
/// Report whether or not this Sprite has been drawn. If it has, then it needs to be erased at
/// some point in time.
/// </summary>
public bool HasBeenDrawn { get { return _HasBeenDrawn ; } private set { _HasBeenDrawn = value ; } }
/// <summary>
/// The sprite location as found on the base image. This is usually the easiest location to use.
/// </summary>
public Point BaseImageLocation { get { return new Point ( xPositionOnImage , yPositionOnImage ) ; } }
/// <summary>
/// The sprite location as found on the picture-box that this sprite is associated with. Used when dealing with mouse-clicks
/// </summary>
public Point PictureBoxLocation { get { return new Point ( xPositionOnPictureBox , yPositionOnPictureBox ) ; } }
/// <summary>
/// Return the size of the sprite in reference to the image on which it is drawn. To get the
/// size of the Sprite in relation to the PictureBox, use GetVisibleSize
/// </summary>
public Size GetSize { get { return new Size ( Width , Height ) ; } }
/// <summary>
/// Return the relative size of the Sprite in relation to the PictureBox. If the box has been
/// stretched or shrunk, that affects the visible size of the sprite.
/// </summary>
public Size GetVisibleSize { get { return new Size ( VisibleWidth , VisibleHeight ) ; } }
//This is the rotation of the item. If we change this, we will draw the sprite rotated.
int _Rotation = 0 ;
/// <summary>
/// Change the rotation of the sprite, using degrees. 0 degrees is to the right. 90 is up.
/// 180 left, 270 down. But, if your sprite was drawn facing up, then rotating it 90 degrees
/// will have it pointing left. The angle goes counter-clockwise. The image will be scaled
/// such that it continues to fit within the rectangle that it was originally in. This results
/// in a little bit of shrinking at times, but you should rarely notice that.
/// </summary>
public int Rotation { set { if ( value > = 0 & & value < = 360 ) _Rotation = value ; } get { return _Rotation ; } }
/// <summary>
/// Flip the image when it gets printed. If your sprite is walking left, flipping it will
/// make it look like it is going right.
/// This works great for many things. But, if your program is gobbling memory or CPU, you may need to
/// consider using <see cref="SpriteLibrary.Sprite.AddAnimation(int, bool, bool)">Sprite.AddAnimation</see>
/// </summary>
public bool MirrorHorizontally = false ;
/// <summary>
/// Flip the image when it gets printed. If your sprite looks like it is facing up, doing
/// this will make it look like it faces down.
/// This works great for many things. But, if your program is gobbling memory or CPU, you may need to
/// consider using <see cref="SpriteLibrary.Sprite.AddAnimation(int, bool, bool)">Sprite.AddAnimation</see>
/// </summary>
public bool MirrorVertically = false ;
2017-09-12 14:45:04 +02:00
internal SpriteController MySpriteController ;
2017-09-03 03:35:32 +02:00
private bool _Destroying = false ;
/// <summary>
/// If the Sprite is in the middle of being Destroyed, this is set to true. When a Sprite is
/// Destroyed, it needs to erase itself and do some house-cleaning before it actually vanishes.
/// During this time, you may not want to use it. It is always a good thing to verify a Sprite
/// is not in the middle of being destroyed before you do something important with it. To Destroy
/// a Sprite, use the Sprite.Destroy() function.
/// </summary>
public bool Destroying { get { return _Destroying ; } }
/// <summary>
/// This is true unless we are using MoveTo(point) or MoveTo(list of points) to tell the sprite to move
/// from one place to the next. This boolean tells us if it has finished or not.
/// </summary>
public bool SpriteReachedEndPoint { get { return _SpriteReachedEndPoint ; } internal set { _SpriteReachedEndPoint = value ; } }
bool _SpriteReachedEndPoint = true ;
/// <summary>
/// The visible Height as seen in the PictureBox. It may be stretched, or shrunk from the actual
/// image size.
/// </summary>
public int VisibleHeight { get { return MySpriteController . ReturnPictureBoxAdjustedHeight ( Height ) ; } }
/// <summary>
/// The visible width as seen in the PictureBox. The Sprite may be stretched or shrunk from the
/// actual image size.
/// </summary>
public int VisibleWidth { get { return MySpriteController . ReturnPictureBoxAdjustedWidth ( Width ) ; } }
/// <summary>
/// A Sprite can hold a payload. Use this to store extra information about the various Sprites. Health, Armor,
/// Shoot time, etc. But, to store information in the payload, you need to make a new class of SpritePayload. The syntax
/// for doing so is:
/// <code Lang="C#">
/// public class TankPayload : SpritePayload { public int Armor; public int Speed; }
/// </code>
/// You can access the payload and retrieve the various values.
/// </summary>
public SpritePayload payload = null ; //The user can put anything they want here.
//We changed the payload from being an object. The Object was too vague. By making it a defined type,
//It helps with some level of type protection.
//public object payload = null; //The user can put anything they want here.
private List < Sprite > CollisionSprites = new List < Sprite > ( ) ; //The list of sprites that are colliding with this one
//*********************************
//**** Add event stubs **********
/// <summary>
/// A delegate that has a SpriteEventArgs instead of EventArgs. Used for most
/// of the Sprite events. This allows us to pass more information from sprite events than
/// a basic EventArgs allows for
/// </summary>
/// <param name="sender">The Sprite that triggers the event</param>
/// <param name="e">A SpriteEventArgs class which contains Sprite Event values</param>
public delegate void SpriteEventHandler ( object sender , SpriteEventArgs e ) ;
/// <summary>
/// This event happens right after the sprite is created. Use this to immediately set a
/// sprite to animate once or something like that.
/// </summary>
public event SpriteEventHandler SpriteInitializes = delegate { } ;
/// <summary>
/// This happens when the sprite hits the border of the picture-box.
/// Useful for when you want to have shots explode when they hit the side.
/// </summary>
public event SpriteEventHandler SpriteHitsPictureBox = delegate { } ;
/// <summary>
/// This happens when the sprite has exited the picture box. Useful when you want to
/// keep sprites from traveling on forever after exiting.
/// </summary>
public event SpriteEventHandler SpriteExitsPictureBox = delegate { } ;
/// <summary>
/// Only used when you tell an animation to animate once. At the end of the animation,
/// this function fires off.
/// </summary>
public event SpriteEventHandler SpriteAnimationComplete = delegate { } ;
/// <summary>
/// This happens when two sprites hit each-other. The SpriteEventArgs that is returned
/// contains the sprite that this sprite hits.
/// </summary>
public event SpriteEventHandler SpriteHitsSprite = delegate { } ;
/// <summary>
/// This event fires off before a sprite is drawn. Use it if you have constraints. You
/// can change the location or cancel the move entirely.
/// </summary>
public event SpriteEventHandler CheckBeforeMove = delegate { } ;
/// <summary>
/// This event happens when someone clicks on the sprite (on the rectangle in which the sprite is).
/// If you want the event to fire off only when someone clicks on the visible part of the sprite,
/// use ClickTransparent instead.
/// </summary>
public event SpriteEventHandler Click = delegate { } ;
/// <summary>
/// This event happens when someone clicks on the sprite (on the sprite image itself).
/// If the sprite is sometimes hidden, but you want the click to work even if it is not
/// visible at that instant, use Click instead.
/// </summary>
public event SpriteEventHandler ClickTransparent = delegate { } ;
/// <summary>
/// This event happens when the mouse moves over the sprite, and then pauses. We use the hover timing from the
/// parent form.
/// </summary>
public event SpriteEventHandler MouseHover = delegate { } ;
/// <summary>
/// When the mouse moves over the sprite. Use this for a menu, when you want the menu item to glow when the
/// mouse is over the menu item sprite.
/// </summary>
public event SpriteEventHandler MouseEnter = delegate { } ;
/// <summary>
/// When the mouse moves off the sprite. Use this for a menu, when you want the menu item to stop glowing when
/// the mouse moves away from the menu item sprite.
/// </summary>
public event SpriteEventHandler MouseLeave = delegate { } ;
/// <summary>
/// This event happens when the mouse moves over a non-transparent portion of the sprite, and then pauses.
/// We use the hover timing from the parent form.
/// </summary>
public event SpriteEventHandler MouseHoverTransparent = delegate { } ;
/// <summary>
/// When the mouse moves over a non-transparent portoin of the sprite. Use this for a menu, when you want the
/// menu item to glow when the mouse is over the menu item sprite.
/// </summary>
public event SpriteEventHandler MouseEnterTransparent = delegate { } ;
/// <summary>
/// When the mouse moves off the non-transparent portion of the sprite. Use this for a menu, when you want the
/// menu item to stop glowing when
/// the mouse moves away from the menu item sprite.
/// </summary>
public event SpriteEventHandler MouseLeaveTransparent = delegate { } ;
/// <summary>
/// When the frame of an animation changes. If you want to have something happen every time
/// the foot of your monster comes down, when the swing of your sword is at certain points, etc.
/// Check to see that the Animaton and FrameIndex are what you expect them to be.
/// </summary>
public event SpriteEventHandler SpriteChangesAnimationFrames = delegate { } ;
/// <summary>
/// An event for when you tell a Sprite to MoveTo(Point) a specific point, or, when you
/// tell the Sprite to MoveTo(list of points). When the Sprite has reached the final destination,
/// the Sprite fires off this event.
/// </summary>
public event SpriteEventHandler SpriteArrivedAtEndPoint = delegate { } ;
/// <summary>
/// When you tell a sprite to MoveTo(list of points), this fires off every time it gets to
/// one of the points. When it gets to the final point, only the SpriteAtEndPoint event fires off.
/// </summary>
public event SpriteEventHandler SpriteArrivedAtWaypoint = delegate { } ;
/// <summary>
/// The Sprite has just been told to be destroyed. You might want to do some cleanup.
/// If you need to destroy some payload data, or tell something to cleanup after the sprite
/// this is where to do that.
/// </summary>
public event SpriteEventHandler SpriteBeingDestroyed = delegate { } ;
// **********************************************************
// ************* Start of Sprite Code **********
// *************************************
/// <summary>
/// Generate a new sprite. It takes the image and the width and height. If there are multiple images of that width
/// and height in the image, an animation is created.
/// </summary>
/// <param name="Controller">The sprite controller that manages this sprite</param>
/// <param name="SpriteImage">The image we pull the animation from</param>
/// <param name="width">The width of one animation frame</param>
/// <param name="height">The height of one animation frame</param>
public Sprite ( SpriteController Controller , Image SpriteImage , int width , int height )
{
MySpriteController = Controller ;
ID = MySpriteController . SpriteCount ;
Width = width ;
Height = height ;
MyImage = new SmartImage ( MySpriteController , SpriteImage ) ;
MySpriteController . AddSprite ( this ) ;
}
/// <summary>
/// Generate a new sprite. It takes the image and the width and height. If there are multiple images of that width
/// and height in the image, an animation is created.
/// </summary>
/// <param name="Controller">The sprite controller that manages this sprite</param>
/// <param name="SpriteImage">The image we pull the animation from</param>
/// <param name="SpriteSize">The size of the animation frame</param>
public Sprite ( SpriteController Controller , Image SpriteImage , Size SpriteSize )
{
MySpriteController = Controller ;
ID = MySpriteController . SpriteCount ;
Width = SpriteSize . Width ;
Height = SpriteSize . Height ;
MyImage = new SmartImage ( MySpriteController , SpriteImage ) ;
MySpriteController . AddSprite ( this ) ;
}
/// <summary>
/// Generate a new single-frame sprite from the specified image.
/// </summary>
/// <param name="Controller">The sprite controller that manages this sprite</param>
/// <param name="SpriteImage">The image we pull the animation from</param>
public Sprite ( SpriteController Controller , Image SpriteImage )
{
MySpriteController = Controller ;
ID = MySpriteController . SpriteCount ;
Width = SpriteImage . Width ;
Height = SpriteImage . Height ;
MyImage = new SmartImage ( MySpriteController , SpriteImage ) ;
MySpriteController . AddSprite ( this ) ;
}
/// <summary>
/// Generate a new sprite. It takes a width, height, and the duration in Milliseconds for each frame
/// </summary>
/// <param name="Controller">The sprite controller</param>
/// <param name="SpriteImage">The image we pull the animations from</param>
/// <param name="width">The width of one animation frame</param>
/// <param name="height">the height of one animation frame</param>
/// <param name="durationInMilliseconds">The number of milliseconds each frame is shown for as it animates.</param>
public Sprite ( SpriteController Controller , Image SpriteImage , int width , int height , int durationInMilliseconds )
{
MySpriteController = Controller ;
ID = MySpriteController . SpriteCount ;
Width = width ;
Height = height ;
MyImage = new SmartImage ( MySpriteController , SpriteImage , width , height , durationInMilliseconds ) ;
MySpriteController . AddSprite ( this ) ;
}
/// <summary>
/// Create a Sprite from an animation image, specifying the number of consecutive
/// frames to grab.
/// </summary>
/// <param name="Start">A point on the specified image where we begin grabbing frames</param>
/// <param name="Controller">The Sprite controller we are associating the sprite with</param>
/// <param name="SpriteImage">An image that we grab the frames from</param>
/// <param name="width">The width of one frame</param>
/// <param name="height">The height of one frame</param>
/// <param name="duration">The number of milliseconds each frame is displayed for</param>
/// <param name="Count">The number of frames to grab as a part of this animation</param>
public Sprite ( Point Start , SpriteController Controller , Image SpriteImage , int width , int height , int duration , int Count )
{
MySpriteController = Controller ;
ID = MySpriteController . SpriteCount ;
Width = width ;
Height = height ;
MyImage = new SmartImage ( Start , MySpriteController , SpriteImage , width , height , duration , Count ) ;
MySpriteController . AddSprite ( this ) ;
}
/// <summary>
/// Create a Sprite that is based off of the specified sprite. Clone the Sprite except that
/// we set SpriteName = "" and OrigSpriteName = the OldSprite.SpriteName. That way we know that
/// the sprite was duplicated from the original, and we can still distinguish the original from
/// the duplicate.
/// </summary>
/// <param name="OldSprite">The Sprite to make a copy of</param>
/// <param name="RetainName">If we want to set this sprite name to be that of the original. This is a terrible idea. Never do it.</param>
public Sprite ( Sprite OldSprite , bool RetainName = false )
{
MySpriteController = OldSprite . MySpriteController ;
ID = MySpriteController . SpriteCount ;
Width = OldSprite . Width ;
Height = OldSprite . Height ;
MyImage = OldSprite . MyImage ;
MovementSpeed = OldSprite . MovementSpeed ;
SpriteOriginName = OldSprite . SpriteName ;
//duplicate any eventhandlers we may have added to the one being cloned.
SpriteHitsPictureBox + = OldSprite . SpriteHitsPictureBox ;
SpriteExitsPictureBox + = OldSprite . SpriteExitsPictureBox ;
SpriteHitsSprite + = OldSprite . SpriteHitsSprite ;
SpriteAnimationComplete + = OldSprite . SpriteAnimationComplete ;
SpriteInitializes + = OldSprite . SpriteInitializes ;
CheckBeforeMove + = OldSprite . CheckBeforeMove ;
Click + = OldSprite . Click ;
ClickTransparent + = OldSprite . ClickTransparent ;
SpriteChangesAnimationFrames + = OldSprite . SpriteChangesAnimationFrames ;
SpriteArrivedAtEndPoint + = OldSprite . SpriteArrivedAtEndPoint ;
SpriteArrivedAtWaypoint + = OldSprite . SpriteArrivedAtWaypoint ;
SpriteBeingDestroyed + = OldSprite . SpriteBeingDestroyed ;
MouseEnter + = OldSprite . MouseEnter ;
MouseHover + = OldSprite . MouseHover ;
MouseLeave + = OldSprite . MouseLeave ;
if ( RetainName )
SpriteName = OldSprite . SpriteName ;
MySpriteController . AddSprite ( this ) ;
}
// *******************
/// <summary>
/// Give this sprite a name. This way we can make a duplicate of it by specifying the name
/// </summary>
/// <param name="Name">A string that represents the new name of the sprite</param>
public void SetName ( string Name )
{
SpriteName = Name ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
/// <param name="width">The width of each frame</param>
/// <param name="height">The height of each frame</param>
public void AddAnimation ( Image SpriteImage , int width , int height )
{
int duration = GetAnimationSpeed ( 0 ) ;
MyImage . AddAnimation ( SpriteImage , width , height , duration ) ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
/// <param name="SpriteSize">The size of each frame</param>
public void AddAnimation ( Image SpriteImage , Size SpriteSize )
{
int duration = GetAnimationSpeed ( 0 ) ;
MyImage . AddAnimation ( SpriteImage , SpriteSize . Width , SpriteSize . Height , duration ) ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
public void AddAnimation ( Image SpriteImage )
{
int duration = GetAnimationSpeed ( 0 ) ;
MyImage . AddAnimation ( SpriteImage , SpriteImage . Width , SpriteImage . Height , duration ) ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
/// <param name="duration">The duration the single frame uses before refreshing. 1000 is a good number.</param>
public void AddAnimation ( Image SpriteImage , int duration )
{
MyImage . AddAnimation ( SpriteImage , SpriteImage . Width , SpriteImage . Height , duration ) ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
/// <param name="width">The width of each frame</param>
/// <param name="height">The height of each frame</param>
/// <param name="duration">The time in milliseconds we use for each frame</param>
public void AddAnimation ( Image SpriteImage , int width , int height , int duration )
{
MyImage . AddAnimation ( SpriteImage , width , height , duration ) ;
}
/// <summary>
/// Add another animation to an existing Sprite. After you add animations, you can use
/// ChangeAnimation to select which animation you want the specified sprite to show.
/// For example, you may want to have Animation 0 be a guy walking left, and animation 1 is
/// that same guy walking right. Because we do not specify the number of frames, it starts
/// at the top-left corner and grabs as many frames as it can from the image.
/// </summary>
/// <param name="SpriteImage">The animation image to grab the frames from</param>
/// <param name="width">The width of each frame</param>
/// <param name="height">The height of each frame</param>
/// <param name="duration">The time in milliseconds we use for each frame</param>
/// <param name="Count">The number of frames we grab from the image</param>
/// <param name="Start">The starting position on the Image where we grab the first frame</param>
public void AddAnimation ( Point Start , Image SpriteImage , int width , int height , int duration , int Count )
{
MyImage . AddAnimation ( Start , SpriteImage , width , height , duration , Count ) ;
}
/// <summary>
/// Duplicate an animation, except rotated by the specified number of degrees. For example, if you have
/// a single animation (0), and you want to rotate it by 90 degrees, it will create animation 1 with that
/// rotation to it. In the long haul, generating a few rotated animations is less memory intensive than
/// rotating it on demand.
/// </summary>
/// <param name="AnimationToCopy">An integer value specifying the animation to duplicate</param>
/// <param name="RotationDegrees">The amount of counter-clockwise rotation to add</param>
public void AddAnimation ( int AnimationToCopy , int RotationDegrees )
{
if ( AnimationToCopy < 0 ) return ;
if ( AnimationToCopy > MyImage . AnimationCount ) return ;
MyImage . AddAnimation ( AnimationToCopy , RotationDegrees ) ;
}
/// <summary>
/// Duplicate an animation, except rotated by the specified number of degrees. For example, if you have
/// a single animation (0), and you want to rotate it by 90 degrees, it will create animation 1 with that
/// rotation to it. In the long haul, generating a few rotated animations is less memory intensive than
/// rotating it on demand using the <see cref="Sprite.MirrorHorizontally"/> or <see cref="Sprite.MirrorVertically"/> booleans.
/// </summary>
/// <param name="AnimationToCopy">An integer value specifying the animation to duplicate</param>
/// <param name="MirrorHorizontal">A boolean, stating if we should mirror horizontally</param>
/// <param name="MirrorVertical">A boolean, stating if we should mirror vertically</param>
public void AddAnimation ( int AnimationToCopy , bool MirrorHorizontal , bool MirrorVertical )
{
if ( AnimationToCopy < 0 ) return ;
if ( AnimationToCopy > MyImage . AnimationCount ) return ;
MyImage . AddAnimation ( AnimationToCopy , MirrorHorizontal , MirrorVertical ) ;
}
/// <summary>
/// Start a new animation, but do it just once. You can use AnimateJustAFewTimes(1) to the same effect.
/// Or, you can use AnimateJustAFewTimes with a different number. The SpriteAnimationComplete event will
/// fire off when the animation completes. The variable, Sprite.AnimationDone will be true once the
/// animation finishes animating.
/// </summary>
/// <param name="AnimationFrameToEndOn">Once the animation has finished, display this animation frame.
/// -1, or any number that is not an actual frame, will show the last frame of the animation.</param>
/// <param name="WhichAnimation">The animation index you want to use</param>
public void AnimateOnce ( int WhichAnimation , int AnimationFrameToEndOn = - 1 )
{
AnimateJustAFewTimes ( WhichAnimation , 1 ) ;
_FinalFrameAfterAnimation = AnimationFrameToEndOn ;
}
/// <summary>
/// Start a new animation. It will complete the animation the number of times you specify.
/// For example, if your sprite is walking, and one animation is one step, specifying 4 here
/// will result in your sprite taking 4 steps and then the animation stops. You will want
/// to make sure you are checking for when the animation stops, using the SpriteAnimationComplete event,
/// checking the Sprite.AnimationDone flag.
/// </summary>
/// <param name="WhichAnimation">The animation index you want to use</param>
/// <param name="HowManyAnimations">The number of animations to do before it stops</param>
/// <param name="AnimationFrameToEndOn">Once the animation has finished, display this animation frame.
/// -1, or any number that is not an actual frame, will show the last frame of the animation.</param>
public void AnimateJustAFewTimes ( int WhichAnimation , int HowManyAnimations , int AnimationFrameToEndOn = - 1 )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{ //We have a valid animation. Do it once
SetToAnimateOnce = true ;
AnimationNumberCount = HowManyAnimations ;
//Console.WriteLine("Setting to animate: " + HowManyAnimations);
AnimationDone = false ;
AnimationIndex = WhichAnimation ;
FrameIndex = 0 ;
ForceRedraw = true ;
LastResetImage = DateTime . UtcNow ;
_FinalFrameAfterAnimation = AnimationFrameToEndOn ;
}
}
/// <summary>
/// Start a new animation index from scratch
/// </summary>
/// <param name="WhichAnimation">The animation index you want to use</param>
/// <param name="StartFrame">The first frame you want to start the animation at.</param>
public void ChangeAnimation ( int WhichAnimation , int StartFrame = 0 )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{ //We have a valid animation. Do it once
SetToAnimateOnce = false ;
AnimationDone = false ;
AnimationIndex = WhichAnimation ;
FrameIndex = 0 ;
int NumFrames = MyImage . AnimationFrameCount ( WhichAnimation ) ;
if ( StartFrame > = 0 & & StartFrame < = NumFrames )
FrameIndex = StartFrame ;
ForceRedraw = true ;
LastResetImage = DateTime . UtcNow ; //start from this second.
}
}
/// <summary>
/// Change the animation speed of a particular animation. This looks at the first frame
/// and compares that frame to the speed specified. It adjusts all the animations by the
/// same percentage.
/// </summary>
/// <param name="WhichAnimation">The integer representing the animation to change</param>
/// <param name="newSpeed">The speed in milliseconds for the new animation</param>
public void ChangeAnimationSpeed ( int WhichAnimation , int newSpeed )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{ //We have a valid animation
TimeSpan CurrentTS = MyImage . GetCurrentDuration ( WhichAnimation , 0 ) ;
double Current = CurrentTS . TotalMilliseconds ;
double New = 1 ; //This is 1/2 times slower. 1 is the actual speed...
if ( ( int ) Current ! = newSpeed )
{
//We have a different speed
if ( Current = = 0 ) Current = 10000 ; //A duration of zero should not rotate
if ( newSpeed = = 0 )
New = 1 ;
else
{
New = Current / newSpeed ; //We have a ratio.
}
}
//Console.WriteLine("New SpeedAdjust: " + New.ToString());
SpeedAdjust = New ;
}
}
/// <summary>
/// Change the animation speed of a specific frame. Beware. This affects every sprite using this frame
/// </summary>
/// <param name="WhichAnimation">The index of the animation</param>
/// <param name="WhichFrame">The index of the frame within the animation</param>
/// <param name="newSpeed">The new frame duration in milliseconds</param>
public void ChangeFrameAnimationSpeed ( int WhichAnimation , int WhichFrame , int newSpeed )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{
Animation tAnimation = MyImage . getAnimation ( WhichAnimation ) ;
if ( WhichFrame < 0 | | WhichFrame > = tAnimation . Frames . Count ) return ;
tAnimation . Frames [ WhichFrame ] . Duration = TimeSpan . FromMilliseconds ( newSpeed ) ;
}
}
/// <summary>
/// Get the animation speed of a single frame.
/// </summary>
/// <param name="WhichAnimation">The animation we are looking at</param>
/// <param name="WhichFrame">The index of the frame we wish to get the speed of</param>
/// <returns>-1 if either index is out of range. Otherwise, return the total milliseconds of the specified frame.</returns>
public int GetFrameAnimationSpeed ( int WhichAnimation , int WhichFrame )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{
Animation tAnimation = MyImage . getAnimation ( WhichAnimation ) ;
if ( WhichFrame < 0 | | WhichFrame > = tAnimation . Frames . Count ) return - 1 ;
return ( int ) tAnimation . Frames [ WhichFrame ] . Duration . TotalMilliseconds ;
}
return - 1 ;
}
/// <summary>
/// Return the animation speed of this particualar animation of the sprite.
/// </summary>
/// <param name="WhichAnimation">The animation we are looking at</param>
/// <returns>The speed which was set. The speed is calculated in pixels per amount of time. A higher number is faster than a lower number</returns>
public int GetAnimationSpeed ( int WhichAnimation )
{
if ( WhichAnimation > - 1 & & WhichAnimation < MyImage . AnimationCount )
{
TimeSpan CurrentTS = MyImage . GetCurrentDuration ( WhichAnimation , 0 ) ;
return ( int ) ( CurrentTS . TotalMilliseconds / SpeedAdjust ) ;
}
return - 1 ;
}
private void EraseMe ( bool SkipInvalidate = false )
{
EraseMe ( MyRectangle . Location , SkipInvalidate ) ;
}
private void EraseMe ( Point tLocation , bool SkipInvalidate = false )
{
Image ChangedImage = MySpriteController . BackgroundImage ; //This is the image itself. Changes to this affect what is displayed
Image OriginalImage = MySpriteController . OriginalImage ;
System . Drawing . Rectangle oldPlace = new System . Drawing . Rectangle ( tLocation . X , tLocation . Y , Width , Height ) ;
lock ( ChangedImage ) lock ( OriginalImage )
{
using ( Graphics gx = Graphics . FromImage ( ChangedImage ) )
{
gx . InterpolationMode = System . Drawing . Drawing2D . InterpolationMode . NearestNeighbor ;
gx . DrawImage ( OriginalImage , oldPlace , oldPlace , GraphicsUnit . Pixel ) ;
}
}
if ( ! SkipInvalidate )
MySpriteController . Invalidate ( oldPlace ) ;
//Tell any sprite we overlap with that it needs to redraw
foreach ( Sprite CollidesWith in CollisionSprites . ToList ( ) )
{
CollidesWith . RedrawMeAndAllICollideWith ( ) ;
}
}
private void RedrawMeAndAllICollideWith ( )
{
if ( NeedsDrawingAtEndOfTick ) return ; //we are already doing this
NeedsDrawingAtEndOfTick = true ;
//if (Opacity != 1)
EraseMe ( true ) ;
//foreach (Sprite CollidesWith in CollisionSprites)
//{
// CollidesWith.RedrawMeAndAllICollideWith();
//}
}
private void DrawMe ( bool SkipInvalidate = false , bool ActuallyDraw = false )
{
DrawMe ( MyRectangle . Location , SkipInvalidate , ActuallyDraw ) ;
}
private void DrawMe ( Point tLocation , bool SkipInvalidate = false , bool ActuallyDraw = false )
{
if ( ActuallyDraw )
{
lock ( MySpriteController . BackgroundImage )
{
Image ChangedImage = MySpriteController . BackgroundImage ; //This is the image itself. Changes to this affect what is displayed
System . Drawing . Rectangle ThePlace = new System . Drawing . Rectangle ( tLocation . X , tLocation . Y , MyRectangle . Width , MyRectangle . Height ) ;
ColorMatrix matrix = new ColorMatrix ( ) ;
matrix . Matrix33 = Opacity ;
ImageAttributes attributes = new ImageAttributes ( ) ;
attributes . SetColorMatrix ( matrix , ColorMatrixFlag . Default , ColorAdjustType . Bitmap ) ;
Image MeImage = MyImage . GetImage ( AnimationIndex , FrameIndex , GetVisibleSize ) ;
if ( MirrorHorizontally | | MirrorVertically )
{
MeImage = ( Bitmap ) MeImage . Clone ( ) ;
if ( MirrorHorizontally ) MeImage . RotateFlip ( RotateFlipType . RotateNoneFlipX ) ;
if ( MirrorVertically ) MeImage . RotateFlip ( RotateFlipType . RotateNoneFlipY ) ;
}
GraphicsUnit tUnit = GraphicsUnit . Pixel ;
if ( Rotation ! = 0 & & MeImage ! = null )
{
Image rotatedImage = new Bitmap ( MeImage . Width , MeImage . Height ) ;
using ( Graphics g = Graphics . FromImage ( rotatedImage ) )
{
g . TranslateTransform ( MeImage . Width / 2 , MeImage . Height / 2 ) ; //set the rotation point as the center into the matrix
g . RotateTransform ( Rotation ) ; //rotate
g . TranslateTransform ( - MeImage . Width / 2 , - MeImage . Height / 2 ) ; //restore rotation point into the matrix
g . DrawImage ( MeImage , MeImage . GetBounds ( ref tUnit ) , rotatedImage . GetBounds ( ref tUnit ) , tUnit ) ; //draw the image on the new bitmap
}
//GC.Collect();
MeImage = rotatedImage ;
}
if ( MeImage ! = null )
{
//g.DrawImage(MeImage, new Rectangle(0, 0, MeImage.Width, MeImage.Height), 0, 0, rotatedImage.Width, rotatedImage.Height, tUnit, attributes); //draw the image on the new bitmap
//Graphics.FromImage(ChangedImage).DrawImage(MeImage, ThePlace, MeImage.GetBounds(ref tUnit), tUnit);
Graphics . FromImage ( ChangedImage ) . DrawImage ( MeImage , ThePlace , 0 , 0 , MeImage . Width , MeImage . Height , tUnit , attributes ) ; //draw the image on the new bitmap
//Pen tmpPen = new Pen(Color.Black);
//Graphics.FromImage(ChangedImage).DrawRectangle(tmpPen, MyRectangle);
MySpriteController . Invalidate ( MyRectangle ) ;
}
NeedsDrawingAtEndOfTick = false ;
}
}
else
{
NeedsDrawingAtEndOfTick = true ;
}
}
/// <summary>
/// Actually draw the Sprite. Never use this. It is used by the SpriteController
/// </summary>
internal void ActuallyDraw ( )
{
if ( NeedsDrawingAtEndOfTick )
DrawMe ( false , true ) ;
}
/// <summary>
/// Put the Sprite at a specified location, using the dimentions of the BackgroundImage.
/// Unless you are using coordinates you have gotten from a mouse-click, this is how you want
/// to place a Sprite somewhere. It is the easiest way to track things. But, if you are
/// doing something using mouse-click coordinates, you want to use PutPictureBoxLocation
/// </summary>
/// <param name="NewLocationOnImage">The new point on the Image</param>
public void PutBaseImageLocation ( Point NewLocationOnImage )
{
Point PointOnPictureBox = MySpriteController . ReturnPictureBoxAdjustedPoint ( NewLocationOnImage ) ;
if ( MyRectangle . Location ! = NewLocationOnImage | | ! HasBeenDrawn )
{
if ( HasBeenDrawn )
EraseMe ( ) ;
xPositionOnImage = NewLocationOnImage . X ;
yPositionOnImage = NewLocationOnImage . Y ;
xPositionOnPictureBox = PointOnPictureBox . X ;
yPositionOnPictureBox = PointOnPictureBox . Y ;
xOnVector = NewLocationOnImage . X ;
yOnVector = NewLocationOnImage . Y ;
DrawMe ( NewLocationOnImage ) ;
HasBeenDrawn = true ;
}
}
/// <summary>
/// Put the Sprite at a specified location, using the dimentions of the BackgroundImage.
/// Unless you are using coordinates you have gotten from a mouse-click, this is how you want
/// to place a Sprite somewhere. It is the easiest way to track things. But, if you are
/// doing something using mouse-click coordinates, you want to use PutPictureBoxLocation
/// </summary>
/// <param name="X">The X location on the background image</param>
/// <param name="Y">the Y location on the background image</param>
public void PutBaseImageLocation ( double X , double Y )
{
Point NewLocation = new Point ( ( int ) X , ( int ) Y ) ;
Point actualPoint = MySpriteController . ReturnPictureBoxAdjustedPoint ( NewLocation ) ;
if ( MyRectangle . Location ! = NewLocation | | ! HasBeenDrawn )
{
if ( HasBeenDrawn )
EraseMe ( ) ;
xPositionOnImage = NewLocation . X ;
yPositionOnImage = NewLocation . Y ;
xPositionOnPictureBox = actualPoint . X ;
yPositionOnPictureBox = actualPoint . Y ;
xOnVector = X ;
yOnVector = Y ;
DrawMe ( ) ;
HasBeenDrawn = true ;
}
}
/// <summary>
/// Put the Sprite at a specified location, using the dimentions of the PictureBox.
/// You want to use this if you got your X/Y position from a mouse-click. Otherwise,
/// this is the harder way to track things, particularly if your window can resize. Use
/// PutBaseImageLocation instead.
/// </summary>
/// <param name="NewLocationOnPictureBox">A point on the PictureBox</param>
public void PutPictureBoxLocation ( Point NewLocationOnPictureBox )
{
//We adjust to the location
Point actualPoint = MySpriteController . ReturnPointAdjustedForImage ( NewLocationOnPictureBox ) ;
PutBaseImageLocation ( actualPoint ) ;
xPositionOnPictureBox = NewLocationOnPictureBox . X ;
yPositionOnPictureBox = NewLocationOnPictureBox . Y ;
}
/// <summary>
/// Done when the box resizes. We need to recompute the picturebox location. The resize function
/// automatically calls this. You should never need to do so.
/// </summary>
public void RecalcPictureBoxLocation ( )
{
Point nPoint = MySpriteController . ReturnPictureBoxAdjustedPoint ( new Point ( xPositionOnImage , yPositionOnImage ) ) ;
xPositionOnPictureBox = nPoint . X ;
yPositionOnPictureBox = nPoint . Y ;
}
private void SetupForDrawing ( )
{
//We make sure we have the correct index for the image
TimeSpan duration = DateTime . UtcNow - LastResetImage ;
if ( PausedAnimation )
{
TimeSpan newduration = DateTime . UtcNow - PausedAnimationTime ;
LastResetImage = LastResetImage + newduration ;
PausedAnimationTime = DateTime . UtcNow ;
duration = DateTime . UtcNow - LastResetImage ;
}
double orig = duration . TotalMilliseconds ;
if ( SpeedAdjust ! = 1 )
{
double Adjust = duration . TotalMilliseconds * SpeedAdjust ;
//Console.WriteLine(SpriteName + " " + SpriteOriginName + " " + orig.ToString() + " " + Adjust.ToString());
duration = TimeSpan . FromMilliseconds ( Adjust ) ;
}
if ( MyImage . NeedsNewImage ( AnimationIndex , FrameIndex , duration ) | | ForceRedraw )
{
EraseMe ( ) ;
bool AtEnd = false ;
int oldindex = FrameIndex ;
if ( SetToAnimateOnce & & AnimationNumberCount < = 1 ) AtEnd = true ;
//Console.Write("Changing Animation Frame: " + FrameIndex);
TimeSpan difference = MyImage . ChangeIndex ( AnimationIndex , ref _FrameIndex , duration , AtEnd ) ;
//Console.WriteLine(" To: " + FrameIndex);
SpriteChangesAnimationFrames ( this , new SpriteEventArgs ( ) ) ;
LastResetImage = DateTime . UtcNow - difference ;
if ( SetToAnimateOnce )
{
if ( ( FrameIndex = = 0 & & oldindex ! = FrameIndex ) | | MyImage . AnimationDone ( AnimationIndex , FrameIndex , SetToAnimateOnce ) )
{
//Console.WriteLine("We are at the end frame:");
TimeSpan HowLong = MyImage . GetCurrentDuration ( AnimationIndex , FrameIndex ) ;
if ( difference > HowLong | | FrameIndex = = 0 )
{
AnimationNumberCount - - ;
//Console.WriteLine("One animation done. Now we have: " + AnimationNumberCount);
if ( AnimationNumberCount < 1 )
{
AnimationDone = true ;
if ( _FinalFrameAfterAnimation > = 0 & & _FinalFrameAfterAnimation < AnimationCount )
{
AnimationIndex = _FinalFrameAfterAnimation ;
_FinalFrameAfterAnimation = - 1 ;
}
if ( ! PausedEvents )
SpriteAnimationComplete ( this , new SpriteEventArgs ( ) ) ;
}
}
}
}
DrawMe ( ) ;
ForceRedraw = false ; //We have done a redraw. No need to do it every time
}
}
/// <summary>
/// This is run from the sprite controller every 10 miliseconds. You should never
/// need to call this yourself.
/// </summary>
internal void Tick ( )
{
if ( ! SpriteHasInitialized )
{
SpriteHasInitialized = true ;
SpriteInitializes ( this , new SpriteEventArgs ( ) ) ;
}
if ( HasBeenDrawn )
{
SetupForDrawing ( ) ;
TryToMove ( ) ;
}
}
/// <summary>
/// Return the point that this sprite needs to be shooting for, for the center of this sprite to
/// hit the center of the destination sprite.
/// </summary>
/// <param name="destination">The sprite we are shooting for trying to hit</param>
/// <returns>A point which allows the moving sprite to collide with the destination sprite.</returns>
private Point GetMoveToSpritePoint ( Sprite destination )
{
if ( destination . Destroying ) return new Point ( - 1 , - 1 ) ;
Point DestCenter = destination . GetSpriteBaseImageCenter ( ) ;
Point targetPoint = new Point ( DestCenter . X - Width / 2 , DestCenter . Y - Height / 2 ) ;
return targetPoint ;
}
private void TryToMove ( )
{
if ( AutomaticallyMoves & & MovementSpeed > 1 )
{
//Now, we move it.
//Take 1 sec and divide it by the speed. That is how many ms we need before we move
double MS = 50 / MovementSpeed ;
if ( MS < 2 ) MS = 2 ;
TimeSpan Elapsed = DateTime . UtcNow - LastMovement ;
if ( PausedMovement )
{
TimeSpan newduration = DateTime . UtcNow - PausedMovementTime ;
LastMovement = LastMovement + newduration ;
PausedMovementTime = DateTime . UtcNow ;
Elapsed = DateTime . UtcNow - LastMovement ;
}
if ( Elapsed . TotalMilliseconds > MS )
{
if ( MovingToSprite ! = null & & ! MovingToSprite . Destroying )
{
MovementDestinations . Clear ( ) ;
Point TargetPoint = GetMoveToSpritePoint ( MovingToSprite ) ;
MovementDestinations . Add ( TargetPoint ) ;
}
else if ( MovingToSprite ! = null & & MovingToSprite . Destroying )
{
MovingToSprite = null ; //We stop shooting for the location of the sprite.
//We are still shooting for where the sprite used to be.
}
if ( MovingToPoint & & MovementDestinations . Count > 0 )
SetSpriteDirectionToPoint ( MovementDestinations [ 0 ] ) ; //recalculate to decrease error
double MovementDelta = Elapsed . TotalMilliseconds / MS ;
double newX = xOnVector + ( MovementDelta * SpriteVector . X ) ;
double newY = yOnVector + ( MovementDelta * SpriteVector . Y ) ;
//if(double.IsNaN(newX) || newX > 1000 || newX < -1000)
//{
// Console.WriteLine("Major issue. NAN: xOnVecor:" + xOnVector.ToString() + " MovementDelta:" + MovementDelta.ToString() + " SpriteVec:" + SpriteVector.X.ToString());
//}
//if (double.IsNaN(newY) || newY > 1000 || newY < -1000)
//{
// Console.WriteLine("Major issue. NAN: yOnVecor:" + yOnVector.ToString() + " MovementDelta:" + MovementDelta.ToString() + " SpriteVec:" + SpriteVector.Y.ToString());
//}
if ( CannotMoveOutsideBox )
{
double tNewx = newX ;
double tNewy = newY ;
Image tImage = MySpriteController . BackgroundImage ;
if ( newX < - 1 ) newX = - 1 ;
if ( newY < - 1 ) newY = - 1 ;
if ( newX > tImage . Width - Width ) newX = ( tImage . Width - Width ) + 1 ;
if ( newY > tImage . Height - Height ) newY = ( tImage . Height - Height ) + 1 ;
if ( tNewx ! = newX | | tNewy ! = newY & & MovingToPoint )
{
//We are not allowed to go outside the box, but our point is outside the box. Cancel
CancelMoveTo ( ) ;
}
}
SpriteEventArgs e = new SpriteEventArgs ( ) ;
e . NewLocation = new Point ( ( int ) Math . Round ( newX ) , ( int ) Math . Round ( newY ) ) ;
if ( MovingToPoint & & MovementDestinations . Count > 0 )
{
//do not go past the destination point
if ( SpriteVector . X < 0 & & newX < MovementDestinations [ 0 ] . X ) newX = MovementDestinations [ 0 ] . X ;
if ( SpriteVector . X > 0 & & newX > MovementDestinations [ 0 ] . X ) newX = MovementDestinations [ 0 ] . X ;
if ( SpriteVector . X = = 0 ) newX = MovementDestinations [ 0 ] . X ;
if ( SpriteVector . Y < 0 & & newY < MovementDestinations [ 0 ] . Y ) newY = MovementDestinations [ 0 ] . Y ;
if ( SpriteVector . Y > 0 & & newY > MovementDestinations [ 0 ] . Y ) newY = MovementDestinations [ 0 ] . Y ;
if ( SpriteVector . Y = = 0 ) newY = MovementDestinations [ 0 ] . Y ;
//Check to see if we have hit the waypoint
//Console.WriteLine("Heading to: " + MovementDestinations[0].ToString() + " At: " + newX.ToString() + " " + newY.ToString());
if ( ( int ) newX = = MovementDestinations [ 0 ] . X & & ( int ) newY = = MovementDestinations [ 0 ] . Y )
{
//check to see if we have hit the endpoint
MovementDestinations . RemoveAt ( 0 ) ; //Yank the destination
if ( MovementDestinations . Count = = 0 )
{
CancelMoveTo ( ) ;
SpriteArrivedAtEndPoint ( this , e ) ;
}
else
{
SpriteArrivedAtWaypoint ( this , e ) ;
SetSpriteDirectionToPoint ( MovementDestinations [ 0 ] ) ;
}
}
}
else if ( MovingToPoint ) {
CancelMoveTo ( ) ;
}
if ( ! PausedEvents )
CheckBeforeMove ( this , e ) ; //See if there is any code to let us go or not go
//if(e.Cancel)
//{
// Console.WriteLine("canceled. " + newX.ToString() + " " + newY.ToString() + " " + MovementDelta.ToString());
//}
if ( ! e . Cancel )
{
//This allows our 'check before move'function to adjust the destination.
PutBaseImageLocation ( e . NewLocation ) ;
}
LastMovement = DateTime . UtcNow ;
CheckForEvents ( ) ;
}
}
}
/// <summary>
/// Resize the sprite using the base image coordinates. The width and height specified
/// are relative to the size of the background image, not the picturebox.
/// </summary>
/// <param name="NewSize">The size (width, height) to make the sprite</param>
public void SetSize ( Size NewSize )
{
if ( NewSize . Width = = MyRectangle . Width & & NewSize . Height = = MyRectangle . Height )
return ; //No need to do anything if we are not making changes
if ( HasBeenDrawn )
{
EraseMe ( ) ; //Erase ourselves
}
Width = NewSize . Width ;
Height = NewSize . Height ;
if ( HasBeenDrawn )
{
DrawMe ( ) ;
}
}
/// <summary>
/// Tell the sprite to kill itself. It will erase itself and then
/// be removed from the spritelist. Then it will be gone forever.
/// </summary>
public void Destroy ( )
{
//If we are not already destroying ourselves
if ( ! _Destroying )
{
//Mark ourselves as being destroyed
_Destroying = true ;
//Erase ourselves
EraseMe ( ) ;
//Remove ourselves from the controller (and the tick process)
SpriteBeingDestroyed ( this , new SpriteEventArgs ( ) ) ;
MySpriteController . DestroySprite ( this ) ;
}
}
/// <summary>
/// Remove the sprite from the field. This does not destroy the sprite. It simply removes it from action.
/// Use UnhideSprite to show it again.
/// </summary>
public void HideSprite ( )
{
EraseMe ( ) ;
HasBeenDrawn = false ;
}
/// <summary>
/// Make the sprite reappear. If you have not positioned it yet, it will show up at the top corner. It is best to only
/// use this when you have hidden it using HideSprite
/// </summary>
public void UnhideSprite ( )
{
DrawMe ( ) ;
HasBeenDrawn = true ;
}
/// <summary>
/// Return true or false, asking if the specifiec sprite is at the point on the picturebox.
/// You can use this with a mouse-click to see if you are clicking on a sprite. Use the
/// SpriteCollisionMethod "transparent" to see if you have clicked on an actual pixel of the
/// sprite instead of just within the sprite rectangle.
/// </summary>
/// <param name="location">The x and y location in ImageBox coordinates.</param>
/// <param name="method">The method of determining if the sprite is at that position</param>
/// <returns>True if the sprite is at the specified location, false if it is not</returns>
public bool SpriteAtPictureBoxPoint ( Point location , SpriteCollisionMethod method = SpriteCollisionMethod . rectangle )
{
//Translate the position to a position on the drawing pane
SpriteAdjustmentRatio PictureBoxRatio = MySpriteController . ReturnAdjustmentRatio ( ) ;
int x = ( int ) ( location . X / PictureBoxRatio . width_ratio ) ;
int y = ( int ) ( location . Y / PictureBoxRatio . height_ratio ) ;
//The x,y is now the pixel in the sprite.
return SpriteAtImagePoint ( new Point ( x , y ) , method ) ;
}
/// <summary>
/// Because sprites are scaled (shrunk or stretched), this function finds the point
/// within the sprite that is specified by the location. this function is used by
/// a number of internal processes, but may be useful to you. But probably not.
/// </summary>
/// <param name="location">A point given in Image coordinates</param>
/// <returns>A point within the pixel that can be used to find a particular pixel in a sprite.</returns>
public Point SpriteAdjustedPoint ( Point location )
{
Point internalPoint = new Point ( location . X - MyRectangle . Location . X , location . Y - MyRectangle . Location . Y ) ;
SpriteAdjustmentRatio Ratio = ReturnAdjustmentRatio ( ) ;
Point AdjustedPoint = new Point ( ( int ) ( internalPoint . X / Ratio . width_ratio ) , ( int ) ( internalPoint . Y / Ratio . height_ratio ) ) ;
return AdjustedPoint ;
}
/// <summary>
/// Check to see if the sprite exists at the point specified. The point given is
/// in coordinates used by the image (not the PictureBox, use SpriteAtPictureBox for that)
/// </summary>
/// <param name="location">An imagebox location</param>
/// <param name="method">the method to use to determine if the image is there</param>
/// <returns>true if the sprite is at that position, false if it is not</returns>
public bool SpriteAtImagePoint ( Point location , SpriteCollisionMethod method = SpriteCollisionMethod . rectangle )
{
if ( location . X < MyRectangle . Location . X ) return false ;
if ( location . X > MyRectangle . Location . X + MyRectangle . Width ) return false ;
if ( location . Y < MyRectangle . Location . Y ) return false ;
if ( location . Y > MyRectangle . Location . Y + MyRectangle . Height ) return false ;
//We need to adjust to the sprite being stretched or shrunk
Point internalPoint = new Point ( location . X - MyRectangle . Location . X , location . Y - MyRectangle . Location . Y ) ;
SpriteAdjustmentRatio Ratio = ReturnAdjustmentRatio ( ) ;
Point AdjustedPoint = SpriteAdjustedPoint ( location ) ;
if ( method = = SpriteCollisionMethod . transparency )
{
Bitmap tImage = ( Bitmap ) GetImage ( ) ;
//Check the point within the sprite
if ( AdjustedPoint . X < 0 | | AdjustedPoint . X > = tImage . Width ) return false ;
if ( AdjustedPoint . Y < 0 | | AdjustedPoint . Y > = tImage . Height ) return false ;
Color OneSpace = tImage . GetPixel ( AdjustedPoint . X , AdjustedPoint . Y ) ;
if ( OneSpace . A = = 0 )
return false ; //It is transparent. No collision at this point. 255 is solid
return true ;
}
if ( method = = SpriteCollisionMethod . circle )
{
Point center = new Point (
MyRectangle . Width / 2 ,
MyRectangle . Height / 2 ) ;
double _xRadius = MyRectangle . Width / 2 ;
double _yRadius = MyRectangle . Height / 2 ;
if ( _xRadius < = 0.0 | | _yRadius < = 0.0 )
return false ;
/ * This is a more general form of the circle equation
*
* X ^ 2 / a ^ 2 + Y ^ 2 / b ^ 2 < = 1
* /
Point normalized = new Point ( location . X - center . X ,
location . Y - center . Y ) ;
return ( ( double ) ( normalized . X * normalized . X )
/ ( _xRadius * _xRadius ) ) + ( ( double ) ( normalized . Y * normalized . Y ) / ( _yRadius * _yRadius ) )
< = 1.0 ;
}
return true ;
}
/// <summary>
/// return the current image frame. Warning: If you write to this image, it will
/// affect all sprites using this frame.
/// </summary>
/// <returns>An image that is the current sprite frame for the current animation</returns>
public Image GetImage ( )
{
if ( AnimationIndex < 0 ) AnimationIndex = 0 ;
if ( FrameIndex < 0 ) FrameIndex = 0 ;
return MyImage . GetImage ( AnimationIndex , FrameIndex , GetVisibleSize ) ;
}
/// <summary>
/// return the frame for the given index. Warning: If you write to this image, it will
/// affect all sprites using this frame.
/// </summary>
/// <param name="Animation_Index">The Animation index we are trying to find</param>
/// <param name="Frame_Index">The Frame index we are trying to find</param>
/// <returns>An image that is the current sprite frame for the current animation</returns>
public Image GetImage ( int Animation_Index , int Frame_Index )
{
if ( Animation_Index < 0 ) Animation_Index = 0 ;
if ( Animation_Index > MyImage . AnimationCount ) return null ;
if ( Frame_Index < 0 ) Frame_Index = 0 ;
if ( Frame_Index > MyImage . AnimationFrameCount ( Animation_Index ) ) return null ;
return MyImage . GetImage ( Animation_Index , Frame_Index , GetVisibleSize ) ;
}
/// <summary>
/// Replace a sprite image. It will replace the current frame unless you specify both an animation
/// and the frame within the animation you wish to replace. Warning: This replaces the image_frame
/// for every sprite that uses that is based off the same image.
/// </summary>
/// <param name="newimage">The new image to use</param>
/// <param name="animation">The animation you want to change</param>
/// <param name="frame">The frame within the animation you want to change</param>
public void ReplaceImage ( Image newimage , int animation = - 1 , int frame = - 1 )
{
if ( newimage = = null ) return ; //do not replace it with nothing
if ( AnimationIndex < 0 ) AnimationIndex = 0 ;
if ( FrameIndex < 0 ) FrameIndex = 0 ;
if ( animation = = - 1 ) animation = AnimationIndex ;
if ( frame = = - 1 ) frame = FrameIndex ;
MyImage . ReplaceImage ( newimage , animation , frame ) ;
RedrawMeAndAllICollideWith ( ) ;
}
/// <summary>
/// Taking into consideration how the sprite is stretched or shrunk, it
/// returns a SpriteAdjustmentRatio that can be used to work with the sprite
/// itself.
/// </summary>
/// <returns>The current SpriteAdjustmentRatio used to display this sprite</returns>
public SpriteAdjustmentRatio ReturnAdjustmentRatio ( )
{
SpriteAdjustmentRatio Ratio = new SpriteAdjustmentRatio ( ) ;
Image tImage = GetImage ( ) ;
//if(tImage == null) Console.WriteLine(this.ID.ToString());
Ratio . width_ratio = ( double ) MyRectangle . Width / ( double ) tImage . Width ;
Ratio . height_ratio = ( double ) MyRectangle . Height / ( double ) tImage . Height ;
return Ratio ;
}
/// <summary>
/// Return true if the sprite can go to this point and still be on the drawing-board.
/// </summary>
/// <param name="newpoint">The point, given in pixels and corresponding to pixels on the picturebox</param>
/// <returns>true or false</returns>
public bool SpriteCanMoveOnPictureBox ( Point newpoint )
{
return SpriteCanMoveOnImage ( MySpriteController . ReturnPointAdjustedForImage ( newpoint ) ) ;
}
/// <summary>
/// Return true if the sprite can go to this point and still be on the drawing-board.
/// </summary>
/// <param name="newpoint">The point, given in pixels and corresponding to pixels on the background image</param>
/// <returns>true or false</returns>
public bool SpriteCanMoveOnImage ( Point newpoint )
{
Image tImage = MySpriteController . BackgroundImage ;
if ( newpoint . X < 0 ) return false ;
if ( newpoint . X + Width > tImage . Width ) return false ;
if ( newpoint . Y < 0 ) return false ;
if ( newpoint . Y + Height > tImage . Height ) return false ;
return true ;
}
/// <summary>
/// Move to where the destination sprite currently is at. This is a dumb move. It does not take into
/// consideration the movement direction of the destination sprite. So the moving sprite does need to be
/// moving a bit faster than the sprite you are trying to hit for it to do so.
/// </summary>
/// <param name="Destination">The sprite we are trying to hit</param>
public void MoveTo ( Sprite Destination )
{
MovingToSprite = Destination ;
LastMovement = DateTime . UtcNow ;
MovementDestinations . Clear ( ) ;
Point TargetPoint = GetMoveToSpritePoint ( Destination ) ;
MovementDestinations . Add ( TargetPoint ) ;
MovingToPoint = true ;
SpriteReachedEndPoint = false ;
SetSpriteDirectionToPoint ( TargetPoint ) ;
}
/// <summary>
/// Tell the Sprite to move towards a destination. You need to give the sprite a MovementSpeed
/// and tell the sprite that it can automatically move. But the sprite will begin a journey towards
/// that point at the MovementSpeed you have set. When it gets to the point, the SpriteArrivedAtEndPoint event
/// will fire off. Also, the SpriteReachedEnd bool will be true.
/// </summary>
/// <param name="Destination">An image-point that the sprite will move to.</param>
public void MoveTo ( Point Destination )
{
List < Point > tList = new List < Point > ( ) ;
tList . Add ( Destination ) ;
MoveTo ( tList ) ; //This way we only have one function to make.
}
/// <summary>
/// Tell the sprite to move towards each point in turn. The sprite will move in a straight line until the first point.
/// From there it moves to the next point, until it has reached the last point. Every time it reaches a point, the
/// SpriteArrivedAtWaypoint event is triggered. When it reaches the final point in the list, the SpriteArrivedAtEndPoint
/// event is triggered. While the sprite is moving, the SpriteReachedEndPoint attribute is set to false. When it has
/// arrived, it is set to true.
/// </summary>
/// <param name="DestinationList">A list of Image-Points that the sprite will follow, one after the other</param>
public void MoveTo ( List < Point > DestinationList )
{
MovingToSprite = null ; //If we were trying to hit something, we are no longer trying to hit it.
LastMovement = DateTime . UtcNow ;
if ( DestinationList . Count = = 0 ) CancelMoveTo ( ) ; //If we tell it to move nowhere, cancel it
MovementDestinations . Clear ( ) ;
foreach ( Point one in DestinationList )
MovementDestinations . Add ( one ) ;
MovingToPoint = true ;
SpriteReachedEndPoint = false ;
SetSpriteDirectionToPoint ( DestinationList [ 0 ] ) ;
}
/// <summary>
/// Sets the Sprite Moving towards a given point. You are responsible to do something with it once it gets there.
/// If you want it to automatically stop upon reaching it, use MoveTo instead. Actually, the MoveTo function works
/// a lot better than this one. Because of integer rounding and a few other things, this function is a little
/// bit imprecise. If you send it towards a point, it will go in that general direction. The MoveTo function
/// will perpetually recalculate its way to the destination point and actually reach that point. SetSpriteDirectionToPoint
/// will sort-of head in the direction of the point. But MoveTo will go to that point.
/// </summary>
/// <param name="ImagePointDestination">The destination, based off a point on the background image, that we send the sprite towards.</param>
public void SetSpriteDirectionToPoint ( Point ImagePointDestination )
{
double x = ImagePointDestination . X - xPositionOnImage ;
double y = ImagePointDestination . Y - yPositionOnImage ;
double distance = Math . Sqrt ( x * x + y * y ) ;
if ( distance = = 0 ) return ; //No need to go anywhere.
System . Windows . Vector newVec = new System . Windows . Vector ( x / distance , y / distance ) ;
//if(double.IsNaN(newVec.X) || double.IsNaN(newVec.Y))
//{
// Console.WriteLine("SetSpriteDirectionToPoint: Creating invalid vector " + distance);
//}
SetSpriteDirection ( newVec ) ;
//Console.WriteLine("SetDirectionToPoint " + ImagePointDestination.ToString());
}
/// <summary>
/// Cancel a MoveTo command. The sprite will stop moving, and all the waypoints will be removed.
/// </summary>
public void CancelMoveTo ( )
{
SpriteReachedEndPoint = true ;
MovementDestinations . Clear ( ) ;
MovingToSprite = null ; //If we were heading towards a sprite. Stop doing so.
if ( ! MovingToPoint ) return ; //We do not do anything if we are not moving.
MovementSpeed = 0 ;
//AutomaticallyMoves = false;
SetSpriteDirectionDegrees ( 0 ) ; //Basically reset it to nothing
MovingToPoint = false ;
}
/// <summary>
/// Given a "degree" (from 0 to 360, set the direction
/// that the sprite moves automatically. 0 is right, 90 is up, 180 is left
/// and 270 is down.
/// </summary>
/// <param name="AngleInDegrees">the degrees to use</param>
public void SetSpriteDirectionDegrees ( double AngleInDegrees )
{
//convert to Radians and set it with that
double Radians = ConvertDegreesToRadians ( AngleInDegrees ) ;
SetSpriteDirectionRadians ( Radians ) ;
}
/// <summary>
/// Set the sprite direction using Radians. Most people do not want to use this.
/// Use SetSpriteDirectionDegrees instead unless you like math and know what you
/// are doing with Radians.
/// </summary>
/// <param name="AngleInRadians">The angle in radians</param>
public void SetSpriteDirectionRadians ( double AngleInRadians )
{
//Turn it into a vector
System . Windows . Vector newVector = new System . Windows . Vector ( ( float ) Math . Cos ( AngleInRadians ) ,
- ( float ) Math . Sin ( AngleInRadians ) ) ;
SetSpriteDirection ( newVector ) ;
}
/// <summary>
/// Set the sprite direction using a vector. The vector may contain
/// a speed as well as the movement delta (amount of x shift, and amount
/// of y shift.) If so, this function may also affect the movement speed
/// Most people prefer to use SetSpriteDirectionDegrees instead of using
/// vectors.
/// </summary>
/// <param name="newVector">A vector</param>
public void SetSpriteDirection ( System . Windows . Vector newVector )
{
System . Windows . Vector oldVector = newVector ;
//use the specified vector
if ( Math . Round ( newVector . Length ) ! = 1 )
{
double NewSpeed = Math . Round ( newVector . Length ) ;
MovementSpeed = ( int ) NewSpeed ;
}
newVector . Normalize ( ) ;
if ( double . IsNaN ( newVector . X ) | | double . IsNaN ( newVector . Y ) )
{
//Console.WriteLine("SetSpriteDirection: Error setting direction. " + oldVector.ToString());
newVector = oldVector ;
}
SpriteVector = newVector ;
}
/// <summary>
/// Convert a number from degrees to radians.
/// </summary>
/// <param name="Degrees">The number from 0 to 360 in degrees</param>
/// <returns>The corresponding number converted to radians</returns>
public double ConvertDegreesToRadians ( double Degrees )
{
return ( Math . PI / 180.0 ) * Degrees ;
}
/// <summary>
/// Convert a number from radians to degrees.
/// </summary>
/// <param name="Radians">The number of radians</param>
/// <returns>The corresponding number in degrees</returns>
public double ConvertRadiansToDegrees ( double Radians )
{
double degrees = ( 180.0 / Math . PI ) * Radians ;
if ( Radians < 0 ) degrees + = 360 ;
return degrees ;
}
/// <summary>
/// Return the current vector that the sprite is moving along
/// </summary>
/// <returns>The current sprite vector</returns>
public System . Windows . Vector GetSpriteVector ( )
{
return SpriteVector ;
}
/// <summary>
/// Returns the direction the sprite is currently traveling, using Radians.
/// </summary>
/// <returns>The direction in radians that the sprite is traveling in</returns>
public double GetSpriteRadans ( )
{
//double radians = Math.Atan2((double)SpriteVector.Y, (double)SpriteVector.X);
double radians ;
if ( SpriteVector . Y > 0 & & SpriteVector . X > 0 )
{
//calculate it in the other quadrant and then adjust.
radians = Math . Atan2 ( ( double ) SpriteVector . Y , ( double ) SpriteVector . X ) ;
radians = ( 2 * Math . PI ) - radians ;
}
else
radians = Math . Atan2 ( - ( double ) SpriteVector . Y , ( double ) SpriteVector . X ) ;
return radians ;
}
/// <summary>
/// Get the direction that the sprite is traveling in in degrees. You may want to
/// use Math.Round on the results. The value returned is usually just a tiny bit off
/// from what you set it with. For example, if you set the sprite movement direction
/// to be 270 degrees (down), this function may return it as 269.999992. Rounding the
/// number will give it back to you at probably the same direction you set it as.
/// </summary>
/// <returns>A double (it has a decimal place) that represents the direction in degrees</returns>
public double GetSpriteDegrees ( )
{
double radians = GetSpriteRadans ( ) ;
double degrees = ConvertRadiansToDegrees ( radians ) ;
if ( degrees < 0 ) degrees = 360 + degrees ;
return degrees ;
}
/// <summary>
/// Return the centerpoint of the sprite, as found on the background image
/// </summary>
/// <returns>a point with the x and y based off the background image location</returns>
public Point GetSpriteBaseImageCenter ( )
{
Point corner = BaseImageLocation ;
return new Point ( corner . X + Width / 2 , corner . Y + Height / 2 ) ;
}
/// <summary>
/// Return the centerpoint of the sprite, as found on the picturebox
/// </summary>
/// <returns>A point with the x and y found on the picturebox</returns>
public Point GetSpritePictureboxCenter ( )
{
Point corner = PictureBoxLocation ;
return new Point ( corner . X + VisibleWidth / 2 , corner . Y + VisibleHeight / 2 ) ;
}
// ***************** Events ***********
private bool CheckForHittingEdgeOfImage ( )
{
Image tImage = MySpriteController . BackgroundImage ;
bool outOfBounds = false ;
if ( xPositionOnImage < 0 ) outOfBounds = true ;
if ( xPositionOnImage + Width > tImage . Width ) outOfBounds = true ;
if ( yPositionOnImage < 0 ) outOfBounds = true ;
if ( yPositionOnImage + Height > tImage . Height ) outOfBounds = true ;
if ( outOfBounds )
{
if ( ! PausedEvents )
SpriteHitsPictureBox ( this , new SpriteEventArgs ( ) ) ;
}
return outOfBounds ;
}
private bool CheckForExitingImage ( )
{
Image tImage = MySpriteController . BackgroundImage ;
bool outOfBounds = false ;
if ( xPositionOnImage + Width < 0 ) outOfBounds = true ;
if ( xPositionOnImage > tImage . Width ) outOfBounds = true ;
if ( yPositionOnImage + Width < 0 ) outOfBounds = true ;
if ( yPositionOnImage > tImage . Height ) outOfBounds = true ;
if ( outOfBounds )
{
if ( ! PausedEvents )
SpriteExitsPictureBox ( this , new SpriteEventArgs ( ) ) ;
}
return outOfBounds ;
}
private bool CheckToSeeIfSpriteHitsSprite ( Sprite target , SpriteCollisionMethod how )
{
if ( target = = this ) return false ; //We do not collide with ourselves.
if ( target . xPositionOnImage + target . Width < xPositionOnImage ) return false ;
if ( target . xPositionOnImage > xPositionOnImage + Width ) return false ;
if ( target . yPositionOnImage + target . Height < yPositionOnImage ) return false ;
if ( target . yPositionOnImage > yPositionOnImage + Height ) return false ;
//If we get here, we have two rectangles ovelapping.
if ( how = = SpriteCollisionMethod . circle )
{
}
if ( how = = SpriteCollisionMethod . transparency )
{
}
return true ;
}
/// <summary>
/// Check to see if the specified rectangle overlaps with the sprite.
/// </summary>
/// <param name="target">The rectangle we are looking to see if we hit</param>
/// <returns>True if the rectangle overlaps the sprite rectabgle</returns>
public bool SpriteIntersectsRectangle ( Rectangle target )
{
if ( target . X + target . Width < xPositionOnImage ) return false ;
if ( target . X > xPositionOnImage + Width ) return false ;
if ( target . Y + target . Height < yPositionOnImage ) return false ;
if ( target . Y > yPositionOnImage + Height ) return false ;
//If we get here, we have two rectangles ovelapping.
return true ;
}
/// <summary>
/// Check to see if two sprites hit each-other. The sprite collision methods are
/// not all programmed in.
/// </summary>
/// <param name="target">The Sprite we are checking to see if we hit</param>
/// <param name="how">The method we use to determine if they hit</param>
public void CheckSpriteHitsSprite ( Sprite target , SpriteCollisionMethod how )
{
if ( target = = this ) return ;
if ( CheckToSeeIfSpriteHitsSprite ( target , how ) )
{
target . NoteSpriteHitsSprite ( this , how ) ;
NoteSpriteHitsSprite ( target , how ) ;
CollisionSprites . Add ( target ) ;
}
}
/// <summary>
/// This is used when two sprites hit each-other.
/// </summary>
/// <param name="target">The sprite it hits</param>
/// <param name="how">the method for checking</param>
internal void NoteSpriteHitsSprite ( Sprite target , SpriteCollisionMethod how )
{
if ( target = = this ) return ;
SpriteEventArgs newArgs = new SpriteEventArgs ( ) ;
newArgs . TargetSprite = target ;
newArgs . CollisionMethod = how ;
if ( SpriteHitsSprite ! = null )
{
if ( ! PausedEvents )
SpriteHitsSprite ( this , newArgs ) ;
}
}
internal void CheckForEvents ( )
{
if ( ! CheckForExitingImage ( ) )
{
CheckForHittingEdgeOfImage ( ) ;
}
}
internal void ClearCollisionList ( )
{
CollisionSprites . Clear ( ) ;
}
/// <summary>
/// Make the sprite show up in front of all other sprites.
/// </summary>
public void SendToFront ( )
{
MySpriteController . SpriteToFront ( this ) ;
}
/// <summary>
/// Make the sprite go behind all other sprites
/// </summary>
public void SendToBack ( )
{
MySpriteController . SpriteToBack ( this ) ;
}
/// <summary>
/// Pause the sprite. We can pause just the animation (and still let it move), pause movement (and let it animate), or pause everything.
/// </summary>
/// <param name="What">Which aspects of the sprite you want to pause.</param>
public void Pause ( SpritePauseType What = SpritePauseType . PauseAll )
{
if ( ! PausedAnimation & & ( What = = SpritePauseType . PauseAnimation | | What = = SpritePauseType . PauseAll ) )
{
PausedAnimationTime = DateTime . UtcNow ;
PausedAnimation = true ;
}
if ( ! PausedMovement & & ( What = = SpritePauseType . PauseMovement | | What = = SpritePauseType . PauseAll ) )
{
PausedMovementTime = DateTime . UtcNow ;
PausedMovement = true ;
}
if ( What = = SpritePauseType . PauseEvents | | What = = SpritePauseType . PauseAll )
{
PausedEvents = true ;
}
}
/// <summary>
/// unpause the sprite.
/// </summary>
/// <param name="What">Which aspects of the sprite you want to unpause.</param>
public void UnPause ( SpritePauseType What = SpritePauseType . PauseAll )
{
TimeSpan duration ;
if ( PausedAnimation & & What = = SpritePauseType . PauseAnimation | | What = = SpritePauseType . PauseAll )
{
duration = DateTime . UtcNow - PausedAnimationTime ;
LastResetImage = LastResetImage + duration ;
PausedAnimation = false ;
}
if ( PausedMovement & & What = = SpritePauseType . PauseMovement | | What = = SpritePauseType . PauseAll )
{
duration = DateTime . UtcNow - PausedMovementTime ;
LastMovement = LastResetImage + duration ;
PausedMovement = false ;
}
if ( PausedEvents & & What = = SpritePauseType . PauseEvents | | What = = SpritePauseType . PauseAll )
{
PausedEvents = false ;
}
}
/// <summary>
/// Ask if the sprite is paused using the specified sprite type (default is PauseAll)
/// </summary>
/// <param name="What">The spritePauseType to see if the sprite is paused with</param>
/// <returns>True if the sprite is set to pause the specified item, false if not</returns>
public bool IsPaused ( SpritePauseType What = SpritePauseType . PauseAll )
{
if ( What = = SpritePauseType . PauseAnimation & & PausedAnimation ) return true ;
if ( What = = SpritePauseType . PauseMovement & & PausedMovement ) return true ;
if ( What = = SpritePauseType . PauseEvents & & PausedMovement ) return true ;
if ( What = = SpritePauseType . PauseAll & & PausedAnimation & & PausedMovement & & PausedEvents ) return true ;
return false ;
}
internal void ClickedOn ( SpriteCollisionMethod how )
{
if ( how = = SpriteCollisionMethod . rectangle )
Click ( this , new SpriteEventArgs ( ) ) ;
if ( how = = SpriteCollisionMethod . transparency )
ClickTransparent ( this , new SpriteEventArgs ( ) ) ;
}
internal void HoverOver ( )
{
MouseHover ( this , new SpriteEventArgs ( ) ) ;
}
internal void Enter ( )
{
MouseEnter ( this , new SpriteEventArgs ( ) ) ;
}
internal void Leave ( )
{
MouseLeave ( this , new SpriteEventArgs ( ) ) ;
}
internal void HoverOverTransparent ( )
{
MouseHoverTransparent ( this , new SpriteEventArgs ( ) ) ;
}
internal void EnterTransparent ( )
{
MouseEnterTransparent ( this , new SpriteEventArgs ( ) ) ;
}
internal void LeaveTransparent ( )
{
MouseLeaveTransparent ( this , new SpriteEventArgs ( ) ) ;
}
}
}