Open TheAio opened 2 years ago
Here are the functions I am using to generate random terrain in webcraft. You need to update the world.js file:
world.js `// ========================================== // World container // // This class contains the elements that make up the game world. // Other modules retrieve information from the world or alter it // using this class. // ==========================================
// Constructor( sx, sy, sz ) // // Creates a new world container with the specified world size. // Up and down should always be aligned with the Z-direction. // // sx - World size in the X-direction. // sy - World size in the Y-direction. // sz - World size in the Z-direction.
//function World( sx, sy, sz ) function World(sx, sy, sz, roughness, smoothAmount, smoothAmt) { // Initialise world array
this.blocks = new Array( sx );
for ( var x = 0; x < sx; x++ )
{
this.blocks[x] = new Array( sy );
for ( var y = 0; y < sy; y++ )
{
this.blocks[x][y] = new Array( sz );
}
}
// Set all blocks to air
for(var x = 0; x < sx;x++){
for(var y = 0; y < sy;y++){
for(var z = 0; z < sz;z++){
this.blocks[x][y][z] = BLOCK.AIR;
}
}
}
this.sx = sx;
this.sy = sy;
this.sz = sz;
this.roughness = roughness;
this.smoothAmount = smoothAmount;
this.smoothAmt = smoothAmt;
this.players = {};
}
// createFlatWorld() // // Sets up the world so that the bottom half is filled with dirt // and the top half with air.
World.prototype.createFlatWorld = function( height ) { this.spawnPoint = new Vector( this.sx / 2 + 0.5, this.sy / 2 + 0.5, height );
for ( var x = 0; x < this.sx; x++ )
for ( var y = 0; y < this.sy; y++ )
for ( var z = 0; z < this.sz; z++ )
this.blocks[x][y][z] = z < height ? BLOCK.DIRT : BLOCK.AIR;
}
World.prototype.getPower = function(a,b){ if(b == 0)return 1; var d = a; for(var c = 1;c < b;c++)a = a*d; return a; }
World.prototype.getPowerOfTwo = function(a,b){ if(a<b)a = b; for(b = 0;this.getPower(2, b) <= a;b++); return b; }
World.prototype.createWorld = function() { var map = this.generate(this.getPower(2, this.getPowerOfTwo(this.sx, this.sy)), this.roughness, this.smoothAmount, this.smoothAmt);
for ( var x = 0; x < this.sx; x++ ){
for ( var y = 0; y < this.sy; y++ ){
this.blocks[x][y][0] = BLOCK.BEDROCK;
var pointHeight = map[x][y] * (this.sz - 10);
for(var z = 1; z < this.sz;z++){
if(z < pointHeight){
if(!(z < pointHeight - 4))this.blocks[x][y][z] = BLOCK.DIRT;
else if(z < pointHeight - 3){
var random = Math.random() * 100;
if(random < 1){
this.blocks[x][y][z] = BLOCK.DIAMOND_ORE;
}
else if(random < 3){
this.blocks[x][y][z] = BLOCK.GOLD_ORE;
}
else if(random < 6){
this.blocks[x][y][z] = BLOCK.REDSTONE_ORE;
}
else if(random < 14){
this.blocks[x][y][z] = BLOCK.IRON_ORE;
}
else if(random < 24){
this.blocks[x][y][z] = BLOCK.COAL_ORE;
}
else this.blocks[x][y][z] = BLOCK.CONCRETE;
}
else {
if(z < 45) { this.blocks[x][y][z] = BLOCK.DIRT; }
if(z > 45) { this.blocks[x][y][z] = BLOCK.SNOW; }
}
}
else if(z < 10 && z > 7){
this.blocks[x][y][z] = BLOCK.WATER;
}
else if (z < 14) {
}
else if(y != this.sy/2 && x != this.sx/2 && ( z < pointHeight + 1 && z > pointHeight - 1) && (x > 2 && x < this.sx - 2) && (y > 2 && y < this.sy - 2)){
var random = Math.random() * 100;
if(random < 1){
this.createTree(x,y,z,5,3,2);
}
}
}
}
}
this.spawnPoint = new Vector( this.sx/2 + 0.5, this.sy/2 + 0.5, map[this.sx/2][this.sy/2] * (this.sz + 10));
};
World.prototype.createTree = function(x,y,z,trunkHeight,leavesHeight,range) { for(iz = 0;iz < trunkHeight;iz++){ this.blocks[x][y][z + iz] = BLOCK.WOOD; }
for(var ix = -1*range;ix <= range;ix++){
for(var iy = -1*range;iy <= range;iy++){
for(iz = 0;iz < leavesHeight;iz++){
if(!((Math.abs(iy) == range) && (Math.abs(ix) == range) && (iz == (leavesHeight - 1))))
this.setBlock(x + ix, y + iy, z + iz + trunkHeight, BLOCK.LEAVES);
}
}
}
};
// createFromString( str ) // // Creates a world from a string representation. // This is the opposite of toNetworkString(). // // NOTE: The world must have already been created // with the appropriate size!
World.prototype.createFromString = function( str ) { var i = 0;
for ( var x = 0; x < this.sx; x++ ) {
for ( var y = 0; y < this.sy; y++ ) {
for ( var z = 0; z < this.sz; z++ ) {
this.blocks[x][y][z] = BLOCK.fromId( str.charCodeAt( i ) - 97 );
i = i + 1;
}
}
}
}
// getBlock( x, y, z ) // // Get the type of the block at the specified position. // Mostly for neatness, since accessing the array // directly is easier and faster.
World.prototype.getBlock = function( x, y, z ) { if ( x < 0 || y < 0 || z < 0 || x > this.sx - 1 || y > this.sy - 1 || z > this.sz - 1 ) return BLOCK.AIR; return this.blocks[x][y][z]; }
// setBlock( x, y, z )
World.prototype.setBlock = function( x, y, z, type ) { if(this.blocks[x][y][z]) { this.blocks[x][y][z] = type; //console.log("renderer: "+this.renderer); if ( this.renderer != null ) this.renderer.onBlockChanged( x, y, z ); } }
// toNetworkString() // // Returns a string representation of this world.
World.prototype.toNetworkString = function() { var blockArray = [];
for ( var x = 0; x < this.sx; x++ )
for ( var y = 0; y < this.sy; y++ )
for ( var z = 0; z < this.sz; z++ )
blockArray.push( String.fromCharCode( 97 + this.blocks[x][y][z].id ) );
return blockArray.join( "" );
}
// Export to node.js if ( typeof( exports ) != "undefined" ) { // loadFromFile( filename ) // // Load a world from a file previously saved with saveToFile(). // The world must have already been allocated with the // appropriate dimensions.
World.prototype.loadFromFile = function( filename )
{
var fs = require( "fs" );
try {
fs.lstatSync( filename );
var data = fs.readFileSync( filename, "utf8" ).split( "," );
this.createFromString( data[3] );
this.spawnPoint = new Vector( parseInt( data[0] ), parseInt( data[1] ), parseInt( data[2] ) );
return true;
} catch ( e ) {
return false;
}
}
// saveToFile( filename )
//
// Saves a world and the spawn point to a file.
// The world can be loaded from it afterwards with loadFromFile().
World.prototype.saveToFile = function( filename )
{
console.log( "Saved world to file: " + filename );
var data = this.spawnPoint.x + "," + this.spawnPoint.y + "," + this.spawnPoint.z + "," + this.toNetworkString();
require( "fs" ).writeFileSync( filename, data );
}
exports.World = World;
}
/////////////////////////////////////////////////////////////////////////////////////
World.prototype.generate = function(mapSize, roughness, smoothAmount, smoothAmt){ var map; map = this.generateMapTerrain(mapSize, roughness); for(var i = 0; i < smoothAmount; i++){ map = smooth(map, mapSize,smoothAmt); }
function round(n)
{
if (n-(parseInt(n, 10)) >= 0.5){
return parseInt(n, 10)+1;
}else{
return parseInt(n, 10);
}
}
function smooth(data, size, amt) {
/* Rows, left to right */
for (var x = 1; x < size; x++){
for (var z = 0; z < size; z++){
data[x][z] = data[x - 1][z] * (1 - amt) + data[x][z] * amt;
}
}
/* Rows, right to left*/
for (x = size - 2; x < -1; x--){
for (z = 0; z < size; z++){
data[x][z] = data[x + 1][z] * (1 - amt) + data[x][z] * amt;
}
}
/* Columns, bottom to top */
for (x = 0; x < size; x++){
for (z = 1; z < size; z++){
data[x][z] = data[x][z - 1] * (1 - amt) + data[x][z] * amt;
}
}
/* Columns, top to bottom */
for (x = 0; x < size; x++){
for (z = size; z < -1; z--){
data[x][z] = data[x][z + 1] * (1 - amt) + data[x][z] * amt;
}
}
return data;
}
return map; };
World.prototype.generateMapTerrain = function(mapSize, roughness) { var map = create2DArray(mapSize+1); startDisplacement(map, mapSize); return map;
function create2DArray(d1) {
var x = new Array(d1),
i = 0,
j = 0;
for (i = 0; i < d1; i += 1) {
x[i] = new Array(d1);
}
for (i=0; i < d1; i += 1) {
for (j = 0; j < d1; j += 1) {
x[i][j] = 0;
}
}
return x;
}
// Starts off the map generation, seeds the first 4 corners
function startDisplacement(map,mapSize){
var tr, tl, t, br, bl, b, r, l, center;
// top left
map[0][0] = Math.random(1.0);
tl = map[0][0];
// bottom left
map[0][mapSize] = Math.random(1.0);
bl = map[0][mapSize];
// top right
map[mapSize][0] = Math.random(1.0);
tr = map[mapSize][0];
// bottom right
map[mapSize][mapSize] = Math.random(1.0);
br = map[mapSize][mapSize];
// Center
map[mapSize/2][mapSize/2] = map[0][0] + map[0][mapSize] + map[mapSize][0] + map[mapSize][mapSize] / 4;
map[mapSize/2][mapSize/2] = normalize(map[mapSize/2][mapSize/2]);
center = map[mapSize/2][mapSize/2];
/* Non wrapping terrain */
map[mapSize/2][mapSize] = bl + br + center / 3;
map[mapSize/2][0] = tl + tr + center / 3;
map[mapSize][mapSize/2] = tr + br + center / 3;
map[0][mapSize/2] = tl + bl + center / 3;
// Call displacment
midpointDisplacment(mapSize);
}
// Workhorse of the terrain generation.
function midpointDisplacment(mSize){
var newSize = mSize/2,
top, topRight, topLeft, bottom, bottomLeft, bottomRight, right, left, center,
x = 0, y = 0,
i = 0, j = 0;
if (newSize > 1 && newSize > 1){
for(i = newSize; i <= mapSize; i += newSize){
for(j = newSize; j <= mapSize; j += newSize){
x = i - (newSize / 2);
y = j - (newSize / 2);
topLeft = map[i - newSize][j - newSize];
topRight = map[i][j - newSize];
bottomLeft = map[i - newSize][j];
bottomRight = map[i][j];
// Center
map[x][y] = (topLeft + topRight + bottomLeft + bottomRight) / 4 + displace(mSize); ///tu
map[x][y] = normalize(map[x][y]);
center = map[x][y];
// Top
if(j - (newSize * 2) + (newSize / 2) > 0){
map[x][j - newSize] = (topLeft + topRight + center + map[x][j - mSize + (newSize/2)]) / 4 + displace(mSize);
}else{
map[x][j - newSize] = (topLeft + topRight + center) / 3+ displace(mSize);
}
map[x][j - newSize] = normalize(map[x][j - newSize]);
// Bottom
if(j + (newSize / 2) < mapSize){
map[x][j] = (bottomLeft + bottomRight + center + map[x][j + (newSize/2)]) / 4+ displace(mSize);
}else{
map[x][j] = (bottomLeft + bottomRight + center) / 3+ displace(mSize);
}
map[x][j] = normalize(map[x][j]);
//Right
if(i + (newSize / 2) < mapSize){
map[i][y] = (topRight + bottomRight + center + map[i + (newSize/2)][y]) / 4+ displace(mSize);
}else{
map[i][y] = (topRight + bottomRight + center) / 3+ displace(mSize);
}
map[i][y] = normalize(map[i][y]);
// Left
if(i - (newSize * 2) + (newSize / 2) > 0){
map[i - newSize][y] = (topLeft + bottomLeft + center + map[i - mSize + (newSize/2)][y]) / 4 + displace(mSize);
}else{
map[i - newSize][y] = (topLeft + bottomLeft + center) / 3+ displace(mSize);
}
map[i - newSize][y] = normalize(map[i - newSize][y]);
}
}
midpointDisplacment(newSize);
}
}
function displace(num){
var max = num / (mapSize + mapSize) * roughness;
return (Math.random(1.0)- 0.5) * max;
}
function normalize(value){
if(value > 1)
value = 1;
else if(value < 0)
value = 0;
return value;
}
}; `
Create generator.js:
`function generate(mapSize, roughness, smoothAmount, smoothAmt){ var map; map = generateMapTerrain(mapSize, roughness); for(var i = 0; i < smoothAmount; i++){ map = smooth(map, mapSize,smoothAmt); }
function round(n)
{
if (n-(parseInt(n, 10)) >= 0.5){
return parseInt(n, 10)+1;
}else{
return parseInt(n, 10);
}
}
function smooth(data, size, amt) {
/* Rows, left to right */
for (var x = 1; x < size; x++){
for (var z = 0; z < size; z++){
data[x][z] = data[x - 1][z] * (1 - amt) + data[x][z] * amt;
}
}
/* Rows, right to left*/
for (x = size - 2; x < -1; x--){
for (z = 0; z < size; z++){
data[x][z] = data[x + 1][z] * (1 - amt) + data[x][z] * amt;
}
}
/* Columns, bottom to top */
for (x = 0; x < size; x++){
for (z = 1; z < size; z++){
data[x][z] = data[x][z - 1] * (1 - amt) + data[x][z] * amt;
}
}
/* Columns, top to bottom */
for (x = 0; x < size; x++){
for (z = size; z < -1; z--){
data[x][z] = data[x][z + 1] * (1 - amt) + data[x][z] * amt;
}
}
return data;
}
return map; };
function generateMapTerrain(mapSize, roughness) { var map = create2DArray(mapSize+1); startDisplacement(map, mapSize); return map;
function create2DArray(d1) {
var x = new Array(d1),
i = 0,
j = 0;
for (i = 0; i < d1; i += 1) {
x[i] = new Array(d1);
}
for (i=0; i < d1; i += 1) {
for (j = 0; j < d1; j += 1) {
x[i][j] = 0;
}
}
return x;
}
// Starts off the map generation, seeds the first 4 corners
function startDisplacement(map,mapSize){
var tr, tl, t, br, bl, b, r, l, center;
// top left
map[0][0] = Math.random(1.0);
tl = map[0][0];
// bottom left
map[0][mapSize] = Math.random(1.0);
bl = map[0][mapSize];
// top right
map[mapSize][0] = Math.random(1.0);
tr = map[mapSize][0];
// bottom right
map[mapSize][mapSize] = Math.random(1.0);
br = map[mapSize][mapSize];
// Center
map[mapSize/2][mapSize/2] = map[0][0] + map[0][mapSize] + map[mapSize][0] + map[mapSize][mapSize] / 4;
map[mapSize/2][mapSize/2] = normalize(map[mapSize/2][mapSize/2]);
center = map[mapSize/2][mapSize/2];
/* Non wrapping terrain */
map[mapSize/2][mapSize] = bl + br + center / 3;
map[mapSize/2][0] = tl + tr + center / 3;
map[mapSize][mapSize/2] = tr + br + center / 3;
map[0][mapSize/2] = tl + bl + center / 3;
// Call displacment
midpointDisplacment(mapSize);
}
// Workhorse of the terrain generation.
function midpointDisplacment(mSize){
var newSize = mSize/2,
top, topRight, topLeft, bottom, bottomLeft, bottomRight, right, left, center,
x = 0, y = 0,
i = 0, j = 0;
if (newSize > 1 && newSize > 1){
for(i = newSize; i <= mapSize; i += newSize){
for(j = newSize; j <= mapSize; j += newSize){
x = i - (newSize / 2);
y = j - (newSize / 2);
topLeft = map[i - newSize][j - newSize];
topRight = map[i][j - newSize];
bottomLeft = map[i - newSize][j];
bottomRight = map[i][j];
// Center
map[x][y] = (topLeft + topRight + bottomLeft + bottomRight) / 4 + displace(mSize); ///tu
map[x][y] = normalize(map[x][y]);
center = map[x][y];
// Top
if(j - (newSize * 2) + (newSize / 2) > 0){
map[x][j - newSize] = (topLeft + topRight + center + map[x][j - mSize + (newSize/2)]) / 4 + displace(mSize);
}else{
map[x][j - newSize] = (topLeft + topRight + center) / 3+ displace(mSize);
}
map[x][j - newSize] = normalize(map[x][j - newSize]);
// Bottom
if(j + (newSize / 2) < mapSize){
map[x][j] = (bottomLeft + bottomRight + center + map[x][j + (newSize/2)]) / 4+ displace(mSize);
}else{
map[x][j] = (bottomLeft + bottomRight + center) / 3+ displace(mSize);
}
map[x][j] = normalize(map[x][j]);
//Right
if(i + (newSize / 2) < mapSize){
map[i][y] = (topRight + bottomRight + center + map[i + (newSize/2)][y]) / 4+ displace(mSize);
}else{
map[i][y] = (topRight + bottomRight + center) / 3+ displace(mSize);
}
map[i][y] = normalize(map[i][y]);
// Left
if(i - (newSize * 2) + (newSize / 2) > 0){
map[i - newSize][y] = (topLeft + bottomLeft + center + map[i - mSize + (newSize/2)][y]) / 4 + displace(mSize);
}else{
map[i - newSize][y] = (topLeft + bottomLeft + center) / 3+ displace(mSize);
}
map[i - newSize][y] = normalize(map[i - newSize][y]);
}
}
midpointDisplacment(newSize);
}
}
function displace(num){
var max = num / (mapSize + mapSize) * roughness;
return (Math.random(1.0)- 0.5) * max;
}
function normalize(value){
if(value > 1)
value = 1;
else if(value < 0)
value = 0;
return value;
}
};
`
Hello i have a server and this old code is still working great with a few updates. However i would like to implement terrain generation (currently everything is in a flat world)
I saw a function for creating terrain worlds but i dont really understand how to use it, simply replacing world.createFlatWorld with world.createFromString does not appear to work. Could someone help me?