using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StardewValley;
using StardustCore.UIUtilities;
namespace StardustCore.Animations
{
/// 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.
public class AnimationManager
{
public Dictionary> animations = new SerializableDictionary>();
public string currentAnimationName;
public int currentAnimationListIndex;
public List currentAnimationList = new List();
public Texture2DExtended objectTexture; ///Might not be necessary if I use the CoreObject texture sheet.
public Animation defaultDrawFrame;
public Animation currentAnimation;
public bool enabled;
public bool loopAnimation;
public string animationDataString;
public bool IsNull => this.defaultDrawFrame == null && this.objectTexture == null;
///
/// Checks to see if there is an animation playing.
///
public bool IsAnimationPlaying
{
get
{
return !(this.defaultDrawFrame == this.currentAnimation);
}
}
/// Construct an instance.
public AnimationManager() { }
/// Constructor for Animation Manager class.
/// The texture that will be used for the animation. This is typically the same as the object this class is attached to.
/// This is used if no animations will be available to the animation manager.
/// Whether or not animations play by default. Default value is true.
public AnimationManager(Texture2DExtended ObjectTexture, Animation DefaultFrame, bool EnabledByDefault = true)
{
this.currentAnimationListIndex = 0;
this.objectTexture = ObjectTexture;
this.defaultDrawFrame = DefaultFrame;
this.enabled = EnabledByDefault;
this.currentAnimation = this.defaultDrawFrame;
this.currentAnimationName = "";
this.animationDataString = "";
}
public AnimationManager(Texture2DExtended ObjectTexture, Animation DefaultFrame, string animationString, string startingAnimationKey, int startingAnimationFrame = 0, bool EnabledByDefault = true)
{
this.currentAnimationListIndex = 0;
this.objectTexture = ObjectTexture;
this.defaultDrawFrame = DefaultFrame;
this.enabled = EnabledByDefault;
this.animationDataString = animationString;
this.animations = parseAnimationsFromXNB(animationString);
if (this.animations.TryGetValue(startingAnimationKey, out this.currentAnimationList))
this.setAnimation(startingAnimationKey, startingAnimationFrame);
else
{
this.currentAnimation = this.defaultDrawFrame;
this.currentAnimationName = "";
}
}
public AnimationManager(Texture2DExtended ObjectTexture, Animation DefaultFrame, Dictionary> animationString, string startingAnimationKey, int startingAnimationFrame = 0, bool EnabledByDefault = true)
{
this.currentAnimationListIndex = 0;
this.objectTexture = ObjectTexture;
this.defaultDrawFrame = DefaultFrame;
this.enabled = EnabledByDefault;
this.animations = animationString;
if (this.animations.TryGetValue(startingAnimationKey, out this.currentAnimationList))
this.setAnimation(startingAnimationKey, startingAnimationFrame);
else
{
this.currentAnimation = this.defaultDrawFrame;
this.currentAnimationName = "";
}
}
/// Update the animation frame once after drawing the object.
public void tickAnimation()
{
try
{
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();
this.currentAnimation.tickAnimationFrame();
}
catch (Exception err)
{
ModCore.ModMonitor.Log("An internal error occured when trying to tick the animation.");
ModCore.ModMonitor.Log(err.ToString(), StardewModdingAPI.LogLevel.Error);
}
}
/// Get the next animation frame in the list of animations.
public void getNextAnimation()
{
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.
if (this.loopAnimation)
{
this.currentAnimationListIndex = 0;
}
else
{
this.playDefaultAnimation();
return;
}
//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();
}
/// Gets the animation from the dictionary of all animations available.
///
///
public bool setAnimation(string AnimationName, int StartingFrame = 0)
{
if (this.animations.TryGetValue(AnimationName, out List dummyList))
{
if (dummyList.Count != 0 || StartingFrame >= dummyList.Count)
{
this.currentAnimationList = dummyList;
this.currentAnimation = this.currentAnimationList[StartingFrame];
this.currentAnimationName = AnimationName;
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;
}
}
///
/// Plays the animation for the animation manager.
///
///
///
///
///
public bool playAnimation(string AnimationName,bool overrideSameAnimation=false,int StartingFrame = 0)
{
if (this.animations.TryGetValue(AnimationName, out List 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 = true;
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;
}
}
///
/// Plays the animation for the animation manager only once.
///
///
///
///
///
public bool playAnimationOnce(string AnimationName, bool overrideSameAnimation = false, int StartingFrame = 0)
{
if (this.animations.TryGetValue(AnimationName, out List 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;
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;
}
}
///
/// Plays the default animation.
///
public void playDefaultAnimation()
{
this.currentAnimation = this.defaultDrawFrame;
this.currentAnimationName = "";
this.currentAnimationListIndex = 0;
}
/// Sets the animation manager to an on state, meaning that this animation will update on the draw frame.
public void enableAnimation()
{
this.enabled = true;
}
/// Sets the animation manager to an off state, meaning that this animation will no longer update on the draw frame.
public void disableAnimation()
{
this.enabled = false;
}
public static Dictionary> parseAnimationsFromXNB(string s)
{
string[] array = s.Split('*');
Dictionary> parsedDic = new Dictionary>();
foreach (string v in array)
{
// Log.AsyncC(v);
string[] animationArray = v.Split(' ');
if (parsedDic.ContainsKey(animationArray[0]))
{
List animations = parseAnimationFromString(v);
foreach (var animation in animations)
{
parsedDic[animationArray[0]].Add(animation);
}
}
else
{
parsedDic.Add(animationArray[0], new List());
List aniList = new List();
aniList = parseAnimationFromString(v);
foreach (var ani in aniList)
{
parsedDic[animationArray[0]].Add(ani);
}
}
}
return parsedDic;
}
public static List parseAnimationFromString(string s)
{
List ok = new List();
string[] array2 = s.Split('>');
foreach (string q in array2)
{
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]));
// ModCore.ModMonitor.Log(ani.sourceRectangle.ToString());
ok.Add(ani);
}
catch { }
}
return ok;
}
/// Used to handle general drawing functionality using the animation manager.
/// We need a spritebatch to draw.
/// The texture to draw.
/// The onscreen position to draw to.
/// The source rectangle on the texture to draw.
/// The color to draw the thing passed in.
/// The rotation of the animation texture being drawn.
/// The origin of the texture.
/// The scale of the texture.
/// Effects that get applied to the sprite.
/// The dept at which to draw the texture.
public void draw(SpriteBatch spriteBatch, Texture2D texture, Vector2 Position, Rectangle? sourceRectangle, Color drawColor, float rotation, Vector2 origin, float 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());
}
}
///
/// Draws the texture to the screen.
///
///
///
///
///
///
///
///
///
///
///
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());
}
}
///
/// Used to draw the current animation to the screen.
///
///
///
///
///
///
///
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());
}
}
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());
}
}
///
/// Draws the animated texture to the screen.
///
/// The Sprite Batch used to draw.
/// The position to draw the sprite to.
/// The color to draw the sprite to.
/// The scale for the sprite as a Vector2. (Width,Height)
/// If the sprite is flipped.
/// The depth of the sprite.
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());
}
}
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());
}
}
public Texture2DExtended getExtendedTexture()
{
return this.objectTexture;
}
public void setExtendedTexture(Texture2DExtended texture)
{
this.objectTexture = texture;
}
public void setEnabled(bool enabled)
{
this.enabled = enabled;
}
public Texture2D getTexture()
{
return this.objectTexture.getTexture();
}
}
}