From b6ef9cba15d64014bb01093bb8f70c280e8880c8 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sun, 20 Jan 2013 10:20:51 -0500 Subject: [PATCH] Player movement restricted to map boundaries This is an initial working concept; refactoring is certainly needed (in particular, note the MapStae.movePlayer() 'bounds' argument). --- index.html | 1 + lib/ClassicGame.js | 8 +- lib/MapAction.js | 23 +++++- lib/MapBounds.js | 190 +++++++++++++++++++++++++++++++++++++++++++++ lib/MapState.js | 23 +++--- 5 files changed, 226 insertions(+), 19 deletions(-) create mode 100644 lib/MapBounds.js diff --git a/index.html b/index.html index 648996a..05a005d 100644 --- a/index.html +++ b/index.html @@ -130,6 +130,7 @@ + diff --git a/lib/ClassicGame.js b/lib/ClassicGame.js index 57cc674..79f7aa5 100644 --- a/lib/ClassicGame.js +++ b/lib/ClassicGame.js @@ -115,9 +115,9 @@ ltjs.ClassicGame = Class( 'ClassicGame' ) this._render.clearCanvas(); } - var map = this._mapSet.getMapByNumber( 1 ); - - var map_state = ltjs.MapState( map, this._gameObjFactory ); + var map = this._mapSet.getMapByNumber( 1 ), + map_state = ltjs.MapState( map, this._gameObjFactory ), + bounds = ltjs.MapBounds( map ); // render the first map (hardcoded for now) this._render = ltjs.MapRender( ctx, this._tileSet ) @@ -141,7 +141,7 @@ ltjs.ClassicGame = Class( 'ClassicGame' ) return; } - map_state.movePlayer( dir ); + map_state.movePlayer( dir, bounds ); }; return this; diff --git a/lib/MapAction.js b/lib/MapAction.js index 8dd1354..a36f46c 100644 --- a/lib/MapAction.js +++ b/lib/MapAction.js @@ -29,15 +29,25 @@ ltjs.MapAction = Class( 'MapAction', 'private _dir': 0, + 'private _srcPos': 0, + 'public srcPos': 0, + + 'private _bounds': null, 'private _stateCallback': null, 'private _moveCallback': null, - __construct: function( state_callback, move_callback ) + __construct: function( bounds, state_callback, move_callback ) { + if ( !( Class.isA( ltjs.MapBounds, bounds ) ) ) + { + throw TypeError( 'Invalid MapBounds provided' ); + } + this._dir = this.__self.$( 'D_UP' ); + this._bounds = bounds; this._stateCallback = state_callback; this._moveCallback = move_callback; }, @@ -51,7 +61,16 @@ ltjs.MapAction = Class( 'MapAction', 'public move': function() { - this._moveCallback( this._dir ); + var method = [ + 'getLeftPos', + 'getUpperPos', + 'getRightPos', + 'getLowerPos', + ][ this._dir ]; + + this._moveCallback( + this._bounds[ method ].call( this._bounds, this.srcPos ) + ); }, diff --git a/lib/MapBounds.js b/lib/MapBounds.js new file mode 100644 index 0000000..6f9797c --- /dev/null +++ b/lib/MapBounds.js @@ -0,0 +1,190 @@ +/** + * Handles map boundaries for collision detection and movement + * + * Copyright (C) 2012 Mike Gerwitz + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +/** + * Calculates map bounding box + * + * This simply encapsulates the process of determining whether a given position + * is against an edge of the map. + */ +ltjs.MapBounds = Class( 'MapBounds', +{ + /** + * Map width (number of tiles) + * @type {number} + */ + 'private _mw': 0, + + /** + * Map height (number of tiles) + * @type {number} + */ + 'private _mh': 0, + + + /** + * Initialize bounding box for a given map + * + * @param {Map} map map for which bounds should be calculated + */ + __construct: function( map ) + { + if ( !( Class.isA( ltjs.Map, map ) ) ) + { + throw TypeError( 'Invalid Map provided' ); + } + + // we are only interested in the dimensions of the map + var dimen = map.getDimensions(); + this._mw = dimen[ 0 ]; + this._mh = dimen[ 1 ]; + }, + + + /** + * Retrieve the tile position above the given position + * + * If the given tile position is at the top of the map, then the given + * position will be returned. + * + * @param {number} pos original tile position + * + * @return {number} tile position above given position or current position + */ + 'public getUpperPos': function( pos ) + { + return ( this.isAtTop( pos ) ) + ? pos + : pos - 1; + }, + + + /** + * Retrieve the tile position below the given position + * + * If the given tile position is at the bottom of the map, then the given + * position will be returned. + * + * @param {number} pos original tile position + * + * @return {number} tile position below given position or current position + */ + 'public getLowerPos': function( pos ) + { + return ( this.isAtBottom( pos ) ) + ? pos + : pos + 1; + }, + + + /** + * 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 + * given position will be returned. + * + * @param {number} pos original tile position + * + * @return {number} tile position left of given position or current + * position + */ + 'public getLeftPos': function( pos ) + { + return ( this.isAtLeft( pos ) ) + ? pos + : pos - this._mh; + }, + + + /** + * 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 + * the given position will be returned. + * + * @param {number} pos original tile position + * + * @return {number} tile position right of given position or current + * position + */ + 'public getRightPos': function( pos ) + { + return ( this.isAtRight( pos ) ) + ? pos + : pos + this._mh; + }, + + + /** + * Determines if the given position is in the topmost row of the map + * + * @param {number} pos tile position + * + * @return {boolean} true if within topmost row, otherwise false + */ + 'public isAtTop': function( pos ) + { + // since tile positions are zero-indexed, we know that we're at the top + // if the map height divides the position + return ( pos % this._mh === 0 ); + }, + + + /** + * Determines if the given position is in the bottom row of the map + * + * @param {number} pos tile position + * + * @return {boolean} true if within bottom row, otherwise false + */ + 'public isAtBottom': function( pos ) + { + // this "works" because tile positions are vertically indexed + return this.isAtTop( pos + 1 ); + }, + + + /** + * Determines if the given position is in the leftmost column of the map + * + * @param {number} pos tile position + * + * @return {boolean} true if within leftmost column, otherwise false + */ + 'public isAtLeft': function( pos ) + { + // check if index is within the left-most column + return ( pos <= this._mh ); + }, + + + /** + * Determines if the given position is in the rightmost column of the map + * + * @param {number} pos tile position + * + * @return {boolean} true if within rightmost column, otherwise false + */ + 'public isAtRight': function( pos ) + { + // check if index is within the right-most column + return ( pos >= ( this._mh * ( this._mw - 1 ) ) ); + }, +} ); diff --git a/lib/MapState.js b/lib/MapState.js index ada6865..af67506 100644 --- a/lib/MapState.js +++ b/lib/MapState.js @@ -437,13 +437,6 @@ ltjs.MapState = Class( 'MapState', }, - // TODO - 'private _calcPos': function( pos, dir ) - { - return pos - 1; - }, - - /** * Creates a callback to alter the state of an object * @@ -497,15 +490,14 @@ ltjs.MapState = Class( 'MapState', { var _self = this; - return function( dir ) + return function( dest ) { - var newpos = _self._calcPos( pos, dir ); - _self._moveObj( obj, pos, newpos ); + _self._moveObj( obj, pos, dest ); // call continuation with new position, if requested if ( typeof c === 'function' ) { - c( newpos ); + c( dest ); } }; }, @@ -517,17 +509,21 @@ ltjs.MapState = Class( 'MapState', * The directions, as defined in MapAction, are: 0:left, 1:up, 2:right, * 3:down. * - * @param {number} direction direction code + * XXX: the bounds argument is temporary + * + * @param {number} direction direction code + * @param {MapBounds} bounds map boundaries * * @return {undefined} */ - 'public movePlayer': function( direction ) + 'public movePlayer': function( direction, bounds ) { var _self = this, player = this._player; // XXX: tightly coupled var action = ltjs.MapAction( + bounds, this._createStateCallback( player, this._playerPos, function( o ) { _self._player = o; @@ -540,6 +536,7 @@ ltjs.MapState = Class( 'MapState', ); action.direction = direction; + action.srcPos = this._playerPos; player.move( action ); } } );