2018-12-30 18:00:05 +08:00
using System ;
using System.Collections.Generic ;
using Microsoft.Xna.Framework ;
2017-09-12 09:35:31 +08:00
using Microsoft.Xna.Framework.Graphics ;
2019-08-28 07:47:22 +08:00
using Newtonsoft.Json ;
2018-03-06 12:46:45 +08:00
using StardewValley ;
using StardustCore.UIUtilities ;
2017-09-12 09:35:31 +08:00
namespace StardustCore.Animations
{
2018-12-30 18:00:05 +08:00
/// <summary>Used to play animations for Stardust.CoreObject type objects and all objects that extend from it. In draw code of object make sure to use this info instead.</summary>
public class AnimationManager
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
public Dictionary < string , List < Animation > > animations = new SerializableDictionary < string , List < Animation > > ( ) ;
public string currentAnimationName ;
public int currentAnimationListIndex ;
public List < Animation > currentAnimationList = new List < Animation > ( ) ;
2019-07-18 01:18:51 +08:00
public Texture2DExtended objectTexture ; ///Might not be necessary if I use the CoreObject texture sheet.
2018-12-30 18:00:05 +08:00
public Animation defaultDrawFrame ;
public Animation currentAnimation ;
public bool enabled ;
2019-07-23 07:50:49 +08:00
public bool loopAnimation ;
2018-08-07 11:01:59 +08:00
public string animationDataString ;
2018-12-16 12:54:58 +08:00
2019-08-28 07:47:22 +08:00
[JsonIgnore]
public bool requiresUpdate ;
2019-07-18 01:18:51 +08:00
public bool IsNull = > this . defaultDrawFrame = = null & & this . objectTexture = = null ;
2019-07-23 08:15:23 +08:00
/// <summary>
/// Checks to see if there is an animation playing.
/// </summary>
public bool IsAnimationPlaying
{
get
{
return ! ( this . defaultDrawFrame = = this . currentAnimation ) ;
}
}
2019-07-18 01:18:51 +08:00
/// <summary>Construct an instance.</summary>
2018-12-30 18:00:05 +08:00
public AnimationManager ( ) { }
2018-12-16 12:54:58 +08:00
2019-07-18 01:18:51 +08:00
2018-12-30 18:00:05 +08:00
/// <summary>Constructor for Animation Manager class.</summary>
2017-09-12 09:35:31 +08:00
/// <param name="ObjectTexture">The texture that will be used for the animation. This is typically the same as the object this class is attached to.</param>
/// <param name="DefaultFrame">This is used if no animations will be available to the animation manager.</param>
/// <param name="EnabledByDefault">Whether or not animations play by default. Default value is true.</param>
2018-12-30 18:00:05 +08:00
public AnimationManager ( Texture2DExtended ObjectTexture , Animation DefaultFrame , bool EnabledByDefault = true )
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
this . currentAnimationListIndex = 0 ;
2017-09-12 09:35:31 +08:00
this . objectTexture = ObjectTexture ;
this . defaultDrawFrame = DefaultFrame ;
this . enabled = EnabledByDefault ;
2018-12-30 18:00:05 +08:00
this . currentAnimation = this . defaultDrawFrame ;
2018-08-07 11:01:59 +08:00
this . currentAnimationName = "" ;
this . animationDataString = "" ;
2017-09-12 09:35:31 +08:00
}
2018-12-30 18:00:05 +08:00
public AnimationManager ( Texture2DExtended ObjectTexture , Animation DefaultFrame , string animationString , string startingAnimationKey , int startingAnimationFrame = 0 , bool EnabledByDefault = true )
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
this . currentAnimationListIndex = 0 ;
2017-09-12 09:35:31 +08:00
this . objectTexture = ObjectTexture ;
this . defaultDrawFrame = DefaultFrame ;
this . enabled = EnabledByDefault ;
2018-08-07 11:01:59 +08:00
this . animationDataString = animationString ;
this . animations = parseAnimationsFromXNB ( animationString ) ;
2019-07-18 01:18:51 +08:00
if ( this . animations . TryGetValue ( startingAnimationKey , out this . currentAnimationList ) )
2018-12-30 18:00:05 +08:00
this . setAnimation ( startingAnimationKey , startingAnimationFrame ) ;
2018-12-15 19:00:32 +08:00
else
{
2018-12-30 18:00:05 +08:00
this . currentAnimation = this . defaultDrawFrame ;
2018-12-15 19:00:32 +08:00
this . currentAnimationName = "" ;
}
2017-09-12 09:35:31 +08:00
}
2018-12-30 18:00:05 +08:00
public AnimationManager ( Texture2DExtended ObjectTexture , Animation DefaultFrame , Dictionary < string , List < Animations . Animation > > animationString , string startingAnimationKey , int startingAnimationFrame = 0 , bool EnabledByDefault = true )
2018-08-07 11:01:59 +08:00
{
2018-12-30 18:00:05 +08:00
this . currentAnimationListIndex = 0 ;
2018-08-07 11:01:59 +08:00
this . objectTexture = ObjectTexture ;
this . defaultDrawFrame = DefaultFrame ;
this . enabled = EnabledByDefault ;
this . animations = animationString ;
2018-12-30 18:00:05 +08:00
if ( this . animations . TryGetValue ( startingAnimationKey , out this . currentAnimationList ) )
this . setAnimation ( startingAnimationKey , startingAnimationFrame ) ;
2018-12-15 19:00:32 +08:00
else
{
2018-12-30 18:00:05 +08:00
this . currentAnimation = this . defaultDrawFrame ;
2018-12-15 19:00:32 +08:00
this . currentAnimationName = "" ;
}
2018-08-07 11:01:59 +08:00
}
2018-12-30 18:00:05 +08:00
/// <summary>Update the animation frame once after drawing the object.</summary>
2017-09-12 09:35:31 +08:00
public void tickAnimation ( )
{
try
{
2018-12-30 18:00:05 +08:00
if ( this . currentAnimation . frameDuration = = - 1 | | ! this . enabled | | this . currentAnimation = = this . defaultDrawFrame )
return ; //This is if this is a default animation or the animation stops here.
if ( this . currentAnimation . frameCountUntilNextAnimation = = 0 )
this . getNextAnimation ( ) ;
2017-09-12 09:35:31 +08:00
this . currentAnimation . tickAnimationFrame ( ) ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2017-09-12 09:35:31 +08:00
}
2018-12-30 18:00:05 +08:00
catch ( Exception err )
2017-09-12 09:35:31 +08:00
{
ModCore . ModMonitor . Log ( "An internal error occured when trying to tick the animation." ) ;
ModCore . ModMonitor . Log ( err . ToString ( ) , StardewModdingAPI . LogLevel . Error ) ;
}
}
2019-07-23 07:50:49 +08:00
/// <summary>Get the next animation frame in the list of animations.</summary>
2019-08-28 07:47:22 +08:00
private void getNextAnimation ( )
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
this . currentAnimationListIndex + + ;
if ( this . currentAnimationListIndex = = this . currentAnimationList . Count ) //If the animation frame I'm tryting to get is 1 outside my list length, reset the list.
2019-07-23 07:50:49 +08:00
if ( this . loopAnimation )
{
this . currentAnimationListIndex = 0 ;
}
else
{
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2019-07-23 07:50:49 +08:00
this . playDefaultAnimation ( ) ;
return ;
}
2018-12-30 18:00:05 +08:00
//Get the next animation from the list and reset it's counter to the starting frame value.
this . currentAnimation = this . currentAnimationList [ this . currentAnimationListIndex ] ;
this . currentAnimation . startAnimation ( ) ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2017-09-12 09:35:31 +08:00
}
2018-12-30 18:00:05 +08:00
/// <summary>Gets the animation from the dictionary of all animations available.</summary>
2019-07-18 01:18:51 +08:00
/// <param name="AnimationName"></param>
/// <param name="StartingFrame"></param>
2018-12-30 18:00:05 +08:00
public bool setAnimation ( string AnimationName , int StartingFrame = 0 )
2017-09-12 09:35:31 +08:00
{
2019-07-18 01:18:51 +08:00
if ( this . animations . TryGetValue ( AnimationName , out List < Animation > dummyList ) )
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
if ( dummyList . Count ! = 0 | | StartingFrame > = dummyList . Count )
2017-09-12 09:35:31 +08:00
{
2018-12-30 18:00:05 +08:00
this . currentAnimationList = dummyList ;
this . currentAnimation = this . currentAnimationList [ StartingFrame ] ;
this . currentAnimationName = AnimationName ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2017-09-13 07:30:30 +08:00
return true ;
2017-09-12 09:35:31 +08:00
}
else
2019-07-18 03:51:29 +08:00
{
if ( dummyList . Count = = 0 )
ModCore . ModMonitor . Log ( "Error: Current animation " + AnimationName + " has no animation frames associated with it." ) ;
if ( dummyList . Count > dummyList . Count )
ModCore . ModMonitor . Log ( "Error: Animation frame " + StartingFrame + " is outside the range of provided animations. Which has a maximum count of " + dummyList . Count ) ;
return false ;
}
}
else
{
ModCore . ModMonitor . Log ( "Error setting animation: " + AnimationName + " animation does not exist in list of available animations. Did you make sure to add it in?" ) ;
return false ;
}
}
2019-07-23 07:50:49 +08:00
/// <summary>
/// Plays the animation for the animation manager.
/// </summary>
2019-07-18 03:51:29 +08:00
/// <param name="AnimationName"></param>
2019-07-23 07:50:49 +08:00
/// <param name="overrideSameAnimation"></param>
2019-07-18 03:51:29 +08:00
/// <param name="StartingFrame"></param>
2019-07-23 07:50:49 +08:00
/// <returns></returns>
2019-07-18 03:51:29 +08:00
public bool playAnimation ( string AnimationName , bool overrideSameAnimation = false , int StartingFrame = 0 )
{
if ( this . animations . TryGetValue ( AnimationName , out List < Animation > dummyList ) )
{
if ( overrideSameAnimation = = false )
{
if ( this . currentAnimationName = = AnimationName ) return true ;
}
if ( dummyList . Count ! = 0 | | StartingFrame > = dummyList . Count )
{
this . currentAnimationList = dummyList ;
this . currentAnimation = this . currentAnimationList [ StartingFrame ] ;
this . currentAnimationName = AnimationName ;
this . currentAnimation . startAnimation ( ) ;
2019-07-23 07:50:49 +08:00
this . loopAnimation = true ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2019-07-18 03:51:29 +08:00
return true ;
}
else
2017-09-12 09:35:31 +08:00
{
2019-07-18 01:18:51 +08:00
if ( dummyList . Count = = 0 )
ModCore . ModMonitor . Log ( "Error: Current animation " + AnimationName + " has no animation frames associated with it." ) ;
if ( dummyList . Count > dummyList . Count )
ModCore . ModMonitor . Log ( "Error: Animation frame " + StartingFrame + " is outside the range of provided animations. Which has a maximum count of " + dummyList . Count ) ;
2017-09-12 09:35:31 +08:00
return false ;
}
}
else
{
ModCore . ModMonitor . Log ( "Error setting animation: " + AnimationName + " animation does not exist in list of available animations. Did you make sure to add it in?" ) ;
return false ;
}
}
2019-07-23 07:50:49 +08:00
/// <summary>
/// Plays the animation for the animation manager only once.
/// </summary>
/// <param name="AnimationName"></param>
/// <param name="overrideSameAnimation"></param>
/// <param name="StartingFrame"></param>
/// <returns></returns>
public bool playAnimationOnce ( string AnimationName , bool overrideSameAnimation = false , int StartingFrame = 0 )
{
if ( this . animations . TryGetValue ( AnimationName , out List < Animation > dummyList ) )
{
if ( overrideSameAnimation = = false )
{
if ( this . currentAnimationName = = AnimationName ) return true ;
}
if ( dummyList . Count ! = 0 | | StartingFrame > = dummyList . Count )
{
this . currentAnimationList = dummyList ;
this . currentAnimation = this . currentAnimationList [ StartingFrame ] ;
this . currentAnimationName = AnimationName ;
this . currentAnimation . startAnimation ( ) ;
this . loopAnimation = false ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2019-07-23 07:50:49 +08:00
return true ;
}
else
{
if ( dummyList . Count = = 0 )
ModCore . ModMonitor . Log ( "Error: Current animation " + AnimationName + " has no animation frames associated with it." ) ;
if ( dummyList . Count > dummyList . Count )
ModCore . ModMonitor . Log ( "Error: Animation frame " + StartingFrame + " is outside the range of provided animations. Which has a maximum count of " + dummyList . Count ) ;
return false ;
}
}
else
{
ModCore . ModMonitor . Log ( "Error setting animation: " + AnimationName + " animation does not exist in list of available animations. Did you make sure to add it in?" ) ;
return false ;
}
}
/// <summary>
/// Plays the default animation.
/// </summary>
public void playDefaultAnimation ( )
{
this . currentAnimation = this . defaultDrawFrame ;
this . currentAnimationName = "" ;
this . currentAnimationListIndex = 0 ;
2019-08-28 07:47:22 +08:00
this . requiresUpdate = true ;
2019-07-23 07:50:49 +08:00
}
2018-12-30 18:00:05 +08:00
/// <summary>Sets the animation manager to an on state, meaning that this animation will update on the draw frame.</summary>
2017-09-12 09:35:31 +08:00
public void enableAnimation ( )
{
this . enabled = true ;
}
2018-02-06 17:27:28 +08:00
2018-12-30 18:00:05 +08:00
/// <summary>Sets the animation manager to an off state, meaning that this animation will no longer update on the draw frame.</summary>
2017-09-12 09:35:31 +08:00
public void disableAnimation ( )
{
this . enabled = false ;
}
2017-09-13 07:30:30 +08:00
public static Dictionary < string , List < Animation > > parseAnimationsFromXNB ( string s )
{
string [ ] array = s . Split ( '*' ) ;
2018-12-30 18:00:05 +08:00
Dictionary < string , List < Animation > > parsedDic = new Dictionary < string , List < Animation > > ( ) ;
foreach ( string v in array )
2017-09-13 07:30:30 +08:00
{
2018-12-30 18:00:05 +08:00
// Log.AsyncC(v);
string [ ] animationArray = v . Split ( ' ' ) ;
if ( parsedDic . ContainsKey ( animationArray [ 0 ] ) )
2017-09-13 07:30:30 +08:00
{
2019-07-18 01:18:51 +08:00
List < Animation > animations = parseAnimationFromString ( v ) ;
foreach ( var animation in animations )
{
parsedDic [ animationArray [ 0 ] ] . Add ( animation ) ;
}
2017-09-13 07:30:30 +08:00
}
else
{
2018-12-30 18:00:05 +08:00
parsedDic . Add ( animationArray [ 0 ] , new List < Animation > ( ) ) ;
2019-07-18 01:18:51 +08:00
List < Animation > aniList = new List < Animation > ( ) ;
aniList = parseAnimationFromString ( v ) ;
2017-11-17 18:50:22 +08:00
foreach ( var ani in aniList )
2019-07-18 01:18:51 +08:00
{
2018-12-30 18:00:05 +08:00
parsedDic [ animationArray [ 0 ] ] . Add ( ani ) ;
2019-07-18 01:18:51 +08:00
}
2017-09-13 07:30:30 +08:00
}
}
return parsedDic ;
}
2017-11-17 18:50:22 +08:00
public static List < Animation > parseAnimationFromString ( string s )
2017-09-13 07:30:30 +08:00
{
2018-12-30 18:00:05 +08:00
List < Animation > ok = new List < Animation > ( ) ;
2017-11-17 18:50:22 +08:00
string [ ] array2 = s . Split ( '>' ) ;
2018-12-30 18:00:05 +08:00
foreach ( string q in array2 )
{
2017-11-17 18:50:22 +08:00
string [ ] array = q . Split ( ' ' ) ;
try
{
Animation ani = new Animation ( new Rectangle ( Convert . ToInt32 ( array [ 1 ] ) , Convert . ToInt32 ( array [ 2 ] ) , Convert . ToInt32 ( array [ 3 ] ) , Convert . ToInt32 ( array [ 4 ] ) ) , Convert . ToInt32 ( array [ 5 ] ) ) ;
2018-12-30 18:00:05 +08:00
// ModCore.ModMonitor.Log(ani.sourceRectangle.ToString());
2017-11-17 18:50:22 +08:00
ok . Add ( ani ) ;
}
2018-12-30 18:00:05 +08:00
catch { }
2017-11-17 18:50:22 +08:00
}
return ok ;
2017-09-13 07:30:30 +08:00
}
2018-12-30 18:00:05 +08:00
/// <summary>Used to handle general drawing functionality using the animation manager.</summary>
2018-03-06 12:46:45 +08:00
/// <param name="spriteBatch">We need a spritebatch to draw.</param>
/// <param name="texture">The texture to draw.</param>
/// <param name="Position">The onscreen position to draw to.</param>
/// <param name="sourceRectangle">The source rectangle on the texture to draw.</param>
/// <param name="drawColor">The color to draw the thing passed in.</param>
/// <param name="rotation">The rotation of the animation texture being drawn.</param>
/// <param name="origin">The origin of the texture.</param>
/// <param name="scale">The scale of the texture.</param>
/// <param name="spriteEffects">Effects that get applied to the sprite.</param>
/// <param name="LayerDepth">The dept at which to draw the texture.</param>
2018-12-30 18:00:05 +08:00
public void draw ( SpriteBatch spriteBatch , Texture2D texture , Vector2 Position , Rectangle ? sourceRectangle , Color drawColor , float rotation , Vector2 origin , float scale , SpriteEffects spriteEffects , float LayerDepth )
2018-03-06 12:46:45 +08:00
{
//Log.AsyncC("Animation Manager is working!");
spriteBatch . Draw ( texture , Position , sourceRectangle , drawColor , rotation , origin , scale , spriteEffects , LayerDepth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2017-09-13 07:30:30 +08:00
2019-07-22 07:58:57 +08:00
/// <summary>
/// Draws the texture to the screen.
/// </summary>
/// <param name="spriteBatch"></param>
/// <param name="texture"></param>
/// <param name="Position"></param>
/// <param name="sourceRectangle"></param>
/// <param name="drawColor"></param>
/// <param name="rotation"></param>
/// <param name="origin"></param>
/// <param name="scale"></param>
/// <param name="spriteEffects"></param>
/// <param name="LayerDepth"></param>
public void draw ( SpriteBatch spriteBatch , Texture2D texture , Vector2 Position , Rectangle ? sourceRectangle , Color drawColor , float rotation , Vector2 origin , Vector2 scale , SpriteEffects spriteEffects , float LayerDepth )
{
//Log.AsyncC("Animation Manager is working!");
spriteBatch . Draw ( texture , Position , sourceRectangle , drawColor , rotation , origin , scale , spriteEffects , LayerDepth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2019-07-18 01:18:51 +08:00
/// <summary>
/// Used to draw the current animation to the screen.
/// </summary>
/// <param name="b"></param>
/// <param name="Position"></param>
/// <param name="drawColor"></param>
/// <param name="scale"></param>
/// <param name="flipped"></param>
/// <param name="depth"></param>
public void draw ( SpriteBatch b , Vector2 Position , Color drawColor , float scale , SpriteEffects flipped , float depth )
{
b . Draw ( this . objectTexture . texture , Position , this . currentAnimation . sourceRectangle , drawColor , 0f , Vector2 . Zero , scale , flipped , depth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2019-07-25 09:37:43 +08:00
public void draw ( SpriteBatch b , Vector2 Position , Color drawColor , float scale , float Rotation , SpriteEffects flipped , float depth )
{
b . Draw ( this . objectTexture . texture , Position , this . currentAnimation . sourceRectangle , drawColor , Rotation , Vector2 . Zero , scale , flipped , depth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2019-07-22 07:58:57 +08:00
/// <summary>
/// Draws the animated texture to the screen.
/// </summary>
/// <param name="b">The Sprite Batch used to draw.</param>
/// <param name="Position">The position to draw the sprite to.</param>
/// <param name="drawColor">The color to draw the sprite to.</param>
/// <param name="scale">The scale for the sprite as a Vector2. (Width,Height)</param>
/// <param name="flipped">If the sprite is flipped.</param>
/// <param name="depth">The depth of the sprite.</param>
public void draw ( SpriteBatch b , Vector2 Position , Color drawColor , Vector2 scale , SpriteEffects flipped , float depth )
{
b . Draw ( this . objectTexture . texture , Position , this . currentAnimation . sourceRectangle , drawColor , 0f , Vector2 . Zero , scale , flipped , depth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2019-07-25 09:37:43 +08:00
public void draw ( SpriteBatch b , Vector2 Position , Color drawColor , Vector2 scale , float Rotation , SpriteEffects flipped , float depth )
{
b . Draw ( this . objectTexture . texture , Position , this . currentAnimation . sourceRectangle , drawColor , Rotation , Vector2 . Zero , scale , flipped , depth ) ;
try
{
this . tickAnimation ( ) ;
// Log.AsyncC("Tick animation");
}
catch ( Exception err )
{
ModCore . ModMonitor . Log ( err . ToString ( ) ) ;
}
}
2018-06-14 02:11:56 +08:00
public Texture2DExtended getExtendedTexture ( )
{
return this . objectTexture ;
}
2018-08-07 11:01:59 +08:00
public void setExtendedTexture ( Texture2DExtended texture )
{
this . objectTexture = texture ;
}
public void setEnabled ( bool enabled )
{
this . enabled = enabled ;
}
2018-06-14 02:11:56 +08:00
public Texture2D getTexture ( )
{
return this . objectTexture . getTexture ( ) ;
}
2017-09-12 09:35:31 +08:00
}
}