<commit_before>using System;
using System.Globalization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Delta.Structures;

namespace Delta.Graphics
{

    [Flags]
    internal enum SpriteState
    {
        None = 0x0,
        Paused = 0x1,
        Looped = 0x2,
        RandomFrameStart = 0x4,
        Finished = 0x8,
        //temporary?
        Overlay = 0x10,
        OutlineTop = 0x20,
        OutlineRight = 0x40,
        OutlineBottom = 0x80,
        OutlineLeft = 0x100,
    }

    public class SpriteEntity : Entity, IRecyclable
    {
        static Pool<SpriteEntity> _pool;

        [ContentSerializer(ElementName = "SpriteSheet")]
        internal string _spriteSheetName = string.Empty;
        internal SpriteSheet _spriteSheet = null;
        [ContentSerializer(ElementName = "Animation")]
        internal string _animationName = string.Empty;
        internal Animation _animation = null;
        internal Rectangle _sourceRectangle = Rectangle.Empty;
        internal float _frameDurationTimer = 0f;
        [ContentSerializer(ElementName = "Frame")]
        internal int _animationFrame = 0;
        [ContentSerializer(ElementName = "State")]
        internal SpriteState _state = SpriteState.Looped;
        [ContentSerializer(ElementName = "FrameOffset")] //for Rob's convience
        public int AnimationFrameOffset { get; set; }
        [ContentSerializer]
        public SpriteEffects SpriteEffects { get; set; }

        [ContentSerializerIgnore]
        public bool IsPaused
        {
            get { return _state.HasFlag(SpriteState.Paused); }
            set
            {
                if (value)
                    _state |= SpriteState.Paused;
                else
                    _state &= ~SpriteState.Paused;
            }
        }

        [ContentSerializerIgnore]
        public bool IsLooped
        {
            get { return _state.HasFlag(SpriteState.Looped); }
            set
            {
                if (value)
                    _state |= SpriteState.Looped;
                else
                    _state &= ~SpriteState.Looped;
            }
        }

        [ContentSerializerIgnore]
        public bool IsFinished
        {
            get { return _state.HasFlag(SpriteState.Finished); }
            private set
            {
                if (value)
                    _state |= SpriteState.Finished;
                else
                    _state &= ~SpriteState.Finished;
            }
        }

        [ContentSerializerIgnore]
        public bool IsOverlay
        {
            get { return _state.HasFlag(SpriteState.Overlay); }
            set
            {
                if (value)
                    _state |= SpriteState.Overlay;
                else
                    _state &= ~SpriteState.Overlay;
            }
        }

        [ContentSerializerIgnore]
        public Color OutlineColor
        {
            get;
            set;
        }

        static SpriteEntity()
        {
            _pool = new Pool<SpriteEntity>(100);
        }
        
        public static SpriteEntity Create(string spriteSheet) 
        {
            SpriteEntity spriteEntity = _pool.Fetch();
            spriteEntity._spriteSheetName = spriteSheet;
            return spriteEntity;
        }

        public SpriteEntity()
            : base()
        {
            AnimationFrameOffset = 0;
        }

        public SpriteEntity(string spriteSheet)
            : this()
        {
            _spriteSheetName= spriteSheet;
        }

#if WINDOWS
        protected internal override bool ImportCustomValues(string name, string value)
        {
            switch (name)
            {
                case "spritesheet":
                case "spritesheetname":
                    _spriteSheetName = value;
                    return true;
                case "animation":
                case "animationname":
                    _animationName = value;
                    return true;
                //temporary?
                case "isshadow":
                case "shadow":
                case "islight":
                case "light":
                case "overlay":
                case "isoverlay":
                    IsOverlay = bool.Parse(value);
                    return true;
                case "mirror":
                case "flip":
                    string[] split = value.Split(new string[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries);
                    switch (split[0])
                    {
                        case "left":
                        case "right":
                        case "horizontal":
                        case "h":
                            SpriteEffects |= SpriteEffects.FlipHorizontally;
                            return true;
                        case "up":
                        case "down":
                        case "vertical":
                        case "v":
                            SpriteEffects |= SpriteEffects.FlipVertically;
                            return true;
                    }
                    if (split.Length > 1)
                    {
                        switch (split[1])
                        {
                            case "left":
                            case "right":
                            case "horizontal":
                            case "h":
                                SpriteEffects |= SpriteEffects.FlipHorizontally;
                                return true;
                            case "up":
                            case "down":
                            case "vertical":
                            case "v":
                                SpriteEffects |= SpriteEffects.FlipVertically;
                                return true;
                        }
                    }
                    break;
                case "startrandom":
                case "random":
                    if (bool.Parse(value))
                        _state |= SpriteState.RandomFrameStart;
                    else
                        _state ^= SpriteState.RandomFrameStart;
                    return true;
                case "ispaused":
                case "paused":
                case "isstopped":
                case "stopped":
                    IsPaused = bool.Parse(value);
                    return true;
                case "foffset":
                case "frameoffset":
                    AnimationFrameOffset = int.Parse(value, CultureInfo.InvariantCulture);
                    return true;
            }
            return base.ImportCustomValues(name, value);
        }
#endif

        public override void LoadContent()
        {
            if (!string.IsNullOrEmpty(_spriteSheetName))
                _spriteSheet = G.Content.Load<SpriteSheet>(_spriteSheetName);
            Play(_animationName);
            base.LoadContent();
        }

        protected override void LightUpdate(DeltaTime time)
        {
            if (_animation != null && !IsFinished && !IsPaused)
                UpdateAnimationFrame(time);
            base.LightUpdate(time);
        }

        protected internal virtual void UpdateAnimationFrame(DeltaTime time)
        {
            _frameDurationTimer -= (float)time.TotalSeconds;
            if (_frameDurationTimer <= 0f)
            {
                _frameDurationTimer = _animation.FrameDuration;
                _animationFrame = (_animationFrame + 1).Wrap(0, _animation.Frames.Count - 1);
                if (!IsLooped && _animationFrame >= _animation.Frames.Count - 1)
                {
                    _state |= SpriteState.Finished;
                    _frameDurationTimer = 0;
                }
                Rectangle previousSourceRectangle = _sourceRectangle;
                _sourceRectangle = _spriteSheet.GetFrameSourceRectangle(_animation.ImageName, _animation.Frames[_animationFrame] + AnimationFrameOffset);
                if (_sourceRectangle != Rectangle.Empty)
                {
                    if (previousSourceRectangle.Width != _sourceRectangle.Width || previousSourceRectangle.Height != _sourceRectangle.Height)
                        Size = new Vector2(_sourceRectangle.Width, _sourceRectangle.Height);
                }
            }
        }

        protected override bool CanDraw()
        {
            if (_animation == null || _spriteSheet == null || _spriteSheet.Texture == null)
                return false;
            return base.CanDraw();
        }

        //protected virtual bool OnCamera()
        //{
        //    Rectangle spriteArea = new Rectangle((int)Position.X, (int)Position.Y, (int)(Size.X * Scale.X), (int)(Size.Y * Size.Y));
        //    Rectangle viewingArea = G.World.Camera.ViewingArea;
        //    viewingArea.Inflate(16, 16); // pad the viewing area with a border of off-screen tiles. for smooth scrolling, otherwise tiles seem to 'pop' in.
        //    return (viewingArea.Contains(spriteArea) || viewingArea.Intersects(spriteArea));
        //}

        protected override void Draw(DeltaTime gameTime, SpriteBatch spriteBatch)
        {
            if (_state.HasFlag(SpriteState.OutlineTop) || _state.HasFlag(SpriteState.OutlineRight) || _state.HasFlag(SpriteState.OutlineBottom) || _state.HasFlag(SpriteState.OutlineLeft))
            {
                spriteBatch.End();
                G.SimpleEffect.SetTechnique(Effects.SimpleEffect.Technique.FillColor);
                G.SimpleEffect.Color = OutlineColor;
                //spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, G.SimpleEffect, G.World.Camera.View);
                //if (_state.HasFlag(SpriteState.OutlineTop))
                //    spriteBatch.Draw(_spriteSheet.Texture, RenderPosition - Vector2.UnitY, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                //if (_state.HasFlag(SpriteState.OutlineRight))
                //    spriteBatch.Draw(_spriteSheet.Texture, RenderPosition + Vector2.UnitX, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                //if (_state.HasFlag(SpriteState.OutlineBottom))
                //    spriteBatch.Draw(_spriteSheet.Texture, RenderPosition + Vector2.UnitY, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                //if (_state.HasFlag(SpriteState.OutlineLeft))
                //    spriteBatch.Draw(_spriteSheet.Texture, RenderPosition - Vector2.UnitX, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                //spriteBatch.End();
                //spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, G.World.Camera.View);
            }

            spriteBatch.Draw(_spriteSheet.Texture, RenderPosition, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
        }

        public void Play(string animation)
        {
            _animationName = animation;
            if (_spriteSheet == null)
            {
                _animation = null;
                return;
            }
            _animation = _spriteSheet.GetAnimation(_animationName);
            if (_animation != null)
                OnAnimationChanged();
        }

        public void Outline(bool top, bool right, bool bottom, bool left)
        {
            if (top)
                _state |= SpriteState.OutlineTop;
            else
                _state &= ~SpriteState.OutlineTop;
            if (right)
                _state |= SpriteState.OutlineRight;
            else
                _state &= ~SpriteState.OutlineRight;
            if (bottom)
                _state |= SpriteState.OutlineBottom;
            else
                _state &= ~SpriteState.OutlineBottom;
            if (left)
                _state |= SpriteState.OutlineLeft;
            else
                _state &= ~SpriteState.OutlineLeft;
        }

        protected internal override void OnPositionChanged()
        {
            base.OnPositionChanged();
            UpdateRenderOrigin();
            UpdateRenderPosition();
        }

        protected internal virtual void OnAnimationChanged()
        {
            _animationFrame = AnimationFrameOffset;
            if (_state.HasFlag(SpriteState.RandomFrameStart))
                _animationFrame = G.Random.Next(0, _animation.Frames.Count - 1);
            _frameDurationTimer = _animation.FrameDuration;
            _state &= ~SpriteState.Finished;
            // draw the first frame
            _sourceRectangle = _spriteSheet.GetFrameSourceRectangle(_animation.ImageName, _animation.Frames[_animationFrame] + AnimationFrameOffset);
            Size = new Vector2(_sourceRectangle.Width, _sourceRectangle.Height);
        }

        public override void Recycle()
        {
            base.Recycle();
            _state = SpriteState.Looped;
            _spriteSheet = null;
            _spriteSheetName = string.Empty;
            _animation = null;
            _animationName = string.Empty;
            _animationFrame = 0;
            _sourceRectangle = Rectangle.Empty;
            _frameDurationTimer = 0f;
            AnimationFrameOffset = 0;
            SpriteEffects = SpriteEffects.None;
            OutlineColor = Color.White;

            RemoveNextUpdate = true;
            _pool.Release(this);
        }
    }

}
<commit_msg>So apparently my "bitstate" idea creates some garbage. Just going back to regular booleans for "SpriteState".<commit_after>using System;
using System.Globalization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Delta.Structures;

namespace Delta.Graphics
{
    public class SpriteEntity : Entity, IRecyclable
    {
        static Pool<SpriteEntity> _pool;

        [ContentSerializer(ElementName = "SpriteSheet")]
        internal string _spriteSheetName = string.Empty;
        internal SpriteSheet _spriteSheet = null;
        [ContentSerializer(ElementName = "Animation")]
        internal string _animationName = string.Empty;
        internal Animation _animation = null;
        internal Rectangle _sourceRectangle = Rectangle.Empty;
        internal float _frameDurationTimer = 0f;
        [ContentSerializer(ElementName = "Frame")]
        internal int _animationFrame = 0;
        [ContentSerializer(ElementName = "FrameOffset")] //for Rob's convience
        public int AnimationFrameOffset { get; set; }
        [ContentSerializer]
        public SpriteEffects SpriteEffects { get; set; }
        [ContentSerializer]
        public bool IsLooped { get; set; }
        [ContentSerializer]
        public bool IsPaused { get; set; }
        [ContentSerializer]
        public bool IsFinished { get; set; }
        [ContentSerializer]
        public bool IsOverlay { get; set; }
        [ContentSerializer]
        public bool StartOnRandomFrame { get; set; }
        [ContentSerializer]
        public bool Outline { get; set; }

        [ContentSerializerIgnore]
        public Color OutlineColor
        {
            get;
            set;
        }

        static SpriteEntity()
        {
            _pool = new Pool<SpriteEntity>(100);
        }
        
        public static SpriteEntity Create(string spriteSheet) 
        {
            SpriteEntity spriteEntity = _pool.Fetch();
            spriteEntity._spriteSheetName = spriteSheet;
            return spriteEntity;
        }

        public SpriteEntity()
            : base()
        {
            AnimationFrameOffset = 0;
            IsLooped = true;
        }

        public SpriteEntity(string spriteSheet)
            : this()
        {
            _spriteSheetName= spriteSheet;
        }

#if WINDOWS
        protected internal override bool ImportCustomValues(string name, string value)
        {
            switch (name)
            {
                case "spritesheet":
                case "spritesheetname":
                    _spriteSheetName = value;
                    return true;
                case "animation":
                case "animationname":
                    _animationName = value;
                    return true;
                //temporary?
                case "isshadow":
                case "shadow":
                case "islight":
                case "light":
                case "overlay":
                case "isoverlay":
                    IsOverlay = bool.Parse(value);
                    return true;
                case "mirror":
                case "flip":
                    string[] split = value.Split(new string[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries);
                    switch (split[0])
                    {
                        case "left":
                        case "right":
                        case "horizontal":
                        case "h":
                            SpriteEffects |= SpriteEffects.FlipHorizontally;
                            return true;
                        case "up":
                        case "down":
                        case "vertical":
                        case "v":
                            SpriteEffects |= SpriteEffects.FlipVertically;
                            return true;
                    }
                    if (split.Length > 1)
                    {
                        switch (split[1])
                        {
                            case "left":
                            case "right":
                            case "horizontal":
                            case "h":
                                SpriteEffects |= SpriteEffects.FlipHorizontally;
                                return true;
                            case "up":
                            case "down":
                            case "vertical":
                            case "v":
                                SpriteEffects |= SpriteEffects.FlipVertically;
                                return true;
                        }
                    }
                    break;
                case "startrandom":
                case "random":
                    StartOnRandomFrame = bool.Parse(value);
                    return true;
                case "ispaused":
                case "paused":
                case "isstopped":
                case "stopped":
                    IsPaused = bool.Parse(value);
                    return true;
                case "foffset":
                case "frameoffset":
                    AnimationFrameOffset = int.Parse(value, CultureInfo.InvariantCulture);
                    return true;
            }
            return base.ImportCustomValues(name, value);
        }
#endif

        public override void LoadContent()
        {
            if (!string.IsNullOrEmpty(_spriteSheetName))
                _spriteSheet = G.Content.Load<SpriteSheet>(_spriteSheetName);
            Play(_animationName);
            base.LoadContent();
        }

        protected override void LightUpdate(DeltaTime time)
        {
            if (_animation != null && !IsFinished && !IsPaused)
                UpdateAnimationFrame(time);
            base.LightUpdate(time);
        }

        protected internal virtual void UpdateAnimationFrame(DeltaTime time)
        {
            _frameDurationTimer -= (float)time.TotalSeconds;
            if (_frameDurationTimer <= 0f)
            {
                _frameDurationTimer = _animation.FrameDuration;
                _animationFrame = (_animationFrame + 1).Wrap(0, _animation.Frames.Count - 1);
                if (!IsLooped && _animationFrame >= _animation.Frames.Count - 1)
                {
                    IsFinished = true;
                    _frameDurationTimer = 0;
                }
                Rectangle previousSourceRectangle = _sourceRectangle;
                _sourceRectangle = _spriteSheet.GetFrameSourceRectangle(_animation.ImageName, _animation.Frames[_animationFrame] + AnimationFrameOffset);
                if (_sourceRectangle != Rectangle.Empty)
                {
                    if (previousSourceRectangle.Width != _sourceRectangle.Width || previousSourceRectangle.Height != _sourceRectangle.Height)
                        Size = new Vector2(_sourceRectangle.Width, _sourceRectangle.Height);
                }
            }
        }

        protected override bool CanDraw()
        {
            if (_animation == null || _spriteSheet == null || _spriteSheet.Texture == null)
                return false;
            return base.CanDraw();
        }

        //protected virtual bool OnCamera()
        //{
        //    Rectangle spriteArea = new Rectangle((int)Position.X, (int)Position.Y, (int)(Size.X * Scale.X), (int)(Size.Y * Size.Y));
        //    Rectangle viewingArea = G.World.Camera.ViewingArea;
        //    viewingArea.Inflate(16, 16); // pad the viewing area with a border of off-screen tiles. for smooth scrolling, otherwise tiles seem to 'pop' in.
        //    return (viewingArea.Contains(spriteArea) || viewingArea.Intersects(spriteArea));
        //}

        protected override void Draw(DeltaTime gameTime, SpriteBatch spriteBatch)
        {
            if (Outline)
            {
                spriteBatch.End();
                G.SimpleEffect.SetTechnique(Effects.SimpleEffect.Technique.FillColor);
                G.SimpleEffect.Color = OutlineColor;
                spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, G.SimpleEffect, G.World.Camera.View);
                spriteBatch.Draw(_spriteSheet.Texture, RenderPosition - Vector2.UnitY, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                spriteBatch.Draw(_spriteSheet.Texture, RenderPosition + Vector2.UnitX, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                spriteBatch.Draw(_spriteSheet.Texture, RenderPosition + Vector2.UnitY, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                spriteBatch.Draw(_spriteSheet.Texture, RenderPosition - Vector2.UnitX, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
                spriteBatch.End();
                spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, G.World.Camera.View);
            }
            spriteBatch.Draw(_spriteSheet.Texture, RenderPosition, _sourceRectangle, Tint, Rotation, RenderOrigin, Scale, SpriteEffects, 0);
        }

        public void Play(string animation)
        {
            _animationName = animation;
            IsPaused = false;
            if (_spriteSheet == null)
            {
                _animation = null;
                return;
            }
            _animation = _spriteSheet.GetAnimation(_animationName);
            if (_animation != null)
                OnAnimationChanged();
        }

        protected internal override void OnPositionChanged()
        {
            base.OnPositionChanged();
            UpdateRenderOrigin();
            UpdateRenderPosition();
        }

        protected internal virtual void OnAnimationChanged()
        {
            _animationFrame = AnimationFrameOffset;
            if (StartOnRandomFrame)
                _animationFrame = G.Random.Next(0, _animation.Frames.Count - 1);
            _frameDurationTimer = _animation.FrameDuration;
            IsFinished = false;
            // draw the first frame
            _sourceRectangle = _spriteSheet.GetFrameSourceRectangle(_animation.ImageName, _animation.Frames[_animationFrame] + AnimationFrameOffset);
            Size = new Vector2(_sourceRectangle.Width, _sourceRectangle.Height);
        }

        public override void Recycle()
        {
            base.Recycle();
            IsLooped = true;
            _spriteSheet = null;
            _spriteSheetName = string.Empty;
            _animation = null;
            _animationName = string.Empty;
            _animationFrame = 0;
            _sourceRectangle = Rectangle.Empty;
            _frameDurationTimer = 0f;
            AnimationFrameOffset = 0;
            SpriteEffects = SpriteEffects.None;
            OutlineColor = Color.White;

            RemoveNextUpdate = true;
            _pool.Release(this);
        }
    }

}

{'fraction_non_alphanumeric': 0.08158747078840556, 'fraction_numerical': 0.003935877987782378, 'mean_word_length': 1.472329211433205, 'pattern_counts': {'":': 72, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5545270', 'n_tokens_mistral': 6118, 'n_tokens_neox': 5789, 'n_words': 1269}