using System; using System.Collections.Generic; using System.Drawing; using System.IO; using ClassicalSharp.GraphicsAPI; namespace ClassicalSharp.TexturePack { /// Contains and describes the various animations applied to the terrain atlas. public class Animations { Game game; IGraphicsApi api; Bitmap bmp; FastBitmap fastBmp; List animations = new List(); public bool Enabled = true; public Animations( Game game ) { this.game = game; api = game.Graphics; } /// Sets the atlas bitmap that animation frames are contained within. public void SetAtlas( Bitmap bmp ) { Dispose(); this.bmp = bmp; fastBmp = new FastBitmap( bmp, true ); } /// Runs through all animations and if necessary updates the terrain atlas. public void Tick( double delta ) { if( animations.Count == 0 || !Enabled ) 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 ); } 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]; FastBitmap part = new FastBitmap( size, size, size * 4, (IntPtr)temp ); FastBitmap.MovePortion( data.FrameX + data.CurrentState * size, data.FrameY, 0, 0, fastBmp, part, size ); api.UpdateTexturePart( atlas.TexIds[index], 0, rowNum * game.TerrainAtlas.elementSize, part ); } 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 Dispose() { animations.Clear(); if( bmp == null ) return; fastBmp.Dispose(); bmp.Dispose(); bmp = null; } } }