1
0
forked from 0ad/0ad
0ad/binaries/data/mods/public/maps/random/rmgen/map.js
2011-07-19 08:52:17 +00:00

350 lines
7.5 KiB
JavaScript

//////////////////////////////////////////////////////////////////////
// Map
//
// Class for holding map data and providing basic API to change it
//
// size: Size of the map in tiles
// baseHeight: Starting height of the map
//
//////////////////////////////////////////////////////////////////////
function Map(size, baseHeight)
{
// Size must be 0 to 1024, divisible by patches
this.size = size;
// Create 2D arrays for textures, object, and areas
this.texture = new Array(size);
this.terrainObjects = new Array(size);
this.area = new Array(size);
for (var i = 0; i < size; i++)
{
this.texture[i] = new Uint16Array(size); // uint16 - texture IDs
this.terrainObjects[i] = new Array(size); // array of entities
this.area[i] = new Uint16Array(size); // uint16 - area IDs
for (var j = 0; j < size; j++)
{
this.terrainObjects[i][j] = [];
}
}
// Create 2D array for heightmap
var mapSize = size+1;
this.height = new Array(mapSize);
for (var i = 0; i < mapSize; i++)
{
this.height[i] = new Float32Array(mapSize); // float32
for (var j = 0; j < mapSize; j++)
{ // Initialize height map to baseHeight
this.height[i][j] = baseHeight;
}
}
// Create name <-> id maps for textures
this.nameToID = {};
this.IDToName = []; //string
// Other arrays
this.objects = []; //object
this.tileClasses = []; //int
this.areaID = 0;
// Starting entity ID
this.entityCount = 150;
}
Map.prototype.initTerrain = function(baseTerrain)
{
// Initialize base terrain
var size = this.size;
for (var i = 0; i < size; i++)
{
for (var j = 0; j < size; j++)
{
baseTerrain.place(i, j);
}
}
};
// Return ID of texture (by name)
Map.prototype.getTextureID = function(texture)
{
if (texture in (this.nameToID))
{
return this.nameToID[texture];
}
// Add new texture
var id = this.IDToName.length;
this.nameToID[texture] = id;
this.IDToName[id] = texture;
return id;
};
// Return next free entity ID
Map.prototype.getEntityID = function()
{
return this.entityCount++;
}
// Check bounds on tile map
Map.prototype.validT = function(x, z)
{
if (g_MapSettings.CircularMap)
{ // Within map circle
var halfSize = Math.floor(0.5*this.size);
var dx = (x - halfSize);
var dz = (z - halfSize);
return Math.round(Math.sqrt(dx*dx + dz*dz)) < halfSize;
}
else
{ // Within map square
return x >= 0 && z >= 0 && x < this.size && z < this.size;
}
};
// Check bounds on height map (size + 1 by size + 1)
Map.prototype.validH = function(x, z)
{
return x >= 0 && z >= 0 && x <= this.size && z <= this.size;
};
// Check bounds on tile class
Map.prototype.validClass = function(c)
{
return c >= 0 && c < this.tileClasses.length;
};
Map.prototype.getTexture = function(x, z)
{
if (!this.validT(x, z))
{
throw("getTexture: invalid tile position ("+x+", "+z+")");
}
return this.IDToName[this.texture[x][z]];
};
Map.prototype.setTexture = function(x, z, texture)
{
if (!this.validT(x, z))
{
throw("setTexture: invalid tile position ("+x+", "+z+")");
}
this.texture[x][z] = this.getTextureID(texture);
};
Map.prototype.getHeight = function(x, z)
{
if (!this.validH(x, z))
{
throw("getHeight: invalid vertex position ("+x+", "+z+")");
}
return this.height[x][z];
};
Map.prototype.setHeight = function(x, z, height)
{
if (!this.validH(x, z))
{
throw("setHeight: invalid vertex position ("+x+", "+z+")");
}
this.height[x][z] = height;
};
Map.prototype.getTerrainObjects = function(x, z)
{
if (!this.validT(x, z))
{
throw("getTerrainObjects: invalid tile position ("+x+", "+z+")");
}
return this.terrainObjects[x][z];
};
Map.prototype.setTerrainObject = function(x, z, object)
{
if (!this.validT(x, z))
{
throw("setTerrainObject: invalid tile position ("+x+", "+z+")");
}
this.terrainObjects[x][z] = object;
};
Map.prototype.placeTerrain = function(x, z, terrain)
{
terrain.place(x, z);
};
Map.prototype.addObject = function(obj)
{
this.objects.push(obj);
};
Map.prototype.createArea = function(placer, painter, constraint)
{
// Check for multiple painters
if (painter instanceof Array)
{
var painterArray = painter;
painter = new MultiPainter(painterArray);
}
// Check for null constraint
if (constraint === undefined || constraint === null)
{
constraint = new NullConstraint();
}
else if (constraint instanceof Array)
{ // Check for multiple constraints
var constraintArray = constraint;
constraint = new AndConstraint(constraintArray);
}
var points = placer.place(constraint);
if (!points)
return undefined;
var newID = ++this.areaID;
var area = new Area(points, newID);
for (var i=0; i < points.length; i++)
{
this.area[points[i].x][points[i].z] = newID;
}
painter.paint(area);
return area;
};
Map.prototype.createObjectGroup = function(placer, player, constraint)
{
// Check for null constraint
if (constraint === undefined || constraint === null)
{
constraint = new NullConstraint();
}
else if (constraint instanceof Array)
{ // Check for multiple constraints
var constraintArray = constraint;
constraint = new AndConstraint(constraintArray);
}
return placer.place(player, constraint);
};
Map.prototype.createTileClass = function()
{
var newID = this.tileClasses.length;
this.tileClasses.push(new TileClass(this.size, newID));
return newID;
};
// Get height taking into account terrain curvature
Map.prototype.getExactHeight = function(x, z)
{
var xi = min(Math.floor(x), this.size);
var zi = min(Math.floor(z), this.size);
var xf = x - xi;
var zf = z - zi;
var h00 = this.height[xi][zi];
var h01 = this.height[xi][zi+1];
var h10 = this.height[xi+1][zi];
var h11 = this.height[xi+1][zi+1];
return ( 1 - zf ) * ( ( 1 - xf ) * h00 + xf * h10 ) + zf * ( ( 1 - xf ) * h01 + xf * h11 ) ;
};
Map.prototype.getMapData = function()
{
var data = {};
// Build entity array
var entities = [];
// Terrain objects first (trees)
var size = this.size;
for (var x = 0; x < size; ++x)
{
for (var z = 0; z < size; ++z)
{
if (this.terrainObjects[x][z] !== undefined)
{
entities.push(this.terrainObjects[x][z]);
}
}
}
// Now other entities
for (var i = 0; i < this.objects.length; ++i)
{
entities.push(this.objects[i]);
}
data["entities"] = entities;
log("Number of entities: "+entities.length);
// Terrain
data["size"] = this.size;
// Convert 2D heightmap array to flat array
// Flat because it's easier to handle by the engine
var mapSize = size+1;
var height16 = new Uint16Array(mapSize*mapSize); // uint16
for (var x = 0; x < mapSize; x++)
{
for (var z = 0; z < mapSize; z++)
{
var intHeight = Math.floor((this.height[x][z] + SEA_LEVEL) * HEIGHT_UNITS_PER_METRE);
// Prevent under/overflow in terrain data
if (intHeight > 0xFFFF)
{
intHeight = 0xFFFF;
}
else if (intHeight < 0)
{
intHeight = 0;
}
height16[z*mapSize + x] = intHeight;
}
}
data["height"] = height16;
data["seaLevel"] = SEA_LEVEL;
// Get array of textures used in this map
var textureNames = [];
for (var name in this.nameToID)
{
textureNames.push(name);
}
data["textureNames"] = textureNames;
// Convert 2D tile data to flat array
var tileIndex = new Uint16Array(size*size); // uint16
var tilePriority = new Uint16Array(size*size); // uint16
for (var x = 0; x < size; x++)
{
for (var z = 0; z < size; z++)
{
// TODO: For now just use the texture's index as priority, might want to do this another way
tileIndex[z*size + x] = this.texture[x][z];
tilePriority[z*size + x] = this.texture[x][z];
}
}
data["tileData"] = {"index": tileIndex, "priority": tilePriority};
return data;
};