1
0
Fork 0

Abstracted tile definition

master
Mike Gerwitz 2012-03-12 21:14:39 -04:00
parent 3ed7959434
commit 1364c08cf4
4 changed files with 369 additions and 92 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* 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.<Array.<string,number>>} 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.<number>} tile width, tile height, tiles per row
*/
'public getTileDimensions': function()
{
return [ 32, 32, 10 ];
}
} );

137
lib/TileDfn.js 100644
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*
* 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.<Array.<string,number>>} 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.<number>} tile width, tile height, tiles per row
*/
'public getTileDimensions': []
} );

View File

@ -68,102 +68,113 @@
*/ */
ltjs.TileMasker = Class( 'TileMasker', 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) * Canvas 2D context (used for masking and tile slicing)
* @type {CanvasRenderingContext2d} * @type {CanvasRenderingContext2d}
*/ */
'private _context': null, 'private _context': null,
/**
* Tile definition to use for all operations
* @type {Array.<Array.<string,number>>}
*/
'private _tileDfn': null,
/** /**
* Initialize loader with a 2D canvas context * Width of each individual tile
* * @type {number}
* The context will be used for masking the game bitmap and slicing the
* tiles.
*/ */
__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 // 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 // memory to perform our operations (it will not be added to the DOM, so
// these operations will not be visible to the user) // these operations will not be visible to the user)
var context = document.createElement( 'canvas' ).getContext( '2d' ), var context = document.createElement( 'canvas' ).getContext( '2d' );
sizes = this.__self.$( '_TSIZE' );
// size the canvas so that it can fit the entire tileset // size the canvas so that it can fit the entire tileset
context.canvas.width = sizes[ 2 ]; context.canvas.width = this._setWidth;
context.canvas.height = sizes[ 3 ]; context.canvas.height = this._setHeight;
this._context = context; 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) * Retrieve image data for each individual tile (pre-masked)
* *
@ -221,17 +232,15 @@ ltjs.TileMasker = Class( 'TileMasker',
*/ */
'virtual protected getMaskedTileSet': function( data_mask, callback ) 'virtual protected getMaskedTileSet': function( data_mask, callback )
{ {
var tdata = this.__self.$( '_TDATA' ), var tdata = this._tileDfn,
tiles = {}, tiles = {},
i = -1, i = -1,
len = tdata.length, len = tdata.length,
// get tile width and height in pixels, and the number of tiles in // shorten the names
// each row (xn) tw = this._tileWidth,
sizes = this.__self.$( '_TSIZE' ), th = this._tileHeight,
tw = sizes[ 0 ], xn = this._tilesPerRow;
th = sizes[ 1 ],
xn = ( sizes[ 2 ] / tw );
// create each tile (preserving order, thus no decrementing) // create each tile (preserving order, thus no decrementing)
while ( ++i < len ) while ( ++i < len )
@ -330,8 +339,9 @@ ltjs.TileMasker = Class( 'TileMasker',
**/ **/
'protected getTileData': function( x, y ) 'protected getTileData': function( x, y )
{ {
var sizes = this.__self.$( '_TSIZE' ); return this._context.getImageData(
return this._context.getImageData( x, y, sizes[ 0 ], sizes[ 1 ] ); x, y, this._tileWidth, this._tileHeight
);
}, },
@ -377,10 +387,12 @@ ltjs.TileMasker = Class( 'TileMasker',
this._renderImage( bmp, function() this._renderImage( bmp, function()
{ {
var size = _self.__self.$( '_TSIZE' ), callback(
data = _self._context.getImageData( 0, 0, size[ 2 ], size[ 3 ] ); _self._context.getImageData(
0, 0, _self._setWidth, _self._setHeight
)
);
callback( data );
} ); } );
} }
} ); } );

View File

@ -53,6 +53,7 @@
<script> <script>
window.ltjs = {}; window.ltjs = {};
window.Class = easejs.Class; window.Class = easejs.Class;
window.Interface = easejs.Interface;
var ctx = document.getElementById( 'canvas' ).getContext( '2d' ), var ctx = document.getElementById( 'canvas' ).getContext( '2d' ),
ltg = document.getElementById( 'ltg' ); ltg = document.getElementById( 'ltg' );
@ -70,8 +71,8 @@
reader.onload = function( event ) reader.onload = function( event )
{ {
var loader = ltjs.LtgLoader( ctx ), var loader = ltjs.LtgLoader(),
masker = ltjs.TileMasker( ctx ), masker = ltjs.TileMasker( ltjs.ClassicTileDfn() ),
meta = loader.fromString( event.target.result ), meta = loader.fromString( event.target.result ),
bmp_game = document.getElementById( 'bmp_game' ), bmp_game = document.getElementById( 'bmp_game' ),
@ -124,6 +125,8 @@
// invoke immediately, as we may have a filename prefilled from a refresh // invoke immediately, as we may have a filename prefilled from a refresh
fileChange(); fileChange();
</script> </script>
<script src="../lib/TileDfn.js"></script>
<script src="../lib/ClassicTileDfn.js"></script>
<script src="../lib/LtgLoader.js"></script> <script src="../lib/LtgLoader.js"></script>
<script src="../lib/TileMasker.js"></script> <script src="../lib/TileMasker.js"></script>
</body> </body>