mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-06-21 10:03:43 +00:00
rmgen random placement and Entity instantiation refactoring, fixes #4992.
ChainPlacer, ClumpPlacer, SimpleObject receive the vectors that are in place everywhere already, refs #4845. Add public setCenterPosition to CenteredPlacer and Group rather than writing private properties of the prototypes. ChainPlacer and ClumpPlacer simplifications, deduplication and renames, refs #4805. Replace placeObject global with RandomMap placeEntity members, refs #4804. Split to placeEntityPassable / placeEntityAnywhere, as well as validTilePassable / validTileAnywhere to more cleanly distinguish actor and casual entity placement. No more does SimpleObject create Entity instances and register entityIDs if they are never placed. Removes the map global reference from the Entity constructor, refs #4964. By definition of what is passed to the engine, an Entity has an ID and position, so keep it impossible to create Entities without IDs. Implement randomPositionOnTile so that there aren't different implementations thereof, including unintented ones as in ardennes_forest.js in2f7610160f. On Caledonian Meadows, remove unused pathplacing code, to be superseded by #4368. On Schwarzwald, delete unused startLocations followingb180ad3e5d. On Latium, replace complicated duplicated hardcoded fish location computation with a simple HeightConstraint, refs #4960. This was SVN commit r21069.
This commit is contained in:
@@ -265,7 +265,11 @@ for (var ix = 0; ix < mapSize; ix++)
|
||||
|
||||
if (h > 35 && randBool(0.1) ||
|
||||
h < 15 && randBool(0.05) && clHillDeco.countMembersInRadius(position, 1) == 0)
|
||||
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), pickRandom(aTrees), 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(
|
||||
pickRandom(aTrees),
|
||||
0,
|
||||
randomPositionOnTile(position),
|
||||
randomAngle());
|
||||
}
|
||||
|
||||
var explorableArea = g_Map.createArea(explorablePoints);
|
||||
|
||||
@@ -368,7 +368,7 @@ for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; x++)
|
||||
}
|
||||
|
||||
if (template)
|
||||
placeObject(position, template, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(template, 0, position, randomAngle());
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -407,7 +407,7 @@ else
|
||||
new Vector2D(-0.75 * resourceSpacing * Math.floor(resourceCount / 2), 0).rotate(-uAngle - Math.PI/2)
|
||||
]);
|
||||
|
||||
placeObject(pos, j % 2 ? "gaia/flora_tree_cypress" : "gaia/flora_bush_berry", 0, randomAngle());
|
||||
g_Map.placeEntityPassable(j % 2 ? "gaia/flora_tree_cypress" : "gaia/flora_bush_berry", 0, pos, randomAngle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,41 +2,35 @@ Engine.LoadLibrary("rmgen");
|
||||
Engine.LoadLibrary("rmbiome");
|
||||
Engine.LoadLibrary("heightmap");
|
||||
|
||||
var tGrove = "temp_grass_plants";
|
||||
var tPath = "road_rome_a";
|
||||
|
||||
var oGroveEntities = ["structures/gaul_outpost", "gaia/flora_tree_oak_new"];
|
||||
|
||||
var g_Map = new RandomMap(0, "whiteness");
|
||||
|
||||
/**
|
||||
* Drags a path to a target height smoothing it at the edges and return some points along the path.
|
||||
*/
|
||||
function placeRandomPathToHeight(
|
||||
start, target, targetHeight, tileClass = undefined, texture = "road_rome_a",
|
||||
width = 10, distance = 4, strength = 0.08, heightmap = g_Map.height)
|
||||
function placeRandomPathToHeight(start, target, targetHeight, tileClass, texture, width, distance, strength, heightmap)
|
||||
{
|
||||
let pathPoints = [];
|
||||
let position = clone(start);
|
||||
while (true)
|
||||
let painters = [new TerrainPainter(texture)];
|
||||
|
||||
if (tileClass)
|
||||
painters.push(new TileClassPainter(tileClass));
|
||||
|
||||
let position = start.clone();
|
||||
while (position.distanceTo(target) >= distance / 2)
|
||||
{
|
||||
rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap);
|
||||
if (texture)
|
||||
{
|
||||
let painters = [new TerrainPainter(texture)];
|
||||
rectangularSmoothToHeight(position, width * 3, width * 3, targetHeight, strength, heightmap);
|
||||
|
||||
if (tileClass)
|
||||
painters.push(new TileClassPainter(tileClass));
|
||||
createArea(
|
||||
new ClumpPlacer(diskArea(width), 1, 1, 1, position),
|
||||
painters);
|
||||
|
||||
createArea(
|
||||
new ClumpPlacer(diskArea(0.3 * width), 1, 1, 1, position),
|
||||
painters);
|
||||
}
|
||||
pathPoints.push({ "x": position.x, "y": position.y, "dist": distance });
|
||||
// Check for distance to target and setup for next loop if needed
|
||||
if (Math.euclidDistance2D(position.x, position.y, target.x, target.y) < distance / 2)
|
||||
break;
|
||||
let angleToTarget = getAngle(position.x, position.y, target.x, target.y);
|
||||
let angleOff = randFloat(-1, 1) * Math.PI / 2;
|
||||
position.x += distance * Math.cos(angleToTarget + angleOff);
|
||||
position.y += distance * Math.sin(angleToTarget + angleOff);
|
||||
position.add(new Vector2D(distance, 0).rotate(
|
||||
-getAngle(position.x, position.y, target.x, target.y) - randFloat(-1, 1) * Math.PI / 2));
|
||||
}
|
||||
return pathPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,15 +47,15 @@ let decorations = [
|
||||
|
||||
function placeMine(point, centerEntity)
|
||||
{
|
||||
placeObject(point, centerEntity, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(centerEntity, 0, point, randomAngle());
|
||||
let quantity = randIntInclusive(11, 23);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
|
||||
for (let i = 0; i < quantity; ++i)
|
||||
placeObject(
|
||||
Vector2D.add(point, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
|
||||
g_Map.placeEntityPassable(
|
||||
pickRandom(decorations),
|
||||
0,
|
||||
Vector2D.add(point, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
|
||||
randomAngle());
|
||||
}
|
||||
|
||||
@@ -123,7 +117,7 @@ let clGrove = g_Map.createTileClass();
|
||||
|
||||
function placeGrove(point)
|
||||
{
|
||||
placeObject(point, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(oGroveEntities), 0, point, randomAngle());
|
||||
let quantity = randIntInclusive(20, 30);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
for (let i = 0; i < quantity; ++i)
|
||||
@@ -134,11 +128,11 @@ function placeGrove(point)
|
||||
if (i % 3 == 0)
|
||||
objectList = groveActors;
|
||||
let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle));
|
||||
placeObject(position, pickRandom(objectList), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(objectList), 0, position, randomAngle());
|
||||
createArea(
|
||||
new ClumpPlacer(5, 1, 1, 1, position),
|
||||
[
|
||||
new TerrainPainter("temp_grass_plants"),
|
||||
new TerrainPainter(tGrove),
|
||||
new TileClassPainter(clGrove)
|
||||
]);
|
||||
}
|
||||
@@ -153,14 +147,14 @@ function placeCamp(point,
|
||||
]
|
||||
)
|
||||
{
|
||||
placeObject(point, centerEntity, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(centerEntity, 0, point, randomAngle());
|
||||
let quantity = randIntInclusive(5, 11);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
for (let i = 0; i < quantity; ++i)
|
||||
{
|
||||
let angle = dAngle * randFloat(i, i + 1);
|
||||
let dist = randFloat(1, 3);
|
||||
placeObject(Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), pickRandom(otherEntities), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(otherEntities), 0, Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), randomAngle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +179,7 @@ function placeStartLocationResources(point, foodEntities = ["gaia/flora_bush_ber
|
||||
if (i % 2 == 0)
|
||||
objectList = groveActors;
|
||||
let woodPosition = Vector2D.add(point, new Vector2D(randFloat(10, 15), 0).rotate(-angle));
|
||||
placeObject(woodPosition, pickRandom(objectList), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(objectList), 0, woodPosition, randomAngle());
|
||||
createArea(
|
||||
new ClumpPlacer(5, 1, 1, 1, woodPosition),
|
||||
[
|
||||
@@ -209,7 +203,7 @@ function placeStartLocationResources(point, foodEntities = ["gaia/flora_bush_ber
|
||||
{
|
||||
angle = currentAngle + randFloat(0, dAngle);
|
||||
let berriesPosition = Vector2D.add(point, new Vector2D(randFloat(10, 15), 0).rotate(-angle));
|
||||
placeObject(berriesPosition, pickRandom(foodEntities), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(foodEntities), 0, berriesPosition, randomAngle());
|
||||
currentAngle += dAngle;
|
||||
}
|
||||
}
|
||||
@@ -321,27 +315,22 @@ myBiome.push({ // 10 Hilltop
|
||||
"textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0]
|
||||
});
|
||||
|
||||
let [playerIDs, startLocations] = sortPlayersByLocation(getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] }, 1000, 30));
|
||||
let [playerIDs, playerPosition] = sortPlayersByLocation(getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] }, 1000, 30));
|
||||
Engine.SetProgress(30);
|
||||
|
||||
log("Smooth player locations...");
|
||||
for (let p = 0; p < playerIDs.length; ++p)
|
||||
rectangularSmoothToHeight(startLocations[p], 35, 35, playerHeight, 0.7);
|
||||
rectangularSmoothToHeight(playerPosition[p], 35, 35, playerHeight, 0.7);
|
||||
|
||||
log("Creating paths...");
|
||||
let tchm = getTileCenteredHeightmap(); // Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false)
|
||||
let pathPoints = [];
|
||||
let tchm = getTileCenteredHeightmap();
|
||||
let clPath = g_Map.createTileClass();
|
||||
for (let i = 0; i < startLocations.length; ++i)
|
||||
{
|
||||
let start = startLocations[i];
|
||||
let target = startLocations[(i + 1) % startLocations.length];
|
||||
pathPoints = pathPoints.concat(placeRandomPathToHeight(start, target, playerHeight, clPath));
|
||||
}
|
||||
for (let i = 0; i < playerPosition.length; ++i)
|
||||
placeRandomPathToHeight(playerPosition[i], playerPosition[(i + 1) % playerPosition.length], playerHeight, clPath, tPath, 4, 4, 0.08, g_Map.height);
|
||||
Engine.SetProgress(45);
|
||||
|
||||
log("Determining resource locations...");
|
||||
let avoidPoints = clone(startLocations);
|
||||
let avoidPoints = playerPosition.map(pos => pos.clone());
|
||||
for (let i = 0; i < avoidPoints.length; ++i)
|
||||
avoidPoints[i].dist = 30;
|
||||
let resourceSpots = getPointsByHeight({ "min": (heighLimits[3] + heighLimits[4]) / 2, "max": (heighLimits[5] + heighLimits[6]) / 2 }, avoidPoints, clPath);
|
||||
@@ -366,8 +355,8 @@ for (let x = 0; x < tchm.length; ++x)
|
||||
areas[h].push(position);
|
||||
break;
|
||||
}
|
||||
else
|
||||
minHeight = heighLimits[h];
|
||||
|
||||
minHeight = heighLimits[h];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +404,7 @@ for (let h = 0; h < heighLimits.length; ++h)
|
||||
g_Map.setTexture(point, texture);
|
||||
|
||||
if (actor)
|
||||
placeObject(Vector2D.add(point, new Vector2D(randFloat(0, 1), randFloat(0, 1))), actor, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(actor, 0, randomPositionOnTile(point), randomAngle());
|
||||
}
|
||||
Engine.SetProgress(80);
|
||||
|
||||
@@ -425,9 +414,8 @@ if (isNomad())
|
||||
else
|
||||
for (let p = 0; p < playerIDs.length; ++p)
|
||||
{
|
||||
let pos = new Vector2D(startLocations[p].x, startLocations[p].y);
|
||||
placeCivDefaultStartingEntities(pos, playerIDs[p], true);
|
||||
placeStartLocationResources(pos);
|
||||
placeCivDefaultStartingEntities(playerPosition[p], playerIDs[p], true);
|
||||
placeStartLocationResources(playerPosition[p]);
|
||||
}
|
||||
|
||||
log("Placing resources, farmsteads, groves and camps...");
|
||||
|
||||
@@ -338,7 +338,11 @@ createStragglerTrees(
|
||||
log("Creating treasures...");
|
||||
for (let i = 0; i < randIntInclusive(3, 8); ++i)
|
||||
for (let template of [oFoodTreasure, oWoodTreasure])
|
||||
placeObject(Vector2D.add(mapCenter, new Vector2D(randFloat(0, 7), 0).rotate(randomAngle())), template, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(
|
||||
template,
|
||||
0,
|
||||
Vector2D.add(mapCenter, new Vector2D(randFloat(0, 7), 0).rotate(randomAngle())),
|
||||
randomAngle());
|
||||
|
||||
placePlayersNomad(
|
||||
clPlayer,
|
||||
|
||||
@@ -298,17 +298,17 @@ if (gallicCC)
|
||||
new TileClassPainter(clRitualPlace)
|
||||
]);
|
||||
|
||||
placeObject(meetingPlacePosition, aCampfire, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(aCampfire, 0, meetingPlacePosition, randomAngle());
|
||||
|
||||
for (let participants of ritualParticipants)
|
||||
{
|
||||
let [positions, angles] = distributePointsOnCircle(participants.count, startAngle, participants.radius * mRadius, meetingPlacePosition);
|
||||
for (let i = 0; i < positions.length; ++i)
|
||||
placeObject(positions[i], pickRandom(participants.templates), 0, angles[i] + participants.angle);
|
||||
g_Map.placeEntityPassable(pickRandom(participants.templates), 0, positions[i], angles[i] + participants.angle);
|
||||
}
|
||||
}
|
||||
|
||||
placeObject(civicCenterPosition, oCivicCenter, 0, startAngle + BUILDING_ORIENTATION + Math.PI * 3/2 * i);
|
||||
g_Map.placeEntityPassable(oCivicCenter, 0, civicCenterPosition, startAngle + BUILDING_ORIENTATION + Math.PI * 3/2 * i);
|
||||
|
||||
// Create the city patch
|
||||
createArea(
|
||||
@@ -324,10 +324,10 @@ if (gallicCC)
|
||||
|
||||
// Place treasure, potentially inside buildings
|
||||
for (let i = 0; i < gallicCCTreasureCount; ++i)
|
||||
placeObject(
|
||||
Vector2D.add(civicCenterPosition, new Vector2D(randFloat(-1, 1) * 0.8 * gaulCityRadius, 0).rotate(randomAngle())),
|
||||
g_Map.placeEntityPassable(
|
||||
pickRandom(oTreasures),
|
||||
0,
|
||||
Vector2D.add(civicCenterPosition, new Vector2D(randFloat(-0.8, 0.8) * gaulCityRadius, 0).rotate(randomAngle())),
|
||||
randomAngle());
|
||||
}
|
||||
}
|
||||
@@ -716,7 +716,7 @@ log("Creating patrol points for land attackers...");
|
||||
clMiddle.add(mapCenter);
|
||||
|
||||
log("Creating triggerpoint to allow the triggerscript to determine the river direction...");
|
||||
placeObject(Vector2D.add(mapCenter, new Vector2D(0, 1).rotate(startAngle)), triggerPointRiverDirection, 0, 0);
|
||||
g_Map.placeEntityAnywhere(triggerPointRiverDirection, 0, Vector2D.add(mapCenter, new Vector2D(0, 1).rotate(startAngle)), randomAngle());
|
||||
|
||||
for (let i = 0; i < 2; ++i)
|
||||
{
|
||||
|
||||
@@ -134,7 +134,7 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
let angle = playerAngle[i] + angleDist * (rIndex + 1) / (resourcePerPlayer.length + 1);
|
||||
let position = Vector2D.add(mapCenter, new Vector2D(resourceRadius, 0).rotate(-angle)).round();
|
||||
|
||||
placeObject(position, resourcePerPlayer[rIndex], 0, randomAngle());
|
||||
g_Map.placeEntityPassable(resourcePerPlayer[rIndex], 0, position, randomAngle());
|
||||
|
||||
createArea(
|
||||
new ClumpPlacer(40, 1/2, 1/8, 1, position),
|
||||
@@ -147,7 +147,7 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
Engine.SetProgress(60);
|
||||
|
||||
log("Placing temple...");
|
||||
placeObject(mapCenter, templateTemple, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(templateTemple, 0, mapCenter, randomAngle());
|
||||
clBaseResource.add(mapCenter);
|
||||
|
||||
log("Creating central mountain...");
|
||||
|
||||
@@ -173,8 +173,7 @@ createObjectGroup(
|
||||
[new SimpleObject(oMetalLarge, 3, 6, 25, Math.floor(fractionToTiles(0.25)))],
|
||||
true,
|
||||
clBaseResource,
|
||||
mapCenter.x,
|
||||
mapCenter.y),
|
||||
mapCenter),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]);
|
||||
|
||||
@@ -183,8 +182,7 @@ createObjectGroup(
|
||||
[new SimpleObject(oStoneLarge, 3, 6, 25, Math.floor(fractionToTiles(0.25)))],
|
||||
true,
|
||||
clBaseResource,
|
||||
mapCenter.x,
|
||||
mapCenter.y),
|
||||
mapCenter),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]);
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
for (let treasure of treasures)
|
||||
{
|
||||
let position = Vector2D.add(playerPosition[i], new Vector2D(10, 0).rotate(-bbAngle))
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(oFoodTreasure, 5, 5, 0, 2)], true, clBaseResource, position.x, position.y), 0);
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(oFoodTreasure, 5, 5, 0, 2)], true, clBaseResource, position), 0);
|
||||
bbAngle += Math.PI / 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,41 +53,49 @@ function rescaleHeightmap(minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, height
|
||||
|
||||
/**
|
||||
* Get start location with the largest minimum distance between players
|
||||
* @param {array} [heightRange] - The height range start locations are allowed
|
||||
* @param {object} [heightRange] - The height range start locations are allowed
|
||||
* @param {integer} [maxTries=1000] - How often random player distributions are rolled to be compared
|
||||
* @param {float} [minDistToBorder=20] - How far start locations have to be away from the map border
|
||||
* @param {integer} [numberOfPlayers=g_MapSettings.PlayerData.length] - How many start locations should be placed
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap for the start locations to be placed on
|
||||
* @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular
|
||||
* @return {array} [finalStartLoc] - Array of 2D points in the format { "x": float, "y": float}
|
||||
* @return {Vector2D[]}
|
||||
*/
|
||||
function getStartLocationsByHeightmap(heightRange, maxTries = 1000, minDistToBorder = 20, numberOfPlayers = g_MapSettings.PlayerData.length - 1, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)
|
||||
{
|
||||
let validStartLoc = [];
|
||||
let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius
|
||||
for (let x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x)
|
||||
for (let y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y)
|
||||
if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max) // Is in height range
|
||||
if (!isCircular || r - Math.euclidDistance2D(x, y, r, r) >= minDistToBorder) // Is far enough away from map border
|
||||
validStartLoc.push({ "x": x, "y": y });
|
||||
let mapCenter = g_Map.getCenter();
|
||||
let mapSize = g_Map.getSize();
|
||||
|
||||
let heightConstraint = new HeightConstraint(heightRange.min, heightRange.max);
|
||||
|
||||
for (let x = minDistToBorder; x < mapSize - minDistToBorder; ++x)
|
||||
for (let y = minDistToBorder; y < mapSize - minDistToBorder; ++y)
|
||||
{
|
||||
let position = new Vector2D(x, y);
|
||||
if (heightConstraint.allows(position) && (!isCircular || position.distanceTo(mapCenter)) < mapSize / 2 - minDistToBorder)
|
||||
validStartLoc.push(position);
|
||||
}
|
||||
|
||||
let maxMinDist = 0;
|
||||
let finalStartLoc;
|
||||
|
||||
for (let tries = 0; tries < maxTries; ++tries)
|
||||
{
|
||||
let startLoc = [];
|
||||
let minDist = Infinity;
|
||||
|
||||
for (let p = 0; p < numberOfPlayers; ++p)
|
||||
startLoc.push(pickRandom(validStartLoc));
|
||||
|
||||
for (let p1 = 0; p1 < numberOfPlayers - 1; ++p1)
|
||||
{
|
||||
for (let p2 = p1 + 1; p2 < numberOfPlayers; ++p2)
|
||||
{
|
||||
let dist = Math.euclidDistance2D(startLoc[p1].x, startLoc[p1].y, startLoc[p2].x, startLoc[p2].y);
|
||||
let dist = startLoc[p1].distanceTo(startLoc[p2]);
|
||||
if (dist < minDist)
|
||||
minDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
if (minDist > maxMinDist)
|
||||
{
|
||||
maxMinDist = minDist;
|
||||
@@ -230,7 +238,7 @@ function globalSmoothHeightmap(strength = 0.8, heightmap = g_Map.height, smoothM
|
||||
/**
|
||||
* Pushes a rectangular area towards a given height smoothing it into the original terrain
|
||||
* @note The window function to determine the smooth is not exactly a gaussian to ensure smooth edges
|
||||
* @param {object} [center] - The x and y coordinates of the center point (rounded in this function)
|
||||
* @param {Vector2D} center - The x and y coordinates of the center point (rounded in this function)
|
||||
* @param {float} [dx] - Distance from the center in x direction the rectangle ends (half width, rounded in this function)
|
||||
* @param {float} [dy] - Distance from the center in y direction the rectangle ends (half depth, rounded in this function)
|
||||
* @param {float} [targetHeight] - Height the center of the rectangle will be pushed to
|
||||
|
||||
@@ -111,7 +111,7 @@ for (let i = 0; i < teams.length; ++i)
|
||||
{
|
||||
let position = Vector2D.add(playerPosition[p], new Vector2D(g_InitialMineDistance, 0).rotate(-playerAngle[p] - mine.angle));
|
||||
createObjectGroup(
|
||||
new SimpleGroup([new SimpleObject(mine.template, 1, 1, 0, 4)], true, clBaseResource, position.x, position.y),
|
||||
new SimpleGroup([new SimpleObject(mine.template, 1, 1, 0, 4)], true, clBaseResource, position),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 5)]);
|
||||
}
|
||||
@@ -124,11 +124,10 @@ for (let i = 0; i < teams.length; ++i)
|
||||
{
|
||||
let tAngle = playerAngle[p] + randFloat(-1, 1) * 2 * Math.PI / teams[i].length;
|
||||
let treePosition = Vector2D.add(playerPosition[p], new Vector2D(16, 0).rotate(-tAngle)).round();
|
||||
let group = new SimpleGroup(
|
||||
[new SimpleObject(oTree2, g_InitialTrees, g_InitialTrees, 0, 7)],
|
||||
true, clBaseResource, treePosition.x, treePosition.y
|
||||
);
|
||||
if (createObjectGroup(group, 0, [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 4)]))
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup([new SimpleObject(oTree2, g_InitialTrees, g_InitialTrees, 0, 7)], true, clBaseResource, treePosition),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 4)]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -153,17 +152,17 @@ for (let i = 0; i < teams.length; ++i)
|
||||
log("Creating huntable animals for team " + i + "...");
|
||||
for (let p = 0; p < teams[i].length; ++p)
|
||||
{
|
||||
let group = new SimpleGroup(
|
||||
[new SimpleObject(oMainHuntableAnimal, 2 * numPlayers / numTeams, 2 * numPlayers / numTeams, 0, Math.floor(fractionToTiles(0.2)))],
|
||||
true, clBaseResource, teamPosition[i].x, teamPosition[i].y
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
|
||||
createObjectGroup(
|
||||
new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 2 * numPlayers / numTeams, 2 * numPlayers / numTeams, 0, Math.floor(fractionToTiles(0.2)))], true, clBaseResource, teamPosition[i]),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
|
||||
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oSecondaryHuntableAnimal, 4 * numPlayers / numTeams, 4 * numPlayers / numTeams, 0, Math.floor(fractionToTiles(0.2)))],
|
||||
true, clBaseResource, teamPosition[i].x, teamPosition[i].y
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(oSecondaryHuntableAnimal, 4 * numPlayers / numTeams, 4 * numPlayers / numTeams, 0, Math.floor(fractionToTiles(0.2)))],
|
||||
true, clBaseResource, teamPosition[i]),
|
||||
0,
|
||||
[avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ if (!isNomad())
|
||||
]);
|
||||
|
||||
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , heightLand - 0.5, heightLand);
|
||||
placeObject(dockLocation, oDock, playerIDs[i], playerAngle[i] + Math.PI);
|
||||
g_Map.placeEntityPassable(oDock, playerIDs[i], dockLocation, playerAngle[i] + Math.PI);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,14 +304,14 @@ for (var ix = 0; ix < mapSize; ix++)
|
||||
{
|
||||
t = (diffH > 1.2) ? tGrassCliff : tGrassDry;
|
||||
if (diffH < 0.5 && randBool(0.02))
|
||||
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), aGrassDry, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(aGrassDry, 0, randomPositionOnTile(position), randomAngle());
|
||||
}
|
||||
else if (grassNoise > 0.61)
|
||||
{
|
||||
t = (diffH > 1.2 ? tGrassRock : tGrassShrubs);
|
||||
}
|
||||
else if (diffH < 0.5 && randBool(0.02))
|
||||
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), aGrass, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(aGrass, 0, randomPositionOnTile(position), randomAngle());
|
||||
}
|
||||
|
||||
createTerrain(t).place(position);
|
||||
@@ -426,27 +426,15 @@ createObjectGroupsDeprecated(group, 0,
|
||||
Engine.SetProgress(85);
|
||||
|
||||
log("Creating fish...");
|
||||
var num = scaleByMapSize(4, 16);
|
||||
var offsetX = mapSize * WATER_WIDTH/2;
|
||||
for (let i = 0; i < num; ++i)
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(oFish, 1, 1, 0, 1)],
|
||||
true,
|
||||
clFood,
|
||||
randIntInclusive(offsetX / 2, offsetX * 3/2),
|
||||
Math.round((i + 0.5) * mapSize / num)),
|
||||
0);
|
||||
|
||||
for (let i = 0; i < num; ++i)
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(oFish, 1, 1, 0, 1)],
|
||||
true,
|
||||
clFood,
|
||||
randIntInclusive(mapSize - offsetX * 3/2, mapSize - offsetX / 2),
|
||||
Math.round((i + 0.5) * mapSize / num)),
|
||||
0);
|
||||
createObjectGroups(
|
||||
new SimpleGroup([new SimpleObject(oFish, 1, 1, 0, 1)], true, clFood),
|
||||
0,
|
||||
[
|
||||
avoidClasses(clFood, 10),
|
||||
stayClasses(clWater, 4),
|
||||
new HeightConstraint(-Infinity, heightLand)
|
||||
],
|
||||
scaleByMapSize(8, 32));
|
||||
|
||||
Engine.SetProgress(90);
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
continue;
|
||||
|
||||
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 2.6, 3);
|
||||
placeObject(dockLocation, oDock, playerIDs[i], playerAngle[i] + Math.PI);
|
||||
g_Map.placeEntityPassable(oDock, playerIDs[i], dockLocation, playerAngle[i] + Math.PI);
|
||||
}
|
||||
Engine.SetProgress(10);
|
||||
|
||||
|
||||
@@ -166,8 +166,7 @@ for (var i = 0; i < num; ++i)
|
||||
],
|
||||
true,
|
||||
clFood,
|
||||
animalPosition.x,
|
||||
animalPosition.y),
|
||||
animalPosition),
|
||||
0);
|
||||
}
|
||||
|
||||
@@ -185,7 +184,7 @@ for (var i = 0; i < num; ++i)
|
||||
++r;
|
||||
} while (!constraint.allows(fishPosition) && r < mapSize / 2);
|
||||
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(oFish, 1, 1, 0, 1)], true, clFood, fishPosition.x, fishPosition.y), 0);
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(oFish, 1, 1, 0, 1)], true, clFood, fishPosition), 0);
|
||||
}
|
||||
Engine.SetProgress(35);
|
||||
|
||||
|
||||
@@ -91,24 +91,10 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
waterPosition = Vector2D.add(forestPosition, new Vector2D(6, 0).rotate(-waterAngle)).round();
|
||||
|
||||
let flowerPosition = Vector2D.add(forestPosition, new Vector2D(3, 0).rotate(-waterAngle)).round();
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(aFlower1, 1, 5, 0, 3)],
|
||||
true,
|
||||
undefined,
|
||||
flowerPosition.x,
|
||||
flowerPosition.y),
|
||||
0);
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(aFlower1, 1, 5, 0, 3)], true, undefined, flowerPosition), 0);
|
||||
|
||||
let reedsPosition = Vector2D.add(forestPosition, new Vector2D(5, 0).rotate(-waterAngle)).round();
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(aReedsA, 1, 3, 0, 0)],
|
||||
true,
|
||||
undefined,
|
||||
reedsPosition.x,
|
||||
reedsPosition.y),
|
||||
0);
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(aReedsA, 1, 3, 0, 0)], true, undefined, reedsPosition), 0);
|
||||
|
||||
} while (
|
||||
!createArea(
|
||||
@@ -265,8 +251,7 @@ for (let i = 0; i < scaleByMapSize(5, 30); ++i)
|
||||
],
|
||||
true,
|
||||
clFood,
|
||||
animalPos.x,
|
||||
animalPos.y),
|
||||
animalPos),
|
||||
0);
|
||||
}
|
||||
Engine.SetProgress(90);
|
||||
@@ -280,6 +265,11 @@ createObjectGroupsDeprecated(group, 0,
|
||||
scaleByMapSize(10, 40), 20
|
||||
);
|
||||
|
||||
var objectsWaterFlora = [
|
||||
new SimpleObject(aReedsA, 5, 12, 0, 2),
|
||||
new SimpleObject(aReedsB, 5, 12, 0, 2)
|
||||
];
|
||||
|
||||
log("Creating sand blows and beautifications");
|
||||
for (var sandx = 0; sandx < mapSize; sandx += 4)
|
||||
for (var sandz = 0; sandz < mapSize; sandz += 4)
|
||||
@@ -290,36 +280,21 @@ for (var sandx = 0; sandx < mapSize; sandx += 4)
|
||||
if (height > heightSand)
|
||||
{
|
||||
if (randBool((height - heightSand) / 1.4))
|
||||
{
|
||||
group = new SimpleGroup( [new SimpleObject(aSand, 0,1, 0,2)], true, undefined, sandx,sandz );
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(aSand, 0, 1, 0, 2)], true, undefined, position), 0);
|
||||
}
|
||||
else if (height > heightFloraMin && height < heightFloraMax)
|
||||
{
|
||||
if (randBool(0.4))
|
||||
{
|
||||
group = new SimpleGroup( [new SimpleObject(aWaterFlower, 1,4, 1,2)], true, undefined, sandx,sandz );
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(aWaterFlower, 1, 4, 1, 2)], true, undefined, position), 0);
|
||||
else if (randBool(0.7) && height < heightFloraReedsMax)
|
||||
{
|
||||
group = new SimpleGroup( [new SimpleObject(aReedsA, 5,12, 0,2),new SimpleObject(aReedsB, 5,12, 0,2)], true, undefined, sandx,sandz );
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
createObjectGroup(new SimpleGroup(objectsWaterFlora, true, undefined, position), 0);
|
||||
|
||||
if (clPassage.countMembersInRadius(position, 2))
|
||||
{
|
||||
if (randBool(0.4))
|
||||
{
|
||||
group = new SimpleGroup( [new SimpleObject(aWaterFlower, 1,4, 1,2)], true, undefined, sandx,sandz );
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
createObjectGroup(new SimpleGroup([new SimpleObject(aWaterFlower, 1, 4, 1, 2)], true, undefined, position), 0);
|
||||
else if (randBool(0.7) && height < heightFloraReedsMax)
|
||||
{
|
||||
group = new SimpleGroup( [new SimpleObject(aReedsA, 5,12, 0,2),new SimpleObject(aReedsB, 5,12, 0,2)], true, undefined, sandx,sandz );
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
createObjectGroup(new SimpleGroup(objectsWaterFlora, true, undefined, position), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ if (!isNomad())
|
||||
for (let i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
let marketPos = Vector2D.add(playerPosition[i], new Vector2D(12, 0).rotate(randomAngle())).round();
|
||||
placeObject(marketPos, oMarket, playerIDs[i], BUILDING_ORIENTATION);
|
||||
g_Map.placeEntityPassable(oMarket, playerIDs[i], marketPos, BUILDING_ORIENTATION);
|
||||
addCivicCenterAreaToClass(marketPos, clBaseResource);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
/**
|
||||
* @file The Entity class stores the given template name, owner and location of an entity and assigns an entityID.
|
||||
* Instances of this class (with the position using the tile coordinate system) are passed as such to the engine.
|
||||
*
|
||||
* @param orientation - rotation of this entity about the y-axis (up).
|
||||
* Instances of this class (with the position using the tile coordinate system) are
|
||||
* converted by ScriptConversions.cpp to the Entity struct defined in source/graphics/Entity.h and passed to MapReader.cpp.
|
||||
*/
|
||||
// TODO: support full position and rotation
|
||||
function Entity(templateName, playerID, x, z, orientation = 0)
|
||||
function Entity(entityID, templateName, playerID, position, orientation)
|
||||
{
|
||||
this.id = g_Map.getEntityID();
|
||||
this.templateName = templateName;
|
||||
this.player = playerID;
|
||||
|
||||
this.position = {
|
||||
"x": x,
|
||||
"y": 0,
|
||||
"z": z
|
||||
};
|
||||
|
||||
this.rotation = {
|
||||
"x": 0,
|
||||
"y": orientation,
|
||||
"z": 0
|
||||
};
|
||||
this.templateName = templateName;
|
||||
this.id = entityID;
|
||||
this.position = new Vector3D(position.x, 0, position.y);
|
||||
this.rotation = new Vector3D(0, orientation, 0);
|
||||
}
|
||||
|
||||
Entity.prototype.GetPosition2D = function()
|
||||
{
|
||||
return Vector2D.from3D(this.position);
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ function createStoneMineFormation(position, templateName, terrain, radius = 2.5,
|
||||
for (let i = 0; i < count; ++i)
|
||||
{
|
||||
let pos = Vector2D.add(position, new Vector2D(radius + randFloat(0, maxOffset), 0).rotate(-angle)).round();
|
||||
placeObject(pos, templateName, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(templateName, 0, pos, randomAngle());
|
||||
angle += 3/2 * Math.PI / count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,8 +271,7 @@ function createVolcano(position, tileClass, terrainTexture, lavaTextures, smoke,
|
||||
[new SimpleObject("actor|particle/smoke.xml", num, num, 0, 7)],
|
||||
false,
|
||||
clLava,
|
||||
position.x,
|
||||
position.y),
|
||||
position),
|
||||
0,
|
||||
stayClasses(tileClass, 1));
|
||||
}
|
||||
|
||||
@@ -16,56 +16,67 @@
|
||||
* @param objects - An array of Objects, for instance SimpleObjects.
|
||||
* @param avoidSelf - Objects will not overlap.
|
||||
* @param tileClass - Optional TileClass that tiles with placed entities are marked with.
|
||||
* @param x, z - The location the group is placed around. Can be omitted if the x and z properties are set externally.
|
||||
* @param centerPosition - The location the group is placed around. Can be omitted if the property is set externally.
|
||||
*/
|
||||
function SimpleGroup(objects, avoidSelf = false, tileClass = undefined, x = -1, z = -1)
|
||||
function SimpleGroup(objects, avoidSelf = false, tileClass = undefined, centerPosition = undefined)
|
||||
{
|
||||
this.objects = objects;
|
||||
this.tileClass = tileClass;
|
||||
this.avoidSelf = avoidSelf;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
this.centerPosition = undefined;
|
||||
|
||||
if (centerPosition)
|
||||
this.setCenterPosition(centerPosition);
|
||||
}
|
||||
|
||||
SimpleGroup.prototype.setCenterPosition = function(position)
|
||||
{
|
||||
this.centerPosition = deepfreeze(position.clone().round());
|
||||
};
|
||||
|
||||
SimpleGroup.prototype.place = function(player, constraint)
|
||||
{
|
||||
let resultObjs = [];
|
||||
let entitySpecsResult = [];
|
||||
|
||||
// Test if the Objects can be placed at the given location
|
||||
// Place none of them if one can't be placed.
|
||||
for (let object of this.objects)
|
||||
{
|
||||
let objs = object.place(this.x, this.z, player, this.avoidSelf, constraint);
|
||||
let entitySpecs = object.place(this.centerPosition, player, this.avoidSelf, constraint);
|
||||
|
||||
if (objs === undefined)
|
||||
if (!entitySpecs)
|
||||
return undefined;
|
||||
|
||||
resultObjs = resultObjs.concat(objs);
|
||||
entitySpecsResult = entitySpecsResult.concat(entitySpecs);
|
||||
}
|
||||
|
||||
// Add all objects to the map
|
||||
for (let obj of resultObjs)
|
||||
// Create and place entities as specified
|
||||
let entities = [];
|
||||
for (let entitySpecs of entitySpecsResult)
|
||||
{
|
||||
let position = new Vector2D(obj.position.x, obj.position.z);
|
||||
|
||||
if (g_Map.validTile(position))
|
||||
g_Map.addObject(obj);
|
||||
entities.push(
|
||||
g_Map.placeEntityPassable(entitySpecs.templateName, entitySpecs.playerID, entitySpecs.position, entitySpecs.angle));
|
||||
|
||||
if (this.tileClass)
|
||||
this.tileClass.add(position.clone().floor());
|
||||
this.tileClass.add(entitySpecs.position.clone().floor());
|
||||
}
|
||||
|
||||
return resultObjs;
|
||||
return entities;
|
||||
};
|
||||
|
||||
/**
|
||||
* Randomly choses one of the given Objects and places it just like the SimpleGroup.
|
||||
*/
|
||||
function RandomGroup(objects, avoidSelf = false, tileClass = undefined, x = -1, z = -1)
|
||||
function RandomGroup(objects, avoidSelf = false, tileClass = undefined, centerPosition = undefined)
|
||||
{
|
||||
this.simpleGroup = new SimpleGroup([pickRandom(objects)], avoidSelf, tileClass, x, z);
|
||||
this.simpleGroup = new SimpleGroup([pickRandom(objects)], avoidSelf, tileClass, centerPosition);
|
||||
}
|
||||
|
||||
RandomGroup.prototype.setCenterPosition = function(position)
|
||||
{
|
||||
this.simpleGroup.setCenterPosition(position);
|
||||
};
|
||||
|
||||
RandomGroup.prototype.place = function(player, constraint)
|
||||
{
|
||||
return this.simpleGroup.place(player, constraint);
|
||||
|
||||
@@ -46,6 +46,11 @@ function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
|
||||
return min + (max - min) * (g_MapSettings.Size - minMapSize) / (maxMapSize - minMapSize);
|
||||
}
|
||||
|
||||
function randomPositionOnTile(tilePosition)
|
||||
{
|
||||
return Vector2D.add(tilePosition, new Vector2D(randFloat(0, 1), randFloat(0, 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries the given function with those arguments as often as specified.
|
||||
*/
|
||||
@@ -73,41 +78,6 @@ function retryPlacing(placeFunc, retryFactor, amount, getResult, behaveDeprecate
|
||||
return getResult ? results : good;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x and z property of the given object (typically a Placer or Group) to a random point on the map.
|
||||
* @param passableOnly - Should be true for entity placement and false for terrain or elevation operations.
|
||||
*/
|
||||
function randomizeCoordinates(obj, passableOnly)
|
||||
{
|
||||
let border = passableOnly ? MAP_BORDER_WIDTH : 0;
|
||||
if (g_Map.isCircularMap())
|
||||
{
|
||||
// Polar coordinates
|
||||
// Uniformly distributed on the disk
|
||||
let halfMapSize = g_Map.size / 2 - border;
|
||||
let r = halfMapSize * Math.sqrt(randFloat(0, 1));
|
||||
let theta = randomAngle();
|
||||
obj.x = Math.floor(r * Math.cos(theta)) + halfMapSize;
|
||||
obj.z = Math.floor(r * Math.sin(theta)) + halfMapSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rectangular coordinates
|
||||
obj.x = randIntExclusive(border, g_Map.size - border);
|
||||
obj.z = randIntExclusive(border, g_Map.size - border);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the x and z property of the given JS object (typically a Placer or Group) to a random point of the area.
|
||||
*/
|
||||
function randomizeCoordinatesFromAreas(obj, areas)
|
||||
{
|
||||
let pt = pickRandom(pickRandom(areas).points);
|
||||
obj.x = pt.x;
|
||||
obj.z = pt.y;
|
||||
}
|
||||
|
||||
// TODO this is a hack to simulate the old behaviour of those functions
|
||||
// until all old maps are changed to use the correct version of these functions
|
||||
function createObjectGroupsDeprecated(group, player, constraint, amount, retryFactor = 10)
|
||||
@@ -127,7 +97,7 @@ function createObjectGroupsByAreasDeprecated(group, player, constraint, amount,
|
||||
function createAreas(centeredPlacer, painter, constraint, amount, retryFactor = 10)
|
||||
{
|
||||
let placeFunc = function() {
|
||||
randomizeCoordinates(centeredPlacer, false);
|
||||
centeredPlacer.setCenterPosition(g_Map.randomCoordinate(false));
|
||||
return createArea(centeredPlacer, painter, constraint);
|
||||
};
|
||||
|
||||
@@ -141,7 +111,7 @@ function createAreas(centeredPlacer, painter, constraint, amount, retryFactor =
|
||||
function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFactor, areas)
|
||||
{
|
||||
let placeFunc = function() {
|
||||
randomizeCoordinatesFromAreas(centeredPlacer, areas);
|
||||
centeredPlacer.setCenterPosition(pickRandom(pickRandom(areas).points));
|
||||
return createArea(centeredPlacer, painter, constraint);
|
||||
};
|
||||
|
||||
@@ -155,7 +125,7 @@ function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFa
|
||||
function createObjectGroups(group, player, constraint, amount, retryFactor = 10, behaveDeprecated = false)
|
||||
{
|
||||
let placeFunc = function() {
|
||||
randomizeCoordinates(group, true);
|
||||
group.setCenterPosition(g_Map.randomCoordinate(true));
|
||||
return createObjectGroup(group, player, constraint);
|
||||
};
|
||||
|
||||
@@ -169,7 +139,7 @@ function createObjectGroups(group, player, constraint, amount, retryFactor = 10,
|
||||
function createObjectGroupsByAreas(group, player, constraint, amount, retryFactor, areas, behaveDeprecated = false)
|
||||
{
|
||||
let placeFunc = function() {
|
||||
randomizeCoordinatesFromAreas(group, areas);
|
||||
group.setCenterPosition(pickRandom(pickRandom(areas).points));
|
||||
return createObjectGroup(group, player, constraint);
|
||||
};
|
||||
|
||||
@@ -183,12 +153,6 @@ function createTerrain(terrain)
|
||||
new RandomTerrain(terrain.map(t => createTerrain(t)));
|
||||
}
|
||||
|
||||
function placeObject(position, type, player, angle)
|
||||
{
|
||||
if (g_Map.validTile(position))
|
||||
g_Map.addObject(new Entity(type, player, position.x, position.y, angle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Area shaped by the Placer meeting the Constraints and calls the Painters there.
|
||||
* Supports both Centered and Non-Centered Placers.
|
||||
|
||||
@@ -77,7 +77,7 @@ function sortPointsShortestCycle(points)
|
||||
}
|
||||
|
||||
// Just add the first 3 points
|
||||
let pointsToAdd = clone(points);
|
||||
let pointsToAdd = points.map(p => p.clone());
|
||||
for (let i = 0; i < 3; ++i)
|
||||
{
|
||||
order.push(i);
|
||||
@@ -86,11 +86,7 @@ function sortPointsShortestCycle(points)
|
||||
distances.push(Math.euclidDistance2D(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y));
|
||||
}
|
||||
|
||||
distances.push(Math.euclidDistance2D(
|
||||
points[order[0]].x,
|
||||
points[order[0]].y,
|
||||
points[order[order.length - 1]].x,
|
||||
points[order[order.length - 1]].y));
|
||||
distances.push(points[order[0]].distanceTo(points[order[order.length - 1]]));
|
||||
|
||||
// Add remaining points so the path lengthens the least
|
||||
let numPointsToAdd = pointsToAdd.length;
|
||||
@@ -102,8 +98,9 @@ function sortPointsShortestCycle(points)
|
||||
let minDist2 = 0;
|
||||
for (let k = 0; k < order.length; ++k)
|
||||
{
|
||||
let dist1 = Math.euclidDistance2D(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y);
|
||||
let dist2 = Math.euclidDistance2D(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y);
|
||||
let dist1 = pointsToAdd[0].distanceTo(points[order[k]]);
|
||||
let dist2 = pointsToAdd[0].distanceTo(points[order[(k + 1) % order.length]]);
|
||||
|
||||
let enlengthen = dist1 + dist2 - distances[k];
|
||||
if (enlengthen < minEnlengthen)
|
||||
{
|
||||
|
||||
@@ -25,13 +25,11 @@ function SimpleObject(templateName, minCount, maxCount, minDistance, maxDistance
|
||||
throw new Error("SimpleObject: minAngle should be less than or equal to maxAngle");
|
||||
}
|
||||
|
||||
SimpleObject.prototype.place = function(centerX, centerZ, player, avoidSelf, constraint, maxFailCount = 20)
|
||||
SimpleObject.prototype.place = function(centerPosition, playerID, avoidSelf, constraint, maxFailCount = 20)
|
||||
{
|
||||
let entities = [];
|
||||
let entitySpecs = [];
|
||||
let failCount = 0;
|
||||
|
||||
let centerPosition = new Vector2D(centerX, centerZ);
|
||||
|
||||
for (let i = 0; i < randIntInclusive(this.minCount, this.maxCount); ++i)
|
||||
while (true)
|
||||
{
|
||||
@@ -41,17 +39,22 @@ SimpleObject.prototype.place = function(centerX, centerZ, player, avoidSelf, con
|
||||
let position = Vector2D.sum([centerPosition, new Vector2D(0.5, 0.5), new Vector2D(distance, 0).rotate(-angle)]);
|
||||
|
||||
if (g_Map.validTile(position) &&
|
||||
(!avoidSelf || entities.every(ent => new Vector2D(ent.position.x, ent.position.z).distanceTo(position) >= 1)) &&
|
||||
constraint.allows(position.clone().round()))
|
||||
(!avoidSelf || entitySpecs.every(entSpec => entSpec.position.distanceTo(position) >= 1)) &&
|
||||
constraint.allows(position.clone().floor()))
|
||||
{
|
||||
entities.push(new Entity(this.templateName, player, position.x, position.y, randFloat(this.minAngle, this.maxAngle)));
|
||||
entitySpecs.push({
|
||||
"templateName": this.templateName,
|
||||
"playerID": playerID,
|
||||
"position": position,
|
||||
"angle": randFloat(this.minAngle, this.maxAngle)
|
||||
});
|
||||
break;
|
||||
}
|
||||
else if (failCount++ > maxFailCount)
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return entities;
|
||||
return entitySpecs;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,7 +65,7 @@ function RandomObject(templateNames, minCount, maxCount, minDistance, maxDistanc
|
||||
this.simpleObject = new SimpleObject(pickRandom(templateNames), minCount, maxCount, minDistance, maxDistance, minAngle, maxAngle);
|
||||
}
|
||||
|
||||
RandomObject.prototype.place = function(centerX, centerZ, player, avoidSelf, constraint, maxFailCount = 20)
|
||||
RandomObject.prototype.place = function(centerPosition, player, avoidSelf, constraint, maxFailCount = 20)
|
||||
{
|
||||
return this.simpleObject.place(centerX, centerZ, player, avoidSelf, constraint, maxFailCount);
|
||||
return this.simpleObject.place(centerPosition, player, avoidSelf, constraint, maxFailCount);
|
||||
};
|
||||
|
||||
@@ -12,29 +12,34 @@
|
||||
// coherence: How much the radius of the clump varies (1.0 = circle, 0.0 = very random)
|
||||
// smoothness: How smooth the border of the clump is (1.0 = few "peaks", 0.0 = very jagged)
|
||||
// failfraction: Percentage of place attempts allowed to fail (optional)
|
||||
// position: Tile coordinates of placer center (optional)
|
||||
// centerPosition: Tile coordinates of placer center (optional)
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function ClumpPlacer(size, coherence, smoothness, failFraction = 0, position = undefined)
|
||||
function ClumpPlacer(size, coherence, smoothness, failFraction = 0, centerPosition = undefined)
|
||||
{
|
||||
this.size = size;
|
||||
this.coherence = coherence;
|
||||
this.smoothness = smoothness;
|
||||
this.failFraction = failFraction;
|
||||
this.x = position ? Math.round(position.x) : -1;
|
||||
this.z = position ? Math.round(position.y) : -1;
|
||||
this.centerPosition = undefined;
|
||||
|
||||
if (centerPosition)
|
||||
this.setCenterPosition(centerPosition);
|
||||
}
|
||||
|
||||
ClumpPlacer.prototype.setCenterPosition = function(position)
|
||||
{
|
||||
this.centerPosition = deepfreeze(position.clone().round());
|
||||
};
|
||||
|
||||
ClumpPlacer.prototype.place = function(constraint)
|
||||
{
|
||||
let centerPosition = new Vector2D(this.x, this.z);
|
||||
|
||||
// Preliminary bounds check
|
||||
if (!g_Map.inMapBounds(centerPosition) || !constraint.allows(centerPosition))
|
||||
if (!g_Map.inMapBounds(this.centerPosition) || !constraint.allows(this.centerPosition))
|
||||
return undefined;
|
||||
|
||||
var retVec = [];
|
||||
var points = [];
|
||||
|
||||
var size = g_Map.getSize();
|
||||
var gotRet = new Array(size).fill(0).map(p => new Uint8Array(size)); // booleans
|
||||
@@ -58,9 +63,9 @@ ClumpPlacer.prototype.place = function(constraint)
|
||||
ctrlVals[i] = randFloat(0, 2);
|
||||
}
|
||||
|
||||
var c = 0;
|
||||
var looped = 0;
|
||||
for (var i=0; i < intPerim; i++)
|
||||
let c = 0;
|
||||
let looped = 0;
|
||||
for (let i = 0; i < intPerim; ++i)
|
||||
{
|
||||
if (ctrlCoords[(c+1) % ctrlPts] < i && !looped)
|
||||
{
|
||||
@@ -78,40 +83,33 @@ ClumpPlacer.prototype.place = function(constraint)
|
||||
ctrlVals[(c + 2) % ctrlPts]);
|
||||
}
|
||||
|
||||
var failed = 0;
|
||||
for (var p=0; p < intPerim; p++)
|
||||
let failed = 0;
|
||||
for (let stepAngle = 0; stepAngle < intPerim; ++stepAngle)
|
||||
{
|
||||
var th = 2 * Math.PI * p / perim;
|
||||
var r = radius * (1 + (1-this.coherence)*noise[p]);
|
||||
var s = Math.sin(th);
|
||||
var c = Math.cos(th);
|
||||
var xx = this.x;
|
||||
var yy = this.z;
|
||||
let position = this.centerPosition.clone();
|
||||
let radiusUnitVector = new Vector2D(0, 1).rotate(-2 * Math.PI * stepAngle / perim);
|
||||
let maxRadiusSteps = Math.ceil(radius * (1 + (1 - this.coherence) * noise[stepAngle]));
|
||||
|
||||
for (var k = 0; k < Math.ceil(r); ++k)
|
||||
for (let stepRadius = 0; stepRadius < maxRadiusSteps; ++stepRadius)
|
||||
{
|
||||
var i = Math.floor(xx);
|
||||
var j = Math.floor(yy);
|
||||
let position = new Vector2D(i, j);
|
||||
let tilePos = position.clone().floor();
|
||||
|
||||
if (g_Map.inMapBounds(position) && constraint.allows(position))
|
||||
if (g_Map.inMapBounds(tilePos) && constraint.allows(tilePos))
|
||||
{
|
||||
if (!gotRet[i][j])
|
||||
if (!gotRet[tilePos.x][tilePos.y])
|
||||
{
|
||||
// Only include each point once
|
||||
gotRet[i][j] = 1;
|
||||
retVec.push(position);
|
||||
gotRet[tilePos.x][tilePos.y] = 1;
|
||||
points.push(tilePos);
|
||||
}
|
||||
}
|
||||
else
|
||||
failed++;
|
||||
++failed;
|
||||
|
||||
xx += s;
|
||||
yy += c;
|
||||
position.add(radiusUnitVector);
|
||||
}
|
||||
}
|
||||
|
||||
return failed > this.size * this.failFraction ? undefined : retVec;
|
||||
return failed > this.size * this.failFraction ? undefined : points;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -124,145 +122,116 @@ ClumpPlacer.prototype.place = function(constraint)
|
||||
// numCircles: the number of the circles
|
||||
// failfraction: Percentage of place attempts allowed to fail (optional)
|
||||
// position: Tile coordinates of placer center (optional)
|
||||
// fcc: Farthest circle center (optional)
|
||||
// q: a list containing numbers. each time if the list still contains values, pops one from the end and uses it as the radius (optional)
|
||||
// maxDistance: Farthest circle center (optional)
|
||||
// queue: a list containing numbers. each time if the list still contains values, pops one from the end and uses it as the radius (optional)
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function ChainPlacer(minRadius, maxRadius, numCircles, failFraction, position, fcc, q)
|
||||
function ChainPlacer(minRadius, maxRadius, numCircles, failFraction = 0, centerPosition = undefined, maxDistance = 0, queue = [])
|
||||
{
|
||||
this.minRadius = minRadius;
|
||||
this.maxRadius = maxRadius;
|
||||
this.numCircles = numCircles;
|
||||
this.failFraction = failFraction !== undefined ? failFraction : 0;
|
||||
this.x = position ? Math.round(position.x) : -1;
|
||||
this.z = position ? Math.round(position.y) : -1;
|
||||
this.fcc = fcc !== undefined ? fcc : 0;
|
||||
this.q = q !== undefined ? q : [];
|
||||
this.failFraction = failFraction;
|
||||
this.maxDistance = maxDistance;
|
||||
this.queue = queue;
|
||||
this.centerPosition = undefined;
|
||||
|
||||
if (centerPosition)
|
||||
this.setCenterPosition(centerPosition);
|
||||
}
|
||||
|
||||
ChainPlacer.prototype.setCenterPosition = function(position)
|
||||
{
|
||||
this.centerPosition = deepfreeze(position.clone().round());
|
||||
};
|
||||
|
||||
ChainPlacer.prototype.place = function(constraint)
|
||||
{
|
||||
let centerPosition = new Vector2D(this.x, this.z);
|
||||
|
||||
// Preliminary bounds check
|
||||
if (!g_Map.inMapBounds(centerPosition) || !constraint.allows(centerPosition))
|
||||
if (!g_Map.inMapBounds(this.centerPosition) || !constraint.allows(this.centerPosition))
|
||||
return undefined;
|
||||
|
||||
var retVec = [];
|
||||
var size = g_Map.getSize();
|
||||
var failed = 0, count = 0;
|
||||
var queueEmpty = !this.q.length;
|
||||
let points = [];
|
||||
let size = g_Map.getSize();
|
||||
let failed = 0;
|
||||
let count = 0;
|
||||
|
||||
var gotRet = new Array(size).fill(0).map(p => new Array(size).fill(-1));
|
||||
let gotRet = new Array(size).fill(0).map(p => new Array(size).fill(-1));
|
||||
--size;
|
||||
|
||||
this.minRadius = Math.min(this.maxRadius, Math.max(this.minRadius, 1));
|
||||
|
||||
var edges = [[this.x, this.z]];
|
||||
for (var i = 0; i < this.numCircles; ++i)
|
||||
let edges = [this.centerPosition];
|
||||
|
||||
for (let i = 0; i < this.numCircles; ++i)
|
||||
{
|
||||
var [cx, cz] = pickRandom(edges);
|
||||
if (queueEmpty)
|
||||
var radius = randIntInclusive(this.minRadius, this.maxRadius);
|
||||
else
|
||||
{
|
||||
var radius = this.q.pop();
|
||||
queueEmpty = !this.q.length;
|
||||
}
|
||||
let chainPos = pickRandom(edges);
|
||||
let radius = this.queue.length ? this.queue.pop() : randIntInclusive(this.minRadius, this.maxRadius);
|
||||
let radius2 = Math.square(radius);
|
||||
|
||||
var sx = cx - radius, lx = cx + radius;
|
||||
var sz = cz - radius, lz = cz + radius;
|
||||
// TODO: get all points in this square to abstract and make the loop onedimensional
|
||||
let sx = Math.max(0, chainPos.x - radius);
|
||||
let sz = Math.max(0, chainPos.y - radius);
|
||||
let lx = Math.min(chainPos.x + radius, size);
|
||||
let lz = Math.min(chainPos.y + radius, size);
|
||||
|
||||
sx = Math.max(0, sx);
|
||||
sz = Math.max(0, sz);
|
||||
lx = Math.min(lx, size);
|
||||
lz = Math.min(lz, size);
|
||||
|
||||
var radius2 = radius * radius;
|
||||
var dx, dz;
|
||||
|
||||
for (var ix = sx; ix <= lx; ++ix)
|
||||
for (var iz = sz; iz <= lz; ++ iz)
|
||||
for (let ix = sx; ix <= lx; ++ix)
|
||||
for (let iz = sz; iz <= lz; ++iz)
|
||||
{
|
||||
let position = new Vector2D(ix, iz);
|
||||
dx = ix - cx;
|
||||
dz = iz - cz;
|
||||
if (dx * dx + dz * dz <= radius2)
|
||||
|
||||
if (position.distanceToSquared(chainPos) >= radius2)
|
||||
continue;
|
||||
|
||||
++count;
|
||||
|
||||
if (!g_Map.inMapBounds(position) || !constraint.allows(position))
|
||||
{
|
||||
if (g_Map.inMapBounds(position) && constraint.allows(position))
|
||||
{
|
||||
var state = gotRet[ix][iz];
|
||||
if (state == -1)
|
||||
{
|
||||
retVec.push(position);
|
||||
gotRet[ix][iz] = -2;
|
||||
}
|
||||
else if (state >= 0)
|
||||
{
|
||||
var s = edges.splice(state, 1);
|
||||
gotRet[ix][iz] = -2;
|
||||
++failed;
|
||||
continue;
|
||||
}
|
||||
|
||||
var edgesLength = edges.length;
|
||||
for (var k = state; k < edges.length; ++k)
|
||||
--gotRet[edges[k][0]][edges[k][1]];
|
||||
}
|
||||
}
|
||||
else
|
||||
++failed;
|
||||
let state = gotRet[ix][iz];
|
||||
if (state == -1)
|
||||
{
|
||||
points.push(position);
|
||||
gotRet[ix][iz] = -2;
|
||||
}
|
||||
else if (state >= 0)
|
||||
{
|
||||
let s = edges.splice(state, 1);
|
||||
gotRet[ix][iz] = -2;
|
||||
|
||||
++count;
|
||||
let edgesLength = edges.length;
|
||||
for (let k = state; k < edges.length; ++k)
|
||||
--gotRet[edges[k].x][edges[k].y];
|
||||
}
|
||||
}
|
||||
|
||||
for (var ix = sx; ix <= lx; ++ix)
|
||||
for (var iz = sz; iz <= lz; ++ iz)
|
||||
for (let ix = sx; ix <= lx; ++ix)
|
||||
for (let iz = sz; iz <= lz; ++ iz)
|
||||
{
|
||||
if (this.fcc)
|
||||
if ((this.x - ix) > this.fcc || (ix - this.x) > this.fcc || (this.z - iz) > this.fcc || (iz - this.z) > this.fcc)
|
||||
continue;
|
||||
let pos = new Vector2D(ix, iz);
|
||||
|
||||
if (gotRet[ix][iz] == -2)
|
||||
if (this.maxDistance &&
|
||||
(Math.abs(this.centerPosition.x - pos.x) > this.maxDistance ||
|
||||
Math.abs(this.centerPosition.y - pos.y) > this.maxDistance))
|
||||
continue;
|
||||
|
||||
if (gotRet[pos.x][pos.y] != -2)
|
||||
continue;
|
||||
|
||||
if (pos.x > 0 && gotRet[pos.x - 1][pos.y] == -1 ||
|
||||
pos.y > 0 && gotRet[pos.x][pos.y - 1] == -1 ||
|
||||
pos.x < size && gotRet[pos.x + 1][pos.y] == -1 ||
|
||||
pos.y < size && gotRet[pos.x][pos.y + 1] == -1)
|
||||
{
|
||||
if (ix > 0)
|
||||
{
|
||||
if (gotRet[ix-1][iz] == -1)
|
||||
{
|
||||
edges.push([ix, iz]);
|
||||
gotRet[ix][iz] = edges.length - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (iz > 0)
|
||||
{
|
||||
if (gotRet[ix][iz-1] == -1)
|
||||
{
|
||||
edges.push([ix, iz]);
|
||||
gotRet[ix][iz] = edges.length - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ix < size)
|
||||
{
|
||||
if (gotRet[ix+1][iz] == -1)
|
||||
{
|
||||
edges.push([ix, iz]);
|
||||
gotRet[ix][iz] = edges.length - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (iz < size)
|
||||
{
|
||||
if (gotRet[ix][iz+1] == -1)
|
||||
{
|
||||
edges.push([ix, iz]);
|
||||
gotRet[ix][iz] = edges.length - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
edges.push(pos);
|
||||
gotRet[pos.x][pos.y] = edges.length - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return failed > count * this.failFraction ? undefined : retVec;
|
||||
return failed > count * this.failFraction ? undefined : points;
|
||||
};
|
||||
|
||||
@@ -287,8 +287,7 @@ RandomPathPlacer.prototype.place = function(constraint)
|
||||
-getAngle(this.pathStart.x, this.pathStart.y, this.pathEnd.x, this.pathEnd.y) +
|
||||
-Math.PI / 2 * (randFloat(-1, 1) + (this.blended ? 0.5 : 0)))).round();
|
||||
|
||||
this.clumpPlacer.x = position.x;
|
||||
this.clumpPlacer.z = position.y;
|
||||
this.clumpPlacer.setCenterPosition(position);
|
||||
|
||||
for (let point of this.clumpPlacer.place(constraint) || [])
|
||||
if (points.every(p => p.x != point.x || p.y != point.y))
|
||||
|
||||
@@ -79,7 +79,7 @@ function placeStartingEntities(location, playerID, civEntities, dist = 6, orient
|
||||
let firstTemplate = civEntities[i].Template;
|
||||
if (firstTemplate.startsWith("structures/"))
|
||||
{
|
||||
placeObject(location, firstTemplate, playerID, orientation);
|
||||
g_Map.placeEntityPassable(firstTemplate, playerID, location, orientation);
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ function placeStartingEntities(location, playerID, civEntities, dist = 6, orient
|
||||
new Vector2D(space * (-num + 0.75 * Math.floor(count / 2)), 0).rotate(angle)
|
||||
]);
|
||||
|
||||
placeObject(position, civEntities[j].Template, playerID, angle);
|
||||
g_Map.placeEntityPassable(civEntities[j].Template, playerID, position, angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,14 +242,13 @@ function placePlayerBaseChicken(args)
|
||||
let success = false;
|
||||
for (let tries = 0; tries < get("maxTries", 30); ++tries)
|
||||
{
|
||||
let loc = new Vector2D(0, get("distance", 9)).rotate(randomAngle()).add(basePosition);
|
||||
let position = new Vector2D(0, get("distance", 9)).rotate(randomAngle()).add(basePosition);
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(get("template", "gaia/fauna_chicken"), 5, 5, 0, get("count", 2))],
|
||||
true,
|
||||
args.BaseResourceClass,
|
||||
loc.x,
|
||||
loc.y),
|
||||
position),
|
||||
0,
|
||||
baseResourceConstraint))
|
||||
{
|
||||
@@ -271,14 +270,13 @@ function placePlayerBaseBerries(args)
|
||||
let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args);
|
||||
for (let tries = 0; tries < get("maxTries", 30); ++tries)
|
||||
{
|
||||
let loc = new Vector2D(0, get("distance", 12)).rotate(randomAngle()).add(basePosition);
|
||||
let position = new Vector2D(0, get("distance", 12)).rotate(randomAngle()).add(basePosition);
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(args.template, get("minCount", 5), get("maxCount", 5), get("maxDist", 1), get("maxDist", 3))],
|
||||
true,
|
||||
args.BaseResourceClass,
|
||||
loc.x,
|
||||
loc.y),
|
||||
position),
|
||||
0,
|
||||
baseResourceConstraint))
|
||||
return;
|
||||
@@ -307,7 +305,7 @@ function placePlayerBaseMines(args)
|
||||
{
|
||||
let angle = startAngle + angleBetweenMines * (i + (mineCount - 1) / 2);
|
||||
pos[i] = new Vector2D(0, get("distance", 12)).rotate(angle).add(basePosition).round();
|
||||
if (!g_Map.validTile(pos[i]) || !baseResourceConstraint.allows(pos[i]))
|
||||
if (!g_Map.validTilePassable(pos[i]) || !baseResourceConstraint.allows(pos[i]))
|
||||
{
|
||||
pos = undefined;
|
||||
break;
|
||||
@@ -332,8 +330,7 @@ function placePlayerBaseMines(args)
|
||||
[new SimpleObject(args.types[i].template, 1, 1, 0, 0)].concat(groupElements),
|
||||
true,
|
||||
args.BaseResourceClass,
|
||||
pos[i].x,
|
||||
pos[i].y),
|
||||
pos[i]),
|
||||
0);
|
||||
}
|
||||
return;
|
||||
@@ -350,15 +347,14 @@ function placePlayerBaseTrees(args)
|
||||
|
||||
for (let x = 0; x < get("maxTries", 30); ++x)
|
||||
{
|
||||
let loc = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round();
|
||||
let position = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round();
|
||||
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(args.template, num, num, get("minDistGroup", 0), get("maxDistGroup", 5))],
|
||||
false,
|
||||
args.BaseResourceClass,
|
||||
loc.x,
|
||||
loc.y),
|
||||
position),
|
||||
0,
|
||||
baseResourceConstraint))
|
||||
return;
|
||||
@@ -379,15 +375,14 @@ function placePlayerBaseTreasures(args)
|
||||
|
||||
for (let tries = 0; tries < get("maxTries", 30); ++tries)
|
||||
{
|
||||
let loc = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round();
|
||||
let position = new Vector2D(0, randFloat(get("minDist", 11), get("maxDist", 13))).rotate(randomAngle()).add(basePosition).round();
|
||||
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(resourceTypeArgs.template, get("count", 14), get("count", 14), get("minDistGroup", 1), get("maxDistGroup", 3))],
|
||||
false,
|
||||
args.BaseResourceClass,
|
||||
loc.x,
|
||||
loc.y),
|
||||
position),
|
||||
0,
|
||||
baseResourceConstraint))
|
||||
{
|
||||
@@ -415,15 +410,14 @@ function placePlayerBaseDecoratives(args)
|
||||
let success = false;
|
||||
for (let x = 0; x < get("maxTries", 30); ++x)
|
||||
{
|
||||
let loc = new Vector2D(0, randIntInclusive(get("minDist", 8), get("maxDist", 11))).rotate(randomAngle()).add(basePosition).round();
|
||||
let position = new Vector2D(0, randIntInclusive(get("minDist", 8), get("maxDist", 11))).rotate(randomAngle()).add(basePosition).round();
|
||||
|
||||
if (createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[new SimpleObject(args.template, get("minCount", 2), get("maxCount", 5), 0, 1)],
|
||||
false,
|
||||
args.BaseResourceClass,
|
||||
loc.x,
|
||||
loc.y),
|
||||
position),
|
||||
0,
|
||||
baseResourceConstraint))
|
||||
{
|
||||
@@ -476,7 +470,7 @@ function placePlayersNomad(playerClass, constraints)
|
||||
if (createObjectGroups(group, playerIDs[i], new AndConstraint([constraint, avoidClasses(playerClass, distance * distanceFactor)]), 1, 200, false))
|
||||
{
|
||||
success = true;
|
||||
playerPosition[i] = new Vector2D(group.x, group.z);
|
||||
playerPosition[i] = group.centerPosition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,18 +27,16 @@ function RandomMap(baseHeight, baseTerrain)
|
||||
}
|
||||
|
||||
// Create 2D arrays for terrain objects and areas
|
||||
this.terrainObjects = [];
|
||||
this.terrainEntities = [];
|
||||
this.area = [];
|
||||
|
||||
for (let i = 0; i < this.size; ++i)
|
||||
{
|
||||
// Area IDs
|
||||
this.area[i] = new Uint16Array(this.size);
|
||||
|
||||
// Entities
|
||||
this.terrainObjects[i] = [];
|
||||
this.terrainEntities[i] = [];
|
||||
for (let j = 0; j < this.size; ++j)
|
||||
this.terrainObjects[i][j] = undefined;
|
||||
this.terrainEntities[i][j] = undefined;
|
||||
}
|
||||
|
||||
// Create 2D array for heightmap
|
||||
@@ -55,8 +53,7 @@ function RandomMap(baseHeight, baseTerrain)
|
||||
this.height[i][j] = baseHeight;
|
||||
}
|
||||
|
||||
// Array of Entities
|
||||
this.objects = [];
|
||||
this.entities = [];
|
||||
|
||||
this.areaID = 0;
|
||||
|
||||
@@ -120,19 +117,27 @@ RandomMap.prototype.getBounds = function()
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the given coordinates are within the given distance of the passable map area.
|
||||
* Should be used to restrict entity placement and path creation.
|
||||
* Determines whether the given coordinates are within the given distance of the map area.
|
||||
* Should be used to restrict terrain texture changes and actor placement.
|
||||
* Entity placement should be checked against validTilePassable to exclude the map border.
|
||||
*/
|
||||
RandomMap.prototype.validTile = function(position, distance = 0)
|
||||
{
|
||||
distance += MAP_BORDER_WIDTH;
|
||||
|
||||
if (this.isCircularMap())
|
||||
return Math.round(position.distanceTo(this.getCenter())) < this.size / 2 - distance - 1;
|
||||
|
||||
return position.x >= distance && position.y >= distance && position.x < this.size - distance && position.y < this.size - distance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the given coordinates are within the given distance of the passable map area.
|
||||
* Should be used to restrict entity placement and path creation.
|
||||
*/
|
||||
RandomMap.prototype.validTilePassable = function(position, distance = 0)
|
||||
{
|
||||
return this.validTile(position, distance + MAP_BORDER_WIDTH);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the given coordinates are within the tile grid, passable or not.
|
||||
* Should be used to restrict texture painting.
|
||||
@@ -157,6 +162,27 @@ RandomMap.prototype.validHeight = function(position)
|
||||
return position.x <= this.size && position.y <= this.size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a random point on the map.
|
||||
* @param passableOnly - Should be true for entity placement and false for terrain or elevation operations.
|
||||
*/
|
||||
RandomMap.prototype.randomCoordinate = function(passableOnly)
|
||||
{
|
||||
let border = passableOnly ? MAP_BORDER_WIDTH : 0;
|
||||
|
||||
if (this.isCircularMap())
|
||||
// Polar coordinates
|
||||
// Uniformly distributed on the disk
|
||||
return Vector2D.add(
|
||||
this.getCenter(),
|
||||
new Vector2D((this.size / 2 - border) * Math.sqrt(randFloat(0, 1)), 0).rotate(randomAngle()).floor());
|
||||
|
||||
// Rectangular coordinates
|
||||
return new Vector2D(
|
||||
randIntExclusive(border, this.size - border),
|
||||
randIntExclusive(border, this.size - border));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the name of the texture of the given tile.
|
||||
*/
|
||||
@@ -198,34 +224,46 @@ RandomMap.prototype.setHeight = function(position, height)
|
||||
this.height[position.x][position.y] = height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the given Entity to the map at the location it defines, even if at the impassable map border.
|
||||
*/
|
||||
RandomMap.prototype.placeEntityAnywhere = function(templateName, playerID, position, orientation)
|
||||
{
|
||||
let entity = new Entity(this.getEntityID(), templateName, playerID, position, orientation);
|
||||
this.entities.push(entity);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the given Entity to the map at the location it defines, if that area is not at the impassable map border.
|
||||
*/
|
||||
RandomMap.prototype.placeEntityPassable = function(templateName, playerID, position, orientation)
|
||||
{
|
||||
if (g_Map.validTilePassable(position))
|
||||
this.placeEntityAnywhere(templateName, playerID, position, orientation);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Entity that was painted by a Terrain class on the given tile or undefined otherwise.
|
||||
*/
|
||||
RandomMap.prototype.getTerrainObject = function(position)
|
||||
RandomMap.prototype.getTerrainEntity = function(position)
|
||||
{
|
||||
if (!this.validTile(position))
|
||||
throw new Error("getTerrainObject: invalid tile position " + uneval(position));
|
||||
if (!this.validTilePassable(position))
|
||||
throw new Error("getTerrainEntity: invalid tile position " + uneval(position));
|
||||
|
||||
return this.terrainObjects[position.x][position.y];
|
||||
return this.terrainEntities[position.x][position.y];
|
||||
};
|
||||
|
||||
/**
|
||||
* Places the Entity on the given tile and allows to later replace it if the terrain was painted over.
|
||||
*/
|
||||
RandomMap.prototype.setTerrainObject = function(position, object)
|
||||
RandomMap.prototype.setTerrainEntity = function(templateName, playerID, position, orientation)
|
||||
{
|
||||
if (!this.validTile(position))
|
||||
throw new Error("setTerrainObject: invalid tile position " + uneval(position));
|
||||
let tilePosition = position.clone().floor();
|
||||
if (!this.validTilePassable(tilePosition))
|
||||
throw new Error("setTerrainEntity: invalid tile position " + uneval(position));
|
||||
|
||||
this.terrainObjects[position.x][position.y] = object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the given Entity to the map at the location it defines.
|
||||
*/
|
||||
RandomMap.prototype.addObject = function(obj)
|
||||
{
|
||||
this.objects.push(obj);
|
||||
this.terrainEntities[tilePosition.x][tilePosition.y] =
|
||||
new Entity(this.getEntityID(), templateName, playerID, position, orientation);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -331,17 +369,17 @@ RandomMap.prototype.getSlope = function(position)
|
||||
RandomMap.prototype.exportEntityList = function()
|
||||
{
|
||||
// Change rotation from simple 2d to 3d befor giving to engine
|
||||
for (let obj of this.objects)
|
||||
obj.rotation.y = Math.PI / 2 - obj.rotation.y;
|
||||
for (let entity of this.entities)
|
||||
entity.rotation.y = Math.PI / 2 - entity.rotation.y;
|
||||
|
||||
// Terrain objects e.g. trees
|
||||
for (let x = 0; x < this.size; ++x)
|
||||
for (let z = 0; z < this.size; ++z)
|
||||
if (this.terrainObjects[x][z])
|
||||
this.objects.push(this.terrainObjects[x][z]);
|
||||
if (this.terrainEntities[x][z])
|
||||
this.entities.push(this.terrainEntities[x][z]);
|
||||
|
||||
log("Number of entities: " + this.objects.length);
|
||||
return this.objects;
|
||||
log("Number of entities: " + this.entities.length);
|
||||
return this.entities;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,12 +19,8 @@ function SimpleTerrain(texture, templateName = undefined)
|
||||
|
||||
SimpleTerrain.prototype.place = function(position)
|
||||
{
|
||||
if (g_Map.validTile(position))
|
||||
g_Map.setTerrainObject(
|
||||
position,
|
||||
this.templateName ?
|
||||
new Entity(this.templateName, 0, position.x + 0.5, position.y + 0.5, randomAngle()) :
|
||||
undefined);
|
||||
if (this.templateName && g_Map.validTilePassable(position))
|
||||
g_Map.setTerrainEntity(this.templateName, 0, Vector2D.add(position, new Vector2D(0.5, 0.5)), randomAngle());
|
||||
|
||||
g_Map.setTexture(position, this.texture);
|
||||
};
|
||||
|
||||
@@ -413,7 +413,7 @@ function placeWall(position, wall = [], style, playerId = 0, orientation = 0)
|
||||
|
||||
for (let align of getWallAlignment(position, wall, style, orientation))
|
||||
if (align.templateName)
|
||||
placeObject(align.position, align.templateName, playerId, align.angle);
|
||||
g_Map.placeEntityPassable(align.templateName, playerId, align.position, align.angle);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,7 +518,7 @@ function placeLinearWall(startPosition, targetPosition, wallPart = undefined, st
|
||||
let place = Vector2D.add(position, new Vector2D(0, wallEle.indent).rotate(-wallAngle));
|
||||
|
||||
if (wallEle.templateName)
|
||||
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
|
||||
g_Map.placeEntityPassable(wallEle.templateName, playerId, place, placeAngle + wallEle.angle);
|
||||
|
||||
position.add(dist);
|
||||
}
|
||||
@@ -529,7 +529,7 @@ function placeLinearWall(startPosition, targetPosition, wallPart = undefined, st
|
||||
let wallLength = (wallEle.length - overlap) / 2;
|
||||
position.add(new Vector2D(scaleFactor * wallLength, 0).rotate(-wallAngle));
|
||||
if (wallEle.templateName)
|
||||
placeObject(position, wallEle.templateName, playerId, placeAngle + wallEle.angle);
|
||||
g_Map.placeEntityPassable(wallEle.templateName, playerId, position, placeAngle + wallEle.angle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +602,7 @@ function placeCircularWall(center, radius, wallPart, style, playerId = 0, orient
|
||||
|
||||
// Placement
|
||||
if (wallEle.templateName)
|
||||
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
|
||||
g_Map.placeEntityPassable(wallEle.templateName, playerId, place, placeAngle + wallEle.angle);
|
||||
|
||||
// Prepare for the next wall element
|
||||
actualAngle += addAngle;
|
||||
@@ -616,7 +616,7 @@ function placeCircularWall(center, radius, wallPart, style, playerId = 0, orient
|
||||
let target = Vector2D.add(center, new Vector2D(radius, 0).rotate(-actualAngle - addAngle))
|
||||
let place = Vector2D.average([position, target]);
|
||||
let placeAngle = actualAngle + addAngle / 2;
|
||||
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
|
||||
g_Map.placeEntityPassable(wallEle.templateName, playerId, place, placeAngle + wallEle.angle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +649,7 @@ function placePolygonalWall(centerPosition, radius, wallPart, cornerWallElement
|
||||
for (let i = 0; i < numCorners; ++i)
|
||||
{
|
||||
let angleToCorner = getAngle(corners[i].x, corners[i].y, centerPosition.x, centerPosition.y);
|
||||
placeObject(corners[i], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
|
||||
g_Map.placeEntityPassable(getWallElement(cornerWallElement, style).templateName, playerId, corners[i], angleToCorner);
|
||||
|
||||
if (!skipFirstWall || i != 0)
|
||||
{
|
||||
@@ -776,7 +776,7 @@ function placeIrregularPolygonalWall(centerPosition, radius, cornerWallElement =
|
||||
for (let i = 0; i < numCorners; ++i)
|
||||
{
|
||||
let angleToCorner = getAngle(corners[i].x, corners[i].y, centerPosition.x, centerPosition.y);
|
||||
placeObject(corners[i], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
|
||||
g_Map.placeEntityPassable(getWallElement(cornerWallElement, style).templateName, playerId, corners[i], angleToCorner);
|
||||
if (!skipFirstWall || i != 0)
|
||||
{
|
||||
let cornerLength = getWallElement(cornerWallElement, style).length / 2;
|
||||
@@ -875,7 +875,7 @@ function placeGenericFortress(center, radius = 20, playerId = 0, style, irregula
|
||||
if (element.templateName)
|
||||
{
|
||||
let pos = Vector2D.add(start, new Vector2D(start.distanceTo(target) / 2, 0).rotate(-angle));
|
||||
placeObject(pos, element.templateName, playerId, angle - Math.PI / 2 + element.angle);
|
||||
g_Map.placeEntityPassable(element.templateName, playerId, pos, angle - Math.PI / 2 + element.angle);
|
||||
}
|
||||
|
||||
// Place tower
|
||||
@@ -883,6 +883,6 @@ function placeGenericFortress(center, radius = 20, playerId = 0, style, irregula
|
||||
angle = getAngle(start.x, start.y, target.x, target.y);
|
||||
|
||||
let tower = getWallElement("tower", style);
|
||||
placeObject(Vector2D.add(center, bestPointDerivation[pointIndex]), tower.templateName, playerId, angle - Math.PI / 2 + tower.angle);
|
||||
g_Map.placeEntityPassable(tower.templateName, playerId, Vector2D.add(center, bestPointDerivation[pointIndex]), angle - Math.PI / 2 + tower.angle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,15 +144,9 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
new SimpleObject(oWildebeest, 5, 6, 0, 4),
|
||||
new SimpleObject(oElephant, 2, 3, 0, 4)
|
||||
];
|
||||
|
||||
for (let object of objects)
|
||||
createObjectGroup(
|
||||
new SimpleGroup(
|
||||
[object],
|
||||
true,
|
||||
clFood,
|
||||
shallowPosition.x,
|
||||
shallowPosition.y),
|
||||
0);
|
||||
createObjectGroup(new SimpleGroup([object], true, clFood, shallowPosition), 0);
|
||||
}
|
||||
|
||||
paintTerrainBasedOnHeight(-6, 2, 1, tWater);
|
||||
|
||||
@@ -134,7 +134,6 @@ var heighLimits = [
|
||||
heightSeaGroundAdjusted + 7/8 * (heightRange.max - heightSeaGroundAdjusted), // 9 Upper forest border
|
||||
heightSeaGroundAdjusted + (heightRange.max - heightSeaGroundAdjusted)]; // 10 Hilltop
|
||||
|
||||
var startLocations = getStartLocationsByHeightmap({'min': heighLimits[4], 'max': heighLimits[5]});
|
||||
var playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
|
||||
|
||||
for (let i = 0; i < numPlayers; ++i)
|
||||
|
||||
@@ -94,16 +94,16 @@ for (let i = 0; i < numPlayers; ++i)
|
||||
log("Placing treasure seeker woman...");
|
||||
let femaleLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 3.5, 3).round();
|
||||
clWomen.add(femaleLocation);
|
||||
placeObject(femaleLocation, oTreasureSeeker, playerIDs[i], playerAngle[i] + Math.PI);
|
||||
g_Map.placeEntityPassable(oTreasureSeeker, playerIDs[i], femaleLocation, playerAngle[i] + Math.PI);
|
||||
|
||||
log("Placing attacker spawn point....");
|
||||
placeObject(attacker[i], aWaypointFlag, 0, Math.PI / 2);
|
||||
placeObject(attacker[i], triggerPointAttacker, playerIDs[i], Math.PI / 2);
|
||||
g_Map.placeEntityAnywhere(aWaypointFlag, 0, attacker[i], Math.PI / 2);
|
||||
g_Map.placeEntityPassable(triggerPointAttacker, playerIDs[i], attacker[i], Math.PI / 2);
|
||||
|
||||
log("Preventing mountains in the area between player and attackers...");
|
||||
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
|
||||
clPlayer.add(attacker);
|
||||
clPlayer.add(halfway);
|
||||
clPlayer.add(attacker[i]);
|
||||
clPlayer.add(halfway[i]);
|
||||
}
|
||||
Engine.SetProgress(20);
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ paintRiver({
|
||||
if (plantID % plantFrequency == 0)
|
||||
{
|
||||
plantID = 0;
|
||||
placeObject(position, aPlants, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(aPlants, 0, position, randomAngle());
|
||||
}
|
||||
++plantID;
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ var g_Map = new RandomMap(0, "grass1");
|
||||
*
|
||||
* Some general notes concerning the arguments:
|
||||
*
|
||||
* - The first two arguments for most placement functions are x/y co-ordinates needed to position the wall. These are received via separate arguments, like in placeObject(), and their exact meaning differs between methods, but should be mostly self explanatory. The exception to this is placeLinearWall(), where the first four arguments are co-ordinates. However, whether two argument or four, the initial x/y co-ordinates are required parameters.
|
||||
* - The first two arguments for most placement functions are x/y co-ordinates needed to position the wall. These are received via separate arguments, like in placeEntityPassable and their exact meaning differs between methods, but should be mostly self explanatory. The exception to this is placeLinearWall(), where the first four arguments are co-ordinates. However, whether two argument or four, the initial x/y co-ordinates are required parameters.
|
||||
*
|
||||
* - For some functions, the next argument is radius, indicating how far from a central point the wall should be drawn. The functions that use this are marked as doing so below.
|
||||
*
|
||||
@@ -106,7 +106,7 @@ for (let styleIndex in wallStyleList)
|
||||
new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)
|
||||
]);
|
||||
|
||||
placeObject(pos, "other/obelisk", playerID, orientation);
|
||||
g_Map.placeEntityPassable("other/obelisk", playerID, pos, orientation);
|
||||
placeFortress(pos, type, style, playerID, orientation);
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ for (let styleIndex in wallStyleList)
|
||||
new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)
|
||||
]);
|
||||
|
||||
placeObject(pos, "other/obelisk", playerID, 0);
|
||||
g_Map.placeEntityPassable("other/obelisk", playerID, pos, 0);
|
||||
placeGenericFortress(pos, radius, playerID, style);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ for (let styleIndex in wallStyleList)
|
||||
// If less than Pi * 2, then the wall will be an arc.
|
||||
let maxAngle = Math.PI / 2 * (styleIndex % 3 + 2);
|
||||
|
||||
placeObject(center, "other/obelisk", playerID, orientation);
|
||||
g_Map.placeEntityPassable("other/obelisk", playerID, center, orientation);
|
||||
placeCircularWall(center, radius, wallPart, style, playerID, orientation, maxAngle);
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ for (let styleIndex in wallStyleList)
|
||||
// If true, the first side will not be drawn, leaving the wall open.
|
||||
let skipFirstWall = true;
|
||||
|
||||
placeObject(centerPosition, "other/obelisk", playerID, orientation);
|
||||
g_Map.placeEntityPassable("other/obelisk", playerID, centerPosition, orientation);
|
||||
placePolygonalWall(centerPosition, radius, wallParts, cornerWallElement, style, playerID, orientation, numCorners, skipFirstWall);
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ for (let styleIndex in wallStyleList)
|
||||
// If true, the first side will not be drawn, leaving the wall open.
|
||||
let skipFirstWall = true;
|
||||
|
||||
placeObject(centerPosition, "other/obelisk", playerID, orientation);
|
||||
g_Map.placeEntityPassable("other/obelisk", playerID, centerPosition, orientation);
|
||||
placeIrregularPolygonalWall(centerPosition, radius, cornerWallElement, style, playerID, orientation, numCorners, irregularity, skipFirstWall);
|
||||
}
|
||||
|
||||
|
||||
@@ -211,15 +211,15 @@ function placeMine(position, centerEntity,
|
||||
]
|
||||
)
|
||||
{
|
||||
placeObject(position, centerEntity, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(centerEntity, 0, position, randomAngle());
|
||||
|
||||
let quantity = randIntInclusive(11, 23);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
for (let i = 0; i < quantity; ++i)
|
||||
placeObject(
|
||||
Vector2D.add(position, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
|
||||
g_Map.placeEntityPassable(
|
||||
pickRandom(decorativeActors),
|
||||
0,
|
||||
Vector2D.add(position, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
|
||||
randomAngle());
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ function placeGrove(point,
|
||||
)
|
||||
{
|
||||
let position = new Vector2D(point.x, point.y);
|
||||
placeObject(position, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, position, randomAngle());
|
||||
|
||||
let quantity = randIntInclusive(20, 30);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
@@ -253,7 +253,7 @@ function placeGrove(point,
|
||||
objectList = groveActors;
|
||||
|
||||
let pos = Vector2D.add(position, new Vector2D(dist, 0).rotate(-angle));
|
||||
placeObject(pos, pickRandom(objectList), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(objectList), 0, pos, randomAngle());
|
||||
|
||||
let painters = [new TerrainPainter(groveTerrainTexture)];
|
||||
if (groveTileClass)
|
||||
@@ -331,7 +331,7 @@ function placeCamp(position,
|
||||
]
|
||||
)
|
||||
{
|
||||
placeObject(position, centerEntity, 0, randomAngle());
|
||||
g_Map.placeEntityPassable(centerEntity, 0, position, randomAngle());
|
||||
|
||||
let quantity = randIntInclusive(5, 11);
|
||||
let dAngle = 2 * Math.PI / quantity;
|
||||
@@ -339,7 +339,7 @@ function placeCamp(position,
|
||||
{
|
||||
let angle = dAngle * randFloat(i, i + 1);
|
||||
let dist = randFloat(1, 3);
|
||||
placeObject(Vector2D.add(position, new Vector2D(dist, 0).rotate(-angle)), pickRandom(otherEntities), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(otherEntities), 0, Vector2D.add(position, new Vector2D(dist, 0).rotate(-angle)), randomAngle());
|
||||
}
|
||||
|
||||
addCivicCenterAreaToClass(position, clGaiaCamp);
|
||||
@@ -384,7 +384,8 @@ function placeStartLocationResources(
|
||||
objectList = groveActors;
|
||||
|
||||
let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle));
|
||||
placeObject(position, pickRandom(objectList), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(objectList), 0, position, randomAngle());
|
||||
|
||||
createArea(
|
||||
new ClumpPlacer(5, 1, 1, 1, position),
|
||||
[
|
||||
@@ -408,7 +409,7 @@ function placeStartLocationResources(
|
||||
{
|
||||
angle = currentAngle + randFloat(0, dAngle);
|
||||
let dist = getRandDist();
|
||||
placeObject(Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), pickRandom(foodEntities), 0, randomAngle());
|
||||
g_Map.placeEntityPassable(pickRandom(foodEntities), 0, Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), randomAngle());
|
||||
currentAngle += dAngle;
|
||||
}
|
||||
}
|
||||
@@ -493,7 +494,7 @@ let resourceSpotHeightRange = { "min" : (heighLimits[2] + heighLimits[3]) / 2, "
|
||||
let playerHeight = (playerHeightRange.min + playerHeightRange.max) / 2; // Average player height
|
||||
|
||||
log("Chosing starting locations...");
|
||||
let [playerIDs, startLocations] = sortPlayersByLocation(getStartLocationsByHeightmap(playerHeightRange, 1000, 30));
|
||||
let [playerIDs, playerPosition] = sortPlayersByLocation(getStartLocationsByHeightmap(playerHeightRange, 1000, 30));
|
||||
|
||||
Engine.SetProgress(30);
|
||||
|
||||
@@ -504,7 +505,7 @@ let playerBaseRadius = 35;
|
||||
if (g_Map.size < 256)
|
||||
playerBaseRadius = 25;
|
||||
for (let p = 0; p < playerIDs.length; ++p)
|
||||
rectangularSmoothToHeight(startLocations[p], playerBaseRadius, playerBaseRadius, playerHeight, 0.7);
|
||||
rectangularSmoothToHeight(playerPosition[p], playerBaseRadius, playerBaseRadius, playerHeight, 0.7);
|
||||
|
||||
/**
|
||||
* Calculate tile centered height map after start position smoothing but before placing paths
|
||||
@@ -578,12 +579,12 @@ for (let h = 0; h < heighLimits.length; ++h)
|
||||
g_Map.setTexture(point, texture);
|
||||
|
||||
if (actor)
|
||||
placeObject(Vector2D.add(point, new Vector2D(randFloat(0, 1), randFloat(0, 1))), actor, 0, randomAngle());
|
||||
g_Map.placeEntityAnywhere(actor, 0, randomPositionOnTile(point), randomAngle());
|
||||
}
|
||||
Engine.SetProgress(80);
|
||||
|
||||
log("Placing resources...");
|
||||
let avoidPoints = clone(startLocations);
|
||||
let avoidPoints = playerPosition.map(pos => pos.clone());
|
||||
for (let i = 0; i < avoidPoints.length; ++i)
|
||||
avoidPoints[i].dist = 30;
|
||||
let resourceSpots = getPointsByHeight(resourceSpotHeightRange, avoidPoints).map(point => new Vector2D(point.x, point.y));
|
||||
@@ -601,9 +602,8 @@ if (isNomad())
|
||||
else
|
||||
for (let p = 0; p < playerIDs.length; ++p)
|
||||
{
|
||||
let point = new Vector2D(startLocations[p].x, startLocations[p].y);
|
||||
placeCivDefaultStartingEntities(point, playerIDs[p], g_Map.size > 192);
|
||||
placeStartLocationResources(point);
|
||||
placeCivDefaultStartingEntities(playerPosition[p], playerIDs[p], g_Map.size > 192);
|
||||
placeStartLocationResources(playerPosition[p]);
|
||||
}
|
||||
|
||||
let mercenaryCamps = isNomad() ? 0 : Math.ceil(g_Map.size / 256);
|
||||
|
||||
Reference in New Issue
Block a user