diff --git a/lib/ClassicTileDfn.js b/lib/ClassicTileDfn.js index 32d98d5..45b661c 100644 --- a/lib/ClassicTileDfn.js +++ b/lib/ClassicTileDfn.js @@ -30,8 +30,8 @@ ltjs.ClassicTileDfn = Class( 'ClassicTileDfn' ) * * The definition should be an array of arrays, with the first index * representing the tile id and the second index representing whether or not - * a mask should be applied (0 or 1). A list of standard tile ids are - * provided at the top of this file. + * a mask should be applied (0 or 1). Multiple tiles with the same id will + * be combined into frames for animation. * * The tiles are expected to be parsed per row, beginning at the upper-left * corner. Specifying the tiles in this manner is both concise and helps to @@ -43,60 +43,60 @@ ltjs.ClassicTileDfn = Class( 'ClassicTileDfn' ) 'public getTileDefinition': function() { return [ - [ 'dirt', 0 ], /* dirt */ - [ 'tup', 1 ], /* tank up */ - [ 'tright', 1 ], /* tank right */ - [ 'tdown', 1 ], /* tank down */ - [ 'tleft', 1 ], /* tank left */ - [ 'base', 0 ], /* base */ - [ 'basealt', 0 ], /* base alt */ - [ 'basealt2', 0 ], /* base alt 2 */ - [ 'water', 0 ], /* water */ - [ 'wateralt', 0 ], /* water alt */ + [ 'dirt', 0 ], /* dirt */ + [ 'tup', 1 ], /* tank up */ + [ 'tright', 1 ], /* tank right */ + [ 'tdown', 1 ], /* tank down */ + [ 'tleft', 1 ], /* tank left */ + [ 'base', 0 ], /* base */ + [ 'base', 0 ], /* base alt */ + [ 'base', 0 ], /* base alt 2 */ + [ 'water', 0 ], /* water */ + [ 'water', 0 ], /* water alt */ - [ 'wateralt2', 0 ], /* water alt 2 */ - [ 'atdownb', 1 ], /* anti-tank, blown up, down */ - [ 'block', 0 ], /* non-movable block */ - [ 'mblock', 0 ], /* movable block */ - [ 'brick', 0 ], /* brick */ - [ 'atup', 1 ], /* anti-tank up */ - [ 'atupalt', 1 ], /* anti-tank up alt */ - [ 'atupalt2', 1 ], /* anti-tank up alt 2 */ - [ 'mblockw', 0 ], /* movable block in water */ - [ 'mirrorul', 1 ], /* mirror up-left */ + [ 'water', 0 ], /* water alt 2 */ + [ 'atdownb', 1 ], /* anti-tank, blown up, down */ + [ 'block', 0 ], /* non-movable block */ + [ 'mblock', 0 ], /* movable block */ + [ 'brick', 0 ], /* brick */ + [ 'atup', 1 ], /* anti-tank up */ + [ 'atup', 1 ], /* anti-tank up alt */ + [ 'atup', 1 ], /* anti-tank up alt 2 */ + [ 'mblockw', 0 ], /* movable block in water */ + [ 'mirrorul', 1 ], /* mirror up-left */ - [ 'mirrorur', 1 ], /* mirror up-right */ - [ 'mirrordr', 1 ], /* mirror down-right */ - [ 'mirrordl', 1 ], /* mirror down-left */ - [ 'owup', 0 ], /* one-way up */ - [ 'owupalt', 0 ], /* one-way up alt */ - [ 'owupalt2', 0 ], /* one-way up alt 2 */ - [ 'owright', 0 ], /* one-way right */ - [ 'owrightalt', 0 ], /* one-way right alt */ - [ 'owrightalt2', 0 ], /* one-way right alt 2 */ - [ 'owdown', 0 ], /* one-way down */ + [ 'mirrorur', 1 ], /* mirror up-right */ + [ 'mirrordr', 1 ], /* mirror down-right */ + [ 'mirrordl', 1 ], /* mirror down-left */ + [ 'owup', 0 ], /* one-way up */ + [ 'owup', 0 ], /* one-way up alt */ + [ 'owup', 0 ], /* one-way up alt 2 */ + [ 'owright', 0 ], /* one-way right */ + [ 'owright', 0 ], /* one-way right alt */ + [ 'owright', 0 ], /* one-way right alt 2 */ + [ 'owdown', 0 ], /* one-way down */ - [ 'owdownalt', 0 ], /* one-way down alt */ - [ 'owdownalt2', 0 ], /* one-way down alt 2 */ - [ 'owleft', 0 ], /* one-way left */ - [ 'owleftalt', 0 ], /* one-way left alt */ - [ 'owleftalt2', 0 ], /* one-way left alt 3 */ - [ 'atright', 1 ], /* anti-tank right */ - [ 'atrightalt', 1 ], /* anti-tank right alt */ - [ 'atrightalt2', 1 ], /* anti-tank right alt 2 */ - [ 'atdown', 1 ], /* anti-tank down */ - [ 'atdownalt', 1 ], /* anti-tank down alt */ + [ 'owdown', 0 ], /* one-way down alt */ + [ 'owdown', 0 ], /* one-way down alt 2 */ + [ 'owleft', 0 ], /* one-way left */ + [ 'owleft', 0 ], /* one-way left alt */ + [ 'owleft', 0 ], /* one-way left alt 3 */ + [ 'atright', 1 ], /* anti-tank right */ + [ 'atright', 1 ], /* anti-tank right alt */ + [ 'atright', 1 ], /* anti-tank right alt 2 */ + [ 'atdown', 1 ], /* anti-tank down */ + [ 'atdown', 1 ], /* anti-tank down alt */ - [ 'atdownalt2', 1 ], /* anti-tank down alt 2 */ - [ 'atleft', 1 ], /* anti-tank left */ - [ 'atleftalt', 1 ], /* anti-tank left alt */ - [ 'atleftalt2', 1 ], /* anti-tank left alt 2 */ - [ 'cblock', 0 ], /* crystal block */ - [ 'cblockht', 0 ], /* crystal block hit by tank */ - [ 'rmirrorul', 0 ], /* roto-mirror up-left */ - [ 'rmirrorur', 0 ], /* roto-mirror up-right */ - [ 'rmirrordr', 0 ], /* roto-mirror down-right */ - [ 'rmirrordl', 0 ], /* roto-mirror down-left */ + [ 'atdown', 1 ], /* anti-tank down alt 2 */ + [ 'atleft', 1 ], /* anti-tank left */ + [ 'atleft', 1 ], /* anti-tank left alt */ + [ 'atleft', 1 ], /* anti-tank left alt 2 */ + [ 'cblock', 0 ], /* crystal block */ + [ 'cblockht', 0 ], /* crystal block hit by tank */ + [ 'rmirrorul', 0 ], /* roto-mirror up-left */ + [ 'rmirrorur', 0 ], /* roto-mirror up-right */ + [ 'rmirrordr', 0 ], /* roto-mirror down-right */ + [ 'rmirrordl', 0 ], /* roto-mirror down-left */ [ 'cblockhat', 0 ], /* crystal block hit by anti-tank */ [ 'atrightb', 1 ], /* anti-tank, blown up, right */ diff --git a/lib/TileDfn.js b/lib/TileDfn.js index 9a2e53c..a83746e 100644 --- a/lib/TileDfn.js +++ b/lib/TileDfn.js @@ -28,72 +28,51 @@ * add additional features to the game, or even to simply restructure the tile * positions of the current one), we have them covered. * + * Many tiles are used for the purpose of creating animations. In order to + * support an arbitrary number of frame tiles for any type of tile, animations + * will be created from tiles that share the same id. When a tile is encountered + * with a duplicate id, it is added to any previous tiles to create animation + * frames in the order in which they appear in the definition. + * * Below is a list of standard ids that must be mapped for classic game play. - * The *alt tiles are used in animation (the terminology comes from the original - * game sources). * - * dirt - dirt - * tup - tank up - * tright - tank right - * tdown - tank down - * tleft - tank left - * base - base - * basealt - base alt - * basealt2 - base alt 2 - * water - water - * wateralt - water alt - * - * wateralt2 - water alt 2 - * atdownb - anti-tank down - * block - block - * mblock - movable block - * brick - brick - * atup - anti-tank up - * atupalt - anti-tank up alt - * atupalt2 - anti-tank up alt 2 - * mblockw - movable block in water - * mirrorul - mirror up-left - * - * mirrorur - mirror up-right - * mirrordr - mirror down-right - * mirrordl - mirror down-left - * owup - one-way up - * owupalt - one-way up alt - * owupalt2 - one-way up alt 2 - * owright - one-way right - * owrightalt - one-way right alt - * owrightalt2 - one-way right alt 2 - * owdown - one-way down - * - * owdownalt - one-way down alt - * owdownalt2 - one-way down alt 2 - * owleft - one-way left - * owleftalt - one-way left alt - * owleftalt2 - one-way left alt 3 - * atright - anti-tank right - * atrightalt - anti-tank right alt - * atrightalt2 - anti-tank right alt 2 - * atdown - anti-tank down - * atdownalt - anti-tank down alt - * - * atdownalt2 - anti-tank down alt 2 - * atleft - anti-tank left - * atleftalt - anti-tank left alt - * atleftalt2 - anti-tank left alt 2 - * cblock - crystal block - * cblockht - crystal block hit by tank - * rmirrorul - roto-mirror up-left - * rmirrorur - roto-mirror up-right - * rmirrordr - roto-mirror down-right - * rmirrordl - roto-mirror down-left - * - * cblockhat - crystal block hit by anti-tank - * atrightb - anti-tank, blown up, right - * atleftb - anti-tank, blown up, left - * atupb - anti-tank, blown up, up - * tunnel - wormhole/tunnel - * ice - ice - * thinice - thin ice + * dirt - dirt + * tup - tank up + * tright - tank right + * tdown - tank down + * tleft - tank left + * base - base + * water - water + * atdownb - anti-tank down + * block - block + * mblock - movable block + * brick - brick + * atup - anti-tank up + * mblockw - movable block in water + * mirrorul - mirror up-left + * mirrorur - mirror up-right + * mirrordr - mirror down-right + * mirrordl - mirror down-left + * owup - one-way up + * owright - one-way right + * owdown - one-way down + * owleft - one-way left + * atright - anti-tank right + * atdown - anti-tank down + * atleft - anti-tank left + * cblock - crystal block + * cblockht - crystal block hit by tank + * rmirrorul - roto-mirror up-left + * rmirrorur - roto-mirror up-right + * rmirrordr - roto-mirror down-right + * rmirrordl - roto-mirror down-left + * cblockhat - crystal block hit by anti-tank + * atrightb - anti-tank, blown up, right + * atleftb - anti-tank, blown up, left + * atupb - anti-tank, blown up, up + * tunnel - wormhole/tunnel + * ice - ice + * thinice - thin ice */ @@ -112,7 +91,8 @@ ltjs.TileDfn = Interface( 'TileDfn', * The definition should be an array of arrays, with the first index * representing the tile id and the second index representing whether or not * a mask should be applied (0 or 1). A list of standard tile ids are - * provided at the top of this file. + * provided at the top of this file. Multiple tiles with the same id will be + * combined into frames to create an animation. * * The tiles are expected to be parsed per row, beginning at the upper-left * corner. Specifying the tiles in this manner is both concise and helps to diff --git a/lib/TileMasker.js b/lib/TileMasker.js index 420b258..40534f1 100644 --- a/lib/TileMasker.js +++ b/lib/TileMasker.js @@ -258,7 +258,7 @@ ltjs.TileMasker = Class( 'TileMasker', // create each tile (preserving order, thus no decrementing) while ( ++i < len ) { - var name = tdata[ i ][ 0 ], + var id = tdata[ i ][ 0 ], mask = tdata[ i ][ 1 ], // calculate the X and Y position of this tile based on the tile @@ -268,15 +268,48 @@ ltjs.TileMasker = Class( 'TileMasker', // the third index indicates whether or not a mask should be applied // to the tile - tiles[ name ] = ( mask === 1 ) + this.appendTileFrame( tiles, id, ( mask === 1 ) ? this.getMaskedTileData( data_mask, x, y ) - : this.getTileData( x, y ); + : this.getTileData( x, y ) + ); } callback( tiles ); }, + /** + * Adds a tile frame to the set, permitting animation + * + * This creates a circular linked list with each of the frames for a given + * tile id. This structure is ideal for looping animations. One can simply + * check to see if the tile has a single frame by performing a strict + * equality check on the current tile and its 'next' entry. + * + * @param {Object.} set set to append tile frame to + * @param {string} id tile id + * @param {Object} data tile ImageData + * + * @return {undefined} + */ + 'protected appendTileFrame': function( set, id, data ) + { + var prev = set[ id ]; + + set[ id ] = { data: data }; + + // If there is a previous frame, set the 'next' entry to its 'next' + // entry to maintain the circular reference. Otherwise, set to self. + set[ id ].next = ( prev ) + ? prev.next + : set[ id ]; + + // if there was a previous entry, set its 'next' entry to our new frame, + // expanding the linked list + prev && ( prev.next = set[ id ] ) + }, + + /** * Retrieve a tile with the mask applied * diff --git a/test/ltgloader-demo.html b/test/ltgloader-demo.html index 3ab7e6a..a43fa84 100644 --- a/test/ltgloader-demo.html +++ b/test/ltgloader-demo.html @@ -97,14 +97,26 @@ // clear canvas to erase any previous LTG contents ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height ); - var i = 0; + var i = j = 0; for ( var tile in tiles ) { - var x = ( ( i % 10 ) * 50 ), - y = ( Math.floor( i / 10 ) * 50 ); + var cur, end; - ctx.putImageData( tiles[ tile ], x, y ); - ctx.fillText( tile, x, ( y + 43 ), 50 ); + // the default position is the last tile in the animation, so + // advance one before beginning output to keep consistent with the + // tile set + end = cur = tiles[ tile ].next; + + do + { + var x = ( ( j % 10 ) * 50 ), + y = ( Math.floor( j / 10 ) * 50 ); + + ctx.putImageData( cur.data, x, y ); + ctx.fillText( tile, x, ( y + 43 ), 50 ); + + j++; + } while ( ( cur = cur.next ) !== end ) i++; }