jshint integration and error fixes
commit
3c2790791d
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"eqeqeq": true,
|
||||||
|
"esnext": true,
|
||||||
|
"forin": true,
|
||||||
|
"freeze": true,
|
||||||
|
"futurehostile": true,
|
||||||
|
"latedef": true,
|
||||||
|
"laxbreak": true,
|
||||||
|
"maxcomplexity": 100,
|
||||||
|
"maxdepth": 3,
|
||||||
|
"maxparams": 5,
|
||||||
|
"noarg": true,
|
||||||
|
"nocomma": true,
|
||||||
|
"node": true,
|
||||||
|
"nonbsp": true,
|
||||||
|
"nonew": true,
|
||||||
|
"undef": true,
|
||||||
|
"unused": true,
|
||||||
|
"varstmt": true
|
||||||
|
}
|
|
@ -31,10 +31,16 @@ modindex: $(nsindex)
|
||||||
|
|
||||||
standalone: lasertank.js
|
standalone: lasertank.js
|
||||||
lasertank.js: modindex
|
lasertank.js: modindex
|
||||||
./node_modules/.bin/browserify -s lasertank --debug src/index.js > "$@"
|
./node_modules/.bin/browserify \
|
||||||
|
-t strictify \
|
||||||
|
-s lasertank \
|
||||||
|
--debug \
|
||||||
|
src/index.js \
|
||||||
|
> "$@"
|
||||||
|
|
||||||
test: check
|
test: check
|
||||||
check:
|
check:
|
||||||
|
jshint src/ scripts/
|
||||||
@PATH="$(PATH):$(CURDIR)/node_modules/mocha/bin" \
|
@PATH="$(PATH):$(CURDIR)/node_modules/mocha/bin" \
|
||||||
mocha --recursive $(TESTARGS)
|
mocha --recursive $(TESTARGS)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": ">=1.9.1",
|
"chai": ">=1.9.1",
|
||||||
"mocha": ">=1.18.2",
|
"mocha": ">=1.18.2",
|
||||||
"browserify": "~12"
|
"browserify": "~12",
|
||||||
|
"strictify": "~0.2"
|
||||||
},
|
},
|
||||||
|
|
||||||
"licenses": [
|
"licenses": [
|
||||||
|
|
|
@ -17,21 +17,30 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
( function()
|
( function()
|
||||||
{
|
{
|
||||||
|
/* jshint browser:true */
|
||||||
|
/* global lasertank */
|
||||||
|
|
||||||
var load_ltg = lasertank.FileLoader( document.getElementById( 'ltg' ) ),
|
const ltg_input = document.getElementById( 'ltg' ),
|
||||||
load_lvl = lasertank.FileLoader( document.getElementById( 'lvl' ) ),
|
lvl_input = document.getElementById( 'lvl' ),
|
||||||
|
|
||||||
menu_bar = lasertank.ui.MenuBar( document.getElementById( 'menubar' ) ),
|
load_ltg = lasertank.FileLoader( ltg_input, new window.FileReader() ),
|
||||||
|
load_lvl = lasertank.FileLoader( lvl_input, new window.FileReader() ),
|
||||||
|
|
||||||
ele_game = document.getElementById( 'game' ),
|
ele_game = document.getElementById( 'game' ),
|
||||||
ctx = document.getElementById( 'render' ).getContext( '2d' ),
|
ctx = document.getElementById( 'render' ).getContext( '2d' );
|
||||||
|
|
||||||
ltg_data = '',
|
let ltg_data = '',
|
||||||
lvl_data = '';
|
lvl_data = '';
|
||||||
|
|
||||||
|
|
||||||
|
// XXX: relies on side-effects of ctor
|
||||||
|
lasertank.ui.MenuBar( document.getElementById( 'menubar' ) );
|
||||||
|
|
||||||
|
|
||||||
load_ltg.onLoad( function( e, data )
|
load_ltg.onLoad( function( e, data )
|
||||||
{
|
{
|
||||||
if ( e ) throw e;
|
if ( e ) throw e;
|
||||||
|
@ -40,6 +49,7 @@ load_ltg.onLoad( function( e, data )
|
||||||
gamechk();
|
gamechk();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
load_lvl.onLoad( function( e, data )
|
load_lvl.onLoad( function( e, data )
|
||||||
{
|
{
|
||||||
if ( e ) throw e;
|
if ( e ) throw e;
|
||||||
|
@ -48,6 +58,7 @@ load_lvl.onLoad( function( e, data )
|
||||||
gamechk();
|
gamechk();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
||||||
function gamechk()
|
function gamechk()
|
||||||
{
|
{
|
||||||
if ( !( ltg_data && lvl_data ) ) return;
|
if ( !( ltg_data && lvl_data ) ) return;
|
||||||
|
@ -55,13 +66,14 @@ function gamechk()
|
||||||
// temporary
|
// temporary
|
||||||
if ( ele_game.className.search( 'opening' ) > -1 ) return;
|
if ( ele_game.className.search( 'opening' ) > -1 ) return;
|
||||||
|
|
||||||
lasertank.ClassicGame( ltg_data, lvl_data )
|
lasertank.ClassicGame( document, ltg_data, lvl_data )
|
||||||
.on( 'ready', function()
|
.on( 'ready', function()
|
||||||
{
|
{
|
||||||
this.renderTo( ctx );
|
this.renderTo( ctx, window );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// temporary
|
// temporary
|
||||||
document.getElementById( 'new' ).onclick = function()
|
document.getElementById( 'new' ).onclick = function()
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,5 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* jshint browser:true */
|
||||||
|
|
||||||
// alert the user on all uncaught errors
|
// alert the user on all uncaught errors
|
||||||
window.onerror = alert;
|
window.onerror = window.alert;
|
||||||
|
|
|
@ -18,17 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
Game = require( './Game' ),
|
Game = require( './Game' ),
|
||||||
ClassicGameObjectFactory = require( './ClassicGameObjectFactory' ),
|
ClassicGameObjectFactory = require( './ClassicGameObjectFactory' ),
|
||||||
ClassicMap = require( './ClassicMap' ),
|
ClassicTileDfn = require( './ClassicTileDfn' ),
|
||||||
ClassicTileDfn = require( './ClassicTileDfn' ),
|
LtgLoader = require( './LtgLoader' ),
|
||||||
LtgLoader = require( './LtgLoader' ),
|
ClassicLevel = require( './level/ClassicLevel' ),
|
||||||
MapBounds = require( './MapBounds' ),
|
LevelBounds = require( './level/LevelBounds' ),
|
||||||
MapRender = require( './MapRender' ),
|
LevelRender = require( './level/LevelRender' ),
|
||||||
MapSet = require( './MapSet' ),
|
LevelSet = require( './level/LevelSet' ),
|
||||||
MapState = require( './MapState' ),
|
LevelState = require( './level/LevelState' ),
|
||||||
TileMasker = require( './TileMasker' );
|
TileMasker = require( './TileMasker' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Facade for the classic (original) game of LaserTank
|
* Facade for the classic (original) game of LaserTank
|
||||||
|
@ -56,10 +57,10 @@ module.exports = Class( 'ClassicGame' )
|
||||||
'private _tileSet': null,
|
'private _tileSet': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of available maps
|
* Set of available levels
|
||||||
* @type {MapSet}
|
* @type {LevelSet}
|
||||||
*/
|
*/
|
||||||
'private _mapSet': null,
|
'private _levelSet': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handlers
|
* Event handlers
|
||||||
|
@ -68,8 +69,8 @@ module.exports = Class( 'ClassicGame' )
|
||||||
'private _callback': {},
|
'private _callback': {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs map rendering
|
* Performs level rendering
|
||||||
* @type {MapRender}
|
* @type {LevelRender}
|
||||||
*/
|
*/
|
||||||
'private _render': null,
|
'private _render': null,
|
||||||
|
|
||||||
|
@ -83,26 +84,31 @@ module.exports = Class( 'ClassicGame' )
|
||||||
/**
|
/**
|
||||||
* Initialize game with LTG and LVL data
|
* Initialize game with LTG and LVL data
|
||||||
*
|
*
|
||||||
* The LTG and LVL data can be changed at any time, but are required in the
|
* The LTG and LVL data can be changed at any time, but are required in
|
||||||
* constructor because they are needed in order for the game to be
|
* the constructor because they are needed in order for the game to be
|
||||||
* functional.
|
* functional.
|
||||||
*
|
*
|
||||||
|
* DOCUMENT is used internally for creating elements; the DOM will not
|
||||||
|
* be manipulated.
|
||||||
|
*
|
||||||
|
* @param {HTMLDocument} document DOM document
|
||||||
|
*
|
||||||
* @param {string} ltg_data binary string containing LTG file data
|
* @param {string} ltg_data binary string containing LTG file data
|
||||||
* @param {string} lvl_data binary string containing LVL file data
|
* @param {string} lvl_data binary string containing LVL file data
|
||||||
*/
|
*/
|
||||||
__construct: function( ltg_data, lvl_data )
|
__construct: function( document, ltg_data, lvl_data )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
this._ltgLoader = LtgLoader();
|
this._ltgLoader = LtgLoader();
|
||||||
this._masker = TileMasker( ClassicTileDfn() );
|
this._masker = TileMasker( ClassicTileDfn(), document );
|
||||||
|
|
||||||
this._gameObjFactory = ClassicGameObjectFactory();
|
this._gameObjFactory = ClassicGameObjectFactory();
|
||||||
|
|
||||||
// load initial tile and map data from the LTG and LVL data
|
// load initial tile and level data from the LTG and LVL data
|
||||||
this.setTileData( ltg_data, function()
|
this.setTileData( ltg_data, function()
|
||||||
{
|
{
|
||||||
this.setMapData( lvl_data, function()
|
this.setLevelData( lvl_data, function()
|
||||||
{
|
{
|
||||||
_self._trigger( 'ready' );
|
_self._trigger( 'ready' );
|
||||||
} );
|
} );
|
||||||
|
@ -113,32 +119,35 @@ module.exports = Class( 'ClassicGame' )
|
||||||
/**
|
/**
|
||||||
* Render to the given 2d canvas context
|
* Render to the given 2d canvas context
|
||||||
*
|
*
|
||||||
* @param {CanvasRenderingContext2d} ctx 2d canvas context
|
* EVENT_TARGET will be monitored for keypresses.
|
||||||
|
*
|
||||||
|
* @param {CanvasRenderingContext2d} ctx 2d canvas context
|
||||||
|
* @param {*} event_target keyboard event target
|
||||||
*
|
*
|
||||||
* @return {ClassicGame} self
|
* @return {ClassicGame} self
|
||||||
*/
|
*/
|
||||||
'public renderTo': function( ctx )
|
'public renderTo': function( ctx, event_target )
|
||||||
{
|
{
|
||||||
// if there is a previous renderer, free its canvas before continuing
|
// if there is a previous renderer, free its canvas before
|
||||||
// (to both clean up and to free any locks, allowing for tile set and
|
// continuing (to both clean up and to free any locks, allowing for
|
||||||
// map changes)
|
// tile set and level changes)
|
||||||
if ( this._render )
|
if ( this._render )
|
||||||
{
|
{
|
||||||
this._render.clearCanvas();
|
this._render.clearCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
var map = this._mapSet.getMapByNumber( 1 ),
|
const level = this._levelSet.getLevelByNumber( 1 ),
|
||||||
map_state = MapState( map, this._gameObjFactory ),
|
level_state = LevelState( level, this._gameObjFactory ),
|
||||||
bounds = MapBounds( map );
|
bounds = LevelBounds( level );
|
||||||
|
|
||||||
// render the first map (hardcoded for now)
|
// render the first level (hardcoded for now)
|
||||||
this._render = MapRender( ctx, this._tileSet )
|
this._render = LevelRender( ctx, this._tileSet )
|
||||||
.render( map, map_state );
|
.render( level, level_state );
|
||||||
|
|
||||||
// POC
|
// POC
|
||||||
window.onkeydown = function( event )
|
event_target.onkeydown = function( event )
|
||||||
{
|
{
|
||||||
var dir;
|
let dir;
|
||||||
|
|
||||||
switch ( event.keyCode )
|
switch ( event.keyCode )
|
||||||
{
|
{
|
||||||
|
@ -153,7 +162,8 @@ module.exports = Class( 'ClassicGame' )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
map_state.movePlayer( dir, bounds );
|
event.preventDefault();
|
||||||
|
level_state.movePlayer( dir, bounds );
|
||||||
};
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -171,8 +181,8 @@ module.exports = Class( 'ClassicGame' )
|
||||||
'public setTileData': function( data, callback )
|
'public setTileData': function( data, callback )
|
||||||
{
|
{
|
||||||
// get tile metadata
|
// get tile metadata
|
||||||
var _self = this,
|
const _self = this,
|
||||||
meta = this._ltgLoader.fromString( data );
|
meta = this._ltgLoader.fromString( data );
|
||||||
|
|
||||||
this._masker.getMaskedTiles( meta.tiles, meta.mask, function( tdata )
|
this._masker.getMaskedTiles( meta.tiles, meta.mask, function( tdata )
|
||||||
{
|
{
|
||||||
|
@ -185,16 +195,16 @@ module.exports = Class( 'ClassicGame' )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set LVL data for maps
|
* Set LVL data for levels
|
||||||
*
|
*
|
||||||
* @param {string} data binary string containing LVL data
|
* @param {string} data binary string containing LVL data
|
||||||
* @param {function()} callback function to call when complete
|
* @param {function()} callback function to call when complete
|
||||||
*
|
*
|
||||||
* @return {ClassicGame} self
|
* @return {ClassicGame} self
|
||||||
*/
|
*/
|
||||||
'public setMapData': function( data, callback )
|
'public setLevelData': function( data, callback )
|
||||||
{
|
{
|
||||||
this._mapSet = MapSet( data, ClassicMap );
|
this._levelSet = LevelSet( data, ClassicLevel );
|
||||||
|
|
||||||
callback.call( this.__inst );
|
callback.call( this.__inst );
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
GameObjectFactory = require( './GameObjectFactory' ),
|
GameObjectFactory = require( './GameObjectFactory' ),
|
||||||
GameObject = require( './gameobjs/GameObject' ),
|
GameObject = require( './gameobjs/GameObject' ),
|
||||||
Tank = require( './gameobjs/Tank' );
|
Tank = require( './gameobjs/Tank' );
|
||||||
|
|
||||||
|
|
||||||
module.exports = Class( 'ClassicGameObjectFactory' )
|
module.exports = Class( 'ClassicGameObjectFactory' )
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
TileDfn = require( './TileDfn' );
|
TileDfn = require( './TileDfn' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
* specs, as it depends on FileReader.
|
* specs, as it depends on FileReader.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,8 +49,9 @@ module.exports = Class( 'FileLoader',
|
||||||
* Initialize file loader, monitoring the given file element
|
* Initialize file loader, monitoring the given file element
|
||||||
*
|
*
|
||||||
* @param {HtmlInputElement} element file element to monitor
|
* @param {HtmlInputElement} element file element to monitor
|
||||||
|
* @param {FileReader} reader file reader
|
||||||
*/
|
*/
|
||||||
__construct: function( element )
|
__construct: function( element, reader )
|
||||||
{
|
{
|
||||||
if ( element.type !== 'file' )
|
if ( element.type !== 'file' )
|
||||||
{
|
{
|
||||||
|
@ -58,6 +59,7 @@ module.exports = Class( 'FileLoader',
|
||||||
}
|
}
|
||||||
|
|
||||||
this._element = element;
|
this._element = element;
|
||||||
|
this._reader = reader;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,23 +102,23 @@ module.exports = Class( 'FileLoader',
|
||||||
*/
|
*/
|
||||||
'private _loadFile': function( event )
|
'private _loadFile': function( event )
|
||||||
{
|
{
|
||||||
var _self = this,
|
const _self = this,
|
||||||
files = event.target.files;
|
files = event.target.files;
|
||||||
|
|
||||||
if ( files.length === 0 ) return;
|
if ( files.length === 0 ) return;
|
||||||
|
|
||||||
var reader = new FileReader();
|
this._reader.onload = function( revent )
|
||||||
reader.onload = function( revent )
|
|
||||||
{
|
{
|
||||||
_self._callback.call( this.__inst, null, revent.target.result );
|
_self._callback.call( this.__inst, null, revent.target.result );
|
||||||
};
|
};
|
||||||
reader.onerror = function( e )
|
|
||||||
|
this._reader.onerror = function( e )
|
||||||
{
|
{
|
||||||
_self._callback.call( this.__inst, e );
|
_self._callback.call( this.__inst, e );
|
||||||
};
|
};
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
reader.readAsBinaryString( files[ 0 ] );
|
this._reader.readAsBinaryString( files[ 0 ] );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Interface = require( 'easejs' ).Interface;
|
const Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,14 +50,14 @@ module.exports = Interface( 'Game',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set LVL data for maps
|
* Set LVL data for levels
|
||||||
*
|
*
|
||||||
* @param {string} data binary string containing LVL data
|
* @param {string} data binary string containing LVL data
|
||||||
* @param {function()} callback function to call when complete
|
* @param {function()} callback function to call when complete
|
||||||
*
|
*
|
||||||
* @return {Game} self
|
* @return {Game} self
|
||||||
*/
|
*/
|
||||||
'public setMapData': [ 'data', 'callback' ],
|
'public setLevelData': [ 'data', 'callback' ],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Interface = require( 'easejs' ).Interface;
|
const Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
module.exports = Interface( 'GameObjectFactory',
|
module.exports = Interface( 'GameObjectFactory',
|
||||||
|
|
|
@ -42,7 +42,10 @@
|
||||||
* identify the file as a bitmap image.)
|
* identify the file as a bitmap image.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
/* XXX: remove me! */
|
||||||
|
/* globals btoa */
|
||||||
|
|
||||||
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +76,7 @@ module.exports = Class( 'LtgLoader',
|
||||||
*/
|
*/
|
||||||
'public fromString': function( ltg_data )
|
'public fromString': function( ltg_data )
|
||||||
{
|
{
|
||||||
var mask_offset = this._getMaskOffsetFromData( ltg_data );
|
const mask_offset = this._getMaskOffsetFromData( ltg_data );
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this._getNameFromData( ltg_data ),
|
name: this._getNameFromData( ltg_data ),
|
||||||
|
@ -112,7 +115,7 @@ module.exports = Class( 'LtgLoader',
|
||||||
sgmt = this.__self.$( sgmt );
|
sgmt = this.__self.$( sgmt );
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = String.prototype.substr.apply( ltg_data, sgmt );
|
const data = String.prototype.substr.apply( ltg_data, sgmt );
|
||||||
|
|
||||||
return ( stripnull )
|
return ( stripnull )
|
||||||
? data.split( '\x00' )[ 0 ]
|
? data.split( '\x00' )[ 0 ]
|
||||||
|
@ -187,8 +190,9 @@ module.exports = Class( 'LtgLoader',
|
||||||
// grab the data and don't bother stripping off the null bytes (it would
|
// grab the data and don't bother stripping off the null bytes (it would
|
||||||
// function the same with them stripped, but let's avoid the confusion
|
// function the same with them stripped, but let's avoid the confusion
|
||||||
// since we are supposed to be working with a 32-bit value)
|
// since we are supposed to be working with a 32-bit value)
|
||||||
var data = this._getDataSegment( ltg_data, '_POS_MOFF', false ),
|
const data = this._getDataSegment( ltg_data, '_POS_MOFF', false );
|
||||||
i = data.length,
|
|
||||||
|
let i = data.length,
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
// convert the DWORD entry (little-endian format, 32-bit) into an
|
// convert the DWORD entry (little-endian format, 32-bit) into an
|
||||||
|
@ -234,7 +238,7 @@ module.exports = Class( 'LtgLoader',
|
||||||
*/
|
*/
|
||||||
'private _getGameBitmap': function( ltg_data, mask_offset )
|
'private _getGameBitmap': function( ltg_data, mask_offset )
|
||||||
{
|
{
|
||||||
var bmp_offset = this.__self.$( '_OFFSET_HEADER_END' );
|
const bmp_offset = this.__self.$( '_OFFSET_HEADER_END' );
|
||||||
|
|
||||||
// return the bitmap data up until the mask offset
|
// return the bitmap data up until the mask offset
|
||||||
return ltg_data.substr( bmp_offset, ( mask_offset - bmp_offset ) );
|
return ltg_data.substr( bmp_offset, ( mask_offset - bmp_offset ) );
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
* thinice - thin ice
|
* thinice - thin ice
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Interface = require( 'easejs' ).Interface;
|
const Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -63,8 +63,8 @@
|
||||||
* rendering.
|
* rendering.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
TileDfn = require( './TileDfn' );
|
TileDfn = require( './TileDfn' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slices tiles and applies masks
|
* Slices tiles and applies masks
|
||||||
|
@ -77,6 +77,12 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'private _context': null,
|
'private _context': null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOM document
|
||||||
|
* @type {HTMLDocument}
|
||||||
|
*/
|
||||||
|
'private _document': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tile definition to use for all operations
|
* Tile definition to use for all operations
|
||||||
* @type {Array.<Array.<string,number>>}
|
* @type {Array.<Array.<string,number>>}
|
||||||
|
@ -121,9 +127,13 @@ module.exports = Class( 'TileMasker',
|
||||||
* allows us to support *any* type of tile set -- not just those that are
|
* allows us to support *any* type of tile set -- not just those that are
|
||||||
* defined by the original game.
|
* defined by the original game.
|
||||||
*
|
*
|
||||||
* @param {TileDfn} tile_dfn tile definition object
|
* DOCUMENT is used internally for creating elements; the DOM will not
|
||||||
|
* be manipulated.
|
||||||
|
*
|
||||||
|
* @param {TileDfn} tile_dfn tile definition object
|
||||||
|
* @param {HTMLDocument} document DOM document
|
||||||
*/
|
*/
|
||||||
__construct: function( tile_dfn )
|
__construct: function( tile_dfn, document )
|
||||||
{
|
{
|
||||||
if ( !( Class.isA( TileDfn, tile_dfn ) ) )
|
if ( !( Class.isA( TileDfn, tile_dfn ) ) )
|
||||||
{
|
{
|
||||||
|
@ -137,13 +147,14 @@ module.exports = Class( 'TileMasker',
|
||||||
// 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' );
|
let context = document.createElement( 'canvas' ).getContext( '2d' );
|
||||||
|
|
||||||
// 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 = this._setWidth;
|
context.canvas.width = this._setWidth;
|
||||||
context.canvas.height = this._setHeight;
|
context.canvas.height = this._setHeight;
|
||||||
|
|
||||||
this._context = context;
|
this._context = context;
|
||||||
|
this._document = document;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,8 +172,8 @@ module.exports = Class( 'TileMasker',
|
||||||
'private _calcSetDimensions': function( tile_dfn )
|
'private _calcSetDimensions': function( tile_dfn )
|
||||||
{
|
{
|
||||||
// these vars are for clarity
|
// these vars are for clarity
|
||||||
var sizes = tile_dfn.getTileDimensions(),
|
const sizes = tile_dfn.getTileDimensions(),
|
||||||
n = this._tileDfn.length;
|
n = this._tileDfn.length;
|
||||||
|
|
||||||
// store values so that we do not have to make additional calls to our
|
// store values so that we do not have to make additional calls to our
|
||||||
// TileDfn instance
|
// TileDfn instance
|
||||||
|
@ -201,7 +212,7 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'public getMaskedTiles': function( bmp_game, bmp_mask, callback )
|
'public getMaskedTiles': function( bmp_game, bmp_mask, callback )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
this._getImageData( bmp_mask, function( data_mask )
|
this._getImageData( bmp_mask, function( data_mask )
|
||||||
{
|
{
|
||||||
|
@ -248,26 +259,27 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'virtual protected getMaskedTileSet': function( data_mask, callback )
|
'virtual protected getMaskedTileSet': function( data_mask, callback )
|
||||||
{
|
{
|
||||||
var tdata = this._tileDfn,
|
const tdata = this._tileDfn,
|
||||||
tiles = {},
|
len = tdata.length,
|
||||||
i = -1,
|
|
||||||
len = tdata.length,
|
|
||||||
|
|
||||||
// shorten the names
|
// shorten the names
|
||||||
tw = this._tileWidth,
|
tw = this._tileWidth,
|
||||||
th = this._tileHeight,
|
th = this._tileHeight,
|
||||||
xn = this._tilesPerRow;
|
xn = this._tilesPerRow;
|
||||||
|
|
||||||
|
let tiles = {},
|
||||||
|
i = -1;
|
||||||
|
|
||||||
// create each tile (preserving order, thus no decrementing)
|
// create each tile (preserving order, thus no decrementing)
|
||||||
while ( ++i < len )
|
while ( ++i < len )
|
||||||
{
|
{
|
||||||
var id = tdata[ i ][ 0 ],
|
const 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
|
||||||
// and bitmap dimensions
|
// and bitmap dimensions
|
||||||
x = ( ( i % xn ) * th ),
|
const x = ( ( i % xn ) * th ),
|
||||||
y = ( ( Math.floor( i / xn ) ) * tw );
|
y = ( ( Math.floor( i / xn ) ) * tw );
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -297,7 +309,7 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'protected appendTileFrame': function( set, id, mask, data )
|
'protected appendTileFrame': function( set, id, mask, data )
|
||||||
{
|
{
|
||||||
var prev = set[ id ];
|
const prev = set[ id ];
|
||||||
|
|
||||||
set[ id ] = {
|
set[ id ] = {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -314,7 +326,7 @@ module.exports = Class( 'TileMasker',
|
||||||
|
|
||||||
// if there was a previous entry, set its 'next' entry to our new frame,
|
// if there was a previous entry, set its 'next' entry to our new frame,
|
||||||
// expanding the linked list
|
// expanding the linked list
|
||||||
prev && ( prev.next = set[ id ] )
|
if ( prev ) prev.next = set[ id ];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -347,18 +359,19 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'virtual protected getMaskedTileData': function( data_mask, x, y )
|
'virtual protected getMaskedTileData': function( data_mask, x, y )
|
||||||
{
|
{
|
||||||
var raw = this.getTileData( x, y ),
|
const raw = this.getTileData( x, y ),
|
||||||
w = raw.width,
|
w = raw.width,
|
||||||
h = raw.height,
|
h = raw.height,
|
||||||
mw = data_mask.width,
|
mw = data_mask.width;
|
||||||
yi = h;
|
|
||||||
|
let yi = h;
|
||||||
|
|
||||||
// apply the mask to the raw tile data (simple and easy-to-understand
|
// apply the mask to the raw tile data (simple and easy-to-understand
|
||||||
// algorithm; we can refine it later if need be), looping through each
|
// algorithm; we can refine it later if need be), looping through each
|
||||||
// pixel
|
// pixel
|
||||||
while ( yi-- )
|
while ( yi-- )
|
||||||
{
|
{
|
||||||
xi = w;
|
let xi = w;
|
||||||
|
|
||||||
while ( xi-- )
|
while ( xi-- )
|
||||||
{
|
{
|
||||||
|
@ -366,8 +379,8 @@ module.exports = Class( 'TileMasker',
|
||||||
// (remember that, although we are dealing with applying the
|
// (remember that, although we are dealing with applying the
|
||||||
// mask to a single tile, the mask image contains all tiles, so
|
// mask to a single tile, the mask image contains all tiles, so
|
||||||
// we must calculate its position accordingly)
|
// we must calculate its position accordingly)
|
||||||
var mi = ( ( ( yi + y ) * ( mw * 4 ) ) + ( ( xi + x ) * 4 ) ),
|
const mi = ( ( ( yi + y ) * ( mw * 4 ) ) + ( ( xi + x ) * 4 ) ),
|
||||||
mr = data_mask.data[ mi ];
|
mr = data_mask.data[ mi ];
|
||||||
|
|
||||||
// manipulate the alpha channel of our tile; if the R value for
|
// manipulate the alpha channel of our tile; if the R value for
|
||||||
// the mask is not 0, then this pixel in our tile should be
|
// the mask is not 0, then this pixel in our tile should be
|
||||||
|
@ -414,8 +427,8 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'private _renderImage': function( bmp, callback )
|
'private _renderImage': function( bmp, callback )
|
||||||
{
|
{
|
||||||
var _self = this,
|
const _self = this,
|
||||||
img = new Image();
|
img = this._document.createElement( 'img' );
|
||||||
|
|
||||||
img.onload = function()
|
img.onload = function()
|
||||||
{
|
{
|
||||||
|
@ -437,7 +450,7 @@ module.exports = Class( 'TileMasker',
|
||||||
*/
|
*/
|
||||||
'private _getImageData': function( bmp, callback )
|
'private _getImageData': function( bmp, callback )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
this._renderImage( bmp, function()
|
this._renderImage( bmp, function()
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
module.exports = Class( 'GameObject',
|
module.exports = Class( 'GameObject',
|
||||||
|
@ -44,6 +44,7 @@ module.exports = Class( 'GameObject',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/* jshint -W098 */
|
||||||
'virtual public move': function( dir, c, sc )
|
'virtual public move': function( dir, c, sc )
|
||||||
{
|
{
|
||||||
// move in the appropriate direction (action has been pre-configured
|
// move in the appropriate direction (action has been pre-configured
|
||||||
|
@ -52,4 +53,5 @@ module.exports = Class( 'GameObject',
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/* jshint +W098 */
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
GameObject = require( './GameObject' );
|
GameObject = require( './GameObject' );
|
||||||
|
|
||||||
|
|
||||||
module.exports = Class( 'Tank' )
|
module.exports = Class( 'Tank' )
|
||||||
|
@ -26,12 +26,12 @@ module.exports = Class( 'Tank' )
|
||||||
{
|
{
|
||||||
'override public move': function( direction, c, sc )
|
'override public move': function( direction, c, sc )
|
||||||
{
|
{
|
||||||
var state = [ 'tleft', 'tup', 'tright', 'tdown' ][ direction ];
|
const state = [ 'tleft', 'tup', 'tright', 'tdown' ][ direction ];
|
||||||
|
|
||||||
if ( state !== this.getTid() )
|
if ( state !== this.getTid() )
|
||||||
{
|
{
|
||||||
sc( state );
|
sc( state );
|
||||||
return;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let parent handle the movement
|
// let parent handle the movement
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
Tank = require( '../Tank' );
|
Tank = require( '../Tank' );
|
||||||
|
|
||||||
|
|
||||||
module.exports = Class( 'TankDown' )
|
module.exports = Class( 'TankDown' )
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Represents a classic map (level)
|
* Represents a classic level
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012, 2015 Mike Gerwitz
|
* Copyright (C) 2012, 2015 Mike Gerwitz
|
||||||
*
|
*
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Each map is concatenated in the file and consists of the following
|
* Each level is concatenated in the file and consists of the following
|
||||||
* information:
|
* information:
|
||||||
*
|
*
|
||||||
* - Playfield data (game objects), 16x16 multidimensional char array
|
* - Playfield data (game objects), 16x16 multidimensional char array
|
||||||
|
@ -27,34 +27,34 @@
|
||||||
* - Difficulty, 16-bit integer, little endian
|
* - Difficulty, 16-bit integer, little endian
|
||||||
*
|
*
|
||||||
* One can easily calculate the position of the level in a file, given its
|
* One can easily calculate the position of the level in a file, given its
|
||||||
* number, by multiplying by the size of the data structure (see TLEVEL, LTANK.H
|
* number, by multiplying by the size of the data structure (see TLEVEL,
|
||||||
* in the original sources).
|
* LTANK.H in the original sources).
|
||||||
*
|
*
|
||||||
* It is worth mentioning how the playfield data is stored. Since the
|
* It is worth mentioning how the playfield data is stored. Since the
|
||||||
* multidimensional array is defined as [x][y], the array is created as an
|
* multidimensional array is defined as [x][y], the array is created as an
|
||||||
* "array of columns", meaning that the data is organized in columns instead of
|
* "array of columns", meaning that the data is organized in columns instead
|
||||||
* rows. For example, when viewing the data in a HEX editor that displays 16
|
* of rows. For example, when viewing the data in a HEX editor that displays
|
||||||
* bytes per line (e.g. xxd), the map would appear to be mirrored rotated 90
|
* 16 bytes per line (e.g. xxd), the level would appear to be mirrored
|
||||||
* degrees counter-clockwise. To make it easier to visualize, one can create a
|
* rotated 90 degrees counter-clockwise. To make it easier to visualize, one
|
||||||
* map with a number of tunnels in a pattern to take advantage of the ASCII
|
* can create a level with a number of tunnels in a pattern to take
|
||||||
* display.
|
* advantage of the ASCII display.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
Map = require( './Map' );
|
Level = require( './Level' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a classic map, as they exist in the original game.
|
* Represents a classic level, as they exist in the original game.
|
||||||
*
|
*
|
||||||
* Classic maps are 16x16 tiles in size (for a total of 256 tiles).
|
* Classic levels are 16x16 tiles in size (for a total of 256 tiles).
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'ClassicMap' )
|
module.exports = Class( 'ClassicLevel' )
|
||||||
.implement( Map )
|
.implement( Level )
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Size of each map in bytes
|
* Size of each level in bytes
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
'private const _SIZE': 576,
|
'private const _SIZE': 576,
|
||||||
|
@ -70,13 +70,13 @@ module.exports = Class( 'ClassicMap' )
|
||||||
'private const _GOSIZE': [ 0, 256 ],
|
'private const _GOSIZE': [ 0, 256 ],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset and length of map name
|
* Offset and length of level name
|
||||||
* @type {Array.<number>}
|
* @type {Array.<number>}
|
||||||
*/
|
*/
|
||||||
'private const _NAMESIZE': [ 256, 31 ],
|
'private const _NAMESIZE': [ 256, 31 ],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset and length of map hint
|
* Offset and length of level hint
|
||||||
* @type {Array.<number>}
|
* @type {Array.<number>}
|
||||||
*/
|
*/
|
||||||
'private const _HINTSIZE': [ 287, 256 ],
|
'private const _HINTSIZE': [ 287, 256 ],
|
||||||
|
@ -105,12 +105,13 @@ module.exports = Class( 'ClassicMap' )
|
||||||
/**
|
/**
|
||||||
* Colors used to identify tunnels
|
* Colors used to identify tunnels
|
||||||
*
|
*
|
||||||
* These colors will be rendered in the background and will bleed through
|
* These colors will be rendered in the background and will bleed
|
||||||
* the transparent portions of the tile. We use explicit HEX codes rather
|
* through the transparent portions of the tile. We use explicit HEX
|
||||||
* than CSS color names because environments may vary the colors used.
|
* codes rather than CSS color names because environments may vary the
|
||||||
|
* colors used.
|
||||||
*
|
*
|
||||||
* Taken from ColorList in LTANK2.C in the original sources. Note that there
|
* Taken from ColorList in LTANK2.C in the original sources. Note that
|
||||||
* is an endianness difference.
|
* there is an endianness difference.
|
||||||
*
|
*
|
||||||
* @type {Array.<string>}
|
* @type {Array.<string>}
|
||||||
*/
|
*/
|
||||||
|
@ -121,36 +122,36 @@ module.exports = Class( 'ClassicMap' )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map set data (binary string)
|
* Level set data (binary string)
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
'private _data': null,
|
'private _data': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map id (1-indexed)
|
* Level id (1-indexed)
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
'private _id': 0,
|
'private _id': 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offset of beginning of map data in bytes
|
* Offset of beginning of level data in bytes
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
'private _offset': 0,
|
'private _offset': 0,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize map with map data and the given id
|
* Initialize level with level data and the given id
|
||||||
*
|
*
|
||||||
* @param {MapSet} set map set data
|
* @param {LevelSet} set level set data
|
||||||
* @param {number} id 1-indexed map id
|
* @param {number} id 1-indexed level id
|
||||||
*/
|
*/
|
||||||
__construct: function( data, id )
|
__construct: function( data, id )
|
||||||
{
|
{
|
||||||
this._data = ''+( data );
|
this._data = ''+( data );
|
||||||
this._id = +id;
|
this._id = +id;
|
||||||
|
|
||||||
// calculate map offset in LVL data
|
// calculate level offset in LVL data
|
||||||
this._offset = ( this.__self.$( '_SIZE' ) * ( this._id - 1 ) );
|
this._offset = ( this.__self.$( '_SIZE' ) * ( this._id - 1 ) );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -158,20 +159,20 @@ module.exports = Class( 'ClassicMap' )
|
||||||
/**
|
/**
|
||||||
* Retrieve game objects
|
* Retrieve game objects
|
||||||
*
|
*
|
||||||
* The game objects are returned in a manner consistent with the original
|
* The game objects are returned in a manner consistent with the
|
||||||
* sources - in columns, not rows. The reason for this is that the original
|
* original sources - in columns, not rows. The reason for this is that
|
||||||
* game uses a multi-dimensional array [x][y], which creates an array of
|
* the original game uses a multi-dimensional array [x][y], which
|
||||||
* columns (TPLAYFIELD, LTANK.H).
|
* creates an array of columns (TPLAYFIELD, LTANK.H).
|
||||||
*
|
*
|
||||||
* The object data at the requested position will be loaded and converted to
|
* The object data at the requested position will be loaded and
|
||||||
* integers (from a binary string).
|
* converted to integers (from a binary string).
|
||||||
*
|
*
|
||||||
* @return {Array.<number>} array of game objects
|
* @return {Array.<number>} array of game objects
|
||||||
*/
|
*/
|
||||||
'public getObjects': function()
|
'public getObjects': function()
|
||||||
{
|
{
|
||||||
var tiles = this._getDataSegment( '_GOSIZE', false ).split( '' ),
|
const tiles = this._getDataSegment( '_GOSIZE', false ).split( '' );
|
||||||
i = tiles.length;
|
let i = tiles.length;
|
||||||
|
|
||||||
while ( i-- )
|
while ( i-- )
|
||||||
{
|
{
|
||||||
|
@ -192,8 +193,8 @@ module.exports = Class( 'ClassicMap' )
|
||||||
{
|
{
|
||||||
stripnull = ( arguments.length < 2 ) ? true : !!stripnull;
|
stripnull = ( arguments.length < 2 ) ? true : !!stripnull;
|
||||||
|
|
||||||
var s = this.__self.$( name ),
|
const s = this.__self.$( name ),
|
||||||
data = this._data.substr( ( this._offset + s[ 0 ] ), s[ 1 ] );
|
data = this._data.substr( ( this._offset + s[ 0 ] ), s[ 1 ] );
|
||||||
|
|
||||||
return ( stripnull )
|
return ( stripnull )
|
||||||
? data.split( '\0' )[ 0 ]
|
? data.split( '\0' )[ 0 ]
|
||||||
|
@ -202,7 +203,7 @@ module.exports = Class( 'ClassicMap' )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map dimensions
|
* Retrieve level dimensions
|
||||||
*
|
*
|
||||||
* @return {Array.<number>} width and height in tiles
|
* @return {Array.<number>} width and height in tiles
|
||||||
*/
|
*/
|
||||||
|
@ -213,11 +214,11 @@ module.exports = Class( 'ClassicMap' )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map of object codes to their appropriate tiles
|
* Retrieve level of object codes to their appropriate tiles
|
||||||
*
|
*
|
||||||
* @return {Array.<string>}
|
* @return {Array.<string>}
|
||||||
*/
|
*/
|
||||||
'public getObjectTileMap': function()
|
'public getObjectTileLevel': function()
|
||||||
{
|
{
|
||||||
// we return these values here instead of returning, say, a constant,
|
// we return these values here instead of returning, say, a constant,
|
||||||
// because we would have to clone it to ensure that our inner state
|
// because we would have to clone it to ensure that our inner state
|
||||||
|
@ -235,9 +236,10 @@ module.exports = Class( 'ClassicMap' )
|
||||||
/**
|
/**
|
||||||
* Retrieve tunnel color
|
* Retrieve tunnel color
|
||||||
*
|
*
|
||||||
* The color will be rendered in the background and will bleed through the
|
* The color will be rendered in the background and will bleed through
|
||||||
* transparent portions of the tile. We use explicit HEX codes rather than
|
* the transparent portions of the tile. We use explicit HEX codes
|
||||||
* CSS color names because environments may vary the colors used.
|
* rather than CSS color names because environments may vary the colors
|
||||||
|
* used.
|
||||||
*
|
*
|
||||||
* @param {number} oid tunnel object id
|
* @param {number} oid tunnel object id
|
||||||
*
|
*
|
||||||
|
@ -245,9 +247,9 @@ module.exports = Class( 'ClassicMap' )
|
||||||
*/
|
*/
|
||||||
'public getTunnelColor': function( oid )
|
'public getTunnelColor': function( oid )
|
||||||
{
|
{
|
||||||
// get the tunnel id by stripping off the tunnel bitmask and then grab
|
// get the tunnel id by stripping off the tunnel bitmask and then
|
||||||
// the associated color
|
// grab the associated color
|
||||||
var tunnel_id = ( ( +oid ^ this.__self.$( '_TMASK' ) ) / 2 );
|
const tunnel_id = ( ( +oid ^ this.__self.$( '_TMASK' ) ) / 2 );
|
||||||
|
|
||||||
return this.__self.$( '_TCOLORS' )[ tunnel_id ] || 'black';
|
return this.__self.$( '_TCOLORS' )[ tunnel_id ] || 'black';
|
||||||
},
|
},
|
||||||
|
@ -265,79 +267,81 @@ module.exports = Class( 'ClassicMap' )
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map name
|
* Retrieve level name
|
||||||
*
|
*
|
||||||
* @return {string} map name
|
* @return {string} level name
|
||||||
*/
|
*/
|
||||||
'public getMapName': function()
|
'public getLevelName': function()
|
||||||
{
|
{
|
||||||
return this._getDataSegment( '_NAMESIZE' );
|
return this._getDataSegment( '_NAMESIZE' );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map author name
|
* Retrieve level author name
|
||||||
*
|
*
|
||||||
* @return {string} map author name
|
* @return {string} level author name
|
||||||
*/
|
*/
|
||||||
'public getMapAuthor': function()
|
'public getLevelAuthor': function()
|
||||||
{
|
{
|
||||||
return this._getDataSegment( '_AUTHORSIZE' );
|
return this._getDataSegment( '_AUTHORSIZE' );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map hint
|
* Retrieve level hint
|
||||||
*
|
*
|
||||||
* @return {string} map hint
|
* @return {string} level hint
|
||||||
*/
|
*/
|
||||||
'public getMapHint': function()
|
'public getLevelHint': function()
|
||||||
{
|
{
|
||||||
return this._getDataSegment( '_HINTSIZE' );
|
return this._getDataSegment( '_HINTSIZE' );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map difficulty
|
* Retrieve level difficulty
|
||||||
*
|
*
|
||||||
* The map difficulty will be returned as a 0-indexed value between 0 and 4,
|
* The level difficulty will be returned as a 0-indexed value between 0
|
||||||
* with 0 representing "kids" and 4 representing "deadly".
|
* and 4, with 0 representing "kids" and 4 representing "deadly".
|
||||||
*
|
*
|
||||||
* The original game uses a bitmask for this value (thus the 16-bit
|
* The original game uses a bitmask for this value (thus the 16-bit
|
||||||
* integer), which is really of no particular use. For simplicity's sake, we
|
* integer), which is really of no particular use. For simplicity's
|
||||||
* will convert it.
|
* sake, we will convert it.
|
||||||
*
|
*
|
||||||
* @return {number} 0-indexed difficulty level
|
* @return {number} 0-indexed difficulty level
|
||||||
*/
|
*/
|
||||||
'public getMapDifficulty': function()
|
'public getLevelDifficulty': function()
|
||||||
{
|
{
|
||||||
var val = this._getDataSegment( '_DIFFSIZE', false ),
|
const val = this._getDataSegment( '_DIFFSIZE', false );
|
||||||
i = val.length,
|
|
||||||
n = 0;
|
let i = val.length,
|
||||||
|
n = 0;
|
||||||
|
|
||||||
// first, convert the value to an integer (from little-endian)
|
// first, convert the value to an integer (from little-endian)
|
||||||
while ( i-- ) n += ( val.charCodeAt( i ) << ( 8 * i ) );
|
while ( i-- ) n += ( val.charCodeAt( i ) << ( 8 * i ) );
|
||||||
|
|
||||||
// Finally, convert to a simple 0-4 value to represent difficulty by
|
// Finally, convert to a simple 0-4 value to represent difficulty by
|
||||||
// taking the binary logarithm of the value (lg(n); original game uses
|
// taking the binary logarithm of the value (lg(n); original game
|
||||||
// bitmasks). For those who do not understand logarithms, the concept
|
// uses bitmasks). For those who do not understand logarithms, the
|
||||||
// here is simple: if we are given a difficulty of "hard" (value of 8),
|
// concept here is simple: if we are given a difficulty of "hard"
|
||||||
// that has a binary representation of: 1000. We are interested in the
|
// (value of 8), that has a binary representation of: 1000. We are
|
||||||
// position of the 1-bit. Since each bit position is an exponent of two,
|
// interested in the position of the 1-bit. Since each bit position
|
||||||
// we can reverse that calculation with a binary logarithm. So, log2(8),
|
// is an exponent of two, we can reverse that calculation with a
|
||||||
// also written as lg(8), is equal to 3, since 2^3 = 8. Similarly,
|
// binary logarithm. So, log2(8), also written as lg(8), is equal to
|
||||||
// "deadly" = 16 = 0001 0000 => lg(16) = 4, and "kids" = 1 = 0001 =>
|
// 3, since 2^3 = 8. Similarly, "deadly" = 16 = 0001 0000 => lg(16)
|
||||||
// lg(1) = 0. This gives us a 0-indexed difficulty value.
|
// = 4, and "kids" = 1 = 0001 => lg(1) = 0. This gives us a
|
||||||
|
// 0-indexed difficulty value.
|
||||||
return ( Math.log( n ) / Math.log( 2 ) );
|
return ( Math.log( n ) / Math.log( 2 ) );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve size of map in bytes
|
* Retrieve size of level in bytes
|
||||||
*
|
*
|
||||||
* @return {number} size of map in bytes
|
* @return {number} size of level in bytes
|
||||||
*/
|
*/
|
||||||
'public static getMapSize': function()
|
'public static getLevelSize': function()
|
||||||
{
|
{
|
||||||
return this.$( '_SIZE' );
|
return this.$( '_SIZE' );
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The details on exactly how the map data is stored is left to specific
|
* The details on exactly how the level data is stored is left to specific
|
||||||
* implementations. However, the following is common to each file format:
|
* implementations. However, the following is common to each file format:
|
||||||
*
|
*
|
||||||
* - All game objects for the playfield should be returned in columns rather
|
* - All game objects for the playfield should be returned in columns rather
|
||||||
|
@ -29,26 +29,26 @@
|
||||||
* tunnel identified by index 0 is 0x40, index 1 is 0x42, and so on.
|
* tunnel identified by index 0 is 0x40, index 1 is 0x42, and so on.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Interface = require( 'easejs' ).Interface;
|
const Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a map (level)
|
* Represents a game level
|
||||||
*
|
*
|
||||||
* Maps simply act as basic wrappers around a set of maps, returning only the
|
* Levels simply act as basic wrappers around a set of maps, returning only the
|
||||||
* data associated with the requested map. This allows the data to be lazily
|
* data associated with the requested level. This allows the data to be lazily
|
||||||
* sliced out of the map file.
|
* sliced out of the level file.
|
||||||
*
|
*
|
||||||
* Note that this interface specifies a constructor definition; this allows it
|
* Note that this interface specifies a constructor definition; this allows it
|
||||||
* to be used in place of a separate Factory class.
|
* to be used in place of a separate Factory class.
|
||||||
*/
|
*/
|
||||||
module.exports = Interface( 'Map',
|
module.exports = Interface( 'Level',
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initialize map with map data and the given id
|
* Initialize level with level data and the given id
|
||||||
*
|
*
|
||||||
* @param {MapSet} set map set data
|
* @param {LevelSet} set level set data
|
||||||
* @param {number} id 1-indexed map id
|
* @param {number} id 1-indexed level id
|
||||||
*/
|
*/
|
||||||
__construct: [ 'set', 'id' ],
|
__construct: [ 'set', 'id' ],
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ module.exports = Interface( 'Map',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map dimensions
|
* Retrieve level dimensions
|
||||||
*
|
*
|
||||||
* @return {Array.<number>} width and height in tiles
|
* @return {Array.<number>} width and height in tiles
|
||||||
*/
|
*/
|
||||||
|
@ -75,11 +75,11 @@ module.exports = Interface( 'Map',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map of object codes to their appropriate tiles
|
* Retrieve level of object codes to their appropriate tiles
|
||||||
*
|
*
|
||||||
* @return {Array.<string>}
|
* @return {Array.<string>}
|
||||||
*/
|
*/
|
||||||
'public getObjectTileMap': [],
|
'public getObjectTileLevel': [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,44 +106,44 @@ module.exports = Interface( 'Map',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map name
|
* Retrieve level name
|
||||||
*
|
*
|
||||||
* @return {string} map name
|
* @return {string} level name
|
||||||
*/
|
*/
|
||||||
'public getMapName': [],
|
'public getLevelName': [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map author name
|
* Retrieve level author name
|
||||||
*
|
*
|
||||||
* @return {string} map author name
|
* @return {string} level author name
|
||||||
*/
|
*/
|
||||||
'public getMapAuthor': [],
|
'public getLevelAuthor': [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map hint
|
* Retrieve level hint
|
||||||
*
|
*
|
||||||
* @return {string} map hint
|
* @return {string} level hint
|
||||||
*/
|
*/
|
||||||
'public getMapHint': [],
|
'public getLevelHint': [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map difficulty
|
* Retrieve level difficulty
|
||||||
*
|
*
|
||||||
* The map difficulty should be returned as a 0-indexed value between 0 and
|
* The level difficulty should be returned as a 0-indexed value between
|
||||||
* 4, with 0 representing "kids" and 4 representing "deadly".
|
* 0 and 4, with 0 representing "kids" and 4 representing "deadly".
|
||||||
*
|
*
|
||||||
* @return {number} 0-indexed difficulty level
|
* @return {number} 0-indexed difficulty level
|
||||||
*/
|
*/
|
||||||
'public getMapDifficulty': [],
|
'public getLevelDifficulty': [],
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve size of map in bytes
|
* Retrieve size of level in bytes
|
||||||
*
|
*
|
||||||
* @return {number} size of map in bytes
|
* @return {number} size of level in bytes
|
||||||
*/
|
*/
|
||||||
'public static getMapSize': []
|
'public static getLevelSize': []
|
||||||
} );
|
} );
|
|
@ -17,11 +17,11 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
MapBounds = require( './MapBounds' );
|
LevelBounds = require( './LevelBounds' );
|
||||||
|
|
||||||
|
|
||||||
module.exports = Class( 'MapAction',
|
module.exports = Class( 'LevelAction',
|
||||||
{
|
{
|
||||||
// arranged by keycode
|
// arranged by keycode
|
||||||
'const D__MIN': 0,
|
'const D__MIN': 0,
|
||||||
|
@ -43,9 +43,9 @@ module.exports = Class( 'MapAction',
|
||||||
|
|
||||||
__construct: function( bounds, move_callback )
|
__construct: function( bounds, move_callback )
|
||||||
{
|
{
|
||||||
if ( !( Class.isA( MapBounds, bounds ) ) )
|
if ( !( Class.isA( LevelBounds, bounds ) ) )
|
||||||
{
|
{
|
||||||
throw TypeError( 'Invalid MapBounds provided' );
|
throw TypeError( 'Invalid LevelBounds provided' );
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dir = this.__self.$( 'D_UP' );
|
this._dir = this.__self.$( 'D_UP' );
|
||||||
|
@ -57,7 +57,7 @@ module.exports = Class( 'MapAction',
|
||||||
|
|
||||||
'public move': function()
|
'public move': function()
|
||||||
{
|
{
|
||||||
var method = [
|
const method = [
|
||||||
'getLeftPos',
|
'getLeftPos',
|
||||||
'getUpperPos',
|
'getUpperPos',
|
||||||
'getRightPos',
|
'getRightPos',
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Handles map boundaries for collision detection and movement
|
* Handles level boundaries for collision detection and movement
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012, 2015 Mike Gerwitz
|
* Copyright (C) 2012, 2015 Mike Gerwitz
|
||||||
*
|
*
|
||||||
|
@ -17,45 +17,46 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
Map = require( './Map' );
|
Level = require( './Level' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates map bounding box
|
* Calculates level bounding box
|
||||||
*
|
*
|
||||||
* This simply encapsulates the process of determining whether a given position
|
* This simply encapsulates the process of determining whether a given
|
||||||
* is against an edge of the map.
|
* position is against an edge of the level.
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'MapBounds',
|
module.exports = Class( 'LevelBounds',
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Map width (number of tiles)
|
* Level width (number of tiles)
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
'private _mw': 0,
|
'private _mw': 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map height (number of tiles)
|
* Level height (number of tiles)
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
'private _mh': 0,
|
'private _mh': 0,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize bounding box for a given map
|
* Initialize bounding box for a given level
|
||||||
*
|
*
|
||||||
* @param {Map} map map for which bounds should be calculated
|
* @param {Level} level level for which bounds should be calculated
|
||||||
*/
|
*/
|
||||||
__construct: function( map )
|
__construct: function( level )
|
||||||
{
|
{
|
||||||
if ( !( Class.isA( Map, map ) ) )
|
if ( !( Class.isA( Level, level ) ) )
|
||||||
{
|
{
|
||||||
throw TypeError( 'Invalid Map provided' );
|
throw TypeError( 'Invalid Level provided' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are only interested in the dimensions of the map
|
// we are only interested in the dimensions of the level
|
||||||
var dimen = map.getDimensions();
|
const dimen = level.getDimensions();
|
||||||
|
|
||||||
this._mw = dimen[ 0 ];
|
this._mw = dimen[ 0 ];
|
||||||
this._mh = dimen[ 1 ];
|
this._mh = dimen[ 1 ];
|
||||||
},
|
},
|
||||||
|
@ -64,7 +65,7 @@ module.exports = Class( 'MapBounds',
|
||||||
/**
|
/**
|
||||||
* Retrieve the tile position above the given position
|
* Retrieve the tile position above the given position
|
||||||
*
|
*
|
||||||
* If the given tile position is at the top of the map, then the given
|
* If the given tile position is at the top of the level, then the given
|
||||||
* position will be returned.
|
* position will be returned.
|
||||||
*
|
*
|
||||||
* @param {number} pos original tile position
|
* @param {number} pos original tile position
|
||||||
|
@ -82,8 +83,8 @@ module.exports = Class( 'MapBounds',
|
||||||
/**
|
/**
|
||||||
* Retrieve the tile position below the given position
|
* Retrieve the tile position below the given position
|
||||||
*
|
*
|
||||||
* If the given tile position is at the bottom of the map, then the given
|
* If the given tile position is at the bottom of the level, then the
|
||||||
* position will be returned.
|
* given position will be returned.
|
||||||
*
|
*
|
||||||
* @param {number} pos original tile position
|
* @param {number} pos original tile position
|
||||||
*
|
*
|
||||||
|
@ -100,8 +101,8 @@ module.exports = Class( 'MapBounds',
|
||||||
/**
|
/**
|
||||||
* Retrieve the tile position to the left of the given position
|
* Retrieve the tile position to the left of the given position
|
||||||
*
|
*
|
||||||
* If the given tile position is at the leftmost column of the map, then the
|
* If the given tile position is at the leftmost column of the level,
|
||||||
* given position will be returned.
|
* then the given position will be returned.
|
||||||
*
|
*
|
||||||
* @param {number} pos original tile position
|
* @param {number} pos original tile position
|
||||||
*
|
*
|
||||||
|
@ -119,8 +120,8 @@ module.exports = Class( 'MapBounds',
|
||||||
/**
|
/**
|
||||||
* Retrieve the tile position to the right of the given position
|
* Retrieve the tile position to the right of the given position
|
||||||
*
|
*
|
||||||
* If the given tile position is at the rightmost column of the map, then
|
* If the given tile position is at the rightmost column of the level,
|
||||||
* the given position will be returned.
|
* then the given position will be returned.
|
||||||
*
|
*
|
||||||
* @param {number} pos original tile position
|
* @param {number} pos original tile position
|
||||||
*
|
*
|
||||||
|
@ -136,7 +137,7 @@ module.exports = Class( 'MapBounds',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given position is in the topmost row of the map
|
* Determines if the given position is in the topmost row of the level
|
||||||
*
|
*
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
*
|
*
|
||||||
|
@ -144,14 +145,14 @@ module.exports = Class( 'MapBounds',
|
||||||
*/
|
*/
|
||||||
'public isAtTop': function( pos )
|
'public isAtTop': function( pos )
|
||||||
{
|
{
|
||||||
// since tile positions are zero-indexed, we know that we're at the top
|
// since tile positions are zero-indexed, we know that we're at the
|
||||||
// if the map height divides the position
|
// top if the level height divides the position
|
||||||
return ( pos % this._mh === 0 );
|
return ( pos % this._mh === 0 );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given position is in the bottom row of the map
|
* Determines if the given position is in the bottom row of the level
|
||||||
*
|
*
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
*
|
*
|
||||||
|
@ -165,7 +166,8 @@ module.exports = Class( 'MapBounds',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given position is in the leftmost column of the map
|
* Determines if the given position is in the leftmost column of the
|
||||||
|
* level
|
||||||
*
|
*
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
*
|
*
|
||||||
|
@ -179,7 +181,8 @@ module.exports = Class( 'MapBounds',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given position is in the rightmost column of the map
|
* Determines if the given position is in the rightmost column of the
|
||||||
|
* level
|
||||||
*
|
*
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
*
|
*
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Renders a given map
|
* Renders a given level
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012, 2015 Mike Gerwitz
|
* Copyright (C) 2012, 2015 Mike Gerwitz
|
||||||
*
|
*
|
||||||
|
@ -17,20 +17,20 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
MapState = require( './MapState' );
|
LevelState = require( './LevelState' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a map to a canvas
|
* Renders a level to a canvas
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'MapRender',
|
module.exports = Class( 'LevelRender',
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Property to hold lock bit on canvas element
|
* Property to hold lock bit on canvas element
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
'private const _LOCK': '__$$MapRenderLock$$',
|
'private const _LOCK': '__$$LevelRenderLock$$',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animation interval in milliseconds
|
* Animation interval in milliseconds
|
||||||
|
@ -40,7 +40,7 @@ module.exports = Class( 'MapRender',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 2d context to which map should be drawn
|
* 2d context to which level should be drawn
|
||||||
* @type {CanvasRenderingContext2d}
|
* @type {CanvasRenderingContext2d}
|
||||||
*/
|
*/
|
||||||
'private _ctx': null,
|
'private _ctx': null,
|
||||||
|
@ -67,8 +67,8 @@ module.exports = Class( 'MapRender',
|
||||||
/**
|
/**
|
||||||
* Initialize renderer with a canvas context and a tile set
|
* Initialize renderer with a canvas context and a tile set
|
||||||
*
|
*
|
||||||
* An additional canvas of equal dimensions will be created and laid atop of
|
* An additional canvas of equal dimensions will be created and laid
|
||||||
* the provided canvas to render masked game objects.
|
* atop of the provided canvas to render masked game objects.
|
||||||
*
|
*
|
||||||
* @param {CanvasRenderingContext2d} ctx canvas 2d context
|
* @param {CanvasRenderingContext2d} ctx canvas 2d context
|
||||||
* @param {Object} tiles tile set to render
|
* @param {Object} tiles tile set to render
|
||||||
|
@ -79,7 +79,7 @@ module.exports = Class( 'MapRender',
|
||||||
this._tiles = tiles;
|
this._tiles = tiles;
|
||||||
|
|
||||||
// ensure that we are exclusively rendering to this canvas (no other
|
// ensure that we are exclusively rendering to this canvas (no other
|
||||||
// MapRenders)
|
// LevelRenders)
|
||||||
this._lockCanvas();
|
this._lockCanvas();
|
||||||
|
|
||||||
this._ctxObj = this._getObjCanvas();
|
this._ctxObj = this._getObjCanvas();
|
||||||
|
@ -87,22 +87,24 @@ module.exports = Class( 'MapRender',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock the canvas to prevent other MapRender instances from rendering to it
|
* Lock the canvas to prevent other LevelRender instances from rendering
|
||||||
|
* to it
|
||||||
*
|
*
|
||||||
* The purpose of this is to provide feedback to the user/developer in the
|
* The purpose of this is to provide feedback to the user/developer in
|
||||||
* event that multiple MapRender instances are attempting to render to the
|
* the event that multiple LevelRender instances are attempting to
|
||||||
* same canvas, which would certainly cause display issues and confusion.
|
* render to the same canvas, which would certainly cause display issues
|
||||||
|
* and confusion.
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'private _lockCanvas': function()
|
'private _lockCanvas': function()
|
||||||
{
|
{
|
||||||
var o = this._ctx,
|
const o = this._ctx,
|
||||||
l = this.__self.$( '_LOCK' );
|
l = this.__self.$( '_LOCK' );
|
||||||
|
|
||||||
// simple one-line check to both set the lock and fail if the lock is
|
// simple one-line check to both set the lock and fail if the lock
|
||||||
// already set (implying that something else is already rendering to the
|
// is already set (implying that something else is already rendering
|
||||||
// canvas)
|
// to the canvas)
|
||||||
if ( ( o[ l ] ^= 1 ) !== 1 )
|
if ( ( o[ l ] ^= 1 ) !== 1 )
|
||||||
{
|
{
|
||||||
// reset the lock
|
// reset the lock
|
||||||
|
@ -110,24 +112,24 @@ module.exports = Class( 'MapRender',
|
||||||
|
|
||||||
throw Error(
|
throw Error(
|
||||||
'Could not set exclusive lock on canvas (in use by another ' +
|
'Could not set exclusive lock on canvas (in use by another ' +
|
||||||
'MapRender instance)'
|
'LevelRender instance)'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove exclusive lock on canvas to permit other MapRender instances to
|
* Remove exclusive lock on canvas to permit other LevelRender instances
|
||||||
* render to it
|
* to render to it
|
||||||
*
|
*
|
||||||
* This will also destroy the overlay canvas that the masked objects were
|
* This will also destroy the overlay canvas that the masked objects
|
||||||
* rendered to. The remaining canvas will not be cleared.
|
* were rendered to. The remaining canvas will not be cleared.
|
||||||
*
|
*
|
||||||
* @return {MapRender} self
|
* @return {LevelRender} self
|
||||||
*/
|
*/
|
||||||
'public freeCanvas': function()
|
'public freeCanvas': function()
|
||||||
{
|
{
|
||||||
var c = this._ctxObj.canvas;
|
const c = this._ctxObj.canvas;
|
||||||
|
|
||||||
// clear any running animations
|
// clear any running animations
|
||||||
this._clearAnimation();
|
this._clearAnimation();
|
||||||
|
@ -148,8 +150,9 @@ module.exports = Class( 'MapRender',
|
||||||
*/
|
*/
|
||||||
'private _getObjCanvas': function()
|
'private _getObjCanvas': function()
|
||||||
{
|
{
|
||||||
var canvas = this._ctx.canvas,
|
const canvas = this._ctx.canvas,
|
||||||
canvas_obj = document.createElement( 'canvas' );
|
document = this._getDocument( canvas ),
|
||||||
|
canvas_obj = document.createElement( 'canvas' );
|
||||||
|
|
||||||
// mimic the dimensions and positions of the original canvas
|
// mimic the dimensions and positions of the original canvas
|
||||||
canvas_obj.style.position = 'absolute';
|
canvas_obj.style.position = 'absolute';
|
||||||
|
@ -166,46 +169,63 @@ module.exports = Class( 'MapRender',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the provided map
|
* Get HTMLDocument node from element ELEMENT
|
||||||
*
|
*
|
||||||
* @param {Map} map map to render
|
* ELEMENT must be on a DOM. This allows us to always reference the
|
||||||
|
* proper document node without being coupled with the browser's
|
||||||
|
* window.document, which may not be what we're interested in.
|
||||||
*
|
*
|
||||||
* @return {MapRender} self
|
* @param {HTMLElement} element reference element
|
||||||
|
*
|
||||||
|
* @return {HTMLDocument} document node
|
||||||
*/
|
*/
|
||||||
'public render': function( map, map_state )
|
'private _getDocument': function( element )
|
||||||
{
|
{
|
||||||
if ( !( Class.isA( MapState, map_state ) ) )
|
return ( element.parentElement === null )
|
||||||
|
? element.parentNode
|
||||||
|
: this._getDocument( element.parentElement );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the provided level
|
||||||
|
*
|
||||||
|
* @param {Level} level level to render
|
||||||
|
*
|
||||||
|
* @return {LevelRender} self
|
||||||
|
*/
|
||||||
|
'public render': function( level, level_state )
|
||||||
|
{
|
||||||
|
if ( !( Class.isA( LevelState, level_state ) ) )
|
||||||
{
|
{
|
||||||
throw TypeError( 'Invalid MapState provided' );
|
throw TypeError( 'Invalid LevelState provided' );
|
||||||
}
|
}
|
||||||
|
|
||||||
var objs = map.getObjects(),
|
const objs = level.getObjects(),
|
||||||
size = map.getDimensions(),
|
size = level.getDimensions(),
|
||||||
omap = map.getObjectTileMap(),
|
sizex = size[ 0 ],
|
||||||
sizex = size[ 0 ],
|
sizey = size[ 1 ],
|
||||||
sizey = size[ 1 ],
|
|
||||||
i = objs.length,
|
|
||||||
|
|
||||||
// tiles to animate
|
// tiles to animate
|
||||||
anim = [],
|
anim = [],
|
||||||
|
|
||||||
// get the width and height from one of the tiles
|
// get the width and height from one of the tiles
|
||||||
t = this._tiles.dirt.data,
|
t = this._tiles.dirt.data,
|
||||||
w = t.width,
|
w = t.width,
|
||||||
h = t.height;
|
h = t.height;
|
||||||
|
|
||||||
this._clearCanvases();
|
this._clearCanvases();
|
||||||
|
|
||||||
var _self = this;
|
const _self = this;
|
||||||
map_state.onChange( function( obj, pos )
|
level_state.onChange( function( obj, pos )
|
||||||
{
|
{
|
||||||
var oid = objs[ pos ],
|
const oid = objs[ pos ],
|
||||||
tid = ( obj ) ? obj.getTid() : 'dirt',
|
tid = ( obj ) ? obj.getTid() : 'dirt',
|
||||||
tile = ( _self._tiles[ tid ] || {} ).first,
|
tile = ( _self._tiles[ tid ] || {} ).first,
|
||||||
|
|
||||||
v = _self._getTileVector( pos, sizex, sizey, w, h ),
|
v = _self._getTileVector( pos, sizex, sizey, w, h ),
|
||||||
x = v[ 0 ],
|
x = v[ 0 ],
|
||||||
y = v[ 1 ];
|
y = v[ 1 ];
|
||||||
|
|
||||||
if ( obj === null )
|
if ( obj === null )
|
||||||
{
|
{
|
||||||
|
@ -214,9 +234,9 @@ module.exports = Class( 'MapRender',
|
||||||
}
|
}
|
||||||
|
|
||||||
// tunnels are handled a bit differently than other objects
|
// tunnels are handled a bit differently than other objects
|
||||||
if ( map.isObjectTunnel( oid ) )
|
if ( level.isObjectTunnel( oid ) )
|
||||||
{
|
{
|
||||||
_self._renderTunnel( x, y, map.getTunnelColor( oid ) );
|
_self._renderTunnel( x, y, level.getTunnelColor( oid ) );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,10 +249,10 @@ module.exports = Class( 'MapRender',
|
||||||
_self._drawTile( tile, x, y );
|
_self._drawTile( tile, x, y );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
map_state.flush();
|
level_state.flush();
|
||||||
|
|
||||||
// render each object (remember, we're dealing with columns, not rows;
|
// render each object (remember, we're dealing with columns, not
|
||||||
// see Map.getObjects())
|
// rows; see Level.getObjects())
|
||||||
|
|
||||||
this._beginAnimation( anim );
|
this._beginAnimation( anim );
|
||||||
|
|
||||||
|
@ -241,12 +261,12 @@ module.exports = Class( 'MapRender',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a vector representing the x and y position coordinates of a tile
|
* Retrieve a vector representing the x and y position coordinates of a
|
||||||
* position
|
* tile position
|
||||||
*
|
*
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
* @param {number} sizex number of horizontal tiles in map
|
* @param {number} sizex number of horizontal tiles in level
|
||||||
* @param {number} sizey number of vertical tiles in map
|
* @param {number} sizey number of vertical tiles in level
|
||||||
* @param {number} w tile width
|
* @param {number} w tile width
|
||||||
* @param {number} h tile height
|
* @param {number} h tile height
|
||||||
*
|
*
|
||||||
|
@ -264,26 +284,27 @@ module.exports = Class( 'MapRender',
|
||||||
/**
|
/**
|
||||||
* Clears overlay canvas
|
* Clears overlay canvas
|
||||||
*
|
*
|
||||||
* This should be used before first rendering a map to ensure that any
|
* This should be used before first rendering a level to ensure that any
|
||||||
* artifacts from previous map renderings will be erased.
|
* artifacts from previous level renderings will be erased.
|
||||||
*
|
*
|
||||||
* We need only clear the overlay canvas, because the lower canvas will
|
* We need only clear the overlay canvas, because the lower canvas will
|
||||||
* always be overwritten with tiles in every location. Because none of the
|
* always be overwritten with tiles in every location. Because none of
|
||||||
* tiles written to the lower canvas are masked, nothing from the previous
|
* the tiles written to the lower canvas are masked, nothing from the
|
||||||
* render would ever peek through (of course, putImageData() would overwrite
|
* previous render would ever peek through (of course, putImageData()
|
||||||
* it even if that were the case). As such, clearing the lower canvas would
|
* would overwrite it even if that were the case). As such, clearing the
|
||||||
* simply be a waste of time and only serve to degrade performance
|
* lower canvas would simply be a waste of time and only serve to
|
||||||
* (especially if this is being used with maps larger than the classic
|
* degrade performance (especially if this is being used with levels
|
||||||
* 16x16).
|
* larger than the classic 16x16).
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'private _clearCanvases': function()
|
'private _clearCanvases': function()
|
||||||
{
|
{
|
||||||
var ctx = this._ctxObj,
|
const ctx = this._ctxObj,
|
||||||
c = ctx.canvas;
|
c = ctx.canvas;
|
||||||
|
|
||||||
// we need only clear the overlay (to which masked tiles are rendered)
|
// we need only clear the overlay (to which masked tiles are
|
||||||
|
// rendered)
|
||||||
ctx.clearRect( 0, 0, c.width, c.height );
|
ctx.clearRect( 0, 0, c.width, c.height );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -297,7 +318,7 @@ module.exports = Class( 'MapRender',
|
||||||
*/
|
*/
|
||||||
'private _canAnimate': function( tid )
|
'private _canAnimate': function( tid )
|
||||||
{
|
{
|
||||||
var tdata = this._tiles[ tid ];
|
const tdata = this._tiles[ tid ];
|
||||||
return ( tdata.next !== tdata );
|
return ( tdata.next !== tdata );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -305,9 +326,10 @@ module.exports = Class( 'MapRender',
|
||||||
/**
|
/**
|
||||||
* Draw the tile identified by the given id
|
* Draw the tile identified by the given id
|
||||||
*
|
*
|
||||||
* The tile will be drawn to the appropriate canvas depending on whether or
|
* The tile will be drawn to the appropriate canvas depending on whether
|
||||||
* not it has been masked. If it does have a mask, it will be drawn to the
|
* or not it has been masked. If it does have a mask, it will be drawn
|
||||||
* overlaying canvas and the dirt tile will be drawn underneath it.
|
* to the overlaying canvas and the dirt tile will be drawn underneath
|
||||||
|
* it.
|
||||||
*
|
*
|
||||||
* @param {string} tid tile id
|
* @param {string} tid tile id
|
||||||
* @param {number} x left position
|
* @param {number} x left position
|
||||||
|
@ -317,7 +339,7 @@ module.exports = Class( 'MapRender',
|
||||||
*/
|
*/
|
||||||
'private _drawTile': function( tile, x, y )
|
'private _drawTile': function( tile, x, y )
|
||||||
{
|
{
|
||||||
var ctx = ( tile.masked ) ? this._ctxObj : this._ctx;
|
const ctx = ( tile.masked ) ? this._ctxObj : this._ctx;
|
||||||
|
|
||||||
ctx.putImageData( tile.data, x, y );
|
ctx.putImageData( tile.data, x, y );
|
||||||
|
|
||||||
|
@ -338,8 +360,8 @@ module.exports = Class( 'MapRender',
|
||||||
* Render the given tunnel
|
* Render the given tunnel
|
||||||
*
|
*
|
||||||
* The tunnel background color (which will peek through the mask) is
|
* The tunnel background color (which will peek through the mask) is
|
||||||
* rendered to the base canvas, whereas the tunnel tile itself is rendered
|
* rendered to the base canvas, whereas the tunnel tile itself is
|
||||||
* on the overlaying canvas.
|
* rendered on the overlaying canvas.
|
||||||
*
|
*
|
||||||
* @param {number} x left position
|
* @param {number} x left position
|
||||||
* @param {number} y top position
|
* @param {number} y top position
|
||||||
|
@ -349,7 +371,7 @@ module.exports = Class( 'MapRender',
|
||||||
*/
|
*/
|
||||||
'private _renderTunnel': function( x, y, color )
|
'private _renderTunnel': function( x, y, color )
|
||||||
{
|
{
|
||||||
var tdata = this._tiles.tunnel.data;
|
const tdata = this._tiles.tunnel.data;
|
||||||
|
|
||||||
// fill tile with the appropriate background color for this tile
|
// fill tile with the appropriate background color for this tile
|
||||||
this._ctx.fillStyle = color;
|
this._ctx.fillStyle = color;
|
||||||
|
@ -363,8 +385,8 @@ module.exports = Class( 'MapRender',
|
||||||
/**
|
/**
|
||||||
* Begin basic tile animation
|
* Begin basic tile animation
|
||||||
*
|
*
|
||||||
* At each animation interval, each tile will be advanced a single frame and
|
* At each animation interval, each tile will be advanced a single frame
|
||||||
* rendered atop of the previous.
|
* and rendered atop of the previous.
|
||||||
*
|
*
|
||||||
* @param {Array.<Array.<Object,number,number>>} anim array of tiles to
|
* @param {Array.<Array.<Object,number,number>>} anim array of tiles to
|
||||||
* animate; tdata,x,y
|
* animate; tdata,x,y
|
||||||
|
@ -373,32 +395,32 @@ module.exports = Class( 'MapRender',
|
||||||
*/
|
*/
|
||||||
'private _beginAnimation': function( anim )
|
'private _beginAnimation': function( anim )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
// clear any existing rendering animations
|
// clear any existing rendering animations
|
||||||
this._clearAnimation();
|
this._clearAnimation();
|
||||||
|
|
||||||
return this._animTimer = setInterval( function()
|
return ( this._animTimer = setInterval( function()
|
||||||
{
|
{
|
||||||
var i = anim.length;
|
let i = anim.length;
|
||||||
|
|
||||||
while ( i-- )
|
while ( i-- )
|
||||||
{
|
{
|
||||||
var cur = anim[ i ];
|
const cur = anim[ i ];
|
||||||
|
|
||||||
// draw next frame
|
// draw next frame
|
||||||
cur[ 0 ] = cur[ 0 ].next;
|
cur[ 0 ] = cur[ 0 ].next;
|
||||||
_self._drawTile.apply( _self, anim[ i ] );
|
_self._drawTile.apply( _self, anim[ i ] );
|
||||||
}
|
}
|
||||||
}, this.__self.$( '_ANIM_INTERVAL' ) );
|
}, this.__self.$( '_ANIM_INTERVAL' ) ) );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear any running animation timers
|
* Clear any running animation timers
|
||||||
*
|
*
|
||||||
* It is important that this be done when a MapRender instance is done being
|
* It is important that this be done when a LevelRender instance is done
|
||||||
* used, or it will remain in memory indefinitely!
|
* being used, or it will remain in memory indefinitely!
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
|
@ -17,22 +17,20 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Maps (the term "level" is used in the game) are stored in a fixed-width LVL
|
* Levels are stored in a fixed-width LVL file.
|
||||||
* file.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles delegation of LVL data
|
* Handles delegation of LVL data
|
||||||
*
|
*
|
||||||
* This acts as a Map factory, leaving all processing responsibilities to the
|
* This acts as a Level factory, leaving all processing responsibilities to
|
||||||
* Map instance. Consequently, any custom map format would be supported,
|
* the Level instance. Consequently, any custom level format would be
|
||||||
* provided that the appropriate Map is handling it.
|
* supported, provided that the appropriate Level is handling it.
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'MapSet',
|
module.exports = Class( 'LevelSet',
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Raw level set data (binary)
|
* Raw level set data (binary)
|
||||||
|
@ -41,61 +39,62 @@ module.exports = Class( 'MapSet',
|
||||||
'private _data': '',
|
'private _data': '',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used to create new map instances
|
* Constructor used to create new level instances
|
||||||
* @type {Function}
|
* @type {Function}
|
||||||
*/
|
*/
|
||||||
'private _mapCtor': null,
|
'private _levelCtor': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of maps in the given LVL data
|
* Number of levels in the given LVL data
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
'private _mapCount': 0,
|
'private _levelCount': 0,
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize map set with LVL data and a Map constructor
|
* Initialize level set with LVL data and a Level constructor
|
||||||
*
|
*
|
||||||
* The Map constructor is used in place of a separate Map factory.
|
* The Level constructor is used in place of a separate Level factory.
|
||||||
*
|
*
|
||||||
* @param {string} data binary LVL data
|
* @param {string} data binary LVL data
|
||||||
* @param {Map} map_ctor Map constructor
|
* @param {Level} level_ctor Level constructor
|
||||||
*/
|
*/
|
||||||
__construct: function( data, map_ctor )
|
__construct: function( data, level_ctor )
|
||||||
{
|
{
|
||||||
this._data = ''+( data );
|
this._data = ''+( data );
|
||||||
this._mapCtor = map_ctor;
|
this._levelCtor = level_ctor;
|
||||||
|
|
||||||
// perform a simple integrity check on the provided data
|
// perform a simple integrity check on the provided data
|
||||||
this._mapDataCheck();
|
this._levelDataCheck();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a simple map data integrity check
|
* Perform a simple level data integrity check
|
||||||
*
|
*
|
||||||
* This is intended to throw an error if we believe the LVL data to be
|
* 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.
|
* invalid, or if the LVL data is invalid for the given Level
|
||||||
* The check will simply ensure that the map size (in bytes) divides into
|
* constructor. The check will simply ensure that the level size (in
|
||||||
* the total LVL size (in bytes) without any remainder.
|
* bytes) divides into the total LVL size (in bytes) without any
|
||||||
|
* remainder.
|
||||||
*
|
*
|
||||||
* This is by no means fool-proof, but it should catch most.
|
* This is by no means fool-proof, but it should catch most.
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'private _mapDataCheck': function()
|
'private _levelDataCheck': function()
|
||||||
{
|
{
|
||||||
var n = ( this._data.length / this._mapCtor.getMapSize() );
|
const n = ( this._data.length / this._levelCtor.getLevelSize() );
|
||||||
|
|
||||||
// if the result is not an integer, then it is either not an LVL, the
|
// if the result is not an integer, then it is either not an LVL,
|
||||||
// file is corrupt, or we were given the wrong Map constructor
|
// the file is corrupt, or we were given the wrong Level constructor
|
||||||
if ( n % 1 )
|
if ( n % 1 )
|
||||||
{
|
{
|
||||||
throw Error( 'Invalid or corrupt LVL data' );
|
throw Error( 'Invalid or corrupt LVL data' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// we already calculated it, so we may as well store it
|
// we already calculated it, so we may as well store it
|
||||||
this._mapCount = n;
|
this._levelCount = n;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,19 +103,19 @@ module.exports = Class( 'MapSet',
|
||||||
*
|
*
|
||||||
* @param {number} id number of level to load, 1-indexed
|
* @param {number} id number of level to load, 1-indexed
|
||||||
*/
|
*/
|
||||||
'public getMapByNumber': function( id )
|
'public getLevelByNumber': function( id )
|
||||||
{
|
{
|
||||||
return this._mapCtor( this._data, id );
|
return this._levelCtor( this._data, id );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the number of maps in the LVL file
|
* Retrieve the number of levels in the LVL file
|
||||||
*
|
*
|
||||||
* @return {number} number of maps
|
* @return {number} number of levels
|
||||||
*/
|
*/
|
||||||
'public getMapCount': function()
|
'public getLevelCount': function()
|
||||||
{
|
{
|
||||||
return this._mapCount;
|
return this._levelCount;
|
||||||
},
|
},
|
||||||
} );
|
} );
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Represents the current state of a map
|
* Represents the current state of a level
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012, 2015 Mike Gerwitz
|
* Copyright (C) 2012, 2015 Mike Gerwitz
|
||||||
*
|
*
|
||||||
|
@ -17,16 +17,16 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class,
|
const Class = require( 'easejs' ).Class,
|
||||||
MapAction = require( './MapAction' ),
|
LevelAction = require( './LevelAction' ),
|
||||||
GameObjectFactory = require( './GameObjectFactory' ),
|
GameObjectFactory = require( '../GameObjectFactory' ),
|
||||||
GameObject = require( './gameobjs/GameObject' );
|
GameObject = require( '../gameobjs/GameObject' );
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current state of a map
|
* Represents the current state of a level
|
||||||
*/
|
*/
|
||||||
module.exports = Class( 'MapState',
|
module.exports = Class( 'LevelState',
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Game object factory
|
* Game object factory
|
||||||
|
@ -47,7 +47,7 @@ module.exports = Class( 'MapState',
|
||||||
'private _player': null,
|
'private _player': null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Game objects representing every object on the map
|
* Game objects representing every object on the level
|
||||||
* @type {GameObject}
|
* @type {GameObject}
|
||||||
*/
|
*/
|
||||||
'private _objs': [],
|
'private _objs': [],
|
||||||
|
@ -60,39 +60,41 @@ module.exports = Class( 'MapState',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes map state with a given map and factory with which to create
|
* Initializes level state with a given level and factory with which to
|
||||||
* game objects
|
* create game objects
|
||||||
*
|
*
|
||||||
* Game objects influence map state rules, therefore fundamental game logic
|
* Game objects influence level state rules, therefore fundamental game
|
||||||
* may be altered simply by passing in a custom GameObjectFactory instance.
|
* logic may be altered simply by passing in a custom GameObjectFactory
|
||||||
|
* instance.
|
||||||
*
|
*
|
||||||
* @param {Map} map game map
|
* @param {Level} level game level
|
||||||
* @param {GameObjectFactory} obj_factory game object factory
|
* @param {GameObjectFactory} obj_factory game object factory
|
||||||
*/
|
*/
|
||||||
__construct: function( map, obj_factory )
|
__construct: function( level, obj_factory )
|
||||||
{
|
{
|
||||||
if ( !( Class.isA( GameObjectFactory, obj_factory ) ) )
|
if ( !( Class.isA( GameObjectFactory, obj_factory ) ) )
|
||||||
{
|
{
|
||||||
throw TypeError( 'Invalid GameObjectFactory provided' );
|
throw TypeError( 'Invalid GameObjectFactory provided' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize game objects based on the initial map state
|
// initialize game objects based on the initial level state
|
||||||
this._objFactory = obj_factory;
|
this._objFactory = obj_factory;
|
||||||
this._initObjects( map.getObjects(), map.getObjectTileMap() );
|
this._initObjects( level.getObjects(), level.getObjectTileLevel() );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush map state, triggering the change event for each game object
|
* Flush level state, triggering the change event for each game object
|
||||||
*
|
*
|
||||||
* This may be used to perform an initial rendering or re-draw of the entire
|
* This may be used to perform an initial rendering or re-draw of the
|
||||||
* map. Note that this should not be used to re-render the map after
|
* entire level. Note that this should not be used to re-render the
|
||||||
* movements or state changes, as that would not be performant (especially
|
* level after movements or state changes, as that would not be
|
||||||
* since the map size could be arbitrarily large).
|
* performant (especially since the level size could be arbitrarily
|
||||||
|
* large).
|
||||||
*/
|
*/
|
||||||
'public flush': function()
|
'public flush': function()
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
// emit the change event for each game object
|
// emit the change event for each game object
|
||||||
this._forEachObj( function( obj, pos )
|
this._forEachObj( function( obj, pos )
|
||||||
|
@ -105,20 +107,20 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Register an object change callback
|
* Register an object change callback
|
||||||
*
|
*
|
||||||
* Registers a continuation to be called when a game object changes state
|
* Registers a continuation to be called when a game object changes
|
||||||
* (a state change may represent an object transforming into another, a
|
* state (a state change may represent an object transforming into
|
||||||
* movement, or anything else that requires re-rendering).
|
* another, a movement, or anything else that requires re-rendering).
|
||||||
*
|
*
|
||||||
* TODO: use event base
|
* TODO: use event base
|
||||||
*
|
*
|
||||||
* The continuation will be passed the game object in its new state its tile
|
* The continuation will be passed the game object in its new state its
|
||||||
* position. If the game object is null, then it is to be assumed that the
|
* tile position. If the game object is null, then it is to be assumed
|
||||||
* object no longer exists (e.g. has moved to another location) and should
|
* that the object no longer exists (e.g. has moved to another location)
|
||||||
* be cleared.
|
* and should be cleared.
|
||||||
*
|
*
|
||||||
* @param {function(GameObject,number)} callback continuation
|
* @param {function(GameObject,number)} callback continuation
|
||||||
*
|
*
|
||||||
* @return {MapState} self
|
* @return {LevelState} self
|
||||||
*/
|
*/
|
||||||
'public onChange': function( callback )
|
'public onChange': function( callback )
|
||||||
{
|
{
|
||||||
|
@ -130,11 +132,11 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Emit a change event for the given object
|
* Emit a change event for the given object
|
||||||
*
|
*
|
||||||
* States that an object's state has changed; see the onChange() method for
|
* States that an object's state has changed; see the onChange() method
|
||||||
* additional information. A change may not necessarily imply that the
|
* for additional information. A change may not necessarily imply that
|
||||||
* object itself has changed---the change may simply represent a new
|
* the object itself has changed---the change may simply represent a new
|
||||||
* position or---in the case of a null object---that the position has been
|
* position or---in the case of a null object---that the position has
|
||||||
* cleared of an object.
|
* been cleared of an object.
|
||||||
*
|
*
|
||||||
* @param {GameObject} obj the game object that has updated, or null
|
* @param {GameObject} obj the game object that has updated, or null
|
||||||
* @param {number} pos tile position of the game object
|
* @param {number} pos tile position of the game object
|
||||||
|
@ -143,8 +145,8 @@ module.exports = Class( 'MapState',
|
||||||
*/
|
*/
|
||||||
'private _emitChange': function( obj, pos )
|
'private _emitChange': function( obj, pos )
|
||||||
{
|
{
|
||||||
var i = -1,
|
const l = this._stateCallbacks.length;
|
||||||
l = this._stateCallbacks.length;
|
let i = -1;
|
||||||
|
|
||||||
while ( ++i < l )
|
while ( ++i < l )
|
||||||
{
|
{
|
||||||
|
@ -153,7 +155,7 @@ module.exports = Class( 'MapState',
|
||||||
|
|
||||||
if ( obj === null )
|
if ( obj === null )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
this._objs[ pos ].forEach( function( o )
|
this._objs[ pos ].forEach( function( o )
|
||||||
{
|
{
|
||||||
if ( o === null ) return;
|
if ( o === null ) return;
|
||||||
|
@ -165,25 +167,25 @@ module.exports = Class( 'MapState',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize game objects for the map's original (default) state
|
* Initialize game objects for the level's original (default) state
|
||||||
*
|
*
|
||||||
* All necessary game objects will be created to represent all objects on
|
* All necessary game objects will be created to represent all objects
|
||||||
* the map at its default state. Effectively, this creates the default map
|
* on the level at its default state. Effectively, this creates the
|
||||||
* state.
|
* default level state.
|
||||||
*
|
*
|
||||||
* @param {Array.<number>} objs game object data (object ids)
|
* @param {Array.<number>} objs game object data (object ids)
|
||||||
* @param {Array.<string>} objmap map from object ids to their tile ids
|
* @param {Array.<string>} objlevel level from object ids to their tile ids
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'private _initObjects': function( objs, objmap )
|
'private _initObjects': function( objs, objlevel )
|
||||||
{
|
{
|
||||||
var i = objs.length;
|
let i = objs.length;
|
||||||
|
|
||||||
while ( i-- )
|
while ( i-- )
|
||||||
{
|
{
|
||||||
var val = objs[ i ],
|
const val = objs[ i ],
|
||||||
obj = this._createObj( objmap[ val ] );
|
obj = this._createObj( objlevel[ val ] );
|
||||||
|
|
||||||
this._objs[ i ] = [];
|
this._objs[ i ] = [];
|
||||||
this._addObj( obj, i );
|
this._addObj( obj, i );
|
||||||
|
@ -198,14 +200,14 @@ module.exports = Class( 'MapState',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a game object from a given tile id and (optionally) a previous
|
* Creates a game object from a given tile id and (optionally) a
|
||||||
* object
|
* previous object
|
||||||
*
|
*
|
||||||
* A previous game object may be provided if state is to be transferred
|
* A previous game object may be provided if state is to be transferred
|
||||||
* between objects---that is, the transformation of one game object into
|
* between objects---that is, the transformation of one game object into
|
||||||
* another may require a certain transfer of information. If no such game
|
* another may require a certain transfer of information. If no such
|
||||||
* object is provided, then the game object will be created with its default
|
* game object is provided, then the game object will be created with
|
||||||
* state.
|
* its default state.
|
||||||
*
|
*
|
||||||
* @param {string} tid tile id
|
* @param {string} tid tile id
|
||||||
* @param {GameObject?} from optional game object for state change data
|
* @param {GameObject?} from optional game object for state change data
|
||||||
|
@ -214,9 +216,10 @@ module.exports = Class( 'MapState',
|
||||||
*/
|
*/
|
||||||
'private _createObj': function( tid, from )
|
'private _createObj': function( tid, from )
|
||||||
{
|
{
|
||||||
var obj = this._objFactory.createObject( tid );
|
const obj = this._objFactory.createObject( tid );
|
||||||
|
|
||||||
// if a previous object was provided, copy over its mutable attributes
|
// if a previous object was provided, copy over its mutable
|
||||||
|
// attributes
|
||||||
if ( from )
|
if ( from )
|
||||||
{
|
{
|
||||||
from.cloneTo( obj );
|
from.cloneTo( obj );
|
||||||
|
@ -227,7 +230,7 @@ module.exports = Class( 'MapState',
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a continuation for each game object on the map
|
* Invokes a continuation for each game object on the level
|
||||||
*
|
*
|
||||||
* The continuation will be called with the game object and its tile
|
* The continuation will be called with the game object and its tile
|
||||||
* position.
|
* position.
|
||||||
|
@ -251,7 +254,8 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Add a game object at the given tile position
|
* Add a game object at the given tile position
|
||||||
*
|
*
|
||||||
* It is an error to provide an invalid tile position or non-game object.
|
* It is an error to provide an invalid tile position or non-game
|
||||||
|
* object.
|
||||||
*
|
*
|
||||||
* @param {GameObject} obj game object to add
|
* @param {GameObject} obj game object to add
|
||||||
* @param {number} pos tile position
|
* @param {number} pos tile position
|
||||||
|
@ -273,22 +277,22 @@ module.exports = Class( 'MapState',
|
||||||
* Replaces the given game object at the given tile position with a new game
|
* Replaces the given game object at the given tile position with a new game
|
||||||
* object
|
* object
|
||||||
*
|
*
|
||||||
* If the given game object can be found at the given tile position, then it
|
* If the given game object can be found at the given tile position,
|
||||||
* will be replaced with the new given game object. If it cannot be found,
|
* then it will be replaced with the new given game object. If it cannot
|
||||||
* then it will be added at the given tile position without any replacement
|
* be found, then it will be added at the given tile position without
|
||||||
* being made (effectively an append), unless the given replacement object
|
* any replacement being made (effectively an append), unless the given
|
||||||
* is null.
|
* replacement object is null.
|
||||||
*
|
*
|
||||||
* If appending, the object will be placed in any open space (represented by
|
* If appending, the object will be placed in any open space
|
||||||
* a null); ``open'' space is created when an object is replaced with null,
|
* (represented by a null); ``open'' space is created when an object is
|
||||||
* which has the effect of removing an object entirely (with no
|
* replaced with null, which has the effect of removing an object
|
||||||
* replacement).
|
* entirely (with no replacement).
|
||||||
*
|
*
|
||||||
* The change will result in the notification of any observers that have
|
* The change will result in the notification of any observers that have
|
||||||
* registered continuations for game object state changes.
|
* registered continuations for game object state changes.
|
||||||
*
|
*
|
||||||
* It is an error to provide an unknown tile position or a replacement that
|
* It is an error to provide an unknown tile position or a replacement
|
||||||
* is not a game object.
|
* that is not a game object.
|
||||||
*
|
*
|
||||||
* @param {GameObject} cur game object to replace (or null)
|
* @param {GameObject} cur game object to replace (or null)
|
||||||
* @param {GameObject} newobj replacement game object
|
* @param {GameObject} newobj replacement game object
|
||||||
|
@ -301,8 +305,7 @@ module.exports = Class( 'MapState',
|
||||||
*/
|
*/
|
||||||
'private _replaceObj': function( cur, newobj, pos )
|
'private _replaceObj': function( cur, newobj, pos )
|
||||||
{
|
{
|
||||||
var o = this._objs[ pos ],
|
const o = this._objs[ pos ];
|
||||||
i = o.length;
|
|
||||||
|
|
||||||
// type checks
|
// type checks
|
||||||
if ( !( Array.isArray( o ) ) )
|
if ( !( Array.isArray( o ) ) )
|
||||||
|
@ -316,29 +319,27 @@ module.exports = Class( 'MapState',
|
||||||
throw TypeError( "Invalid GameObject or null provided: " + newobj );
|
throw TypeError( "Invalid GameObject or null provided: " + newobj );
|
||||||
}
|
}
|
||||||
|
|
||||||
var free = null;
|
let i = o.length,
|
||||||
|
free = null;
|
||||||
|
|
||||||
( function()
|
while ( i-- )
|
||||||
{
|
{
|
||||||
while ( i-- )
|
if ( o[ i ] === cur )
|
||||||
{
|
{
|
||||||
if ( o[ i ] === cur )
|
o[ i ] = newobj;
|
||||||
{
|
return;
|
||||||
o[ i ] = newobj;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ( o[ i ] === null )
|
|
||||||
{
|
|
||||||
// record this as a free position for additions
|
|
||||||
free = i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if ( o[ i ] === null )
|
||||||
|
{
|
||||||
|
// record this as a free position for additions
|
||||||
|
free = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// not found; add
|
// not found; add
|
||||||
if ( newobj === null ) return;
|
if ( newobj === null ) return;
|
||||||
else if ( free ) o[ i ] = newobj;
|
else if ( free ) o[ i ] = newobj;
|
||||||
else o.push( newobj );
|
else o.push( newobj );
|
||||||
} )();
|
|
||||||
|
|
||||||
// notify observers of the change
|
// notify observers of the change
|
||||||
this._emitChange( newobj, pos );
|
this._emitChange( newobj, pos );
|
||||||
|
@ -369,8 +370,8 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Move a game object from one tile position to another
|
* Move a game object from one tile position to another
|
||||||
*
|
*
|
||||||
* This has the direct effect of (a) removing the given game object from its
|
* This has the direct effect of (a) removing the given game object from
|
||||||
* original position and (b) adding it to its new position.
|
* its original position and (b) adding it to its new position.
|
||||||
*
|
*
|
||||||
* It is an error to specify an object that is not a game object, or to
|
* It is an error to specify an object that is not a game object, or to
|
||||||
* specify tile positions that do not exist.
|
* specify tile positions that do not exist.
|
||||||
|
@ -395,8 +396,8 @@ module.exports = Class( 'MapState',
|
||||||
* Initializes player game object and tile position references
|
* Initializes player game object and tile position references
|
||||||
*
|
*
|
||||||
* These references exist purely for performance, preventing the need to
|
* These references exist purely for performance, preventing the need to
|
||||||
* scan for game objects that may represent the player. This also allows for
|
* scan for game objects that may represent the player. This also allows
|
||||||
* any arbitrary game object to represent the "player".
|
* for any arbitrary game object to represent the "player".
|
||||||
*
|
*
|
||||||
* @param {GameObject} obj game object representing the player
|
* @param {GameObject} obj game object representing the player
|
||||||
* @param {number} pos player tile position
|
* @param {number} pos player tile position
|
||||||
|
@ -411,11 +412,12 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Changes the state of a game object at a given tile position
|
* Changes the state of a game object at a given tile position
|
||||||
*
|
*
|
||||||
* The "state" of a game object is represented by the object's type. If the
|
* The "state" of a game object is represented by the object's type. If
|
||||||
* state is unchanged, then no action will be taken. Otherwise, the game
|
* the state is unchanged, then no action will be taken. Otherwise, the
|
||||||
* object at the given tile position, if available, will be replaced with a
|
* game object at the given tile position, if available, will be
|
||||||
* new game object representing the given state. If a game object cannot be
|
* replaced with a new game object representing the given state. If a
|
||||||
* found at the given tile position, then it will be added.
|
* game object cannot be found at the given tile position, then it will
|
||||||
|
* be added.
|
||||||
*
|
*
|
||||||
* @param {GameObject} cur current game object
|
* @param {GameObject} cur current game object
|
||||||
* @param {string} state new object state
|
* @param {string} state new object state
|
||||||
|
@ -435,7 +437,7 @@ module.exports = Class( 'MapState',
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace game object with a new one
|
// replace game object with a new one
|
||||||
var newobj = this._createObj( state, cur );
|
const newobj = this._createObj( state, cur );
|
||||||
this._replaceObj( cur, newobj, pos );
|
this._replaceObj( cur, newobj, pos );
|
||||||
|
|
||||||
return newobj;
|
return newobj;
|
||||||
|
@ -448,8 +450,9 @@ module.exports = Class( 'MapState',
|
||||||
* Produces a continuation that will perform a state change on the given
|
* Produces a continuation that will perform a state change on the given
|
||||||
* game object. The desired state should be passed to the continuation.
|
* game object. The desired state should be passed to the continuation.
|
||||||
*
|
*
|
||||||
* If an additional continuation c is provided, it will be invoked after the
|
* If an additional continuation c is provided, it will be invoked after
|
||||||
* state change and may be used to process the resulting game object.
|
* the state change and may be used to process the resulting game
|
||||||
|
* object.
|
||||||
*
|
*
|
||||||
* @param {GameObject} cur game object to alter
|
* @param {GameObject} cur game object to alter
|
||||||
* @param {number} pos game object tile position
|
* @param {number} pos game object tile position
|
||||||
|
@ -460,11 +463,11 @@ module.exports = Class( 'MapState',
|
||||||
*/
|
*/
|
||||||
'private _createStateCallback': function( cur, pos, c )
|
'private _createStateCallback': function( cur, pos, c )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
return function( state )
|
return function( state )
|
||||||
{
|
{
|
||||||
var newobj = _self._changeState( cur, state, pos );
|
const newobj = _self._changeState( cur, state, pos );
|
||||||
|
|
||||||
if ( typeof c === 'function' )
|
if ( typeof c === 'function' )
|
||||||
{
|
{
|
||||||
|
@ -478,11 +481,11 @@ module.exports = Class( 'MapState',
|
||||||
* Creates a callback to move a game object to a new tile position
|
* Creates a callback to move a game object to a new tile position
|
||||||
*
|
*
|
||||||
* Produces a continuation that will perform a tile position move on the
|
* Produces a continuation that will perform a tile position move on the
|
||||||
* given game object. The desired direction of movement should be passed to
|
* given game object. The desired direction of movement should be passed
|
||||||
* the continuation (see MapAction for direction codes).
|
* to the continuation (see LevelAction for direction codes).
|
||||||
*
|
*
|
||||||
* If an additional continuation c is provided, it will be invoked after the
|
* If an additional continuation c is provided, it will be invoked after
|
||||||
* movement and may be used to process the new position.
|
* the movement and may be used to process the new position.
|
||||||
*
|
*
|
||||||
* @param {GameObject} cur game object to alter
|
* @param {GameObject} cur game object to alter
|
||||||
* @param {number} pos game object tile position
|
* @param {number} pos game object tile position
|
||||||
|
@ -493,7 +496,7 @@ module.exports = Class( 'MapState',
|
||||||
*/
|
*/
|
||||||
'private _createMoveCallback': function( obj, pos, c )
|
'private _createMoveCallback': function( obj, pos, c )
|
||||||
{
|
{
|
||||||
var _self = this;
|
const _self = this;
|
||||||
|
|
||||||
return function( dest )
|
return function( dest )
|
||||||
{
|
{
|
||||||
|
@ -511,23 +514,23 @@ module.exports = Class( 'MapState',
|
||||||
/**
|
/**
|
||||||
* Move a player in the given direction
|
* Move a player in the given direction
|
||||||
*
|
*
|
||||||
* The directions, as defined in MapAction, are: 0:left, 1:up, 2:right,
|
* The directions, as defined in LevelAction, are: 0:left, 1:up,
|
||||||
* 3:down.
|
* 2:right, 3:down.
|
||||||
*
|
*
|
||||||
* XXX: the bounds argument is temporary
|
* XXX: the bounds argument is temporary
|
||||||
*
|
*
|
||||||
* @param {number} direction direction code
|
* @param {number} direction direction code
|
||||||
* @param {MapBounds} bounds map boundaries
|
* @param {LevelBounds} bounds level boundaries
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
'public movePlayer': function( direction, bounds )
|
'public movePlayer': function( direction, bounds )
|
||||||
{
|
{
|
||||||
var _self = this,
|
const _self = this,
|
||||||
player = this._player;
|
player = this._player;
|
||||||
|
|
||||||
// XXX: tightly coupled
|
// XXX: tightly coupled
|
||||||
var action = MapAction(
|
const action = LevelAction(
|
||||||
bounds,
|
bounds,
|
||||||
this._createMoveCallback( player, this._playerPos, function( pos )
|
this._createMoveCallback( player, this._playerPos, function( pos )
|
||||||
{
|
{
|
||||||
|
@ -535,10 +538,14 @@ module.exports = Class( 'MapState',
|
||||||
} )
|
} )
|
||||||
);
|
);
|
||||||
|
|
||||||
var sc = this._createStateCallback( player, this._playerPos, function( o )
|
const sc = this._createStateCallback(
|
||||||
{
|
player,
|
||||||
_self._player = o;
|
this._playerPos,
|
||||||
} );
|
function( o )
|
||||||
|
{
|
||||||
|
_self._player = o;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
action.direction = direction;
|
action.direction = direction;
|
||||||
action.srcPos = this._playerPos;
|
action.srcPos = this._playerPos;
|
|
@ -17,7 +17,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Class = require( 'easejs' ).Class;
|
const Class = require( 'easejs' ).Class;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,16 +68,15 @@ module.exports = Class( 'MenuBar',
|
||||||
*/
|
*/
|
||||||
'private _initMenuActivation': function()
|
'private _initMenuActivation': function()
|
||||||
{
|
{
|
||||||
var _self = this,
|
const id = this._bar.id,
|
||||||
id = this._bar.id,
|
menus = this._bar.parentNode.querySelectorAll( '#'+id+' > li > a' ),
|
||||||
menus = this._bar.parentNode.querySelectorAll( '#'+id+' > li > a' ),
|
click = function( event )
|
||||||
i = menus.length,
|
{
|
||||||
|
event.target.parentNode.parentNode.className += ' focus';
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
click = function( event )
|
let i = menus.length;
|
||||||
{
|
|
||||||
event.target.parentNode.parentNode.className += ' focus';
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// on menu click, apply focus class (this allows the menu to be opened
|
// on menu click, apply focus class (this allows the menu to be opened
|
||||||
// properly on click rather than a simple CSS hover menu)
|
// properly on click rather than a simple CSS hover menu)
|
||||||
|
@ -103,8 +102,8 @@ module.exports = Class( 'MenuBar',
|
||||||
*/
|
*/
|
||||||
'private _hookMenuMouseOut': function()
|
'private _hookMenuMouseOut': function()
|
||||||
{
|
{
|
||||||
var _self = this,
|
const _self = this,
|
||||||
bar = this._bar;
|
bar = this._bar;
|
||||||
|
|
||||||
this._bar.addEventListener( 'mouseout', function( event )
|
this._bar.addEventListener( 'mouseout', function( event )
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var major = @MAJOR@,
|
let major = @MAJOR@,
|
||||||
minor = @MINOR@,
|
minor = @MINOR@,
|
||||||
rev = @REV@,
|
rev = @REV@,
|
||||||
suffix = '@SUFFIX@',
|
suffix = '@SUFFIX@',
|
||||||
|
|
|
@ -73,14 +73,14 @@
|
||||||
Your browser does not support the canvas element.
|
Your browser does not support the canvas element.
|
||||||
</canvas>
|
</canvas>
|
||||||
|
|
||||||
<canvas id="canvas_map" width="512" height="512"></canvas>
|
<canvas id="canvas_level" width="512" height="512"></canvas>
|
||||||
|
|
||||||
<script src="../lasertank.js"></script>
|
<script src="../lasertank.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var ctx = document.getElementById( 'canvas' ).getContext( '2d' ),
|
var ctx = document.getElementById( 'canvas' ).getContext( '2d' ),
|
||||||
ctxmap = document.getElementById( 'canvas_map' ).getContext( '2d' ),
|
ctxlevel = document.getElementById( 'canvas_level' ).getContext( '2d' ),
|
||||||
ltg = document.getElementById( 'ltg' ),
|
ltg = document.getElementById( 'ltg' ),
|
||||||
lvl = document.getElementById( 'lvl' ),
|
lvl = document.getElementById( 'lvl' ),
|
||||||
|
|
||||||
render = null,
|
render = null,
|
||||||
|
|
||||||
|
@ -104,8 +104,11 @@
|
||||||
reader.onload = function( event )
|
reader.onload = function( event )
|
||||||
{
|
{
|
||||||
var loader = lasertank.LtgLoader(),
|
var loader = lasertank.LtgLoader(),
|
||||||
masker = lasertank.TileMasker( lasertank.ClassicTileDfn() ),
|
|
||||||
meta = loader.fromString( event.target.result ),
|
meta = loader.fromString( event.target.result ),
|
||||||
|
masker = lasertank.TileMasker(
|
||||||
|
lasertank.ClassicTileDfn(),
|
||||||
|
document
|
||||||
|
),
|
||||||
|
|
||||||
bmp_game = document.getElementById( 'bmp_game' ),
|
bmp_game = document.getElementById( 'bmp_game' ),
|
||||||
bmp_mask = document.getElementById( 'bmp_mas' );
|
bmp_mask = document.getElementById( 'bmp_mas' );
|
||||||
|
@ -175,8 +178,8 @@
|
||||||
}
|
}
|
||||||
}, 200 );
|
}, 200 );
|
||||||
|
|
||||||
// trigger map change (in case of page refresh or tile set change
|
// trigger level change (in case of page refresh or tile set change
|
||||||
// after map is already loaded)
|
// after level is already loaded)
|
||||||
lvlChange();
|
lvlChange();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -201,18 +204,18 @@
|
||||||
|
|
||||||
reader.onload = function( event )
|
reader.onload = function( event )
|
||||||
{
|
{
|
||||||
var map_set = lasertank.MapSet(
|
var level_set = lasertank.level.LevelSet(
|
||||||
event.target.result,
|
event.target.result,
|
||||||
lasertank.ClassicMap
|
lasertank.level.ClassicLevel
|
||||||
);
|
);
|
||||||
|
|
||||||
// clean up any previous render and create a new one to render the
|
// clean up any previous render and create a new one to render the
|
||||||
// chosen level with our chosen tile set
|
// chosen level with our chosen tile set
|
||||||
render && render.freeCanvas();
|
render && render.freeCanvas();
|
||||||
render = lasertank.MapRender( ctxmap, tile_set );
|
render = lasertank.level.LevelRender( ctxlevel, tile_set );
|
||||||
|
|
||||||
var lvlsel = document.getElementById( 'lvl_id' ),
|
var lvlsel = document.getElementById( 'lvl_id' ),
|
||||||
count = map_set.getMapCount();
|
count = level_set.getLevelCount();
|
||||||
|
|
||||||
lvlsel.innerHTML = '';
|
lvlsel.innerHTML = '';
|
||||||
for ( var i = 1; i <= count; i++ )
|
for ( var i = 1; i <= count; i++ )
|
||||||
|
@ -228,23 +231,23 @@
|
||||||
|
|
||||||
function lvlchange()
|
function lvlchange()
|
||||||
{
|
{
|
||||||
var map = map_set.getMapByNumber( lvlsel.selectedIndex + 1 );
|
var level = level_set.getLevelByNumber( lvlsel.selectedIndex + 1 );
|
||||||
render.render(
|
render.render(
|
||||||
map,
|
level,
|
||||||
lasertank.MapState(
|
lasertank.level.LevelState(
|
||||||
map,
|
level,
|
||||||
lasertank.ClassicGameObjectFactory()
|
lasertank.ClassicGameObjectFactory()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
document.getElementById( 'lvl_name' ).innerHTML =
|
document.getElementById( 'lvl_name' ).innerHTML =
|
||||||
map.getMapName();
|
level.getLevelName();
|
||||||
document.getElementById( 'lvl_author' ).innerHTML =
|
document.getElementById( 'lvl_author' ).innerHTML =
|
||||||
map.getMapAuthor();
|
level.getLevelAuthor();
|
||||||
document.getElementById( 'lvl_diff' ).innerHTML =
|
document.getElementById( 'lvl_diff' ).innerHTML =
|
||||||
map.getMapDifficulty();
|
level.getLevelDifficulty();
|
||||||
document.getElementById( 'lvl_hint' ).innerHTML =
|
document.getElementById( 'lvl_hint' ).innerHTML =
|
||||||
map.getMapHint();
|
level.getLevelHint();
|
||||||
};
|
};
|
||||||
|
|
||||||
lvlchange();
|
lvlchange();
|
||||||
|
|
Loading…
Reference in New Issue