// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using System.Collections.Generic; using System.Drawing; using System.IO; using ClassicalSharp.Events; using ClassicalSharp.GraphicsAPI; #if ANDROID using Android.Graphics; #endif namespace ClassicalSharp.TexturePack { /// Contains and describes the various animations applied to the terrain atlas. public class Animations : IGameComponent { Game game; IGraphicsApi api; Bitmap bmp; FastBitmap fastBmp; List animations = new List(); public void Init( Game game ) { this.game = game; api = game.Graphics; game.Events.TexturePackChanged += TexturePackChanged; game.Events.TextureChanged += TextureChanged; } public void Ready( Game game ) { } public void Reset( Game game ) { } public void OnNewMap( Game game ) { } public void OnNewMapLoaded( Game game ) { } void TexturePackChanged( object sender, EventArgs e ) { animations.Clear(); } void TextureChanged( object sender, TextureEventArgs e ) { if( e.Name == "animations.png" || e.Name == "animation.png" ) { MemoryStream stream = new MemoryStream( e.Data ); SetAtlas( Platform.ReadBmp( stream ) ); } else if( e.Name == "animations.txt" || e.Name == "animation.txt" ) { MemoryStream stream = new MemoryStream( e.Data ); StreamReader reader = new StreamReader( stream ); ReadAnimationsDescription( reader ); } } /// Sets the atlas bitmap that animation frames are contained within. public void SetAtlas( Bitmap bmp ) { if( !FastBitmap.CheckFormat( bmp.PixelFormat ) ) game.Drawer2D.ConvertTo32Bpp( ref bmp ); Clear(); this.bmp = bmp; fastBmp = new FastBitmap( bmp, true, true ); } /// Runs through all animations and if necessary updates the terrain atlas. public void Tick( double delta ) { if( animations.Count == 0 ) return; if( fastBmp == null ) { game.Chat.Add( "&cCurrent texture pack specifies it uses animations," ); game.Chat.Add( "&cbut is missing animations.png" ); animations.Clear(); return; } foreach( AnimationData anim in animations ) ApplyAnimation( anim ); } /// Reads a text file that contains a number of lines, with each line describing:
/// 1) the target tile in the terrain atlas 2) the start location of animation frames
/// 3) the size of each animation frame 4) the number of animation frames
/// 5) the delay between advancing animation frames.
public void ReadAnimationsDescription( StreamReader reader ) { string line; while( (line = reader.ReadLine()) != null ) { if( line.Length == 0 || line[0] == '#' ) continue; string[] parts = line.Split( ' ' ); if( parts.Length < 7 ) { Utils.LogDebug( "Not enough arguments for animation: {0}", line ); continue; } byte tileX, tileY; if( !Byte.TryParse( parts[0], out tileX ) || !Byte.TryParse( parts[1], out tileY ) || tileX >= 16 || tileY >= 16 ) { Utils.LogDebug( "Invalid animation tile coordinates: {0}", line ); continue; } int frameX, frameY; if( !Int32.TryParse( parts[2], out frameX ) || !Int32.TryParse( parts[3], out frameY ) || frameX < 0 || frameY < 0 ) { Utils.LogDebug( "Invalid animation coordinates: {0}", line ); continue; } int frameSize, statesCount, tickDelay; if( !Int32.TryParse( parts[4], out frameSize ) || !Int32.TryParse( parts[5], out statesCount ) || !Int32.TryParse( parts[6], out tickDelay ) || frameSize < 0 || statesCount < 0 || tickDelay < 0 ) { Utils.LogDebug( "Invalid animation: {0}", line ); continue; } DefineAnimation( tileX, tileY, frameX, frameY, frameSize, statesCount, tickDelay ); } } /// Adds an animation described by the arguments to the list of animations /// that are applied to the terrain atlas. public void DefineAnimation( int tileX, int tileY, int frameX, int frameY, int frameSize, int statesNum, int tickDelay ) { AnimationData data = new AnimationData(); data.TileX = tileX; data.TileY = tileY; data.FrameX = frameX; data.FrameY = frameY; data.FrameSize = frameSize; data.StatesCount = statesNum; data.TickDelay = tickDelay; animations.Add( data ); } FastBitmap animPart = new FastBitmap(); unsafe void ApplyAnimation( AnimationData data ) { data.Tick--; if( data.Tick >= 0 ) return; data.CurrentState++; data.CurrentState %= data.StatesCount; data.Tick = data.TickDelay; TerrainAtlas1D atlas = game.TerrainAtlas1D; int texId = ( data.TileY << 4 ) | data.TileX; int index = atlas.Get1DIndex( texId ); int rowNum = atlas.Get1DRowId( texId ); int size = data.FrameSize; byte* temp = stackalloc byte[size * size * 4]; animPart.SetData( size, size, size * 4, (IntPtr)temp, false ); FastBitmap.MovePortion( data.FrameX + data.CurrentState * size, data.FrameY, 0, 0, fastBmp, animPart, size ); api.UpdateTexturePart( atlas.TexIds[index], 0, rowNum * game.TerrainAtlas.elementSize, animPart ); } class AnimationData { public int TileX, TileY; // Tile (not pixel) coordinates in terrain.png public int FrameX, FrameY; // Top left pixel coordinates of start frame in animatons.png public int FrameSize; // Size of each frame in pixel coordinates public int CurrentState; // Current animation frame index public int StatesCount; // Total number of animation frames public int Tick, TickDelay; } /// Disposes the atlas bitmap that contains animation frames, and clears /// the list of defined animations. public void Clear() { animations.Clear(); if( bmp == null ) return; fastBmp.Dispose(); fastBmp = null; bmp.Dispose(); bmp = null; } public void Dispose() { Clear(); game.Events.TextureChanged -= TextureChanged; game.Events.TexturePackChanged -= TexturePackChanged; } } }