Change placeObject, placeStartingWalls, wall builder library and wall demo to use vectors, refs #4845, #4992.

This was SVN commit r21021.
This commit is contained in:
elexis
2018-01-26 20:53:37 +00:00
parent 2156171994
commit 2f7610160f
19 changed files with 288 additions and 341 deletions
@@ -267,7 +267,7 @@ for (var ix = 0; ix < mapSize; ix++)
if (h > 35 && randBool(0.1) ||
h < 15 && randBool(0.05) && hillDecoClass.countMembersInRadius(ix, iz, 1) == 0)
placeObject(ix + randFloat(0, 1), iz + randFloat(0, 1), pickRandom(aTrees), 0, randomAngle());
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), pickRandom(aTrees), 0, randomAngle());
}
var explorableArea = g_Map.createArea(explorablePoints);
@@ -259,7 +259,6 @@ else if (mapSize > 400)
propDensity = 3/4;
for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; x++)
{
for (var y = minTerrainDistToBorder; y < mapSize - minTerrainDistToBorder; y++)
{
let position = new Vector2D(x, y);
@@ -270,109 +269,113 @@ for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; x++)
{
createTerrain(textueByHeight[i].terrain).place(position);
// Add some props at...
let template;
if (i == 0) // ...deep water
{
if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/pond_lillies_large.xml", 0, randomAngle());
template = "actor|props/flora/pond_lillies_large.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/water_lillies.xml", 0, randomAngle());
template = "actor|props/flora/water_lillies.xml";
}
if (i == 1) // ...medium water (with fish)
{
if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/pond_lillies_large.xml", 0, randomAngle());
template = "actor|props/flora/pond_lillies_large.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/water_lillies.xml", 0, randomAngle());
template = "actor|props/flora/water_lillies.xml";
}
if (i == 2) // ...low water/mud
{
if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/water_log.xml", 0, randomAngle());
template = "actor|props/flora/water_log.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/water_lillies.xml", 0, randomAngle());
template = "actor|props/flora/water_lillies.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|geology/highland_c.xml", 0, randomAngle());
template = "actor|geology/highland_c.xml";
else if (randBool(propDensity / 20))
placeObject(x, y, "actor|props/flora/reeds_pond_lush_b.xml", 0, randomAngle());
template = "actor|props/flora/reeds_pond_lush_b.xml";
else if (randBool(propDensity / 10))
placeObject(x, y, "actor|props/flora/reeds_pond_lush_a.xml", 0, randomAngle());
template = "actor|props/flora/reeds_pond_lush_a.xml";
}
if (i == 3) // ...water suroundings/bog
{
if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/water_log.xml", 0, randomAngle());
template = "actor|props/flora/water_log.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|geology/highland_c.xml", 0, randomAngle());
template = "actor|geology/highland_c.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/reeds_pond_lush_a.xml", 0, randomAngle());
template = "actor|props/flora/reeds_pond_lush_a.xml";
}
if (i == 4) // ...low height grass
{
if (randBool(propDensity / 800))
placeObject(x, y, "actor|props/flora/grass_field_flowering_tall.xml", 0, randomAngle());
template = "actor|props/flora/grass_field_flowering_tall.xml";
else if (randBool(propDensity / 400))
placeObject(x, y, "actor|geology/gray_rock1.xml", 0, randomAngle());
template = "actor|geology/gray_rock1.xml";
else if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/bush_tempe_sm_lush.xml", 0, randomAngle());
template = "actor|props/flora/bush_tempe_sm_lush.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/bush_tempe_b.xml", 0, randomAngle());
template = "actor|props/flora/bush_tempe_b.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/grass_soft_small_tall.xml", 0, randomAngle());
template = "actor|props/flora/grass_soft_small_tall.xml";
}
if (i == 5) // ...medium height grass
{
if (randBool(propDensity / 800))
placeObject(x, y, "actor|geology/decal_stone_medit_a.xml", 0, randomAngle());
template = "actor|geology/decal_stone_medit_a.xml";
else if (randBool(propDensity / 400))
placeObject(x, y, "actor|props/flora/decals_flowers_daisies.xml", 0, randomAngle());
template = "actor|props/flora/decals_flowers_daisies.xml";
else if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/bush_tempe_underbrush.xml", 0, randomAngle());
template = "actor|props/flora/bush_tempe_underbrush.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/grass_soft_small_tall.xml", 0, randomAngle());
template = "actor|props/flora/grass_soft_small_tall.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/grass_temp_field.xml", 0, randomAngle());
template = "actor|props/flora/grass_temp_field.xml";
}
if (i == 6) // ...high height grass
{
if (randBool(propDensity / 400))
placeObject(x, y, "actor|geology/stone_granite_boulder.xml", 0, randomAngle());
template = "actor|geology/stone_granite_boulder.xml";
else if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/foliagebush.xml", 0, randomAngle());
template = "actor|props/flora/foliagebush.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/bush_tempe_underbrush.xml", 0, randomAngle());
template = "actor|props/flora/bush_tempe_underbrush.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/grass_soft_small_tall.xml", 0, randomAngle());
template = "actor|props/flora/grass_soft_small_tall.xml";
else if (randBool(propDensity / 20))
placeObject(x, y, "actor|props/flora/ferns.xml", 0, randomAngle());
template = "actor|props/flora/ferns.xml";
}
if (i == 7) // ...forest border (with wood/food plants/deer/rabits)
{
if (randBool(propDensity / 400))
placeObject(x, y, "actor|geology/highland_c.xml", 0, randomAngle());
template = "actor|geology/highland_c.xml";
else if (randBool(propDensity / 200))
placeObject(x, y, "actor|props/flora/bush_tempe_a.xml", 0, randomAngle());
template = "actor|props/flora/bush_tempe_a.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/ferns.xml", 0, randomAngle());
template = "actor|props/flora/ferns.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/grass_soft_tuft_a.xml", 0, randomAngle());
template = "actor|props/flora/grass_soft_tuft_a.xml";
}
if (i == 8) // ...woods
{
if (randBool(propDensity / 200))
placeObject(x, y, "actor|geology/highland2_moss.xml", 0, randomAngle());
template = "actor|geology/highland2_moss.xml";
else if (randBool(propDensity / 100))
placeObject(x, y, "actor|props/flora/grass_soft_tuft_a.xml", 0, randomAngle());
template = "actor|props/flora/grass_soft_tuft_a.xml";
else if (randBool(propDensity / 40))
placeObject(x, y, "actor|props/flora/ferns.xml", 0, randomAngle());
template = "actor|props/flora/ferns.xml";
}
if (template)
placeObject(position, template, 0, randomAngle());
break;
}
else
textureMinHeight = textueByHeight[i].upperHeightLimit;
}
}
}
Engine.SetProgress(90);
@@ -404,7 +407,7 @@ else
new Vector2D(-0.75 * resourceSpacing * Math.floor(resourceCount / 2), 0).rotate(-uAngle - Math.PI/2)
]);
placeObject(pos.x, pos.y, j % 2 ? "gaia/flora_tree_cypress" : "gaia/flora_bush_berry", 0, randomAngle());
placeObject(pos, j % 2 ? "gaia/flora_tree_cypress" : "gaia/flora_bush_berry", 0, randomAngle());
}
}
}
@@ -53,15 +53,16 @@ let decorations = [
function placeMine(point, centerEntity)
{
placeObject(point.x, point.y, centerEntity, 0, randomAngle());
placeObject(point, centerEntity, 0, randomAngle());
let quantity = randIntInclusive(11, 23);
let dAngle = 2 * Math.PI / quantity;
for (let i = 0; i < quantity; ++i)
{
let angle = dAngle * randFloat(i, i + 1);
let dist = randFloat(2, 5);
placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(decorations), 0, randomAngle());
}
placeObject(
Vector2D.add(point, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
pickRandom(decorations),
0,
randomAngle());
}
// Food, fences with domestic animals
@@ -122,7 +123,7 @@ let clGrove = g_Map.createTileClass();
function placeGrove(point)
{
placeObject(point.x, point.y, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
placeObject(point, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
let quantity = randIntInclusive(20, 30);
let dAngle = 2 * Math.PI / quantity;
for (let i = 0; i < quantity; ++i)
@@ -133,7 +134,7 @@ function placeGrove(point)
if (i % 3 == 0)
objectList = groveActors;
let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle));
placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle());
placeObject(position, pickRandom(objectList), 0, randomAngle());
createArea(
new ClumpPlacer(5, 1, 1, 1, position),
[
@@ -152,14 +153,14 @@ function placeCamp(point,
]
)
{
placeObject(point.x, point.y, centerEntity, 0, randomAngle());
placeObject(point, centerEntity, 0, 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(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(otherEntities), 0, randomAngle());
placeObject(Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), pickRandom(otherEntities), 0, randomAngle());
}
}
@@ -184,7 +185,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.x, woodPosition.y, pickRandom(objectList), 0, randomAngle());
placeObject(woodPosition, pickRandom(objectList), 0, randomAngle());
createArea(
new ClumpPlacer(5, 1, 1, 1, woodPosition),
[
@@ -208,7 +209,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.x, berriesPosition.y, pickRandom(foodEntities), 0, randomAngle());
placeObject(berriesPosition, pickRandom(foodEntities), 0, randomAngle());
currentAngle += dAngle;
}
}
@@ -413,7 +414,7 @@ for (let h = 0; h < heighLimits.length; ++h)
g_Map.setTexture(point, texture);
if (actor)
placeObject(point.x + randFloat(0, 1), point.y + randFloat(0, 1), actor, 0, randomAngle());
placeObject(Vector2D.add(point, new Vector2D(randFloat(0, 1), randFloat(0, 1))), actor, 0, randomAngle());
}
Engine.SetProgress(80);
@@ -423,25 +424,26 @@ if (isNomad())
else
for (let p = 0; p < playerIDs.length; ++p)
{
let point = startLocations[p];
placeCivDefaultStartingEntities(point, playerIDs[p], true);
placeStartLocationResources(startLocations[p]);
let pos = new Vector2D(startLocations[p].x, startLocations[p].y);
placeCivDefaultStartingEntities(pos, playerIDs[p], true);
placeStartLocationResources(pos);
}
log("Placing resources, farmsteads, groves and camps...");
for (let i = 0; i < resourceSpots.length; ++i)
{
let pos = new Vector2D(resourceSpots[i].x, resourceSpots[i].y);
let choice = i % 5;
if (choice == 0)
placeMine(resourceSpots[i], "gaia/geology_stonemine_temperate_formation");
placeMine(pos, "gaia/geology_stonemine_temperate_formation");
if (choice == 1)
placeMine(resourceSpots[i], "gaia/geology_metal_temperate_slabs");
placeMine(pos, "gaia/geology_metal_temperate_slabs");
if (choice == 2)
placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randomAngle());
placeCustomFortress(pos, pickRandom(fences), "other", 0, randomAngle());
if (choice == 3)
placeGrove(resourceSpots[i]);
placeGrove(pos);
if (choice == 4)
placeCamp(resourceSpots[i]);
placeCamp(pos);
}
g_Map.ExportMap();
@@ -338,7 +338,7 @@ createStragglerTrees(
log("Creating treasures...");
for (let i = 0; i < randIntInclusive(3, 8); ++i)
for (let template of [oFoodTreasure, oWoodTreasure])
placeObject(mapCenter.x + randFloat(-7, 7), mapCenter.y + randFloat(-7, 7), template, 0, randomAngle());
placeObject(Vector2D.add(mapCenter, new Vector2D(randFloat(0, 7), 0).rotate(randomAngle())), template, 0, randomAngle());
placePlayersNomad(
clPlayer,
@@ -298,17 +298,17 @@ if (gallicCC)
new TileClassPainter(clRitualPlace)
]);
placeObject(meetingPlacePosition.x, meetingPlacePosition.y, aCampfire, 0, randomAngle());
placeObject(meetingPlacePosition, aCampfire, 0, 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].x, positions[i].y, pickRandom(participants.templates), 0, angles[i] + participants.angle);
placeObject(positions[i], pickRandom(participants.templates), 0, angles[i] + participants.angle);
}
}
placeObject(civicCenterPosition.x, civicCenterPosition.y, oCivicCenter, 0, startAngle + BUILDING_ORIENTATION + Math.PI * 3/2 * i);
placeObject(civicCenterPosition, oCivicCenter, 0, startAngle + BUILDING_ORIENTATION + Math.PI * 3/2 * i);
// Create the city patch
createArea(
@@ -319,14 +319,13 @@ if (gallicCC)
]);
// Place walls and buildings
placeCustomFortress(civicCenterPosition.x, civicCenterPosition.y, fortressDanubiusVillage, "danubius_village", 0, startAngle + Math.PI);
placeCustomFortress(civicCenterPosition.x, civicCenterPosition.y, fortressDanubiusSpikes, "danubius_spikes", 0, startAngle + Math.PI);
placeCustomFortress(civicCenterPosition, fortressDanubiusVillage, "danubius_village", 0, startAngle + Math.PI);
placeCustomFortress(civicCenterPosition, fortressDanubiusSpikes, "danubius_spikes", 0, startAngle + Math.PI);
// Place treasure, potentially inside buildings
for (let i = 0; i < gallicCCTreasureCount; ++i)
placeObject(
civicCenterPosition.x + randFloat(-0.8, 0.8) * gaulCityRadius,
civicCenterPosition.y + randFloat(-0.8, 0.8) * gaulCityRadius,
Vector2D.add(civicCenterPosition, new Vector2D(randFloat(-1, 1) * 0.8 * gaulCityRadius, 0).rotate(randomAngle())),
pickRandom(oTreasures),
0,
randomAngle());
@@ -715,8 +714,8 @@ for (let i = 0; i < 2; ++i)
log("Creating patrol points for land attackers...");
addToClass(mapCenter.x, mapCenter.y, clMiddle);
var riverDirectionPosition = Vector2D.add(mapCenter, new Vector2D(0, 1).rotate(startAngle));
placeObject(riverDirectionPosition.x, riverDirectionPosition.y, triggerPointRiverDirection, 0, 0);
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);
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.x, position.y, resourcePerPlayer[rIndex], 0, randomAngle());
placeObject(position, resourcePerPlayer[rIndex], 0, 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.x, mapCenter.y, templateTemple, 0, randomAngle());
placeObject(mapCenter, templateTemple, 0, randomAngle());
addToClass(mapCenter.x, mapCenter.y, clBaseResource);
log("Creating central mountain...");
@@ -132,7 +132,7 @@ for (let i = 0; i < numPlayers; ++i)
"cornerIn", "long", "house", "tower", "long", "tower", "long",
"cornerIn", "long", "house", "tower"];
}
placeCustomFortress(playerPosition[i].x, playerPosition[i].y, new Fortress("Spahbod", wall), civ, playerIDs[i], -Math.PI/4);
placeCustomFortress(playerPosition[i], new Fortress("Spahbod", wall), civ, playerIDs[i], -Math.PI/4);
}
log("Creating lakes...");
@@ -84,7 +84,7 @@ if (!isNomad())
]);
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , heightLand - 0.5, heightLand);
placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI);
placeObject(dockLocation, oDock, playerIDs[i], 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(randFloat(ix, ix + 1), randFloat(iz, iz + 1), aGrassDry, 0, randomAngle());
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), aGrassDry, 0, randomAngle());
}
else if (grassNoise > 0.61)
{
t = (diffH > 1.2 ? tGrassRock : tGrassShrubs);
}
else if (diffH < 0.5 && randBool(0.02))
placeObject(randFloat(ix, ix + 1), randFloat(iz, iz + 1), aGrass, 0, randomAngle());
placeObject(Vector2D.add(position, new Vector2D(1, 1).mult(randFloat(0, 1))), aGrass, 0, randomAngle());
}
createTerrain(t).place(position);
@@ -87,7 +87,7 @@ for (let i = 0; i < numPlayers; ++i)
continue;
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 2.6, 3);
placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI);
placeObject(dockLocation, oDock, playerIDs[i], playerAngle[i] + Math.PI);
}
Engine.SetProgress(10);
@@ -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.x, marketPos.y, oMarket, playerIDs[i], BUILDING_ORIENTATION);
placeObject(marketPos, oMarket, playerIDs[i], BUILDING_ORIENTATION);
addCivicCenterAreaToClass(marketPos, clBaseResource);
}
@@ -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.x, pos.y, templateName, 0, randomAngle());
placeObject(pos, templateName, 0, randomAngle());
angle += 3/2 * Math.PI / count;
}
}
@@ -183,11 +183,10 @@ function createTerrain(terrain)
new RandomTerrain(terrain.map(t => createTerrain(t)));
}
function placeObject(x, z, type, player, angle)
function placeObject(position, type, player, angle)
{
let position = new Vector2D(x, z);
if (g_Map.validTile(position))
g_Map.addObject(new Entity(type, player, x, z, angle));
g_Map.addObject(new Entity(type, player, position.x, position.y, angle));
}
function getTileClass(id)
@@ -79,7 +79,7 @@ function placeStartingEntities(location, playerID, civEntities, dist = 6, orient
let firstTemplate = civEntities[i].Template;
if (firstTemplate.startsWith("structures/"))
{
placeObject(location.x, location.y, firstTemplate, playerID, orientation);
placeObject(location, firstTemplate, playerID, orientation);
++i;
}
@@ -91,38 +91,41 @@ function placeStartingEntities(location, playerID, civEntities, dist = 6, orient
let count = civEntities[j].Count || 1;
for (let num = 0; num < count; ++num)
placeObject(
location.x + dist * Math.cos(angle) + space * (-num + 0.75 * Math.floor(count / 2)) * Math.sin(angle),
location.y + dist * Math.sin(angle) + space * (num - 0.75 * Math.floor(count / 2)) * Math.cos(angle),
civEntities[j].Template,
playerID,
angle);
{
let position = Vector2D.sum([
location,
new Vector2D(dist, 0).rotate(-angle),
new Vector2D(space * (-num + 0.75 * Math.floor(count / 2)), 0).rotate(angle)
]);
placeObject(position, civEntities[j].Template, playerID, angle);
}
}
}
/**
* Places the default starting entities as defined by the civilization definition, optionally including city walls.
*/
function placeCivDefaultStartingEntities(location, playerID, wallType, dist = 6, orientation = BUILDING_ORIENTATION)
function placeCivDefaultStartingEntities(position, playerID, wallType, dist = 6, orientation = BUILDING_ORIENTATION)
{
placeStartingEntities(location, playerID, getStartingEntities(playerID), dist, orientation);
placeStartingWalls(location.x, location.y, playerID, wallType, orientation);
placeStartingEntities(position, playerID, getStartingEntities(playerID), dist, orientation);
placeStartingWalls(position, playerID, wallType, orientation);
}
/**
* If the map is large enough and the civilization defines them, places the initial city walls or towers.
* @param {string|boolean} wallType - Either "towers" to only place the wall turrets or a boolean indicating enclosing city walls.
*/
function placeStartingWalls(x, z, playerID, wallType, orientation = BUILDING_ORIENTATION)
function placeStartingWalls(position, playerID, wallType, orientation = BUILDING_ORIENTATION)
{
let civ = getCivCode(playerID);
if (civ != "iber" || g_Map.getSize() <= 128)
return;
if (wallType == "towers")
placePolygonalWall(x, z, 15, ["entry"], "tower", civ, playerID, orientation, 7);
placePolygonalWall(position, 15, ["entry"], "tower", civ, playerID, orientation, 7);
else if (wallType)
placeGenericFortress(x, z, 20, playerID);
placeGenericFortress(position, 20, playerID);
}
/**
@@ -279,19 +279,17 @@ function readyWallElement(path, civCode)
* Placing the first wall element at startX/startY placed with an angle given by orientation
* An alignment can be used to get the "center" of a "wall" (more likely used for fortresses) with getCenterToFirstElement
*
* @param {number} startX
* @param {number} startY
* @param {Vector2D} position
* @param {array} [wall]
* @param {string} [style]
* @param {number} [orientation]
* @returns {array}
*/
function getWallAlignment(startX, startY, wall = [], style = "athen_stone", orientation = 0)
function getWallAlignment(position, wall = [], style = "athen_stone", orientation = 0)
{
style = validateStyle(style);
let alignment = [];
let wallX = startX;
let wallY = startY;
let wallPosition = position.clone();
for (let i = 0; i < wall.length; ++i)
{
@@ -302,14 +300,9 @@ function getWallAlignment(startX, startY, wall = [], style = "athen_stone", orie
continue;
}
// Indentation
let placeX = wallX - element.indent * Math.cos(orientation);
let placeY = wallY - element.indent * Math.sin(orientation);
// Add wall elements entity placement arguments to the alignment
alignment.push({
"x": placeX,
"y": placeY,
"position": Vector2D.sub(wallPosition, new Vector2D(element.indent, 0).rotate(-orientation)),
"templateName": element.templateName,
"angle": orientation + element.angle
});
@@ -336,12 +329,11 @@ function getWallAlignment(startX, startY, wall = [], style = "athen_stone", orie
distance += indent * Math.sin(bend);
// Indent correction to normalize indentation
wallX += indent * Math.cos(orientation);
wallY += indent * Math.sin(orientation);
wallPosition.add(new Vector2D(indent).rotate(-orientation));
}
// Set the next coordinates of the next element in the wall without indentation adjustment
wallX -= distance * Math.sin(orientation);
wallY += distance * Math.cos(orientation);
wallPosition.add(new Vector2D(distance, 0).rotate(-orientation).perpendicular());
}
}
return alignment;
@@ -357,13 +349,7 @@ function getWallAlignment(startX, startY, wall = [], style = "athen_stone", orie
*/
function getCenterToFirstElement(alignment)
{
let centerToFirstElement = { "x": 0, "y": 0 };
for (let align of alignment)
{
centerToFirstElement.x -= align.x / alignment.length;
centerToFirstElement.y -= align.y / alignment.length;
}
return centerToFirstElement;
return alignment.reduce((result, align) => result.sub(Vector2D.div(align.position, alignment.length)), new Vector2D(0, 0));
}
/**
@@ -412,8 +398,7 @@ function validateStyle(style, playerId = 0)
/**
* Places an abitrary wall beginning at the location comprised of the array of elements provided.
*
* @param {number} startX
* @param {number} startY
* @param {Vector2D} position
* @param {array} [wall] - Array of wall element types. Example: ["start", "long", "tower", "long", "end"]
* @param {string} [style] - Wall style string.
* @param {number} [playerId] - Identifier of the player for whom the wall will be placed.
@@ -422,14 +407,13 @@ function validateStyle(style, playerId = 0)
* It will then be build towards top/positive Y (if no bending wall elements like corners are used)
* Raising orientation means the wall is rotated counter-clockwise like placeObject
*/
function placeWall(startX, startY, wall = [], style, playerId = 0, orientation = 0)
function placeWall(position, wall = [], style, playerId = 0, orientation = 0)
{
style = validateStyle(style, playerId);
let AM = getWallAlignment(startX, startY, wall, style, orientation);
for (let iWall = 0; iWall < wall.length; ++iWall)
if (AM[iWall].templateName)
placeObject(AM[iWall].x, AM[iWall].y, AM[iWall].templateName, playerId, AM[iWall].angle);
for (let align of getWallAlignment(position, wall, style, orientation))
if (align.templateName)
placeObject(align.position, align.templateName, playerId, align.angle);
}
/**
@@ -439,14 +423,13 @@ function placeWall(startX, startY, wall = [], style, playerId = 0, orientation =
* The fortress wall should always start with the main entrance (like
* "entry" or "gate") to get the orientation correct.
*
* @param {number} centerX
* @param {number} centerY
* @param {Vector2D} centerPosition
* @param {object} [fortress] - If not provided, defaults to the predefined "medium" fortress type.
* @param {string} [style] - Wall style string.
* @param {number} [playerId] - Identifier of the player for whom the wall will be placed.
* @param {number} [orientation] - Angle the first wall element (should be a gate or entrance) is placed. Default is 0
*/
function placeCustomFortress(centerX, centerY, fortress, style, playerId = 0, orientation = 0)
function placeCustomFortress(centerPosition, fortress, style, playerId = 0, orientation = 0)
{
fortress = fortress || g_FortressTypes.medium;
style = validateStyle(style, playerId);
@@ -454,12 +437,16 @@ function placeCustomFortress(centerX, centerY, fortress, style, playerId = 0, or
// Calculate center if fortress.centerToFirstElement is undefined (default)
let centerToFirstElement = fortress.centerToFirstElement;
if (centerToFirstElement === undefined)
centerToFirstElement = getCenterToFirstElement(getWallAlignment(0, 0, fortress.wall, style));
centerToFirstElement = getCenterToFirstElement(getWallAlignment(new Vector2D(0, 0), fortress.wall, style));
// Placing the fortress wall
let startX = centerX + centerToFirstElement.x * Math.cos(orientation) - centerToFirstElement.y * Math.sin(orientation);
let startY = centerY + centerToFirstElement.y * Math.cos(orientation) + centerToFirstElement.x * Math.sin(orientation);
placeWall(startX, startY, fortress.wall, style, playerId, orientation);
let position = Vector2D.sum([
centerPosition,
new Vector2D(centerToFirstElement.x, 0).rotate(-orientation),
new Vector2D(centerToFirstElement.y, 0).perpendicular().rotate(-orientation)
]);
placeWall(position, fortress.wall, style, playerId, orientation);
}
/**
@@ -469,10 +456,9 @@ function placeCustomFortress(centerX, centerY, fortress, style, playerId = 0, or
*
* @param {string} [type] - Predefined fortress type, as used as a key in g_FortressTypes.
*/
function placeFortress(centerX, centerY, type = "medium", style, playerId = 0, orientation = 0)
function placeFortress(centerPosition, type = "medium", style, playerId = 0, orientation = 0)
{
// Call placeCustomFortress with the given arguments
placeCustomFortress(centerX, centerY, g_FortressTypes[type], style, playerId, orientation);
placeCustomFortress(centerPosition, g_FortressTypes[type], style, playerId, orientation);
}
/**
@@ -481,15 +467,13 @@ function placeFortress(centerX, centerY, type = "medium", style, playerId = 0, o
*
* Note: Any "bending" wall pieces passed will be complained about.
*
* @param {number} startX - Approximate start point of the wall.
* @param {number} startY - Approximate start point of the wall.
* @param {number} targetX - Approximate end point of the wall.
* @param {number} targetY - Approximate end point of the wall.
* @param {Vector2D} startPosition - Approximate start point of the wall.
* @param {Vector2D} targetPosition - Approximate end point of the wall.
* @param {array} [wallPart=["tower", "long"]]
* @param {number} [playerId]
* @param {boolean} [endWithFirst] - If true, the first wall element will also be the last.
*/
function placeLinearWall(startX, startY, targetX, targetY, wallPart = undefined, style, playerId = 0, endWithFirst = true)
function placeLinearWall(startPosition, targetPosition, wallPart = undefined, style, playerId = 0, endWithFirst = true)
{
wallPart = wallPart || ["tower", "long"];
style = validateStyle(style, playerId);
@@ -501,7 +485,7 @@ function placeLinearWall(startX, startY, targetX, targetY, wallPart = undefined,
// Setup number of wall parts
let totalLength = Math.euclidDistance2D(startX, startY, targetX, targetY);
let totalLength = startPosition.distanceTo(targetPosition);
let wallPartLength = getWallLength(style, wallPart);
let numParts = Math.ceil(totalLength / wallPartLength);
if (endWithFirst)
@@ -513,47 +497,39 @@ function placeLinearWall(startX, startY, targetX, targetY, wallPart = undefined,
scaleFactor = totalLength / (numParts * wallPartLength + getWallElement(wallPart[0], style).length);
// Setup angle
let wallAngle = getAngle(startX, startY, targetX, targetY); // NOTE: function "getAngle()" is about to be changed...
let wallAngle = getAngle(startPosition.x, startPosition.y, targetPosition.x, targetPosition.y);
let placeAngle = wallAngle - Math.PI / 2;
// Place wall entities
let x = startX;
let y = startY;
let position = startPosition.clone();
let overlap = g_WallStyles[style].overlap;
for (let partIndex = 0; partIndex < numParts; ++partIndex)
{
for (let elementIndex = 0; elementIndex < wallPart.length; ++elementIndex)
{
let wallEle = getWallElement(wallPart[elementIndex], style);
let wallLength = (wallEle.length - overlap) / 2;
let distX = scaleFactor * wallLength * Math.cos(wallAngle);
let distY = scaleFactor * wallLength * Math.sin(wallAngle);
let dist = new Vector2D(scaleFactor * wallLength, 0).rotate(-wallAngle);
// Length correction
x += distX;
y += distY;
position.add(dist);
// Indent correction
let placeX = x - wallEle.indent * Math.sin(wallAngle);
let placeY = y + wallEle.indent * Math.cos(wallAngle);
let place = Vector2D.add(position, new Vector2D(0, wallEle.indent).rotate(-wallAngle));
// Placement
if (wallEle.templateName)
placeObject(placeX, placeY, wallEle.templateName, playerId, placeAngle + wallEle.angle);
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
// Prep for next object
x += distX;
y += distY;
position.add(dist);
}
}
if (endWithFirst)
{
let wallEle = getWallElement(wallPart[0], style);
let wallLength = (wallEle.length - overlap) / 2;
x += scaleFactor * wallLength * Math.cos(wallAngle);
y += scaleFactor * wallLength * Math.sin(wallAngle);
position.add(new Vector2D(scaleFactor * wallLength, 0).rotate(-wallAngle));
if (wallEle.templateName)
placeObject(x, y, wallEle.templateName, playerId, placeAngle + wallEle.angle);
placeObject(position, wallEle.templateName, playerId, placeAngle + wallEle.angle);
}
}
@@ -568,8 +544,7 @@ function placeLinearWall(startX, startY, targetX, targetY, wallPart = undefined,
*
* Note: Any "bending" wall pieces passed will be complained about.
*
* @param {number} centerX - Center of the circle or arc.
* @param {number} centerY - Center of the circle or arc.
* @param {Vector2D} center - Center of the circle or arc.
* @param (number} radius - Approximate radius of the circle. (Given the maxBendOff argument)
* @param {array} [wallPart]
* @param {string} [style]
@@ -579,7 +554,7 @@ function placeLinearWall(startX, startY, targetX, targetY, wallPart = undefined,
* @param {boolean} [endWithFirst] - If true, the first wall element will also be the last. For full circles, the default is false. For arcs, true.
* @param {number} [maxBendOff] Optional. How irregular the circle should be. 0 means regular circle, PI/2 means very irregular. Default is 0 (regular circle)
*/
function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId = 0, orientation = 0, maxAngle = Math.PI * 2, endWithFirst, maxBendOff = 0)
function placeCircularWall(center, radius, wallPart, style, playerId = 0, orientation = 0, maxAngle = Math.PI * 2, endWithFirst, maxBendOff = 0)
{
wallPart = wallPart || ["tower", "long"];
style = validateStyle(style, playerId);
@@ -609,8 +584,7 @@ function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId =
// Place wall entities
let actualAngle = orientation + (Math.PI * 2 - maxAngle) / 2;
let x = centerX + radius * Math.cos(actualAngle);
let y = centerY + radius * Math.sin(actualAngle);
let position = Vector2D.add(center, new Vector2D(radius, 0).rotate(-actualAngle));
let overlap = g_WallStyles[style].overlap;
for (let partIndex = 0; partIndex < numParts; ++partIndex)
for (let wallEle of wallPart)
@@ -619,36 +593,30 @@ function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId =
// Width correction
let addAngle = scaleFactor * (wallEle.length - overlap) / radius;
let targetX = centerX + radius * Math.cos(actualAngle + addAngle);
let targetY = centerY + radius * Math.sin(actualAngle + addAngle);
let placeX = x + (targetX - x) / 2;
let placeY = y + (targetY - y) / 2;
let target = Vector2D.add(center, new Vector2D(radius, 0).rotate(-actualAngle - addAngle));
let place = Vector2D.average([position, target]);
let placeAngle = actualAngle + addAngle / 2;
// Indent correction
placeX -= wallEle.indent * Math.cos(placeAngle);
placeY -= wallEle.indent * Math.sin(placeAngle);
place.sub(new Vector2D(wallEle.indent, 0).rotate(-placeAngle));
// Placement
if (wallEle.templateName)
placeObject(placeX, placeY, wallEle.templateName, playerId, placeAngle + wallEle.angle);
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
// Prepare for the next wall element
actualAngle += addAngle;
x = centerX + radius * Math.cos(actualAngle);
y = centerY + radius * Math.sin(actualAngle);
position = Vector2D.add(center, new Vector2D(radius, 0).rotate(-actualAngle));
}
if (endWithFirst)
{
let wallEle = getWallElement(wallPart[0], style);
let addAngle = scaleFactor * wallEle.length / radius;
let targetX = centerX + radius * Math.cos(actualAngle + addAngle);
let targetY = centerY + radius * Math.sin(actualAngle + addAngle);
let placeX = x + (targetX - x) / 2;
let placeY = y + (targetY - y) / 2;
let target = Vector2D.add(center, new Vector2D(radius, 0).rotate(-actualAngle - addAngle))
let place = Vector2D.average([position, target]);
let placeAngle = actualAngle + addAngle / 2;
placeObject(placeX, placeY, wallEle.templateName, playerId, placeAngle + wallEle.angle);
placeObject(place, wallEle.templateName, playerId, placeAngle + wallEle.angle);
}
}
@@ -658,8 +626,7 @@ function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId =
*
* Note: Any "bending" wall pieces passed will be ignored.
*
* @param {number} centerX
* @param {number} centerY
* @param {Vector2D} centerPosition
* @param {number} radius
* @param {array} [wallPart]
* @param {string} [cornerWallElement] - Wall element to be placed at the polygon's corners.
@@ -669,42 +636,35 @@ function placeCircularWall(centerX, centerY, radius, wallPart, style, playerId =
* @param {number} [numCorners] - How many corners the polygon will have.
* @param {boolean} [skipFirstWall] - If the first linear wall part will be left opened as entrance.
*/
function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElement = "tower", style, playerId = 0, orientation = 0, numCorners = 8, skipFirstWall = true)
function placePolygonalWall(centerPosition, radius, wallPart, cornerWallElement = "tower", style, playerId = 0, orientation = 0, numCorners = 8, skipFirstWall = true)
{
wallPart = wallPart || ["long", "tower"];
style = validateStyle(style, playerId);
// Setup angles
let angleAdd = Math.PI * 2 / numCorners;
let angleStart = orientation - angleAdd / 2;
let corners = new Array(numCorners).fill(0).map((zero, i) =>
Vector2D.add(centerPosition, new Vector2D(radius, 0).rotate(-angleStart - i * angleAdd)));
// Setup corners
let corners = [];
for (let i = 0; i < numCorners; ++i)
corners.push([
centerX + radius * Math.cos(angleStart + i * angleAdd),
centerY + radius * Math.sin(angleStart + i * angleAdd)
]);
// Place Corners and walls
for (let i = 0; i < numCorners; ++i)
{
let angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
placeObject(corners[i][0], corners[i][1], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
let angleToCorner = getAngle(corners[i].x, corners[i].y, centerPosition.x, centerPosition.y);
placeObject(corners[i], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
if (!skipFirstWall || i != 0)
{
let cornerLength = getWallElement(cornerWallElement, style).length / 2;
let cornerAngle = angleToCorner + angleAdd / 2;
let cornerX = cornerLength * Math.sin(cornerAngle);
let cornerY = cornerLength * Math.cos(cornerAngle);
let targetCorner = (i + 1) % numCorners;
let cornerPosition = new Vector2D(cornerLength, 0).rotate(-cornerAngle).perpendicular();
placeLinearWall(
// Adjustment to the corner element width (approximately)
corners[i][0] + cornerX, // startX
corners[i][1] - cornerY, // startY
corners[targetCorner][0] - cornerX, // targetX
corners[targetCorner][1] + cornerY, // targetY
wallPart, style, playerId);
Vector2D.sub(corners[i], cornerPosition),
Vector2D.add(corners[targetCorner], cornerPosition),
wallPart,
style,
playerId);
}
}
}
@@ -718,8 +678,7 @@ function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElemen
*
* Note: The wallPartsAssortment is last because it's the hardest to set.
*
* @param {number} centerX
* @param {number} centerY
* @param {Vector2D} centerPosition
* @param {number} radius
* @param {string} [cornerWallElement] - Wall element to be placed at the polygon's corners.
* @param {string} [style]
@@ -730,7 +689,7 @@ function placePolygonalWall(centerX, centerY, radius, wallPart, cornerWallElemen
* @param {boolean} [skipFirstWall] - If true, the first linear wall part will be left open as an entrance.
* @param {array} [wallPartsAssortment] - An array of wall part arrays to choose from for each linear wall connecting the corners.
*/
function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement = "tower", style, playerId = 0, orientation = 0, numCorners, irregularity = 0.5, skipFirstWall = false, wallPartsAssortment)
function placeIrregularPolygonalWall(centerPosition, radius, cornerWallElement = "tower", style, playerId = 0, orientation = 0, numCorners, irregularity = 0.5, skipFirstWall = false, wallPartsAssortment)
{
style = validateStyle(style, playerId);
numCorners = numCorners || randIntInclusive(5, 7);
@@ -775,10 +734,8 @@ function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement
let angleActual = orientation - angleAddList[0] / 2;
for (let i = 0; i < numCorners; ++i)
{
corners.push([
centerX + radius * Math.cos(angleActual),
centerY + radius * Math.sin(angleActual)
]);
corners.push(Vector2D.add(centerPosition, new Vector2D(radius, 0).rotate(-angleActual)));
if (i < numCorners - 1)
angleActual += angleAddList[i + 1];
}
@@ -801,7 +758,7 @@ function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement
let bestWallLength = Infinity;
let targetCorner = (i + 1) % numCorners;
// NOTE: This is not quite the length the wall will be in the end. Has to be tweaked...
let wallLength = Math.euclidDistance2D(corners[i][0], corners[i][1], corners[targetCorner][0], corners[targetCorner][1]);
let wallLength = corners[i].distanceTo(corners[targetCorner]);
let numWallParts = Math.ceil(wallLength / maxWallPartLength);
for (let partIndex = 0; partIndex < wallPartsAssortment.length; ++partIndex)
{
@@ -818,21 +775,24 @@ function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement
// Place Corners and walls
for (let i = 0; i < numCorners; ++i)
{
let angleToCorner = getAngle(corners[i][0], corners[i][1], centerX, centerY);
placeObject(corners[i][0], corners[i][1], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
let angleToCorner = getAngle(corners[i].x, corners[i].y, centerPosition.x, centerPosition.y);
placeObject(corners[i], getWallElement(cornerWallElement, style).templateName, playerId, angleToCorner);
if (!skipFirstWall || i != 0)
{
let cornerLength = getWallElement(cornerWallElement, style).length / 2;
let targetCorner = (i + 1) % numCorners;
let startAngle = angleToCorner + angleAddList[i] / 2;
let targetAngle = angleToCorner + angleAddList[targetCorner] / 2;
placeLinearWall(
// Adjustment to the corner element width (approximately)
corners[i][0] + cornerLength * Math.sin(startAngle), // startX
corners[i][1] - cornerLength * Math.cos(startAngle), // startY
corners[targetCorner][0] - cornerLength * Math.sin(targetAngle), // targetX
corners[targetCorner][1] + cornerLength * Math.cos(targetAngle), // targetY
wallPartList[i], style, playerId, false);
Vector2D.sub(corners[i], new Vector2D(cornerLength, 0).perpendicular().rotate(-startAngle)),
Vector2D.add(corners[targetCorner], new Vector2D(cornerLength, 0).rotate(-targetAngle - Math.PI / 2)),
wallPartList[i],
style,
playerId,
false);
}
}
}
@@ -848,23 +808,21 @@ function placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement
*
* This is the default Iberian civ bonus starting wall.
*
* @param {number} centerX - The approximate center coordinates of the fortress
* @param {number} centerY - The approximate center coordinates of the fortress
* @param {Vector2D} center - The approximate center coordinates of the fortress
* @param {number} [radius] - The approximate radius of the wall to be placed.
* @param {number} [playerId]
* @param {string} [style]
* @param {number} [irregularity] - 0 = circle, 1 = very spiky
* @param {number} [gateOccurence] - Integer number, every n-th walls will be a gate instead.
* @param {number} [maxTrys] - How often the function tries to find a better fitting shape.
* @param {number} [maxTries] - How often the function tries to find a better fitting shape.
*/
function placeGenericFortress(centerX, centerY, radius = 20, playerId = 0, style, irregularity = 0.5, gateOccurence = 3, maxTrys = 100)
function placeGenericFortress(center, radius = 20, playerId = 0, style, irregularity = 0.5, gateOccurence = 3, maxTries = 100)
{
style = validateStyle(style, playerId);
// Setup some vars
let startAngle = randFloat(0, Math.PI * 2);
let actualOffX = radius * Math.cos(startAngle);
let actualOffY = radius * Math.sin(startAngle);
let startAngle = randomAngle();
let actualOff = new Vector2D(radius, 0).rotate(-startAngle);
let actualAngle = startAngle;
let pointDistance = getWallLength(style, ["long", "tower"]);
@@ -873,33 +831,31 @@ function placeGenericFortress(centerX, centerY, radius = 20, playerId = 0, style
let bestPointDerivation;
let minOverlap = 1000;
let overlap;
while (tries < maxTrys && minOverlap > g_WallStyles[style].overlap)
while (tries < maxTries && minOverlap > g_WallStyles[style].overlap)
{
let pointDerivation = [];
let distanceToTarget = 1000;
let targetReached = false;
while (!targetReached)
while (true)
{
let indent = randFloat(-irregularity * pointDistance, irregularity * pointDistance);
let tmpAngle = getAngle(actualOffX, actualOffY,
(radius + indent) * Math.cos(actualAngle + pointDistance / radius),
(radius + indent) * Math.sin(actualAngle + pointDistance / radius)
);
actualOffX += pointDistance * Math.cos(tmpAngle);
actualOffY += pointDistance * Math.sin(tmpAngle);
actualAngle = getAngle(0, 0, actualOffX, actualOffY);
pointDerivation.push([actualOffX, actualOffY]);
distanceToTarget = Math.euclidDistance2D(actualOffX, actualOffY, ...pointDerivation[0]);
let tmp = new Vector2D(radius + indent, 0).rotate(-actualAngle - pointDistance / radius);
let tmpAngle = getAngle(actualOff.x, actualOff.y, tmp.x, tmp.y);
actualOff.add(new Vector2D(pointDistance, 0).rotate(-tmpAngle));
actualAngle = getAngle(0, 0, actualOff.x, actualOff.y);
pointDerivation.push(actualOff.clone());
distanceToTarget = pointDerivation[0].distanceTo(actualOff);
let numPoints = pointDerivation.length;
if (numPoints > 3 && distanceToTarget < pointDistance) // Could be done better...
{
targetReached = true;
overlap = pointDistance - Math.euclidDistance2D(...pointDerivation[numPoints - 1], ...pointDerivation[0]);
overlap = pointDistance - pointDerivation[numPoints - 1].distanceTo(pointDerivation[0]);
if (overlap < minOverlap)
{
minOverlap = overlap;
bestPointDerivation = pointDerivation;
}
break;
}
}
++tries;
@@ -909,34 +865,24 @@ function placeGenericFortress(centerX, centerY, radius = 20, playerId = 0, style
// Place wall
for (let pointIndex = 0; pointIndex < bestPointDerivation.length; ++pointIndex)
{
let startX = centerX + bestPointDerivation[pointIndex][0];
let startY = centerY + bestPointDerivation[pointIndex][1];
let targetX = centerX + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][0];
let targetY = centerY + bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length][1];
let angle = getAngle(startX, startY, targetX, targetY);
let start = Vector2D.add(center, bestPointDerivation[pointIndex]);
let target = Vector2D.add(center, bestPointDerivation[(pointIndex + 1) % bestPointDerivation.length]);
let angle = getAngle(start.x, start.y, target.x, target.y);
let element = (pointIndex + 1) % gateOccurence == 0 ? "gate" : "long";
element = getWallElement(element, style);
if (element.templateName)
{
let dist = Math.euclidDistance2D(startX, startY, targetX, targetY) / 2;
placeObject(
startX + dist * Math.cos(angle), // placeX
startY + dist * Math.sin(angle), // placeY
element.templateName, playerId, angle - Math.PI / 2 + element.angle
);
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);
}
// Place tower
startX = centerX + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][0];
startY = centerY + bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length][1];
angle = getAngle(startX, startY, targetX, targetY);
start = Vector2D.add(center, bestPointDerivation[(pointIndex + bestPointDerivation.length - 1) % bestPointDerivation.length]);
angle = getAngle(start.x, start.y, target.x, target.y);
let tower = getWallElement("tower", style);
placeObject(
centerX + bestPointDerivation[pointIndex][0],
centerY + bestPointDerivation[pointIndex][1],
tower.templateName, playerId, angle - Math.PI / 2 + tower.angle
);
placeObject(Vector2D.add(center, bestPointDerivation[pointIndex]), tower.templateName, playerId, angle - Math.PI / 2 + tower.angle);
}
}
@@ -94,11 +94,11 @@ for (let i = 0; i < numPlayers; ++i)
log("Placing treasure seeker woman...");
let femaleLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 3.5, 3).round();
addToClass(femaleLocation.x, femaleLocation.y, clWomen);
placeObject(femaleLocation.x, femaleLocation.y, oTreasureSeeker, playerIDs[i], playerAngle[i] + Math.PI);
placeObject(femaleLocation, oTreasureSeeker, playerIDs[i], playerAngle[i] + Math.PI);
log("Placing attacker spawn point....");
placeObject(attacker[i].x, attacker[i].y, aWaypointFlag, 0, Math.PI / 2);
placeObject(attacker[i].x, attacker[i].y, triggerPointAttacker, playerIDs[i], Math.PI / 2);
placeObject(attacker[i], aWaypointFlag, 0, Math.PI / 2);
placeObject(attacker[i], triggerPointAttacker, playerIDs[i], Math.PI / 2);
log("Preventing mountains in the area between player and attackers...");
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
@@ -155,7 +155,7 @@ paintRiver({
if (plantID % plantFrequency == 0)
{
plantID = 0;
placeObject(position.x, position.y, aPlants, 0, randomAngle());
placeObject(position, aPlants, 0, randomAngle());
}
++plantID;
},
@@ -50,8 +50,8 @@ var mapSize = g_Map.getSize();
const distToMapBorder = 5;
const distToOtherWalls = 10;
var buildableMapSize = mapSize - 2 * distToMapBorder;
var actualX = distToMapBorder;
var actualY = distToMapBorder;
var position = new Vector2D(distToMapBorder, distToMapBorder);
var playerID = 0;
const wallStyleList = Object.keys(g_WallStyles);
@@ -69,18 +69,14 @@ const wallStyleList = Object.keys(g_WallStyles);
*/
for (let styleIndex in wallStyleList)
{
let x = actualX + styleIndex * buildableMapSize / wallStyleList.length;
let y = actualY;
let pos = Vector2D.add(position, new Vector2D(styleIndex * buildableMapSize / wallStyleList.length));
let wall = ['start', 'long', 'tower', 'tower', 'tower', 'medium', 'outpost', 'medium', 'cornerOut', 'medium', 'cornerIn', 'medium', 'house', 'end', 'entryTower', 'start', 'short', 'barracks', 'gate', 'tower', 'medium', 'fort', 'medium', 'end'];
let style = wallStyleList[styleIndex];
let orientation = Math.PI / 16 * Math.sin(styleIndex * Math.PI / 4);
placeWall(x, y, wall, style, playerID, orientation);
placeWall(pos, wall, style, playerID, orientation);
}
// Prep for next set of walls
actualX = distToMapBorder;
actualY += 80 + distToOtherWalls;
position.y += 80 + distToOtherWalls;
/**
* Default fortress placement (chosen by fortress type string)
@@ -100,19 +96,21 @@ var fortressRadius = 15; // The space the fortresses take in average. Just for d
for (let styleIndex in wallStyleList)
{
let x = actualX + fortressRadius + styleIndex * buildableMapSize / wallStyleList.length;
let y = actualY + fortressRadius;
let type = "tiny";
let style = wallStyleList[styleIndex];
let orientation = styleIndex * Math.PI / 32;
placeObject(x, y, "other/obelisk", playerID, orientation);
placeFortress(x, y, type, style, playerID, orientation);
let pos = Vector2D.sum([
position,
new Vector2D(1, 1).mult(fortressRadius),
new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)
]);
placeObject(pos, "other/obelisk", playerID, orientation);
placeFortress(pos, type, style, playerID, orientation);
}
// Prep for next set of walls
actualX = distToMapBorder;
actualY += 2 * fortressRadius + distToOtherWalls;
position.y += 2 * fortressRadius + distToOtherWalls;
/**
* 'Generic' fortress placement (iberian wall circuit code)
@@ -125,20 +123,22 @@ actualY += 2 * fortressRadius + distToOtherWalls;
*
* We also supply a radius value to dictate how wide the circuit of walls should be.
*/
var radius = Math.min((mapSize - actualY - distToOtherWalls) / 3, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
var radius = Math.min((mapSize - position.y - distToOtherWalls) / 3, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
for (let styleIndex in wallStyleList)
{
let centerX = actualX + radius + styleIndex * buildableMapSize / wallStyleList.length;
let centerY = actualY + radius;
let style = wallStyleList[styleIndex];
placeObject(centerX, centerY, 'other/obelisk', playerID, 0);
placeGenericFortress(centerX, centerY, radius, playerID, style);
let pos = Vector2D.sum([
position,
new Vector2D(radius, radius),
new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)
]);
placeObject(pos, "other/obelisk", playerID, 0);
placeGenericFortress(pos, radius, playerID, style);
}
// Prep for next set of walls
actualX = distToMapBorder;
actualY += 2 * radius + distToOtherWalls;
position.y += 2 * radius + distToOtherWalls;
/**
* Circular wall placement
@@ -162,11 +162,10 @@ actualY += 2 * radius + distToOtherWalls;
* arc faces. If the wall is to be a complete circle, then this is used as
* the orientation of the first wall piece.
*/
radius = Math.min((mapSize - actualY - distToOtherWalls) / 3, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
radius = Math.min((mapSize - position.y - distToOtherWalls) / 3, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
for (let styleIndex in wallStyleList)
{
let centerX = actualX + radius + styleIndex * buildableMapSize / wallStyleList.length;
let centerY = actualY + radius;
let center = Vector2D.sum([position, new Vector2D(1, 1).mult(radius), new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)]);
let wallPart = ['tower', 'medium', 'house'];
let style = wallStyleList[styleIndex];
let orientation = styleIndex * Math.PI / 16;
@@ -176,13 +175,11 @@ 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(centerX, centerY, 'other/obelisk', playerID, orientation);
placeCircularWall(centerX, centerY, radius, wallPart, style, playerID, orientation, maxAngle);
placeObject(center, "other/obelisk", playerID, orientation);
placeCircularWall(center, radius, wallPart, style, playerID, orientation, maxAngle);
}
// Prep for next set of walls.
actualX = distToMapBorder;
actualY += 2 * radius + distToOtherWalls;
position.y += 2 * radius + distToOtherWalls;
/**
* Regular Polygonal wall placement
@@ -208,11 +205,11 @@ actualY += 2 * radius + distToOtherWalls;
* outward side facing or, if the `skipFirstWall` argument is true, the
* opening in the wall.
*/
radius = Math.min((mapSize - actualY - distToOtherWalls) / 2, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
radius = Math.min((mapSize - position.y - distToOtherWalls) / 2, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2);
for (let styleIndex in wallStyleList)
{
let centerX = actualX + radius + styleIndex * buildableMapSize / wallStyleList.length;
let centerY = actualY + radius;
let centerPosition = Vector2D.sum([position, new Vector2D(1, 1).mult(radius), new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)]);
let wallParts = ['medium', 'tower']; // Function default: ['long', 'tower']
// Which wall element to use for the corners of the polygon
@@ -227,13 +224,11 @@ for (let styleIndex in wallStyleList)
// If true, the first side will not be drawn, leaving the wall open.
let skipFirstWall = true;
placeObject(centerX, centerY, 'other/obelisk', playerID, orientation);
placePolygonalWall(centerX, centerY, radius, wallParts, cornerWallElement, style, playerID, orientation, numCorners, skipFirstWall);
placeObject(centerPosition, "other/obelisk", playerID, orientation);
placePolygonalWall(centerPosition, radius, wallParts, cornerWallElement, style, playerID, orientation, numCorners, skipFirstWall);
}
// Prep for next set of walls.
actualX = distToMapBorder;
actualY += 2 * radius + distToOtherWalls;
position.y += 2 * radius + distToOtherWalls;
/**
* Irregular Polygonal wall placement
@@ -263,11 +258,14 @@ actualY += 2 * radius + distToOtherWalls;
* the wall. It is not defined in this example (so as to use the defaults)
* as it is not easy to comprehend.
*/
radius = Math.min((mapSize - actualY - distToOtherWalls) / 2, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2); // The radius of wall polygons
radius = Math.min((mapSize - position.y - distToOtherWalls) / 2, (buildableMapSize / wallStyleList.length - distToOtherWalls) / 2); // The radius of wall polygons
for (let styleIndex in wallStyleList)
{
let centerX = actualX + radius + styleIndex * buildableMapSize / wallStyleList.length;
let centerY = actualY + radius;
let centerPosition = Vector2D.sum([
position,
new Vector2D(1, 1).mult(radius),
new Vector2D(styleIndex * buildableMapSize / wallStyleList.length, 0)
]);
// Which wall element type will be used for the corners of the polygon.
let cornerWallElement = 'tower';
@@ -284,13 +282,11 @@ for (let styleIndex in wallStyleList)
// If true, the first side will not be drawn, leaving the wall open.
let skipFirstWall = true;
placeObject(centerX, centerY, 'other/obelisk', playerID, orientation);
placeIrregularPolygonalWall(centerX, centerY, radius, cornerWallElement, style, playerID, orientation, numCorners, irregularity, skipFirstWall);
placeObject(centerPosition, "other/obelisk", playerID, orientation);
placeIrregularPolygonalWall(centerPosition, radius, cornerWallElement, style, playerID, orientation, numCorners, irregularity, skipFirstWall);
}
// Prep for next set of walls.
actualX = distToMapBorder;
actualY += 2 * radius + distToOtherWalls;
position.y += 2 * radius + distToOtherWalls;
/**
* Linear wall placement
@@ -306,25 +302,20 @@ actualY += 2 * radius + distToOtherWalls;
* of the walls is facing the top of the screen.
*/
// Two vars, just for this map; firstly how long the longest wall will be.
var maxWallLength = (mapSize - actualY - distToMapBorder - distToOtherWalls);
var maxWallLength = (mapSize - position.y - distToMapBorder - distToOtherWalls);
// And secondly, how many walls of the same style will be placed.
var numWallsPerStyle = Math.floor(buildableMapSize / distToOtherWalls / wallStyleList.length);
for (let styleIndex in wallStyleList)
for (let wallIndex = 0; wallIndex < numWallsPerStyle; ++wallIndex)
{
// Start point.
let startX = actualX + (styleIndex * numWallsPerStyle + wallIndex) * buildableMapSize / wallStyleList.length / numWallsPerStyle;
let startY = actualY;
let offsetX = (styleIndex * numWallsPerStyle + wallIndex) * buildableMapSize / wallStyleList.length / numWallsPerStyle;
let start = Vector2D.add(position, new Vector2D(offsetX, 0));
// End point.
let endX = startX;
let endY = actualY + (wallIndex + 1) * maxWallLength / numWallsPerStyle;
let offsetY = (wallIndex + 1) * maxWallLength / numWallsPerStyle;
let end = Vector2D.add(position, new Vector2D(offsetX, offsetY));
let wallPart = ['tower', 'medium'];
let style = wallStyleList[styleIndex];
placeLinearWall(startX, startY, endX, endY, wallPart, style, playerID);
placeLinearWall(start, end, ['tower', 'medium'], wallStyleList[styleIndex], playerID);
}
g_Map.ExportMap();
@@ -203,8 +203,7 @@ var mercenaryCampGuards = {
* Resource spots and other points of interest
*/
// Mines
function placeMine(point, centerEntity,
function placeMine(position, centerEntity,
decorativeActors = [
g_Decoratives.grass, g_Decoratives.grassShort,
g_Decoratives.rockLarge, g_Decoratives.rockMedium,
@@ -212,15 +211,16 @@ function placeMine(point, centerEntity,
]
)
{
placeObject(point.x, point.y, centerEntity, 0, randomAngle());
placeObject(position, centerEntity, 0, randomAngle());
let quantity = randIntInclusive(11, 23);
let dAngle = 2 * Math.PI / quantity;
for (let i = 0; i < quantity; ++i)
{
let angle = dAngle * randFloat(i, i + 1);
let dist = randFloat(2, 5);
placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(decorativeActors), 0, randomAngle());
}
placeObject(
Vector2D.add(position, new Vector2D(randFloat(2, 5), 0).rotate(-dAngle * randFloat(i, i + 1))),
pickRandom(decorativeActors),
0,
randomAngle());
}
// Groves, only Wood
@@ -238,7 +238,9 @@ function placeGrove(point,
groveTerrainTexture = getArray(g_Terrains.forestFloor1)
)
{
placeObject(point.x, point.y, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
let position = new Vector2D(point.x, point.y);
placeObject(position, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randomAngle());
let quantity = randIntInclusive(20, 30);
let dAngle = 2 * Math.PI / quantity;
for (let i = 0; i < quantity; ++i)
@@ -249,15 +251,15 @@ function placeGrove(point,
if (i % 3 == 0)
objectList = groveActors;
let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle));
placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle());
let pos = Vector2D.add(position, new Vector2D(dist, 0).rotate(-angle));
placeObject(pos, pickRandom(objectList), 0, randomAngle());
let painters = [new TerrainPainter(groveTerrainTexture)];
if (groveTileClass)
painters.push(new TileClassPainter(groveTileClass));
createArea(
new ClumpPlacer(5, 1, 1, 1, position),
new ClumpPlacer(5, 1, 1, 1, pos),
painters);
}
}
@@ -320,7 +322,7 @@ for (let i = 0; i < num; ++i)
fences.push(new Fortress("fence", clone(fences[i].wall).reverse()));
// Camps with fire and gold treasure
function placeCamp(point,
function placeCamp(position,
centerEntity = "actor|props/special/eyecandy/campfire.xml",
otherEntities = ["gaia/special_treasure_metal", "gaia/special_treasure_standing_stone",
"units/brit_infantry_slinger_b", "units/brit_infantry_javelinist_b", "units/gaul_infantry_slinger_b", "units/gaul_infantry_javelinist_b", "units/gaul_champion_fanatic",
@@ -328,14 +330,15 @@ function placeCamp(point,
]
)
{
placeObject(point.x, point.y, centerEntity, 0, randomAngle());
placeObject(position, centerEntity, 0, 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(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(otherEntities), 0, randomAngle());
placeObject(Vector2D.add(position, new Vector2D(dist, 0).rotate(-angle)), pickRandom(otherEntities), 0, randomAngle());
}
}
@@ -362,7 +365,7 @@ function placeStartLocationResources(
// Stone
let dAngle = 4/9 * Math.PI;
let angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4);
placeMine({ "x": point.x + averageDistToCC * Math.cos(angle), "y": point.y + averageDistToCC * Math.sin(angle) }, g_Gaia.stoneLarge);
placeMine(Vector2D.add(point, new Vector2D(averageDistToCC, 0).rotate(-angle)), g_Gaia.stoneLarge);
currentAngle += dAngle;
@@ -378,7 +381,7 @@ function placeStartLocationResources(
objectList = groveActors;
let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle));
placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle());
placeObject(position, pickRandom(objectList), 0, randomAngle());
createArea(
new ClumpPlacer(5, 1, 1, 1, position),
[
@@ -392,7 +395,7 @@ function placeStartLocationResources(
// Metal
dAngle = 4/9 * Math.PI;
angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4);
placeMine({ "x": point.x + averageDistToCC * Math.cos(angle), "y": point.y + averageDistToCC * Math.sin(angle) }, g_Gaia.metalLarge);
placeMine(Vector2D.add(point, new Vector2D(averageDistToCC, 0).rotate(-angle)), g_Gaia.metalLarge);
currentAngle += dAngle;
// Berries and domestic animals
@@ -402,7 +405,7 @@ function placeStartLocationResources(
{
angle = currentAngle + randFloat(0, dAngle);
let dist = getRandDist();
placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(foodEntities), 0, randomAngle());
placeObject(Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)), pickRandom(foodEntities), 0, randomAngle());
currentAngle += dAngle;
}
}
@@ -580,7 +583,7 @@ for (let h = 0; h < heighLimits.length; ++h)
g_Map.setTexture(point, texture);
if (actor)
placeObject(point.x + randFloat(0, 1), point.y + randFloat(0, 1), actor, 0, randomAngle());
placeObject(Vector2D.add(point, new Vector2D(randFloat(0, 1), randFloat(0, 1))), actor, 0, randomAngle());
}
Engine.SetProgress(80);
@@ -588,7 +591,8 @@ log("Placing resources...");
let avoidPoints = clone(startLocations);
for (let i = 0; i < avoidPoints.length; ++i)
avoidPoints[i].dist = 30;
let resourceSpots = getPointsByHeight(resourceSpotHeightRange, avoidPoints, clPath);
let resourceSpots = getPointsByHeight(resourceSpotHeightRange, avoidPoints, clPath).map(point => new Vector2D(point.x, point.y));
Engine.SetProgress(55);
log("Placing players...");
@@ -597,7 +601,7 @@ if (isNomad())
else
for (let p = 0; p < playerIDs.length; ++p)
{
let point = startLocations[p];
let point = new Vector2D(startLocations[p].x, startLocations[p].y);
placeCivDefaultStartingEntities(point, playerIDs[p], g_Map.size > 192);
placeStartLocationResources(point);
}
@@ -628,7 +632,7 @@ for (let i = 0; i < resourceSpots.length; ++i)
}
else
{
placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randomAngle());
placeCustomFortress(resourceSpots[i], pickRandom(fences), "other", 0, randomAngle());
rectangularSmoothToHeight(resourceSpots[i], 10, 10, g_Map.getHeight(resourceSpots[i]), 0.5);
}
}