diff --git a/lib/ClassicMap.js b/lib/ClassicMap.js index 0a3221c..8f12f7d 100644 --- a/lib/ClassicMap.js +++ b/lib/ClassicMap.js @@ -220,5 +220,16 @@ ltjs.ClassicMap = Class( 'ClassicMap' ) 'public isObjectTunnel': function( oid ) { return ( oid & this.__self.$( '_TMASK' ) ); + }, + + + /** + * Retrieve size of map in bytes + * + * @return {number} size of map in bytes + */ + 'public static getMapSize': function() + { + return this.$( '_SIZE' ); } } ); diff --git a/lib/Map.js b/lib/Map.js index 789ecfd..385b3e0 100644 --- a/lib/Map.js +++ b/lib/Map.js @@ -100,5 +100,13 @@ ltjs.Map = Interface( 'Map', * * @return {boolean} true if tunnel, otherwise false */ - 'public isObjectTunnel': [ 'object_id' ] + 'public isObjectTunnel': [ 'object_id' ], + + + /** + * Retrieve size of map in bytes + * + * @return {number} size of map in bytes + */ + 'public static getMapSize': [] } ); diff --git a/lib/MapRender.js b/lib/MapRender.js index 39b9cc9..43f18b4 100644 --- a/lib/MapRender.js +++ b/lib/MapRender.js @@ -191,6 +191,8 @@ ltjs.MapRender = Class( 'MapRender', w = t.width, h = t.height; + this._clearCanvases(); + // render each object (remember, we're dealing with columns, not rows; // see Map.getObjects()) while ( i-- ) @@ -224,6 +226,40 @@ ltjs.MapRender = Class( 'MapRender', }, + /** + * Clears overlay canvas + * + * This should be used before first rendering a map to ensure that any + * artifacts from previous map renderings will be erased. + * + * We need only clear the overlay canvas, because the lower canvas will + * always be overwritten with tiles in every location. Because none of the + * tiles written to the lower canvas are masked, nothing from the previous + * render would ever peek through (of course, putImageData() would overwrite + * it even if that were the case). As such, clearing the lower canvas would + * simply be a waste of time and only serve to degrade performance + * (especially if this is being used with maps larger than the classic + * 16x16). + * + * @return {undefined} + */ + 'private _clearCanvases': function() + { + var ctx = this._ctxObj, + c = ctx.canvas; + + // we need only clear the overlay (to which masked tiles are rendered) + ctx.clearRect( 0, 0, c.width, c.height ); + }, + + + /** + * Determine if the provided tile can be animated + * + * This simply checks to see if the tile has more than one frame. + * + * @return {boolean} true if multiple frames, otherwise false + */ 'private _canAnimate': function( tid ) { var tdata = this._tiles[ tid ]; diff --git a/lib/MapSet.js b/lib/MapSet.js index da05f88..d0e2a6d 100644 --- a/lib/MapSet.js +++ b/lib/MapSet.js @@ -37,6 +37,18 @@ ltjs.MapSet = Class( 'MapSet', */ 'private _data': '', + /** + * Constructor used to create new map instances + * @type {Function} + */ + 'private _mapCtor': null, + + /** + * Number of maps in the given LVL data + * @type {number} + */ + 'private _mapCount': 0, + /** * Initialize map set with LVL data and a Map constructor @@ -50,6 +62,37 @@ ltjs.MapSet = Class( 'MapSet', { this._data = ''+( data ); this._mapCtor = map_ctor; + + // perform a simple integrity check on the provided data + this._mapDataCheck(); + }, + + + /** + * Perform a simple map data integrity check + * + * This is intended to throw an error if we believe the LVL data to be + * invalid, or if the LVL data is invalid for the given Map constructor. + * The check will simply ensure that the map size (in bytes) divides into + * the total LVL size (in bytes) without any remainder. + * + * This is by no means fool-proof, but it should catch most. + * + * @return {undefined} + */ + 'private _mapDataCheck': function() + { + var n = ( this._data.length / this._mapCtor.getMapSize() ); + + // if the result is not an integer, then it is either not an LVL, the + // file is corrupt, or we were given the wrong Map constructor + if ( n % 1 ) + { + throw Error( 'Invalid or corrupt LVL data' ); + } + + // we already calculated it, so we may as well store it + this._mapCount = n; }, @@ -61,5 +104,16 @@ ltjs.MapSet = Class( 'MapSet', 'public getMapByNumber': function( id ) { return this._mapCtor( this._data, id ); - } + }, + + + /** + * Retrieve the number of maps in the LVL file + * + * @return {number} number of maps + */ + 'public getMapCount': function() + { + return this._mapCount; + }, } ); diff --git a/test/ltgloader-demo.html b/test/ltgloader-demo.html index 2be432c..3481824 100644 --- a/test/ltgloader-demo.html +++ b/test/ltgloader-demo.html @@ -38,6 +38,7 @@ +
Name
@@ -197,6 +198,26 @@ render = ltjs.MapRender( ctxmap, tile_set ); render.render( map_set.getMapByNumber( 1 ) ); + + var lvlsel = document.getElementById( 'lvl_id' ), + count = map_set.getMapCount(); + + lvlsel.innerHTML = ''; + for ( var i = 1; i <= count; i++ ) + { + var option = document.createElement( 'option' ); + option.value = i; + option.innerHTML = i; + + lvlsel.appendChild( option ); + } + + lvlsel.onchange = function( event ) + { + render.render( + map_set.getMapByNumber( event.target.selectedIndex + 1 ) + ); + }; }; reader.readAsBinaryString( file );