/* micropolisJS. Adapted by Graeme McCutcheon from Micropolis. * * This code is released under the GNU GPL v3, with some additional terms. * Please see the files LICENSE and COPYING for details. Alternatively, * consult http://micropolisjs.graememcc.co.uk/LICENSE and * http://micropolisjs.graememcc.co.uk/COPYING * */ Micro.TERRAIN_CREATE_ISLAND = 0; Micro.TERRAIN_TREE_LEVEL = -1;//level for tree creation Micro.TERRAIN_LAKE_LEVEL = -1; //level for river curviness; -1==auto, 0==none, >0==level Micro.TERRAIN_CURVE_LEVEL = -1;//level for lake creation; -1==auto, 0==none, >0==level Micro.ISLAND_RADIUS = 18; Micro.generateMap = function() { // w = w || Micro.MAP_WIDTH; // h = h || Micro.MAP_HEIGHT; this.SRMatrix = [ [ 0, 0, 3, 3, 0, 0 ], [ 0, 3, 2, 2, 3, 0 ], [ 3, 2, 2, 2, 2, 3 ], [ 3, 2, 2, 2, 2, 3 ], [ 0, 3, 2, 2, 3, 0 ], [ 0, 0, 3, 3, 0, 0 ] /*[0, 0, Tile.REDGE, Tile.REDGE, 0, 0], [0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0], [Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE], [Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE], [0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0], [0, 0, Tile.REDGE, Tile.REDGE, 0, 0]*/ ]; this.BRMatrix = [ [ 0, 0, 0, 3, 3, 3, 0, 0, 0 ], [ 0, 0, 3, 2, 2, 2, 3, 0, 0 ], [ 0, 3, 2, 2, 2, 2, 2, 3, 0 ], [ 3, 2, 2, 2, 2, 2, 2, 2, 3 ], [ 3, 2, 2, 2, 4, 2, 2, 2, 3 ], [ 3, 2, 2, 2, 2, 2, 2, 2, 3 ], [ 0, 3, 2, 2, 2, 2, 2, 3, 0 ], [ 0, 0, 3, 2, 2, 2, 3, 0, 0 ], [ 0, 0, 0, 3, 3, 3, 0, 0, 0 ] /*[0, 0, 0, Tile.REDGE, Tile.REDGE, Tile.REDGE, 0, 0, 0], [0, 0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0, 0], [0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0], [Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE], [Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.CHANNEL, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE], [Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE], [0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0], [0, 0, Tile.REDGE, Tile.RIVER, Tile.RIVER, Tile.RIVER, Tile.REDGE, 0, 0], [0, 0, 0, Tile.REDGE, Tile.REDGE, Tile.REDGE, 0, 0, 0]*/ ]; this.riverEdges = [ 13, 13, 17, 15, 5 , 2 , 19, 17, 9 , 11, 2 , 13, 7 , 9 , 5 , 2/* 13 | Tile.BULLBIT, 13 | Tile.BULLBIT, 17 | Tile.BULLBIT, 15 | Tile.BULLBIT, 5 | Tile.BULLBIT, 2, 19 | Tile.BULLBIT, 17 | Tile.BULLBIT, 9 | Tile.BULLBIT, 11 | Tile.BULLBIT, 2, 13 | Tile.BULLBIT, 7 | Tile.BULLBIT, 9 | Tile.BULLBIT, 5 | Tile.BULLBIT, 2*/ ]; //this.map = new Micro.GameMap(w, h); // Construct land. /*if (Micro.TERRAIN_CREATE_ISLAND < 0) { if (Random.getRandom(100) < 10) { this.makeIsland(); return this.map; } } if (Micro.TERRAIN_CREATE_ISLAND === 1) this.makeNakedIsland(); else this.clearMap(); // Lay a river. if (Micro.TERRAIN_CURVE_LEVEL !== 0) { var terrainXStart = 40 + Random.getRandom(this.map.width - 80); var terrainYStart = 33 + Random.getRandom(this.map.height - 67); var terrainPos = new this.map.Position(terrainXStart, terrainYStart); this.doRivers(terrainPos); } // Lay a few lakes. if (Micro.TERRAIN_LAKE_LEVEL !== 0) this.makeLakes(); this.smoothRiver(); // And add trees. if (Micro.TERRAIN_TREE_LEVEL !== 0) this.doTrees(); return this.map;*/ }; Micro.generateMap.prototype = { constructor: Micro.generateMap, construct : function( w, h ){ Micro.TERRAIN_TREE_LEVEL = -1; Micro.TERRAIN_LAKE_LEVEL = -1; Micro.TERRAIN_CURVE_LEVEL = -1; Micro.ISLAND_RADIUS = 18; //console.time("start newmap"); this.map = new Micro.GameMap( w || Micro.MAP_WIDTH, h || Micro.MAP_HEIGHT ); //this.makeIsland(); //this.makeLand(); //return this.map; Micro.TERRAIN_CREATE_ISLAND = Random.getRandom(2) - 1; if (Micro.TERRAIN_CREATE_ISLAND < 0) { if (Random.getRandom(100) < 10) { this.makeIsland(); return this.map; } } if (Micro.TERRAIN_CREATE_ISLAND === 1) this.makeNakedIsland(); else this.clearMap(); // Lay a river. if (Micro.TERRAIN_CURVE_LEVEL !== 0) { var terrainXStart = 40 + Random.getRandom( this.map.width - 79 ); var terrainYStart = 33 + Random.getRandom( this.map.height - 66 ); var terrainPos = new this.map.Position( terrainXStart, terrainYStart ); this.doRivers( terrainPos ); } // Lay a few lakes. if (Micro.TERRAIN_LAKE_LEVEL !== 0) this.makeLakes(); //this.smoothWater(); this.smoothRiver(); // And add trees. if (Micro.TERRAIN_TREE_LEVEL !== 0) this.doTrees(); //console.timeEnd("start newmap"); return this.map; }, clearMap : function() { var x, y, W = this.map.width, H = this.map.height; for ( x = 0; x < W; x++) { for ( y = 0; y < H; y++) { //this.map.setTo(x, y, new Micro.Tile(Tile.DIRT)); this.map.setTile(x, y, Tile.DIRT, 0); } } }, clearUnnatural : function() { var x, y, tileValue, W = this.map.width, H = this.map.height; for ( x = 0; x < W; x++) { for ( y = 0; y < H; y++) { tileValue = this.map.getTileValue(x, y); if (tileValue > Tile.WOODS) this.map.setTile(x, y, Tile.DIRT, 0); //this.map.setTo( x, y, new Micro.Tile( Tile.DIRT ) ); } } }, makeNakedIsland : function() { this.map.isIsland = true; var terrainIslandRadius = Micro.ISLAND_RADIUS; var x, y, mapX, mapY, W = this.map.width, H = this.map.height; for ( y = 0; y < H; y++) { for ( x = 0; x < W; x++) { //map[y][x] = RIVER; //this.map.setTo(x, y, new Micro.Tile(Tile.RIVER)); this.map.setTile(x, y, Tile.RIVER, 0); } } for ( y = 5; y < H - 5; y++) { for ( x = 5; x < W - 5; x++) { //map[y][x] = DIRT; //this.map.setTo(x, y, new Micro.Tile(Tile.DIRT)); this.map.setTile(x, y, Tile.DIRT, 0); } } /*for ( x = 0; x < W; x++ ) { for (y = 0; y < H; y++) { if ((x < 5) || (x >= W - 5) || (y < 5) || (y >= H - 5)) { //this.map.setTile(x, y, Tile.RIVER, 0);// this.map.setTo(x, y, new Micro.Tile(Tile.RIVER)); } else { //this.map.setTile(x, y, Tile.DIRT, 0);/// this.map.setTo(x, y, new Micro.Tile(Tile.DIRT)); } } }*/ for (x = 0; x < W - 5; x += 2) { mapY = Random.getERandom(terrainIslandRadius+1); this.plopBRiver(new this.map.Position(x, mapY)); mapY = (H - 10) - Random.getERandom(terrainIslandRadius+1); this.plopBRiver( new this.map.Position( x, mapY )) this.plopSRiver( new this.map.Position( x, 0 )); this.plopSRiver( new this.map.Position( x, H - 6 )); } for ( y = 0; y < H - 5; y += 2 ) { mapX = Random.getERandom( terrainIslandRadius+1 ); this.plopBRiver( new this.map.Position( mapX, y )); mapX = W - 10 - Random.getERandom(terrainIslandRadius+1); this.plopBRiver( new this.map.Position( mapX, y )); this.plopSRiver( new this.map.Position( 0, y )); this.plopSRiver( new this.map.Position( W - 6, y )); } }, /*makeLand : function() { console.time("start land"); if (Micro.TERRAIN_CURVE_LEVEL !== 0) { var terrainXStart = 40 + Random.getRandom( this.map.width - 80 ); var terrainYStart = 33 + Random.getRandom( this.map.height - 67 ); var terrainPos = new this.map.Position( terrainXStart, terrainYStart ); console.time("start river"); this.doRivers( terrainPos ); console.timeEnd("start river"); } // Lay a few lakes. console.time("start lake"); if (Micro.TERRAIN_LAKE_LEVEL !== 0) this.makeLakes(); console.timeEnd("start lake"); console.time("start smoothWater"); this.smoothWater(); console.timeEnd("start smoothWater"); console.time("start smoothRiver"); this.smoothRiver(); console.timeEnd("start smoothRiver"); // And add trees. console.time("start tree"); if (Micro.TERRAIN_TREE_LEVEL !== 0) this.doTrees(); console.timeEnd("start tree") console.timeEnd("start land"); },*/ makeIsland : function() { //console.time("start island"); this.makeNakedIsland(); //this.smoothWater(); this.smoothRiver(); this.doTrees(); //console.timeEnd("start island"); }, makeLakes : function() { var x, y, W = this.map.width, H = this.map.height; var numLakes; if (Micro.TERRAIN_LAKE_LEVEL < 0) numLakes = Random.getRandom(11); else numLakes = Micro.TERRAIN_LAKE_LEVEL * 0.5; while (numLakes > 0) { x = Random.getRandom( W - 20) + 10; y = Random.getRandom( H - 19) + 10; this.makeSingleLake( new this.map.Position(x, y) ); numLakes--; } }, makeSingleLake : function( pos ) { var numPlops = Random.getRandom(13) + 2; while (numPlops > 0) { var plopPos = new this.map.Position(pos, Random.getRandom(13) - 6, Random.getRandom(13) - 6); if (Random.getRandom(5)) this.plopSRiver(plopPos); else this.plopBRiver( plopPos ); numPlops--; } }, treeSplash : function( x, y ) { var numTrees; if (Micro.TERRAIN_TREE_LEVEL < 0) numTrees = Random.getRandom(150) + 50; else numTrees = Random.getRandom(100 + (Micro.TERRAIN_TREE_LEVEL * 2)) + 50; var treePos = new this.map.Position(x, y); while (numTrees > 0) { var dir = Direction.NORTH + Random.getRandom(7); treePos.move( dir ); // XXX Should use the fact that positions return success/failure for moves if (!this.map.testBounds(treePos.x, treePos.y)) return; if ( this.map.getTileValue(treePos) === Tile.DIRT ){ //this.map.setTo(treePos, new Micro.Tile( Tile.WOODS, Tile.BLBNBIT )); this.map.setTile(treePos, Tile.WOODS, Tile.BLBNBIT); } numTrees--; } }, doTrees : function() { var i, x, y , amount, W = this.map.width, H = this.map.height; if (Micro.TERRAIN_TREE_LEVEL < 0) amount = Random.getRandom(100) + 50; else amount = Micro.TERRAIN_TREE_LEVEL + 3; for ( i = 0; i < amount; i++) { x = Random.getRandom( W - 1); y = Random.getRandom( H - 1); this.treeSplash( x, y ); } this.smoothTrees(); this.smoothTrees(); }, smoothRiver : function() { var x, y, z, W = this.map.width, H = this.map.height, xt, yt, tt, tb; var dx = [-1, 0, 1, 0]; var dy = [0, 1, 0, -1]; for ( x = 0; x < W; x++) { for ( y = 0; y < H; y++) { if (this.map.getTileValue(x, y) === Tile.REDGE) { var bitIndex = 0; for ( z = 0; z < 4; z++) { bitIndex = bitIndex << 1; xt = x + dx[z]; yt = y + dy[z]; tb = this.map.testBounds( xt, yt ); if(tb){ tt = this.map.getTileValue( xt, yt ); if( tt !== Tile.DIRT && ( tt < Tile.WOODS_LOW || tt > Tile.WOODS_HIGH ) ) bitIndex++; } //if (this.map.testBounds(xTemp, yTemp) && this.map.getTileValue(xTemp, yTemp) !== Tile.DIRT && (this.map.getTileValue(xTemp, yTemp) < Tile.WOODS_LOW || this.map.getTileValue(xTemp, yTemp) > Tile.WOODS_HIGH)) { // bitIndex++; //} } var temp = this.riverEdges[bitIndex & 15]; if ( temp !== Tile.RIVER && Random.getRandom(1) ){ temp++; } //if ( temp !== Tile.RIVER ){ temp++; } //this.map.setTo(x, y, new Micro.Tile(temp)); //this.map.setTile( x, y, temp ); if(temp !== 2 ) this.map.setTile( x, y, temp | Tile.BULLBIT ); else this.map.setTile( x, y, temp ); //this.map.setTileValue(x, y, temp, 0); } } } }, isTree : function(tileValue) { return tileValue >= Tile.WOODS_LOW && tileValue <= Tile.WOODS_HIGH; }, smoothTrees : function() { var x, y, W = this.map.width, H = this.map.height; for ( x = 0; x < W; x++) { for ( y = 0; y < H; y++) { if (this.isTree(this.map.getTileValue(x, y))) this.smoothTreesAt( x, y, false); } } }, smoothTreesAt : function(x, y, preserve) { var dx = [-1, 0, 1, 0 ]; var dy = [ 0, 1, 0, -1 ]; var treeTable = [ 0, 0, 0, 34, 0, 0, 36, 35, 0, 32, 0, 33, 30, 31, 29, 37 ]; if (!this.isTree(this.map.getTileValue(x, y))) return; var bitIndex = 0; for (var i = 0; i < 4; i++) { bitIndex = bitIndex << 1; var xTemp = x + dx[i]; var yTemp = y + dy[i]; if (this.map.testBounds(xTemp, yTemp) && this.isTree(this.map.getTileValue(xTemp, yTemp))) bitIndex++; } var temp = treeTable[ bitIndex & 15 ]; if (temp) { if (temp !== Tile.WOODS) { if ((x + y) & 1) temp -= 8; } //this.map.setTo(x, y, new Micro.Tile(temp, Tile.BLBNBIT)); this.map.setTile(x, y, temp, Tile.BLBNBIT); } else { if (!preserve) this.map.setTo(x, y, new Micro.Tile(temp)); //if (!preserve) this.map.setTile(x, y, temp, 0); //if (!preserve) this.map.setTileValue(x, y, temp, 0); } }, doRivers : function(terrainPos) { var riverDir = Direction.NORTH + Random.getRandom(3) * 2; this.doBRiver( terrainPos, riverDir, riverDir ); riverDir = Direction.rotate180(riverDir); var terrainDir = this.doBRiver( terrainPos, riverDir, riverDir ); riverDir = Direction.NORTH + Random.getRandom(3) * 2; this.doSRiver( terrainPos, riverDir, terrainDir ); }, doBRiver : function( riverPos, riverDir, terrainDir) { var rate1, rate2; if (Micro.TERRAIN_CURVE_LEVEL < 0) { rate1 = 100; rate2 = 200; } else { rate1 = Micro.TERRAIN_CURVE_LEVEL + 10; rate2 = Micro.TERRAIN_CURVE_LEVEL + 100; } var pos = new this.map.Position(riverPos); while (this.map.testBounds(pos.x + 4, pos.y + 4)) { this.plopBRiver(pos); if (Random.getRandom(rate1+1) < 10) { terrainDir = riverDir; } else { if (Random.getRandom(rate2+1) > 90) terrainDir = Direction.rotate45(terrainDir); if (Random.getRandom(rate2+1) > 90) terrainDir = Direction.rotate45(terrainDir, 7); } pos.move(terrainDir); } return terrainDir; }, doSRiver : function(riverPos, riverDir, terrainDir) { var rate1, rate2; if (Micro.TERRAIN_CURVE_LEVEL < 0) { rate1 = 100; rate2 = 200; } else { rate1 = Micro.TERRAIN_CURVE_LEVEL + 10; rate2 = Micro.TERRAIN_CURVE_LEVEL + 100; } var pos = new this.map.Position(riverPos); while (this.map.testBounds(pos.x + 3, pos.y + 3)) { this.plopSRiver(pos); if (Random.getRandom(rate1+1) < 10) { terrainDir = riverDir; } else { if (Random.getRandom(rate2+1) > 90) terrainDir = Direction.rotate45(terrainDir); if (Random.getRandom(rate2+1) > 90) terrainDir = Direction.rotate45(terrainDir, 7); } pos.move(terrainDir); } return terrainDir; }, putOnMap : function(newVal, x, y) { if (newVal === 0) return; if (!this.map.testBounds(x, y)) return; var tmp = this.map.getTileValue(x, y); if (tmp !== Tile.DIRT) { if (tmp === Tile.RIVER && newVal !== Tile.CHANNEL) return; if (tmp === Tile.CHANNEL) return; } //this.map.setTo(x, y, new Micro.Tile(newVal)); this.map.setTile(x, y, newVal, 0); }, plopBRiver : function(pos) { for (var x = 0; x < 9; x++) { for (var y = 0; y < 9; y++) { this.putOnMap(this.BRMatrix[y][x], pos.x + x, pos.y + y); } } }, plopSRiver : function(pos) { for (var x = 0; x < 6; x++) { for (var y = 0; y < 6; y++) { this.putOnMap(this.SRMatrix[y][x], pos.x + x, pos.y + y); } } }, smoothWater : function() { var x, y, tile, pos, dir, W = this.map.width, H = this.map.height; for (x = 0; x < W; x++) { for (y = 0; y < H; y++) { tile = this.map.getTileValue(x, y); // if (tile >= Tile.WATER_Tile.LOW && tile <= Tile.WATER_Tile.HIGH) { if (tile >= Tile.WATER_LOW && tile <= Tile.WATER_HIGH) { pos = new this.map.Position(x, y); for (dir = Direction.BEGIN; dir < Direction.END; dir = Direction.increment90(dir)) { tile = this.map.getTileFromMapOrDefault(pos, dir, Tile.WATER_LOW); // If nearest object is not water: if (tile < Tile.WATER_LOW || tile > Tile.WATER_HIGH) { //this.map.setTo(x, y, new Micro.Tile(Tile.REDGE)); // set river edge this.map.setTile(x, y, Tile.REDGE, 0); break; // Continue with next tile } } } } } for (x = 0; x < W; x++) { for (y = 0; y < H; y++) { tile = this.map.getTileValue(x, y); if (tile !== Tile.CHANNEL && tile >= Tile.WATER_LOW && tile <= Tile.WATER_HIGH) { var makeRiver = true; pos = new this.map.Position(x, y); for (dir = Direction.BEGIN; dir < Direction.END; dir = Direction.increment90(dir)) { tile = this.map.getTileFromMapOrDefault(pos, dir, Tile.WATER_LOW); if (tile < Tile.WATER_LOW || tile > Tile.WATER_HIGH) { makeRiver = false; break; } } //if (makeRiver) this.map.setTo(x, y, new Micro.Tile(Tile.RIVER)); if (makeRiver) this.map.setTile(x, y, Tile.RIVER, 0); } } } for (x = 0; x < W; x++) { for (y = 0; y < H; y++) { tile = this.map.getTileValue(x, y); if (tile >= Tile.WOODS_LOW && tile <= Tile.WOODS_HIGH) { pos = new this.map.Position(x, y); for (dir = Direction.BEGIN; dir < Direction.END; dir = Direction.increment90(dir)) { tile = this.map.getTileFromMapOrDefault(pos, dir, Tile.TILE_INVALID); if (tile === Tile.RIVER || tile === Tile.CHANNEL) { //this.map.setTo(x, y, new Micro.Tile(Tile.REDGE)); // make it water's edge this.map.setTile(x, y, Tile.REDGE, 0); break; } } } } } } }