1
0
Fork 0

Using circular linked lists for tile animations

This will drastically simplify animations and allow for a more basic mapping
of "game objects" to tiles
master
Mike Gerwitz 2012-03-14 20:37:31 -04:00
parent ae01d6ee08
commit 5e60d6c9e5
4 changed files with 150 additions and 125 deletions

View File

@ -30,8 +30,8 @@ ltjs.ClassicTileDfn = Class( 'ClassicTileDfn' )
* *
* The definition should be an array of arrays, with the first index * The definition should be an array of arrays, with the first index
* representing the tile id and the second index representing whether or not * 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 * a mask should be applied (0 or 1). Multiple tiles with the same id will
* provided at the top of this file. * be combined into frames for animation.
* *
* The tiles are expected to be parsed per row, beginning at the upper-left * 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 * corner. Specifying the tiles in this manner is both concise and helps to
@ -43,60 +43,60 @@ ltjs.ClassicTileDfn = Class( 'ClassicTileDfn' )
'public getTileDefinition': function() 'public getTileDefinition': function()
{ {
return [ return [
[ 'dirt', 0 ], /* dirt */ [ 'dirt', 0 ], /* dirt */
[ 'tup', 1 ], /* tank up */ [ 'tup', 1 ], /* tank up */
[ 'tright', 1 ], /* tank right */ [ 'tright', 1 ], /* tank right */
[ 'tdown', 1 ], /* tank down */ [ 'tdown', 1 ], /* tank down */
[ 'tleft', 1 ], /* tank left */ [ 'tleft', 1 ], /* tank left */
[ 'base', 0 ], /* base */ [ 'base', 0 ], /* base */
[ 'basealt', 0 ], /* base alt */ [ 'base', 0 ], /* base alt */
[ 'basealt2', 0 ], /* base alt 2 */ [ 'base', 0 ], /* base alt 2 */
[ 'water', 0 ], /* water */ [ 'water', 0 ], /* water */
[ 'wateralt', 0 ], /* water alt */ [ 'water', 0 ], /* water alt */
[ 'wateralt2', 0 ], /* water alt 2 */ [ 'water', 0 ], /* water alt 2 */
[ 'atdownb', 1 ], /* anti-tank, blown up, down */ [ 'atdownb', 1 ], /* anti-tank, blown up, down */
[ 'block', 0 ], /* non-movable block */ [ 'block', 0 ], /* non-movable block */
[ 'mblock', 0 ], /* movable block */ [ 'mblock', 0 ], /* movable block */
[ 'brick', 0 ], /* brick */ [ 'brick', 0 ], /* brick */
[ 'atup', 1 ], /* anti-tank up */ [ 'atup', 1 ], /* anti-tank up */
[ 'atupalt', 1 ], /* anti-tank up alt */ [ 'atup', 1 ], /* anti-tank up alt */
[ 'atupalt2', 1 ], /* anti-tank up alt 2 */ [ 'atup', 1 ], /* anti-tank up alt 2 */
[ 'mblockw', 0 ], /* movable block in water */ [ 'mblockw', 0 ], /* movable block in water */
[ 'mirrorul', 1 ], /* mirror up-left */ [ 'mirrorul', 1 ], /* mirror up-left */
[ 'mirrorur', 1 ], /* mirror up-right */ [ 'mirrorur', 1 ], /* mirror up-right */
[ 'mirrordr', 1 ], /* mirror down-right */ [ 'mirrordr', 1 ], /* mirror down-right */
[ 'mirrordl', 1 ], /* mirror down-left */ [ 'mirrordl', 1 ], /* mirror down-left */
[ 'owup', 0 ], /* one-way up */ [ 'owup', 0 ], /* one-way up */
[ 'owupalt', 0 ], /* one-way up alt */ [ 'owup', 0 ], /* one-way up alt */
[ 'owupalt2', 0 ], /* one-way up alt 2 */ [ 'owup', 0 ], /* one-way up alt 2 */
[ 'owright', 0 ], /* one-way right */ [ 'owright', 0 ], /* one-way right */
[ 'owrightalt', 0 ], /* one-way right alt */ [ 'owright', 0 ], /* one-way right alt */
[ 'owrightalt2', 0 ], /* one-way right alt 2 */ [ 'owright', 0 ], /* one-way right alt 2 */
[ 'owdown', 0 ], /* one-way down */ [ 'owdown', 0 ], /* one-way down */
[ 'owdownalt', 0 ], /* one-way down alt */ [ 'owdown', 0 ], /* one-way down alt */
[ 'owdownalt2', 0 ], /* one-way down alt 2 */ [ 'owdown', 0 ], /* one-way down alt 2 */
[ 'owleft', 0 ], /* one-way left */ [ 'owleft', 0 ], /* one-way left */
[ 'owleftalt', 0 ], /* one-way left alt */ [ 'owleft', 0 ], /* one-way left alt */
[ 'owleftalt2', 0 ], /* one-way left alt 3 */ [ 'owleft', 0 ], /* one-way left alt 3 */
[ 'atright', 1 ], /* anti-tank right */ [ 'atright', 1 ], /* anti-tank right */
[ 'atrightalt', 1 ], /* anti-tank right alt */ [ 'atright', 1 ], /* anti-tank right alt */
[ 'atrightalt2', 1 ], /* anti-tank right alt 2 */ [ 'atright', 1 ], /* anti-tank right alt 2 */
[ 'atdown', 1 ], /* anti-tank down */ [ 'atdown', 1 ], /* anti-tank down */
[ 'atdownalt', 1 ], /* anti-tank down alt */ [ 'atdown', 1 ], /* anti-tank down alt */
[ 'atdownalt2', 1 ], /* anti-tank down alt 2 */ [ 'atdown', 1 ], /* anti-tank down alt 2 */
[ 'atleft', 1 ], /* anti-tank left */ [ 'atleft', 1 ], /* anti-tank left */
[ 'atleftalt', 1 ], /* anti-tank left alt */ [ 'atleft', 1 ], /* anti-tank left alt */
[ 'atleftalt2', 1 ], /* anti-tank left alt 2 */ [ 'atleft', 1 ], /* anti-tank left alt 2 */
[ 'cblock', 0 ], /* crystal block */ [ 'cblock', 0 ], /* crystal block */
[ 'cblockht', 0 ], /* crystal block hit by tank */ [ 'cblockht', 0 ], /* crystal block hit by tank */
[ 'rmirrorul', 0 ], /* roto-mirror up-left */ [ 'rmirrorul', 0 ], /* roto-mirror up-left */
[ 'rmirrorur', 0 ], /* roto-mirror up-right */ [ 'rmirrorur', 0 ], /* roto-mirror up-right */
[ 'rmirrordr', 0 ], /* roto-mirror down-right */ [ 'rmirrordr', 0 ], /* roto-mirror down-right */
[ 'rmirrordl', 0 ], /* roto-mirror down-left */ [ 'rmirrordl', 0 ], /* roto-mirror down-left */
[ 'cblockhat', 0 ], /* crystal block hit by anti-tank */ [ 'cblockhat', 0 ], /* crystal block hit by anti-tank */
[ 'atrightb', 1 ], /* anti-tank, blown up, right */ [ 'atrightb', 1 ], /* anti-tank, blown up, right */

View File

@ -28,72 +28,51 @@
* add additional features to the game, or even to simply restructure the tile * add additional features to the game, or even to simply restructure the tile
* positions of the current one), we have them covered. * 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. * 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 * dirt - dirt
* tup - tank up * tup - tank up
* tright - tank right * tright - tank right
* tdown - tank down * tdown - tank down
* tleft - tank left * tleft - tank left
* base - base * base - base
* basealt - base alt * water - water
* basealt2 - base alt 2 * atdownb - anti-tank down
* water - water * block - block
* wateralt - water alt * mblock - movable block
* * brick - brick
* wateralt2 - water alt 2 * atup - anti-tank up
* atdownb - anti-tank down * mblockw - movable block in water
* block - block * mirrorul - mirror up-left
* mblock - movable block * mirrorur - mirror up-right
* brick - brick * mirrordr - mirror down-right
* atup - anti-tank up * mirrordl - mirror down-left
* atupalt - anti-tank up alt * owup - one-way up
* atupalt2 - anti-tank up alt 2 * owright - one-way right
* mblockw - movable block in water * owdown - one-way down
* mirrorul - mirror up-left * owleft - one-way left
* * atright - anti-tank right
* mirrorur - mirror up-right * atdown - anti-tank down
* mirrordr - mirror down-right * atleft - anti-tank left
* mirrordl - mirror down-left * cblock - crystal block
* owup - one-way up * cblockht - crystal block hit by tank
* owupalt - one-way up alt * rmirrorul - roto-mirror up-left
* owupalt2 - one-way up alt 2 * rmirrorur - roto-mirror up-right
* owright - one-way right * rmirrordr - roto-mirror down-right
* owrightalt - one-way right alt * rmirrordl - roto-mirror down-left
* owrightalt2 - one-way right alt 2 * cblockhat - crystal block hit by anti-tank
* owdown - one-way down * atrightb - anti-tank, blown up, right
* * atleftb - anti-tank, blown up, left
* owdownalt - one-way down alt * atupb - anti-tank, blown up, up
* owdownalt2 - one-way down alt 2 * tunnel - wormhole/tunnel
* owleft - one-way left * ice - ice
* owleftalt - one-way left alt * thinice - thin ice
* 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
*/ */
@ -112,7 +91,8 @@ ltjs.TileDfn = Interface( 'TileDfn',
* The definition should be an array of arrays, with the first index * The definition should be an array of arrays, with the first index
* representing the tile id and the second index representing whether or not * 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 * 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 * 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 * corner. Specifying the tiles in this manner is both concise and helps to

View File

@ -258,7 +258,7 @@ ltjs.TileMasker = Class( 'TileMasker',
// create each tile (preserving order, thus no decrementing) // create each tile (preserving order, thus no decrementing)
while ( ++i < len ) while ( ++i < len )
{ {
var name = tdata[ i ][ 0 ], var id = tdata[ i ][ 0 ],
mask = tdata[ i ][ 1 ], mask = tdata[ i ][ 1 ],
// calculate the X and Y position of this tile based on the tile // 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 // the third index indicates whether or not a mask should be applied
// to the tile // to the tile
tiles[ name ] = ( mask === 1 ) this.appendTileFrame( tiles, id, ( mask === 1 )
? this.getMaskedTileData( data_mask, x, y ) ? this.getMaskedTileData( data_mask, x, y )
: this.getTileData( x, y ); : this.getTileData( x, y )
);
} }
callback( tiles ); 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.<string>} 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 * Retrieve a tile with the mask applied
* *

View File

@ -97,14 +97,26 @@
// clear canvas to erase any previous LTG contents // clear canvas to erase any previous LTG contents
ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height ); ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
var i = 0; var i = j = 0;
for ( var tile in tiles ) for ( var tile in tiles )
{ {
var x = ( ( i % 10 ) * 50 ), var cur, end;
y = ( Math.floor( i / 10 ) * 50 );
ctx.putImageData( tiles[ tile ], x, y ); // the default position is the last tile in the animation, so
ctx.fillText( tile, x, ( y + 43 ), 50 ); // 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++; i++;
} }