Jump to content

RANDOM MAPS: Suggestions, Bug reports and Ideas about random maps here.


 Share

Recommended Posts

Borders are messy in RMs. The placer won't really let you place things out of the boundaries, but the check is a bit restricite, and probably should be abanonned for terrain placement (since all maps are squares displayed as circles).

Exactly. The check should only happen when creating areas.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

I wrote a rmgen lib to support n-dimensional discrete vector analysis e.g. for simulating floating water erosion.

I'm not quite sure that's the best way to do it and I am sure it's not the fastest/best readable code.

I hope for someone more into programming/javascript to give me tips how to do better (but please without removing the generalized approach).

Indeed it might be possible to do all this with vector algebra as well and for that fully documented javascript libs might be out there so if anyone has information about that, plz let me know.

Here's the code (indentation missing due to erased tabs):


//////////
// Discrete vector analysis functionality
//////////

/*
getScalarField
Generate an n-dimensional discrete hypercubic scalar field of the given size.
Arguments:
dimension: The dimension of the field
size: The "width" of the hypercube containing the field
defaultValue: The value all scalars in the field (so it's always flat and don't contain undefined values)
*/
function getScalarField(dimension, size, defaultValue)
{
dimension = (dimension || 2);
size = (size || mapSize + 1); // Default for heightmaps
defaultValue = (defaultValue || 0);
var scalarField = defaultValue;
for (var d = 0; d < dimension; d++)
{
var newScalarField = [];
for (var s = 0; s < size; s++)
newScalarField.push(scalarField)
scalarField = deepcopy(newScalarField);
}
return scalarField;
}

/*
getVectorField
Generate an n-dimensional discrete hypercubic vetor field of the given size.
Arguments:
dimension: int, The dimension of the field
size: int, The "width" of the hypercube containing the field (So float coordinates are not supported!)
defaultValue: float, The value all vectors components in the field (So div and curl of this vector field is always 0 and vectors are colinear to (1, 1, 1, ...) or the 0-vector)
*/
function getVectorField(dimension, size, defaultValue)
{
dimension = (dimension || 2);
size = (size || mapSize + 1); // Default for heightmaps
defaultValue = (defaultValue || 0);
var vector = [];
for (var d = 0; d < dimension; d++)
vector.push(defaultValue);
var vectorField = vector;
for (var d = 0; d < dimension; d++)
{
var newVectorField = [];
for (var s = 0; s < size; s++)
newVectorField.push(vectorField)
vectorField = deepcopy(newVectorField);
}
return vectorField;
}

/*
getDimensionOfField
Takes an discrete hypercubic scalar or vector field and returns it's dimension.
*/
function getDimensionOfField(field)
{
var nextDepthObject = deepcopy(field);
var size = field.length;
var dimension = 0;
while (typeof(nextDepthObject) == "object")
{
if (nextDepthObject.length == size)
{
dimension++;
nextDepthObject = nextDepthObject[0];
}
else
break;
}
return dimension;
}

function getAllCoordinatesInHypercube(dimension, size)
{
var coordinates = [];
for (var i = 0; i < Math.pow(size, dimension); i++)
{
var vector = [];
var sum = 0
for (var d = 0; d < dimension; d++)
{
vector.push(((i - sum) % (Math.pow(size, d + 1))) / Math.pow(size, d));
sum += vector[d]*Math.pow(size, d);
}
coordinates.push(vector);
}
return coordinates;
}

function getVectorLength(vector)
{
var squareSum = 0;
for (var d = 0; d < vector.length; d++)
squareSum += Math.pow(vector[d], 2);
return Math.pow(squareSum, 1/2);
}

function getFieldValueByCoordinate(field, vector)
{
var dimension = vector.length;
var size = field.length;
var value = deepcopy(field);
for (var d = 0; d < dimension; d++)
{
value = value[vector[d]];
}
return value;
}

function setFieldValueByCoordinate(field, vector, value)
{
var coordinate = vector.shift();
if (vector.length == 0)
field[coordinate] = value;
else
setFieldValueByCoordinate(field[coordinate], vector, value);
}

function getNormalizedField(field, scaleToLength, isVectorField)
{
scaleToLength = (scaleToLength || 1);
var dimension = getDimensionOfField(field);
var size = field.length;
var coordinates = getAllCoordinatesInHypercube(dimension, size);
isVectorField = (isVectorField || typeof(getFieldValueByCoordinate(field, coordinates[0])) == "object");
var maxLength = 0;
for (var i = 0; i < coordinates.length; i++)
{
if (isVectorField)
var length = getVectorLength(getFieldValueByCoordinate(field, coordinates[i]));
else
var length = abs(getFieldValueByCoordinate(field, coordinates[i]));
if (length > maxLength)
maxLength = length;
}
var newField = deepcopy(field);
for (var i = 0; i < coordinates.length; i++)
{
if (isVectorField)
{
var newValue = [];
for (var d = 0; d < dimension; d++)
newValue.push(getFieldValueByCoordinate(field, coordinates[i])[d] / maxLength * scaleToLength);
}
else
var newValue = getFieldValueByCoordinate(field, coordinates[i]) / maxLength * scaleToLength;
setFieldValueByCoordinate(newField, coordinates[i], newValue);
}
return newField
}

/*
getGrad
Returns the gradient of a given discrete hypercuibic scalar field, a discrete hypercubic vector field (gradient field). Dimension might depend on method: scalar fields width or the same - 1.
Problems:
The calculated vectorfield would be of size sizeOfScalarField - 1. To avoid this the left is wrapped to the right and the bottom to the top.
Using 2 appending fields results in a shift of shiftVector = 1/2*(1, 1, 1, ... , 1), abs(shiftVector) = 1/2*sqareroot(dimension) between the given scalar and the resulting vector field.
Using the 2 fiels appending the given tile avoids the shift but results in 2 areas independent of each other shifted towards each other by 1. I don't know if that is bad.
It might be a good idea to include the diagonal appending tiles for calculation to couple those two independent areas...
*/
function getGrad(scalarField, dimension, size)
{
dimension = (dimension || getDimensionOfField(scalarField));
size = (size || scalarField.length);
var vectorField = getVectorField(dimension, size);
var coordinates = getAllCoordinatesInHypercube(dimension, size);
for (var i = 0; i < coordinates.length; i++)
{
var vector = [];
for (var d = 0; d < dimension; d++)
{
var addCoordinate = deepcopy(coordinates[i]);
addCoordinate[d] = (addCoordinate[d] + 1) % size;
var subCoordinate = deepcopy(coordinates[i]);
subCoordinate[d] = (subCoordinate[d] - 1 + size) % size;
vector.push(getFieldValueByCoordinate(scalarField, addCoordinate) - getFieldValueByCoordinate(scalarField, subCoordinate));
}
setFieldValueByCoordinate(vectorField, coordinates[i], vector);
}
return vectorField;
}

// Same problems like in getGrad so all dimensions wrapped and the actual coordinates value ignored
function getDiv(vectorField, dimension, size)
{
dimension = (dimension || getDimensionOfField(vectorField));
size = (size || vectorField.length);
var scalarField = getVectorField(dimension, size);
var coordinates = getAllCoordinatesInHypercube(dimension, size);
for (var i = 0; i < coordinates.length; i++)
{
var value = 0;
for (var d = 0; d < dimension; d++)
{
var addCoordinate = deepcopy(coordinates[i]);
addCoordinate[d] = (addCoordinate[d] - 1 + size) % size;
value += getFieldValueByCoordinate(vectorField, addCoordinate)[d];
var subCoordinate = deepcopy(coordinates[i]);
subCoordinate[d] = (subCoordinate[d] + 1) % size;
value -= getFieldValueByCoordinate(vectorField, subCoordinate)[d];
}
setFieldValueByCoordinate(scalarField, coordinates[i], value);
}
return scalarField;
}

...and the ziped rmgen lib: discreteVectorAnalysis2012-9-10.zip

And some test code:


//////////
// Test discrete vector analysis functionality
//////////
var testScalarField = getScalarField(2, 5); // Get an "empty" scalar field (all values = 0)
var testCoordinates = getAllCoordinatesInHypercube(2, 5); // Get all valid (integer) coordinates of the area the field is defined in
for (var i = 0; i < testCoordinates.length; i++)
setFieldValueByCoordinate(testScalarField, testCoordinates[i], randInt(-9, 9)); // Ranomize the scalar fields values
var gradField = getGrad(testScalarField); // Get the gradient field (a vector field) determining the slope in each coordinated direction (or the direction water would flow)
var divGradField = getDiv(gradField); // Get the divergence of the gradient field (Would be something like how much water will leave this coordinate more then gathered from other coordinates)
var normalizedTestScalarField = getNormalizedField(testScalarField); // Normalized version, needed for non-linear effects e.g. roughtly considering friction
var normalizedGradField = getNormalizedField(gradField); // Normalized version, needed for non-linear effects e.g. roughtly considering friction
var normalizedDivGradField = getNormalizedField(divGradField); // Normalized version, needed for non-linear effects e.g. roughtly considering friction
log("uneval(testScalarField) = " + uneval(testScalarField));
log("uneval(normalizedTestScalarField) = " + uneval(normalizedTestScalarField));
log("uneval(gradField) = " + uneval(gradField));
log("uneval(normalizedGradField) = " + uneval(normalizedGradField));
log("uneval(divGradField) = " + uneval(divGradField));
log("uneval(normalizedDivGradField) = " + uneval(normalizedDivGradField));

EDIT:

One problem is that defining divergence and gradient would shift the field by 1/2 of the maximum cell diameter of the grid and reduce the size of the hypercube the field is defined in by 1. The size reduction can be avoided by "wrapping" the cube's side in each dimension e.g. wrapping the left to the right. The shift comes from taking the difference of the cell itself to an appending field in each dimensions direction. So the "point" where the calculation "takes place" is between the cells. The shift can be avoided by ignoring the field the operation is applied to and use the previous and next field instead. That however leads to two decoupled grids like black and white fields on a chess board. Using such a decoupled approach for erosion leads to two smooth fields stacked in each other and together are extremely rough (so nothing is gained). The decoupling could be avoided by also taking into account the diagonal differences (like the last example here: http://en.wikipedia.org/wiki/Discrete_Laplace_operator#Implementation_in_Image_Processing )

Edited by FeXoR
Link to comment
Share on other sites

Much to our regret we must report the program has encountered an error.

Please let us know at http://trac.wildfiregames.com/ and attach the crashlog.txt and crashlog.dmp files.

Details: unhandled exception (Access violation reading 0x00000024)

Location: unknown:0 (?)

Call stack:

(error while dumping stack: No stack frames found)

errno = 0 (No error reported here)

OS error = 487 (Attempt to access invalid address.)

i found this error in the map editor it caused it to close down

Link to comment
Share on other sites

Don't know if you are already aware of this, but sometimes walls are generated which exceed territory boundaries, which apparently causes them to gradually lose health and eventually die:

This happens because some wall parts are outside the map while the territory border can't expand over the map border. I could add restrictions not to place wall entities outside the map.

The better way to deal with that would IMO be a check in each random map for the size of the map and dependent on that and how close the player start locations can be to the border the map designer should disable Iberian walls (e.g. by placing them with "placeCivDefaultEntities(x, y, playerID, BUILDING_ANGlE, {'iberWall' : false});"). I just can't know well what the map designer wants/ how much space each map grants. I could change the default to: Map size = tiny -> no Iberian walls, map size = small -> Iberians get towers only, map size medium or greater -> Iberians get full walls.

Feedback welcome...

Link to comment
Share on other sites

Much to our regret we must report the program has encountered an error.

Please let us know at http://trac.wildfiregames.com/ and attach the crashlog.txt and crashlog.dmp files.

Details: unhandled exception (Access violation reading 0x00000024)

Location: unknown:0 (?)

Call stack:

(error while dumping stack: No stack frames found)

errno = 0 (No error reported here)

OS error = 487 (Attempt to access invalid address.)

i found this error in the map editor it caused it to close down

Please attach the crashlog.txt and crashlog.dmp files. Also please tell us what you were doing in atlas at the time of the crash.

Link to comment
Share on other sites

  • 2 weeks later...

Just noticed that RMGEN function SimpleTerrain.placeNew() random placement angle is 0 to Pi but should be 0 to 2*Pi.

ATM I'm trying to figure out how to place a terrain with an actor similar to "terrain|entityPath" but it's not working with placeTerrain().

Edited by FeXoR
Link to comment
Share on other sites

  • 3 weeks later...
  • 1 month later...

FeXoR: Nice Idea. Although I'm afraid it won't have enough room if it is played with many players in small maps.

True and it's buggy on tiny maps with 4+ Players, small maps with 6+ Players and even on medium maps with 8 players:

post-14196-0-10170900-1354613135_thumb.j post-14196-0-19917800-1354613157_thumb.j post-14196-0-49764300-1354613182_thumb.j

But for reasonable map size to player ratio it's quite OK:

post-14196-0-64862100-1354613508_thumb.j post-14196-0-43898200-1354613593_thumb.j post-14196-0-38789700-1354613686_thumb.j

post-14196-0-97542400-1354613786_thumb.j post-14196-0-78504200-1354613897_thumb.j

The problem that maps don't work properly/are quite unplayable with to many players on to small maps is quite common for random maps.

My main concern is that most parts of the wall are to simple IMO. So if anyone has cool ideas plz let me know.

ATM I'm thinking of adding palisades in front of the main walls...

BTW: I found a typo in wall_builder.js. Replace:


wallStyles[style]["defenseTower"] = new WallElement("defenseTower", "structures/" + civ + "_defenseTower", PI, 0, -4*wallScaleByType[style]);

with:


wallStyles[style]["defenseTower"] = new WallElement("defenseTower", "structures/" + civ + "_defense_tower", PI, 0, -4*wallScaleByType[style]);

("_defenseTower" -> "_defense_tower")

It doesn't effect the official maps but raises an error if that wall element is used in other maps. It would be nice if you could fix that, Spahbod.

A side note: It feels like the tower upgrade that adds twice the number of arrows for each garrisoned unit is overpowered. Maybe scale it to 1 + ceil(1.5 * numberOfGarrisonedUnits) arrows? That way a tower garrisoned with 5 units (capable of adding arrows to the tower) would have 9 arrows which seams enough (compared to 11 arrow as is now or 7 arrows with the other tower upgrade). So ungarrisoned towers would have more arrows with the other tower upgrade while towers garrisoned with 3 to 5 units have more arrows with the upgrade in question (towers with 1 or 2 garrisoned units would have the same amount of arrows despite the upgrade chosen).

Edited by FeXoR
Link to comment
Share on other sites

looks very funny map, but i asked if works fine because the map Borders and some wall end segments. and obvious put it fauna and flora.

that i means.

UYXEOl.jpg

what happens if this happens xD

The wall outside the map will slowly fall apart (because the territory border is not expanded beyonf the map border).

This is a know issue and I put (untested code) for this (removes all objects outside the map border) here: http://www.wildfireg...=40#entry254333

(See the spoiler)

A better way IMO would be to just not place the civ default walls for Iberians on maps that don't grand space for it (by the map designer).

Edited by FeXoR
Link to comment
Share on other sites

The wall outside the map will slowly fall apart (because the territory border is not expanded beyonf the map border).

This is a know issue and I put (untested code) for this (removes all objects outside the map border) here: http://www.wildfireg...=40#entry254333

(See the spoiler)

A better way IMO would be to just not place the civ default walls for Iberians on maps that don't grand space for it (by the map designer).

I think we shouldn't place those walls in tiny maps.

Link to comment
Share on other sites

I think we shouldn't place those walls in tiny maps.

That would be this:

misc.zip

misc.js.diff

That does:

As long as the map does NOT explicitly set the defenses to add for Iberians it adds nothing in tiny maps and walls on non-tiny maps. Explicitly setting Iberians defenses to "walls" overrides the default and so walls will be place for any Iberian player on any map size.

Example to override default behavior:

placeCivDefaultEntities(x, y, playerId, BUILDING_ANGlE, {"iberWall": "walls"});

I think that is best.

EDIT: I should add the abiltiy to set the radius of the defenses, too. In the map "Snowflake Searocks" for example the radius is to big. That way RMS designers could tweak the iberian walls better. Please let me know when/if the patch is added, Spahbod, so I can add that functionality. THX.

EDIT2: While you're at it could you please fix the typo I mentioned in http://www.wildfireg...=60#entry257116 Oh!, Already fixed, thx!

Edited by FeXoR
Link to comment
Share on other sites

Is there a particular reason why the Iberians in Acropolis 7 (the southern acropolis) don't get any gates, when the Iberians in Acropolis 2 (the northern acropolis) get gates? In Acropolis 7, the southern ramp can be walled off but the wall segment is too short to support a gate, while the northern ramp can't even be walled off without rebuilding the walls. Also, the default AI in Acropolis 7 is Aegis Bot when at least most of the other maps have qbot as the default AI instead.

Link to comment
Share on other sites

Is there a particular reason why the Iberians in Acropolis 7 (the southern acropolis) don't get any gates, when the Iberians in Acropolis 2 (the northern acropolis) get gates? In Acropolis 7, the southern ramp can be walled off but the wall segment is too short to support a gate, while the northern ramp can't even be walled off without rebuilding the walls. Also, the default AI in Acropolis 7 is Aegis Bot when at least most of the other maps have qbot as the default AI instead.

i noticed that too. i report that, but i forgot about that.
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...