using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; using StardewValley; using StardustCore.UIUtilities; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; 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(); private Texture2DExtended objectTexture; ///Might not be necessary if I use the CoreObject texture sheet. public Animation defaultDrawFrame; public Animation currentAnimation; public bool enabled; public string animationDataString; /// /// 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) { currentAnimationListIndex = 0; this.objectTexture = ObjectTexture; this.defaultDrawFrame = DefaultFrame; this.enabled = EnabledByDefault; currentAnimation = this.defaultDrawFrame; this.currentAnimationName = ""; this.animationDataString = ""; } public AnimationManager(Texture2DExtended ObjectTexture,Animation DefaultFrame ,string animationString, string startingAnimationKey, int startingAnimationFrame=0,bool EnabledByDefault=true) { currentAnimationListIndex = 0; this.objectTexture = ObjectTexture; this.defaultDrawFrame = DefaultFrame; this.enabled = EnabledByDefault; this.animationDataString = animationString; this.animations = parseAnimationsFromXNB(animationString); bool f = animations.TryGetValue(startingAnimationKey, out currentAnimationList); if (f == true) { setAnimation(startingAnimationKey, startingAnimationFrame); } else currentAnimation = this.defaultDrawFrame; } public AnimationManager(Texture2DExtended ObjectTexture, Animation DefaultFrame, Dictionary> animationString, string startingAnimationKey, int startingAnimationFrame = 0, bool EnabledByDefault = true) { currentAnimationListIndex = 0; this.objectTexture = ObjectTexture; this.defaultDrawFrame = DefaultFrame; this.enabled = EnabledByDefault; this.animations = animationString; bool f = animations.TryGetValue(startingAnimationKey, out currentAnimationList); if (f == true) { setAnimation(startingAnimationKey, startingAnimationFrame); } else currentAnimation = this.defaultDrawFrame; } /// /// Update the animation frame once after drawing the object. /// public void tickAnimation() { try { if (this.currentAnimation.frameDuration == -1 || this.enabled == false || this.currentAnimation == this.defaultDrawFrame) return; //This is if this is a default animation or the animation stops here. if (this.currentAnimation.frameCountUntilNextAnimation == 0) 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 in the list of animations. /// public void getNextAnimation() { currentAnimationListIndex++; if(currentAnimationListIndex==currentAnimationList.Count) //If the animation frame I'm tryting to get is 1 outside my list length, reset the list. { currentAnimationListIndex = 0; } //Get the next animation from the list and reset it's counter to the starting frame value. this.currentAnimation = currentAnimationList[currentAnimationListIndex]; this.currentAnimation.startAnimation(); } /// /// Gets the animation from the dictionary of all animations available. /// /// /// /// public bool setAnimation(string AnimationName, int StartingFrame=0) { List dummyList = new List(); bool f = animations.TryGetValue(AnimationName, out dummyList); if (f == true) { if (dummyList.Count != 0 || StartingFrame>=dummyList.Count) { currentAnimationList = dummyList; currentAnimation = currentAnimationList[StartingFrame]; 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; } } /// /// 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(var v in array) { // Log.AsyncC(v); string[] AnimationArray = v.Split(' '); if (parsedDic.ContainsKey(AnimationArray[0])) { List aniList = new List(); aniList = parseAnimationFromString(v); foreach(var ani in aniList) { parsedDic[AnimationArray[0]].Add(ani); } } 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(var 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(Exception err) { err.ToString(); continue; } } 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()); } } 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(); } } }