diff --git a/lib/ClassicTileDfn.js b/lib/ClassicTileDfn.js
new file mode 100644
index 0000000..32d98d5
--- /dev/null
+++ b/lib/ClassicTileDfn.js
@@ -0,0 +1,125 @@
+/**
+ * Contains tile definition for classic (original game) tile sets
+ *
+ * Copyright (C) 2012 Mike Gerwitz
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+/**
+ * Definition for classic tile sets found in the original game
+ */
+ltjs.ClassicTileDfn = Class( 'ClassicTileDfn' )
+ .implement( ltjs.TileDfn )
+ .extend(
+{
+ /**
+ * Retrieve the tile definition
+ *
+ * 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.
+ *
+ * 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
+ * eliminate human error and maintenance concerns that may arise from the
+ * manual specification of tile coordinates.
+ *
+ * @return {Array.>} tile definition
+ */
+ '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 */
+
+ [ '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 */
+
+ [ '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 */
+
+ [ '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 */
+
+ [ '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 */
+
+ [ 'cblockhat', 0 ], /* crystal block hit by anti-tank */
+ [ 'atrightb', 1 ], /* anti-tank, blown up, right */
+ [ 'atleftb', 1 ], /* anti-tank, blown up, left */
+ [ 'atupb', 1 ], /* anti-tank, blown up, up */
+ [ 'tunnel', 1 ], /* wormhole/tunnel */
+ [ 'ice', 0 ], /* ice */
+ [ 'thinice', 0 ] /* thin ice */
+ ];
+ },
+
+
+ /**
+ * Retrieve tile dimensions
+ *
+ * This method should return an array containing three values: the width and
+ * height of the individual tiles and the number of tiles per row. From this
+ * data, any individual tile can be extracted from the tile set.
+ *
+ * @return {Array.} tile width, tile height, tiles per row
+ */
+ 'public getTileDimensions': function()
+ {
+ return [ 32, 32, 10 ];
+ }
+} );
diff --git a/lib/TileDfn.js b/lib/TileDfn.js
new file mode 100644
index 0000000..9a2e53c
--- /dev/null
+++ b/lib/TileDfn.js
@@ -0,0 +1,137 @@
+/**
+ * Contains tile definition interface
+ *
+ * Copyright (C) 2012 Mike Gerwitz
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * This interface provides a definition for the processing of tile sets.
+ * Specifically, it includes the width and height of each individual tile, the
+ * number of tiles per row, the ids to map each tile to and a flag indicating
+ * whether or not the tile should have a mask applied to it.
+ *
+ * The true benefit of this interface is that it abstracts the tile
+ * interpretation logic in such a way that *any* tile set is supported. That is,
+ * should someone decide to support entirely separate tile sets (perhaps to
+ * add additional features to the game, or even to simply restructure the tile
+ * positions of the current one), we have them covered.
+ *
+ * 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
+ */
+
+
+/**
+ * Defines the contents of a tile set
+ *
+ * The tile set is expected to be an image of any width (W) and height (H)
+ * containing individual tiles (of width w and height h) such that W % w === 0
+ * and H % h === 0. All tiles are expected to share the same dimensions.
+ */
+ltjs.TileDfn = Interface( 'TileDfn',
+{
+ /**
+ * Retrieve the tile definition
+ *
+ * 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.
+ *
+ * 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
+ * eliminate human error and maintenance concerns that may arise from the
+ * manual specification of tile coordinates.
+ *
+ * @return {Array.>} tile definition
+ */
+ 'public getTileDefinition': [],
+
+
+ /**
+ * Retrieve tile dimensions
+ *
+ * This method should return an array containing three values: the width and
+ * height of the individual tiles and the number of tiles per row. From this
+ * data, any individual tile can be extracted from the tile set.
+ *
+ * @return {Array.} tile width, tile height, tiles per row
+ */
+ 'public getTileDimensions': []
+} );
diff --git a/lib/TileMasker.js b/lib/TileMasker.js
index 1056518..c577ef5 100644
--- a/lib/TileMasker.js
+++ b/lib/TileMasker.js
@@ -68,102 +68,113 @@
*/
ltjs.TileMasker = Class( 'TileMasker',
{
- 'private const _TDATA': [
- [ '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 */
-
- [ '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 */
-
- [ '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 */
-
- [ '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 2 */
- [ '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 */
-
- [ '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 */
-
- [ 'cblockhat', 0 ], /* crystal block hit by anti-tank */
- [ 'atrightb', 1 ], /* anti-tank, blown up, right */
- [ 'atleftb', 1 ], /* anti-tank, blown up, left */
- [ 'atupb', 1 ], /* anti-tank, blown up, up */
- [ 'tunnel', 1 ], /* wormhole/tunnel */
- [ 'ice', 0 ], /* ice */
- [ 'thinice', 0 ] /* thin ice */
- ],
-
- 'private const _TSIZE': [ 32, 32, 320, 192 ],
-
/**
* Canvas 2D context (used for masking and tile slicing)
* @type {CanvasRenderingContext2d}
*/
'private _context': null,
+ /**
+ * Tile definition to use for all operations
+ * @type {Array.>}
+ */
+ 'private _tileDfn': null,
/**
- * Initialize loader with a 2D canvas context
- *
- * The context will be used for masking the game bitmap and slicing the
- * tiles.
+ * Width of each individual tile
+ * @type {number}
*/
- __construct: function()
+ 'private _tileWidth': 0,
+
+ /**
+ * Height of each individual tile
+ * @type {number}
+ */
+ 'private _tileHeight': 0,
+
+ /**
+ * Number of tiles per row
+ * @type {number}
+ */
+ 'private _tilesPerRow': 0,
+
+ /**
+ * Calculated width of tile set provided a tile definition
+ * @type {number}
+ */
+ 'private _setWidth': 0,
+
+ /**
+ * Calculated height of tile set provided a tile definition
+ * @type {number}
+ */
+ 'private _setHeight': 0,
+
+
+ /**
+ * Initialize loader with a tile definition
+ *
+ * The tile definition defines how a tile set should be interpreted. This
+ * allows us to support *any* type of tile set -- not just those that are
+ * defined by the original game.
+ *
+ * @param {ltjs.TileDfn} tile_dfn tile definition object
+ */
+ __construct: function( tile_dfn )
{
+ if ( !( Class.isA( ltjs.TileDfn, tile_dfn ) ) )
+ {
+ throw TypeError( "Invalid tile definition provided." );
+ }
+
+ // pre-calculate our tile information
+ this._tileDfn = tile_dfn.getTileDefinition();
+ this._calcSetDimensions( tile_dfn );
+
// rather than accepting a context, we will create our own canvas in
// memory to perform our operations (it will not be added to the DOM, so
// these operations will not be visible to the user)
- var context = document.createElement( 'canvas' ).getContext( '2d' ),
- sizes = this.__self.$( '_TSIZE' );
+ var context = document.createElement( 'canvas' ).getContext( '2d' );
// size the canvas so that it can fit the entire tileset
- context.canvas.width = sizes[ 2 ];
- context.canvas.height = sizes[ 3 ];
+ context.canvas.width = this._setWidth;
+ context.canvas.height = this._setHeight;
this._context = context;
},
+ /**
+ * Calculate tile set dimensions from the given tile definition object
+ *
+ * These dimensions are cached, as these are frequently used and it is
+ * unwise to continuously invoke methods unnecessarily (who knows what the
+ * developer of the given tile definition did!).
+ *
+ * @param {ltjs.TileDfn} tile_dfn tile definition object
+ *
+ * @return {undefined}
+ */
+ 'private _calcSetDimensions': function( tile_dfn )
+ {
+ // these vars are for clarity
+ var sizes = tile_dfn.getTileDimensions(),
+ n = this._tileDfn.length;
+
+ // store values so that we do not have to make additional calls to our
+ // TileDfn instance
+ this._tileWidth = sizes[ 0 ];
+ this._tileHeight = sizes[ 1 ];
+ this._tilesPerRow = sizes[ 2 ];
+
+ // calculate full width and height of tile set
+ this._setWidth = ( this._tileWidth * this._tilesPerRow );
+ this._setHeight = (
+ Math.ceil( n / this._tilesPerRow ) * this._tileHeight
+ );
+ },
+
+
/**
* Retrieve image data for each individual tile (pre-masked)
*
@@ -221,17 +232,15 @@ ltjs.TileMasker = Class( 'TileMasker',
*/
'virtual protected getMaskedTileSet': function( data_mask, callback )
{
- var tdata = this.__self.$( '_TDATA' ),
+ var tdata = this._tileDfn,
tiles = {},
i = -1,
len = tdata.length,
- // get tile width and height in pixels, and the number of tiles in
- // each row (xn)
- sizes = this.__self.$( '_TSIZE' ),
- tw = sizes[ 0 ],
- th = sizes[ 1 ],
- xn = ( sizes[ 2 ] / tw );
+ // shorten the names
+ tw = this._tileWidth,
+ th = this._tileHeight,
+ xn = this._tilesPerRow;
// create each tile (preserving order, thus no decrementing)
while ( ++i < len )
@@ -248,7 +257,7 @@ ltjs.TileMasker = Class( 'TileMasker',
// to the tile
tiles[ name ] = ( mask === 1 )
? this.getMaskedTileData( data_mask, x, y )
- : this.getTileData( x, y);
+ : this.getTileData( x, y );
}
callback( tiles );
@@ -330,8 +339,9 @@ ltjs.TileMasker = Class( 'TileMasker',
**/
'protected getTileData': function( x, y )
{
- var sizes = this.__self.$( '_TSIZE' );
- return this._context.getImageData( x, y, sizes[ 0 ], sizes[ 1 ] );
+ return this._context.getImageData(
+ x, y, this._tileWidth, this._tileHeight
+ );
},
@@ -377,10 +387,12 @@ ltjs.TileMasker = Class( 'TileMasker',
this._renderImage( bmp, function()
{
- var size = _self.__self.$( '_TSIZE' ),
- data = _self._context.getImageData( 0, 0, size[ 2 ], size[ 3 ] );
+ callback(
+ _self._context.getImageData(
+ 0, 0, _self._setWidth, _self._setHeight
+ )
+ );
- callback( data );
} );
}
} );
diff --git a/test/ltgloader-demo.html b/test/ltgloader-demo.html
index ab64ec3..3ab7e6a 100644
--- a/test/ltgloader-demo.html
+++ b/test/ltgloader-demo.html
@@ -52,7 +52,8 @@
+
+