Index: ps/trunk/binaries/data/mods/public/maps/random/alpine_valley.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/alpine_valley.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/alpine_valley.js (revision 20971) @@ -1,542 +1,542 @@ Engine.LoadLibrary("rmgen"); TILE_CENTERED_HEIGHT_MAP = true; /** * This class creates random mountainranges without enclosing any area completely. * * To determine their location, a graph is created where each vertex is a possible starting or * ending location of a mountainrange and each edge a possible mountainrange. * * That graph starts nearly complete (i.e almost every vertex is connected to most other vertices). * After a random edge was chosen and placed as a mountainrange, * all edges that intersect, that leave a too small gap to another mountainrange or that are connected to * too many other mountainranges are removed from the graph. * This is repeated until all edges were removed. */ function MountainRangeBuilder(args) { /** * These parameters paint the mountainranges after their location was determined. */ this.pathplacer = args.pathplacer; this.painters = args.painters; this.constraint = args.constraint; this.mountainWidth = args.mountainWidth; /** * Minimum geometric distance between two mountains that don't end in one place (disjoint edges). */ this.minDistance = args.mountainWidth + args.passageWidth; /** * Array of Vector2D locations where a mountainrange can start or end. */ this.vertices = args.points; /** * Number of mountainranges starting or ending at the given point. */ this.vertexDegree = this.vertices.map(p => 0); /** * Highest number of mountainranges that can meet in one point (maximum degree of each vertex). */ this.maxDegree = args.maxDegree; /** * Each possible edge is an array containing two vertex indices. * The algorithm adds possible edges consecutively and removes subsequently invalid edges. */ this.possibleEdges = []; this.InitPossibleEdges(); /** * A two-dimensional array of booleans that are true if the two corresponding vertices may be connected by a new edge (mountainrange). * It is initialized with some points that should never be connected and updated with every placed edge. * The purpose is to rule out any cycles in the graph, i.e. prevent any territory enclosed by mountainranges. */ this.verticesConnectable = []; this.InitConnectable(); /** * Currently iterated item of possibleEdges that is either used as a mountainrange or removed from the possibleEdges. */ this.index = undefined; /** * These variables hold the indices of the two points of that edge and the location of them as a Vector2D. */ this.currentEdge = undefined; this.currentEdgeStart = undefined; this.currentEdgeEnd = undefined; } MountainRangeBuilder.prototype.InitPossibleEdges = function() { for (let i = 0; i < this.vertices.length; ++i) for (let j = numPlayers; j < this.vertices.length; ++j) if (j > i) this.possibleEdges.push([i, j]); }; MountainRangeBuilder.prototype.InitConnectable = function() { for (let i = 0; i < this.vertices.length; ++i) { this.verticesConnectable[i] = []; for (let j = 0; j < this.vertices.length; ++j) this.verticesConnectable[i][j] = i >= numPlayers || j >= numPlayers || i == j || i != j - 1 && i != j + 1; } }; MountainRangeBuilder.prototype.SetConnectable = function(isConnectable) { this.verticesConnectable[this.currentEdge[0]][this.currentEdge[1]] = isConnectable; this.verticesConnectable[this.currentEdge[1]][this.currentEdge[0]] = isConnectable; }; MountainRangeBuilder.prototype.UpdateCurrentEdge = function() { this.currentEdge = this.possibleEdges[this.index]; this.currentEdgeStart = this.vertices[this.currentEdge[0]]; this.currentEdgeEnd = this.vertices[this.currentEdge[1]]; }; /** * Remove all edges that are too close to the current mountainrange or intersect. */ MountainRangeBuilder.prototype.RemoveInvalidEdges = function() { for (let i = 0; i < this.possibleEdges.length; ++i) { this.UpdateCurrentEdge(); let comparedEdge = this.possibleEdges[i]; let comparedEdgeStart = this.vertices[comparedEdge[0]]; let comparedEdgeEnd = this.vertices[comparedEdge[1]]; let edge0Equal = this.currentEdgeStart == comparedEdgeStart; let edge1Equal = this.currentEdgeStart == comparedEdgeEnd; let edge2Equal = this.currentEdgeEnd == comparedEdgeEnd; let edge3Equal = this.currentEdgeEnd == comparedEdgeStart; if (!edge0Equal && !edge2Equal && !edge1Equal && !edge3Equal && testLineIntersection(this.currentEdgeStart, this.currentEdgeEnd, comparedEdgeStart, comparedEdgeEnd, this.minDistance) || ( edge0Equal && !edge2Equal || !edge1Equal && edge3Equal) && distanceOfPointFromLine(this.currentEdgeStart, this.currentEdgeEnd, comparedEdgeEnd) < this.minDistance || (!edge0Equal && edge2Equal || edge1Equal && !edge3Equal) && distanceOfPointFromLine(this.currentEdgeStart, this.currentEdgeEnd, comparedEdgeStart) < this.minDistance) { this.possibleEdges.splice(i, 1); --i; if (this.index > i) --this.index; } } }; /** * Tests using depth-first-search if the graph according to pointsConnectable contains a cycle, * i.e. if adding the currentEdge would result in an area enclosed by mountainranges. */ MountainRangeBuilder.prototype.HasCycles = function() { let tree = []; let backtree = []; let pointQueue = [this.currentEdge[0]]; while (pointQueue.length) { let selectedPoint = pointQueue.shift(); if (tree.indexOf(selectedPoint) == -1) { tree.push(selectedPoint); backtree.push(-1); } for (let i = 0; i < this.vertices.length; ++i) { if (this.verticesConnectable[selectedPoint][i] || i == backtree[tree.lastIndexOf(selectedPoint)]) continue; // If the current point was encountered already, then a cycle was identified. if (tree.indexOf(i) != -1) return true; // Otherwise visit this point next pointQueue.unshift(i); tree.push(i); backtree.push(selectedPoint); } } return false; }; MountainRangeBuilder.prototype.PaintCurrentEdge = function() { this.pathplacer.start = this.currentEdgeStart; this.pathplacer.end = this.currentEdgeEnd; this.pathplacer.width = this.mountainWidth; log("Creating mountainrange..."); if (!createArea(this.pathplacer, this.painters, this.constraint)) return false; log("Creating circular mountains at both ends of that mountainrange..."); for (let point of [this.currentEdgeStart, this.currentEdgeEnd]) createArea( - new ClumpPlacer(Math.floor(diskArea(this.mountainWidth / 2)), 0.95, 0.6, 10, point.x, point.y), + new ClumpPlacer(diskArea(this.mountainWidth / 2), 0.95, 0.6, 10, point), this.painters, this.constraint); return true; }; /** * This is the only function meant to be publicly accessible. */ MountainRangeBuilder.prototype.CreateMountainRanges = function() { while (this.possibleEdges.length) { this.index = randIntExclusive(0, this.possibleEdges.length); this.UpdateCurrentEdge(); this.SetConnectable(false); if (this.vertexDegree[this.currentEdge[0]] < this.maxDegree && this.vertexDegree[this.currentEdge[1]] < this.maxDegree && !this.HasCycles() && this.PaintCurrentEdge()) { ++this.vertexDegree[this.currentEdge[0]]; ++this.vertexDegree[this.currentEdge[1]]; this.RemoveInvalidEdges(); } else this.SetConnectable(true); this.possibleEdges.splice(this.index, 1); } }; if (randBool()) { log("Late spring biome..."); var tPrimary = ["alpine_dirt_grass_50"]; var tForestFloor = "alpine_forrestfloor"; var tCliff = ["alpine_cliff_a", "alpine_cliff_b", "alpine_cliff_c"]; var tSecondary = "alpine_grass_rocky"; var tHalfSnow = ["alpine_grass_snow_50", "alpine_dirt_snow"]; var tSnowLimited = ["alpine_snow_rocky"]; var tDirt = "alpine_dirt"; var tRoad = "new_alpine_citytile"; var tRoadWild = "new_alpine_citytile"; var oPine = "gaia/flora_tree_pine"; var oBerryBush = "gaia/flora_bush_berry"; var oDeer = "gaia/fauna_deer"; var oRabbit = "gaia/fauna_rabbit"; var oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; var oStoneSmall = "gaia/geology_stone_alpine_a"; var oMetalLarge = "gaia/geology_metal_alpine_slabs"; var aGrass = "actor|props/flora/grass_soft_small_tall.xml"; var aGrassShort = "actor|props/flora/grass_soft_large.xml"; var aRockLarge = "actor|geology/stone_granite_med.xml"; var aRockMedium = "actor|geology/stone_granite_med.xml"; var aBushMedium = "actor|props/flora/bush_medit_me.xml"; var aBushSmall = "actor|props/flora/bush_medit_sm.xml"; } else { log("Winter biome..."); var tPrimary = ["alpine_snow_a", "alpine_snow_b"]; var tForestFloor = "alpine_forrestfloor_snow"; var tCliff = ["alpine_cliff_snow"]; var tSecondary = "alpine_grass_snow_50"; var tHalfSnow = ["alpine_grass_snow_50", "alpine_dirt_snow"]; var tSnowLimited = ["alpine_snow_a", "alpine_snow_b"]; var tDirt = "alpine_dirt"; var tRoad = "new_alpine_citytile"; var tRoadWild = "new_alpine_citytile"; var oPine = "gaia/flora_tree_pine_w"; var oBerryBush = "gaia/flora_bush_berry"; var oDeer = "gaia/fauna_deer"; var oRabbit = "gaia/fauna_rabbit"; var oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; var oStoneSmall = "gaia/geology_stone_alpine_a"; var oMetalLarge = "gaia/geology_metal_alpine_slabs"; var aGrass = "actor|props/flora/grass_soft_dry_small_tall.xml"; var aGrassShort = "actor|props/flora/grass_soft_dry_large.xml"; var aRockLarge = "actor|geology/stone_granite_med.xml"; var aRockMedium = "actor|geology/stone_granite_med.xml"; var aBushMedium = "actor|props/flora/bush_medit_me_dry.xml"; var aBushSmall = "actor|props/flora/bush_medit_sm_dry.xml"; } var heightLand = 3; var heightOffsetBump = 2; var snowlineHeight = 29; var heightMountain = 30; const pForest = [tForestFloor + TERRAIN_SEPARATOR + oPine, tForestFloor]; InitMap(heightLand, tPrimary); const numPlayers = getNumPlayers(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oPine }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(20); new MountainRangeBuilder({ "pathplacer": new PathPlacer(undefined, undefined, undefined, 0.4, scaleByMapSize(3, 12), 0.1, 0.1, 0.1), "painters":[ new LayeredPainter([tCliff, tPrimary], [3]), new SmoothElevationPainter(ELEVATION_SET, heightMountain, 2), paintClass(clHill) ], "constraint": avoidClasses(clPlayer, 20), "passageWidth": scaleByMapSize(10, 15), "mountainWidth": scaleByMapSize(9, 15), "maxDegree": 3, "points": [ // Four points near each player ...distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.49), mapCenter)[0], ...distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers * 1.4, fractionToTiles(0.34), mapCenter)[0], ...distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers * 0.6, fractionToTiles(0.34), mapCenter)[0], ...distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.18), mapCenter)[0], mapCenter ] }).CreateMountainRanges(); Engine.SetProgress(35); paintTerrainBasedOnHeight(heightLand + 0.1, snowlineHeight, 0, tCliff); paintTerrainBasedOnHeight(snowlineHeight, heightMountain, 3, tSnowLimited); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), avoidClasses(clPlayer, 10), scaleByMapSize(100, 200)); Engine.SetProgress(40); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(40, 150), 0.2, 0.1, 1), [ new LayeredPainter([tCliff, tSnowLimited], [2]), new SmoothElevationPainter(ELEVATION_SET, heightMountain, 2), paintClass(clHill) ], avoidClasses(clPlayer, 20, clHill, 14), scaleByMapSize(10, 80) * numPlayers ); Engine.SetProgress(50); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); var types = [ [[tForestFloor, tPrimary, pForest], [tForestFloor, pForest]] ]; var size = forestTrees / (scaleByMapSize(2,8) * numPlayers); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses(clPlayer, 12, clForest, 10, clHill, 0), num); Engine.SetProgress(60); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter([[tDirt, tHalfSnow], [tHalfSnow, tSnowLimited]], [2]), paintClass(clDirt) ], avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45)); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tSecondary), avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45)); Engine.SetProgress(65); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); log("Creating small stone mines..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1), scaleByMapSize(4,16), 100 ); Engine.SetProgress(70); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(8, 131), 50 ); Engine.SetProgress(75); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50 ); log("Creating rabbit..."); group = new SimpleGroup( [new SimpleObject(oRabbit, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); Engine.SetProgress(85); createStragglerTrees( [oPine], avoidClasses(clForest, 1, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); log("Creating small grass tufts..."); var planetm = 1; group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(95); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clHill, 1, clPlayer, 1, clDirt, 1), planetm * scaleByMapSize(13, 200), 50 ); placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet(pickRandom(["cirrus", "cumulus", "sunny"])); setSunRotation(randomAngle()); setSunElevation(Math.PI * randFloat(1/5, 1/3)); setWaterColor(0.0, 0.047, 0.286); // dark majestic blue setWaterTint(0.471, 0.776, 0.863); // light blue setWaterMurkiness(0.72); setWaterWaviness(2.0); setWaterType("lake"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/ardennes_forest.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/ardennes_forest.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/ardennes_forest.js (revision 20971) @@ -1,469 +1,469 @@ Engine.LoadLibrary("rmgen"); const tPrimary = ["temp_forestfloor_pine", "temp_forestfloor_pine", "alpine_cliff_c", "alpine_grass_rocky"]; const tGrass = ["new_alpine_grass_b", "new_alpine_grass_c", "new_alpine_grass_d"]; const tPineForestFloor = "temp_forestfloor_pine"; const tForestFloor = [tPineForestFloor, tPineForestFloor, "alpine_dirt_grass_50"]; const tCliff = ["alpine_cliff_c", "alpine_cliff_c", "alpine_grass_rocky"]; const tCity = ["new_alpine_citytile", "new_alpine_grass_dirt_a"]; const tGrassPatch = ["alpine_grass_a", "alpine_grass_b"]; const oBoar = "gaia/fauna_boar"; const oDeer = "gaia/fauna_deer"; const oBear = "gaia/fauna_bear"; const oPig = "gaia/fauna_pig"; const oBerryBush = "gaia/flora_bush_berry"; const oMetalSmall = "gaia/geology_metal_alpine"; const oMetalLarge = "gaia/geology_metal_temperate_slabs"; const oStoneSmall = "gaia/geology_stone_alpine_a"; const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; const oOak = "gaia/flora_tree_oak"; const oOakLarge = "gaia/flora_tree_oak_large"; const oPine = "gaia/flora_tree_pine"; const oAleppoPine = "gaia/flora_tree_aleppo_pine"; const aTreeA = "actor|flora/trees/oak.xml"; const aTreeB = "actor|flora/trees/oak_large.xml"; const aTreeC = "actor|flora/trees/pine.xml"; const aTreeD = "actor|flora/trees/aleppo_pine.xml"; const aTrees = [aTreeA, aTreeB, aTreeC, aTreeD]; const aGrassLarge = "actor|props/flora/grass_soft_large.xml"; const aWoodLarge = "actor|props/special/eyecandy/wood_pile_1_b.xml"; const aWoodA = "actor|props/special/eyecandy/wood_sm_pile_a.xml"; const aWoodB = "actor|props/special/eyecandy/wood_sm_pile_b.xml"; const aBarrel = "actor|props/special/eyecandy/barrel_a.xml"; const aWheel = "actor|props/special/eyecandy/wheel_laying.xml"; const aCeltHomestead = "actor|structures/celts/homestead.xml"; const aCeltHouse = "actor|structures/celts/house.xml"; const aCeltLongHouse = "actor|structures/celts/longhouse.xml"; var pForest = [ tPineForestFloor+TERRAIN_SEPARATOR+oOak, tForestFloor, tPineForestFloor+TERRAIN_SEPARATOR+oPine, tForestFloor, tPineForestFloor+TERRAIN_SEPARATOR+oAleppoPine, tForestFloor, tForestFloor ]; const heightRavineValley = 2; const heightLand = 30; const heightRavineHill = 40; const heightHill = 50; const heightOffsetRavine = 10; InitMap(heightHill, tPrimary); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clForestJoin = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clHillDeco = createTileClass(); log("Creating the central dip..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.42)), 0.94, 0.05, 0.1, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.42)), 0.94, 0.05, 0.1, mapCenter), [ new LayeredPainter([tCliff, tGrass], [3]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 3) ]); Engine.SetProgress(5); // Find all hills var noise0 = new Noise2D(20); for (var ix = 0; ix < mapSize; ix++) for (var iz = 0; iz < mapSize; iz++) { let position = new Vector2D(ix, iz); var h = getHeight(ix,iz); if (h > heightRavineHill) { addToClass(ix,iz,clHill); // Add hill noise var x = ix / (mapSize + 1.0); var z = iz / (mapSize + 1.0); var n = (noise0.get(x, z) - 0.5) * heightRavineHill; g_Map.setHeight(position, h + n); } } var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.3)); function distanceToPlayers(x, z) { var r = 10000; for (var i = 0; i < numPlayers; i++) { var dx = x - playerPosition[i].x; var dz = z - playerPosition[i].y; r = Math.min(r, Math.square(dx) + Math.square(dz)); } return Math.sqrt(r); } function playerNearness(x, z) { var d = fractionToTiles(distanceToPlayers(x,z)); if (d < 13) return 0; if (d < 19) return (d-13)/(19-13); return 1; } Engine.SetProgress(10); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "BaseResourceClass": clBaseResource, // Playerclass marked below "CityPatch": { "outerTerrain": tCity, "innerTerrain": tCity, "radius": scaleByMapSize(5, 6), "smoothness": 0.05 }, "Chicken": { "template": oPig }, "Berries": { "template": oBerryBush, "minCount": 3, "maxCount": 3 }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ], "distance": 16 }, "Trees": { "template": oOak, "count": 2 } // No decoratives }); log("Marking player territory larger than the city patch..."); for (let i = 0; i < numPlayers; ++i) createArea( - new ClumpPlacer(250, 0.95, 0.3, 0.1, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(250, 0.95, 0.3, 0.1, playerPosition[i]), paintClass(clPlayer)); Engine.SetProgress(30); log("Creating hills..."); for (let size of [scaleByMapSize(50, 800), scaleByMapSize(50, 400), scaleByMapSize(10, 30), scaleByMapSize(10, 30)]) { let mountains = createAreas( new ClumpPlacer(size, 0.1, 0.2, 0.1), [ new LayeredPainter([tCliff, [tForestFloor, tForestFloor, tCliff]], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, size < 50 ? 2 : 4), paintClass(clHill) ], avoidClasses(clPlayer, 8, clBaseResource, 2, clHill, 5), scaleByMapSize(1, 4)); if (size > 100 && mountains.length) createAreasInAreas( new ClumpPlacer(size * 0.3, 0.94, 0.05, 0.1), [ new LayeredPainter([tCliff, tForestFloor], [2]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetRavine, 3) ], stayClasses(clHill, 4), mountains.length * 2, 20, mountains); let ravine = createAreas( new ClumpPlacer(size, 0.1, 0.2, 0.1), [ new LayeredPainter([tCliff, tForestFloor], [2]), new SmoothElevationPainter(ELEVATION_SET, heightRavineValley, 2), paintClass(clHill) ], avoidClasses(clPlayer, 6, clBaseResource, 2, clHill, 5), scaleByMapSize(1, 3)); if (size > 150 && ravine.length) { log("Placing huts in ravines..."); createObjectGroupsByAreasDeprecated( new RandomGroup( [ new SimpleObject(aCeltHouse, 0, 1, 4, 5), new SimpleObject(aCeltLongHouse, 1, 1, 4, 5) ], true, clHillDeco), 0, [avoidClasses(clHillDeco, 3), stayClasses(clHill, 3)], ravine.length * 5, 20, ravine); createObjectGroupsByAreasDeprecated( new RandomGroup([new SimpleObject(aCeltHomestead, 1, 1, 1, 1)], true, clHillDeco), 0, [avoidClasses(clHillDeco, 5), stayClasses(clHill, 4)], ravine.length * 2, 100, ravine); // Place noise createAreasInAreas( new ClumpPlacer(size * 0.3, 0.94, 0.05, 0.1), [ new LayeredPainter([tCliff, tForestFloor], [2]), new SmoothElevationPainter(ELEVATION_SET, heightRavineValley, 2) ], [avoidClasses(clHillDeco, 2), stayClasses(clHill, 0)], ravine.length * 2, 20, ravine); createAreasInAreas( new ClumpPlacer(size * 0.1, 0.3, 0.05, 0.1), [ new LayeredPainter([tCliff, tForestFloor], [2]), new SmoothElevationPainter(ELEVATION_SET, heightRavineHill, 2), paintClass(clHill) ], [avoidClasses(clHillDeco, 2), borderClasses(clHill, 15, 1)], ravine.length * 2, 50, ravine); } } Engine.SetProgress(50); var explorableArea = {}; explorableArea.points = []; var playerClass = getTileClass(clPlayer); var hillDecoClass = getTileClass(clHillDeco); for (var ix = 0; ix < mapSize; ix++) { for (var iz = 0; iz < mapSize; iz++) { var h = getHeight(ix,iz); if(h > 15 && h < 45 && playerClass.countMembersInRadius(ix, iz, 1) == 0) { // explorable area var pt = {}; pt.x = ix; pt.z = iz; explorableArea.points.push(pt); } 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()); } } Engine.SetProgress(55); // Add some general noise - after placing height dependant trees for (var ix = 0; ix < mapSize; ix++) { var x = ix / (mapSize + 1.0); for (var iz = 0; iz < mapSize; iz++) { let position = new Vector2D(ix, iz); var z = iz / (mapSize + 1.0); var h = getHeight(ix,iz); var pn = playerNearness(x,z); var n = (noise0.get(x,z) - 0.5) * 10; g_Map.setHeight(position, h + (n * pn)); } } Engine.SetProgress(60); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(400, 6000, 0.8); var [forestTreesJoin, forestTrees] = getTreeCounts(forestTrees, forestTrees, 0.25); var num = forestTrees / (scaleByMapSize(6, 16) * numPlayers); createAreasInAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new TerrainPainter(pForest), paintClass(clForest) ], avoidClasses(clPlayer, 5, clBaseResource, 4, clForest, 6, clHill, 4), num, 100, [explorableArea] ); var num = forestTreesJoin / (scaleByMapSize(4,6) * numPlayers); createAreasInAreas( new ClumpPlacer(forestTreesJoin / num, 0.1, 0.1, 1), [ new TerrainPainter(pForest), paintClass(clForest), paintClass(clForestJoin) ], [avoidClasses(clPlayer, 5, clBaseResource, 4, clForestJoin, 5, clHill, 4), borderClasses(clForest, 1, 4)], num, 100, [explorableArea] ); Engine.SetProgress(70); log("Creating grass patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new LayeredPainter([[tGrass, tGrassPatch], [tGrassPatch, tGrass], [tGrass, tGrassPatch]], [1, 1]), avoidClasses(clForest, 0, clHill, 2, clPlayer, 5), scaleByMapSize(15, 45)); log("Creating chopped forest patches..."); for (let size of [scaleByMapSize(20, 120)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tForestFloor), avoidClasses(clForest, 1, clHill, 2, clPlayer, 5), scaleByMapSize(4, 12)); Engine.SetProgress(75); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 1,2, 0,4), new SimpleObject(oStoneLarge, 0,1, 0,4)], true, clRock); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clHill, 4, clForest, 2, clPlayer, 20, clRock, 10)], scaleByMapSize(6,20), 100, [explorableArea] ); log("Creating small stone mines..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clHill, 4, clForest, 2, clPlayer, 20, clRock, 10)], scaleByMapSize(6,20), 100, [explorableArea] ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalSmall, 1,2, 0,4), new SimpleObject(oMetalLarge, 0,1, 0,4)], true, clMetal); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clHill, 4, clForest, 2, clPlayer, 20, clMetal, 10, clRock, 5)], scaleByMapSize(6,20), 100, [explorableArea] ); Engine.SetProgress(80); log("Creating wildlife..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clHill, 4, clForest, 0, clPlayer, 0, clBaseResource, 20), 3 * numPlayers, 100, [explorableArea] ); group = new SimpleGroup( [new SimpleObject(oBoar, 2,3, 0,5)], true, clFood ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clHill, 4, clForest, 0, clPlayer, 0, clBaseResource, 15), numPlayers, 50, [explorableArea] ); group = new SimpleGroup( [new SimpleObject(oBear, 1,1, 0,4)], false, clFood ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clHill, 4, clForest, 0, clPlayer, 20), scaleByMapSize(3, 12), 200, [explorableArea] ); Engine.SetProgress(85); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clHill, 4, clFood, 20), randIntInclusive(3, 12) * numPlayers + 2, 50 ); log("Creating decorative props..."); group = new SimpleGroup( [ new SimpleObject(aWoodA, 1,2, 0,1), new SimpleObject(aWoodB, 1,3, 0,1), new SimpleObject(aWheel, 0,2, 0,1), new SimpleObject(aWoodLarge, 0,1, 0,1), new SimpleObject(aBarrel, 0,2, 0,1) ], true ); createObjectGroupsByAreasDeprecated( group, 0, avoidClasses(clForest, 0), scaleByMapSize(5, 50), 50, [explorableArea] ); Engine.SetProgress(90); log("Creating straggler trees..."); var types = [oOak, oOakLarge, oPine, oAleppoPine]; var num = Math.floor(stragglerTrees / types.length); for (let type of types) createObjectGroupsByAreasDeprecated( new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest), 0, avoidClasses(clForest, 4, clHill, 5, clPlayer, 10, clBaseResource, 2, clMetal, 5, clRock, 5), num, 20, [explorableArea]); Engine.SetProgress(95); log("Creating grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassLarge, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clHill, 2, clPlayer, 2), scaleByMapSize(50, 300), 20, [explorableArea] ); placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setTerrainAmbientColor(0.44,0.51,0.56); setUnitsAmbientColor(0.44,0.51,0.56); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js (revision 20971) @@ -1,451 +1,454 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); Engine.LoadLibrary("heightmap"); InitMap(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) { let pathPoints = []; let position = clone(start); while (true) { rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap); if (texture) { let painters = [new TerrainPainter(texture)]; + if (tileClass !== undefined) painters.push(paintClass(tileClass)); + createArea( - new ClumpPlacer(0.3 * Math.square(width), 1, 1, 1, Math.floor(position.x), Math.floor(position.y)), + 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); } return pathPoints; } /** * Design resource spots */ // Mines let decorations = [ "actor|geology/gray1.xml", "actor|geology/gray_rock1.xml", "actor|geology/highland1.xml", "actor|geology/highland2.xml", "actor|geology/highland3.xml", "actor|geology/highland_c.xml", "actor|geology/highland_d.xml", "actor|geology/highland_e.xml", "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml", "actor|props/flora/bush_tempe_a.xml", "actor|props/flora/bush_tempe_b.xml", "actor|props/flora/ferns.xml" ]; function placeMine(point, centerEntity) { placeObject(point.x, point.y, 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()); } } // Food, fences with domestic animals g_WallStyles.other = { "overlap": 0, "fence": readyWallElement("other/fence_long", "gaia"), "fence_short": readyWallElement("other/fence_short", "gaia"), "bench": { "angle": Math.PI / 2, "length": 1.5, "indent": 0, "bend": 0, "templateName": "other/bench" }, "sheep": { "angle": 0, "length": 0, "indent": 0.75, "bend": 0, "templateName": "gaia/fauna_sheep" }, "foodBin": { "angle": Math.PI / 2, "length": 1.5, "indent": 0, "bend": 0, "templateName": "gaia/special_treasure_food_bin" }, "farmstead": { "angle": Math.PI, "length": 0, "indent": -3, "bend": 0, "templateName": "structures/brit_farmstead" } }; let fences = [ new Fortress("fence", [ "foodBin", "farmstead", "bench", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "fence", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "bench", "sheep", "fence", "turn_0.25", "sheep", "turn_0.25", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "turn_0.5", "bench", "turn_-0.5", "fence_short", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence_short", "sheep", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "turn_0.5", "fence_short", "turn_-0.5", "bench", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence", "turn_0.25", "sheep", "turn_0.25", "fence_short", "sheep", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "fence", "turn_0.25", "sheep", "turn_0.25", "bench", "sheep", "fence", "turn_0.25", "sheep", "turn_0.25", "fence_short", "sheep", "fence", "turn_0.25", "sheep", "turn_0.25", "fence_short", "sheep", "fence" ]) ]; let num = fences.length; for (let i = 0; i < num; ++i) fences.push(new Fortress("fence", clone(fences[i].wall).reverse())); // Groves, only Wood let groveEntities = ["gaia/flora_bush_temperate", "gaia/flora_tree_euro_beech"]; let groveActors = [ "actor|geology/highland1_moss.xml", "actor|geology/highland2_moss.xml", "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml", "actor|props/flora/bush_tempe_a.xml", "actor|props/flora/bush_tempe_b.xml", "actor|props/flora/ferns.xml" ]; let clGrove = createTileClass(); function placeGrove(point) { placeObject(point.x, point.y, 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) { let angle = dAngle * randFloat(i, i + 1); let dist = randFloat(2, 5); let objectList = groveEntities; if (i % 3 == 0) objectList = groveActors; - let x = point.x + dist * Math.cos(angle); - let y = point.y + dist * Math.sin(angle); - placeObject(x, y, pickRandom(objectList), 0, randomAngle()); - createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter("temp_grass_plants"), paintClass(clGrove)]); + let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)); + placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle()); + createArea( + new ClumpPlacer(5, 1, 1, 1, position), + [ + new TerrainPainter("temp_grass_plants"), + paintClass(clGrove) + ]); } } // Camps with fire and gold treasure function placeCamp(point, 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", "actor|props/special/common/waypoint_flag.xml", "actor|props/special/eyecandy/barrel_a.xml", "actor|props/special/eyecandy/basket_celt_a.xml", "actor|props/special/eyecandy/crate_a.xml", "actor|props/special/eyecandy/dummy_a.xml", "actor|props/special/eyecandy/handcart_1.xml", "actor|props/special/eyecandy/handcart_1_broken.xml", "actor|props/special/eyecandy/sack_1.xml", "actor|props/special/eyecandy/sack_1_rough.xml" ] ) { placeObject(point.x, point.y, 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()); } } function placeStartLocationResources(point, foodEntities = ["gaia/flora_bush_berry", "gaia/fauna_chicken", "gaia/fauna_chicken"]) { let currentAngle = randomAngle(); // Stone and chicken let dAngle = 4/9 * Math.PI; - let angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4); - let dist = 12; - let x = point.x + dist * Math.cos(angle); - let y = point.y + dist * Math.sin(angle); - placeMine({ "x": x, "y": y }, "gaia/geology_stonemine_temperate_quarry"); + let angle = currentAngle + randFloat(1, 3) * dAngle / 4; + let stonePosition = Vector2D.add(point, new Vector2D(12, 0).rotate(-angle)); + placeMine(stonePosition, "gaia/geology_stonemine_temperate_quarry"); currentAngle += dAngle; // Wood let quantity = 80; dAngle = 2 * Math.PI / quantity / 3; for (let i = 0; i < quantity; ++i) { angle = currentAngle + randFloat(0, dAngle); - dist = randFloat(10, 15); let objectList = groveEntities; if (i % 2 == 0) objectList = groveActors; - x = point.x + dist * Math.cos(angle); - y = point.y + dist * Math.sin(angle); - placeObject(x, y, pickRandom(objectList), 0, randomAngle()); - createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter("temp_grass_plants"), paintClass(clGrove)]); + let woodPosition = Vector2D.add(point, new Vector2D(randFloat(10, 15), 0).rotate(-angle)); + placeObject(woodPosition.x, woodPosition.y, pickRandom(objectList), 0, randomAngle()); + createArea( + new ClumpPlacer(5, 1, 1, 1, woodPosition), + [ + new TerrainPainter("temp_grass_plants"), + paintClass(clGrove) + ]); currentAngle += dAngle; } // Metal and chicken dAngle = 2 * Math.PI * 2 / 9; - angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4); - dist = 13; - x = point.x + dist * Math.cos(angle); - y = point.y + dist * Math.sin(angle); - placeMine({ "x": x, "y": y }, "gaia/geology_metal_temperate_slabs"); + angle = currentAngle + dAngle * randFloat(1, 3) / 4; + let metalPosition = Vector2D.add(point, new Vector2D(13, 0).rotate(-angle)); + placeMine(metalPosition, "gaia/geology_metal_temperate_slabs"); currentAngle += dAngle; // Berries quantity = 15; dAngle = 2 * Math.PI / quantity * 2 / 9; for (let i = 0; i < quantity; ++i) { angle = currentAngle + randFloat(0, dAngle); - dist = randFloat(10, 15); - x = point.x + dist * Math.cos(angle); - y = point.y + dist * Math.sin(angle); - placeObject(x, y, pickRandom(foodEntities), 0, randomAngle()); + let berriesPosition = Vector2D.add(point, new Vector2D(randFloat(10, 15), 0).rotate(-angle)); + placeObject(berriesPosition.x, berriesPosition.y, pickRandom(foodEntities), 0, randomAngle()); currentAngle += dAngle; } } /** * Environment settings */ setBiome("alpine"); g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0.01 }; g_Environment.Water.WaterBody.Colour = { "r" : 0.3, "g" : 0.05, "b" : 0.1, "a" : 0.1 }; g_Environment.Water.WaterBody.Murkiness = 0.4; /** * Base terrain shape generation and settings */ let heightScale = (g_Map.size + 256) / 768 / 4; let heightRange = { "min": MIN_HEIGHT * heightScale, "max": MAX_HEIGHT * heightScale }; // Water coverage let averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value let heightSeaGround = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine let heightSeaGroundAdjusted = heightSeaGround + MIN_HEIGHT; // Water height in RMGEN setWaterHeight(heightSeaGround); log("Generating terrain using diamon-square..."); let medH = (heightRange.min + heightRange.max) / 2; let initialHeightmap = [[medH, medH], [medH, medH]]; setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8); log("Apply erosion..."); for (let i = 0; i < 5; ++i) splashErodeMap(0.1); rescaleHeightmap(heightRange.min, heightRange.max); Engine.SetProgress(25); let heighLimits = [ heightRange.min + 1/3 * (heightSeaGroundAdjusted - heightRange.min), // 0 Deep water heightRange.min + 2/3 * (heightSeaGroundAdjusted - heightRange.min), // 1 Medium Water heightRange.min + (heightSeaGroundAdjusted - heightRange.min), // 2 Shallow water heightSeaGroundAdjusted + 1/8 * (heightRange.max - heightSeaGroundAdjusted), // 3 Shore heightSeaGroundAdjusted + 2/8 * (heightRange.max - heightSeaGroundAdjusted), // 4 Low ground heightSeaGroundAdjusted + 3/8 * (heightRange.max - heightSeaGroundAdjusted), // 5 Player and path height heightSeaGroundAdjusted + 4/8 * (heightRange.max - heightSeaGroundAdjusted), // 6 High ground heightSeaGroundAdjusted + 5/8 * (heightRange.max - heightSeaGroundAdjusted), // 7 Lower forest border heightSeaGroundAdjusted + 6/8 * (heightRange.max - heightSeaGroundAdjusted), // 8 Forest heightSeaGroundAdjusted + 7/8 * (heightRange.max - heightSeaGroundAdjusted), // 9 Upper forest border heightSeaGroundAdjusted + (heightRange.max - heightSeaGroundAdjusted)]; // 10 Hilltop let playerHeight = (heighLimits[4] + heighLimits[5]) / 2; // Average player height log("Determining height-dependent biome..."); // Texture and actor presets let myBiome = []; myBiome.push({ // 0 Deep water "texture": ["shoreline_stoney_a"], "actor": [["gaia/fauna_fish", "actor|geology/stone_granite_boulder.xml"], 0.02], "textureHS": ["alpine_mountainside"], "actorHS": [["gaia/fauna_fish"], 0.1] }); myBiome.push({ // 1 Medium Water "texture": ["shoreline_stoney_a", "alpine_shore_rocks"], "actor": [["actor|geology/stone_granite_boulder.xml", "actor|geology/stone_granite_med.xml"], 0.03], "textureHS": ["alpine_mountainside"], "actorHS": [["actor|geology/stone_granite_boulder.xml", "actor|geology/stone_granite_med.xml"], 0.0] }); myBiome.push({ // 2 Shallow water "texture": ["alpine_shore_rocks"], "actor": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_large.xml", "actor|geology/stone_granite_med.xml", "actor|props/flora/reeds_pond_lush_b.xml"], 0.2], "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); myBiome.push({ // 3 Shore "texture": ["alpine_shore_rocks_grass_50", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "gaia/flora_bush_badlands", "actor|geology/highland1_moss.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml"], 0.3], "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); myBiome.push({ // 4 Low ground "texture": ["alpine_dirt_grass_50", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); myBiome.push({ // 5 Player and path height "texture": ["new_alpine_grass_c", "new_alpine_grass_b", "new_alpine_grass_d"], "actor": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml"], 0.1] }); myBiome.push({ // 6 High ground "texture": ["new_alpine_grass_a", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml", "actor|props/flora/bush_highlands.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml"], 0.1] }); myBiome.push({ // 7 Lower forest border "texture": ["new_alpine_grass_mossy", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "gaia/flora_tree_oak", "actor|props/flora/grass_tufts_a.xml", "gaia/flora_bush_berry", "actor|geology/highland2_moss.xml", "gaia/fauna_goat", "actor|props/flora/bush_tempe_underbrush.xml"], 0.3], "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|props/flora/grass_tufts_a.xml", "actor|geology/highland2_moss.xml"], 0.1] }); myBiome.push({ // 8 Forest "texture": ["alpine_forrestfloor"], "actor": [["gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "actor|geology/highland2_moss.xml", "actor|props/flora/bush_highlands.xml"], 0.5], "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland2_moss.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); myBiome.push({ // 9 Upper forest border "texture": ["alpine_forrestfloor_snow", "new_alpine_grass_dirt_a"], "actor": [["gaia/flora_tree_pine", "actor|geology/snow1.xml"], 0.3], "textureHS": ["alpine_cliff_b"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|geology/snow1.xml"], 0.1] }); myBiome.push({ // 10 Hilltop "texture": ["alpine_cliff_a", "alpine_cliff_snow"], "actor": [["actor|geology/highland1.xml"], 0.05], "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0] }); let [playerIDs, startLocations] = 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); 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 clPath = 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)); } Engine.SetProgress(45); log("Determining resource locations..."); let avoidPoints = clone(startLocations); 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); Engine.SetProgress(55); /** * Divide tiles in areas by height and avoid paths */ let areas = []; for (let h = 0; h < heighLimits.length; ++h) areas.push([]); for (let x = 0; x < tchm.length; ++x) for (let y = 0; y < tchm[0].length; ++y) { if (g_Map.tileClasses[clPath].inclusionCount[x][y] > 0) // Avoid paths continue; let minHeight = heightRange.min; for (let h = 0; h < heighLimits.length; ++h) { if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h]) { areas[h].push({ "x": x, "y": y }); break; } else minHeight = heighLimits[h]; } } /** * Get max slope of each area */ let slopeMap = getSlopeMap(); let minSlope = []; let maxSlope = []; for (let h = 0; h < heighLimits.length; ++h) { minSlope[h] = Infinity; maxSlope[h] = 0; for (let t = 0; t < areas[h].length; ++t) { let x = areas[h][t].x; let y = areas[h][t].y; let slope = slopeMap[x][y]; if (slope > maxSlope[h]) maxSlope[h] = slope; if (slope < minSlope[h]) minSlope[h] = slope; } } log("Painting areas by height and slope..."); for (let h = 0; h < heighLimits.length; ++h) for (let t = 0; t < areas[h].length; ++t) { let x = areas[h][t].x; let y = areas[h][t].y; let actor = undefined; let texture = pickRandom(myBiome[h].texture); if (slopeMap[x][y] < 0.4 * (minSlope[h] + maxSlope[h])) { if (randBool(myBiome[h].actor[1])) actor = pickRandom(myBiome[h].actor[0]); } else { texture = pickRandom(myBiome[h].textureHS); if (randBool(myBiome[h].actorHS[1])) actor = pickRandom(myBiome[h].actorHS[0]); } g_Map.texture[x][y] = g_Map.getTextureID(texture); if (actor) placeObject(randFloat(x, x + 1), randFloat(y, y + 1), actor, 0, randomAngle()); } Engine.SetProgress(80); log("Placing players..."); if (isNomad()) placePlayersNomad(createTileClass(), new HeightConstraint(heighLimits[4], heighLimits[5])); else for (let p = 0; p < playerIDs.length; ++p) { let point = startLocations[p]; placeCivDefaultStartingEntities(point, playerIDs[p], true); placeStartLocationResources(startLocations[p]); } log("Placing resources, farmsteads, groves and camps..."); for (let i = 0; i < resourceSpots.length; ++i) { let choice = i % 5; if (choice == 0) placeMine(resourceSpots[i], "gaia/geology_stonemine_temperate_formation"); if (choice == 1) placeMine(resourceSpots[i], "gaia/geology_metal_temperate_slabs"); if (choice == 2) placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randomAngle()); if (choice == 3) placeGrove(resourceSpots[i]); if (choice == 4) placeCamp(resourceSpots[i]); } ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/cantabrian_highlands.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/cantabrian_highlands.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/cantabrian_highlands.js (revision 20971) @@ -1,282 +1,282 @@ Engine.LoadLibrary("rmgen"); const tPrimary = "temp_grass_long"; const tGrass = ["temp_grass", "temp_grass", "temp_grass_d"]; const tGrassPForest = "temp_plants_bog"; const tGrassDForest = "temp_plants_bog"; const tGrassA = "temp_grass_plants"; const tGrassB = "temp_plants_bog"; const tGrassC = "temp_mud_a"; const tHill = ["temp_highlands", "temp_grass_long_b"]; const tCliff = ["temp_cliff_a", "temp_cliff_b"]; const tRoad = "temp_road"; const tRoadWild = "temp_road_overgrown"; const tGrassPatchBlend = "temp_grass_long_b"; const tGrassPatch = ["temp_grass_d", "temp_grass_clovers"]; const tShoreBlend = "temp_grass_plants"; const tShore = "temp_plants_bog"; const tWater = "temp_mud_a"; const oOak = "gaia/flora_tree_oak"; const oOakLarge = "gaia/flora_tree_oak_large"; const oApple = "gaia/flora_tree_apple"; const oPine = "gaia/flora_tree_pine"; const oAleppoPine = "gaia/flora_tree_aleppo_pine"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oSheep = "gaia/fauna_sheep"; const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; const oStoneSmall = "gaia/geology_stone_temperate"; const oMetalLarge = "gaia/geology_metal_temperate_slabs"; const aGrass = "actor|props/flora/grass_soft_large_tall.xml"; const aGrassShort = "actor|props/flora/grass_soft_large.xml"; const aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; const aLillies = "actor|props/flora/pond_lillies_large.xml"; const aRockLarge = "actor|geology/stone_granite_large.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBushMedium = "actor|props/flora/bush_medit_me.xml"; const aBushSmall = "actor|props/flora/bush_medit_sm.xml"; const pForestD = [tGrassDForest + TERRAIN_SEPARATOR + oOak, tGrassDForest + TERRAIN_SEPARATOR + oOakLarge, tGrassDForest]; const pForestP = [tGrassPForest + TERRAIN_SEPARATOR + oPine, tGrassPForest + TERRAIN_SEPARATOR + oAleppoPine, tGrassPForest]; const heightSeaGround = -7; const heightLand = 3; const heightHill = 20; InitMap(heightLand, tPrimary); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var playerHillRadius = defaultPlayerBaseRadius() / (isNomad() ? 1.5 : 1); var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35)); log("Creating player hills and ramps..."); for (let i = 0; i < numPlayers; ++i) { createArea( - new ClumpPlacer(diskArea(playerHillRadius), 0.95, 0.6, 10, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(diskArea(playerHillRadius), 0.95, 0.6, 10, playerPosition[i]), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clPlayer) ]); let angle = playerAngle[i] + Math.PI * (1 + randFloat(-1, 1) / 8); createPassage({ "start": Vector2D.add(playerPosition[i], new Vector2D(playerHillRadius + 15, 0).rotate(-angle)), "end": Vector2D.add(playerPosition[i], new Vector2D(playerHillRadius - 3, 0).rotate(-angle)), "startWidth": 10, "endWidth": 10, "smoothWidth": 2, "tileClass": clPlayer, "terrain": tHill, "edgeTerrain": tCliff }); } placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": false, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oOak, "count": 2 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(10); log("Creating lakes..."); var numLakes = Math.round(scaleByMapSize(1,4) * numPlayers); var waterAreas = createAreas( new ClumpPlacer(scaleByMapSize(100, 250), 0.8, 0.1, 10), [ new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 6), paintClass(clWater) ], avoidClasses(clPlayer, 2, clWater, 20), numLakes ); Engine.SetProgress(15); log("Creating reeds..."); var group = new SimpleGroup( [new SimpleObject(aReeds, 5,10, 0,4), new SimpleObject(aLillies, 0,1, 0,4)], true ); createObjectGroupsByAreasDeprecated(group, 0, [borderClasses(clWater, 3, 0), stayClasses(clWater, 1)], numLakes, 100, waterAreas ); Engine.SetProgress(20); log("Creating fish..."); createObjectGroupsByAreasDeprecated( new SimpleGroup( [new SimpleObject(oFish, 1,1, 0,1)], true, clFood ), 0, [stayClasses(clWater, 4), avoidClasses(clFood, 8)], numLakes / 4, 50, waterAreas ); Engine.SetProgress(25); createBumps(avoidClasses(clWater, 2, clPlayer, 0)); Engine.SetProgress(30); log("Creating hills..."); createHills([tCliff, tCliff, tHill], avoidClasses(clPlayer, 2, clWater, 5, clHill, 15), clHill, scaleByMapSize(1, 4) * numPlayers); Engine.SetProgress(35); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); createForests( [tGrass, tGrassDForest, tGrassPForest, pForestP, pForestD], avoidClasses(clPlayer, 1, clWater, 3, clForest, 17, clHill, 1), clForest, forestTrees); Engine.SetProgress(40); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tGrass,tGrassA],[tGrassA,tGrassB], [tGrassB,tGrassC]], [1,1], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(45); log("Creating grass patches..."); createLayeredPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], [tGrassPatchBlend, tGrassPatch], [1], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(50); log("Creating stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], avoidClasses(clWater, 0, clForest, 1, clPlayer, 5, clRock, 10, clHill, 1), clRock); Engine.SetProgress(55); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clWater, 0, clForest, 1, clPlayer, 5, clMetal, 10, clRock, 5, clHill, 1), clMetal ); Engine.SetProgress(60); createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 1, 2, 0, 1)], [new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ scaleByMapSize(16, 262), scaleByMapSize(8, 131), scaleByMapSize(13, 200), scaleByMapSize(13, 200), scaleByMapSize(13, 200) ], avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0)); Engine.SetProgress(70); createFood( [ [new SimpleObject(oSheep, 2, 3, 0, 2)], [new SimpleObject(oDeer, 5, 7, 0, 4)] ], [ 3 * numPlayers, 3 * numPlayers ], avoidClasses(clWater, 0, clForest, 0, clPlayer, 1, clHill, 1, clFood, 20), clFood); Engine.SetProgress(80); createFood( [ [new SimpleObject(oBerryBush, 5, 7, 0, 4)] ], [ randIntInclusive(1, 4) * numPlayers + 2 ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), clFood); Engine.SetProgress(85); createStragglerTrees( [oOak, oOakLarge, oPine, oApple], avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 1, clMetal, 6, clRock, 6), clForest, stragglerTrees); Engine.SetProgress(90); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("cirrus"); setWaterColor(0.447, 0.412, 0.322); // muddy brown setWaterTint(0.447, 0.412, 0.322); setWaterMurkiness(1.0); setWaterWaviness(3.0); setWaterType("lake"); setFogThickness(0.25); setFogFactor(0.4); setPPEffect("hdr"); setPPSaturation(0.62); setPPContrast(0.62); setPPBloom(0.3); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/canyon.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/canyon.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/canyon.js (revision 20971) @@ -1,353 +1,347 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.hill; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const oWoodTreasure = "gaia/special_treasure_wood"; const oFoodTreasure = "gaia/special_treasure_food_bin"; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const aTree = g_Decoratives.tree; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightLand = 3; const heightHill = 30; InitMap(heightHill, tMainTerrain); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clHill2 = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); var playerCanyonRadius = scaleByMapSize(18, 32); var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); log("Reserving space for the players, their initial forests and some less space therein without trees..."); for (let i = 0; i < numPlayers; ++i) for (let j = 1; j <= 2; ++j) createArea( - new ClumpPlacer( - diskArea(playerCanyonRadius / j), - 0.65, - 0.1, - 10, - playerPosition[i].x, - playerPosition[i].y), + new ClumpPlacer(diskArea(playerCanyonRadius / j), 0.65, 0.1, 10, playerPosition[i]), [ new LayeredPainter([tMainTerrain, tMainTerrain], [2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 2), paintClass(j == 1 || isNomad() ? clLand : clPlayer) ]); log("Creating center area..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.16)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.16)), 0.7, 0.1, 10, mapCenter), [ new LayeredPainter([tMainTerrain, tMainTerrain], [3]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 3), paintClass(clLand) ]); createArea( - new ClumpPlacer(150, 0.6, 0.3, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(150, 0.6, 0.3, 10, mapCenter), paintClass(clHill)); log("Creating hills..."); for (let i = 0; i < scaleByMapSize(9, 16); ++i) createArea( new PathPlacer( new Vector2D(randIntExclusive(1, mapSize), randIntExclusive(1, mapSize)), new Vector2D(randIntExclusive(1, mapSize), randIntExclusive(1, mapSize)), scaleByMapSize(11, 16), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0), [ new SmoothElevationPainter(ELEVATION_SET, heightHill, 3), paintClass(clHill2) ], avoidClasses(clPlayer, 6, clHill2, 3, clHill, 2)); for (let g = 0; g < scaleByMapSize(5, 30); ++g) { let position = new Vector2D(randIntInclusive(1, mapSize - 1), randIntInclusive(1, mapSize - 1)); let newarea = createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.06)), 0.7, 0.1, 10, position.x, position.y), + new ClumpPlacer(diskArea(fractionToTiles(0.06)), 0.7, 0.1, 10, position), [ new LayeredPainter([tMainTerrain, tMainTerrain], [3]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 3), paintClass(clLand) ], avoidClasses(clLand, 6)); if (newarea !== null) { var distances = []; var d1 = 9999; var d2 = 9999; var p1 = -1; var p2 = 0; for (let i = 0; i < numPlayers; ++i) distances.push(position.distanceTo(playerPosition[i])); for (let a = 0; a < numPlayers; ++a) { if (d1 >= distances[a]) { d2 = d1; d1 = distances[a]; p2 = p1; p1 = a; } else if (d2 >= distances[a]) { d2 = distances[a]; p2 = a; } } for (let playerID of [p1, p2]) if (playerPosition[playerID]) createArea( new PathPlacer(position, playerPosition[playerID], scaleByMapSize(11, 17), 0.4, scaleByMapSize(3, 12), 0.1, 0.1), [ new LayeredPainter([tMainTerrain, tMainTerrain], [3]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 3), paintClass(clLand) ]); } } for (let i = 0; i < numPlayers; ++i) { log("Creating path from player to center and to neighbor..."); let neighbor = i + 1 < numPlayers ? i + 1 : 0; for (let position of [playerPosition[neighbor], mapCenter]) createArea( new PathPlacer( playerPosition[i], position, scaleByMapSize(8, 13), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0), [ new LayeredPainter([tRoadWild, tRoad], [1]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 2), paintClass(clLand), paintClass(clHill) ]); } log("Painting center place..."); createArea( - new ClumpPlacer(150, 0.6, 0.3, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(150, 0.6, 0.3, 10, mapCenter), new LayeredPainter([tRoad, tRoad], [1])); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], // PlayerTileClass already marked above "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoad, "innerTerrain": tRoad, "radius": playerCanyonRadius / 3 }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ], "distance": 11 }, "Trees": { "template": oTree1 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(20); paintTerrainBasedOnHeight(3.1, 29, 0, tCliff); paintTileClassBasedOnHeight(3.1, 32, 0, clHill2); createBumps([avoidClasses(clPlayer, 2), stayClasses(clLand, 2)]); createHills([tCliff, tCliff, tHill], [avoidClasses(clPlayer, 2, clHill, 8, clHill2, 8), stayClasses(clLand, 5)], clHill, scaleByMapSize(10, 40)); // create hills outside the canyon createHills([tCliff, tCliff, tMainTerrain], avoidClasses(clLand, 1, clHill, 1), clHill, scaleByMapSize(20, 150), undefined, undefined, undefined, undefined, 40); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], [avoidClasses(clPlayer, 1, clForest, 15, clHill, 1, clHill2, 0), stayClasses(clLand, 4)], clForest, forestTrees); Engine.SetProgress(50); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tMainTerrain,tTier1Terrain],[tTier1Terrain,tTier2Terrain], [tTier2Terrain,tTier3Terrain]], [1,1], [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 4, clHill2, 0), stayClasses(clLand, 3)], scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], tTier4Terrain, [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 4, clHill2, 0), stayClasses(clLand, 3)], scaleByMapSize(15, 45), clDirt); log("Creating stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], [avoidClasses(clForest, 1, clPlayer, 3, clRock, 10, clHill, 1, clHill2, 1), stayClasses(clLand, 2)], clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], [avoidClasses(clForest, 1, clPlayer, 3, clMetal, 10, clRock, 5, clHill, 1, clHill2, 1), stayClasses(clLand, 2)], clMetal ); Engine.SetProgress(65); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 1, 2, 0, 1)], [new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ 3 * scaleByMapSize(16, 262), 3 * scaleByMapSize(8, 131), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200) ], avoidClasses(clForest, 0, clPlayer, 0, clHill, 0)); log("Creating actor trees..."); var group = new SimpleGroup( [new SimpleObject(aTree, 1,1, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clLand, 5), scaleByMapSize(200, 800), 50 ); Engine.SetProgress(70); createFood( [ [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)] ], [ 3 * numPlayers, 3 * numPlayers ], [avoidClasses(clForest, 0, clPlayer, 4, clHill, 1, clFood, 20, clHill2, 1), stayClasses(clLand, 3)], clFood); createFood( [ [new SimpleObject(oFruitBush, 5, 7, 0, 4)] ], [ 3 * numPlayers ], [avoidClasses(clForest, 0, clPlayer, 4, clHill, 1, clFood, 10, clHill2, 1), stayClasses(clLand, 3)], clFood); Engine.SetProgress(85); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], [avoidClasses(clForest, 1, clHill, 1, clPlayer, 9, clMetal, 6, clRock, 6, clHill2, 1), stayClasses(clLand, 3)], clForest, stragglerTrees); 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()); placePlayersNomad( clPlayer, [stayClasses(clLand, 4), avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clHill2, 4, clFood, 2)]); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/corinthian_isthmus.js (revision 20971) @@ -1,268 +1,268 @@ Engine.LoadLibrary("rmgen"); TILE_CENTERED_HEIGHT_MAP = true; const tCity = "medit_city_pavement"; const tCityPlaza = "medit_city_pavement"; const tHill = ["medit_grass_shrubs", "medit_rocks_grass_shrubs", "medit_rocks_shrubs", "medit_rocks_grass", "medit_shrubs"]; const tMainDirt = "medit_dirt"; const tCliff = "medit_cliff_aegean"; const tForestFloor = "medit_grass_shrubs"; const tGrass = ["medit_grass_field", "medit_grass_field_a"]; const tGrassSand50 = "medit_grass_field_a"; const tGrassSand25 = "medit_grass_field_b"; const tDirt = "medit_dirt_b"; const tDirt2 = "medit_rocks_grass"; const tDirt3 = "medit_rocks_shrubs"; const tDirtCracks = "medit_dirt_c"; const tShore = "medit_sand"; const tWater = "medit_sand_wet"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oSheep = "gaia/fauna_sheep"; const oGoat = "gaia/fauna_goat"; const oStoneLarge = "gaia/geology_stonemine_medit_quarry"; const oStoneSmall = "gaia/geology_stone_mediterranean"; const oMetalLarge = "gaia/geology_metal_mediterranean_slabs"; const oDatePalm = "gaia/flora_tree_cretan_date_palm_short"; const oSDatePalm = "gaia/flora_tree_cretan_date_palm_tall"; const oCarob = "gaia/flora_tree_carob"; const oFanPalm = "gaia/flora_tree_medit_fan_palm"; const oPoplar = "gaia/flora_tree_poplar_lombardy"; const oCypress = "gaia/flora_tree_cypress"; const aBush1 = "actor|props/flora/bush_medit_sm.xml"; const aBush2 = "actor|props/flora/bush_medit_me.xml"; const aBush3 = "actor|props/flora/bush_medit_la.xml"; const aBush4 = "actor|props/flora/bush_medit_me.xml"; const aDecorativeRock = "actor|geology/stone_granite_med.xml"; const pForest = [tForestFloor, tForestFloor + TERRAIN_SEPARATOR + oCarob, tForestFloor + TERRAIN_SEPARATOR + oDatePalm, tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, tForestFloor]; var heightSeaGround = -4; var heightLand = 3; InitMap(heightLand, tHill); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clGrass = createTileClass(); var clHill = createTileClass(); log("Creating the main river"); var riverAngle = randomAngle(); var riverWidth = scaleByMapSize(15, 70); var riverStart = new Vector2D(mapCenter.x, 0).rotateAround(riverAngle, mapCenter); var riverEnd = new Vector2D(mapCenter.x, mapSize).rotateAround(riverAngle, mapCenter); createArea( new PathPlacer(riverStart, riverEnd, riverWidth, 0.2, 15 * scaleByMapSize(1, 3), 0.04, 0.01), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4)); log("Creating small puddles at the map border to ensure players being separated..."); for (let point of [riverStart, riverEnd]) createArea( - new ClumpPlacer(Math.floor(diskArea(riverWidth / 2)), 0.95, 0.6, 10, point.x, point.y), + new ClumpPlacer(diskArea(riverWidth / 2), 0.95, 0.6, 10, point), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4)); log("Creating passage connecting the two riversides..."); var passageStart = riverStart.rotateAround(Math.PI / 2, mapCenter); var passageEnd = riverEnd.rotateAround(Math.PI / 2, mapCenter); createArea( new PathPlacer( passageStart, passageEnd, scaleByMapSize(10, 30), 0.5, 3 * scaleByMapSize(1, 4), 0.1, 0.01), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4)); paintTerrainBasedOnHeight(-6, 1, 1, tWater); paintTerrainBasedOnHeight(1, 2, 1, tShore); paintTerrainBasedOnHeight(2, 5, 1, tGrass); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); placePlayerBases({ "PlayerPlacement": playerPlacementRiver(riverAngle, fractionToTiles(0.6)), "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "outerTerrain": tCityPlaza, "innerTerrain": tCity }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oCarob, "count": 2 }, "Decoratives": { "template": aBush1 } }); Engine.SetProgress(40); createBumps(avoidClasses(clWater, 2, clPlayer, 20)); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); createForests( [tForestFloor, tForestFloor, tForestFloor, pForest, pForest], avoidClasses(clPlayer, 20, clForest, 17, clWater, 2, clBaseResource, 3), clForest, forestTrees); Engine.SetProgress(50); if (randBool()) createHills([tGrass, tCliff, tHill], avoidClasses(clPlayer, 20, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); else createMountains(tCliff, avoidClasses(clPlayer, 20, clForest, 1, clHill, 15, clWater, 3), clHill, scaleByMapSize(3, 15)); log("Creating grass patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tGrass,tGrassSand50],[tGrassSand50,tGrassSand25], [tGrassSand25,tGrass]], [1,1], avoidClasses(clForest, 0, clGrass, 2, clPlayer, 10, clWater, 2, clDirt, 2, clHill, 1), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(55); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [tDirt3, tDirt2,[tDirt,tMainDirt], [tDirtCracks,tMainDirt]], [1,1,1], avoidClasses(clForest, 0, clDirt, 2, clPlayer, 10, clWater, 2, clGrass, 2, clHill, 1), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(60); log("Creating stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], avoidClasses(clForest, 4, clPlayer, 15, clRock, 10, clWater, 4, clHill, 4), clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clForest, 4, clPlayer, 15, clMetal, 10, clRock, 5, clWater, 4, clHill, 4), clMetal ); Engine.SetProgress(65); createDecoration( [ [ new SimpleObject(aDecorativeRock, 1, 3, 0, 1) ], [ new SimpleObject(aBush2, 1, 2, 0, 1), new SimpleObject(aBush1, 1, 3, 0, 2), new SimpleObject(aBush4, 1, 2, 0, 1), new SimpleObject(aBush3, 1, 3, 0, 2) ] ], [ scaleByMapSize(16, 262), scaleByMapSize(40, 360) ], avoidClasses(clWater, 2, clForest, 0, clPlayer, 5, clBaseResource, 6, clHill, 1, clRock, 6, clMetal, 6)); Engine.SetProgress(70); createFood( [ [new SimpleObject(oFish, 2, 3, 0, 2)] ], [ 3 * scaleByMapSize(5, 20) ], [avoidClasses(clFood, 10), stayClasses(clWater, 5)], clFood); createFood( [ [new SimpleObject(oSheep, 5, 7, 0, 4)], [new SimpleObject(oGoat, 2, 4, 0, 3)], [new SimpleObject(oDeer, 2, 4, 0, 2)] ], [ scaleByMapSize(5,20), scaleByMapSize(5,20), scaleByMapSize(5,20) ], avoidClasses(clForest, 0, clPlayer, 10, clBaseResource, 6, clWater, 1, clFood, 10, clHill, 1, clRock, 6, clMetal, 6), clFood); createFood( [ [new SimpleObject(oBerryBush, 5, 7, 0, 4)] ], [ 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10, clRock, 6, clMetal, 6), clFood); Engine.SetProgress(90); createStragglerTrees( [oDatePalm, oSDatePalm, oCarob, oFanPalm, oPoplar, oCypress], avoidClasses(clForest, 1, clWater, 2, clPlayer, 8, clBaseResource, 6, clMetal, 6, clRock, 6, clHill, 1), clForest, stragglerTrees); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("sunny"); setSunColor(0.917, 0.828, 0.734); setWaterColor(0, 0.501961, 1); setWaterTint(0.501961, 1, 1); setWaterWaviness(2.5); setWaterType("ocean"); setWaterMurkiness(0.49); setFogFactor(0.3); setFogThickness(0.25); setPPEffect("hdr"); setPPContrast(0.62); setPPSaturation(0.51); setPPBloom(0.12); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/corsica.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/corsica.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/corsica.js (revision 20971) @@ -1,518 +1,518 @@ Engine.LoadLibrary("rmgen"); var tGrass = ["medit_grass_field", "medit_grass_field_b", "temp_grass_c"]; var tLushGrass = ["medit_grass_field","medit_grass_field_a"]; var tSteepCliffs = ["temp_cliff_b", "temp_cliff_a"]; var tCliffs = ["temp_cliff_b", "medit_cliff_italia", "medit_cliff_italia_grass"]; var tHill = ["medit_cliff_italia_grass","medit_cliff_italia_grass", "medit_grass_field", "medit_grass_field", "temp_grass"]; var tMountain = ["medit_cliff_italia_grass","medit_cliff_italia"]; var tRoad = ["medit_city_tile","medit_rocks_grass","medit_grass_field_b"]; var tRoadWild = ["medit_rocks_grass","medit_grass_field_b"]; var tShoreBlend = ["medit_sand_wet","medit_rocks_wet"]; var tShore = ["medit_rocks","medit_sand","medit_sand"]; var tSandTransition = ["medit_sand","medit_rocks_grass","medit_rocks_grass","medit_rocks_grass"]; var tVeryDeepWater = ["medit_sea_depths","medit_sea_coral_deep"]; var tDeepWater = ["medit_sea_coral_deep","tropic_ocean_coral"]; var tCreekWater = "medit_sea_coral_plants"; var ePine = "gaia/flora_tree_aleppo_pine"; var ePalmTall = "gaia/flora_tree_cretan_date_palm_tall"; var eFanPalm = "gaia/flora_tree_medit_fan_palm"; var eApple = "gaia/flora_tree_apple"; var eBush = "gaia/flora_bush_berry"; var eFish = "gaia/fauna_fish"; var ePig = "gaia/fauna_pig"; var eStoneMine = "gaia/geology_stonemine_medit_quarry"; var eMetalMine = "gaia/geology_metal_mediterranean_slabs"; var aRock = "actor|geology/stone_granite_med.xml"; var aLargeRock = "actor|geology/stone_granite_large.xml"; var aBushA = "actor|props/flora/bush_medit_sm_lush.xml"; var aBushB = "actor|props/flora/bush_medit_me_lush.xml"; var aPlantA = "actor|props/flora/plant_medit_artichoke.xml"; var aPlantB = "actor|props/flora/grass_tufts_a.xml"; var aPlantC = "actor|props/flora/grass_soft_tuft_a.xml"; var aStandingStone = "actor|props/special/eyecandy/standing_stones.xml"; var heightSeaGround = -8; var heightCreeks = -5; var heightBeaches = -1; var heightMain = 5; var heightOffsetMainRelief = 30; var heightOffsetLevel1 = 9; var heightOffsetLevel2 = 8; var heightOffsetBumps = 2; var heightOffsetAntiBumps = -5; InitMap(heightSeaGround, tVeryDeepWater); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var mapCenter = getMapCenter(); var clIsland = createTileClass(); var clCreek = createTileClass(); var clWater = createTileClass(); var clCliffs = createTileClass(); var clForest = createTileClass(); var clShore = createTileClass(); var clPlayer = createTileClass(); var clBaseResource = createTileClass(); var clPassage = createTileClass(); var clSettlement = createTileClass(); var radiusBeach = fractionToTiles(0.57); var radiusCreeks = fractionToTiles(0.52); var radiusIsland = fractionToTiles(0.4); var radiusLevel1 = fractionToTiles(0.35); var radiusPlayer = fractionToTiles(0.25); var radiusLevel2 = fractionToTiles(0.2); var creeksArea = () => randBool() ? randFloat(10, 50) : scaleByMapSize(75, 100) + randFloat(0, 20); var nbCreeks = scaleByMapSize(6, 15); var nbSubIsland = 5; var nbBeaches = scaleByMapSize(2, 5); var nbPassagesLevel1 = scaleByMapSize(4, 8); var nbPassagesLevel2 = scaleByMapSize(2, 4); log("Creating Corsica and Sardinia..."); var swapAngle = randBool() ? Math.PI / 2 : 0; var islandLocations = [new Vector2D(0.05, 0.05), new Vector2D(0.95, 0.95)].map(v => v.mult(mapSize).rotateAround(-swapAngle, mapCenter)); for (let island = 0; island < 2; ++island) { log("Creating island area..."); createArea( - new ClumpPlacer(diskArea(radiusIsland), 1, 0.5, 10, islandLocations[island].x, islandLocations[island].y), + new ClumpPlacer(diskArea(radiusIsland), 1, 0.5, 10, islandLocations[island]), [ new LayeredPainter([tCliffs, tGrass], [2]), new SmoothElevationPainter(ELEVATION_SET, heightMain, 0), paintClass(clIsland) ]); log("Creating subislands..."); for (let i = 0; i < nbSubIsland + 1; ++i) { let angle = Math.PI * (island + i / (nbSubIsland * 2)) + swapAngle; let location = Vector2D.add(islandLocations[island], new Vector2D(radiusIsland, 0).rotate(-angle)); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.09)), 0.6, 0.03, 10, location.x, location.y), + new ClumpPlacer(diskArea(fractionToTiles(0.09)), 0.6, 0.03, 10, location), [ new LayeredPainter([tCliffs, tGrass], [2]), new SmoothElevationPainter(ELEVATION_SET, heightMain, 1), paintClass(clIsland) ]); } log("Creating creeks..."); for (let i = 0; i < nbCreeks + 1; ++i) { let angle = Math.PI * (island + i * (1 / (nbCreeks * 2))) + swapAngle; let location = Vector2D.add(islandLocations[island], new Vector2D(radiusCreeks, 0).rotate(-angle)); createArea( - new ClumpPlacer(creeksArea(), 0.4, 0.01, 10, location.x, location.y), + new ClumpPlacer(creeksArea(), 0.4, 0.01, 10, location), [ new TerrainPainter(tSteepCliffs), new SmoothElevationPainter(ELEVATION_SET, heightCreeks, 0), paintClass(clCreek) ]); } log("Creating beaches..."); for (let i = 0; i < nbBeaches + 1; ++i) { let angle = Math.PI * (island + (i / (nbBeaches * 2.5)) + 1 / (nbBeaches * 6) + randFloat(-1, 1) / (nbBeaches * 7)) + swapAngle; let start = Vector2D.add(islandLocations[island], new Vector2D(radiusIsland, 0).rotate(-angle)); let end = Vector2D.add(islandLocations[island], new Vector2D(radiusBeach, 0).rotate(-angle)); createArea( - new ClumpPlacer(130, 0.7, 0.8, 10, Math.round((start.x + end.x * 3) / 4), Math.round((start.y + end.y * 3) / 4)), + new ClumpPlacer(130, 0.7, 0.8, 10, Vector2D.add(start, Vector2D.mult(end, 3)).div(4)), new SmoothElevationPainter(ELEVATION_SET, heightBeaches, 5)); createPassage({ "start": start, "end": end, "startWidth": 18, "endWidth": 25, "smoothWidth": 4, "tileClass": clShore }); } log("Creating main relief..."); createArea( - new ClumpPlacer(diskArea(radiusIsland), 1, 0.2, 10, islandLocations[island].x, islandLocations[island].y), + new ClumpPlacer(diskArea(radiusIsland), 1, 0.2, 10, islandLocations[island]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetMainRelief, fractionToTiles(0.45))); log("Creating first level plateau..."); createArea( - new ClumpPlacer(diskArea(radiusLevel1), 0.95, 0.02, 10, islandLocations[island].x, islandLocations[island].y), + new ClumpPlacer(diskArea(radiusLevel1), 0.95, 0.02, 10, islandLocations[island]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetLevel1, 1)); log("Creating first level passages..."); for (let i = 0; i <= nbPassagesLevel1; ++i) { let angle = Math.PI * (i / 7 + 1 / 9 + island) + swapAngle; createPassage({ "start": Vector2D.add(islandLocations[island], new Vector2D(radiusLevel1 + 10, 0).rotate(-angle)), "end": Vector2D.add(islandLocations[island], new Vector2D(radiusLevel1 - 4, 0).rotate(-angle)), "startWidth": 10, "endWidth": 6, "smoothWidth": 3, "tileClass": clPassage }); } if (mapSize > 150) { log("Creating second level plateau..."); createArea( - new ClumpPlacer(diskArea(radiusLevel2), 0.98, 0.04, 10, islandLocations[island].x, islandLocations[island].y), + new ClumpPlacer(diskArea(radiusLevel2), 0.98, 0.04, 10, islandLocations[island]), [ new LayeredPainter([tCliffs, tGrass], [2]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetLevel2, 1) ]); log("Creating second level passages..."); for (let i = 0; i < nbPassagesLevel2; ++i) { let angle = Math.PI * (i / (2 * nbPassagesLevel2) + 1 / (4 * nbPassagesLevel2) + island) + swapAngle; createPassage({ "start": Vector2D.add(islandLocations[island], new Vector2D(radiusLevel2 + 3, 0).rotate(-angle)), "end": Vector2D.add(islandLocations[island], new Vector2D(radiusLevel2 - 6, 0).rotate(-angle)), "startWidth": 4, "endWidth": 6, "smoothWidth": 2, "tileClass": clPassage }); } } } Engine.SetProgress(30); log("Determining player locations..."); var playerIDs = sortAllPlayers(); var playerPosition = []; var playerAngle = []; var p = 0; for (let island = 0; island < 2; ++island) { let playersPerIsland = island == 0 ? Math.ceil(numPlayers / 2) : Math.floor(numPlayers / 2); for (let i = 0; i < playersPerIsland; ++i) { playerAngle[p] = Math.PI * ((i + 0.5) / (2 * playersPerIsland) + island) + swapAngle; playerPosition[p] = Vector2D.add(islandLocations[island], new Vector2D(radiusPlayer).rotate(-playerAngle[p])); ++p; } } placePlayerBases({ "PlayerPlacement": [sortAllPlayers(), playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": false, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad, "coherence": 0.8, "radius": 6, "painters": [ paintClass(clSettlement) ] }, "Chicken": { }, "Berries": { "template": eBush }, "Mines": { "types": [ { "template": eMetalMine }, { "template": eStoneMine } ] } // Sufficient starting trees around, no decoratives }); Engine.SetProgress(40); log("Creating bumps..."); createAreas( new ClumpPlacer(70, 0.6, 0.1, 4), [new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBumps, 3)], [ stayClasses(clIsland, 2), avoidClasses(clPlayer, 6, clPassage, 2) ], scaleByMapSize(20, 100), 5); log("Creating anti bumps..."); createAreas( new ClumpPlacer(120, 0.3, 0.1, 4), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetAntiBumps, 6), avoidClasses(clPlayer, 6, clPassage, 2, clIsland, 2), scaleByMapSize(20, 100), 5); log("Painting water..."); paintTileClassBasedOnHeight(-Infinity, 0, Elevation_ExcludeMin_ExcludeMax, clWater); log("Painting land..."); for (let mapX = 0; mapX < mapSize; ++mapX) for (let mapZ = 0; mapZ < mapSize; ++mapZ) { let terrain = getCosricaSardiniaTerrain(mapX, mapZ); if (!terrain) continue; createTerrain(terrain).place(mapX, mapZ); if (terrain == tCliffs || terrain == tSteepCliffs) addToClass(mapX, mapZ, clCliffs); } function getCosricaSardiniaTerrain(mapX, mapZ) { let position = new Vector2D(mapX, mapZ); let isWater = getTileClass(clWater).countMembersInRadius(mapX, mapZ, 3); let isShore = getTileClass(clShore).countMembersInRadius(mapX, mapZ, 2); let isPassage = getTileClass(clPassage).countMembersInRadius(mapX, mapZ, 2); let isSettlement = getTileClass(clSettlement).countMembersInRadius(mapX, mapZ, 2); if (isSettlement) return undefined; let height = getHeight(mapX, mapZ); let slope = g_Map.getSlope(position); if (height >= 0.5 && height < 1.5 && isShore) return tSandTransition; // Paint land cliffs and grass if (height >= 1 && !isWater) { if (isPassage) return tGrass; if (slope >= 1.25) return height > 25 ? tSteepCliffs : tCliffs; if (height < 17) return tGrass; if (slope < 0.625) return tHill; return tMountain; } if (slope >= 1.125) return tCliffs; if (height >= 1.5) return undefined; if (height >= -0.75) return tShore; if (height >= -3) return tShoreBlend; if (height >= -6) return tCreekWater; if (height > -10 && slope < 0.75) return tDeepWater; return undefined; } Engine.SetProgress(65); log("Creating mines..."); for (let mine of [eMetalMine, eStoneMine]) createObjectGroupsDeprecated( new SimpleGroup( [ new SimpleObject(mine, 1,1, 0,0), new SimpleObject(aBushB, 1,1, 2,2), new SimpleObject(aBushA, 0,2, 1,3) ], true, clBaseResource), 0, [ stayClasses(clIsland, 1), avoidClasses( clWater, 3, clPlayer, 6, clBaseResource, 4, clPassage, 2, clCliffs, 1) ], scaleByMapSize(6, 25), 1000); log("Creating grass patches..."); createAreas( new ClumpPlacer(20, 0.3, 0.06, 0.5), [ new TerrainPainter(tLushGrass), paintClass(clForest) ], avoidClasses( clWater, 1, clPlayer, 6, clBaseResource, 3, clCliffs, 1), scaleByMapSize(10, 40)); log("Creating forests..."); createObjectGroupsDeprecated( new SimpleGroup( [ new SimpleObject(ePine, 3, 6, 1, 3), new SimpleObject(ePalmTall, 1, 3, 1, 3), new SimpleObject(eFanPalm, 0, 2, 0, 2), new SimpleObject(eApple, 0, 1, 1, 2) ], true, clForest), 0, [ stayClasses(clIsland, 3), avoidClasses( clWater, 1, clForest, 0, clPlayer, 3, clBaseResource, 4, clPassage, 2, clCliffs, 2) ], scaleByMapSize(350, 2500), 100); Engine.SetProgress(75); log("Creating small decorative rocks..."); createObjectGroupsDeprecated( new SimpleGroup( [ new SimpleObject(aRock, 1, 3, 0, 1), new SimpleObject(aStandingStone, 0, 2, 0, 3) ], true), 0, avoidClasses( clWater, 0, clForest, 0, clPlayer, 6, clBaseResource, 4, clPassage, 2), scaleByMapSize(16, 262), 50); log("Creating large decorative rocks..."); var rocksGroup = new SimpleGroup( [ new SimpleObject(aLargeRock, 1, 2, 0, 1), new SimpleObject(aRock, 1, 3, 0, 2) ], true); createObjectGroupsDeprecated( rocksGroup, 0, avoidClasses( clWater, 0, clForest, 0, clPlayer, 6, clBaseResource, 4, clPassage, 2), scaleByMapSize(8, 131), 50); createObjectGroupsDeprecated( rocksGroup, 0, borderClasses(clWater, 5, 10), scaleByMapSize(100, 800), 500); log("Creating decorative plants..."); var plantGroups = [ new SimpleGroup( [ new SimpleObject(aPlantA, 3, 7, 0, 3), new SimpleObject(aPlantB, 3,6, 0, 3), new SimpleObject(aPlantC, 1,4, 0, 4) ], true), new SimpleGroup( [ new SimpleObject(aPlantB, 5, 20, 0, 5), new SimpleObject(aPlantC, 4,10, 0,4) ], true) ]; for (let group of plantGroups) createObjectGroupsDeprecated( group, 0, avoidClasses( clWater, 0, clBaseResource, 4, clShore, 3), scaleByMapSize(100, 600), 50); Engine.SetProgress(80); log("Creating animals..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(ePig, 2,4, 0,3)]), 0, avoidClasses( clWater, 3, clBaseResource, 4, clPlayer, 6), scaleByMapSize(20, 100), 50); log("Creating fish..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(eFish, 1,2, 0,3)]), 0, [ stayClasses(clWater, 3), avoidClasses(clCreek, 3, clShore, 3) ], scaleByMapSize(50, 150), 100); Engine.SetProgress(95); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clBaseResource, 4, clCliffs, 4)); setSkySet(pickRandom(["cumulus", "sunny"])); setSunColor(0.8, 0.66, 0.48); setSunElevation(0.828932); setSunRotation((swapAngle ? 0.288 : 0.788) * Math.PI); setTerrainAmbientColor(0.564706,0.543726,0.419608); setUnitsAmbientColor(0.53,0.55,0.45); setWaterColor(0.2,0.294,0.49); setWaterTint(0.208, 0.659, 0.925); setWaterMurkiness(0.72); setWaterWaviness(2.0); setWaterType("ocean"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/cycladic_archipelago.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/cycladic_archipelago.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/cycladic_archipelago.js (revision 20971) @@ -1,362 +1,362 @@ Engine.LoadLibrary("rmgen"); const tOceanRockDeep = "medit_sea_coral_deep"; const tOceanCoral = "medit_sea_coral_plants"; const tBeachWet = "medit_sand_wet"; const tBeachDry = "medit_sand"; const tBeach = ["medit_rocks_grass","medit_sand", "medit_rocks_grass_shrubs"]; const tBeachBlend = ["medit_rocks_grass", "medit_rocks_grass_shrubs"]; const tCity = "medit_city_tile"; const tGrassDry = ["medit_grass_field_dry", "medit_grass_field_b"]; const tGrass = ["medit_rocks_grass", "medit_rocks_grass","medit_dirt","medit_rocks_grass_shrubs"]; const tGrassShrubs = "medit_shrubs"; const tCliffShrubs = ["medit_cliff_aegean_shrubs", "medit_cliff_italia_grass","medit_cliff_italia"]; const tCliff = ["medit_cliff_italia", "medit_cliff_italia", "medit_cliff_italia_grass"]; const tForestFloor = "medit_forestfloor_a"; const oBeech = "gaia/flora_tree_euro_beech"; const oBerryBush = "gaia/flora_bush_berry"; const oCarob = "gaia/flora_tree_carob"; const oCypress1 = "gaia/flora_tree_cypress"; const oCypress2 = "gaia/flora_tree_cypress"; const oLombardyPoplar = "gaia/flora_tree_poplar_lombardy"; const oPalm = "gaia/flora_tree_medit_fan_palm"; const oPine = "gaia/flora_tree_aleppo_pine"; const oDateT = "gaia/flora_tree_cretan_date_palm_tall"; const oDateS = "gaia/flora_tree_cretan_date_palm_short"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oWhale = "gaia/fauna_whale_humpback"; const oStoneLarge = "gaia/geology_stonemine_medit_quarry"; const oStoneSmall = "gaia/geology_stone_mediterranean"; const oMetalLarge = "gaia/geology_metal_mediterranean_slabs"; const oShipwreck = "other/special_treasure_shipwreck"; const oShipDebris = "other/special_treasure_shipwreck_debris"; const aRockLarge = "actor|geology/stone_granite_large.xml"; const aRockMed = "actor|geology/stone_granite_med.xml"; const aRockSmall = "actor|geology/stone_granite_small.xml"; const pPalmForest = [tForestFloor+TERRAIN_SEPARATOR+oPalm, tGrass]; const pPineForest = [tForestFloor+TERRAIN_SEPARATOR+oPine, tGrass]; const pPoplarForest = [tForestFloor+TERRAIN_SEPARATOR+oLombardyPoplar, tGrass]; const pMainForest = [tForestFloor+TERRAIN_SEPARATOR+oCarob, tForestFloor+TERRAIN_SEPARATOR+oBeech, tGrass, tGrass]; const heightSeaGround = -5; const heightLand = 3; const heightHill = 12; const heightOffsetBump = 2; InitMap(heightSeaGround, tWater); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clCoral = createTileClass(); var clPlayer = createTileClass(); var clIsland = createTileClass(); var clCity = createTileClass(); var clDirt = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); //array holding starting islands based on number of players var startingPlaces=[[0],[0,3],[0,2,4],[0,1,3,4],[0,1,2,3,4],[0,1,2,3,4,5]]; var startAngle = randomAngle(); var islandRadius = scaleByMapSize(15, 40); var islandCount = Math.max(6, numPlayers); var islandPosition = distributePointsOnCircle(islandCount, startAngle, fractionToTiles(0.39), mapCenter)[0].map(position => position.round()); var centralIslandRadius = scaleByMapSize(15, 30); var centralIslandCount = Math.floor(scaleByMapSize(1, 4)); var centralIslandPosition = new Array(numPlayers).fill(0).map((v, i) => Vector2D.add(mapCenter, new Vector2D(fractionToTiles(randFloat(0.1, 0.16)), 0).rotate( -startAngle - Math.PI * (i * 2 / centralIslandCount + randFloat(-1, 1) / 8)).round())); var areas = []; var nPlayer = 0; var playerPosition = []; function createCycladicArchipelagoIsland(position, tileClass, radius, coralRadius) { log("Creating deep ocean rocks..."); createArea( - new ClumpPlacer(diskArea(radius + coralRadius), 0.7, 0.1, 10, position.x, position.y), + new ClumpPlacer(diskArea(radius + coralRadius), 0.7, 0.1, 10, position), [ new LayeredPainter([tOceanRockDeep, tOceanCoral], [5]), paintClass(clCoral) ], avoidClasses(clCoral, 0, clPlayer, 0)); log("Creating island..."); areas.push( createArea( - new ClumpPlacer(diskArea(radius), 0.7, 0.1, 10, position.x, position.y), + new ClumpPlacer(diskArea(radius), 0.7, 0.1, 10, position), [ new LayeredPainter([tOceanCoral, tBeachWet, tBeachDry, tBeach, tBeachBlend, tGrass], [1, 3, 1, 1, 2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 5), paintClass(tileClass) ], avoidClasses(clPlayer, 0))); } log("Creating player islands..."); for (let i = 0; i < islandCount; ++i) { let isPlayerIsland = numPlayers >= 6 || i == startingPlaces[numPlayers - 1][nPlayer]; if (isPlayerIsland) { playerPosition[nPlayer] = islandPosition[i]; ++nPlayer; } createCycladicArchipelagoIsland(islandPosition[i], isPlayerIsland ? clPlayer : clIsland, islandRadius, scaleByMapSize(1, 5)); } log("Creating central islands..."); for (let position of centralIslandPosition) createCycladicArchipelagoIsland(position, clIsland, centralIslandRadius, 2); placePlayerBases({ "PlayerPlacement": [sortAllPlayers(), playerPosition], // PlayerTileClass is clCity here and painted below "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "radius": 6, "outerTerrain": tGrass, "innerTerrain": tCity, "painters": [ paintClass(clCity) ] }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oPalm, "count": 2 } // No decoratives }); Engine.SetProgress(20); log("Creating bumps..."); createAreasInAreas( new ClumpPlacer(scaleByMapSize(20, 60), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 3), avoidClasses(clCity, 0), scaleByMapSize(25, 75),15, areas); Engine.SetProgress(34); log("Creating hills..."); createAreasInAreas( new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), [ new LayeredPainter([tCliff, tCliffShrubs], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], avoidClasses(clCity, 15, clHill, 15), scaleByMapSize(5, 30), 15, areas); Engine.SetProgress(38); paintTileClassBasedOnHeight(-Infinity, 0, Elevation_ExcludeMin_ExcludeMax, clWater); log("Creating forests..."); var forestTypes = [ [[tForestFloor, tGrass, pPalmForest], [tForestFloor, pPalmForest]], [[tForestFloor, tGrass, pPineForest], [tForestFloor, pPineForest]], [[tForestFloor, tGrass, pPoplarForest], [tForestFloor, pPoplarForest]], [[tForestFloor, tGrass, pMainForest], [tForestFloor, pMainForest]] ]; for (let type of forestTypes) createAreasInAreas( new ClumpPlacer(randIntInclusive(6, 17), 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses(clCity, 1, clWater, 3, clForest, 3, clHill, 1, clBaseResource, 4), scaleByMapSize(10, 64), 20, areas); Engine.SetProgress(42); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 5, clRock, 6)], scaleByMapSize(4,16), 200, areas ); Engine.SetProgress(46); log("Creating small stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 5, clRock, 2)], scaleByMapSize(4,16), 200, areas ); Engine.SetProgress(50); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsByAreasDeprecated(group, 0, [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 5, clMetal, 6, clRock, 6)], scaleByMapSize(4,16), 200, areas ); Engine.SetProgress(54); log("Creating shrub patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreasInAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter([tBeachBlend, tGrassShrubs], [1]), paintClass(clDirt) ], avoidClasses(clWater, 3, clHill, 0, clDirt, 6, clCity, 0, clBaseResource, 4), scaleByMapSize(4, 16), 20, areas); Engine.SetProgress(58); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreasInAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter([tGrassDry], []), paintClass(clDirt) ], avoidClasses(clWater, 3, clHill, 0, clDirt, 6, clCity, 0, clBaseResource, 4), scaleByMapSize(4, 16), 20, areas); Engine.SetProgress(62); log("Creating straggler trees..."); for (let tree of [oCarob, oBeech, oLombardyPoplar, oLombardyPoplar, oPine]) createObjectGroupsByAreasDeprecated( new SimpleGroup([new SimpleObject(tree, 1,1, 0,1)], true, clForest), 0, avoidClasses(clWater, 2, clForest, 2, clCity, 3, clBaseResource, 4, clRock, 6, clMetal, 6, clPlayer, 1, clHill, 1), scaleByMapSize(2, 38), 50, areas ); Engine.SetProgress(66); log("Create straggler cypresses..."); group = new SimpleGroup( [new SimpleObject(oCypress2, 1,3, 0,3), new SimpleObject(oCypress1, 0,2, 0,2)], true ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clWater, 2, clForest, 2, clCity, 3, clBaseResource, 4, clRock, 6, clMetal, 6, clPlayer, 1, clHill, 1), scaleByMapSize(5, 75), 50, areas ); Engine.SetProgress(70); log("Create straggler date palms..."); group = new SimpleGroup( [new SimpleObject(oDateS, 1,3, 0,3), new SimpleObject(oDateT, 0,2, 0,2)], true ); createObjectGroupsByAreasDeprecated(group, 0, avoidClasses(clWater, 2, clForest, 1, clCity, 0, clBaseResource, 4, clRock, 6, clMetal, 6, clPlayer, 1, clHill, 1), scaleByMapSize(5, 75), 50, areas ); Engine.SetProgress(74); log("Creating rocks..."); group = new SimpleGroup( [new SimpleObject(aRockSmall, 0,3, 0,2), new SimpleObject(aRockMed, 0,2, 0,2), new SimpleObject(aRockLarge, 0,1, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 0, clCity, 0), scaleByMapSize(30, 180), 50 ); Engine.SetProgress(78); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 5, clForest, 1, clHill, 1, clCity, 10, clMetal, 6, clRock, 2, clFood, 8), 3 * numPlayers, 50 ); Engine.SetProgress(82); log("Creating berry bushes..."); group = new SimpleGroup([new SimpleObject(oBerryBush, 5,7, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clForest, 1, clHill, 1, clCity, 10, clMetal, 6, clRock, 2, clFood, 8), 1.5 * numPlayers, 100 ); Engine.SetProgress(86); log("Creating Fish..."); group = new SimpleGroup([new SimpleObject(oFish, 1,1, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, [stayClasses(clWater,1),avoidClasses(clFood, 8)], scaleByMapSize(40,200), 100 ); Engine.SetProgress(90); log("Creating Whales..."); group = new SimpleGroup([new SimpleObject(oWhale, 1,1, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, [stayClasses(clWater,1),avoidClasses(clFood, 8, clPlayer,4,clIsland,4)], scaleByMapSize(10,40), 100 ); Engine.SetProgress(94); log("Creating shipwrecks..."); group = new SimpleGroup([new SimpleObject(oShipwreck, 1,1, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, [stayClasses(clWater,1),avoidClasses(clFood, 8)], scaleByMapSize(6,16), 100 ); Engine.SetProgress(98); log("Creating shipwreck debris..."); group = new SimpleGroup([new SimpleObject(oShipDebris, 1,2, 0,4)], true, clFood); createObjectGroupsDeprecated(group, 0, [stayClasses(clWater,1),avoidClasses(clFood, 8)], scaleByMapSize(10,20), 100 ); Engine.SetProgress(99); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clBaseResource, 4, clHill, 4, clMetal, 4, clRock, 4, clFood, 1)); setSkySet("sunny"); setWaterColor(0.2,0.294,0.49); setWaterTint(0.208, 0.659, 0.925); setWaterMurkiness(0.72); setWaterWaviness(3.0); setWaterType("ocean"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/danubius.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/danubius.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/danubius.js (revision 20971) @@ -1,809 +1,809 @@ Engine.LoadLibrary("rmgen"); // Spawn ships away from the shoreline, but patrol close to the shoreline const triggerPointShipSpawn = "trigger/trigger_point_A"; const triggerPointShipPatrol = "trigger/trigger_point_B"; const triggerPointShipUnloadLeft = "trigger/trigger_point_C"; const triggerPointShipUnloadRight = "trigger/trigger_point_D"; const triggerPointLandPatrolLeft = "trigger/trigger_point_E"; const triggerPointLandPatrolRight = "trigger/trigger_point_F"; const triggerPointCCAttackerPatrolLeft = "trigger/trigger_point_G"; const triggerPointCCAttackerPatrolRight = "trigger/trigger_point_H"; const triggerPointRiverDirection = "trigger/trigger_point_I"; const tPrimary = ["temp_grass_aut", "temp_grass_plants_aut", "temp_grass_c_aut", "temp_grass_d_aut"]; const tRoad = "steppe_river_rocks"; const tIsland = ["temp_grass_long_b_aut", "temp_grass_plants_aut", "temp_forestfloor_aut"]; const tCliff = "temp_cliff_a"; const tForestFloor = "temp_forestfloor_aut"; const tGrass = "medit_shrubs_golden"; const tGrass2 ="grass_mediterranean_dry_1024test"; const tGrass3 = "medit_grass_field_b"; const tShore = "temp_dirt_gravel_b"; const tWater = "steppe_river_rocks_wet"; const tSeaDepths = "medit_sea_depths"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oSheep = "gaia/fauna_sheep"; const oGoat = "gaia/fauna_goat"; const oWolf = "gaia/fauna_wolf"; const oHawk = "gaia/fauna_hawk"; const oRabbit = "gaia/fauna_rabbit"; const oBoar = "gaia/fauna_boar"; const oBear = "gaia/fauna_bear"; const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; const oStoneRuins = "gaia/special_ruins_standing_stone"; const oMetalLarge = "gaia/geology_metal_mediterranean_slabs"; const oApple = "gaia/flora_tree_apple"; const oAcacia = "gaia/flora_tree_acacia"; const oOak = "gaia/flora_tree_oak_aut"; const oOak2 = "gaia/flora_tree_oak_aut_new"; const oOak3 = "gaia/flora_tree_oak_dead"; const oOak4 = "gaia/flora_tree_oak"; const oPopolar = "gaia/flora_tree_poplar_lombardy"; const oBeech = "gaia/flora_tree_euro_beech_aut"; const oBeech2 = "gaia/flora_tree_euro_beech"; const oTreasures = [ "gaia/special_treasure_food_barrel", "gaia/special_treasure_food_bin", "gaia/special_treasure_stone", "gaia/special_treasure_wood", "gaia/special_treasure_metal" ]; const oCivicCenter = "structures/gaul_civil_centre"; const oHouse = "structures/gaul_house"; const oTemple = "structures/gaul_temple"; const oTavern = "structures/gaul_tavern"; const oTower = "structures/gaul_defense_tower"; const oSentryTower = "structures/gaul_sentry_tower"; const oOutpost = "structures/gaul_outpost"; const oHut = "other/celt_hut"; const oLongHouse = "other/celt_longhouse"; const oPalisadeTower = "other/palisades_rocks_watchtower"; const oTallSpikes = "other/palisades_tall_spikes"; const oAngleSpikes = "other/palisades_angle_spike"; const oFemale = "units/gaul_support_female_citizen"; const oHealer = "units/gaul_support_healer_b"; const oSkirmisher = "units/gaul_infantry_javelinist_b"; const oNakedFanatic = "units/gaul_champion_fanatic"; const aBush1 = "actor|props/flora/bush_tempe_sm.xml"; const aBush2 = "actor|props/flora/bush_tempe_me.xml"; const aBush3 = "actor|props/flora/bush_tempe_la.xml"; const aBush4 = "actor|props/flora/bush_tempe_me.xml"; const aRock1 = "actor|geology/stone_granite_med.xml"; const aRock2 = "actor|geology/stone_granite_boulder.xml"; const aRock3 = "actor|geology/stone_granite_greek_boulder.xml"; const aRock4 = "actor|geology/stonemine_alpine_a.xml"; const aFerns = "actor|props/flora/ferns.xml"; const aBucket = "actor|props/structures/celts/blacksmith_bucket"; const aBarrel = "actor|props/structures/gauls/storehouse_barrel_b"; const aTartan = "actor|props/structures/celts/tartan_a"; const aWheel = "actor|props/special/eyecandy/wheel_laying"; const aWell = "actor|props/special/eyecandy/well_1_b"; const aWoodcord = "actor|props/special/eyecandy/woodcord"; const aWaterLog = "actor|props/flora/water_log.xml"; const aCampfire = "actor|props/special/eyecandy/campfire"; const aBench = "actor|props/special/eyecandy/bench_1"; const aRug = "actor|props/special/eyecandy/rug_stand_iber"; const treeTypes = [oOak, oOak2, oOak3, oOak4, oBeech, oBeech2, oAcacia]; const pForest1 = [ tForestFloor, tForestFloor + TERRAIN_SEPARATOR + oOak, tForestFloor + TERRAIN_SEPARATOR + oOak2, tForestFloor + TERRAIN_SEPARATOR + oOak3, tForestFloor + TERRAIN_SEPARATOR + oOak4, tForestFloor ]; const pForest2 = [ tForestFloor, tForestFloor + TERRAIN_SEPARATOR + oPopolar, tForestFloor + TERRAIN_SEPARATOR + oBeech, tForestFloor + TERRAIN_SEPARATOR + oBeech2, tForestFloor + TERRAIN_SEPARATOR + oAcacia, tForestFloor ]; const smallMapSize = 192; const mediumMapSize = 256; const normalMapSize = 320; // Minimum distance from the map border to ship ungarrison points const ShorelineDistance = 15; const heightSeaGround = -3; const heightShore = 1; const heightLand = 3; const heightPath = 5; const heightIsland = 6; InitMap(heightLand, tPrimary); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); const mapBounds = getMapBounds(); var clMiddle = createTileClass(); var clPlayer = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clLand = [createTileClass(), createTileClass()]; var clLandPatrolPoint = [createTileClass(), createTileClass()]; var clCCAttackerPatrolPoint = [createTileClass(), createTileClass()]; var clShore = [createTileClass(), createTileClass()]; var clShoreUngarrisonPoint = [createTileClass(), createTileClass()]; var clShip = createTileClass(); var clShipPatrol = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clHill = createTileClass(); var clIsland = createTileClass(); var clTreasure = createTileClass(); var clWaterLog = createTileClass(); var clGauls = createTileClass(); var clTower = createTileClass(); var clOutpost = createTileClass(); var clPath = createTileClass(); var clRitualPlace = createTileClass(); var startAngle = randomAngle(); var waterWidth = fractionToTiles(0.3); // How many treasures will be placed near the gallic civic centers var gallicCCTreasureCount = randIntInclusive(8, 12); // How many treasures will be placed randomly on the map at most var randomTreasureCount = randIntInclusive(0, 3 * numPlayers); // Place a gallic village on small maps and larger var gallicCC = mapSize >= smallMapSize; if (gallicCC) { log("Creating gallic villages..."); let gaulCityRadius = 12; let gaulCityBorderDistance = mapSize < mediumMapSize ? 10 : 18; // Whether to add a celtic ritual and a path from the gallic city leading to it let addCelticRitual = randBool(0.9); // One village left and right of the river for (let i = 0; i < 2; ++i) { let civicCenterPosition = new Vector2D( i == 0 ? mapBounds.left + gaulCityBorderDistance : mapBounds.right - gaulCityBorderDistance, mapCenter.y).rotateAround(startAngle, mapCenter); if (addCelticRitual) { // Don't position the meeting place at the center of the map let meetingPlacePosition = new Vector2D( i == 0 ? mapBounds.left + waterWidth : mapBounds.right - waterWidth, mapCenter.y + fractionToTiles(randFloat(0.1, 0.4)) * (randBool() ? 1 : -1)).rotateAround(startAngle, mapCenter); // Radius of the meeting place let mRadius = scaleByMapSize(4, 6); // Create a path connecting the gallic city with a meeting place at the shoreline. // To avoid the path going through the palisade wall, start it at the gate, not at the city center. let pathStart = Vector2D.add(civicCenterPosition, new Vector2D(gaulCityRadius * (i == 0 ? 1 : -1), 0).rotate(startAngle)); createArea( new PathPlacer(pathStart, meetingPlacePosition, 4, 0.4, 4, 0.2, 0.05), [ new LayeredPainter([tShore, tRoad, tRoad], [1, 3]), new SmoothElevationPainter(ELEVATION_SET, heightPath, 4), paintClass(clPath) ]); // Create the meeting place near the shoreline at the end of the path createArea( - new ClumpPlacer(diskArea(mRadius), 0.6, 0.3, 10, meetingPlacePosition.x, meetingPlacePosition.y), + new ClumpPlacer(diskArea(mRadius), 0.6, 0.3, 10, meetingPlacePosition), [ new LayeredPainter([tShore, tShore], [1]), paintClass(clPath), paintClass(clRitualPlace) ]); placeObject(meetingPlacePosition.x, meetingPlacePosition.y, aCampfire, 0, randomAngle()); let femaleCount = Math.round(mRadius * 2); let maleCount = Math.round(mRadius * 3); let benchCount = Math.round(mRadius * 2); let rugCount = Math.round(mRadius * 2.5); let goatCount = Math.round(mRadius * 1.5); let femaleRadius = mRadius * 0.3; let maleRadius = mRadius * 0.4; let benchRadius = mRadius * 0.5; let rugRadius = mRadius * 0.6; let goatRadius = mRadius * 0.8; let calcBend = entCount => Math.PI * 2 / entCount; let maleBend = calcBend(maleCount); g_WallStyles.celt_ritual = { "overlap": 0, "female": { "angle": Math.PI, "length": femaleRadius, "indent": 0, "bend": calcBend(femaleCount), "templateName": oFemale }, "skirmisher": { "angle": Math.PI, "length": maleRadius, "indent": 0, "bend": maleBend, "templateName": oSkirmisher }, "healer": { "angle": Math.PI, "length": maleRadius, "indent": 0, "bend": maleBend, "templateName": oHealer }, "fanatic": { "angle": Math.PI, "length": maleRadius, "indent": 0, "bend": maleBend, "templateName": oNakedFanatic }, "bench": { "angle": Math.PI / 2, "length": benchRadius, "indent": 0, "bend": calcBend(benchCount), "templateName": aBench }, "rug": { "angle": 0, "length": rugRadius, "indent": 0, "bend": calcBend(rugCount), "templateName": aRug }, "goat": { "angle": Math.PI, "length": goatRadius, "indent": 0, "bend": calcBend(goatCount), "templateName": oGoat } }; placeCustomFortress(meetingPlacePosition.x, meetingPlacePosition.y, new Fortress("celt ritual females", new Array(femaleCount).fill("female")), "celt_ritual", 0, 0); placeCustomFortress(meetingPlacePosition.x, meetingPlacePosition.y, new Fortress("celt ritual males", new Array(maleCount).fill(0).map(i => pickRandom(["skirmisher", "healer", "fanatic"]))), "celt_ritual", 0, 0); placeCustomFortress(meetingPlacePosition.x, meetingPlacePosition.y, new Fortress("celt ritual bench", new Array(benchCount).fill("bench")), "celt_ritual", 0, 0); placeCustomFortress(meetingPlacePosition.x, meetingPlacePosition.y, new Fortress("celt ritual rug", new Array(rugCount).fill("rug")), "celt_ritual", 0, 0); placeCustomFortress(meetingPlacePosition.x, meetingPlacePosition.y, new Fortress("celt ritual goat", new Array(goatCount).fill("goat")), "celt_ritual", 0, 0); } placeObject(civicCenterPosition.x, civicCenterPosition.y, oCivicCenter, 0, startAngle + BUILDING_ORIENTATION + Math.PI * 3/2 * i); // Create the city patch createArea( - new ClumpPlacer(diskArea(gaulCityRadius), 0.6, 0.3, 10, civicCenterPosition.x, civicCenterPosition.y), + new ClumpPlacer(diskArea(gaulCityRadius), 0.6, 0.3, 10, civicCenterPosition), [ new LayeredPainter([tShore, tShore], [1]), paintClass(clGauls) ]); // Place palisade fortress and some city buildings // Use actors to avoid players capturing the buildings g_WallStyles.gaul = clone(g_WallStyles.gaul_stone); g_WallStyles.gaul.house = { "angle": Math.PI, "length": 0, "indent": 4, "bend": 0, "templateName": oHouse }; g_WallStyles.gaul.hut = { "angle": Math.PI, "length": 0, "indent": 4, "bend": 0, "templateName": oHut }; g_WallStyles.gaul.longhouse = { "angle": Math.PI, "length": 0, "indent": 4, "bend": 0, "templateName": oLongHouse }; g_WallStyles.gaul.tavern = { "angle": Math.PI * 3/2, "length": 0, "indent": 4, "bend": 0, "templateName": oTavern }; g_WallStyles.gaul.temple = { "angle": Math.PI * 3/2, "length": 0, "indent": 4, "bend": 0, "templateName": oTemple }; g_WallStyles.gaul.defense_tower = { "angle": Math.PI / 2, "length": 0, "indent": 4, "bend": 0, "templateName": mapSize >= normalMapSize ? (isNomad() ? oSentryTower : oTower) : oPalisadeTower }; // Replace stone walls with palisade walls for (let element of ["gate", "long", "short", "cornerIn", "cornerOut", "tower"]) g_WallStyles.gaul[element] = getWallElement(element, "palisade"); let wall = [ "gate", "hut", "tower", "long", "long", "cornerIn", "defense_tower", "long", "long", "temple", "tower", "long", "house", "short", "tower", "gate", "tower", "longhouse", "long", "long", "cornerIn", "defense_tower", "long", "tavern", "long", "tower"]; wall = wall.concat(wall); placeCustomFortress(civicCenterPosition.x, civicCenterPosition.y, new Fortress("Geto-Dacian Tribal Confederation", wall), "gaul", 0, startAngle + Math.PI); // Place spikes g_WallStyles.palisade.spikes_tall = readyWallElement("other/palisades_tall_spikes", "gaia"); g_WallStyles.palisade.spike_single = readyWallElement("other/palisades_angle_spike", "gaia"); let threeSpikes = new Array(3).fill("spikes_tall"); let fiveSpikes = new Array(5).fill("spikes_tall"); let spikes = [ "gap_3.6", "spike_single", ...threeSpikes, "turn_0.25", "spike_single", "turn_0.25", ...fiveSpikes, "spike_single", "gap_3.6", "spike_single", ...threeSpikes, "turn_0.25", "spike_single", "turn_0.25", ...threeSpikes, "spike_single" ]; spikes = spikes.concat(spikes); placeCustomFortress(civicCenterPosition.x, civicCenterPosition.y, new Fortress("spikes", spikes), "palisade", 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, pickRandom(oTreasures), 0, randomAngle()); } } Engine.SetProgress(10); placePlayerBases({ "PlayerPlacement": playerPlacementRiver(startAngle, fractionToTiles(0.6)), "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": false, "CityPatch": { "outerTerrain": tShore, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": aBush1 }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ], "distance": scaleByMapSize(9, 14) }, "Trees": { "template": oOak, "count": 20, "minDist": 10, "maxDist": 14 }, "Decoratives": { "template": aBush1 } }); Engine.SetProgress(20); paintRiver({ "parallel": true, "start": new Vector2D(mapCenter.x, mapBounds.top).rotateAround(startAngle, mapCenter), "end": new Vector2D(mapCenter.x, mapBounds.bottom).rotateAround(startAngle, mapCenter), "width": waterWidth, "fadeDist": scaleByMapSize(6, 25), "deviation": 0, "heightRiverbed": heightSeaGround, "heightLand": heightLand, "meanderShort": 30, "meanderLong": 0, "waterFunc": (position, height, riverFraction) => { // Distinguish left and right shoreline if (0 < height && height < 1 && position.y > ShorelineDistance && position.y < mapSize - ShorelineDistance) addToClass(position.x, position.y, clShore[position.x < mapCenter.x ? 0 : 1]); }, "landFunc": (position, shoreDist1, shoreDist2) => { if (shoreDist1 > 0) addToClass(position.x, position.y, clLand[0]); if (shoreDist2 < 0) addToClass(position.x, position.y, clLand[1]); } }); Engine.SetProgress(30); paintTileClassBasedOnHeight(-Infinity, 0.7, Elevation_ExcludeMin_ExcludeMax, clWater); log("Creating shores..."); paintTerrainBasedOnHeight(-Infinity, heightShore, 0, tWater); paintTerrainBasedOnHeight(heightShore, heightLand, 0, tShore); Engine.SetProgress(35); log("Creating bumps..."); createBumps(avoidClasses(clPlayer, 6, clWater, 2, clPath, 1, clGauls, 1), scaleByMapSize(30, 300), 1, 8, 4, 0, 3); Engine.SetProgress(40); log("Creating hills..."); if (randBool()) createHills( [tCliff, tCliff, tCliff], avoidClasses(clPlayer, 18, clHill, 20, clWater, 2, clGauls, 5, clPath, 1), clHill, scaleByMapSize(3, 15)); else createMountains( tCliff, avoidClasses(clPlayer, 18, clHill, 20, clWater, 2, clGauls, 5, clPath, 1), clHill, scaleByMapSize(3, 15)); Engine.SetProgress(45); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); createForests( [tForestFloor, tForestFloor, tForestFloor, pForest1, pForest2], avoidClasses(clPlayer, 16, clForest, 17, clWater, 5, clHill, 2, clGauls, 5, clPath, 1), clForest, forestTrees); Engine.SetProgress(50); log("Creating grass patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tGrass, tGrass2],[tGrass2, tGrass3], [tGrass3, tGrass]], [1, 1], avoidClasses(clForest, 0, clPlayer, 10, clWater, 2, clDirt, 2, clHill, 1, clGauls, 5, clPath, 1), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(55); log("Creating islands..."); createAreas( new ChainPlacer(Math.floor(scaleByMapSize(3, 4)), Math.floor(scaleByMapSize(4, 8)), Math.floor(scaleByMapSize(50, 80)), 0.5), [ new LayeredPainter([tWater, tShore, tIsland], [2, 1]), new SmoothElevationPainter(ELEVATION_SET, heightIsland, 4), paintClass(clIsland) ], [avoidClasses(clIsland, 30), stayClasses (clWater, 10)], scaleByMapSize(1, 4) * numPlayers ); Engine.SetProgress(60); log("Creating island bumps..."); createBumps(stayClasses(clIsland, 2), scaleByMapSize(50, 400), 1, 8, 4, 0, 3); log("Paint seabed..."); paintTerrainBasedOnHeight(-20, -3, 3, tSeaDepths); log("Creating island metal mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), 0, [avoidClasses(clMetal, 50, clRock, 10), stayClasses(clIsland, 5)], 500, 1 ); log("Creating island stone mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock), 0, [avoidClasses(clMetal, 10, clRock, 50), stayClasses(clIsland, 5)], 500, 1 ); Engine.SetProgress(65); log("Creating island towers..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oTower, 1, 1, 0, 4)], true, clTower), 0, [avoidClasses(clMetal, 4, clRock, 4, clTower, 20), stayClasses(clIsland, 7)], 500, 1 ); log("Creating island outposts..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oOutpost, 1, 1, 0, 4)], true, clOutpost), 0, [avoidClasses(clMetal, 4, clRock, 4, clTower, 5, clOutpost, 20), stayClasses(clIsland, 7)], 500, 1 ); log("Creating metal mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), 0, [avoidClasses(clForest, 4, clBaseResource, 20, clMetal, 50, clRock, 20, clWater, 4, clHill, 4, clGauls, 5, clPath, 5)], 500, 1 ); log("Creating stone mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock), 0, [avoidClasses(clForest, 4, clBaseResource, 20, clMetal, 20, clRock, 50, clWater, 4, clHill, 4, clGauls, 5, clPath, 5)], 500, 1 ); log("Creating stone ruins..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneRuins, 1, 1, 0, 4)], true, clRock), 0, [avoidClasses(clForest, 2, clPlayer, 12, clMetal, 6, clRock, 25, clWater, 4, clHill, 4, clGauls, 5, clPath, 1)], 500, 1 ); Engine.SetProgress(70); log("Creating decoratives..."); for (let i = 0; i < 2; ++i) createDecoration( [ [new SimpleObject(aRock1, 1, 1, 0, 1)], [new SimpleObject(aRock2, 1, 1, 0, 1)], [new SimpleObject(aRock3, 1, 1, 0, 1)], [new SimpleObject(aRock4, 1, 1, 0, 1)], [new SimpleObject(aBush1, 1, 3, 0, 2)], [new SimpleObject(aBush2, 1, 2, 0, 1)], [new SimpleObject(aBush3, 1, 3, 0, 2)], [new SimpleObject(aBush4, 1, 2, 0, 1)], [new SimpleObject(aFerns, 2, 5, 2, 4)] ], [ scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(5, 80), scaleByMapSize(20, 80) ], i == 0 ? avoidClasses(clWater, 4, clForest, 1, clPlayer, 16, clRock, 4, clMetal, 4, clHill, 4, clGauls, 5, clPath, 1) : [stayClasses(clIsland, 4) , avoidClasses(clForest, 1, clRock, 4, clMetal, 4)] ); Engine.SetProgress(75); log("Creating fish..."); createFood( [ [new SimpleObject(oFish, 2, 3, 0, 2)] ], [ 20 * scaleByMapSize(5, 20) ], [avoidClasses(clIsland, 2, clFood, 10, clPath, 1), stayClasses(clWater, 5)], clFood ); Engine.SetProgress(80); log("Creating huntable animals..."); createFood( [ [new SimpleObject(oSheep, 5, 5, 0, 4)], [new SimpleObject(oGoat, 5, 5, 0, 4)], [new SimpleObject(oRabbit, 5, 8, 0, 4)], [new SimpleObject(oDeer, 4, 6, 0, 2)], [new SimpleObject(oHawk, 1, 1, 0, 4)] ], [ scaleByMapSize(5, 20), scaleByMapSize(5, 20), scaleByMapSize(5, 20), scaleByMapSize(5, 20), scaleByMapSize(5, 10) ], avoidClasses(clIsland, 2, clFood, 10, clWater, 5, clPlayer, 16, clHill, 2, clGauls, 5, clPath, 1), clFood); log("Creating violent animals..."); if (!isNomad()) createFood( [ [new SimpleObject(oWolf, 1, 3, 0, 4)], [new SimpleObject(oBoar, 1, 1, 0, 4)], [new SimpleObject(oBear, 1, 1, 0, 4)] ], [ scaleByMapSize(5, 20), scaleByMapSize(5, 20), scaleByMapSize(5, 20) ], avoidClasses(clIsland, 2, clFood, 10, clWater, 5, clPlayer, 24, clHill, 2, clGauls, 5, clPath, 1), clFood); Engine.SetProgress(85); log("Creating fruits..."); createFood( [ [new SimpleObject(oApple, 3, 5, 4, 7)], [new SimpleObject(oBerryBush, 4, 6, 0, 4)] ], [ scaleByMapSize(5, 20), scaleByMapSize(5, 20) ], avoidClasses(clWater, 5, clForest, 2, clPlayer, 16, clHill, 4, clFood, 10, clMetal, 4, clRock, 4, clGauls, 5, clPath, 1), clFood); Engine.SetProgress(90); createStragglerTrees( treeTypes, avoidClasses(clForest, 2, clWater, 8, clPlayer, 16, clMetal, 4, clRock, 4, clFood, 1, clHill, 2, clGauls, 5, clPath, 5), clForest, stragglerTrees); createStragglerTrees( treeTypes, [stayClasses(clIsland, 4), avoidClasses(clMetal, 4, clRock, 4, clTower, 4, clOutpost, 4)], clForest, stragglerTrees * 7); Engine.SetProgress(95); log("Creating animals on islands..."); createFood( [ [new SimpleObject(oSheep, 4, 6, 0, 4)], [new SimpleObject(oGoat, 4, 6, 0, 4)], [new SimpleObject(oRabbit, 5, 8, 0, 4)] ], [ 10 * scaleByMapSize(5, 20), 10 * scaleByMapSize(5, 20), 10 * scaleByMapSize(5, 20) ], [avoidClasses(clRock, 4, clMetal, 4, clFood, 3, clForest, 1, clOutpost, 2, clTower, 2), stayClasses(clIsland, 4)], clFood ); Engine.SetProgress(98); log("Creating treasures..."); for (let i = 0; i < randomTreasureCount; ++i) createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject(pickRandom(oTreasures), 1, 1, 0, 2)], true, clTreasure ), 0, avoidClasses(clForest, 1, clPlayer, 15, clHill, 1, clWater, 5, clFood, 1, clRock, 4, clMetal, 4, clTreasure, 10, clGauls, 5), 1, 50 ); log("Creating gallic decoratives..."); createDecoration( [ [new SimpleObject(aBucket, 1, 1, 0, 1)], [new SimpleObject(aBarrel, 1, 1, 0, 1)], [new SimpleObject(aTartan, 3, 3, 4, 4, Math.PI/4, Math.PI/2)], [new SimpleObject(aWheel, 2, 4, 1, 2)], [new SimpleObject(aWell, 1, 1, 0, 2)], [new SimpleObject(aWoodcord, 1, 2, 2, 2, Math.PI/2, Math.PI/2)] ], [ scaleByMapSize(2, 10), scaleByMapSize(2, 10), scaleByMapSize(2, 10), scaleByMapSize(2, 10), scaleByMapSize(3, 4), scaleByMapSize(2, 10) ], avoidClasses(clForest, 1, clPlayer, 10, clBaseResource, 5, clHill, 1, clFood, 1, clWater, 5, clRock, 4, clMetal, 4, clGauls, 5, clPath, 1) ); log("Creating spawn points for ships..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(triggerPointShipSpawn, 1, 1, 0, 0)], true, clShip), 0, [avoidClasses(clShip, 5, clIsland, 4), stayClasses(clWater, 10)], 10000, 1000 ); log("Creating patrol points for ships..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(triggerPointShipPatrol, 1, 1, 0, 0)], true, clShipPatrol), 0, [avoidClasses(clShipPatrol, 5, clIsland, 3), stayClasses(clWater, 4)], 10000, 1000 ); log("Creating ungarrison points for ships..."); for (let i = 0; i < 2; ++i) createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject( i == 0 ? triggerPointShipUnloadLeft : triggerPointShipUnloadRight, 1, 1, 0, 0)], true, clShoreUngarrisonPoint[i]), 0, [avoidClasses(clShoreUngarrisonPoint[i], 4), stayClasses(clShore[i], 0)], 20000, 1 ); 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); for (let i = 0; i < 2; ++i) { createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject( i == 0 ? triggerPointLandPatrolLeft : triggerPointLandPatrolRight, 1, 1, 0, 0)], true, clLandPatrolPoint[i]), 0, [ avoidClasses(clWater, 5, clForest, 3, clHill, 3, clFood, 1, clRock, 5, clMetal, 5, clPlayer, 10, clGauls, 5, clLandPatrolPoint[i], 5), stayClasses(clLand[i], 0) ], 10000, 100 ); if (gallicCC) createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject( i == 0 ? triggerPointCCAttackerPatrolLeft : triggerPointCCAttackerPatrolRight, 1, 1, 0, 0)], true, clCCAttackerPatrolPoint[i]), 0, [ // Don't avoid the forest, so that as many places as possible on the border are visited avoidClasses( clWater, 5, clHill, 3, clFood, 1, clRock, 4, clMetal, 4, clPlayer, 15, clGauls, 0, clCCAttackerPatrolPoint[i], 5, clMiddle, mapSize * 0.5 - 15), stayClasses(clLand[i], 0) ], 10000, 100 ); } log("Creating water logs..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aWaterLog, 1, 1, 0, 0)], true, clWaterLog), 0, [avoidClasses(clShip, 3, clIsland, 4), stayClasses(clWater, 4)], scaleByMapSize(15, 60), 100 ); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clMetal, 4, clRock, 4, clIsland, 4, clGauls, 20, clRitualPlace, 20, clForest, 1, clBaseResource, 4, clHill, 4, clFood, 2)); if (randBool(2/3)) { // Day setSkySet("cumulus"); setSunColor(0.9, 0.8, 0.5); setFogFactor(0.05); setFogThickness(0.25); setWaterColor(0.317, 0.396, 0.294); setWaterTint(0.439, 0.403, 0.262); setPPContrast(0.62); setPPSaturation(0.51); setPPBloom(0.12); } else { // Night setSkySet("dark"); setSunColor(0.4, 0.9, 1.2); setSunElevation(0.13499); setSunRotation(-2.5); setTerrainAmbientColor(0.25, 0.3, 0.45); setUnitsAmbientColor(0.3, 0.35, 0.5); setFogFactor(0.004); setFogThickness(0.25); setFogColor(0.35, 0.45, 0.5); setWaterColor(0.074, 0.101, 0.090); setWaterTint(0.129, 0.160, 0.137); } setPPEffect("hdr"); setWaterWaviness(2.0); setWaterType("lake"); setWaterMurkiness(0.97); setWaterHeight(21); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/deep_forest.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/deep_forest.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/deep_forest.js (revision 20971) @@ -1,202 +1,202 @@ Engine.LoadLibrary("rmgen"); var templateStone = "gaia/geology_stone_temperate"; var templateStoneMine = "gaia/geology_stonemine_temperate_quarry"; var templateMetalMine = "gaia/geology_metal_temperate_slabs"; var templateTemple = "other/unfinished_greek_temple"; var terrainPrimary = ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"]; var terrainWood = ['temp_grass_mossy|gaia/flora_tree_oak', 'temp_forestfloor_pine|gaia/flora_tree_pine', 'temp_mud_plants|gaia/flora_tree_dead', 'temp_plants_bog|gaia/flora_tree_oak_large', "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", 'temp_forestfloor_autumn|gaia/flora_tree_carob']; //'temp_forestfloor_autumn|gaia/flora_tree_fig' var terrainWoodBorder = ['temp_grass_plants|gaia/flora_tree_euro_beech', 'temp_grass_mossy|gaia/flora_tree_poplar', 'temp_grass_mossy|gaia/flora_tree_poplar_lombardy', 'temp_grass_long|gaia/flora_bush_temperate', 'temp_mud_plants|gaia/flora_bush_temperate', 'temp_mud_plants|gaia/flora_bush_badlands', 'temp_grass_long|gaia/flora_tree_apple', 'temp_grass_clovers|gaia/flora_bush_berry', 'temp_grass_clovers_2|gaia/flora_bush_grapes', 'temp_grass_plants|gaia/fauna_deer', "temp_grass_long_b|gaia/fauna_rabbit", "temp_grass_plants"]; var terrainBase = ["temp_dirt_gravel", "temp_grass_b"]; var terrainBaseBorder = ["temp_grass_b", "temp_grass_b", "temp_grass", "temp_grass_c", "temp_grass_mossy"]; var terrainBaseCenter = ['temp_dirt_gravel', 'temp_dirt_gravel', 'temp_grass_b']; var terrainPath = ['temp_road', "temp_road_overgrown", 'temp_grass_b']; var terrainHill = ["temp_highlands", "temp_highlands", "temp_highlands", "temp_dirt_gravel_b", "temp_cliff_a"]; var terrainHillBorder = ["temp_highlands", "temp_highlands", "temp_highlands", "temp_dirt_gravel_b", "temp_dirt_gravel_plants", "temp_highlands", "temp_highlands", "temp_highlands", "temp_dirt_gravel_b", "temp_dirt_gravel_plants", "temp_highlands", "temp_highlands", "temp_highlands", "temp_cliff_b", "temp_dirt_gravel_plants", "temp_highlands", "temp_highlands", "temp_highlands", "temp_cliff_b", "temp_dirt_gravel_plants", "temp_highlands|gaia/fauna_goat"]; var heightPath = -2; var heightLand = 0; var heightOffsetRandomPath = 1; InitMap(heightLand, terrainPrimary); var mapSize = getMapSize(); var mapRadius = mapSize/2; var mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clPath = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clBaseResource = createTileClass(); var numPlayers = getNumPlayers(); var baseRadius = 20; var minPlayerRadius = Math.min(mapRadius - 1.5 * baseRadius, 5/8 * mapRadius); var maxPlayerRadius = Math.min(mapRadius - baseRadius, 3/4 * mapRadius); var playerPosition = []; var playerAngle = []; var playerAngleStart = randomAngle(); var playerAngleAddAvrg = 2 * Math.PI / numPlayers; var playerAngleMaxOff = playerAngleAddAvrg/4; var radiusEC = Math.max(mapRadius/8, baseRadius/2); var resourceRadius = fractionToTiles(1/3); var resourcePerPlayer = [templateStone, templateMetalMine]; // For large maps there are memory errors with too many trees. A density of 256*192/mapArea works with 0 players. // Around each player there is an area without trees so with more players the max density can increase a bit. var maxTreeDensity = Math.min(256 * (192 + 8 * numPlayers) / Math.square(mapSize), 1); // Has to be tweeked but works ok var bushChance = 1/3; // 1 means 50% chance in deepest wood, 0.5 means 25% chance in deepest wood var playerIDs = sortAllPlayers(); for (var i=0; i < numPlayers; i++) { playerAngle[i] = (playerAngleStart + i * playerAngleAddAvrg + randFloat(0, playerAngleMaxOff)) % (2 * Math.PI); playerPosition[i] = Vector2D.add(mapCenter, new Vector2D(randFloat(minPlayerRadius, maxPlayerRadius), 0).rotate(-playerAngle[i]).round()); } Engine.SetProgress(10); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "BaseResourceClass": clBaseResource, // player class painted below "CityPatch": { "radius": 0.8 * baseRadius, "smoothness": 1/8, "painters": [ new LayeredPainter([terrainBaseBorder, terrainBase, terrainBaseCenter], [baseRadius/4, baseRadius/4]), paintClass(clPlayer) ] }, "Chicken": { }, "Berries": { "template": "gaia/flora_bush_grapes", "minCount": 2, "maxCount": 2, "distance": 12, "minDist": 5, "maxDist": 8 }, "Mines": { "types": [ { "template": templateMetalMine }, { "template": templateStoneMine } ], "minAngle": Math.PI / 2, "maxAngle": Math.PI }, "Trees": { "template": "gaia/flora_tree_oak_large", "count": 2 } }); Engine.SetProgress(10); log("Painting paths..."); var pathBlending = numPlayers <= 4; for (let i = 0; i < numPlayers + (pathBlending ? 1 : 0); ++i) for (let j = pathBlending ? 0 : i + 1; j < numPlayers + 1; ++j) { let pathStart = i < numPlayers ? playerPosition[i] : mapCenter; let pathEnd = j < numPlayers ? playerPosition[j] : mapCenter; createArea( new RandomPathPlacer(pathStart, pathEnd, 1.25, baseRadius / 2, pathBlending), [ new TerrainPainter(terrainPath), new SmoothElevationPainter(ELEVATION_SET, heightPath, 2, heightOffsetRandomPath), paintClass(clPath) ], avoidClasses(clHill, 0, clBaseResource, 4)); } Engine.SetProgress(50); log("Placing expansion resources..."); for (let i = 0; i < numPlayers; ++i) for (let rIndex = 0; rIndex < resourcePerPlayer.length; ++rIndex) { let angleDist = numPlayers > 1 ? (playerAngle[(i + 1) % numPlayers] - playerAngle[i] + 2 * Math.PI) % (2 * Math.PI) : 2 * Math.PI; // they are supposed to be in between players on the same radius 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()); createArea( - new ClumpPlacer(40, 1/2, 1/8, 1, position.x, position.y), + new ClumpPlacer(40, 1/2, 1/8, 1, position), [ new LayeredPainter([terrainHillBorder, terrainHill], [1]), new ElevationPainter(randFloat(1, 2)), paintClass(clHill) ]); } Engine.SetProgress(60); log("Placing temple..."); placeObject(mapCenter.x, mapCenter.y, templateTemple, 0, randomAngle()); addToClass(mapCenter.x, mapCenter.y, clBaseResource); log("Creating central mountain..."); createArea( - new ClumpPlacer(Math.square(radiusEC), 1/2, 1/8, 1, mapCenter.x, mapCenter.y), + new ClumpPlacer(Math.square(radiusEC), 1/2, 1/8, 1, mapCenter), [ new LayeredPainter([terrainHillBorder, terrainHill], [radiusEC/4]), new ElevationPainter(randFloat(1, 2)), paintClass(clHill) ]); // Woods and general hight map for (var x = 0; x < mapSize; x++) for (var z = 0;z < mapSize;z++) { let position = new Vector2D(x, z); // The 0.5 is a correction for the entities placed on the center of tiles var radius = mapCenter.distanceTo(Vector2D.add(position, new Vector2D(0.5, 0.5))); var minDistToSL = mapSize; for (var i=0; i < numPlayers; i++) minDistToSL = Math.min(minDistToSL, position.distanceTo(playerPosition[i])); // Woods tile based var tDensFactSL = Math.max(Math.min((minDistToSL - baseRadius) / baseRadius, 1), 0); var tDensFactRad = Math.abs((resourceRadius - radius) / resourceRadius); var tDensFactEC = Math.max(Math.min((radius - radiusEC) / radiusEC, 1), 0); var tDensActual = maxTreeDensity * tDensFactSL * tDensFactRad * tDensFactEC; if (randBool(tDensActual) && g_Map.validT(x, z)) { let border = tDensActual < randFloat(0, bushChance * maxTreeDensity); createArea( - new ClumpPlacer(1, 1, 1, 1, x, z), + new RectPlacer(position.x, position.y, position.x + 1, position.y + 1) [ new TerrainPainter(border ? terrainWoodBorder : terrainWood), new ElevationPainter(randFloat(0, 1)), paintClass(clForest) ], avoidClasses(clPath, 1, clHill, border ? 0 : 1)); } // General hight map var hVarMiddleHill = mapSize / 64 * (1 + Math.cos(3/2 * Math.PI * radius / mapRadius)); var hVarHills = 5 * (1 + Math.sin(x / 10) * Math.sin(z / 10)); g_Map.setHeight(position, getHeight(x, z) + hVarMiddleHill + hVarHills + 1); } Engine.SetProgress(95); placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clBaseResource, 4)); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/extinct_volcano.js (revision 20971) @@ -1,465 +1,465 @@ Engine.LoadLibrary("rmgen"); const tHillDark = "cliff volcanic light"; const tHillMedium1 = "ocean_rock_a"; const tHillMedium2 = "ocean_rock_b"; const tHillVeryDark = ["cliff volcanic coarse", "cave_walls"]; const tRoad = "road1"; const tRoadWild = "road1"; const tForestFloor1 = tHillMedium1; const tForestFloor2 = tHillMedium2; const tGrassA = "cliff volcanic light"; const tGrassB = "ocean_rock_a"; const tGrassPatchBlend = "temp_grass_long_b"; const tGrassPatch = ["temp_grass_d", "temp_grass_clovers"]; const tShoreBlend = "cliff volcanic light"; const tShore = "ocean_rock_a"; const tWater = "ocean_rock_b"; const oTree = "gaia/flora_tree_dead"; const oTree2 = "gaia/flora_tree_euro_beech"; const oTree3 = "gaia/flora_tree_oak"; const oTree4 = "gaia/flora_tree_oak_dead"; const oBush = "gaia/flora_bush_temperate"; const oFruitBush = "gaia/flora_bush_berry"; const oRabbit = "gaia/fauna_rabbit"; const oGoat = "gaia/fauna_goat"; const oBear = "gaia/fauna_bear"; const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; const oStoneSmall = "gaia/geology_stone_temperate"; const oMetalLarge = "gaia/geology_metal_temperate_slabs"; const oTower = "other/palisades_rocks_fort"; const aRockLarge = "actor|geology/stone_granite_med.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBushMedium = "actor|props/flora/bush_tempe_me.xml"; const aBushSmall = "actor|props/flora/bush_tempe_sm.xml"; const aGrass = "actor|props/flora/grass_soft_large_tall.xml"; const aGrassShort = "actor|props/flora/grass_soft_large.xml"; const aRain = "actor|particle/rain_shower.xml"; const pForestD = [ tForestFloor1 + TERRAIN_SEPARATOR + oTree, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor1 ]; const pForestP = [ tForestFloor1 + TERRAIN_SEPARATOR + oTree3, tForestFloor2 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 ]; var heightSeaGround = -4; var heightLand = 1; var heightHill = 18; var heightPlayerHill = 25; InitMap(heightLand, tHillMedium1); var numPlayers = getNumPlayers(); var mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clFood = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clGrass = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clBaseResource = createTileClass(); var clBumps = createTileClass(); var clTower = createTileClass(); var clRain = createTileClass(); var playerMountainSize = defaultPlayerBaseRadius(); var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); log("Creating CC mountains..."); if (!isNomad()) for (let i = 0; i < numPlayers; ++i) { // This one consists of many bumps, creating an omnidirectional ramp createMountain( heightPlayerHill, playerMountainSize, playerMountainSize, Math.floor(scaleByMapSize(4, 10)), undefined, playerPosition[i].x, playerPosition[i].y, tHillDark, clPlayer, 14); // Flatten the initial CC area createArea( - new ClumpPlacer(diskArea(playerMountainSize), 0.95, 0.6, 10, playerPosition[i].x,playerPosition[i].y), + new ClumpPlacer(diskArea(playerMountainSize), 0.95, 0.6, 10, playerPosition[i]), [ new LayeredPainter([tHillVeryDark, tHillMedium1], [playerMountainSize]), new SmoothElevationPainter(ELEVATION_SET, heightPlayerHill, playerMountainSize), paintClass(clPlayer) ]); } Engine.SetProgress(8); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], // PlayerTileClass already marked above "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oTree2 } // No decoratives }); Engine.SetProgress(15); createVolcano(mapCenter, clHill, tHillVeryDark, undefined, false, ELEVATION_SET); Engine.SetProgress(20); log("Creating lakes..."); createAreas( new ChainPlacer(5, 6, Math.floor(scaleByMapSize(10, 14)), 0.1), [ new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 3), paintClass(clWater) ], avoidClasses(clPlayer, 0, clHill, 2, clWater, 12), Math.round(scaleByMapSize(6, 12))); Engine.SetProgress(25); createBumps(avoidClasses(clPlayer, 0, clHill, 0), scaleByMapSize(50, 300), 1, 10, 3, 0, scaleByMapSize(4, 10)); paintTileClassBasedOnHeight(10, 100, 0, clBumps); Engine.SetProgress(30); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), [ new LayeredPainter([tHillDark, tHillDark, tHillDark], [2, 2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], avoidClasses(clPlayer, 0, clHill, 15, clWater, 2, clBaseResource, 2), scaleByMapSize(2, 8) * numPlayers); Engine.SetProgress(35); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(1200, 3000, 0.7); var types = [ [[tGrassB, tGrassA, pForestD], [tGrassB, pForestD]], [[tGrassB, tGrassA, pForestP], [tGrassB, pForestP]] ]; var size = forestTrees / (scaleByMapSize(4, 12) * numPlayers); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses( clPlayer, 4, clForest, 10, clHill, 0, clWater, 2), num); Engine.SetProgress(40); log("Creating hill patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) for (let type of [[tHillMedium1, tHillDark], [tHillDark, tHillMedium2], [tHillMedium1, tHillMedium2]]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter(type, [1]), paintClass(clGrass) ], avoidClasses( clWater, 3, clForest, 0, clHill, 0, clBumps, 0, clPlayer, 0), scaleByMapSize(20, 80)); Engine.SetProgress(45); log("Creating grass patches..."); createLayeredPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], [tGrassPatchBlend, tGrassPatch], [1], avoidClasses( clWater, 1, clForest, 0, clHill, 0, clGrass, 5, clBumps, 0, clPlayer, 0), scaleByMapSize(3, 8), clDirt); Engine.SetProgress(50); log("Creating stone mines..."); createObjectGroupsDeprecated( new SimpleGroup( [ new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4) ], true, clRock), 0, [ stayClasses(clBumps, 1), avoidClasses( clWater, 3, clForest, 1, clPlayer, 0, clRock, 15, clHill, 0) ], 100, 100); Engine.SetProgress(55); log("Creating small stone quarries..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), 0, [ stayClasses(clBumps, 1), avoidClasses( clWater, 3, clForest, 1, clPlayer, 0, clRock, 15, clHill, 0) ], 100, 100); Engine.SetProgress(60); log("Creating metal mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), 0, [ stayClasses(clBumps, 1), avoidClasses( clWater, 3, clForest, 1, clPlayer, 0, clMetal, 15, clRock, 10, clHill, 0) ], 100, 100); Engine.SetProgress(65); if (!isNomad()) { log("Creating towers..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oTower, 1, 1, 0, 4)], true, clTower), 0, [ stayClasses(clBumps, 3), avoidClasses( clMetal, 5, clRock, 5, clHill, 0, clTower, 60, clPlayer, 10, clForest, 2) ], 500, 1); } Engine.SetProgress(67); createDecoration( [ [ new SimpleObject(aGrassShort, 1, 2, 0, 1) ], [ new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5) ], [ new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2) ] ], [ scaleByMapSize(15, 200), scaleByMapSize(15, 200), scaleByMapSize(15, 200) ], [ stayClasses(clGrass, 0), avoidClasses( clWater, 0, clForest, 0, clPlayer, 0, clHill, 0) ]); Engine.SetProgress(70); createDecoration( [ [ new SimpleObject(aRockMedium, 1, 3, 0, 1) ], [ new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2) ] ], [ scaleByMapSize(15, 250), scaleByMapSize(15, 150) ], avoidClasses( clWater, 0, clForest, 0, clPlayer, 0, clHill, 0 )); Engine.SetProgress(75); createFood( [ [new SimpleObject(oRabbit, 5, 7, 2, 4)], [new SimpleObject(oGoat, 3, 5, 2, 4)] ], [ scaleByMapSize(1, 6) * numPlayers, scaleByMapSize(3, 10) * numPlayers ], [ avoidClasses( clWater, 1, clForest, 0, clPlayer, 0, clHill, 1, clFood, 20) ], clFood); Engine.SetProgress(78); createFood( [ [new SimpleObject(oBear, 1, 1, 0, 2)] ], [ 3 * numPlayers ], [ avoidClasses( clWater, 1, clForest, 0, clPlayer, 0, clHill, 1, clFood, 20 ), stayClasses(clForest, 2) ], clFood); Engine.SetProgress(81); createFood( [ [new SimpleObject(oFruitBush, 1, 2, 0, 4)] ], [ 3 * numPlayers ], [stayClasses(clGrass, 1), avoidClasses(clWater, 1, clForest, 0, clPlayer, 0, clHill, 1, clFood, 10)], clFood); Engine.SetProgress(85); createStragglerTrees( [oTree, oTree2, oTree3, oTree4, oBush], [ stayClasses(clGrass, 1), avoidClasses( clWater, 5, clForest, 1, clHill, 1, clPlayer, 0, clMetal, 1, clRock, 1) ], clForest, stragglerTrees); Engine.SetProgress(90); log("Creating straggler bushes..."); createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject(oBush, 1, 3, 0, 3)], true, clForest ), 0, [ stayClasses(clGrass, 3), avoidClasses( clWater, 1, clForest, 1, clPlayer, 0, clMetal, 1, clRock, 1) ], stragglerTrees); Engine.SetProgress(95); log("Creating rain drops..."); createObjectGroupsDeprecated( new SimpleGroup( [new SimpleObject(aRain, 2, 2, 1, 4)], true, clRain), 0, avoidClasses(clRain, 5), scaleByMapSize(80, 250)); Engine.SetProgress(95); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clHill, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("rain"); setWaterType("lake"); setWaterWaviness(2); setWaterColor(0.1, 0.13, 0.15); setWaterTint(0.058, 0.05, 0.035); setWaterMurkiness(0.9); setPPEffect("hdr"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/flood.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/flood.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/flood.js (revision 20971) @@ -1,312 +1,306 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tShore = g_Terrains.shore; const tWater = g_Terrains.water; var tHill = g_Terrains.hill; var tDirt = g_Terrains.dirt; if (currentBiome() == "temperate") { tDirt = ["medit_shrubs_a", "grass_field"]; tHill = ["grass_field", "peat_temp"]; } const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oFish = g_Gaia.fish; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oMetalLarge = g_Gaia.metalLarge; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightSeaGround = -2; const heightLand = 2; const shoreRadius = 6; InitMap(heightSeaGround, tWater); const clPlayer = createTileClass(); const clHill = createTileClass(); const clMountain = createTileClass(); const clForest = createTileClass(); const clDirt = createTileClass(); const clRock = createTileClass(); const clMetal = createTileClass(); const clFood = createTileClass(); const clBaseResource = createTileClass(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); log("Creating player islands...") var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.38)); for (let i = 0; i < numPlayers; ++i) createArea( - new ClumpPlacer( - 2 * diskArea(defaultPlayerBaseRadius()), - 0.8, - 0.1, - 10, - playerPosition[i].x, - playerPosition[i].y), + new ClumpPlacer(diskArea(1.4 * defaultPlayerBaseRadius()), 0.8, 0.1, 10, playerPosition[i]), [ new LayeredPainter([tShore, tMainTerrain], [shoreRadius]), new SmoothElevationPainter(ELEVATION_SET, heightLand, shoreRadius), paintClass(clHill) ]); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": false, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oTree2, "count": 50, "maxDist": 16, "maxDistGroup": 7 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(40); log("Creating central island..."); createArea( new ChainPlacer( 6, Math.floor(scaleByMapSize(10, 15)), Math.floor(scaleByMapSize(200, 300)), 1, mapCenter.x, mapCenter.y, 0, [Math.floor(mapSize * 0.01)]), [ new LayeredPainter([tShore, tMainTerrain], [shoreRadius, 100]), new SmoothElevationPainter(ELEVATION_SET, heightLand, shoreRadius), paintClass(clHill) ], avoidClasses(clPlayer, 40)); for (let m = 0; m < randIntInclusive(20, 34); ++m) { let elevRand = randIntInclusive(6, 20); createArea( new ChainPlacer( 7, 15, Math.floor(scaleByMapSize(15, 20)), 1, randIntExclusive(0, mapSize), randIntExclusive(0, mapSize), 0, [Math.floor(mapSize * 0.01)]), [ new LayeredPainter([tDirt, tHill], [Math.floor(elevRand / 3), 40]), new SmoothElevationPainter(ELEVATION_SET, elevRand, Math.floor(elevRand / 3)), paintClass(clHill) ], [avoidClasses(clBaseResource, 2, clPlayer, 40), stayClasses(clHill, 6)]); } for (let m = 0; m < randIntInclusive(8, 17); ++m) { let elevRand = randIntInclusive(15, 29); createArea( new ChainPlacer( 5, 8, Math.floor(scaleByMapSize(15, 20)), 1, randIntExclusive(0, mapSize), randIntExclusive(0, mapSize), 0, [Math.floor(mapSize * 0.01)]), [ new LayeredPainter([tCliff, tForestFloor2], [Math.floor(elevRand / 3), 40]), new SmoothElevationPainter(ELEVATION_MODIFY, elevRand, Math.floor(elevRand / 3)), paintClass(clMountain) ], [avoidClasses(clBaseResource, 2, clPlayer, 40), stayClasses(clHill, 6)]); } log("Creating center bounty..."); createObjectGroup( new SimpleGroup( [new SimpleObject(oMetalLarge, 3, 6, 25, Math.floor(mapSize * 0.25))], true, clBaseResource, mapCenter.x, mapCenter.y), 0, [avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]); createObjectGroup( new SimpleGroup( [new SimpleObject(oStoneLarge, 3, 6, 25, Math.floor(mapSize * 0.25))], true, clBaseResource, mapCenter.x, mapCenter.y), 0, [avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]); log("Creating fish..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood), 0, avoidClasses(clHill, 10, clFood, 20), 10 * numPlayers, 60); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(0.7)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], [avoidClasses(clPlayer, 25, clForest, 10, clBaseResource, 3, clMetal, 6, clRock, 3, clMountain, 2), stayClasses(clHill, 6)], clForest, forestTrees); let types = [oTree1, oTree2, oTree4, oTree3]; createStragglerTrees( types, [avoidClasses(clBaseResource, 2, clMetal, 6, clRock, 3, clMountain, 2, clPlayer, 25), stayClasses(clHill, 6)], clForest, stragglerTrees); Engine.SetProgress(65); log("Creating dirt patches..."); var numb = currentBiome() == "savanna" ? 3 : 1; for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), [ new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), paintClass(clDirt) ], avoidClasses(clForest, 0, clMountain, 0, clDirt, 5, clPlayer, 10), numb * scaleByMapSize(15, 45)); log("Painting shorelines..."); paintTerrainBasedOnHeight(1, heightLand, 0, tMainTerrain); paintTerrainBasedOnHeight(heightSeaGround, 1, 3, tTier1Terrain); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), new TerrainPainter(tTier4Terrain), avoidClasses(clForest, 0, clMountain, 0, clDirt, 5, clPlayer, 10), numb * scaleByMapSize(15, 45)); log("Creating food..."); createFood( [ [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)] ], [3 * numPlayers, 3 * numPlayers], [avoidClasses(clForest, 0, clPlayer, 20, clMountain, 1, clFood, 4, clRock, 6, clMetal, 6), stayClasses(clHill, 6)], clFood); Engine.SetProgress(75); createFood( [ [new SimpleObject(oFruitBush, 5, 7, 0, 4)] ], [3 * numPlayers], [avoidClasses(clForest, 0, clPlayer, 15, clMountain, 1, clFood, 4, clRock, 6, clMetal, 6), stayClasses(clHill, 6)], clFood); Engine.SetProgress(85); log("Creating decoration..."); var planetm = currentBiome() == "tropic" ? 8 : 1; createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 2, 15, 0, 1)], [new SimpleObject(aGrass, 2, 10, 0, 1.8), new SimpleObject(aGrassShort, 3, 10, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 5, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ scaleByMapSize(16, 262), scaleByMapSize(8, 131), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200) ], avoidClasses(clForest, 2, clPlayer, 20, clMountain, 5, clFood, 1, clBaseResource, 2)); log("Creating water forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(0.1)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], avoidClasses(clPlayer, 30, clHill, 10, clFood, 5), clForest, forestTrees); log("Creating small grass tufts..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1)]), 0, [avoidClasses(clMountain, 2, clPlayer, 2, clDirt, 0), stayClasses(clHill, 8)], planetm * scaleByMapSize(13, 200)); placePlayersNomad( clPlayer, new AndConstraint([ stayClasses(clHill, 2), avoidClasses(clMountain, 2, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)])); setSkySet(pickRandom(["cloudless", "cumulus", "overcast"])); setWaterMurkiness(0.4); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/gear.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/gear.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/gear.js (revision 20971) @@ -1,312 +1,312 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); TILE_CENTERED_HEIGHT_MAP = true; setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.mainTerrain; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tShore = g_Terrains.shore; const tWater = g_Terrains.water; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oFish = g_Gaia.fish; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; var heightSeaGround = -4; var heightShallow = -2; var heightLand = 3; var heightRing = 4; var heightHill = 20; InitMap(heightLand, tMainTerrain); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var radiusPlayers = fractionToTiles(0.35); var radiusCentralLake = fractionToTiles(0.27); var radiusCentralRingLand = fractionToTiles(0.21); var radiusCentralWaterRing = fractionToTiles(0.17); var radiusCentralIsland = fractionToTiles(0.14); var radiusCentralHill = fractionToTiles(0.12); var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(radiusPlayers); log("Determining number of rivers between players..."); var split = 1; if (mapSize == 128 && numPlayers <= 2) split = 2; else if (mapSize == 192 && numPlayers <= 3) split = 2; else if (mapSize == 256) { if (numPlayers <= 3) split = 3; else if (numPlayers == 4) split = 2; } else if (mapSize == 320) { if (numPlayers <= 3) split = 3; else if (numPlayers == 4) split = 2; } else if (mapSize == 384) { if (numPlayers <= 3) split = 4; else if (numPlayers == 4) split = 3; else if (numPlayers == 5) split = 2; } else if (mapSize == 448) { if (numPlayers <= 2) split = 5; else if (numPlayers <= 4) split = 4; else if (numPlayers == 5) split = 3; else if (numPlayers == 6) split = 2; } log("Creating big circular lake..."); createArea( - new ClumpPlacer(diskArea(radiusCentralLake), 1, 1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(radiusCentralLake), 1, 1, 10, mapCenter), new SmoothElevationPainter(ELEVATION_SET, heightShallow, 4)); for (let m = 0; m < numPlayers * split; ++m) { log("Creating rivers between players..."); let angle = startAngle + (m + 0.5) * 2 * Math.PI / (numPlayers * split); let position1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.15), 0).rotate(-angle)); let position2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.6), 0).rotate(-angle)); createArea( new PathPlacer(position1, position2, scaleByMapSize(14, 40), 0, scaleByMapSize(3, 9), 0.2, 0.05), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), avoidClasses(clPlayer, 5)); log("Create path from the island to the center..."); angle = startAngle + m * 2 * Math.PI / (numPlayers * split); position1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.05), 0).rotate(-angle)); position2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.49), 0).rotate(-angle)); createArea( new PathPlacer(position1, position2, scaleByMapSize(10, 40), 0, scaleByMapSize(3, 9), 0.2, 0.05), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4)); } log("Creating ring of land connecting players..."); createArea( - new ClumpPlacer(diskArea(radiusCentralRingLand), 1, 1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(radiusCentralRingLand), 1, 1, 10, mapCenter), new SmoothElevationPainter(ELEVATION_SET, heightRing, 4)); log("Creating ring of water separating the central hill from the ring..."); createArea( - new ClumpPlacer(diskArea(radiusCentralWaterRing), 1, 1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(radiusCentralWaterRing), 1, 1, 10, mapCenter), new SmoothElevationPainter(ELEVATION_SET, heightShallow, 3)); log("Creating central island..."); createArea( - new ClumpPlacer(diskArea(radiusCentralIsland), 1, 1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(radiusCentralIsland), 1, 1, 10, mapCenter), new SmoothElevationPainter(ELEVATION_SET, heightRing, 3)); log("Creating hill on the central island..."); createArea( - new ClumpPlacer(diskArea(radiusCentralHill), 1, 1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(radiusCentralHill), 1, 1, 10, mapCenter), new SmoothElevationPainter(ELEVATION_SET, heightHill, 8)); paintTerrainBasedOnHeight(-6, 1, 1, tWater); paintTerrainBasedOnHeight(1, 2, 1, tShore); paintTerrainBasedOnHeight(2, 21, 1, tMainTerrain); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "baseResourceConstraint": avoidClasses(clWater, 2), "Walls": "towers", "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oTree1, "count": 2 }, "Decoratives": { "template": aGrassShort } }); if (randBool()) createHills([tMainTerrain, tCliff, tHill], avoidClasses(clPlayer, 20, clHill, 15, clWater, 2), clHill, scaleByMapSize(1, 4) * numPlayers); else createMountains(tCliff, avoidClasses(clPlayer, 20, clHill, 15, clWater, 2), clHill, scaleByMapSize(1, 4) * numPlayers); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], avoidClasses(clPlayer, 20, clForest, 17, clHill, 0, clWater, 2), clForest, forestTrees); Engine.SetProgress(50); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tMainTerrain,tTier1Terrain],[tTier1Terrain,tTier2Terrain], [tTier2Terrain,tTier3Terrain]], [1,1], avoidClasses(clWater, 3, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], tTier4Terrain, avoidClasses(clWater, 3, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(55); log("Creating stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1), clMetal ); log("Creating fish..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFish, 1,1, 0,3)], true, clFood), 0, [stayClasses(clWater, 8), avoidClasses(clFood, 14)], scaleByMapSize(400, 2000), 100); Engine.SetProgress(65); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 1, 2, 0, 1)], [new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ scaleByMapSize(16, 262), scaleByMapSize(8, 131), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200) ], avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0)); Engine.SetProgress(70); createFood( [ [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)] ], [ 3 * numPlayers, 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 20), clFood); createFood( [ [new SimpleObject(oFruitBush, 5, 7, 0, 4)] ], [ 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), clFood); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], avoidClasses(clWater, 5, clForest, 7, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/harbor.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/harbor.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/harbor.js (revision 20971) @@ -1,418 +1,418 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen2"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); var heightSeaGround = -18; var heightLand = 2; var heightOffsetHarbor = -11; InitMap(heightLand, g_Terrains.mainTerrain); initTileClasses(); setFogFactor(0.04); createArea( new MapBoundsPlacer(), paintClass(g_TileClasses.land)); Engine.SetProgress(10); const mapSize = getMapSize(); const mapCenter = getMapCenter(); const startAngle = randomAngle(); const players = addBases("radial", fractionToTiles(0.38), fractionToTiles(0.05), startAngle); Engine.SetProgress(20); addCenterLake(); Engine.SetProgress(30); if (mapSize >= 192) { addHarbors(players); Engine.SetProgress(40); } addSpines(); Engine.SetProgress(50); addElements([ { "func": addFish, "avoid": [ g_TileClasses.fish, 12, g_TileClasses.hill, 8, g_TileClasses.mountain, 8, g_TileClasses.player, 8, g_TileClasses.spine, 4 ], "stay": [g_TileClasses.water, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": ["many"] } ]); addElements(shuffleArray([ { "func": addHills, "avoid": [ g_TileClasses.bluff, 5, g_TileClasses.hill, 15, g_TileClasses.mountain, 2, g_TileClasses.plateau, 5, g_TileClasses.player, 20, g_TileClasses.spine, 5, g_TileClasses.valley, 2, g_TileClasses.water, 2 ], "sizes": ["tiny", "small"], "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addMountains, "avoid": [ g_TileClasses.bluff, 20, g_TileClasses.mountain, 25, g_TileClasses.plateau, 20, g_TileClasses.player, 20, g_TileClasses.spine, 20, g_TileClasses.valley, 10, g_TileClasses.water, 15 ], "sizes": ["small"], "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addPlateaus, "avoid": [ g_TileClasses.bluff, 20, g_TileClasses.mountain, 25, g_TileClasses.plateau, 20, g_TileClasses.player, 40, g_TileClasses.spine, 20, g_TileClasses.valley, 10, g_TileClasses.water, 15 ], "sizes": ["small"], "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addBluffs, "baseHeight": heightLand, "avoid": [ g_TileClasses.bluff, 20, g_TileClasses.mountain, 25, g_TileClasses.plateau, 20, g_TileClasses.player, 40, g_TileClasses.spine, 20, g_TileClasses.valley, 10, g_TileClasses.water, 15 ], "sizes": ["normal"], "mixes": g_AllMixes, "amounts": g_AllAmounts } ])); Engine.SetProgress(60); addElements(shuffleArray([ { "func": addMetal, "avoid": [ g_TileClasses.berries, 5, g_TileClasses.bluff, 5, g_TileClasses.forest, 3, g_TileClasses.mountain, 2, g_TileClasses.plateau, 2, g_TileClasses.player, 30, g_TileClasses.rock, 10, g_TileClasses.spine, 5, g_TileClasses.metal, 20, g_TileClasses.water, 3 ], "sizes": ["normal"], "mixes": ["same"], "amounts": ["normal", "many"] }, { "func": addStone, "avoid": [ g_TileClasses.berries, 5, g_TileClasses.bluff, 5, g_TileClasses.forest, 3, g_TileClasses.mountain, 2, g_TileClasses.plateau, 2, g_TileClasses.player, 30, g_TileClasses.rock, 20, g_TileClasses.spine, 5, g_TileClasses.metal, 10, g_TileClasses.water, 3 ], "sizes": ["normal"], "mixes": ["same"], "amounts": ["normal", "many"] }, { "func": addForests, "avoid": [ g_TileClasses.berries, 5, g_TileClasses.bluff, 5, g_TileClasses.forest, 8, g_TileClasses.metal, 3, g_TileClasses.mountain, 5, g_TileClasses.plateau, 5, g_TileClasses.player, 20, g_TileClasses.rock, 3, g_TileClasses.spine, 5, g_TileClasses.water, 2 ], "sizes": ["normal"], "mixes": ["similar"], "amounts": ["many"] } ])); Engine.SetProgress(70); addElements(shuffleArray([ { "func": addBerries, "avoid": [ g_TileClasses.berries, 30, g_TileClasses.bluff, 5, g_TileClasses.forest, 5, g_TileClasses.metal, 10, g_TileClasses.mountain, 2, g_TileClasses.plateau, 2, g_TileClasses.player, 20, g_TileClasses.rock, 10, g_TileClasses.spine, 2, g_TileClasses.water, 3 ], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addAnimals, "avoid": [ g_TileClasses.animals, 20, g_TileClasses.bluff, 5, g_TileClasses.forest, 2, g_TileClasses.metal, 2, g_TileClasses.mountain, 1, g_TileClasses.plateau, 2, g_TileClasses.player, 20, g_TileClasses.rock, 2, g_TileClasses.spine, 2, g_TileClasses.water, 3 ], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addStragglerTrees, "avoid": [ g_TileClasses.berries, 5, g_TileClasses.bluff, 5, g_TileClasses.forest, 7, g_TileClasses.metal, 2, g_TileClasses.mountain, 1, g_TileClasses.plateau, 2, g_TileClasses.player, 12, g_TileClasses.rock, 2, g_TileClasses.spine, 2, g_TileClasses.water, 5 ], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": g_AllAmounts } ])); Engine.SetProgress(80); addElements([ { "func": addLayeredPatches, "avoid": [ g_TileClasses.bluff, 2, g_TileClasses.dirt, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.plateau, 2, g_TileClasses.player, 12, g_TileClasses.spine, 5, g_TileClasses.water, 3 ], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addDecoration, "avoid": [ g_TileClasses.bluff, 2, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.plateau, 2, g_TileClasses.player, 12, g_TileClasses.spine, 5, g_TileClasses.water, 3 ], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] } ]); Engine.SetProgress(90); placePlayersNomad( g_TileClasses.player, avoidClasses( g_TileClasses.bluff, 4, g_TileClasses.water, 4, g_TileClasses.spine, 4, g_TileClasses.plateau, 4, g_TileClasses.forest, 1, g_TileClasses.metal, 4, g_TileClasses.rock, 4, g_TileClasses.mountain, 4, g_TileClasses.animals, 2)); ExportMap(); function addCenterLake() { createArea( new ChainPlacer( 2, Math.floor(scaleByMapSize(2, 12)), Math.floor(scaleByMapSize(35, 160)), 1, mapCenter.x, mapCenter.y, 0, [Math.floor(fractionToTiles(0.2))]), [ new LayeredPainter( [ g_Terrains.shore, g_Terrains.water, g_Terrains.water ], [1, 100] ), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 10), paintClass(g_TileClasses.water) ], avoidClasses(g_TileClasses.player, 20) ); let fDist = 50; if (mapSize <= 192) fDist = 20; } function addHarbors(players) { for (let player of players) { let harborPosition = Vector2D.add(player.position, Vector2D.sub(mapCenter, player.position).div(2.5).round()); createArea( - new ClumpPlacer(1200, 0.5, 0.5, 1, harborPosition.x, harborPosition.y), + new ClumpPlacer(1200, 0.5, 0.5, 1, harborPosition), [ new LayeredPainter([g_Terrains.shore, g_Terrains.water], [2]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHarbor, 3), paintClass(g_TileClasses.water) ], avoidClasses( g_TileClasses.player, 15, g_TileClasses.hill, 1 ) ); } } function addSpines() { let smallSpines = mapSize <= 192; let spineSize = smallSpines ? 0.02 : 0.5; let spineTapering = smallSpines ?-0.1 : -1.4; let heightOffsetSpine = smallSpines ? 20 : 35; let numPlayers = getNumPlayers(); let spineTile = g_Terrains.dirt; if (currentBiome() == "snowy") spineTile = g_Terrains.tier1Terrain; if (currentBiome() == "alpine" || currentBiome() == "savanna") spineTile = g_Terrains.tier2Terrain; if (currentBiome() == "autumn") spineTile = g_Terrains.tier4Terrain; let split = 1; if (numPlayers <= 3 || mapSize >= 320 && numPlayers <= 4) split = 2; for (let i = 0; i < numPlayers * split; ++i) { let tang = startAngle + (i + 0.5) * 2 * Math.PI / (numPlayers * split); let start = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.12), 0).rotate(-tang)); let end = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.4), 0).rotate(-tang)); createArea( new PathPlacer(start, end, scaleByMapSize(14, spineSize), 0.6, 0.1, 0.4, spineTapering), [ new LayeredPainter([g_Terrains.cliff, spineTile], [3]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetSpine, 3), paintClass(g_TileClasses.spine) ], avoidClasses(g_TileClasses.player, 5) ); } addElements([ { "func": addDecoration, "avoid": [ g_TileClasses.bluff, 2, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12, g_TileClasses.water, 3 ], "stay": [g_TileClasses.spine, 5], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] } ]); addElements([ { "func": addProps, "avoid": [ g_TileClasses.forest, 2, g_TileClasses.player, 2, g_TileClasses.prop, 20, g_TileClasses.water, 3 ], "stay": [g_TileClasses.spine, 8], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["scarce"] } ]); } Index: ps/trunk/binaries/data/mods/public/maps/random/islands.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/islands.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/islands.js (revision 20971) @@ -1,364 +1,364 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); TILE_CENTERED_HEIGHT_MAP = true; setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.hill; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tShore = g_Terrains.shore; const tWater = g_Terrains.water; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oFish = g_Gaia.fish; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const oWoodTreasure = "gaia/special_treasure_wood"; const oDock = "skirmish/structures/default_dock"; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightSeaGround = -5; const heightLand = 3; const heightOffsetBump = 2; const heightHill = 18; InitMap(heightSeaGround, tWater); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); var playerIslandRadius = scaleByMapSize(20, 29); var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35)); if (!isNomad()) { log("Creating player islands and docks..."); for (let i = 0; i < numPlayers; i++) { createArea( - new ClumpPlacer(diskArea(playerIslandRadius), 0.8, 0.1, 10, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(diskArea(playerIslandRadius), 0.8, 0.1, 10, playerPosition[i]), [ new LayeredPainter([tMainTerrain , tMainTerrain, tMainTerrain], [1, 6]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), paintClass(clLand), paintClass(clPlayer) ]); let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , heightLand - 0.5, heightLand); placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI); } } log("Creating big islands..."); createAreas( new ChainPlacer( Math.floor(scaleByMapSize(4, 8)), Math.floor(scaleByMapSize(8, 14)), Math.floor(scaleByMapSize(25, 60)), 0.07), [ new LayeredPainter([tMainTerrain, tMainTerrain], [2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), paintClass(clLand) ], avoidClasses(clLand, scaleByMapSize(8, 12)), scaleByMapSize(4, 14)); log("Creating small islands..."); createAreas( new ChainPlacer( Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07), [ new LayeredPainter([tMainTerrain, tMainTerrain], [2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), paintClass(clLand) ], avoidClasses(clLand, scaleByMapSize(8, 12)), scaleByMapSize(6, 54)); paintTerrainBasedOnHeight(1, 3, 0, tShore); paintTerrainBasedOnHeight(-8, 1, 2, tWater); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], // PlayerTileClass marked above "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "radius": playerIslandRadius / 3, "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Treasures": { "types": [ { "template": oWoodTreasure, "count": 14 } ] }, "Trees": { "template": oTree1, "count": 5 }, "Decoratives": { "template": aGrassShort } }); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), [avoidClasses(clPlayer, 0), stayClasses(clLand, 3)], scaleByMapSize(20, 100)); log("Creating hills..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], [avoidClasses(clPlayer, 2, clHill, 15), stayClasses(clLand, 0)], scaleByMapSize(4, 13)); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); var types = [ [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] ]; if (currentBiome() != "savanna") { var size = forestTrees / (scaleByMapSize(3,6) * numPlayers); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), forestTrees / (num * Math.floor(scaleByMapSize(2, 5))), 0.5), [ new LayeredPainter(type, [2]), paintClass(clForest) ], [avoidClasses(clPlayer, 0, clForest, 10, clHill, 0), stayClasses(clLand, 6)], num); } Engine.SetProgress(50); log("Creating dirt patches..."); var numberOfPatches = scaleByMapSize(15, 45) * (currentBiome() == "savanna" ? 3 : 1); for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), [ new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), paintClass(clDirt) ], [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 6)], numberOfPatches); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), new TerrainPainter(tTier4Terrain), [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 6)], numberOfPatches); Engine.SetProgress(55); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 0, clRock, 10, clHill, 1), stayClasses(clLand, 5)], scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 0, clRock, 10, clHill, 1), stayClasses(clLand, 5)], scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 0, clMetal, 10, clRock, 5, clHill, 1), stayClasses(clLand, 5)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(65); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 5)], scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 5)], scaleByMapSize(8, 131), 50 ); Engine.SetProgress(70); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oMainHuntableAnimal, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 1, clFood, 20), stayClasses(clLand, 5)], 3 * numPlayers, 50 ); Engine.SetProgress(75); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oSecondaryHuntableAnimal, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 1, clFood, 20), stayClasses(clLand, 5)], 3 * numPlayers, 50 ); log("Creating fruit bush..."); group = new SimpleGroup( [new SimpleObject(oFruitBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 5)], randIntInclusive(1, 4) * numPlayers + 2, 50 ); log("Creating fish..."); group = new SimpleGroup( [new SimpleObject(oFish, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clLand, 4, clForest, 2, clPlayer, 2, clHill, 2, clFood, 20), 25 * numPlayers, 60 ); Engine.SetProgress(85); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], [avoidClasses(clForest, 1, clHill, 1, clPlayer, 0, clMetal, 6, clRock, 6), stayClasses(clLand, 6)], clForest, stragglerTrees); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 6)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 5)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(95); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 6)], planetm * scaleByMapSize(13, 200), 50 ); placePlayersNomad(clPlayer, [stayClasses(clLand, 4), avoidClasses(clHill, 2, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)]); setSkySet(pickRandom(["cirrus", "cumulus", "sunny"])); setSunRotation(randomAngle()); setSunElevation(randFloat(1/5, 1/3) * Math.PI); setWaterWaviness(2); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/lions_den.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/lions_den.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/lions_den.js (revision 20971) @@ -1,566 +1,566 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen2"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const topTerrain = g_Terrains.tier2Terrain; const heightValley = 0; const heightPath = 10; const heightDen = 15; const heightHill = 50; InitMap(heightHill, topTerrain); const mapCenter = getMapCenter(); const numPlayers = getNumPlayers(); const startAngle = randomAngle(); initTileClasses(["step"]); createArea( new MapBoundsPlacer(), paintClass(g_TileClasses.land)); Engine.SetProgress(10); addBases("radial", fractionToTiles(0.4), fractionToTiles(randFloat(0.05, 0.1)), startAngle); Engine.SetProgress(20); createSunkenTerrain(); Engine.SetProgress(30); addElements([ { "func": addLayeredPatches, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.dirt, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addLayeredPatches, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.dirt, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12 ], "stay": [g_TileClasses.settlement, 7], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addLayeredPatches, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.dirt, 5, g_TileClasses.forest, 2 ], "stay": [g_TileClasses.player, 1], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.forest, 2 ], "stay": [g_TileClasses.player, 1], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12, g_TileClasses.step, 2 ], "stay": [g_TileClasses.valley, 7], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12 ], "stay": [g_TileClasses.settlement, 7], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["normal"] }, { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.forest, 2, g_TileClasses.mountain, 2, g_TileClasses.player, 12 ], "stay": [g_TileClasses.step, 7], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["scarce"] } ]); Engine.SetProgress(40); addElements(shuffleArray([ { "func": addMetal, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 3, g_TileClasses.player, 30, g_TileClasses.rock, 10, g_TileClasses.metal, 20 ], "stay": [g_TileClasses.settlement, 7], "sizes": ["normal"], "mixes": ["same"], "amounts": ["tons"] }, { "func": addMetal, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 3, g_TileClasses.player, 10, g_TileClasses.rock, 10, g_TileClasses.metal, 20, g_TileClasses.mountain, 5, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": ["normal"], "mixes": ["same"], "amounts": g_AllAmounts }, { "func": addStone, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 3, g_TileClasses.player, 30, g_TileClasses.rock, 20, g_TileClasses.metal, 10 ], "stay": [g_TileClasses.settlement, 7], "sizes": ["normal"], "mixes": ["same"], "amounts": ["tons"] }, { "func": addStone, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 3, g_TileClasses.player, 10, g_TileClasses.rock, 20, g_TileClasses.metal, 10, g_TileClasses.mountain, 5, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": ["normal"], "mixes": ["same"], "amounts": g_AllAmounts }, { "func": addForests, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 18, g_TileClasses.metal, 3, g_TileClasses.player, 20, g_TileClasses.rock, 3 ], "stay": [g_TileClasses.settlement, 7], "sizes": ["normal", "big"], "mixes": ["same"], "amounts": ["tons"] }, { "func": addForests, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 3, g_TileClasses.forest, 18, g_TileClasses.metal, 3, g_TileClasses.mountain, 5, g_TileClasses.player, 5, g_TileClasses.rock, 3, g_TileClasses.step, 1 ], "stay": [g_TileClasses.valley, 7], "sizes": ["normal", "big"], "mixes": ["same"], "amounts": ["tons"] } ])); Engine.SetProgress(60); addElements(shuffleArray([ { "func": addBerries, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 30, g_TileClasses.forest, 5, g_TileClasses.metal, 10, g_TileClasses.player, 20, g_TileClasses.rock, 10 ], "stay": [g_TileClasses.settlement, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": ["tons"] }, { "func": addBerries, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 30, g_TileClasses.forest, 5, g_TileClasses.metal, 10, g_TileClasses.mountain, 5, g_TileClasses.player, 10, g_TileClasses.rock, 10, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addAnimals, "avoid": [ g_TileClasses.animals, 20, g_TileClasses.baseResource, 5, g_TileClasses.forest, 0, g_TileClasses.metal, 1, g_TileClasses.player, 20, g_TileClasses.rock, 1 ], "stay": [g_TileClasses.settlement, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": ["tons"] }, { "func": addAnimals, "avoid": [ g_TileClasses.animals, 20, g_TileClasses.baseResource, 5, g_TileClasses.forest, 0, g_TileClasses.metal, 1, g_TileClasses.mountain, 5, g_TileClasses.player, 10, g_TileClasses.rock, 1, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": g_AllAmounts }, { "func": addStragglerTrees, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 7, g_TileClasses.metal, 3, g_TileClasses.player, 12, g_TileClasses.rock, 3 ], "stay": [g_TileClasses.settlement, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": ["tons"] }, { "func": addStragglerTrees, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 7, g_TileClasses.metal, 3, g_TileClasses.mountain, 5, g_TileClasses.player, 10, g_TileClasses.rock, 3, g_TileClasses.step, 5 ], "stay": [g_TileClasses.valley, 7], "sizes": g_AllSizes, "mixes": g_AllMixes, "amounts": ["normal", "many", "tons"] }, { "func": addStragglerTrees, "avoid": [ g_TileClasses.player, 10, g_TileClasses.baseResource, 5, g_TileClasses.berries, 5, g_TileClasses.forest, 3, g_TileClasses.metal, 5, g_TileClasses.rock, 5 ], "stay": [g_TileClasses.player, 1], "sizes": ["huge"], "mixes": ["same"], "amounts": ["tons"] } ])); Engine.SetProgress(75); addElements([ { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.valley, 4, g_TileClasses.player, 4, g_TileClasses.settlement, 4, g_TileClasses.step, 4 ], "stay": [g_TileClasses.land, 2], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["tons"] } ]); Engine.SetProgress(80); addElements([ { "func": addProps, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.valley, 4, g_TileClasses.player, 4, g_TileClasses.settlement, 4, g_TileClasses.step, 4 ], "stay": [g_TileClasses.land, 2], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["scarce"] } ]); Engine.SetProgress(85); addElements([ { "func": addDecoration, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.player, 4, g_TileClasses.settlement, 4, g_TileClasses.step, 4 ], "stay": [g_TileClasses.mountain, 2], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["tons"] } ]); Engine.SetProgress(90); addElements([ { "func": addProps, "avoid": [ g_TileClasses.baseResource, 5, g_TileClasses.player, 4, g_TileClasses.settlement, 4, g_TileClasses.step, 4 ], "stay": [g_TileClasses.mountain, 2], "sizes": ["normal"], "mixes": ["normal"], "amounts": ["scarce"] } ]); Engine.SetProgress(95); placePlayersNomad( g_TileClasses.player, [ new HeightConstraint(heightValley, heightPath), avoidClasses( g_TileClasses.forest, 1, g_TileClasses.metal, 4, g_TileClasses.rock, 4, g_TileClasses.animals, 2) ]); ExportMap(); function createSunkenTerrain() { var base = g_Terrains.mainTerrain; var middle = g_Terrains.dirt; var lower = g_Terrains.tier2Terrain; var road = g_Terrains.road; if (currentBiome() == "snowy") { middle = g_Terrains.tier2Terrain; lower = g_Terrains.tier1Terrain; } if (currentBiome() == "alpine") { middle = g_Terrains.shore; lower = g_Terrains.tier4Terrain; } if (currentBiome() == "mediterranean") { middle = g_Terrains.tier1Terrain; lower = g_Terrains.forestFloor1; } if (currentBiome() == "savanna") { middle = g_Terrains.tier2Terrain; lower = g_Terrains.tier4Terrain; } if (currentBiome() == "tropic" || currentBiome() == "autumn") road = g_Terrains.roadWild; if (currentBiome() == "autumn") middle = g_Terrains.shore; var expSize = diskArea(fractionToTiles(0.14)) / numPlayers; var expDist = 0.1 + numPlayers / 200; var expAngle = 0.75; if (numPlayers <= 2) { expSize = diskArea(fractionToTiles(0.075)); expAngle = 0.72; } var nRoad = 0.44; var nExp = 0.425; if (numPlayers < 4) { nRoad = 0.42; nExp = 0.4; } log("Creating central valley..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.29)), 1, 1, 1, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.29)), 1, 1, 1, mapCenter), [ new LayeredPainter([g_Terrains.cliff, lower], [3]), new SmoothElevationPainter(ELEVATION_SET, heightValley, 3), paintClass(g_TileClasses.valley) ]); log("Creating central hill..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.21)), 1, 1, 1, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.21)), 1, 1, 1, mapCenter), [ new LayeredPainter([g_Terrains.cliff, topTerrain], [3]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 3), paintClass(g_TileClasses.mountain) ]); let getCoords = (distance, playerID, playerIDOffset) => { let angle = startAngle + (playerID + playerIDOffset) * 2 * Math.PI / numPlayers; return Vector2D.add(mapCenter, new Vector2D(fractionToTiles(distance), 0).rotate(-angle)).round(); }; for (let i = 0; i < numPlayers; ++i) { let playerPosition = getCoords(0.4, i, 0); log("Creating path from player to expansion..."); let expansionPosition = getCoords(expDist, i, expAngle); createArea( new PathPlacer(playerPosition, expansionPosition, 12, 0.7, 0.5, 0.1, -1), [ new LayeredPainter([g_Terrains.cliff, middle, road], [3, 4]), new SmoothElevationPainter(ELEVATION_SET, heightPath, 3), paintClass(g_TileClasses.step) ]); log("Creating path from player to the neighbor..."); for (let neighborOffset of [-0.5, 0.5]) { let neighborPosition = getCoords(nRoad, i, neighborOffset); let pathPosition = getCoords(0.47, i, 0); createArea( new PathPlacer(pathPosition, neighborPosition, 19, 0.4, 0.5, 0.1, -0.6), [ new LayeredPainter([g_Terrains.cliff, middle, road], [3, 6]), new SmoothElevationPainter(ELEVATION_SET, heightPath, 3), paintClass(g_TileClasses.step) ]); } log("Creating the den of the player..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.1)) / (isNomad() ? 2 : 1), 0.9, 0.3, 1, playerPosition.x, playerPosition.y), + new ClumpPlacer(diskArea(fractionToTiles(0.1)) / (isNomad() ? 2 : 1), 0.9, 0.3, 1, playerPosition), [ new LayeredPainter([g_Terrains.cliff, base], [3]), new SmoothElevationPainter(ELEVATION_SET, heightDen, 3), paintClass(g_TileClasses.valley) ]); log("Creating the expansion of the player..."); createArea( - new ClumpPlacer(expSize, 0.9, 0.3, 1, expansionPosition.x, expansionPosition.y), + new ClumpPlacer(expSize, 0.9, 0.3, 1, expansionPosition), [ new LayeredPainter([g_Terrains.cliff, base], [3]), new SmoothElevationPainter(ELEVATION_SET, heightDen, 3), paintClass(g_TileClasses.settlement) ], [avoidClasses(g_TileClasses.settlement, 2)]); } log("Creating the expansions between players after the paths were created..."); for (let i = 0; i < numPlayers; ++i) { let position = getCoords(nExp, i, 0.5); createArea( - new ClumpPlacer(expSize, 0.9, 0.3, 1, position.x, position.y), + new ClumpPlacer(expSize, 0.9, 0.3, 1, position), [ new LayeredPainter([g_Terrains.cliff, lower], [3]), new SmoothElevationPainter(ELEVATION_SET, heightValley, 3), paintClass(g_TileClasses.settlement) ]); } } Index: ps/trunk/binaries/data/mods/public/maps/random/lorraine_plain.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/lorraine_plain.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/lorraine_plain.js (revision 20971) @@ -1,321 +1,321 @@ Engine.LoadLibrary("rmgen"); const tPrimary = "temp_grass_long"; const tGrass = ["temp_grass", "temp_grass", "temp_grass_d"]; const tGrassPForest = "temp_plants_bog"; const tGrassDForest = "temp_plants_bog"; const tGrassA = "temp_grass_plants"; const tGrassB = "temp_plants_bog"; const tGrassC = "temp_mud_a"; const tRoad = "temp_road"; const tRoadWild = "temp_road_overgrown"; const tGrassPatchBlend = "temp_grass_long_b"; const tGrassPatch = ["temp_grass_d", "temp_grass_clovers"]; const tShore = "temp_plants_bog"; const tWater = "temp_mud_a"; const oBeech = "gaia/flora_tree_euro_beech"; const oOak = "gaia/flora_tree_oak"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oRabbit = "gaia/fauna_rabbit"; const oStoneLarge = "gaia/geology_stonemine_temperate_quarry"; const oStoneSmall = "gaia/geology_stone_temperate"; const oMetalLarge = "gaia/geology_metal_temperate_slabs"; const aGrass = "actor|props/flora/grass_soft_small_tall.xml"; const aGrassShort = "actor|props/flora/grass_soft_large.xml"; const aRockLarge = "actor|geology/stone_granite_med.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; const aLillies = "actor|props/flora/water_lillies.xml"; const aBushMedium = "actor|props/flora/bush_medit_me.xml"; const aBushSmall = "actor|props/flora/bush_medit_sm.xml"; const pForestB = [tGrassDForest + TERRAIN_SEPARATOR + oBeech, tGrassDForest]; const pForestO = [tGrassPForest + TERRAIN_SEPARATOR + oOak, tGrassPForest]; const pForestR = [tGrassDForest + TERRAIN_SEPARATOR + oBeech, tGrassDForest, tGrassDForest + TERRAIN_SEPARATOR + oOak, tGrassDForest, tGrassDForest, tGrassDForest]; const heightSeaGround = -4; const heightShallows = -2; const heightLand = 3; const heightOffsetBump = 2; InitMap(heightLand, tPrimary); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); const mapBounds = getMapBounds(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clShallow = createTileClass(); var shallowWidth = scaleByMapSize(8, 12); var startAngle = randomAngle(); placePlayerBases({ "PlayerPlacement": playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.5)), // PlayerTileClass marked below "BaseResourceClass": clBaseResource, "Walls": "towers", "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad, "painters": [ paintClass(clPlayer) ] }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oOak, "count": 3 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(20); var riverPositions = [ new Vector2D(mapBounds.left + 1, mapCenter.y), new Vector2D(mapBounds.right - 1, mapCenter.y) ].map(v => v.rotateAround(startAngle, mapCenter)); log("Creating the main river..."); createArea( new PathPlacer(riverPositions[0], riverPositions[1], scaleByMapSize(10, 20), 0.5, 3 * scaleByMapSize(1, 4), 0.1, 0.01), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), avoidClasses(clPlayer, 4)); Engine.SetProgress(25); log("Creating small puddles at the map border to ensure players being separated..."); for (let riverPosition of riverPositions) createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(5, 10))), 0.95, 0.6, 10, riverPosition.x, riverPosition.y), + new ClumpPlacer(diskArea(scaleByMapSize(5, 10)), 0.95, 0.6, 10, riverPosition), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 2), avoidClasses(clPlayer, 8)); Engine.SetProgress(30); log("Creating the shallows of the main river..."); for (let i = 0; i <= randIntInclusive(3, scaleByMapSize(4, 6)); ++i) { let location = fractionToTiles(randFloat(0.15, 0.85)); createPassage({ "start": new Vector2D(location, mapBounds.top).rotateAround(startAngle, mapCenter), "end": new Vector2D(location, mapBounds.bottom).rotateAround(startAngle, mapCenter), "startWidth": shallowWidth, "endWidth": shallowWidth, "smoothWidth": 2, "startHeight": heightShallows, "endHeight": heightShallows, "maxHeight": heightShallows, "tileClass": clShallow }); } Engine.SetProgress(35); createTributaryRivers( startAngle, randIntInclusive(9, scaleByMapSize(13, 21)), scaleByMapSize(10, 20), heightSeaGround, [-6, -1.5], Math.PI / 5, clWater, clShallow, avoidClasses(clPlayer, 3, clBaseResource, 4)); Engine.SetProgress(40); paintTerrainBasedOnHeight(-5, 1, 1, tWater); paintTerrainBasedOnHeight(1, 2, 1, pForestR); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); Engine.SetProgress(50); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), avoidClasses(clWater, 2, clPlayer, 15), scaleByMapSize(100, 200) ); Engine.SetProgress(55); var [forestTrees, stragglerTrees] = getTreeCounts(500, 2500, 0.7); createForests( [tGrass, tGrassDForest, tGrassPForest, pForestB, pForestO], avoidClasses(clPlayer, 15, clWater, 3, clForest, 16, clHill, 1), clForest, forestTrees); Engine.SetProgress(70); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), [ new LayeredPainter([[tGrass,tGrassA], tGrassB, [tGrassB,tGrassC]], [1, 1]), paintClass(clDirt) ], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45) ); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), new LayeredPainter([tGrassPatchBlend, tGrassPatch], [1]), avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45) ); Engine.SetProgress(80); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clWater, 0, clForest, 1, clPlayer, 15, clRock, 10, clHill, 1)], scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clWater, 0, clForest, 1, clPlayer, 15, clRock, 10, clHill, 1)], scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clWater, 0, clForest, 1, clPlayer, 15, clMetal, 10, clRock, 5, clHill, 1)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(86); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(8, 131), 50 ); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 15, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); log("Creating rabbits..."); group = new SimpleGroup( [new SimpleObject(oRabbit, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 15, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 15, clHill, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50 ); createStragglerTrees( [oOak, oBeech], avoidClasses(clWater, 1, clForest, 7, clHill, 1, clPlayer, 5, clMetal, 6, clRock, 6), clForest, stragglerTrees); log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), scaleByMapSize(13, 200) ); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), scaleByMapSize(13, 200) ); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1), scaleByMapSize(13, 200), 50 ); log("Creating shallow flora..."); group = new SimpleGroup( [new SimpleObject(aLillies, 1,2, 0,2), new SimpleObject(aReeds, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, stayClasses(clShallow, 1), 60 * scaleByMapSize(13, 200), 80 ); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("cirrus"); setWaterColor(0.1,0.212,0.422); setWaterTint(0.3,0.1,0.949); setWaterWaviness(3.0); setWaterType("lake"); setWaterMurkiness(0.80); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/migration.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/migration.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/migration.js (revision 20971) @@ -1,372 +1,372 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.hill; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tShore = g_Terrains.shore; const tWater = g_Terrains.water; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oFish = g_Gaia.fish; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const oWoodTreasure = "gaia/special_treasure_wood"; const oDock = "skirmish/structures/default_dock"; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightSeaGround = -5; const heightLand = 3; const heightHill = 18; const heightOffsetBump = 2; InitMap(heightSeaGround, tWater); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); var clIsland = createTileClass(); var startAngle = randomAngle(); var playerIDs = sortAllPlayers(); var [playerPosition, playerAngle] = playerPlacementCustomAngle( fractionToTiles(0.35), mapCenter, i => startAngle - Math.PI * (i + 1) / (numPlayers + 1)); log("Creating player islands and docks..."); for (let i = 0; i < numPlayers; ++i) { createArea( - new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.8, 0.1, 10, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.8, 0.1, 10, playerPosition[i]), [ new LayeredPainter([tWater, tShore, tMainTerrain], [1, 4]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), paintClass(clIsland), paintClass(isNomad() ? clLand : clPlayer) ]); if (isNomad()) continue; let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 2.6, 3); placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI); } Engine.SetProgress(10); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "Walls": false, // No city patch "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Treasures": { "types": [ { "template": oWoodTreasure, "count": 14 } ] }, "Trees": { "template": oTree1, "count": scaleByMapSize(12, 30) }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(15); log("Create the continent body..."); var continentPosition = Vector2D.add(mapCenter, new Vector2D(0, fractionToTiles(0.38)).rotate(-startAngle)).round() createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.4)), 0.8, 0.08, 10, continentPosition.x, continentPosition.y), + new ClumpPlacer(diskArea(fractionToTiles(0.4)), 0.8, 0.08, 10, continentPosition), [ new LayeredPainter([tWater, tShore, tMainTerrain], [4, 2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), paintClass(clLand) ], avoidClasses(clIsland, 8)); Engine.SetProgress(20); log("Creating shore jaggedness..."); createAreas( new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, 1), [ new LayeredPainter([tMainTerrain, tMainTerrain], [2]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), paintClass(clLand) ], [ borderClasses(clLand, 6, 3), avoidClasses(clIsland, 8) ], scaleByMapSize(2, 15) * 20, 150); paintTerrainBasedOnHeight(1, 3, 0, tShore); paintTerrainBasedOnHeight(-8, 1, 2, tWater); Engine.SetProgress(25); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), [avoidClasses(clIsland, 10), stayClasses(clLand, 3)], scaleByMapSize(100, 200) ); Engine.SetProgress(30); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], [avoidClasses(clIsland, 10, clHill, 15), stayClasses(clLand, 7)], scaleByMapSize(1, 4) * numPlayers ); Engine.SetProgress(34); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); var types = [ [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] ]; var size = forestTrees / (scaleByMapSize(2,8) * numPlayers) * (currentBiome() == "savanna" ? 2 : 1); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], [avoidClasses(clPlayer, 6, clForest, 10, clHill, 0), stayClasses(clLand, 7)], num); Engine.SetProgress(38); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter( [[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), paintClass(clDirt) ], [ avoidClasses( clForest, 0, clHill, 0, clDirt, 5, clIsland, 0), stayClasses(clLand, 7) ], scaleByMapSize(15, 45)); Engine.SetProgress(42); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tTier4Terrain), [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clIsland, 0), stayClasses(clLand, 7)], scaleByMapSize(15, 45)); Engine.SetProgress(46); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 7)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(50); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 7)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(54); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1), stayClasses(clLand, 7)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(58); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 6)], scaleByMapSize(16, 262), 50 ); Engine.SetProgress(62); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 6)], scaleByMapSize(8, 131), 50 ); Engine.SetProgress(66); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oMainHuntableAnimal, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), stayClasses(clLand, 7)], 3 * numPlayers, 50 ); Engine.SetProgress(70); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oSecondaryHuntableAnimal, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), stayClasses(clLand, 7)], 3 * numPlayers, 50 ); Engine.SetProgress(74); log("Creating fruit bush..."); group = new SimpleGroup( [new SimpleObject(oFruitBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 7)], randIntInclusive(1, 4) * numPlayers + 2, 50 ); Engine.SetProgress(78); log("Creating fish..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFish, 2,3, 0,2)], true, clFood), 0, avoidClasses(clLand, 2, clPlayer, 2, clHill, 0, clFood, 20), 25 * numPlayers, 60 ); Engine.SetProgress(82); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], [avoidClasses(clForest, 1, clHill, 1, clPlayer, 9, clMetal, 6, clRock, 6), stayClasses(clLand, 9)], clForest, stragglerTrees); Engine.SetProgress(86); var planetm = currentBiome() == "tropic" ? 8 : 1; log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 6)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 6)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(94); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clHill, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 6)], planetm * scaleByMapSize(13, 200), 50 ); Engine.SetProgress(98); setSkySet(pickRandom(["cirrus", "cumulus", "sunny"])); setSunRotation(randomAngle()); setSunElevation(randFloat(1/5, 1/3) * Math.PI); setWaterWaviness(2); placePlayersNomad(clPlayer, [stayClasses(clIsland, 4), avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)]); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/neareastern_badlands.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/neareastern_badlands.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/neareastern_badlands.js (revision 20971) @@ -1,367 +1,361 @@ Engine.LoadLibrary("rmgen"); const tPrimary = ["desert_sand_smooth", "desert_sand_smooth_b"]; const tCity = "desert_city_tile"; const tCityPlaza = "desert_city_tile_plaza"; const tSand = "desert_dirt_rough"; const tDunes = "desert_sand_dunes_100"; const tFineSand = "desert_sand_smooth"; const tCliff = ["desert_cliff_badlands", "desert_cliff_badlands_2"]; const tForestFloor = "desert_forestfloor_palms"; const tGrass = "desert_grass_a"; const tGrassSand25 = "desert_grass_a_stones"; const tDirt = "desert_dirt_rough"; const tShore = "desert_shore_stones"; const tWaterDeep = "desert_shore_stones_wet"; const oBerryBush = "gaia/flora_bush_grapes"; const oCamel = "gaia/fauna_camel"; const oFish = "gaia/fauna_fish"; const oGazelle = "gaia/fauna_gazelle"; const oGiraffe = "gaia/fauna_giraffe"; const oGoat = "gaia/fauna_goat"; const oWildebeest = "gaia/fauna_wildebeest"; const oStoneLarge = "gaia/geology_stonemine_desert_badlands_quarry"; const oStoneSmall = "gaia/geology_stone_desert_small"; const oMetalLarge = "gaia/geology_metal_desert_slabs"; const oDatePalm = "gaia/flora_tree_date_palm"; const oSDatePalm = "gaia/flora_tree_senegal_date_palm"; const aBush1 = "actor|props/flora/bush_desert_a.xml"; const aBush2 = "actor|props/flora/bush_desert_dry_a.xml"; const aBush3 = "actor|props/flora/bush_dry_a.xml"; const aBush4 = "actor|props/flora/plant_desert_a.xml"; const aBushes = [aBush1, aBush2, aBush3, aBush4]; const aDecorativeRock = "actor|geology/stone_desert_med.xml"; const pForest = [tForestFloor + TERRAIN_SEPARATOR + oDatePalm, tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, tForestFloor]; const pForestOasis = [tGrass + TERRAIN_SEPARATOR + oDatePalm, tGrass + TERRAIN_SEPARATOR + oSDatePalm, tGrass]; const heightLand = 10; const heightOffsetOasis = -11; const heightOffsetHill1 = 16; const heightOffsetHill2 = 16; const heightOffsetHill3 = 16; const heightOffsetBump = 2; InitMap(heightLand, tPrimary); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill1 = createTileClass(); var clOasis = createTileClass(); var clForest = createTileClass(); var clPatch = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var oasisRadius = scaleByMapSize(14, 40); var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); if (!isNomad()) for (let i = 0; i < numPlayers; ++i) createArea( - new ClumpPlacer( - diskArea(defaultPlayerBaseRadius()), - 0.9, - 0.5, - 10, - playerPosition[i].x, - playerPosition[i].y), + new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.9, 0.5, 10, playerPosition[i]), paintClass(clPlayer)); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tCity, "innerTerrain": tCityPlaza, "width": 3, "radius": 10 }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oDatePalm } // No decoratives }); Engine.SetProgress(10); log("Creating dune patches..."); createAreas( new ClumpPlacer(scaleByMapSize(40, 150), 0.2, 0.1, 0), [ new TerrainPainter(tDunes), paintClass(clPatch) ], avoidClasses(clPatch, 2, clPlayer, 0), scaleByMapSize(5, 20)); Engine.SetProgress(15); log("Creating sand patches..."); createAreas( new ClumpPlacer(scaleByMapSize(25, 100), 0.2, 0.1, 0), [ new TerrainPainter([tSand, tFineSand]), paintClass(clPatch) ], avoidClasses(clPatch, 2, clPlayer, 0), scaleByMapSize(15, 50)); Engine.SetProgress(20); log("Creating dirt patches..."); createAreas( new ClumpPlacer(scaleByMapSize(25, 100), 0.2, 0.1, 0), [ new TerrainPainter([tDirt]), paintClass(clPatch) ], avoidClasses(clPatch, 2, clPlayer, 0), scaleByMapSize(15, 50)); Engine.SetProgress(25); log("Creating oasis..."); createArea( - new ClumpPlacer(diskArea(oasisRadius), 0.6, 0.15, 0, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(oasisRadius), 0.6, 0.15, 0, mapCenter), [ new LayeredPainter([[tSand, pForest], [tGrassSand25, pForestOasis], tGrassSand25, tShore, tWaterDeep], [2, 3, 1, 1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetOasis, 8), paintClass(clOasis) ]); Engine.SetProgress(30); log("Creating oasis wildlife..."); var num = Math.round(Math.PI * oasisRadius / 8); var constraint = new AndConstraint([borderClasses(clOasis, 0, 3), avoidClasses(clOasis, 0)]); var halfSize = mapSize/2; for (var i = 0; i < num; ++i) { var r = 0; var angle = 2 * Math.PI / num * i; do { // Work outward until constraint met var gx = Math.round(halfSize + r * Math.cos(angle)); var gz = Math.round(halfSize + r * Math.sin(angle)); ++r; } while (!constraint.allows(gx,gz) && r < halfSize); createObjectGroup( new RandomGroup( [ new SimpleObject(oGiraffe, 2,4, 0,3), new SimpleObject(oWildebeest, 3,5, 0,3), new SimpleObject(oGazelle, 5,7, 0,3) ], true, clFood, gx, gz), 0); } constraint = new AndConstraint([borderClasses(clOasis, 15, 0), avoidClasses(clFood, 5)]); num = Math.round(Math.PI * oasisRadius / 16); for (var i = 0; i < num; ++i) { var r = 0; var angle = 2 * Math.PI / num * i; do { // Work outward until constraint met var gx = Math.round(halfSize + r * Math.cos(angle)); var gz = Math.round(halfSize + r * Math.sin(angle)); ++r; } while (!constraint.allows(gx,gz) && r < halfSize); group = new SimpleGroup( [new SimpleObject(oFish, 1,1, 0,1)], true, clFood, gx, gz ); createObjectGroup(group, 0); } Engine.SetProgress(35); log("Creating level 1 hills..."); var hillAreas = createAreas( new ClumpPlacer(scaleByMapSize(50,300), 0.25, 0.1, 0.5), [ new LayeredPainter([tCliff, tSand], [1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill1, 1), paintClass(clHill1) ], avoidClasses(clOasis, 3, clPlayer, 0, clHill1, 10), scaleByMapSize(10,20), 100 ); Engine.SetProgress(40); log("Creating small level 1 hills..."); hillAreas = hillAreas.concat( createAreas( new ClumpPlacer(scaleByMapSize(25,150), 0.25, 0.1, 0.5), [ new LayeredPainter([tCliff, tSand], [1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill2, 1), paintClass(clHill1) ], avoidClasses(clOasis, 3, clPlayer, 0, clHill1, 3), scaleByMapSize(15,25), 100)); Engine.SetProgress(45); log("Creating decorative rocks..."); createObjectGroupsByAreasDeprecated( new SimpleGroup( [new RandomObject([aDecorativeRock, aBush2, aBush3], 3, 8, 0, 2)], true), 0, borderClasses(clHill1, 0, 3), scaleByMapSize(40,200), 50, hillAreas); Engine.SetProgress(50); log("Creating level 2 hills..."); createAreasInAreas( new ClumpPlacer(scaleByMapSize(25, 150), 0.25, 0.1, 0), [ new LayeredPainter([tCliff, tSand], [1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill2, 1) ], [stayClasses(clHill1, 0)], scaleByMapSize(15, 25), 50, hillAreas); Engine.SetProgress(55); log("Creating level 3 hills..."); createAreas( new ClumpPlacer(scaleByMapSize(12, 75), 0.25, 0.1, 0), [ new LayeredPainter([tCliff, tSand], [1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill3, 1) ], [stayClasses(clHill1, 0)], scaleByMapSize(15,25), 50 ); Engine.SetProgress(60); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 0), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), avoidClasses(clOasis, 0, clPlayer, 0, clHill1, 2), scaleByMapSize(100, 200) ); Engine.SetProgress(65); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(500, 2500, 0.5); var num = scaleByMapSize(10,30); createAreas( new ClumpPlacer(forestTrees / num, 0.15, 0.1, 0.5), [ new TerrainPainter([tSand, pForest]), paintClass(clForest) ], avoidClasses(clPlayer, 1, clOasis, 10, clForest, 10, clHill1, 1), num, 50); Engine.SetProgress(70); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4), new RandomObject(aBushes, 2, 4, 0, 2)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clOasis, 1, clForest, 1, clPlayer, 10, clRock, 10, clHill1, 1)], scaleByMapSize(4,16), 100 ); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3), new RandomObject(aBushes, 2,4, 0,2)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clOasis, 1, clForest, 1, clPlayer, 10, clRock, 10, clHill1, 1)], scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4), new RandomObject(aBushes, 2,4, 0,2)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clOasis, 1, clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill1, 1)], scaleByMapSize(4,16), 100 ); Engine.SetProgress(80); log("Creating gazelles..."); group = new SimpleGroup([new SimpleObject(oGazelle, 5,7, 0,4)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 1, clForest, 0, clPlayer, 5, clHill1, 1, clFood, 10), scaleByMapSize(5,20), 50 ); log("Creating goats..."); group = new SimpleGroup([new SimpleObject(oGoat, 2,4, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 1, clForest, 0, clPlayer, 5, clHill1, 1, clFood, 10), scaleByMapSize(5,20), 50 ); log("Creating camels..."); group = new SimpleGroup([new SimpleObject(oCamel, 2,4, 0,2)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 1, clForest, 0, clPlayer, 5, clHill1, 1, clFood, 10), scaleByMapSize(5,20), 50 ); Engine.SetProgress(85); createStragglerTrees( [oDatePalm, oSDatePalm], avoidClasses(clOasis, 1, clForest, 0, clHill1, 1, clPlayer, 4, clMetal, 6, clRock, 6), clForest, stragglerTrees); Engine.SetProgress(90); log("Creating bushes..."); group = new SimpleGroup([new RandomObject(aBushes, 2,3, 0,2)]); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 1, clHill1, 1, clPlayer, 0, clForest, 0), scaleByMapSize(16, 262) ); log("Creating more decorative rocks..."); group = new SimpleGroup([new SimpleObject(aDecorativeRock, 1,2, 0,2)]); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 1, clHill1, 1, clPlayer, 0, clForest, 0), scaleByMapSize(16, 262) ); placePlayersNomad(clPlayer, avoidClasses(clOasis, 4, clForest, 1, clMetal, 4, clRock, 4, clHill1, 4, clFood, 2)); setWaterColor(0, 0.227, 0.843); setWaterTint(0, 0.545, 0.859); setWaterWaviness(1.0); setWaterType("clap"); setWaterMurkiness(0.75); setWaterHeight(20); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/oasis.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/oasis.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/oasis.js (revision 20971) @@ -1,333 +1,333 @@ Engine.LoadLibrary("rmgen"); const tSand = ["desert_sand_dunes_100", "desert_dirt_cracks","desert_sand_smooth", "desert_dirt_rough", "desert_dirt_rough_2", "desert_sand_smooth"]; const tDune = ["desert_sand_dunes_50"]; const tForestFloor = "desert_forestfloor_palms"; const tDirt = ["desert_dirt_rough","desert_dirt_rough","desert_dirt_rough", "desert_dirt_rough_2", "desert_dirt_rocks_2"]; const tRoad = "desert_city_tile";; const tRoadWild = "desert_city_tile";; const tShore = "dirta"; const tWater = "desert_sand_wet"; const ePalmShort = "gaia/flora_tree_cretan_date_palm_short"; const ePalmTall = "gaia/flora_tree_cretan_date_palm_tall"; const eBush = "gaia/flora_bush_grapes"; const eCamel = "gaia/fauna_camel"; const eGazelle = "gaia/fauna_gazelle"; const eLion = "gaia/fauna_lion"; const eLioness = "gaia/fauna_lioness"; const eStoneMine = "gaia/geology_stonemine_desert_quarry"; const eMetalMine = "gaia/geology_metal_desert_slabs"; const aFlower1 = "actor|props/flora/decals_flowers_daisies.xml"; const aWaterFlower = "actor|props/flora/water_lillies.xml"; const aReedsA = "actor|props/flora/reeds_pond_lush_a.xml"; const aReedsB = "actor|props/flora/reeds_pond_lush_b.xml"; const aRock = "actor|geology/stone_desert_med.xml"; const aBushA = "actor|props/flora/bush_desert_dry_a.xml"; const aBushB = "actor|props/flora/bush_desert_dry_a.xml"; const aSand = "actor|particle/blowing_sand.xml"; const pForestMain = [tForestFloor + TERRAIN_SEPARATOR + ePalmShort, tForestFloor + TERRAIN_SEPARATOR + ePalmTall, tForestFloor]; const pOasisForestLight = [tForestFloor + TERRAIN_SEPARATOR + ePalmShort, tForestFloor + TERRAIN_SEPARATOR + ePalmTall, tForestFloor,tForestFloor,tForestFloor ,tForestFloor,tForestFloor,tForestFloor,tForestFloor]; const heightSeaGround = -3; const heightLand = 1; const heightOasisPath = 4; const heightOffsetBump = 4; const heightOffsetDune = 18; InitMap(heightLand, tSand); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clOasis = createTileClass(); var clPassage = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var waterRadius = scaleByMapSize(7, 50) var shoreDistance = scaleByMapSize(4, 10); var forestDistance = scaleByMapSize(6, 20); var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); log("Creating small oasis near the players...") var forestDist = 1.2 * defaultPlayerBaseRadius(); for (let i = 0; i < numPlayers; ++i) { // Create starting batches of wood - let forestX = 0; - let forestY = 0; - let forestAngle = 0 + let forestPosition; + let forestAngle; do { forestAngle = Math.PI / 3 * randFloat(1, 2); - forestX = playerPosition[i].x + Math.round(forestDist * Math.cos(forestAngle)); - forestY = playerPosition[i].y + Math.round(forestDist * Math.sin(forestAngle)); + forestPosition = Vector2D.add(playerPosition[i], new Vector2D(forestDist, 0).rotate(-forestAngle)); } while ( !createArea( - new ClumpPlacer(70, 1, 0.5, 10, forestX, forestY), + new ClumpPlacer(70, 1, 0.5, 10, forestPosition), [ new LayeredPainter([tForestFloor, pForestMain], [0]), paintClass(clBaseResource) ], avoidClasses(clBaseResource, 0))); - // Creating the water patch explaining the forest + log("Creating the water patch explaining the forest for player " + playerIDs[i] + "..."); + let waterPosition; do { - var watAngle = forestAngle + randFloat(1/3, 5/3) * Math.PI; - var watX = Math.round(forestX + 6 * Math.cos(watAngle)); - var watY = Math.round(forestY + 6 * Math.sin(watAngle)); + let waterAngle = forestAngle + randFloat(1, 5) / 3 * Math.PI; + 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, - Math.round(forestX + 3 * Math.cos(watAngle)), - Math.round(forestY + 3 * Math.sin(watAngle))), + flowerPosition.x, + flowerPosition.y), 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, - Math.round(forestX + 5 * Math.cos(watAngle)), - Math.round(forestY + 5 * Math.sin(watAngle))), + reedsPosition.x, + reedsPosition.y), 0); } while ( !createArea( - new ClumpPlacer(60, 0.9, 0.4, 5, watX, watY), + new ClumpPlacer(60, 0.9, 0.4, 5, waterPosition), [ new LayeredPainter([tShore, tWater], [1]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 3) ], avoidClasses(clBaseResource, 0))); } Engine.SetProgress(20); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad, "painters": [ paintClass(clPlayer) ] }, "Chicken": { }, "Berries": { "template": eBush }, "Mines": { "types": [ { "template": eMetalMine }, { "template": eStoneMine }, ], "distance": defaultPlayerBaseRadius(), "maxAngle": Math.PI / 2, "groupElements": shuffleArray([aBushA, aBushB, ePalmShort, ePalmTall]).map(t => new SimpleObject(t, 1, 1, 3, 4)) } // Starting trees were set above // No decoratives }); Engine.SetProgress(30); log("Creating central oasis..."); createArea( - new ClumpPlacer(diskArea(forestDistance + shoreDistance + waterRadius), 0.8, 0.2, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(forestDistance + shoreDistance + waterRadius), 0.8, 0.2, 10, mapCenter), [ new LayeredPainter([pOasisForestLight, tWater], [forestDistance]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, forestDistance + shoreDistance), paintClass(clOasis) ]); Engine.SetProgress(40); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 3), avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4), scaleByMapSize(30, 70)); log("Creating dirt patches..."); createAreas( new ClumpPlacer(80, 0.3, 0.06, 1), new TerrainPainter(tDirt), avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4, clForest, 4), scaleByMapSize(15, 50)); log("Creating dunes..."); createAreas( new ClumpPlacer(120, 0.3, 0.06, 1), [ new TerrainPainter(tDune), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetDune, 30) ], avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4, clForest, 4), scaleByMapSize(15, 50)); Engine.SetProgress(50); if (mapSize > 150 && randBool()) { log("Creating path though the oasis..."); let pathWidth = scaleByMapSize(7, 18); let points = distributePointsOnCircle(2, randomAngle(), waterRadius + shoreDistance + forestDistance + pathWidth, mapCenter)[0]; createArea( new PathPlacer(points[0], points[1], pathWidth, 0.4, 1, 0.2, 0), [ new TerrainPainter(tSand), new SmoothElevationPainter(ELEVATION_SET, heightOasisPath, 5), paintClass(clPassage) ]); } log("Creating some straggler trees around the passage..."); var group = new SimpleGroup([new SimpleObject(ePalmTall, 1,1, 0,0),new SimpleObject(ePalmShort, 1, 2, 1, 2), new SimpleObject(aBushA, 0,2, 1,3)], true, clForest); createObjectGroupsDeprecated(group, 0, stayClasses(clPassage, 3), scaleByMapSize(60, 250), 100); log("Creating stone mines..."); group = new SimpleGroup([new SimpleObject(eStoneMine, 1,1, 0,0),new SimpleObject(ePalmShort, 1,2, 3,3),new SimpleObject(ePalmTall, 0,1, 3,3) ,new SimpleObject(aBushB, 1,1, 2,2), new SimpleObject(aBushA, 0,2, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 10, clForest, 1, clPlayer, 30, clRock, 10,clBaseResource, 2, clHill, 1), scaleByMapSize(6,25), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(eMetalMine, 1,1, 0,0),new SimpleObject(ePalmShort, 1,2, 2,3),new SimpleObject(ePalmTall, 0,1, 2,2) ,new SimpleObject(aBushB, 1,1, 2,2), new SimpleObject(aBushA, 0,2, 1,3)], true, clMetal); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 10, clForest, 1, clPlayer, 30, clMetal, 10,clBaseResource, 2, clRock, 10, clHill, 1), scaleByMapSize(6,25), 100 ); Engine.SetProgress(65); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRock, 2,4, 0,2)], true, undefined ); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 30, scaleByMapSize(10, 50)); Engine.SetProgress(70); log("Creating camels..."); group = new SimpleGroup( [new SimpleObject(eCamel, 1,2, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 1 * numPlayers, 50 ); Engine.SetProgress(75); log("Creating gazelles..."); group = new SimpleGroup( [new SimpleObject(eGazelle, 2,4, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 1 * numPlayers, 50 ); Engine.SetProgress(85); log("Creating oasis animals..."); for (let i = 0; i < scaleByMapSize(5, 30); ++i) { let animalPos = Vector2D.add(mapCenter, new Vector2D(forestDistance + shoreDistance + waterRadius, 0).rotate(randomAngle())); createObjectGroup( new RandomGroup( [ new SimpleObject(eLion, 1, 2, 0, 4), new SimpleObject(eLioness, 1, 2, 2, 4), new SimpleObject(eGazelle, 4, 6, 1, 5), new SimpleObject(eCamel, 1, 2, 1, 5) ], true, clFood, animalPos.x, animalPos.y), 0); } Engine.SetProgress(90); log("Creating bushes..."); var group = new SimpleGroup( [new SimpleObject(aBushB, 1,2, 0,2), new SimpleObject(aBushA, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clOasis, 2, clHill, 1, clPlayer, 1, clPassage, 1), scaleByMapSize(10, 40), 20 ); log("Creating sand blows and beautifications"); for (var sandx = 0; sandx < mapSize; sandx += 4) for (var sandz = 0; sandz < mapSize; sandz += 4) { if (getHeight(sandx,sandz) > 3.4) { if (randBool((getHeight(sandx,sandz) - 3.4) / 1.4)) { group = new SimpleGroup( [new SimpleObject(aSand, 0,1, 0,2)], true, undefined, sandx,sandz ); createObjectGroup(group, 0); } } else if (getHeight(sandx, sandz) > -2.5 && getHeight(sandx,sandz) < -1) { if (randBool(0.4)) { group = new SimpleGroup( [new SimpleObject(aWaterFlower, 1,4, 1,2)], true, undefined, sandx,sandz ); createObjectGroup(group, 0); } else if (randBool(0.7) && getHeight(sandx,sandz) < -1.9) { group = new SimpleGroup( [new SimpleObject(aReedsA, 5,12, 0,2),new SimpleObject(aReedsB, 5,12, 0,2)], true, undefined, sandx,sandz ); createObjectGroup(group, 0); } if (getTileClass(clPassage).countInRadius(sandx,sandz,2,true) > 0) { if (randBool(0.4)) { group = new SimpleGroup( [new SimpleObject(aWaterFlower, 1,4, 1,2)], true, undefined, sandx,sandz ); createObjectGroup(group, 0); } else if (randBool(0.7) && getHeight(sandx,sandz) < -1.9) { group = new SimpleGroup( [new SimpleObject(aReedsA, 5,12, 0,2),new SimpleObject(aReedsB, 5,12, 0,2)], true, undefined, sandx,sandz ); createObjectGroup(group, 0); } } } } placePlayersNomad(clPlayer, avoidClasses(clOasis, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("sunny"); setSunColor(0.914,0.827,0.639); setSunRotation(Math.PI/3); setSunElevation(0.5); setWaterColor(0, 0.227, 0.843); setWaterTint(0, 0.545, 0.859); setWaterWaviness(1.0); setWaterType("clap"); setWaterMurkiness(0.5); setTerrainAmbientColor(0.45, 0.5, 0.6); setUnitsAmbientColor(0.501961, 0.501961, 0.501961); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/pyrenean_sierra.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/pyrenean_sierra.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/pyrenean_sierra.js (revision 20971) @@ -1,481 +1,481 @@ Engine.LoadLibrary("rmgen"); TILE_CENTERED_HEIGHT_MAP = true; const tGrassSpecific = ["new_alpine_grass_d","new_alpine_grass_d", "new_alpine_grass_e"]; const tGrass = ["new_alpine_grass_d", "new_alpine_grass_b", "new_alpine_grass_e"]; const tGrassMidRange = ["new_alpine_grass_b", "alpine_grass_a"]; const tGrassHighRange = ["new_alpine_grass_a", "alpine_grass_a", "alpine_grass_rocky"]; const tHighRocks = ["alpine_cliff_b", "alpine_cliff_c","alpine_cliff_c", "alpine_grass_rocky"]; const tSnowedRocks = ["alpine_cliff_b", "alpine_cliff_snow"]; const tTopSnow = ["alpine_snow_rocky","alpine_snow_a"]; const tTopSnowOnly = ["alpine_snow_a"]; const tDirtyGrass = ["new_alpine_grass_d","alpine_grass_d","alpine_grass_c", "alpine_grass_b"]; const tLushGrass = ["new_alpine_grass_a","new_alpine_grass_d"]; const tMidRangeCliffs = ["alpine_cliff_b","alpine_cliff_c"]; const tHighRangeCliffs = ["alpine_mountainside","alpine_cliff_snow" ]; const tSand = ["beach_c", "beach_d"]; const tSandTransition = ["beach_scrub_50_"]; const tWater = ["sand_wet_a","sand_wet_b","sand_wet_b","sand_wet_b"]; const tGrassLandForest = "alpine_forrestfloor"; const tGrassLandForest2 = "alpine_grass_d"; const tForestTransition = ["new_alpine_grass_d", "new_alpine_grass_b","alpine_grass_d"]; const tRoad = "new_alpine_citytile"; const tRoadWild = "new_alpine_citytile"; const oBeech = "gaia/flora_tree_euro_beech"; const oPine = "gaia/flora_tree_aleppo_pine"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oRabbit = "gaia/fauna_rabbit"; const oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; const oStoneSmall = "gaia/geology_stone_alpine_a"; const oMetalLarge = "gaia/geology_metal_alpine_slabs"; const aGrass = "actor|props/flora/grass_soft_small_tall.xml"; const aGrassShort = "actor|props/flora/grass_soft_large.xml"; const aRockLarge = "actor|geology/stone_granite_med.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBushMedium = "actor|props/flora/bush_medit_me.xml"; const aBushSmall = "actor|props/flora/bush_medit_sm.xml"; const pForestLand = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech, tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tGrassLandForest,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; const pForestLandLight = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech, tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tGrassLandForest,tGrassLandForest2,tForestTransition,tGrassLandForest2, tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition, tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; const pForestLandVeryLight = [ tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tForestTransition,tGrassLandForest2,tForestTransition,tForestTransition,tForestTransition, tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition, tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; const heightInit = -100; const heightOcean = -22; const heightBase = -6; const heightWaterLevel = 8; const heightPyreneans = 15; const heightGrass = 6; const heightGrassMidRange = 18; const heightGrassHighRange = 30; const heightPassage = scaleByMapSize(25, 40); const heightHighRocks = heightPassage + 5; const heightSnowedRocks = heightHighRocks + 10; const heightMountain = heightHighRocks + 20; const heightOffsetHill = 7; const heightOffsetHillRandom = 2; InitMap(heightInit, tGrass); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clPass = createTileClass(); var clPyrenneans = createTileClass(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var startAngle = randomAngle(); var oceanAngle = startAngle + randFloat(-1, 1) * Math.PI / 12; var mountainLength = fractionToTiles(0.68); var mountainWidth = scaleByMapSize(15, 55); var mountainPeaks = 100 * scaleByMapSize(1, 10); var mountainOffset = randFloat(-1, 1) * scaleByMapSize(1, 12); var passageLength = scaleByMapSize(8, 50); var terrainPerHeight = [ { "maxHeight": heightGrass, "steepness": 5, "terrainGround": tGrass, "terrainSteep": tMidRangeCliffs }, { "maxHeight": heightGrassMidRange, "steepness": 8, "terrainGround": tGrassMidRange, "terrainSteep": tMidRangeCliffs }, { "maxHeight": heightGrassHighRange, "steepness": 8, "terrainGround": tGrassHighRange, "terrainSteep": tMidRangeCliffs }, { "maxHeight": heightHighRocks, "steepness": 8, "terrainGround": tHighRocks, "terrainSteep": tHighRangeCliffs }, { "maxHeight": heightSnowedRocks, "steepness": 7, "terrainGround": tSnowedRocks, "terrainSteep": tHighRangeCliffs }, { "maxHeight": Infinity, "steepness": 6, "terrainGround": tTopSnowOnly, "terrainSteep": tTopSnow } ]; log("Creating initial sinusoidal noise..."); var baseHeights = []; for (var ix = 0; ix < mapSize; ix++) { baseHeights.push([]); for (var iz = 0; iz < mapSize; iz++) { let position = new Vector2D(ix, iz); if (g_Map.inMapBounds(ix, iz)) { let height = heightBase + randFloat(-1, 1) + scaleByMapSize(1, 3) * (Math.cos(ix / scaleByMapSize(5, 30)) + Math.sin(iz / scaleByMapSize(5, 30))); g_Map.setHeight(position, height); baseHeights[ix].push(height); } else baseHeights[ix].push(heightInit); } } placePlayerBases({ "PlayerPlacement": [primeSortAllPlayers(), ...playerPlacementCustomAngle( fractionToTiles(0.35), mapCenter, i => oceanAngle + Math.PI * (i % 2 ? 1 : -1) * ((1/2 + 1/3 * (2/numPlayers * (i + 1 - i % 2) - 1))))], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oPine }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(30); log("Creating the pyreneans..."); var mountainVec = new Vector2D(mountainLength, 0).rotate(-startAngle); var mountainStart = Vector2D.sub(mapCenter, Vector2D.div(mountainVec, 2)); var mountainDirection = mountainVec.clone().normalize(); createPyreneans(); paintTileClassBasedOnHeight(heightPyreneans, Infinity, Elevation_ExcludeMin_ExcludeMax, clPyrenneans); Engine.SetProgress(40); /** * Generates the mountain peak noise. * * @param {number} x - between 0 and 1 * @returns {number} between 0 and 1 */ function sigmoid(x, peakPosition) { return 1 / (1 + Math.exp(x)) * // If we're too far from the border, we flatten (0.2 - Math.max(0, Math.abs(0.5 - peakPosition) - 0.3)) * 5; } function createPyreneans() { for (let peak = 0; peak < mountainPeaks; ++peak) { let peakPosition = peak / mountainPeaks; let peakHeight = randFloat(0, 10); for (let distance = 0; distance < mountainWidth; distance += 1/3) { let rest = 2 * (1 - distance / mountainWidth); let sigmoidX = - 1 * (rest - 1.9) + - 4 * (rest - randFloat(0.9, 1.1)) * (rest - randFloat(0.9, 1.1)) * (rest - randFloat(0.9, 1.1)); for (let direction of [-1, 1]) { let pos = Vector2D.sum([ Vector2D.add(mountainStart, Vector2D.mult(mountainDirection, peakPosition * mountainLength)), new Vector2D(mountainOffset, 0).rotate(-peakPosition * Math.PI * 4), new Vector2D(distance, 0).rotate(-startAngle - direction * Math.PI / 2) ]).round(); g_Map.setHeight(pos, baseHeights[pos.x][pos.y] + (heightMountain + peakHeight + randFloat(-9, 9)) * sigmoid(sigmoidX, peakPosition)); } } } } log("Smoothing pyreneans..."); for (let ix = 1; ix < mapSize - 1; ++ix) for (let iz = 1; iz < mapSize - 1; ++iz) { let position = new Vector2D(ix, iz); if (g_Map.validH(ix, iz) && checkIfInClass(ix, iz, clPyrenneans)) { let height = getHeight(ix, iz); let index = 1 / (1 + Math.max(0, height / 7)); g_Map.setHeight(position, height * (1 - index) + g_Map.getAverageHeight(position) * index); } } Engine.SetProgress(48); log("Creating passages..."); var passageLocation = 0.35; var passageVec = mountainDirection.perpendicular().mult(passageLength); for (let passLoc of [passageLocation, 1 - passageLocation]) for (let direction of [1, -1]) { let passageStart = Vector2D.add(mountainStart, Vector2D.mult(mountainVec, passLoc)); let passageEnd = Vector2D.add(passageStart, Vector2D.mult(passageVec, direction)); createPassage({ "start": passageStart, "end": passageEnd, "startHeight": heightPassage, "startWidth": 7, "endWidth": 7, "smoothWidth": 2, "tileClass": clPass }); } Engine.SetProgress(50); log("Smoothing the mountains..."); for (let ix = 1; ix < mapSize - 1; ++ix) for (let iz = 1; iz < mapSize - 1; ++iz) { let position = new Vector2D(ix, iz); if (g_Map.inMapBounds(ix,iz) && checkIfInClass(ix, iz, clPyrenneans)) { let heightNeighbor = g_Map.getAverageHeight(position); let index = 1 / (1 + Math.max(0, (getHeight(ix,iz) - 10) / 7)); g_Map.setHeight(position, getHeight(ix, iz) * (1 - index) + heightNeighbor * index); } } log("Creating oceans..."); for (let ocean of distributePointsOnCircle(2, oceanAngle, fractionToTiles(0.48), mapCenter)[0]) createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.9, 0.05, 10, ocean.x, ocean.y), + new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.9, 0.05, 10, ocean), [ new ElevationPainter(heightOcean), paintClass(clWater) ]); log("Smoothing around the water..."); var smoothDist = 5; for (let ix = 1; ix < mapSize - 1; ++ix) for (let iz = 1; iz < mapSize - 1; ++iz) { let position = new Vector2D(ix, iz); if (!g_Map.inMapBounds(ix, iz) || !getTileClass(clWater).countMembersInRadius(ix, iz, smoothDist)) continue; let averageHeight = 0; let todivide = 0; for (let xx = -smoothDist; xx <= smoothDist; ++xx) for (let yy = -smoothDist; yy <= smoothDist; ++yy) if (g_Map.inMapBounds(ix + xx,iz + yy) && (xx != 0 || yy != 0)) { averageHeight += getHeight(ix + xx, iz + yy) / (Math.abs(xx) + Math.abs(yy)); todivide += 1 / (Math.abs(xx) + Math.abs(yy)); } g_Map.setHeight(position, (averageHeight + 2 * getHeight(ix, iz)) / (todivide + 2)); } Engine.SetProgress(55); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(60, 120), 0.3, 0.06, 5), [ new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill, 4, heightOffsetHillRandom), new TerrainPainter(tGrassSpecific), paintClass(clHill) ], avoidClasses(clWater, 5, clPlayer, 20, clBaseResource, 6, clPyrenneans, 2), scaleByMapSize(5, 35)); log("Creating forests..."); var types = [[tForestTransition, pForestLandVeryLight, pForestLandLight, pForestLand]]; var size = scaleByMapSize(40, 115) * Math.PI; var num = Math.floor(scaleByMapSize(8,40) / types.length); for (let type of types) createAreas( new ClumpPlacer(size, 0.2, 0.1, 1), [ new LayeredPainter(type, [scaleByMapSize(1, 2), scaleByMapSize(3, 6), scaleByMapSize(3, 6)]), paintClass(clForest) ], avoidClasses(clPlayer, 20, clPyrenneans,0, clForest, 7, clWater, 2), num); Engine.SetProgress(60); log("Creating lone trees..."); var num = scaleByMapSize(80,400); var group = new SimpleGroup([new SimpleObject(oPine, 1,2, 1,3),new SimpleObject(oBeech, 1,2, 1,3)], true, clForest); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 8,clPyrenneans, 1), num, 20 ); log("Painting the map..."); for (let x = 0; x < mapSize; ++x) for (let z = 0; z < mapSize; ++z) { let position = new Vector2D(x, z); let height = getHeight(x, z); let heightDiff = g_Map.getSlope(position); if (getTileClass(clPyrenneans).countMembersInRadius(x, z, 2)) { let layer = terrainPerHeight.find(layer => height < layer.maxHeight); createTerrain(heightDiff > layer.steepness ? layer.terrainSteep : layer.terrainGround).place(x, z); } let terrainShore = getShoreTerrain(height, heightDiff, x, z); if (terrainShore) createTerrain(terrainShore).place(x, z); } function getShoreTerrain(height, heightDiff, x, z) { if (height <= -14) return tWater; if (height <= -2 && getTileClass(clWater).countMembersInRadius(x, z, 2)) return heightDiff < 2.5 ? tSand : tMidRangeCliffs; if (height <= 0 && getTileClass(clWater).countMembersInRadius(x, z, 3)) return heightDiff < 2.5 ? tSandTransition : tMidRangeCliffs; return undefined; } log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 20), scaleByMapSize(5, 40), scaleByMapSize(8, 60)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new TerrainPainter(tDirtyGrass), paintClass(clDirt) ], avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45)); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tLushGrass), avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45)); Engine.SetProgress(70); // making more in dirt areas so as to appear different log("Creating small grass tufts..."); var group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 5, clDirt, 0, clPyrenneans,2), scaleByMapSize(13, 200) ); createObjectGroupsDeprecated(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 5, clDirt, 1, clForest, 0, clPyrenneans,2), scaleByMapSize(13, 200) ); createObjectGroupsDeprecated(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10); Engine.SetProgress(75); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clPlayer, 1, clPyrenneans, 1), scaleByMapSize(13, 200), 50 ); Engine.SetProgress(80); log("Creating stone mines..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 8, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 8, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clMetal, 8, clRock, 5, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); Engine.SetProgress(85); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(8, 131), 50 ); Engine.SetProgress(90); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood, 15), 3 * numPlayers, 50 ); log("Creating rabbit..."); group = new SimpleGroup( [new SimpleObject(oRabbit, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood,15), 3 * numPlayers, 50 ); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)],true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50); log("Creating fish..."); group = new SimpleGroup( [new SimpleObject(oFish, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clFood, 15), stayClasses(clWater, 6)], 20 * numPlayers, 60 ); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clPyrenneans, 4, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)); setSunElevation(Math.PI * randFloat(1/5, 1/3)); setSunRotation(randomAngle()); setSkySet("cumulus"); setSunColor(0.73,0.73,0.65); setTerrainAmbientColor(0.45,0.45,0.50); setUnitsAmbientColor(0.4,0.4,0.4); setWaterColor(0.263, 0.353, 0.616); setWaterTint(0.104, 0.172, 0.563); setWaterWaviness(5.0); setWaterType("ocean"); setWaterMurkiness(0.83); setWaterHeight(heightWaterLevel); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/rivers.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rivers.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/rivers.js (revision 20971) @@ -1,289 +1,289 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.hill; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; var tShore = g_Terrains.shore; var tWater = g_Terrains.water; if (currentBiome() == "tropic") { tShore = "tropic_dirt_b_plants"; tWater = "tropic_dirt_b"; } const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oFish = g_Gaia.fish; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aReeds = g_Decoratives.reeds; const aLillies = g_Decoratives.lillies; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightSeaGround = -3; const heightShallows = -1; const heightLand = 1; InitMap(heightLand, tMainTerrain); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clShallow = createTileClass(); var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oTree1, "count": 2 }, "Decoratives": { "template": aGrassShort } }); log("Creating central lake..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.075)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.075)), 0.7, 0.1, 10, mapCenter), [ new LayeredPainter([tShore, tWater, tWater, tWater], [1, 4, 2]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), paintClass(clWater) ]); log("Creating rivers between opponents..."); let numRivers = isNomad() ? randIntInclusive(4, 8) : numPlayers; let rivers = distributePointsOnCircle(numRivers, startAngle + Math.PI / numRivers, fractionToTiles(0.5), mapCenter)[0]; for (let i = 0; i < numRivers; ++i) { if (isNomad() ? randBool() : areAllies(playerIDs[i], playerIDs[(i + 1) % numPlayers])) continue; let shallowLocation = randFloat(0.2, 0.7); let shallowWidth = randFloat(0.12, 0.21); paintRiver({ "parallel": true, "start": rivers[i], "end": mapCenter, "width": scaleByMapSize(10, 30), "fadeDist": 5, "deviation": 0, "heightLand": heightLand, "heightRiverbed": heightSeaGround, "minHeight": heightSeaGround, "meanderShort": 10, "meanderLong": 0, "waterFunc": (position, height, riverFraction) => { addToClass(position.x, position.y, clWater); let isShallow = height < heightShallows && riverFraction > shallowLocation && riverFraction < shallowLocation + shallowWidth; let newHeight = isShallow ? heightShallows : Math.max(height, heightSeaGround); if (getHeight(position.x, position.y) < newHeight) return; g_Map.setHeight(position, newHeight); createTerrain(height >= 0 ? tShore : tWater).place(position.x, position.y); if (isShallow) addToClass(position.x, position.y, clShallow); } }); } Engine.SetProgress(40); createBumps(avoidClasses(clWater, 2, clPlayer, 20)); if (randBool()) createHills([tMainTerrain, tCliff, tHill], avoidClasses(clPlayer, 20, clHill, 15, clWater, 2), clHill, scaleByMapSize(3, 15)); else createMountains(tCliff, avoidClasses(clPlayer, 20, clHill, 15, clWater, 2), clHill, scaleByMapSize(3, 15)); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], avoidClasses(clPlayer, 20, clForest, 17, clHill, 0, clWater, 2), clForest, forestTrees); Engine.SetProgress(50); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tMainTerrain,tTier1Terrain],[tTier1Terrain,tTier2Terrain], [tTier2Terrain,tTier3Terrain]], [1,1], avoidClasses(clWater, 3, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], tTier4Terrain, avoidClasses(clWater, 3, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(15, 45), clDirt); Engine.SetProgress(55); log("Creating stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1), clMetal ); Engine.SetProgress(65); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 1, 2, 0, 1)], [new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ scaleByMapSize(16, 262), scaleByMapSize(8, 131), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200) ], avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0)); createDecoration( [ [new SimpleObject(aReeds, 1, 3, 0, 1)], [new SimpleObject(aLillies, 1, 2, 0, 1)] ], [ scaleByMapSize(800, 12800), scaleByMapSize(800, 12800) ], stayClasses(clShallow, 0)); Engine.SetProgress(70); createFood( [ [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)] ], [ 3 * numPlayers, 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 20), clFood); createFood( [ [new SimpleObject(oFruitBush, 5, 7, 0, 4)] ], [ 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), clFood); createFood( [ [new SimpleObject(oFish, 2, 3, 0, 2)] ], [ 25 * numPlayers ], [avoidClasses(clFood, 20), stayClasses(clWater, 6)], clFood); Engine.SetProgress(85); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], avoidClasses(clWater, 5, clForest, 7, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)); setWaterWaviness(3.0); setWaterType("lake"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/gaia_terrain.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/gaia_terrain.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/gaia_terrain.js (revision 20971) @@ -1,597 +1,597 @@ /** * @file These functions are often used to create a landscape, for instance shaping mountains, hills, rivers or grass and dirt patches. */ /** * Bumps add slight, diverse elevation differences to otherwise completely level terrain. */ function createBumps(constraint, count, minSize, maxSize, spread, failFraction = 0, elevation = 2) { log("Creating bumps..."); createAreas( new ChainPlacer( minSize || 1, maxSize || Math.floor(scaleByMapSize(4, 6)), spread || Math.floor(scaleByMapSize(2, 5)), failFraction), new SmoothElevationPainter(ELEVATION_MODIFY, elevation, 2), constraint, count || scaleByMapSize(100, 200)); } /** * Hills are elevated, planar, impassable terrain areas. */ function createHills(terrainset, constraint, tileClass, count, minSize, maxSize, spread, failFraction = 0.5, elevation = 18, elevationSmoothing = 2) { log("Creating hills..."); createAreas( new ChainPlacer( minSize || 1, maxSize || Math.floor(scaleByMapSize(4, 6)), spread || Math.floor(scaleByMapSize(16, 40)), failFraction), [ new LayeredPainter(terrainset, [1, elevationSmoothing]), new SmoothElevationPainter(ELEVATION_SET, elevation, elevationSmoothing), paintClass(tileClass) ], constraint, count || scaleByMapSize(1, 4) * getNumPlayers()); } /** * Mountains are impassable smoothened cones. */ function createMountains(terrain, constraint, tileClass, count, maxHeight, minRadius, maxRadius, numCircles) { log("Creating mountains..."); let mapSize = getMapSize(); for (let i = 0; i < (count || scaleByMapSize(1, 4) * getNumPlayers()); ++i) createMountain( maxHeight !== undefined ? maxHeight : Math.floor(scaleByMapSize(30, 50)), minRadius || Math.floor(scaleByMapSize(3, 4)), maxRadius || Math.floor(scaleByMapSize(6, 12)), numCircles || Math.floor(scaleByMapSize(4, 10)), constraint, randIntExclusive(0, mapSize), randIntExclusive(0, mapSize), terrain, tileClass, 14); } /** * Create a mountain using a technique very similar to ChainPlacer. */ function createMountain(maxHeight, minRadius, maxRadius, numCircles, constraints, x, z, terrain, tileClass, fcc = 0, q = []) { let constraint = new AndConstraint(constraints); if (!g_Map.inMapBounds(x, z) || !constraint.allows(x, z)) return; let mapSize = getMapSize(); let queueEmpty = !q.length; let gotRet = []; for (let i = 0; i < mapSize; ++i) { gotRet[i] = []; for (let j = 0; j < mapSize; ++j) gotRet[i][j] = -1; } --mapSize; minRadius = Math.max(1, Math.min(minRadius, maxRadius)); let edges = [[x, z]]; let circles = []; for (let i = 0; i < numCircles; ++i) { let badPoint = false; let [cx, cz] = pickRandom(edges); let radius; if (queueEmpty) radius = randIntInclusive(minRadius, maxRadius); else { radius = q.pop(); queueEmpty = !q.length; } let sx = Math.max(0, cx - radius); let sz = Math.max(0, cz - radius); let lx = Math.min(cx + radius, mapSize); let lz = Math.min(cz + radius, mapSize); let radius2 = Math.square(radius); for (let ix = sx; ix <= lx; ++ix) { for (let iz = sz; iz <= lz; ++iz) { if (Math.euclidDistance2D(ix, iz, cx, cz) > radius2 || !g_Map.inMapBounds(ix, iz)) continue; if (!constraint.allows(ix, iz)) { badPoint = true; break; } let state = gotRet[ix][iz]; if (state == -1) { gotRet[ix][iz] = -2; } else if (state >= 0) { edges.splice(state, 1); gotRet[ix][iz] = -2; for (let k = state; k < edges.length; ++k) --gotRet[edges[k][0]][edges[k][1]]; } } if (badPoint) break; } if (badPoint) continue; circles.push([cx, cz, radius]); for (let ix = sx; ix <= lx; ++ix) for (let iz = sz; iz <= lz; ++iz) { if (gotRet[ix][iz] != -2 || fcc && (x - ix > fcc || ix - x > fcc || z - iz > fcc || iz - z > fcc) || ix > 0 && gotRet[ix-1][iz] == -1 || iz > 0 && gotRet[ix][iz-1] == -1 || ix < mapSize && gotRet[ix+1][iz] == -1 || iz < mapSize && gotRet[ix][iz+1] == -1) continue; edges.push([ix, iz]); gotRet[ix][iz] = edges.length - 1; } } for (let [cx, cz, radius] of circles) { let circlePosition = new Vector2D(cx, cz); let sx = Math.max(0, cx - radius); let sz = Math.max(0, cz - radius); let lx = Math.min(cx + radius, mapSize); let lz = Math.min(cz + radius, mapSize); let clumpHeight = radius / maxRadius * maxHeight * randFloat(0.8, 1.2); for (let ix = sx; ix <= lx; ++ix) for (let iz = sz; iz <= lz; ++iz) { let position = new Vector2D(ix, iz); let distance = position.distanceTo(circlePosition); let newHeight = randIntInclusive(0, 2) + Math.round(2/3 * clumpHeight * (Math.sin(Math.PI * 2/3 * (3/4 - distance / radius)) + 0.5)); if (distance > radius) continue; if (getHeight(ix, iz) < newHeight) g_Map.setHeight(position, newHeight); else if (getHeight(ix, iz) >= newHeight && getHeight(ix, iz) < newHeight + 4) g_Map.setHeight(position, newHeight + 4); if (terrain !== undefined) createTerrain(terrain).place(ix, iz); if (tileClass !== undefined) addToClass(ix, iz, tileClass); } } } /** * Generates a volcano mountain. Smoke and lava are optional. * * @param {number} center - Vector2D location on the tilemap. * @param {number} tileClass - Painted onto every tile that is occupied by the volcano. * @param {string} terrainTexture - The texture painted onto the volcano hill. * @param {array} lavaTextures - Three different textures for the interior, from the outside to the inside. * @param {boolean} smoke - Whether to place smoke particles. * @param {number} elevationType - Elevation painter type, ELEVATION_SET = absolute or ELEVATION_MODIFY = relative. */ function createVolcano(position, tileClass, terrainTexture, lavaTextures, smoke, elevationType) { log("Creating volcano..."); let clLava = createTileClass(); let layers = [ { "clumps": diskArea(scaleByMapSize(18, 25)), "elevation": 15, "tileClass": tileClass, "steepness": 3 }, { "clumps": diskArea(scaleByMapSize(16, 23)), "elevation": 25, "tileClass": createTileClass(), "steepness": 3 }, { "clumps": diskArea(scaleByMapSize(10, 15)), "elevation": 45, "tileClass": createTileClass(), "steepness": 3 }, { "clumps": diskArea(scaleByMapSize(8, 11)), "elevation": 62, "tileClass": createTileClass(), "steepness": 3 }, { "clumps": diskArea(scaleByMapSize(4, 6)), "elevation": 42, "tileClass": clLava, "painter": lavaTextures && new LayeredPainter([terrainTexture, ...lavaTextures], [1, 1, 1]), "steepness": 1 } ]; for (let i = 0; i < layers.length; ++i) createArea( - new ClumpPlacer(layers[i].clumps, 0.7, 0.05, 100, position.x, position.y), + new ClumpPlacer(layers[i].clumps, 0.7, 0.05, 100, position), [ layers[i].painter || new LayeredPainter([terrainTexture, terrainTexture], [3]), new SmoothElevationPainter(elevationType, layers[i].elevation, layers[i].steepness), paintClass(layers[i].tileClass) ], i == 0 ? null : stayClasses(layers[i - 1].tileClass, 1)); if (smoke) { let num = Math.floor(diskArea(scaleByMapSize(3, 5))); createObjectGroup( new SimpleGroup( [new SimpleObject("actor|particle/smoke.xml", num, num, 0, 7)], false, clLava, position.x, position.y), 0, stayClasses(tileClass, 1)); } } /** * Paint the given terrain texture in the given sizes at random places of the map to diversify monotone land texturing. */ function createPatches(sizes, terrain, constraint, count, tileClass, failFraction = 0.5) { for (let size of sizes) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, failFraction), [ new TerrainPainter(terrain), paintClass(tileClass) ], constraint, count); } /** * Same as createPatches, but each patch consists of a set of textures drawn depending to the distance of the patch border. */ function createLayeredPatches(sizes, terrains, terrainWidths, constraint, count, tileClass, failFraction = 0.5) { for (let size of sizes) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, failFraction), [ new LayeredPainter(terrains, terrainWidths), paintClass(tileClass) ], constraint, count); } /** * Creates a meandering river at the given location and width. * Optionally calls a function on the affected tiles. * * @property start - A Vector2D in tile coordinates stating where the river starts. * @property end - A Vector2D in tile coordinates stating where the river ends. * @property parallel - Whether the shorelines should be parallel or meander separately. * @property width - Size between the two shorelines. * @property fadeDist - Size of the shoreline. * @property deviation - Fuzz effect on the shoreline if greater than 0. * @property heightRiverbed - Ground height of the riverbed. * @proeprty heightLand - Ground height of the end of the shoreline. * @property meanderShort - Strength of frequent meanders. * @property meanderLong - Strength of less frequent meanders. * @property [constraint] - If given, ignores any tiles that don't satisfy the given Constraint. * @property [waterFunc] - Optional function called on tiles within the river. * Provides location on the tilegrid, new elevation and * the location on the axis parallel to the river as a fraction of the river length. * @property [landFunc] - Optional function called on land tiles, providing ix, iz, shoreDist1, shoreDist2. * @property [minHeight] - If given, only changes the elevation below this height while still calling the given functions. */ function paintRiver(args) { log("Creating river..."); // Model the river meandering as the sum of two sine curves. let meanderShort = fractionToTiles(args.meanderShort / scaleByMapSize(35, 160)); let meanderLong = fractionToTiles(args.meanderLong / scaleByMapSize(35, 100)); // Unless the river is parallel, each riverside will receive an own random seed and starting angle. let seed1 = randFloat(2, 3); let seed2 = randFloat(2, 3); let startingAngle1 = randFloat(0, 1); let startingAngle2 = randFloat(0, 1); // Computes the deflection of the river at a given point. let riverCurve = (riverFraction, startAngle, seed) => meanderShort * rndRiver(startAngle + fractionToTiles(riverFraction) / 128, seed) + meanderLong * rndRiver(startAngle + fractionToTiles(riverFraction) / 256, seed); // Describe river location in vectors. let riverLength = args.start.distanceTo(args.end); let unitVecRiver = Vector2D.sub(args.start, args.end).normalize(); // Describe river boundaries. let riverMinX = Math.min(args.start.x, args.end.x); let riverMinZ = Math.min(args.start.y, args.end.y); let riverMaxX = Math.max(args.start.x, args.end.x); let riverMaxZ = Math.max(args.start.y, args.end.y); let mapSize = getMapSize(); for (let ix = 0; ix < mapSize; ++ix) for (let iz = 0; iz < mapSize; ++iz) { if (args.constraint && !args.constraint.allows(ix, iz)) continue; let vecPoint = new Vector2D(ix, iz); // Compute the shortest distance to the river. let distanceToRiver = distanceOfPointFromLine(args.start, args.end, vecPoint); // Closest point on the river (i.e the foot of the perpendicular). let river = Vector2D.sub(vecPoint, unitVecRiver.perpendicular().mult(distanceToRiver)); // Only process points that actually are perpendicular with the river. if (river.x < riverMinX || river.x > riverMaxX || river.y < riverMinZ || river.y > riverMaxZ) continue; // Coordinate between 0 and 1 on the axis parallel to the river. let riverFraction = river.distanceTo(args.start) / riverLength; // Amplitude of the river at this location. let riverCurve1 = riverCurve(riverFraction, startingAngle1, seed1); let riverCurve2 = args.parallel ? riverCurve1 : riverCurve(riverFraction, startingAngle2, seed2); // Add noise. let deviation = args.deviation * randFloat(-1, 1); // Compute the distance to the shoreline. let sign = Math.sign(distanceToRiver || 1); let shoreDist1 = sign * riverCurve1 + Math.abs(distanceToRiver) - deviation - args.width / 2; let shoreDist2 = sign * riverCurve2 + Math.abs(distanceToRiver) - deviation + args.width / 2; // Create the elevation for the water and the slopy shoreline and call the user functions. if (shoreDist1 < 0 && shoreDist2 > 0) { let height = args.heightRiverbed; if (shoreDist1 > -args.fadeDist) height += (args.heightLand - args.heightRiverbed) * (1 + shoreDist1 / args.fadeDist); else if (shoreDist2 < args.fadeDist) height += (args.heightLand - args.heightRiverbed) * (1 - shoreDist2 / args.fadeDist); if (args.minHeight === undefined || height < args.minHeight) g_Map.setHeight(vecPoint, height); if (args.waterFunc) args.waterFunc(vecPoint, height, riverFraction); } else if (args.landFunc) args.landFunc(vecPoint, shoreDist1, shoreDist2); } } /** * Helper function to create a meandering river. * It works the same as sin or cos function with the difference that it's period is 1 instead of 2 pi. */ function rndRiver(f, seed) { let rndRw = seed; for (let i = 0; i <= f; ++i) rndRw = 10 * (rndRw % 1); let rndRr = f % 1; let retVal = (Math.floor(f) % 2 ? -1 : 1) * rndRr * (rndRr - 1); let rndRe = Math.floor(rndRw) % 5; if (rndRe == 0) retVal *= 2.3 * (rndRr - 0.5) * (rndRr - 0.5); else if (rndRe == 1) retVal *= 2.6 * (rndRr - 0.3) * (rndRr - 0.7); else if (rndRe == 2) retVal *= 22 * (rndRr - 0.2) * (rndRr - 0.3) * (rndRr - 0.3) * (rndRr - 0.8); else if (rndRe == 3) retVal *= 180 * (rndRr - 0.2) * (rndRr - 0.2) * (rndRr - 0.4) * (rndRr - 0.6) * (rndRr - 0.6) * (rndRr - 0.8); else if (rndRe == 4) retVal *= 2.6 * (rndRr - 0.5) * (rndRr - 0.7); return retVal; } /** * Add small rivers with shallows starting at a central river ending at the map border, if the given Constraint is met. */ function createTributaryRivers(riverAngle, riverCount, riverWidth, heightRiverbed, heightRange, maxAngle, tributaryRiverTileClass, shallowTileClass, constraint) { log("Creating tributary rivers..."); let waviness = 0.4; let smoothness = scaleByMapSize(3, 12); let offset = 0.1; let tapering = 0.05; let heightShallow = -2; let mapSize = getMapSize(); let mapCenter = getMapCenter(); let mapBounds = getMapBounds(); let riverConstraint = avoidClasses(tributaryRiverTileClass, 3); if (shallowTileClass) riverConstraint = new AndConstraint([riverConstraint, avoidClasses(shallowTileClass, 2)]); for (let i = 0; i < riverCount; ++i) { // Determining tributary river location let searchCenter = new Vector2D(fractionToTiles(randFloat(tapering, 1 - tapering)), mapCenter.y); let sign = randBool() ? 1 : -1; let distanceVec = new Vector2D(0, sign * tapering); let searchStart = Vector2D.add(searchCenter, distanceVec).rotateAround(riverAngle, mapCenter); let searchEnd = Vector2D.sub(searchCenter, distanceVec).rotateAround(riverAngle, mapCenter); let start = findLocationInDirectionBasedOnHeight(searchStart, searchEnd, heightRange[0], heightRange[1], 4); if (!start) continue; start.round(); let end = Vector2D.add(mapCenter, new Vector2D(mapSize, 0).rotate(riverAngle - sign * randFloat(maxAngle, 2 * Math.PI - maxAngle))).round(); // Create river if (!createArea( new PathPlacer(start, end, riverWidth, waviness, smoothness, offset, tapering), [ new SmoothElevationPainter(ELEVATION_SET, heightRiverbed, 4), paintClass(tributaryRiverTileClass) ], new AndConstraint([constraint, riverConstraint]))) continue; // Create small puddles at the map border to ensure players being separated createArea( - new ClumpPlacer(Math.floor(diskArea(riverWidth / 2)), 0.95, 0.6, 10, end.x, end.y), + new ClumpPlacer(diskArea(riverWidth / 2), 0.95, 0.6, 10, end), new SmoothElevationPainter(ELEVATION_SET, heightRiverbed, 3), constraint); } // Create shallows if (shallowTileClass) for (let z of [0.25, 0.75]) createPassage({ "start": new Vector2D(mapBounds.left, fractionToTiles(z)).rotateAround(riverAngle, mapCenter), "end": new Vector2D(mapBounds.right, fractionToTiles(z)).rotateAround(riverAngle, mapCenter), "startWidth": scaleByMapSize(8, 12), "endWidth": scaleByMapSize(8, 12), "smoothWidth": 2, "startHeight": heightShallow, "endHeight": heightShallow, "maxHeight": heightShallow, "tileClass": shallowTileClass }); } /** * Creates a smooth, passable path between between start and end with the given startWidth and endWidth. * Paints the given tileclass and terrain. * * @property {Vector2D} start - Location of the passage. * @property {Vector2D} end * @property {number} startWidth - Size of the passage (perpendicular to the direction of the passage). * @property {number} endWidth * @property {number} [startHeight] - Fixed height to be used if the height at the location shouldn't be used. * @property {number} [endHeight] * @property {number} [maxHeight] - If given, do not touch any terrain above this height. * @property {number} smoothWidth - Number of tiles at the passage border to apply height interpolation. * @property {number} [tileClass] - Marks the passage with this tile class. * @property {string} [terrain] - Texture to be painted on the passage area. * @property {string} [edgeTerrain] - Texture to be painted on the borders of the passage. */ function createPassage(args) { let bound = x => Math.max(0, Math.min(Math.round(x), getMapSize())); let startHeight = args.startHeight !== undefined ? args.startHeight : getHeight(bound(args.start.x), bound(args.start.y)); let endHeight = args.endHeight !== undefined ? args.endHeight : getHeight(bound(args.end.x), bound(args.end.y)); let passageVec = Vector2D.sub(args.end, args.start); let widthDirection = passageVec.perpendicular().normalize(); let lengthStep = 1 / (2 * passageVec.length()); for (let lengthFraction = 0; lengthFraction <= 1; lengthFraction += lengthStep) { let locationLength = Vector2D.add(args.start, Vector2D.mult(passageVec, lengthFraction)); let halfPassageWidth = (args.startWidth + (args.endWidth - args.startWidth) * lengthFraction) / 2; let passageHeight = startHeight + (endHeight - startHeight) * lengthFraction; for (let stepWidth = -halfPassageWidth; stepWidth <= halfPassageWidth; stepWidth += 0.5) { let location = Vector2D.add(locationLength, Vector2D.mult(widthDirection, stepWidth)).round(); if (!g_Map.inMapBounds(location.x, location.y) || args.maxHeight !== undefined && getHeight(location.x, location.y) > args.maxHeight) continue; let smoothDistance = args.smoothWidth + Math.abs(stepWidth) - halfPassageWidth; g_Map.setHeight( location, smoothDistance > 0 ? (getHeight(location.x, location.y) * smoothDistance + passageHeight / smoothDistance) / (smoothDistance + 1 / smoothDistance) : passageHeight); if (args.tileClass !== undefined) addToClass(location.x, location.y, args.tileClass); if (args.edgeTerrain && smoothDistance > 0) createTerrain(args.edgeTerrain).place(location.x, location.y); else if (args.terrain) createTerrain(args.terrain).place(location.x, location.y); } } } /** * Returns the first location between startPoint and endPoint that lies within the given heightrange. */ function findLocationInDirectionBasedOnHeight(startPoint, endPoint, minHeight, maxHeight, offset = 0) { let stepVec = Vector2D.sub(endPoint, startPoint); let distance = Math.ceil(stepVec.length()); stepVec.normalize(); for (let i = 0; i < distance; ++i) { let pos = Vector2D.add(startPoint, Vector2D.mult(stepVec, i)); let ipos = pos.clone().round(); if (g_Map.validH(ipos.x, ipos.y) && getHeight(ipos.x, ipos.y) >= minHeight && getHeight(ipos.x, ipos.y) <= maxHeight) return pos.add(stepVec.mult(offset)); } return undefined; } Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/placer_centered.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/placer_centered.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/placer_centered.js (revision 20971) @@ -1,261 +1,261 @@ /** * @file A Centered Placer generates a shape (array of points) around a variable center location satisfying a Constraint. * The center is determined by the x and z property which can be modified externally, typically by createAreas. */ ///////////////////////////////////////////////////////////////////////////////////////// // ClumpPlacer // // Class for generating a roughly circular clump of points // // size: The average number of points in the clump // 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) // x, z: Tile coordinates of placer center (optional) // ///////////////////////////////////////////////////////////////////////////////////////// -function ClumpPlacer(size, coherence, smoothness, failFraction, x, z) +function ClumpPlacer(size, coherence, smoothness, failFraction = 0, position = undefined) { this.size = size; this.coherence = coherence; this.smoothness = smoothness; - this.failFraction = failFraction !== undefined ? failFraction : 0; - this.x = x !== undefined ? x : -1; - this.z = z !== undefined ? z : -1; + this.failFraction = failFraction; + this.x = position ? Math.round(position.x) : -1; + this.z = position ? Math.round(position.y) : -1; } ClumpPlacer.prototype.place = function(constraint) { // Preliminary bounds check if (!g_Map.inMapBounds(this.x, this.z) || !constraint.allows(this.x, this.z)) return undefined; var retVec = []; var size = getMapSize(); var gotRet = new Array(size).fill(0).map(p => new Uint8Array(size)); // booleans var radius = Math.sqrt(this.size / Math.PI); var perim = 4 * radius * 2 * Math.PI; var intPerim = Math.ceil(perim); var ctrlPts = 1 + Math.floor(1.0/Math.max(this.smoothness,1.0/intPerim)); if (ctrlPts > radius * 2 * Math.PI) ctrlPts = Math.floor(radius * 2 * Math.PI) + 1; var noise = new Float32Array(intPerim); //float32 var ctrlCoords = new Float32Array(ctrlPts+1); //float32 var ctrlVals = new Float32Array(ctrlPts+1); //float32 // Generate some interpolated noise for (var i=0; i < ctrlPts; i++) { ctrlCoords[i] = i * perim / ctrlPts; ctrlVals[i] = randFloat(0, 2); } var c = 0; var looped = 0; for (var i=0; i < intPerim; i++) { if (ctrlCoords[(c+1) % ctrlPts] < i && !looped) { c = (c+1) % ctrlPts; if (c == ctrlPts-1) looped = 1; } noise[i] = cubicInterpolation( 1, (i - ctrlCoords[c]) / ((looped ? perim : ctrlCoords[(c + 1) % ctrlPts]) - ctrlCoords[c]), ctrlVals[(c + ctrlPts - 1) % ctrlPts], ctrlVals[c], ctrlVals[(c + 1) % ctrlPts], ctrlVals[(c + 2) % ctrlPts]); } var failed = 0; for (var p=0; p < intPerim; p++) { 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; for (var k = 0; k < Math.ceil(r); ++k) { var i = Math.floor(xx); var j = Math.floor(yy); if (g_Map.inMapBounds(i, j) && constraint.allows(i, j)) { if (!gotRet[i][j]) { // Only include each point once gotRet[i][j] = 1; retVec.push({ "x": i, "z": j }); } } else failed++; xx += s; yy += c; } } return failed > this.size * this.failFraction ? undefined : retVec; }; ///////////////////////////////////////////////////////////////////////////////////////// // Chain Placer // // Class for generating a more random clump of points it randomly creates circles around the edges of the current clump // // minRadius: minimum radius of the circles // maxRadius: maximum radius of the circles // numCircles: the number of the circles // failfraction: Percentage of place attempts allowed to fail (optional) // x, z: 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) // ///////////////////////////////////////////////////////////////////////////////////////// function ChainPlacer(minRadius, maxRadius, numCircles, failFraction, x, z, fcc, q) { this.minRadius = minRadius; this.maxRadius = maxRadius; this.numCircles = numCircles; this.failFraction = failFraction !== undefined ? failFraction : 0; this.x = x !== undefined ? x : -1; this.z = z !== undefined ? z : -1; this.fcc = fcc !== undefined ? fcc : 0; this.q = q !== undefined ? q : []; } ChainPlacer.prototype.place = function(constraint) { // Preliminary bounds check if (!g_Map.inMapBounds(this.x, this.z) || !constraint.allows(this.x, this.z)) return undefined; var retVec = []; var size = getMapSize(); var failed = 0, count = 0; var queueEmpty = !this.q.length; var 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) { var [cx, cz] = pickRandom(edges); if (queueEmpty) var radius = randIntInclusive(this.minRadius, this.maxRadius); else { var radius = this.q.pop(); queueEmpty = !this.q.length; } var sx = cx - radius, lx = cx + radius; var sz = cz - radius, lz = cz + radius; 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) { dx = ix - cx; dz = iz - cz; if (dx * dx + dz * dz <= radius2) { if (g_Map.inMapBounds(ix, iz) && constraint.allows(ix, iz)) { var state = gotRet[ix][iz]; if (state == -1) { retVec.push({ "x": ix, "z": iz }); gotRet[ix][iz] = -2; } else if (state >= 0) { var s = edges.splice(state, 1); gotRet[ix][iz] = -2; var edgesLength = edges.length; for (var k = state; k < edges.length; ++k) --gotRet[edges[k][0]][edges[k][1]]; } } else ++failed; ++count; } } for (var ix = sx; ix <= lx; ++ix) for (var 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; if (gotRet[ix][iz] == -2) { 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; } } } } } return failed > count * this.failFraction ? undefined : retVec; }; Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/player.js (revision 20971) @@ -1,662 +1,661 @@ /** * @file These functions locate and place the starting entities of players. */ var g_NomadTreasureTemplates = { "food": "gaia/special_treasure_food_jars", "wood": "gaia/special_treasure_wood", "stone": "gaia/special_treasure_stone", "metal": "gaia/special_treasure_metal" }; /** * These are identifiers of functions that can generate parts of a player base. * There must be a function starting with placePlayerBase and ending with this name. * This is a global so mods can extend this from external files. */ var g_PlayerBaseFunctions = [ // Possibly mark player class first here and use it afterwards "CityPatch", // Create the largest and most important entities first "Trees", "Mines", "Treasures", "Berries", "Chicken", "Decoratives" ]; /** * Gets the default starting entities for the civ of the given player, as defined by the civ file. */ function getStartingEntities(playerID) { return g_CivData[getCivCode(playerID)].StartEntities; } /** * Places the given entities at the given location (typically a civic center and starting units). * @param location - A Vector2D specifying tile coordinates. * @param civEntities - An array of objects with the Template property and optionally a Count property. * The first entity is placed in the center, the other ones surround it. */ function placeStartingEntities(location, playerID, civEntities, dist = 6, orientation = BUILDING_ORIENTATION) { // Place the central structure let i = 0; let firstTemplate = civEntities[i].Template; if (firstTemplate.startsWith("structures/")) { placeObject(location.x, location.y, firstTemplate, playerID, orientation); ++i; } // Place entities surrounding it let space = 2; for (let j = i; j < civEntities.length; ++j) { let angle = orientation - Math.PI * (1 - j / 2); 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); } } /** * 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) { placeStartingEntities(location, playerID, getStartingEntities(playerID), dist, orientation); placeStartingWalls(location.x, location.y, 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) { let civ = getCivCode(playerID); if (civ != "iber" || getMapSize() <= 128) return; if (wallType == "towers") placePolygonalWall(x, z, 15, ["entry"], "tower", civ, playerID, orientation, 7); else if (wallType) placeGenericFortress(x, z, 20, playerID); } /** * Places the civic center and starting resources for all given players. */ function placePlayerBases(playerBaseArgs) { let [playerIDs, playerPosition] = playerBaseArgs.PlayerPlacement; for (let i = 0; i < getNumPlayers(); ++i) { playerBaseArgs.playerID = playerIDs[i]; playerBaseArgs.playerPosition = playerPosition[i]; placePlayerBase(playerBaseArgs); } } /** * Places the civic center and starting resources. */ function placePlayerBase(playerBaseArgs) { if (isNomad()) return; log("Creating base for player " + playerBaseArgs.playerID + "..."); placeCivDefaultStartingEntities(playerBaseArgs.playerPosition, playerBaseArgs.playerID, playerBaseArgs.Walls !== undefined ? playerBaseArgs.Walls : true); if (playerBaseArgs.PlayerTileClass !== undefined) addCivicCenterAreaToClass(playerBaseArgs.playerPosition, playerBaseArgs.PlayerTileClass); for (let functionID of g_PlayerBaseFunctions) { let funcName = "placePlayerBase" + functionID; let func = global[funcName]; if (!func) throw new Error("Could not find " + funcName); if (!playerBaseArgs[functionID]) continue; let args = playerBaseArgs[functionID]; // Copy some global arguments to the arguments for each function for (let prop of ["playerID", "playerPosition", "BaseResourceClass", "baseResourceConstraint"]) args[prop] = playerBaseArgs[prop]; func(args); } } function defaultPlayerBaseRadius() { return scaleByMapSize(15, 25); } /** * Marks the corner and center tiles of an area that is about the size of a Civic Center with the given TileClass. * Used to prevent resource collisions with the Civic Center. */ function addCivicCenterAreaToClass(position, tileClass) { let pos = position.clone().round(); addToClass(pos.x, pos.y, tileClass); addToClass(pos.x, pos.y + 5, tileClass); addToClass(pos.x, pos.y - 5, tileClass); addToClass(pos.x + 5, pos.y, tileClass); addToClass(pos.x - 5, pos.y, tileClass); } /** * Helper function. */ function getPlayerBaseArgs(playerBaseArgs) { let baseResourceConstraint = playerBaseArgs.BaseResourceClass && avoidClasses(playerBaseArgs.BaseResourceClass, 4); if (playerBaseArgs.baseResourceConstraint) baseResourceConstraint = new AndConstraint([baseResourceConstraint, playerBaseArgs.baseResourceConstraint]); return [ (property, defaultVal) => playerBaseArgs[property] === undefined ? defaultVal : playerBaseArgs[property], playerBaseArgs.playerPosition, baseResourceConstraint ]; } function placePlayerBaseCityPatch(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let painters = []; if (args.outerTerrain && args.innerTerrain) painters.push(new LayeredPainter([args.outerTerrain, args.innerTerrain], [get("width", 1)])); if (args.painters) painters = painters.concat(args.painters); createArea( new ClumpPlacer( Math.floor(diskArea(get("radius", defaultPlayerBaseRadius() / 3))), get("coherence", 0.6), get("smoothness", 0.3), get("failFraction", 10), - Math.round(basePosition.x), - Math.round(basePosition.y)), + basePosition), painters); } function placePlayerBaseChicken(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let i = 0; i < get("groupCount", 2); ++i) { let success = false; for (let tries = 0; tries < get("maxTries", 30); ++tries) { let loc = 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), 0, baseResourceConstraint)) { success = true; break; } } if (!success) { error("Could not place chicken for player " + args.playerID); return; } } } 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); 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), 0, baseResourceConstraint)) return; } error("Could not place berries for player " + args.playerID); } function placePlayerBaseMines(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let angleBetweenMines = randFloat(get("minAngle", Math.PI / 6), get("maxAngle", Math.PI / 3)); let mineCount = args.types.length; let groupElements = []; if (args.groupElements) groupElements = groupElements.concat(args.groupElements); for (let tries = 0; tries < get("maxTries", 75); ++tries) { // First find a place where all mines can be placed let pos = []; let startAngle = randomAngle(); for (let i = 0; i < mineCount; ++i) { let angle = startAngle + angleBetweenMines * (i + (mineCount - 1) / 2); pos[i] = new Vector2D(0, get("distance", 12)).rotate(angle).add(basePosition).round(); if (!g_Map.validT(pos[i].x, pos[i].y) || !baseResourceConstraint.allows(pos[i].x, pos[i].y)) { pos = undefined; break; } } if (!pos) continue; // Place the mines for (let i = 0; i < mineCount; ++i) { if (args.types[i].type && args.types[i].type == "stone_formation") { createStoneMineFormation(pos[i].x, pos[i].y, args.types[i].template, args.types[i].terrain); addToClass(pos[i].x, pos[i].y, args.BaseResourceClass); continue; } createObjectGroup( new SimpleGroup( [new SimpleObject(args.types[i].template, 1, 1, 0, 0)].concat(groupElements), true, args.BaseResourceClass, pos[i].x, pos[i].y), 0); } return; } error("Could not place mines for player " + args.playerID); } function placePlayerBaseTrees(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); let num = Math.floor(get("count", scaleByMapSize(7, 20))); 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(); if (createObjectGroup( new SimpleGroup( [new SimpleObject(args.template, num, num, get("minDistGroup", 0), get("maxDistGroup", 5))], false, args.BaseResourceClass, loc.x, loc.y), 0, baseResourceConstraint)) return; } error("Could not place starting trees for player " + args.playerID); } function placePlayerBaseTreasures(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let resourceTypeArgs of args.types) { get = (property, defaultVal) => resourceTypeArgs[property] === undefined ? defaultVal : resourceTypeArgs[property]; let success = false; 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(); 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), 0, baseResourceConstraint)) { success = true; break; } } if (!success) { error("Could not place treasure " + resourceTypeArgs.template + " for player " + args.playerID); return; } } } /** * Typically used for placing grass tufts around the civic centers. */ function placePlayerBaseDecoratives(args) { let [get, basePosition, baseResourceConstraint] = getPlayerBaseArgs(args); for (let i = 0; i < get("count", scaleByMapSize(2, 5)); ++i) { 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(); if (createObjectGroup( new SimpleGroup( [new SimpleObject(args.template, get("minCount", 2), get("maxCount", 5), 0, 1)], false, args.BaseResourceClass, loc.x, loc.y), 0, baseResourceConstraint)) { success = true; break; } } if (!success) // Don't warn since the decoratives are not important return; } } function placePlayersNomad(playerClass, constraints) { if (!isNomad()) return; let distance = scaleByMapSize(60, 240); let constraint = new AndConstraint(constraints); let numPlayers = getNumPlayers(); let playerIDs = shuffleArray(sortAllPlayers()); let playerPosition = []; for (let i = 0; i < numPlayers; ++i) { log("Determine starting units for player " + playerIDs[i] + "..."); let objects = getStartingEntities(playerIDs[i]).filter(ents => ents.Template.startsWith("units/")).map( ents => new SimpleObject(ents.Template, ents.Count || 1, ents.Count || 1, 1, 3)); log("Ensure resources for a civic center..."); let ccCost = Engine.GetTemplate("structures/" + getCivCode(playerIDs[i]) + "_civil_centre").Cost.Resources; for (let resourceType in ccCost) { let treasureTemplate = g_NomadTreasureTemplates[resourceType]; let count = Math.max(0, Math.ceil( (ccCost[resourceType] - (g_MapSettings.StartingResources || 0)) / Engine.GetTemplate(treasureTemplate).ResourceSupply.Amount)); objects.push(new SimpleObject(treasureTemplate, count, count, 3, 5)); } log("Placing player units..."); let group = new SimpleGroup(objects, true, playerClass); let success = false; for (let distanceFactor of [1, 1/2, 1/4, 0]) { 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); break; } } if (!success) throw new Error("Could not place starting units for player " + playerIDs[i] + "!"); } return [playerIDs, playerPosition]; } /** * Sorts an array of player IDs by team index. Players without teams come first. * Randomize order for players of the same team. */ function sortPlayers(playerIDs) { return shuffleArray(playerIDs).sort((playerID1, playerID2) => getPlayerTeam(playerID1) - getPlayerTeam(playerID2)); } /** * Randomize playerIDs but sort by team. * * @returns {Array} - every item is an array of player indices */ function sortAllPlayers() { let playerIDs = []; for (let i = 0; i < getNumPlayers(); ++i) playerIDs.push(i+1); return sortPlayers(playerIDs); } /** * Rearrange order so that teams of neighboring players alternate (if the given IDs are sorted by team). */ function primeSortPlayers(playerIDs) { if (!playerIDs.length) return []; let prime = []; for (let i = 0; i < Math.ceil(playerIDs.length / 2); ++i) { prime.push(playerIDs[i]); prime.push(playerIDs[playerIDs.length - 1 - i]); } return prime; } function primeSortAllPlayers() { return primeSortPlayers(sortAllPlayers()); } /** * Determine player starting positions on a circular pattern. */ function playerPlacementCircle(radius, startingAngle = undefined, center = undefined) { let startAngle = startingAngle !== undefined ? startingAngle : randomAngle(); let [playerPosition, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius, center || getMapCenter()); return [sortAllPlayers(), playerPosition.map(p => p.round()), playerAngle, startAngle]; } /** * Determine player starting positions on a circular pattern, with a custom angle for each player. * Commonly used for gulf terrains. */ function playerPlacementCustomAngle(radius, center, playerAngleFunc) { let playerPosition = []; let playerAngle = []; let numPlayers = getNumPlayers(); for (let i = 0; i < numPlayers; ++i) { playerAngle[i] = playerAngleFunc(i); playerPosition[i] = Vector2D.add(center, new Vector2D(radius, 0).rotate(-playerAngle[i])).round(); } return [playerPosition, playerAngle]; } /** * Returns player starting positions located on two parallel lines, typically used by central river maps. * If there are two teams with an equal number of players, each team will occupy exactly one line. * Angle 0 means the players are placed in north to south direction, i.e. along the Z axis. */ function playerPlacementRiver(angle, width, center = undefined) { let numPlayers = getNumPlayers(); let numPlayersEven = numPlayers % 2 == 0; let mapSize = getMapSize(); let centerPosition = center || getMapCenter(); let playerPosition = []; for (let i = 0; i < numPlayers; ++i) { let currentPlayerEven = i % 2 == 0; let offsetDivident = numPlayersEven || currentPlayerEven ? (i + 1) % 2 : 0; let offsetDivisor = numPlayersEven ? 0 : currentPlayerEven ? +1 : -1; playerPosition[i] = new Vector2D( width * (i % 2) + (mapSize - width) / 2, fractionToTiles(((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1)) ).rotateAround(angle, centerPosition).round(); } return [primeSortAllPlayers(), playerPosition]; } /*** * Returns starting positions located on two parallel lines. * The locations on the first line are shifted in comparison to the other line. */ function playerPlacementLine(angle, center, width) { let playerPosition = []; let numPlayers = getNumPlayers(); for (let i = 0; i < numPlayers; ++i) playerPosition[i] = Vector2D.add( center, new Vector2D( fractionToTiles((i + 1) / (numPlayers + 1) - 0.5), width * (i % 2 - 1/2) ).rotate(angle) ).round(); return playerPosition; } /** * Sorts the playerIDs so that team members are as close as possible. */ function sortPlayersByLocation(startLocations) { // Sort start locations to form a "ring" let startLocationOrder = sortPointsShortestCycle(startLocations); let newStartLocations = []; for (let i = 0; i < startLocations.length; ++i) newStartLocations.push(startLocations[startLocationOrder[i]]); startLocations = newStartLocations; // Sort players by team let playerIDs = []; let teams = []; for (let i = 0; i < g_MapSettings.PlayerData.length - 1; ++i) { playerIDs.push(i+1); let t = g_MapSettings.PlayerData[i + 1].Team; if (teams.indexOf(t) == -1 && t !== undefined) teams.push(t); } playerIDs = sortPlayers(playerIDs); if (!teams.length) return [playerIDs, startLocations]; // Minimize maximum distance between players within a team let minDistance = Infinity; let bestShift; for (let s = 0; s < playerIDs.length; ++s) { let maxTeamDist = 0; for (let pi = 0; pi < playerIDs.length - 1; ++pi) { let t1 = getPlayerTeam(playerIDs[(pi + s) % playerIDs.length]); if (teams.indexOf(t1) === -1) continue; for (let pj = pi + 1; pj < playerIDs.length; ++pj) { if (t1 != getPlayerTeam(playerIDs[(pj + s) % playerIDs.length])) continue; maxTeamDist = Math.max( maxTeamDist, Math.euclidDistance2D( startLocations[pi].x, startLocations[pi].y, startLocations[pj].x, startLocations[pj].y)); } } if (maxTeamDist < minDistance) { minDistance = maxTeamDist; bestShift = s; } } if (bestShift) { let newPlayerIDs = []; for (let i = 0; i < playerIDs.length; ++i) newPlayerIDs.push(playerIDs[(i + bestShift) % playerIDs.length]); playerIDs = newPlayerIDs; } return [playerIDs, startLocations]; } Index: ps/trunk/binaries/data/mods/public/maps/random/sahel_watering_holes.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/sahel_watering_holes.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/sahel_watering_holes.js (revision 20971) @@ -1,391 +1,391 @@ Engine.LoadLibrary("rmgen"); const tGrass = "savanna_grass_a"; const tForestFloor = "savanna_forestfloor_a"; const tCliff = "savanna_cliff_b"; const tDirtRocksA = "savanna_dirt_rocks_c"; const tDirtRocksB = "savanna_dirt_rocks_a"; const tDirtRocksC = "savanna_dirt_rocks_b"; const tHill = "savanna_cliff_a"; const tRoad = "savanna_tile_a_red"; const tRoadWild = "savanna_tile_a_red"; const tGrassPatch = "savanna_grass_b"; const tShore = "savanna_riparian_bank"; const tWater = "savanna_riparian_wet"; const oBaobab = "gaia/flora_tree_baobab"; const oFig = "gaia/flora_tree_fig"; const oBerryBush = "gaia/flora_bush_berry"; const oWildebeest = "gaia/fauna_wildebeest"; const oFish = "gaia/fauna_fish"; const oGazelle = "gaia/fauna_gazelle"; const oElephant = "gaia/fauna_elephant_african_bush"; const oGiraffe = "gaia/fauna_giraffe"; const oZebra = "gaia/fauna_zebra"; const oStoneLarge = "gaia/geology_stonemine_desert_quarry"; const oStoneSmall = "gaia/geology_stone_savanna_small"; const oMetalLarge = "gaia/geology_metal_savanna_slabs"; const aGrass = "actor|props/flora/grass_savanna.xml"; const aGrassShort = "actor|props/flora/grass_medit_field.xml"; const aRockLarge = "actor|geology/stone_savanna_med.xml"; const aRockMedium = "actor|geology/stone_savanna_med.xml"; const aBushMedium = "actor|props/flora/bush_desert_dry_a.xml"; const aBushSmall = "actor|props/flora/bush_dry_a.xml"; const pForest = [tForestFloor + TERRAIN_SEPARATOR + oBaobab, tForestFloor + TERRAIN_SEPARATOR + oBaobab, tForestFloor]; const heightSeaGround = -4; const heightShallows = -2; const heightLand = 3; const heightHill = 35; const heightOffsetBump = 2; InitMap(heightLand, tGrass); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clShallows = createTileClass(); var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oBaobab, "count": 5 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(20); log("Creating rivers..."); var riverStart = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.15), mapCenter)[0]; var riverEnd = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.49), mapCenter)[0]; for (let i = 0; i < numPlayers; ++i) { let neighborID = (i + 1) % numPlayers; log("Creating lake near the center..."); createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 3), 0.95, 0.6, 10, riverStart[i].x, riverStart[i].y), + new ClumpPlacer(diskArea(scaleByMapSize(5, 30)), 0.95, 0.6, 10, riverStart[i]), [ new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), paintClass(clWater) ], avoidClasses(clPlayer, 5)); log("Creating the river between the players..."); createArea( new PathPlacer(riverStart[i], riverEnd[i], scaleByMapSize(10, 50), 0.2, 3 * scaleByMapSize(1, 4), 0.2, 0.05), [ new LayeredPainter([tShore, tWater, tWater], [1, 3]), new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), paintClass(clWater) ], avoidClasses(clPlayer, 5)); log("Creating lake near the map border..."); createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, riverEnd[i].x, riverEnd[i].y), + new ClumpPlacer(diskArea(scaleByMapSize(5, 22)), 0.95, 0.6, 10, riverEnd[i]), [ new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), paintClass(clWater) ], avoidClasses(clPlayer, 5)); log("Creating shallows between neighbors..."); createPassage({ "start": playerPosition[i], "end": playerPosition[neighborID], "startWidth": 10, "endWidth": 10, "smoothWidth": 4, "startHeight": heightShallows, "endHeight": heightShallows, "maxHeight": heightShallows, "tileClass": clShallows }); log("Creating animals in shallows..."); let shallowPosition = Vector2D.average([playerPosition[i], playerPosition[neighborID]]).round(); let objects = [ 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); } paintTerrainBasedOnHeight(-6, 2, 1, tWater); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), avoidClasses(clWater, 2, clPlayer, 20), scaleByMapSize(100, 200)); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), [ new LayeredPainter([tGrass, tCliff, tHill], [1, 2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 3), paintClass(clHill) ], avoidClasses(clPlayer, 20, clHill, 15, clWater, 3), scaleByMapSize(1, 4) * numPlayers); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(160, 900, 0.02); var types = [ [[tForestFloor, tGrass, pForest], [tForestFloor, pForest]] ]; var size = forestTrees / (0.5 * scaleByMapSize(2,8) * numPlayers); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses(clPlayer, 20, clForest, 10, clHill, 0, clWater, 2), num ); Engine.SetProgress(50); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter( [[tGrass, tDirtRocksA], [tDirtRocksA, tDirtRocksB], [tDirtRocksB, tDirtRocksC]], [1, 1]), paintClass(clDirt) ], avoidClasses(clWater, 3, clForest, 0, clHill, 0, clPlayer, 20), scaleByMapSize(15, 45)); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tGrassPatch), avoidClasses(clWater, 3, clForest, 0, clHill, 0, clPlayer, 20), scaleByMapSize(15, 45)); Engine.SetProgress(55); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1), scaleByMapSize(4,16), 100 ); Engine.SetProgress(65); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(8, 131), 50 ); Engine.SetProgress(70); log("Creating wildebeest..."); group = new SimpleGroup( [new SimpleObject(oWildebeest, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 5), 3 * numPlayers, 50 ); Engine.SetProgress(75); log("Creating gazelle..."); group = new SimpleGroup( [new SimpleObject(oGazelle, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 5), 3 * numPlayers, 50 ); log("Creating elephant..."); group = new SimpleGroup( [new SimpleObject(oElephant, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 5), 3 * numPlayers, 50 ); log("Creating giraffe..."); group = new SimpleGroup( [new SimpleObject(oGiraffe, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 5), 3 * numPlayers, 50 ); log("Creating zebra..."); group = new SimpleGroup( [new SimpleObject(oZebra, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 5), 3 * numPlayers, 50 ); log("Creating fish..."); group = new SimpleGroup( [new SimpleObject(oFish, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clFood, 20), stayClasses(clWater, 6)], 25 * numPlayers, 60 ); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50 ); Engine.SetProgress(85); createStragglerTrees( [oBaobab, oBaobab, oBaobab, oFig], avoidClasses(clWater, 5, clForest, 1, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); var planetm = 4; log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 2), planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clForest, 0), planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(95); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 1, clPlayer, 1), planetm * scaleByMapSize(13, 200), 50 ); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clHill, 4, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)); setSkySet("sunny"); setSunRotation(randomAngle()); setSunElevation(Math.PI * randFloat(1/5, 1/4)); setWaterColor(0.478,0.42,0.384); // greyish setWaterTint(0.58,0.22,0.067); // reddish setWaterMurkiness(0.87); setWaterWaviness(0.5); setWaterType("clap"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/survivalofthefittest.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/survivalofthefittest.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/survivalofthefittest.js (revision 20971) @@ -1,203 +1,203 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tHill = g_Terrains.hill; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tTier4Terrain = g_Terrains.tier4Terrain; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const aWaypointFlag = "actor|props/special/common/waypoint_flag.xml"; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const oTreasureSeeker = "undeletable|skirmish/units/default_support_female_citizen"; const triggerPointAttacker = "trigger/trigger_point_A"; const triggerPointTreasures = [ "trigger/trigger_point_B", "trigger/trigger_point_C", "trigger/trigger_point_D" ]; const heightLand = 3; const heightHill = 30; InitMap(heightHill, tMainTerrain); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); var clWomen = createTileClass(); log("Creating central area..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.15)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.15)), 0.7, 0.1, 10, mapCenter), [ new LayeredPainter([tMainTerrain, tMainTerrain], [3]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 3), paintClass(clLand) ]); Engine.SetProgress(10); var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.3)); var halfway = distributePointsOnCircle(numPlayers, startAngle, fractionToTiles(0.375), mapCenter)[0].map(v => v.round()); var attacker = distributePointsOnCircle(numPlayers, startAngle, fractionToTiles(0.45), mapCenter)[0].map(v => v.round()); var passage = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]; log("Creating player bases and attacker points..."); for (let i = 0; i < numPlayers; ++i) { placeStartingEntities(playerPosition[i], playerIDs[i], getStartingEntities(playerIDs[i]).filter(ent => ent.Template.indexOf("civil_centre") != -1 || ent.Template.indexOf("infantry") != -1)); placePlayerBaseDecoratives({ "playerPosition": playerPosition[i], "template": aGrassShort, "BaseResourceClass": clBaseResource }); log("Creating passage separating players..."); createArea( new PathPlacer(mapCenter, passage[i], scaleByMapSize(14, 24), 0.4, scaleByMapSize(3, 9), 0.2, 0.05), [ new LayeredPainter([tMainTerrain, tMainTerrain], [1]), new SmoothElevationPainter(ELEVATION_SET, heightLand, 4) ]); 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); 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); log("Preventing mountains in the area between player and attackers..."); addCivicCenterAreaToClass(playerPosition[i], clPlayer); addToClass(attacker[i].x, attacker[i].y, clPlayer); addToClass(halfway[i].x, halfway[i].y, clPlayer); } Engine.SetProgress(20); paintTerrainBasedOnHeight(3.12, 29, 1, tCliff); paintTileClassBasedOnHeight(3.12, 29, 1, clHill); for (let triggerPointTreasure of triggerPointTreasures) createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(triggerPointTreasure, 1, 1, 0, 0)], true, clWomen), 0, [avoidClasses(clForest, 5, clPlayer, 5, clHill, 5), stayClasses(clLand, 5)], scaleByMapSize(40, 140), 100 ); Engine.SetProgress(25); createBumps(stayClasses(clLand, 5)); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); createForests( [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2], [avoidClasses(clPlayer, 20, clForest, 5, clHill, 0, clBaseResource,2, clWomen, 5), stayClasses(clLand, 4)], clForest, forestTrees); Engine.SetProgress(30); if (randBool()) createHills( [tMainTerrain, tCliff, tHill], [avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5), stayClasses(clLand, 5)], clHill, scaleByMapSize(10, 60) * numPlayers); else createMountains( tCliff, [avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5), stayClasses(clLand, 5)], clHill, scaleByMapSize(10, 60) * numPlayers); Engine.SetProgress(40); createHills( [tCliff, tCliff, tHill], avoidClasses(clPlayer, 20, clHill, 5, clBaseResource, 3, clWomen, 5, clLand, 5), clHill, scaleByMapSize(15, 90) * numPlayers, undefined, undefined, undefined, undefined, 55); Engine.SetProgress(50); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1], [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12, clWomen, 5), stayClasses(clLand, 5)], scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], tTier4Terrain, [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12, clWomen, 5), stayClasses(clLand, 5)], scaleByMapSize(15, 45), clDirt); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; createDecoration( [ [new SimpleObject(aRockMedium, 1, 3, 0, 1)], [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], [new SimpleObject(aGrassShort, 1, 2, 0, 1)], [new SimpleObject(aGrass, 2,4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)], [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)] ], [ scaleByMapSize(16, 262), scaleByMapSize(8, 131), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200), planetm * scaleByMapSize(13, 200) ], [avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 5)]); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], [avoidClasses(clForest, 7, clHill, 1, clPlayer, 9), stayClasses(clLand, 7)], clForest, stragglerTrees); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js (revision 20971) @@ -1,1061 +1,1061 @@ /** * @file This library is used to generate different map variations on the map Unknown, Unknown Land and Unknown Nomad. */ /** * True if all players should be connected via land and false if river or islands can split some if not all the players. */ var g_AllowNaval; TILE_CENTERED_HEIGHT_MAP = true; setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.hill; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tShore = g_Terrains.shore; const tWater = g_Terrains.water; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oFish = g_Gaia.fish; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const oWoodTreasure = "gaia/special_treasure_wood"; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aReeds = g_Decoratives.reeds; const aLillies = g_Decoratives.lillies; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightSeaGround = -5; const heightLand = 3; const heightCliff = 3.12; const heightHill = 18; const heightOffsetBump = 2; InitMap(heightSeaGround, tWater); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); const mapBounds = getMapBounds(); var clPlayer = createTileClass(); var clPlayerTerritory = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clPeninsulaSteam = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); var clShallow = createTileClass(); var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, heightLand, 4); var unknownMapFunctions = { "land": [ "Continent", "CentralSea", "CentralRiver", "EdgeSeas", "Gulf", "Lakes", "Passes", "Lowlands", "Mainland" ], "naval": [ "Archipelago", "RiversAndLake" ] }; /** * The player IDs and locations shall only be determined by the landscape functions if it's not a nomad game, * because nomad maps randomize the locations after the terrain generation. * The locations should only determined by the landscape functions to avoid placing bodies of water and resources into civic centers and the starting resources. */ var playerIDs = sortAllPlayers(); var playerPosition = []; var g_StartingTreasures = false; var g_StartingWalls = true; function createUnknownMap() { let funcs = unknownMapFunctions.land; if (g_AllowNaval) funcs = funcs.concat(unknownMapFunctions.naval); global["unknown" + pickRandom(funcs)](); paintUnknownMapBasedOnHeight(); createUnknownPlayerBases(); createUnknownObjects(); placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2, clWater, 10)); } /** * Chain of islands or many disconnected islands. */ function unknownArchipelago() { g_StartingWalls = "towers"; g_StartingTreasures = true; let [pIDs, islandPosition] = playerPlacementCircle(fractionToTiles(0.35)); if (!isNomad()) { [playerIDs, playerPosition] = [pIDs, islandPosition]; markPlayerArea("large"); } log("Creating islands..."); let islandSize = diskArea(scaleByMapSize(17, 29)); for (let i = 0; i < numPlayers; ++i) createArea( - new ClumpPlacer(islandSize, 0.8, 0.1, 10, islandPosition[i].x, islandPosition[i].y), + new ClumpPlacer(islandSize, 0.8, 0.1, 10, islandPosition[i]), landElevationPainter); let type = isNomad() ? randIntInclusive(1, 2) : randIntInclusive(1, 3); if (type == 1) { log("Creating archipelago..."); createAreas( - new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10), + new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, 10), [ landElevationPainter, paintClass(clLand) ], null, scaleByMapSize(2, 5) * randIntInclusive(8, 14)); log("Creating shore jaggedness with small puddles..."); createAreas( new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, 1), [ new SmoothElevationPainter(ELEVATION_SET, heightLand, 4), paintClass(clLand) ], borderClasses(clLand, 6, 3), scaleByMapSize(12, 130) * 2, 150); } else if (type == 2) { log("Creating islands..."); createAreas( - new ClumpPlacer(Math.floor(islandSize * randFloat(0.6, 1.4)), 0.8, 0.1, randFloat(0.0, 0.2)), + new ClumpPlacer(islandSize * randFloat(0.6, 1.4), 0.8, 0.1, randFloat(0.0, 0.2)), [ landElevationPainter, paintClass(clLand) ], avoidClasses(clLand, 3, clPlayerTerritory, 3), scaleByMapSize(6, 10) * randIntInclusive(8, 14)); log("Creating small islands..."); createAreas( - new ClumpPlacer(Math.floor(islandSize * randFloat(0.3, 0.7)), 0.8, 0.1, 0.07), + new ClumpPlacer(islandSize * randFloat(0.3, 0.7), 0.8, 0.1, 0.07), [ new SmoothElevationPainter(ELEVATION_SET, heightLand, 6), paintClass(clLand) ], avoidClasses(clLand, 3, clPlayerTerritory, 3), scaleByMapSize(2, 6) * randIntInclusive(6, 15), 25); } else if (type == 3) { log("Creating tight islands..."); createAreas( - new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10), + new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, 10), [ landElevationPainter, paintClass(clLand) ], avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3), scaleByMapSize(2, 5) * randIntInclusive(8, 14)); } } /** * Disk shaped mainland with water on the edge. */ function unknownContinent() { let waterHeight = -5; if (!isNomad()) { log("Ensuring player area..."); [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25)); markPlayerArea("small"); for (let i = 0; i < numPlayers; ++i) createArea( new ChainPlacer( 2, Math.floor(scaleByMapSize(5, 9)), Math.floor(scaleByMapSize(5, 20)), 1, playerPosition[i].x, playerPosition[i].y, 0, [Math.floor(scaleByMapSize(23, 50))]), [ landElevationPainter, paintClass(clLand) ]); } log("Creating continent..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, 10, mapCenter), [ landElevationPainter, paintClass(clLand) ]); if (randBool(1/3)) { log("Creating peninsula (i.e. half the map not being surrounded by water)..."); let angle = randomAngle(); let peninsulaPosition1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.25), 0).rotate(-angle)); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, 10, peninsulaPosition1.x, peninsulaPosition1.y), + new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, 10, peninsulaPosition1), [ landElevationPainter, paintClass(clLand) ]); log("Remembering to not paint shorelines into the peninsula..."); let peninsulaPosition2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.35), 0).rotate(-angle)); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.33)), 0.9, 0.01, 10, peninsulaPosition2.x, peninsulaPosition2.y), + new ClumpPlacer(diskArea(fractionToTiles(0.33)), 0.9, 0.01, 10, peninsulaPosition2), paintClass(clPeninsulaSteam)); } createShoreJaggedness(waterHeight, clLand, 7); } /** * Creates a huge central river, possibly connecting the riversides with a narrow piece of land. */ function unknownCentralSea() { let waterHeight = -3; let startAngle = randomAngle(); let [riverStart, riverEnd] = centralRiverCoordinates(startAngle); paintRiver({ "parallel": false, "start": riverStart, "end": riverEnd, "width": fractionToTiles(scaleByMapSize(0.27, 0.42) + randFloat(0, 0.08)), "fadeDist": scaleByMapSize(3, 12), "deviation": 0, "heightRiverbed": waterHeight, "heightLand": heightLand, "meanderShort": 20, "meanderLong": 0, "waterFunc": (position, height, riverFraction) => { if (height < 0) addToClass(position.x, position.y, clWater); }, "landFunc": (position, shoreDist1, shoreDist2) => { g_Map.setHeight(position, 3.1); addToClass(position.x, position.y, clLand); } }); if (!isNomad()) { [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.6)); markPlayerArea("small"); } if (!g_AllowNaval || randBool()) { log("Creating isthmus (i.e. connecting the two riversides with a big land passage)..."); let [isthmusStart, isthmusEnd] = centralRiverCoordinates(startAngle + Math.PI / 2); createArea( new PathPlacer( isthmusStart, isthmusEnd, scaleByMapSize(randIntInclusive(16, 24), randIntInclusive(100, 140)), 0.5, 3 * scaleByMapSize(1, 4), 0.1, 0.01), [ landElevationPainter, paintClass(clLand), unPaintClass(clWater) ]); } createExtensionsOrIslands(); // Don't createShoreJaggedness since it doesn't fit artistically here } /** * Creates a very small central river. */ function unknownCentralRiver() { let waterHeight = -4; let heightShallow = -2; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); let startAngle = randomAngle(); if (!isNomad()) { [playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.5)); markPlayerArea("large"); } log("Creating the main river..."); let [coord1, coord2] = centralRiverCoordinates(startAngle); createArea( new PathPlacer(coord1, coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01), new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), avoidClasses(clPlayerTerritory, 4)); log("Creating small water spots at the map border to ensure separation of players..."); for (let coord of [coord1, coord2]) createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(5, 10))), 0.95, 0.6, 10, coord.x, coord.y), + new ClumpPlacer(diskArea(scaleByMapSize(5, 10)), 0.95, 0.6, 10, coord), new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2), avoidClasses(clPlayerTerritory, 8)); if (!g_AllowNaval || randBool()) { log("Creating the shallows of the main river..."); for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i) { let location = fractionToTiles(randFloat(0.15, 0.85)); createPassage({ "start": new Vector2D(location, mapBounds.top).rotateAround(startAngle, mapCenter), "end": new Vector2D(location, mapBounds.bottom).rotateAround(startAngle, mapCenter), "startWidth": scaleByMapSize(8, 12), "endWidth": scaleByMapSize(8, 12), "smoothWidth": 2, "startHeight": heightShallow, "endHeight": heightShallow, "maxHeight": heightShallow, "tileClass": clShallow }); } } if (randBool(2/3)) createTributaryRivers( startAngle, randIntInclusive(8, scaleByMapSize(12, 16)), scaleByMapSize(10, 20), -4, [-6, -1.5], Math.PI / 5, clWater, clShallow, avoidClasses(clPlayerTerritory, 3)); } /** * Creates a circular lake in the middle and possibly a river between each player ("pizza slices"). */ function unknownRiversAndLake() { let waterHeight = -4; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); let startAngle; if (!isNomad()) { let playerAngle; [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); markPlayerArea("small"); } let lake = randBool(3/4); if (lake) { log("Creating lake..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.17)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.17)), 0.7, 0.1, 10, mapCenter), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ]); createShoreJaggedness(waterHeight, clWater, 3); } // Don't do this on nomad because the imbalances on the different islands are too drastic if (!isNomad() && (!lake || randBool(1/3))) { log("Creating small rivers separating players..."); for (let river of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) { createArea( new PathPlacer(mapCenter, river, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], avoidClasses(clPlayer, 5)); createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, river.x, river.y), + new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, 10, river), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0), paintClass(clWater) ], avoidClasses(clPlayer, 5)); } log("Creating lake..."); createArea( - new ClumpPlacer(Math.square(mapSize) * 0.005, 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.04)), 0.7, 0.1, 10, mapCenter), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ]); } if (!isNomad && lake && randBool()) { log("Creating small central island..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, 10, mapCenter), [ landElevationPainter, paintClass(clWater) ]); } } /** * Align players on a land strip with seas bordering on one or both sides that can hold islands. */ function unknownEdgeSeas() { let waterHeight = -4; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); let startAngle = randomAngle(); if (!isNomad()) { playerIDs = sortAllPlayers(); playerPosition = playerPlacementLine(startAngle + Math.PI / 2, mapCenter, fractionToTiles(0.2)); // Don't place the shoreline inside the CC, but possibly into the players territory markPlayerArea("small"); } for (let side of pickRandom([[0], [Math.PI], [0, Math.PI]])) paintRiver({ "parallel": true, "start": new Vector2D(mapBounds.left, mapBounds.top).rotateAround(side + startAngle, mapCenter), "end": new Vector2D(mapBounds.left, mapBounds.bottom).rotateAround(side + startAngle, mapCenter), "width": scaleByMapSize(80, randFloat(270, 320)), "fadeDist": scaleByMapSize(2, 8), "deviation": 0, "heightRiverbed": waterHeight, "heightLand": heightLand, "meanderShort": 20, "meanderLong": 0 }); createExtensionsOrIslands(); paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); createShoreJaggedness(waterHeight, clLand, 7, false); } /** * Land shaped like a concrescent moon around a central lake. */ function unknownGulf() { let waterHeight = -3; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); let startAngle = randomAngle(); if (!isNomad()) { log("Determining player locations..."); playerPosition = playerPlacementCustomAngle( fractionToTiles(0.35), mapCenter, i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))))[0]; markPlayerArea("large"); } let gulfParts = [ { "radius": fractionToTiles(0.16), "distance": fractionToTiles(0) }, { "radius": fractionToTiles(0.2), "distance": fractionToTiles(0.2) }, { "radius": fractionToTiles(0.22), "distance": fractionToTiles(0.49) } ]; for (let gulfPart of gulfParts) { let position = Vector2D.sub(mapCenter, new Vector2D(gulfPart.distance, 0).rotate(-startAngle)).round(); createArea( - new ClumpPlacer(diskArea(gulfPart.radius), 0.7, 0.05, 10, position.x, position.y), + new ClumpPlacer(diskArea(gulfPart.radius), 0.7, 0.05, 10, position), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], avoidClasses(clPlayerTerritory, defaultPlayerBaseRadius())); } } /** * Mainland style with some small random lakes. */ function unknownLakes() { let waterHeight = -5; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); if (!isNomad()) { [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); markPlayerArea("large"); } log("Creating lakes..."); createAreas( new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, 1), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5), paintClass(clWater) ], [avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()], scaleByMapSize(5, 16)); } /** * A large hill leaving players only a small passage to each of the the two neighboring players. */ function unknownPasses() { let heightMountain = 24; let waterHeight = -4; createArea( new MapBoundsPlacer(), new ElevationPainter(heightLand)); let playerAngle; let startAngle; if (!isNomad()) { [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); markPlayerArea("small"); } else startAngle = randomAngle(); for (let mountain of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0]) { log("Creating a mountain range between neighboring players..."); createArea( new PathPlacer(mapCenter, mountain, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), [ // More smoothing than this often results in the mountainrange becoming passable to one player. new SmoothElevationPainter(ELEVATION_SET, heightMountain, 1), paintClass(clWater) ], avoidClasses(clPlayer, 5)); log("Creating small mountain at the map border between the players to ensure separation of players..."); createArea( - new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, mountain.x, mountain.y), + new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, 10, mountain), new SmoothElevationPainter(ELEVATION_SET, heightMountain, 0), avoidClasses(clPlayer, 5)); } let passes = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mapCenter)[0]; for (let i = 0; i < numPlayers; ++i) { log("Create passages between neighboring players..."); createArea( new PathPlacer( passes[2 * i], passes[2 * ((i + 1) % numPlayers)], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), new SmoothElevationPainter(ELEVATION_SET, heightLand, 2)); } if (randBool(2/5)) { log("Create central lake..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.1)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.1)), 0.7, 0.1, 10, mapCenter), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3), paintClass(clWater) ]); } else { log("Fill area between the paths..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, 10, mapCenter), [ new SmoothElevationPainter(ELEVATION_SET, heightMountain, 4), paintClass(clWater) ]); } } /** * Land enclosed by a hill that leaves small areas for civic centers and large central place. */ function unknownLowlands() { let heightMountain = 30; log("Creating mountain that is going to separate players..."); createArea( new MapBoundsPlacer(), new ElevationPainter(heightMountain)); let playerAngle; let startAngle; if (!isNomad()) { [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); markPlayerArea("small"); } else startAngle = randomAngle(); log("Creating valleys enclosed by the mountain..."); let valleys = numPlayers; if (mapSize >= 128 && numPlayers <= 2 || mapSize >= 192 && numPlayers <= 3 || mapSize >= 320 && numPlayers <= 4 || mapSize >= 384 && numPlayers <= 5 || mapSize >= 448 && numPlayers <= 6) valleys *= 2; for (let valley of distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mapCenter)[0]) { log("Creating player valley..."); createArea( - new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valley.x, valley.y), + new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valley), [ new SmoothElevationPainter(ELEVATION_SET, heightLand, 2), paintClass(clLand) ]); log("Creating passes from player areas to the center..."); createArea( new PathPlacer(mapCenter, valley, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), [ landElevationPainter, paintClass(clWater) ]); } log("Creating the big central area..."); createArea( - new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.7, 0.1, 10, mapCenter.x, mapCenter.y), + new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.7, 0.1, 10, mapCenter), [ landElevationPainter, paintClass(clWater) ]); } /** * No water, no hills. */ function unknownMainland() { createArea( new MapBoundsPlacer(), new ElevationPainter(3)); if (!isNomad()) { [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); markPlayerArea("small"); } } function centralRiverCoordinates(angle) { return [ new Vector2D(mapBounds.left + 1, mapCenter.y), new Vector2D(mapBounds.right - 1, mapCenter.y) ].map(v => v.rotateAround(angle, mapCenter)); } function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true) { log("Creating shore jaggedness..."); for (let i = 0; i < 2; ++i) if (i || inwards) createAreas( new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, 1), [ new SmoothElevationPainter(ELEVATION_SET, i ? heightLand : waterHeight, 4), i ? paintClass(clLand) : unPaintClass(clLand) ], [ avoidClasses(clPlayer, 20, clPeninsulaSteam, 20), borderClasses(borderClass, shoreDist, shoreDist) ], scaleByMapSize(7, 130) * 2, 150); } function createExtensionsOrIslands() { let rnd = randIntInclusive(1, 3); if (rnd == 1) { log("Creating islands..."); createAreas( new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)), [ landElevationPainter, paintClass(clLand) ], avoidClasses(clLand, 3, clPlayer, 3), scaleByMapSize(2, 5) * randIntInclusive(8, 14)); } else if (rnd == 2) { log("Creating extentions..."); createAreas( new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07), [ landElevationPainter, paintClass(clLand) ], null, scaleByMapSize(2, 5) * randIntInclusive(8, 14)); } } /** * Prevent impassable terrain and resource collisions at the the civic center and starting resources. */ function markPlayerArea(size) { for (let i = 0; i < numPlayers; ++i) { addCivicCenterAreaToClass(playerPosition[i], clPlayer); if (size == "large") createArea( - new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, playerPosition[i]), paintClass(clPlayerTerritory)); } } function paintUnknownMapBasedOnHeight() { paintTerrainBasedOnHeight(heightCliff, 40, 1, tCliff); paintTerrainBasedOnHeight(3, heightCliff, 1, tMainTerrain); paintTerrainBasedOnHeight(1, 3, 1, tShore); paintTerrainBasedOnHeight(-8, 1, 2, tWater); unPaintTileClassBasedOnHeight(0, heightCliff, 1, clWater); unPaintTileClassBasedOnHeight(-6, 0, 1, clLand); paintTileClassBasedOnHeight(-6, 0, 1, clWater); paintTileClassBasedOnHeight(0, heightCliff, 1, clLand); paintTileClassBasedOnHeight(heightCliff, 40, 1, clHill); } /** * Place resources and decoratives after the player territory was marked. */ function createUnknownObjects() { log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), [avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)], randIntInclusive(0, scaleByMapSize(1, 2) * 200)); log("Creating hills..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)], randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9)) ); Engine.SetProgress(50); log("Creating forests..."); let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1)); let types = [ [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] ]; let size = numForest / (scaleByMapSize(2, 8) * numPlayers); let num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(numForest / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 2), stayClasses(clLand, 4)], num); Engine.SetProgress(50); log("Creating dirt patches..."); let patchCount = (currentBiome() == "savanna" ? 3 : 1) * scaleByMapSize(15, 45); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), paintClass(clDirt) ], [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], patchCount); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tTier4Terrain), [avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], patchCount); Engine.SetProgress(55); log("Creating stone mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock), 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), 100); log("Creating small stone quarries..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock), 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)], randIntInclusive(scaleByMapSize(2, 9),scaleByMapSize(9, 40)), 100); log("Creating metal mines..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal), 0, [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 2), stayClasses(clLand, 3)], randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), 100); Engine.SetProgress(65); log("Creating small decorative rocks..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true), 0, [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], scaleByMapSize(16, 262), 50); log("Creating large decorative rocks..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true), 0, [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)], scaleByMapSize(8, 131), 50); Engine.SetProgress(70); log("Creating deer..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood), 0, [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), 50); log("Creating berry bush..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood), 0, [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], randIntInclusive(1, 4) * numPlayers + 2, 50); Engine.SetProgress(75); log("Creating sheep..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood), 0, [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)], randIntInclusive(numPlayers + 3, 5 * numPlayers + 4), 50); log("Creating fish..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood), 0, avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 2, clFood, 20), randIntInclusive(15, 40) * numPlayers, 60); Engine.SetProgress(85); log("Creating straggler trees..."); types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4]; num = Math.floor(numStragglers / types.length); for (let type of types) createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest), 0, [avoidClasses(clWater, 1, clForest, 1, clHill, 2, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)], num); let planetm = currentBiome() == "tropic" ? 8 : 1; log("Creating small grass tufts..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]), 0, [avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)], planetm * scaleByMapSize(13, 200)); Engine.SetProgress(90); log("Creating large grass tufts..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -Math.PI / 8, Math.PI / 8)]), 0, [avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)], planetm * scaleByMapSize(13, 200)); Engine.SetProgress(95); log("Creating shallow flora..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]), 0, stayClasses(clShallow, 1), 60 * scaleByMapSize(13, 200), 80); log("Creating bushes..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]), 0, [avoidClasses(clWater, 1, clHill, 2, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)], planetm * scaleByMapSize(13, 200), 50); setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"])); setSunRotation(randomAngle()); setSunElevation(Math.PI * randFloat(1/5, 1/3)); } function createUnknownPlayerBases() { placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "BaseResourceClass": clBaseResource, "Walls": g_StartingWalls, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad, "painters": [ paintClass(clPlayer) ] }, "Chicken": { }, "Berries": { "template": oFruitBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Treasures": { "types": [ { "template": oWoodTreasure, "count": g_StartingTreasures ? 14 : 0 } ] }, "Trees": { "template": oTree1 }, "Decoratives": { "template": aGrassShort } }); } Index: ps/trunk/binaries/data/mods/public/maps/random/saharan_oases.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/saharan_oases.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/saharan_oases.js (revision 20971) @@ -1,240 +1,240 @@ Engine.LoadLibrary("rmgen"); const tPrimary = "desert_sand_dunes_100"; const tCity = "desert_city_tile"; const tCityPlaza = "desert_city_tile_plaza"; const tFineSand = "desert_sand_smooth"; const tDirt1 = "desert_dirt_rough_2"; const tSandDunes = "desert_sand_dunes_50"; const tDirt2 = "desert_dirt_rough"; const tDirtCracks = "desert_dirt_cracks"; const tShore = "desert_shore_stones"; const tWaterDeep = "desert_shore_stones_wet"; const tLush = "desert_grass_a"; const tSLush = "desert_grass_a_sand"; const oGrapeBush = "gaia/flora_bush_grapes"; const oCamel = "gaia/fauna_camel"; const oGazelle = "gaia/fauna_gazelle"; const oGoat = "gaia/fauna_goat"; const oStoneLarge = "gaia/geology_stonemine_desert_badlands_quarry"; const oStoneSmall = "gaia/geology_stone_desert_small"; const oMetalLarge = "gaia/geology_metal_desert_slabs"; const oDatePalm = "gaia/flora_tree_date_palm"; const oSDatePalm = "gaia/flora_tree_cretan_date_palm_short"; const oWoodTreasure = "gaia/special_treasure_wood"; const oFoodTreasure = "gaia/special_treasure_food_bin"; const aBush1 = "actor|props/flora/bush_desert_a.xml"; const aBush2 = "actor|props/flora/bush_desert_dry_a.xml"; const aBush3 = "actor|props/flora/bush_medit_sm_dry.xml"; const aBush4 = "actor|props/flora/plant_desert_a.xml"; const aDecorativeRock = "actor|geology/stone_desert_med.xml"; const pForest = [tLush + TERRAIN_SEPARATOR + oDatePalm, tLush + TERRAIN_SEPARATOR + oSDatePalm, tLush]; const heightLand = 1; const heightOffsetOasis = -3; InitMap(heightLand, tPrimary); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clTreasure = createTileClass(); var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35)); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tCityPlaza, "innerTerrain": tCity }, "Chicken": { }, "Berries": { "template": oGrapeBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oSDatePalm }, "Decoratives": { "template": aBush1 } }); Engine.SetProgress(30); log("Creating oases..."); var oasisRadius = fractionToTiles(scaleByMapSize(0.19, 0.22)); for (let i = 0; i < numPlayers; ++i) { let position = Vector2D.add(mapCenter, new Vector2D(oasisRadius, 0).rotate(-playerAngle[i])); createArea( - new ClumpPlacer(diskArea(scaleByMapSize(16, 60)) * 0.185, 0.6, 0.15, 0, position.x, position.y), + new ClumpPlacer(diskArea(scaleByMapSize(16, 60)) * 0.185, 0.6, 0.15, 0, position), [ new LayeredPainter( [tSLush ,[tLush, pForest], [tLush, pForest], tShore, tShore, tWaterDeep], [2, 2, 1, 3, 1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetOasis, 10), paintClass(clWater) ]); } Engine.SetProgress(50); log("Creating grass patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter( [[tDirt1, tSandDunes], [tSandDunes, tDirt2], [tDirt2, tDirt1]], [1, 1] ), paintClass(clDirt) ], avoidClasses(clForest, 0, clPlayer, 0, clWater, 1, clDirt, 5), scaleByMapSize(15, 45)); Engine.SetProgress(55); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter( [[tDirt2, tDirtCracks], [tDirt2, tFineSand], [tDirtCracks, tFineSand]], [1, 1] ), paintClass(clDirt) ], avoidClasses(clForest, 0, clDirt, 5, clPlayer, 0, clWater, 1), scaleByMapSize(15, 45)); Engine.SetProgress(60); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 26, clRock, 10, clWater, 1), 2*scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 26, clRock, 10, clWater, 1), 2*scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 1, clPlayer, 26, clMetal, 10, clRock, 5, clWater, 1), 2*scaleByMapSize(4,16), 100 ); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aDecorativeRock, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 1, clForest, 0, clPlayer, 0), scaleByMapSize(16, 262), 50 ); log("Creating shrubs..."); group = new SimpleGroup( [new SimpleObject(aBush2, 1,2, 0,1), new SimpleObject(aBush1, 1,3, 0,2), new SimpleObject(aBush4, 1,2, 0,1), new SimpleObject(aBush3, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 1, clPlayer, 0), scaleByMapSize(10, 100), 50 ); log("Creating small decorative rocks on mines..."); group = new SimpleGroup( [new SimpleObject(aDecorativeRock, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, stayClasses(clRock, 0), 5*scaleByMapSize(16, 262), 50 ); group = new SimpleGroup( [new SimpleObject(aDecorativeRock, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, stayClasses(clMetal, 0), 5*scaleByMapSize(16, 262), 50 ); log("Creating gazelles..."); group = new SimpleGroup([new SimpleObject(oGazelle, 5,7, 0,4)], true, clFood); createObjectGroupsDeprecated(group, 0, borderClasses(clWater, 8, 5), 6*scaleByMapSize(5,20), 50 ); log("Creating goats..."); group = new SimpleGroup([new SimpleObject(oGoat, 2,4, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, borderClasses(clWater, 8, 5), 5*scaleByMapSize(5,20), 50 ); log("Creating treasures..."); group = new SimpleGroup([new SimpleObject(oFoodTreasure, 1,1, 0,2)], true, clTreasure); createObjectGroupsDeprecated(group, 0, borderClasses(clWater, 8, 5), 3*scaleByMapSize(5,20), 50 ); group = new SimpleGroup([new SimpleObject(oWoodTreasure, 1,1, 0,2)], true, clTreasure); createObjectGroupsDeprecated(group, 0, borderClasses(clWater, 8, 5), 3*scaleByMapSize(5,20), 50 ); log("Creating camels..."); group = new SimpleGroup([new SimpleObject(oCamel, 2,4, 0,2)], true, clFood); createObjectGroupsDeprecated(group, 0, borderClasses(clWater, 14, 5), 5*scaleByMapSize(5,20), 50 ); placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clMetal, 4, clRock, 4, clFood, 2, clTreasure, 2)); setSkySet("sunny"); setSunColor(0.746, 0.718, 0.539); setWaterColor(0, 0.227, 0.843); setWaterTint(0, 0.545, 0.859); setWaterWaviness(1.0); setWaterType("clap"); setWaterMurkiness(0.5); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/snowflake_searocks.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/snowflake_searocks.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/snowflake_searocks.js (revision 20971) @@ -1,440 +1,440 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); setSelectedBiome(); const tMainTerrain = g_Terrains.mainTerrain; const tForestFloor1 = g_Terrains.forestFloor1; const tForestFloor2 = g_Terrains.forestFloor2; const tCliff = g_Terrains.cliff; const tTier1Terrain = g_Terrains.tier1Terrain; const tTier2Terrain = g_Terrains.tier2Terrain; const tTier3Terrain = g_Terrains.tier3Terrain; const tHill = g_Terrains.mainTerrain; const tRoad = g_Terrains.road; const tRoadWild = g_Terrains.roadWild; const tTier4Terrain = g_Terrains.tier4Terrain; const tWater = g_Terrains.water; const oTree1 = g_Gaia.tree1; const oTree2 = g_Gaia.tree2; const oTree3 = g_Gaia.tree3; const oTree4 = g_Gaia.tree4; const oTree5 = g_Gaia.tree5; const oFruitBush = g_Gaia.fruitBush; const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal; const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal; const oStoneLarge = g_Gaia.stoneLarge; const oStoneSmall = g_Gaia.stoneSmall; const oMetalLarge = g_Gaia.metalLarge; const aGrass = g_Decoratives.grass; const aGrassShort = g_Decoratives.grassShort; const aRockLarge = g_Decoratives.rockLarge; const aRockMedium = g_Decoratives.rockMedium; const aBushMedium = g_Decoratives.bushMedium; const aBushSmall = g_Decoratives.bushSmall; const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2]; const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1]; const heightIsland = 20; const heightSeaGround = -5; InitMap(heightSeaGround, tWater); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapCenter = getMapCenter(); var clPlayer = createTileClass(); var clForest = createTileClass(); var clDirt = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clLand = createTileClass(); const playerIslandRadius = scaleByMapSize(15, 30); const islandBetweenPlayerAndCenterDist = 0.16; const islandBetweenPlayerAndCenterRadius = 0.81; const centralIslandRadius = 0.36; var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); var numIslands = 0; var isConnected = []; var islandPos = []; function initIsConnected() { for (let m = 0; m < numIslands; ++m) { isConnected[m] = []; for (let n = 0; n < numIslands; ++n) isConnected[m][n] = 0; } } function createIsland(islandID, size, tileClass) { createArea( - new ClumpPlacer(size * diskArea(playerIslandRadius), 0.95, 0.6, 10, islandPos[islandID].x, islandPos[islandID].y), + new ClumpPlacer(size * diskArea(playerIslandRadius), 0.95, 0.6, 10, islandPos[islandID]), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightIsland, 2), paintClass(tileClass) ]); } function createIslandAtRadialLocation(playerID, islandID, playerIDOffset, distFromCenter, islandRadius) { let angle = startAngle + (playerID * 2 + playerIDOffset) * Math.PI / numPlayers; islandPos[islandID] = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(distFromCenter), 0).rotate(-angle)).round(); createIsland(islandID, islandRadius, clLand); } function createSnowflakeSearockWithCenter(sizeID) { let [tertiaryIslandDist, tertiaryIslandRadius, islandBetweenPlayersDist, islandBetweenPlayersRadius] = islandSizes[sizeID]; let islandID_center = 4 * numPlayers; numIslands = islandID_center + 1; initIsConnected(); log("Creating central island..."); islandPos[islandID_center] = mapCenter; createIsland(islandID_center, centralIslandRadius, clLand); for (let playerID = 0; playerID < numPlayers; ++playerID) { let playerID_neighbor = playerID + 1 < numPlayers ? playerID + 1 : 0; let islandID_player = playerID; let islandID_playerNeighbor = playerID_neighbor; let islandID_betweenPlayers = playerID + numPlayers; let islandID_betweenPlayerAndCenter = playerID + 2 * numPlayers; let islandID_betweenPlayerAndCenterNeighbor = playerID_neighbor + 2 * numPlayers; let islandID_tertiary = playerID + 3 * numPlayers; log("Creating island between the player and their neighbor..."); isConnected[islandID_betweenPlayers][islandID_player] = 1; isConnected[islandID_betweenPlayers][islandID_playerNeighbor] = 1; createIslandAtRadialLocation(playerID, islandID_betweenPlayers, 1, islandBetweenPlayersDist, islandBetweenPlayersRadius); log("Creating an island between the player and the center..."); isConnected[islandID_betweenPlayerAndCenter][islandID_player] = 1; isConnected[islandID_betweenPlayerAndCenter][islandID_center] = 1; isConnected[islandID_betweenPlayerAndCenter][islandID_betweenPlayerAndCenterNeighbor] = 1; createIslandAtRadialLocation(playerID, islandID_betweenPlayerAndCenter, 0, islandBetweenPlayerAndCenterDist, islandBetweenPlayerAndCenterRadius); log("Creating tertiary island, at the map border..."); isConnected[islandID_tertiary][islandID_betweenPlayers] = 1; createIslandAtRadialLocation(playerID, islandID_tertiary, 1, tertiaryIslandDist, tertiaryIslandRadius); } } /** * Creates one island in front of every player and connects it with the neighbors. */ function createSnowflakeSearockWithoutCenter() { numIslands = 2 * numPlayers; initIsConnected(); for (let playerID = 0; playerID < numPlayers; ++playerID) { let playerID_neighbor = playerID + 1 < numPlayers ? playerID + 1 : 0; let islandID_player = playerID; let islandID_playerNeighbor = playerID_neighbor; let islandID_inFrontOfPlayer = playerID + numPlayers; let islandID_inFrontOfPlayerNeighbor = playerID_neighbor + numPlayers; isConnected[islandID_player][islandID_playerNeighbor] = 1; isConnected[islandID_player][islandID_inFrontOfPlayer] = 1; isConnected[islandID_inFrontOfPlayer][islandID_inFrontOfPlayerNeighbor] = 1; createIslandAtRadialLocation(playerID, islandID_inFrontOfPlayer, 0, islandBetweenPlayerAndCenterDist, islandBetweenPlayerAndCenterRadius); } } function createSnowflakeSearockTiny() { numIslands = numPlayers + 1; initIsConnected(); let islandID_center = numPlayers; log("Creating central island..."); islandPos[islandID_center] = mapCenter; createIsland(numPlayers, 1, clLand); for (let playerID = 0; playerID < numPlayers; ++playerID) { let islandID_player = playerID; isConnected[islandID_player][islandID_center] = 1; } } const islandSizes = { "medium": [0.41, 0.49, 0.26, 1], "large1": [0.41, 0.49, 0.24, 1], "large2": [0.41, 0.36, 0.28, 0.81] }; if (mapSize <= 128) { createSnowflakeSearockTiny(); } else if (mapSize <= 192) { createSnowflakeSearockWithoutCenter(); } else if (mapSize <= 256) { if (numPlayers < 6) createSnowflakeSearockWithCenter("medium"); else createSnowflakeSearockWithoutCenter(); } else if (mapSize <= 320) { if (numPlayers < 8) createSnowflakeSearockWithCenter("medium"); else createSnowflakeSearockWithoutCenter(); } else createSnowflakeSearockWithCenter(numPlayers < 6 ? "large1" : "large2"); log("Creating player islands..."); for (let i = 0; i < numPlayers; ++i) { islandPos[i] = playerPosition[i]; createIsland(i, 1, isNomad() ? clLand : clPlayer); } placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], // PlayerTileClass already marked above "BaseResourceClass": clBaseResource, "baseResourceConstraint": stayClasses(clPlayer, 4), "Walls": "towers", "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad }, "Chicken": { }, "Berries": { "template": oFruitBush, "distance": playerIslandRadius - 4 }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ], "distance": playerIslandRadius - 4 }, "Trees": { "template": oTree1, "count": scaleByMapSize(10, 50), "minDist": 11, "maxDist": 11 }, "Decoratives": { "template": aGrassShort } }); Engine.SetProgress(30); log("Creating connectors..."); for (let i = 0; i < numIslands; ++i) for (let j = 0; j < numIslands; ++j) if (isConnected[i][j]) createPassage({ "start": islandPos[i], "end": islandPos[j], "startWidth": 11, "endWidth": 11, "smoothWidth": 3, "maxHeight": heightIsland - 1, "tileClass": clLand, "terrain": tHill, "edgeTerrain": tCliff }); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1)); var types = [ [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]], [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]] ]; var size = forestTrees / (scaleByMapSize(2, 8) * numPlayers) * (currentBiome() == "savanna" ? 2 : 1); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ClumpPlacer(forestTrees / num, 0.1, 0.1, 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], [avoidClasses(clPlayer, 6, clForest, 10), stayClasses(clLand, 4)], num); Engine.SetProgress(55); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10), stayClasses(clLand, 5)], 5*scaleByMapSize(4,16), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10), stayClasses(clLand, 5)], 5*scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5), stayClasses(clLand, 5)], 5*scaleByMapSize(4,16), 100 ); Engine.SetProgress(65); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), [ new LayeredPainter([[tMainTerrain, tTier1Terrain],[tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]), paintClass(clDirt) ], [avoidClasses(clForest, 0, clDirt, 5, clPlayer, 12), stayClasses(clLand, 5)], scaleByMapSize(15, 45)); log("Creating grass patches..."); for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]) createAreas( new ClumpPlacer(size, 0.3, 0.06, 0.5), new TerrainPainter(tTier4Terrain), [avoidClasses(clForest, 0, clDirt, 5, clPlayer, 12), stayClasses(clLand, 5)], scaleByMapSize(15, 45)); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0), stayClasses(clLand, 4)], scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clForest, 0, clPlayer, 0), stayClasses(clLand, 4)], scaleByMapSize(8, 131), 50 ); Engine.SetProgress(70); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oMainHuntableAnimal, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 10, clFood, 20), stayClasses(clLand, 4)], 3 * numPlayers, 50 ); Engine.SetProgress(75); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oSecondaryHuntableAnimal, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 10, clFood, 20), stayClasses(clLand, 4)], 3 * numPlayers, 50 ); log("Creating fruits..."); group = new SimpleGroup( [new SimpleObject(oFruitBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 0, clPlayer, 10, clFood, 20), stayClasses(clLand, 4)], 3 * numPlayers, 50 ); Engine.SetProgress(85); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], [avoidClasses(clForest, 1, clPlayer, 9, clMetal, 6, clRock, 6), stayClasses(clLand, 4)], clForest, stragglerTrees); var planetm = 1; if (currentBiome() == "tropic") planetm = 8; log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clPlayer, 2, clDirt, 0), stayClasses(clLand, 4)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 4)], planetm * scaleByMapSize(13, 200) ); Engine.SetProgress(95); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, [avoidClasses(clPlayer, 1, clDirt, 1), stayClasses(clLand, 4)], planetm * scaleByMapSize(13, 200), 50 ); placePlayersNomad( clPlayer, [ stayClasses(clLand, 8), avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clFood, 2) ]); setSkySet(pickRandom(["cirrus", "cumulus", "sunny"])); setSunRotation(randomAngle()); setSunElevation(Math.PI * randFloat(1/5, 1/3)); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/syria.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/syria.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/syria.js (revision 20971) @@ -1,278 +1,278 @@ Engine.LoadLibrary("rmgen"); const tMainDirt = ["desert_dirt_rocks_1", "desert_dirt_cracks"]; const tForestFloor1 = "forestfloor_dirty"; const tForestFloor2 = "desert_forestfloor_palms"; const tGrassSands = "desert_grass_a_sand"; const tGrass = "desert_grass_a"; const tSecondaryDirt = "medit_dirt_dry"; const tCliff = ["desert_cliff_persia_1", "desert_cliff_persia_2"]; const tHill = ["desert_dirt_rocks_1", "desert_dirt_rocks_2", "desert_dirt_rocks_3"]; const tDirt = ["desert_dirt_rough", "desert_dirt_rough_2"]; const tRoad = "desert_shore_stones";; const tRoadWild = "desert_grass_a_stones";; const oTamarix = "gaia/flora_tree_tamarix"; const oPalm = "gaia/flora_tree_date_palm"; const oPine = "gaia/flora_tree_aleppo_pine"; const oBush = "gaia/flora_bush_grapes"; const oCamel = "gaia/fauna_camel"; const oGazelle = "gaia/fauna_gazelle"; const oLion = "gaia/fauna_lion"; const oStoneLarge = "gaia/geology_stonemine_desert_quarry"; const oStoneSmall = "gaia/geology_stone_desert_small"; const oMetalLarge = "gaia/geology_metal_desert_slabs"; const aRock = "actor|geology/stone_desert_med.xml"; const aBushA = "actor|props/flora/bush_desert_dry_a.xml"; const aBushB = "actor|props/flora/bush_desert_dry_a.xml"; const aBushes = [aBushA, aBushB]; const pForestP = [tForestFloor2 + TERRAIN_SEPARATOR + oPalm, tForestFloor2]; const pForestT = [tForestFloor1 + TERRAIN_SEPARATOR + oTamarix,tForestFloor2]; const heightLand = 1; const heightHill = 22; const heightOffsetBump = 2; InitMap(heightLand, tMainDirt); const mapCenter = getMapCenter(); const numPlayers = getNumPlayers(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clGrass = createTileClass(); var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35)); for (let i = 0; i < numPlayers; ++i) { log("Marking player territory larger than the city patch..."); if (!isNomad()) createArea( - new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.9, 0.5, 10, playerPosition[i].x, playerPosition[i].y), + new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.9, 0.5, 10, playerPosition[i]), paintClass(clPlayer)); log("Creating big grass patches surrounding the city patches..."); createArea( new ChainPlacer( 2, Math.floor(scaleByMapSize(5, 12)), Math.floor(scaleByMapSize(25, 60)) / (isNomad() ? 2 : 1), 1, playerPosition[i].x, playerPosition[i].y, 0, [Math.floor(scaleByMapSize(16, 30))]), [ new LayeredPainter([tGrassSands, tGrass], [3]), paintClass(clGrass) ]); } Engine.SetProgress(10); placePlayerBases({ "PlayerPlacement": [playerIDs, playerPosition], // PlayerTileClass marked above "BaseResourceClass": clBaseResource, "CityPatch": { "outerTerrain": tRoadWild, "innerTerrain": tRoad, "radius": 10, "width": 3 }, "Chicken": { }, "Berries": { "template": oBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ], "groupElements": [new RandomObject(aBushes, 2, 4, 2, 3)] }, "Trees": { "template": pickRandom([oPalm, oTamarix]), "count": 3 } // No decoratives }); Engine.SetProgress(20); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2), avoidClasses(clPlayer, 13), scaleByMapSize(300, 800)); log("Creating hills..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5), [ new LayeredPainter([tCliff, tHill], [2]), new SmoothElevationPainter(ELEVATION_SET, heightHill, 2), paintClass(clHill) ], avoidClasses(clPlayer, 3, clGrass, 1, clHill, 10), scaleByMapSize(1, 3) * numPlayers * 3); Engine.SetProgress(25); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(400, 2000, 0.7); var types = [ [[tMainDirt, tForestFloor2, pForestP], [tForestFloor2, pForestP]], [[tMainDirt, tForestFloor1, pForestT], [tForestFloor1, pForestT]] ]; var size = forestTrees / (scaleByMapSize(3,6) * numPlayers); var num = Math.floor(size / types.length); for (let type of types) createAreas( new ChainPlacer( 1, Math.floor(scaleByMapSize(3, 5)), forestTrees / (num * Math.floor(scaleByMapSize(2, 4))), 0.5), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses(clPlayer, 1, clGrass, 1, clForest, 10, clHill, 1), num); Engine.SetProgress(40); log("Creating dirt patches..."); for (let size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), new LayeredPainter([tSecondaryDirt, tDirt], [1]), avoidClasses(clHill, 0, clForest, 0, clPlayer, 8, clGrass, 1), scaleByMapSize(50, 90)); Engine.SetProgress(60); log("Creating big patches..."); for (let size of [scaleByMapSize(6, 30), scaleByMapSize(10, 50), scaleByMapSize(16, 70)]) createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5), new LayeredPainter([tSecondaryDirt, tDirt], [1]), avoidClasses(clHill, 0, clForest, 0, clPlayer, 8, clGrass, 1), scaleByMapSize(30, 90)); Engine.SetProgress(70); log("Creating stone mines..."); var group = new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0,4), new RandomObject(aBushes, 2, 4, 0, 2)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1, clGrass, 1)], scaleByMapSize(2,8), 100 ); log("Creating small stone quarries..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3), new RandomObject(aBushes, 2,4, 0,2)], true, clRock); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1, clGrass, 1)], scaleByMapSize(2,8), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4), new RandomObject(aBushes, 2,4, 0,2)], true, clMetal); createObjectGroupsDeprecated(group, 0, [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1, clGrass, 1)], scaleByMapSize(2,8), 100 ); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRock, 1,3, 0,1)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(16, 262), 50 ); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushB, 1,2, 0,1), new SimpleObject(aBushA, 1,3, 0,2)], true ); createObjectGroupsDeprecated( group, 0, avoidClasses(clForest, 0, clPlayer, 0, clHill, 0), scaleByMapSize(50, 500), 50 ); Engine.SetProgress(80); log("Creating gazelle..."); group = new SimpleGroup( [new SimpleObject(oGazelle, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 1, clHill, 1, clFood, 20, clGrass, 2), 3 * numPlayers, 50 ); log("Creating lions..."); group = new SimpleGroup( [new SimpleObject(oLion, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 1, clHill, 1, clFood, 20, clGrass, 2), 3 * numPlayers, 50 ); log("Creating camels..."); group = new SimpleGroup( [new SimpleObject(oCamel, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 1, clHill, 1, clFood, 20, clGrass, 2), 3 * numPlayers, 50 ); Engine.SetProgress(85); createStragglerTrees( [oPalm, oTamarix, oPine], avoidClasses(clForest, 1, clHill, 1, clPlayer, 1, clMetal, 6, clRock, 6), clForest, stragglerTrees); createStragglerTrees( [oPalm, oTamarix, oPine], [avoidClasses(clForest, 1, clHill, 1, clPlayer, 1, clMetal, 6, clRock, 6), stayClasses(clGrass, 3)], clForest, stragglerTrees * (isNomad() ? 3 : 1)); placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); setSkySet("sunny"); setSunElevation(Math.PI / 8); setSunRotation(randomAngle()); setSunColor(0.746, 0.718, 0.539); setWaterColor(0.292, 0.347, 0.691); setWaterTint(0.550, 0.543, 0.437); setWaterMurkiness(0.83); setFogColor(0.8, 0.76, 0.61); setFogThickness(0.2); setFogFactor(0.4); setPPEffect("hdr"); setPPContrast(0.65); setPPSaturation(0.42); setPPBloom(0.6); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js (revision 20970) +++ ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js (revision 20971) @@ -1,634 +1,644 @@ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmbiome"); Engine.LoadLibrary("heightmap"); InitMap(0, "whiteness"); /** * getArray - To ensure a terrain texture is contained within an array */ function getArray(stringOrArrayOfStrings) { if (typeof stringOrArrayOfStrings == "string") return [stringOrArrayOfStrings]; return stringOrArrayOfStrings; } setSelectedBiome(); // Terrain, entities and actors let wildLakeBiome = [ // 0 Deep water { "texture": getArray(g_Terrains.water), "actor": [[g_Gaia.fish], 0.01], "textureHS": getArray(g_Terrains.water), "actorHS": [[g_Gaia.fish], 0.03] }, // 1 Shallow water { "texture": getArray(g_Terrains.water), "actor": [[g_Decoratives.lillies, g_Decoratives.reeds], 0.3], "textureHS": getArray(g_Terrains.water), "actorHS": [[g_Decoratives.lillies], 0.1] }, // 2 Shore { "texture": getArray(g_Terrains.shore), "actor": [ [ g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.mainHuntableAnimal, g_Decoratives.grass, g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.rockMedium, g_Decoratives.bushMedium, g_Decoratives.bushMedium ], 0.3 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] }, // 3 Low ground { "texture": getArray(g_Terrains.tier1Terrain), "actor": [ [ g_Decoratives.grass, g_Decoratives.grassShort, g_Decoratives.rockLarge, g_Decoratives.rockMedium, g_Decoratives.bushMedium, g_Decoratives.bushSmall ], 0.2 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] }, // 4 Mid ground. Player and path height { "texture": getArray(g_Terrains.mainTerrain), "actor": [ [ g_Decoratives.grass, g_Decoratives.grassShort, g_Decoratives.rockLarge, g_Decoratives.rockMedium, g_Decoratives.bushMedium, g_Decoratives.bushSmall ], 0.2 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] }, // 5 High ground { "texture": getArray(g_Terrains.tier2Terrain), "actor": [ [ g_Decoratives.grass, g_Decoratives.grassShort, g_Decoratives.rockLarge, g_Decoratives.rockMedium, g_Decoratives.bushMedium, g_Decoratives.bushSmall ], 0.2 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] }, // 6 Lower hilltop forest border { "texture": getArray(g_Terrains.dirt), "actor": [ [ g_Gaia.tree1, g_Gaia.tree3, g_Gaia.fruitBush, g_Gaia.secondaryHuntableAnimal, g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium ], 0.3 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] }, // 7 Hilltop forest { "texture": getArray(g_Terrains.forestFloor1), "actor": [ [ g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4, g_Gaia.tree5, g_Decoratives.tree, g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium ], 0.5 ], "textureHS": getArray(g_Terrains.cliff), "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1] } ]; var mercenaryCampGuards = { "temperate": [ { "Template" : "structures/merc_camp_egyptian" }, { "Template" : "units/mace_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/mace_cavalry_spearman_e", "Count" : 3 }, { "Template" : "units/mace_infantry_archer_a", "Count" : 4 }, { "Template" : "units/mace_champion_infantry_a", "Count" : 3 } ], "snowy": [ { "Template" : "structures/ptol_mercenary_camp" }, { "Template" : "units/brit_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/brit_cavalry_swordsman_e", "Count" : 3 }, { "Template" : "units/brit_infantry_slinger_a", "Count" : 4 }, { "Template" : "units/brit_champion_infantry", "Count" : 3 } ], "desert": [ { "Template" : "structures/ptol_mercenary_camp" }, { "Template" : "units/pers_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/pers_cavalry_swordsman_e", "Count" : 3 }, { "Template" : "units/pers_infantry_archer_a", "Count" : 4 }, { "Template" : "units/pers_champion_infantry", "Count" : 3 } ], "alpine": [ { "Template" : "structures/ptol_mercenary_camp" }, { "Template" : "units/rome_infantry_swordsman_b", "Count" : 4 }, { "Template" : "units/rome_cavalry_spearman_e", "Count" : 3 }, { "Template" : "units/rome_infantry_javelinist_a", "Count" : 4 }, { "Template" : "units/rome_champion_infantry", "Count" : 3 } ], "mediterranean": [ { "Template" : "structures/merc_camp_egyptian" }, { "Template" : "units/iber_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/iber_cavalry_spearman_e", "Count" : 3 }, { "Template" : "units/iber_infantry_slinger_a", "Count" : 4 }, { "Template" : "units/iber_champion_infantry", "Count" : 3 } ], "savanna": [ { "Template" : "structures/merc_camp_egyptian" }, { "Template" : "units/sele_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/sele_cavalry_spearman_merc_e", "Count" : 3 }, { "Template" : "units/sele_infantry_spearman_a", "Count" : 4 }, { "Template" : "units/sele_champion_infantry_swordsman", "Count" : 3 } ], "tropic": [ { "Template" : "structures/merc_camp_egyptian" }, { "Template" : "units/ptol_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/ptol_cavalry_archer_e", "Count" : 3 }, { "Template" : "units/ptol_infantry_slinger_a", "Count" : 4 }, { "Template" : "units/ptol_champion_infantry_pikeman", "Count" : 3 } ], "autumn": [ { "Template" : "structures/ptol_mercenary_camp" }, { "Template" : "units/gaul_infantry_javelinist_b", "Count" : 4 }, { "Template" : "units/gaul_cavalry_swordsman_e", "Count" : 3 }, { "Template" : "units/gaul_infantry_slinger_a", "Count" : 4 }, { "Template" : "units/gaul_champion_infantry", "Count" : 3 } ] }; /** * Resource spots and other points of interest */ // Mines function placeMine(point, centerEntity, decorativeActors = [ g_Decoratives.grass, g_Decoratives.grassShort, g_Decoratives.rockLarge, g_Decoratives.rockMedium, g_Decoratives.bushMedium, g_Decoratives.bushSmall ] ) { placeObject(point.x, point.y, 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()); } } // Groves, only Wood let groveActors = [g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium]; let clGrove = createTileClass(); function placeGrove(point, groveEntities = [ g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree4, g_Gaia.tree4, g_Gaia.tree5 ], groveActors = [g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium], groveTileClass = undefined, groveTerrainTexture = getArray(g_Terrains.forestFloor1) ) { placeObject(point.x, point.y, 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) { let angle = dAngle * randFloat(i, i + 1); let dist = randFloat(2, 5); let objectList = groveEntities; if (i % 3 == 0) objectList = groveActors; - let x = point.x + dist * Math.cos(angle); - let y = point.y + dist * Math.sin(angle); - placeObject(x, y, pickRandom(objectList), 0, randomAngle()); + + let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)); + placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle()); + + let painters = [new TerrainPainter(groveTerrainTexture)]; if (groveTileClass) - createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture), paintClass(groveTileClass)]); - else - createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture)]); + painters.push(paintClass(groveTileClass)); + + createArea( + new ClumpPlacer(5, 1, 1, 1, position), + painters); } } var farmEntities = { "temperate": { "building": "structures/mace_farmstead", "animal": "gaia/fauna_pig" }, "snowy": { "building": "structures/brit_farmstead", "animal": "gaia/fauna_sheep" }, "desert": { "building": "structures/pers_farmstead", "animal": "gaia/fauna_camel" }, "alpine": { "building": "structures/rome_farmstead", "animal": "gaia/fauna_sheep" }, "mediterranean": { "building": "structures/iber_farmstead", "animal": "gaia/fauna_pig" }, "savanna": { "building": "structures/sele_farmstead", "animal": "gaia/fauna_horse" }, "tropic": { "building": "structures/ptol_farmstead", "animal": "gaia/fauna_camel" }, "autumn": { "building": "structures/gaul_farmstead", "animal": "gaia/fauna_horse" } }; g_WallStyles.other = { "overlap": 0, "fence": readyWallElement("other/fence_long", "gaia"), "fence_short": readyWallElement("other/fence_short", "gaia"), "bench": { "angle": Math.PI / 2, "length": 1.5, "indent": 0, "bend": 0, "templateName": "other/bench" }, "foodBin": { "angle": Math.PI / 2, "length": 1.5, "indent": 0, "bend": 0, "templateName": "gaia/special_treasure_food_bin" }, "animal": { "angle": 0, "length": 0, "indent": 0.75, "bend": 0, "templateName": farmEntities[currentBiome()].animal }, "farmstead": { "angle": Math.PI, "length": 0, "indent": -3, "bend": 0, "templateName": farmEntities[currentBiome()].building } }; let fences = [ new Fortress("fence", [ "foodBin", "farmstead", "bench", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "fence", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "bench", "animal", "fence", "turn_0.25", "animal", "turn_0.25", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "turn_0.5", "bench", "turn_-0.5", "fence_short", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "turn_0.5", "fence_short", "turn_-0.5", "bench", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence", "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence" ]), new Fortress("fence", [ "foodBin", "farmstead", "fence", "turn_0.25", "animal", "turn_0.25", "bench", "animal", "fence", "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence", "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence" ]) ]; let num = fences.length; 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, 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", "actor|props/special/common/waypoint_flag.xml", "actor|props/special/eyecandy/barrel_a.xml", "actor|props/special/eyecandy/basket_celt_a.xml", "actor|props/special/eyecandy/crate_a.xml", "actor|props/special/eyecandy/dummy_a.xml", "actor|props/special/eyecandy/handcart_1.xml", "actor|props/special/eyecandy/handcart_1_broken.xml", "actor|props/special/eyecandy/sack_1.xml", "actor|props/special/eyecandy/sack_1_rough.xml" ] ) { placeObject(point.x, point.y, 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()); } } function placeStartLocationResources( point, foodEntities = [g_Gaia.fruitBush, g_Gaia.chicken], groveEntities = [ g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree4, g_Gaia.tree4, g_Gaia.tree5 ], groveTerrainTexture = getArray(g_Terrains.forestFloor1), averageDistToCC = 10, dAverageDistToCC = 2 ) { function getRandDist() { return averageDistToCC + randFloat(-dAverageDistToCC, dAverageDistToCC); } let currentAngle = randomAngle(); // 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); currentAngle += dAngle; // Wood let quantity = 80; dAngle = 2/3 * Math.PI / quantity; for (let i = 0; i < quantity; ++i) { angle = currentAngle + randFloat(0, dAngle); let dist = getRandDist(); let objectList = groveEntities; if (i % 2 == 0) objectList = groveActors; - let x = point.x + dist * Math.cos(angle); - let y = point.y + dist * Math.sin(angle); - placeObject(x, y, pickRandom(objectList), 0, randomAngle()); - createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture), paintClass(clGrove)]); + + let position = Vector2D.add(point, new Vector2D(dist, 0).rotate(-angle)); + placeObject(position.x, position.y, pickRandom(objectList), 0, randomAngle()); + createArea( + new ClumpPlacer(5, 1, 1, 1, position), + [ + new TerrainPainter(groveTerrainTexture), + paintClass(clGrove) + ]); + currentAngle += dAngle; } // 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); currentAngle += dAngle; // Berries and domestic animals quantity = 15; dAngle = 4/9 * Math.PI / quantity; for (let i = 0; i < quantity; ++i) { 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()); currentAngle += dAngle; } } /** * Base terrain shape generation and settings */ // Height range by map size let heightScale = (g_Map.size + 256) / 768 / 4; let heightRange = { "min": MIN_HEIGHT * heightScale, "max": MAX_HEIGHT * heightScale }; // Water coverage let averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value let heightSeaGround = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine let heightSeaGroundAdjusted = heightSeaGround + MIN_HEIGHT; // Water height as terrain height setWaterHeight(heightSeaGround); // Generate base terrain shape let lowH = heightRange.min; let medH = (heightRange.min + heightRange.max) / 2; // Lake let initialHeightmap = [ [medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH], [medH, medH, lowH, lowH, medH, medH], [medH, medH, lowH, lowH, medH, medH], [medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH], ]; if (g_Map.size < 256) { initialHeightmap = [ [medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH], [medH, medH, lowH, medH, medH], [medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH] ]; } if (g_Map.size >= 384) { initialHeightmap = [ [medH, medH, medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH, medH, medH], [medH, medH, medH, lowH, lowH, medH, medH, medH], [medH, medH, medH, lowH, lowH, medH, medH, medH], [medH, medH, medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH, medH, medH], [medH, medH, medH, medH, medH, medH, medH, medH], ]; } setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8); // Apply simple erosion for (let i = 0; i < 5; ++i) splashErodeMap(0.1); globalSmoothHeightmap(); // Final rescale rescaleHeightmap(heightRange.min, heightRange.max); Engine.SetProgress(25); /** * Prepare terrain texture placement */ let heighLimits = [ heightRange.min + 3/4 * (heightSeaGroundAdjusted - heightRange.min), // 0 Deep water heightSeaGroundAdjusted, // 1 Shallow water heightSeaGroundAdjusted + 2/8 * (heightRange.max - heightSeaGroundAdjusted), // 2 Shore heightSeaGroundAdjusted + 3/8 * (heightRange.max - heightSeaGroundAdjusted), // 3 Low ground heightSeaGroundAdjusted + 4/8 * (heightRange.max - heightSeaGroundAdjusted), // 4 Player and path height heightSeaGroundAdjusted + 6/8 * (heightRange.max - heightSeaGroundAdjusted), // 5 High ground heightSeaGroundAdjusted + 7/8 * (heightRange.max - heightSeaGroundAdjusted), // 6 Lower forest border heightRange.max // 7 Forest ]; let playerHeightRange = { "min" : heighLimits[3], "max" : heighLimits[4] }; let resourceSpotHeightRange = { "min" : (heighLimits[2] + heighLimits[3]) / 2, "max" : (heighLimits[4] + heighLimits[5]) / 2 }; let playerHeight = (playerHeightRange.min + playerHeightRange.max) / 2; // Average player height log("Chosing starting locations..."); let [playerIDs, startLocations] = sortPlayersByLocation(getStartLocationsByHeightmap(playerHeightRange, 1000, 30)); Engine.SetProgress(30); /** * Smooth Start Locations before height region calculation */ 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); /** * Calculate tile centered height map after start position smoothing but before placing paths * This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false! */ let tchm = getTileCenteredHeightmap(); /** * Add paths (If any) */ let clPath = createTileClass(); /** * Divide tiles in areas by height and avoid paths */ let areas = []; for (let h = 0; h < heighLimits.length; ++h) areas.push([]); for (let x = 0; x < tchm.length; ++x) for (let y = 0; y < tchm[0].length; ++y) { if (g_Map.tileClasses[clPath].inclusionCount[x][y] > 0) // Avoid paths continue; let minHeight = heightRange.min; for (let h = 0; h < heighLimits.length; ++h) { if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h]) { areas[h].push({ "x": x, "y": y }); break; } else minHeight = heighLimits[h]; } } /** * Get max slope of each area */ let slopeMap = getSlopeMap(); let minSlope = []; let maxSlope = []; for (let h = 0; h < heighLimits.length; ++h) { minSlope[h] = Infinity; maxSlope[h] = 0; for (let t = 0; t < areas[h].length; ++t) { let x = areas[h][t].x; let y = areas[h][t].y; let slope = slopeMap[x][y]; if (slope > maxSlope[h]) maxSlope[h] = slope; if (slope < minSlope[h]) minSlope[h] = slope; } } /** * Paint areas by height and slope */ for (let h = 0; h < heighLimits.length; ++h) for (let t = 0; t < areas[h].length; ++t) { let x = areas[h][t].x; let y = areas[h][t].y; let actor; let texture = pickRandom(wildLakeBiome[h].texture); if (slopeMap[x][y] < 0.5 * (minSlope[h] + maxSlope[h])) { if (randBool(wildLakeBiome[h].actor[1])) actor = pickRandom(wildLakeBiome[h].actor[0]); } else { texture = pickRandom(wildLakeBiome[h].textureHS); if (randBool(wildLakeBiome[h].actorHS[1])) actor = pickRandom(wildLakeBiome[h].actorHS[0]); } g_Map.texture[x][y] = g_Map.getTextureID(texture); if (actor) placeObject(randFloat(x, x + 1), randFloat(y, y + 1), actor, 0, randomAngle()); } Engine.SetProgress(80); 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); Engine.SetProgress(55); log("Placing players..."); if (isNomad()) placePlayersNomad(createTileClass(), new HeightConstraint(playerHeightRange.min, playerHeightRange.max)); else for (let p = 0; p < playerIDs.length; ++p) { let point = startLocations[p]; placeCivDefaultStartingEntities(point, playerIDs[p], g_Map.size > 192); placeStartLocationResources(point); } let mercenaryCamps = isNomad() ? 0 : Math.ceil(g_Map.size / 256); log("Maximum number of mercenary camps: " + mercenaryCamps); for (let i = 0; i < resourceSpots.length; ++i) { let choice = i % 5; if (choice == 0) placeMine(resourceSpots[i], g_Gaia.stoneLarge); if (choice == 1) placeMine(resourceSpots[i], g_Gaia.metalLarge); if (choice == 2) placeGrove(resourceSpots[i]); if (choice == 3) { placeCamp(resourceSpots[i]); rectangularSmoothToHeight(resourceSpots[i], 5, 5, g_Map.height[resourceSpots[i].x][resourceSpots[i].y] - 10, 0.5); } if (choice == 4) { if (mercenaryCamps) { placeStartingEntities(resourceSpots[i], 0, mercenaryCampGuards[currentBiome()]); rectangularSmoothToHeight(resourceSpots[i], 15, 15, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5); --mercenaryCamps; } else { placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randomAngle()); rectangularSmoothToHeight(resourceSpots[i], 10, 10, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5); } } } ExportMap();