historic_bruno
fffcdbbf21
Attempts to avoid "invalid argument" errors in typed arrays by forcing them to integer (see #658). Removes script preloading from map generator (VFS is thread-safe now) Removes thread checking from ScriptInterface file loading functions. Adjusts starting entities in civ data. This was SVN commit r9435.
538 lines
9.8 KiB
JavaScript
538 lines
9.8 KiB
JavaScript
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Constant definitions
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const PI = Math.PI;
|
|
const TWO_PI = 2 * Math.PI;
|
|
const TERRAIN_SEPARATOR = "|";
|
|
const SEA_LEVEL = 20.0;
|
|
const CELL_SIZE = 4;
|
|
const HEIGHT_UNITS_PER_METRE = 732;
|
|
const MIN_MAP_SIZE = 128;
|
|
const MAX_MAP_SIZE = 512;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Utility functions
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function fractionToTiles(f)
|
|
{
|
|
return getMapSize() * f;
|
|
}
|
|
|
|
function tilesToFraction(t)
|
|
{
|
|
return t / getMapSize();
|
|
}
|
|
|
|
function fractionToSize(f)
|
|
{
|
|
return getMapArea() * f;
|
|
}
|
|
|
|
function sizeToFraction(s)
|
|
{
|
|
return s / getMapArea();
|
|
}
|
|
|
|
function scaleByMapSize(min, max)
|
|
{
|
|
return min + ((max-min) * (getMapSize()-MIN_MAP_SIZE) / (MAX_MAP_SIZE-MIN_MAP_SIZE));
|
|
}
|
|
|
|
function cos(x)
|
|
{
|
|
return Math.cos(x);
|
|
}
|
|
|
|
function sin(x)
|
|
{
|
|
return Math.sin(x);
|
|
}
|
|
|
|
function tan(x)
|
|
{
|
|
return Math.tan(x);
|
|
}
|
|
|
|
function abs(x) {
|
|
return Math.abs(x);
|
|
}
|
|
|
|
function round(x)
|
|
{
|
|
return Math.round(x);
|
|
}
|
|
|
|
function lerp(a, b, t)
|
|
{
|
|
return a + (b-a) * t;
|
|
}
|
|
|
|
function sqrt(x)
|
|
{
|
|
return Math.sqrt(x);
|
|
}
|
|
|
|
function ceil(x)
|
|
{
|
|
return Math.ceil(x);
|
|
}
|
|
|
|
function floor(x)
|
|
{
|
|
return Math.floor(x);
|
|
}
|
|
|
|
function max(a, b)
|
|
{
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
function min(a, b)
|
|
{
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
function println(x)
|
|
{
|
|
print(x);
|
|
print("\n");
|
|
}
|
|
|
|
function argsToArray(x)
|
|
{
|
|
var numArgs = x.length;
|
|
if (numArgs != 1)
|
|
{
|
|
var ret = new Array(numArgs);
|
|
for (var i=0; i < numArgs; i++)
|
|
{
|
|
ret[i] = x[i];
|
|
}
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
return x[0];
|
|
}
|
|
}
|
|
|
|
function chooseRand()
|
|
{
|
|
if (arguments.length==0)
|
|
{
|
|
throw("chooseRand: requires at least 1 argument");
|
|
}
|
|
var ar = argsToArray(arguments);
|
|
return ar[randInt(ar.length)];
|
|
}
|
|
|
|
function createAreas(centeredPlacer, painter, constraint, num, retryFactor)
|
|
{
|
|
if (retryFactor === undefined)
|
|
{
|
|
retryFactor = 10;
|
|
}
|
|
|
|
var maxFail = num * retryFactor;
|
|
var good = 0;
|
|
var bad = 0;
|
|
var result = [];
|
|
var halfSize = getMapSize()/2;
|
|
|
|
while(good < num && bad <= maxFail)
|
|
{
|
|
if (isCircularMap())
|
|
{ // Polar coordinates
|
|
var r = halfSize * Math.sqrt(randFloat()); // uniform distribution
|
|
var theta = randFloat(0, 2 * PI);
|
|
centeredPlacer.x = Math.floor(r * Math.cos(theta)) + halfSize;
|
|
centeredPlacer.z = Math.floor(r * Math.sin(theta)) + halfSize;
|
|
}
|
|
else
|
|
{ // Rectangular coordinates
|
|
centeredPlacer.x = randInt(getMapSize());
|
|
centeredPlacer.z = randInt(getMapSize());
|
|
}
|
|
|
|
var area = g_Map.createArea(centeredPlacer, painter, constraint);
|
|
if (area !== undefined)
|
|
{
|
|
good++;
|
|
result.push(area);
|
|
}
|
|
else
|
|
{
|
|
bad++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function createAreasInAreas(centeredPlacer, painter, constraint, num, retryFactor, areas)
|
|
{
|
|
if (retryFactor === undefined)
|
|
{
|
|
retryFactor = 10;
|
|
}
|
|
|
|
var maxFail = num * retryFactor;
|
|
var good = 0;
|
|
var bad = 0;
|
|
var result = [];
|
|
var numAreas = areas.length;
|
|
|
|
while(good < num && bad <= maxFail && numAreas)
|
|
{
|
|
// Choose random point from area
|
|
var i = randInt(numAreas);
|
|
var size = areas[i].points.length;
|
|
var pt = areas[i].points[randInt(size)];
|
|
centeredPlacer.x = pt.x;
|
|
centeredPlacer.z = pt.z;
|
|
|
|
var area = g_Map.createArea(centeredPlacer, painter, constraint);
|
|
if (area !== undefined)
|
|
{
|
|
good++;
|
|
result.push(area);
|
|
}
|
|
else
|
|
{
|
|
bad++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function createObjectGroups(placer, player, constraint, num, retryFactor)
|
|
{
|
|
if (retryFactor === undefined)
|
|
{
|
|
retryFactor = 10;
|
|
}
|
|
|
|
var maxFail = num * retryFactor;
|
|
var good = 0;
|
|
var bad = 0;
|
|
var halfSize = getMapSize()/2;
|
|
while(good < num && bad <= maxFail)
|
|
{
|
|
if (isCircularMap())
|
|
{ // Polar coordinates
|
|
var r = halfSize * Math.sqrt(randFloat()); // uniform distribution
|
|
var theta = randFloat(0, 2 * PI);
|
|
placer.x = Math.floor(r * Math.cos(theta)) + halfSize;
|
|
placer.z = Math.floor(r * Math.sin(theta)) + halfSize;
|
|
}
|
|
else
|
|
{ // Rectangular coordinates
|
|
placer.x = randInt(getMapSize());
|
|
placer.z = randInt(getMapSize());
|
|
}
|
|
|
|
var result = createObjectGroup(placer, player, constraint);
|
|
if (result !== undefined)
|
|
{
|
|
good++;
|
|
}
|
|
else
|
|
{
|
|
bad++;
|
|
}
|
|
}
|
|
return good;
|
|
}
|
|
|
|
function createObjectGroupsByAreas(placer, player, constraint, num, retryFactor, areas)
|
|
{
|
|
if (retryFactor === undefined)
|
|
{
|
|
retryFactor = 10;
|
|
}
|
|
|
|
var maxFail = num * retryFactor;
|
|
var good = 0;
|
|
var bad = 0;
|
|
var numAreas = areas.length;
|
|
|
|
while(good < num && bad <= maxFail && numAreas)
|
|
{
|
|
// Choose random point from area
|
|
var i = randInt(numAreas);
|
|
var size = areas[i].points.length;
|
|
var pt = areas[i].points[randInt(size)];
|
|
placer.x = pt.x;
|
|
placer.z = pt.z;
|
|
|
|
var result = createObjectGroup(placer, player, constraint);
|
|
if (result !== undefined)
|
|
{
|
|
good++;
|
|
}
|
|
else
|
|
{
|
|
bad++;
|
|
}
|
|
}
|
|
return good;
|
|
}
|
|
|
|
function createTerrain(terrain)
|
|
{
|
|
if (terrain instanceof Array)
|
|
{
|
|
var terrainList = [];
|
|
|
|
for (var i = 0; i < terrain.length; ++i)
|
|
{
|
|
terrainList.push(createTerrain(terrain[i]));
|
|
}
|
|
|
|
return new RandomTerrain(terrainList);
|
|
}
|
|
else
|
|
{
|
|
return createSimpleTerrain(terrain);
|
|
}
|
|
}
|
|
|
|
function createSimpleTerrain(terrain)
|
|
{
|
|
if (typeof(terrain) == "string")
|
|
{ // Split string by pipe | character, this allows specifying terrain + tree type in single string
|
|
var params = terrain.split(TERRAIN_SEPARATOR, 2);
|
|
|
|
if (params.length != 2)
|
|
{
|
|
return new SimpleTerrain(terrain);
|
|
}
|
|
else
|
|
{
|
|
return new SimpleTerrain(params[0], params[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw("createSimpleTerrain expects string as input, received "+terrain);
|
|
}
|
|
}
|
|
|
|
function placeObject(x, z, type, player, angle)
|
|
{
|
|
g_Map.addObject(new Entity(type, player, x, z, angle));
|
|
}
|
|
|
|
function placeTerrain(x, z, terrain)
|
|
{
|
|
// convert terrain param into terrain object
|
|
g_Map.placeTerrain(x, z, createTerrain(terrain));
|
|
|
|
}
|
|
|
|
function isCircularMap()
|
|
{
|
|
return (g_MapSettings.CircularMap ? true : false);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Access global map variable
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function createTileClass()
|
|
{
|
|
return g_Map.createTileClass();
|
|
}
|
|
|
|
function getTileClass(id)
|
|
{
|
|
// Check for valid class id
|
|
if (!g_Map.validClass(id))
|
|
{
|
|
return undefined;
|
|
}
|
|
|
|
return g_Map.tileClasses[id];
|
|
}
|
|
|
|
function createArea(placer, painter, constraint)
|
|
{
|
|
return g_Map.createArea(placer, painter, constraint);
|
|
}
|
|
|
|
function createObjectGroup(placer, player, constraint)
|
|
{
|
|
return g_Map.createObjectGroup(placer, player, constraint);
|
|
}
|
|
|
|
function getMapSize()
|
|
{
|
|
return g_Map.size;
|
|
}
|
|
|
|
function getMapArea()
|
|
{
|
|
return g_Map.size*g_Map.size;
|
|
}
|
|
|
|
function getNumPlayers()
|
|
{
|
|
return g_MapSettings.PlayerData.length;
|
|
}
|
|
|
|
function getCivCode(player)
|
|
{
|
|
return g_MapSettings.PlayerData[player].Civ;
|
|
}
|
|
|
|
function getStartingEntities(player)
|
|
{
|
|
// This is a temporary hack until map generator has a LoadCivData method
|
|
var civStartingEntities = {
|
|
"celt" : [
|
|
{
|
|
"Template": "structures/celt_civil_centre"
|
|
},
|
|
{
|
|
"Template": "units/celt_support_female_citizen",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/celt_infantry_spearman_b",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/celt_cavalry_swordsman_b"
|
|
}
|
|
],
|
|
"hele" : [
|
|
{
|
|
"Template": "structures/hele_civil_centre"
|
|
},
|
|
{
|
|
"Template": "units/hele_support_female_citizen",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/hele_infantry_spearman_b",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/hele_cavalry_swordsman_b"
|
|
}
|
|
],
|
|
"iber" : [
|
|
{
|
|
"Template": "structures/iber_civil_centre"
|
|
},
|
|
{
|
|
"Template": "units/iber_support_female_citizen",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/iber_infantry_spearman_b",
|
|
"Count": 4
|
|
},
|
|
{
|
|
"Template": "units/iber_cavalry_spearman_b"
|
|
}
|
|
]
|
|
};
|
|
|
|
return civStartingEntities[getCivCode(player)];
|
|
}
|
|
|
|
function getHeight(x, z)
|
|
{
|
|
return g_Map.getHeight(x, z);
|
|
}
|
|
|
|
function setHeight(x, z, height)
|
|
{
|
|
g_Map.setHeight(x, z, height);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Utility functions for classes
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Add point to given class by id
|
|
function addToClass(x, z, id)
|
|
{
|
|
var tileClass = getTileClass(id);
|
|
|
|
if (tileClass !== null)
|
|
{
|
|
tileClass.add(x, z);
|
|
}
|
|
}
|
|
|
|
// Create a painter for the given class
|
|
function paintClass(id)
|
|
{
|
|
return new TileClassPainter(getTileClass(id));
|
|
}
|
|
|
|
// Create an avoid constraint for the given classes by the given distances
|
|
function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
|
|
{
|
|
var ar = new Array(arguments.length/2);
|
|
for (var i = 0; i < arguments.length/2; i++)
|
|
{
|
|
ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]);
|
|
}
|
|
|
|
// Return single constraint
|
|
if (ar.length == 1)
|
|
{
|
|
return ar[0];
|
|
}
|
|
else
|
|
{
|
|
return new AndConstraint(ar);
|
|
}
|
|
}
|
|
|
|
// Create a stay constraint for the given classes by the given distances
|
|
function stayClasses(/*class1, dist1, class2, dist2, etc*/)
|
|
{
|
|
var ar = new Array(arguments.length/2);
|
|
for (var i = 0; i < arguments.length/2; i++)
|
|
{
|
|
ar[i] = new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]);
|
|
}
|
|
|
|
// Return single constraint
|
|
if (ar.length == 1)
|
|
{
|
|
return ar[0];
|
|
}
|
|
else
|
|
{
|
|
return new AndConstraint(ar);
|
|
}
|
|
}
|
|
|
|
// Create a border constraint for the given classes by the given distances
|
|
function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
|
|
{
|
|
var ar = new Array(arguments.length/3);
|
|
for (var i = 0; i < arguments.length/3; i++)
|
|
{
|
|
ar[i] = new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]);
|
|
}
|
|
|
|
// Return single constraint
|
|
if (ar.length == 1)
|
|
{
|
|
return ar[0];
|
|
}
|
|
else
|
|
{
|
|
return new AndConstraint(ar);
|
|
}
|
|
}
|