/** * Represents a classic map (level) * * 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 . * * * Each map is concatenated in the file and consists of the following * information: * * - Playfield data (game objects), 16x16 multidimensional char array * - Level name, 31 characters * - Hint, 256 characters * - Author, 31 characters * - Difficulty, 16-bit integer, little endian * * 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 * in the original sources). * * 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 * "array of columns", meaning that the data is organized in columns instead of * rows. For example, when viewing the data in a HEX editor that displays 16 * bytes per line (e.g. xxd), the map would appear to be mirrored rotated 90 * degrees counter-clockwise. To make it easier to visualize, one can create a * map with a number of tunnels in a pattern to take advantage of the ASCII * display. */ /** * Represents a classic map, as they exist in the original game. * * Classic maps are 16x16 tiles in size (for a total of 256 tiles). */ ltjs.ClassicMap = Class( 'ClassicMap' ) .implement( ltjs.Map ) .extend( { 'private const _SIZE': 590, /** * Game object size * * The game objects are stores as a 16x16 multi-dimensional signed char * array (in the C sources). * * @type {number} */ 'private const _GOSIZE': 256, /** * Map set data (binary string) * @type {string} */ 'private _data': null, /** * Map id (1-indexed) * @type {string} */ 'private _id': 0, /** * Initialize map with map data and the given id * * @param {ltjs.MapSet} set map set data * @param {number} id 1-indexed map id */ __construct: function( data, id ) { this._data = ''+( data ); this._id = +id; }, /** * Retrieve game objects * * The game objects are returned in a manner consistent with the original * sources - in columns, not rows. The reason for this is that the original * game uses a multi-dimensional array [x][y], which creates an array of * columns (TPLAYFIELD, LTANK.H). * * @return {Array.} array of game objects */ 'public getObjects': function() { // calculate offset in LVL file var offset = ( this.__self.$( '_SIZE' ) * ( this._id - 1 ) ); return this._getMapData( offset ); }, /** * Retrieve the map data (game objects only) at the requested offset * * The object data at the requested position will be loaded and converted to * integers (from a binary string). * * @param {number} offset offset of the requested map data * * @return {Array.} converted map data */ 'private _getMapData': function( offset ) { var tiles = this._data.substr( offset, this.__self.$( '_GOSIZE' ) ) .split( '' ), i = tiles.length; while ( i-- ) { tiles[ i ] = tiles[ i ].charCodeAt( 0 ); } return tiles; }, /** * Retrieve map dimensions * * @return {Array.} width and height in tiles */ 'public getDimensions': function() { return [ 16, 16 ]; } } );