Index: binaries/data/mods/public/maps/random/rmgen-common/mountain_range_builder.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen-common/mountain_range_builder.js +++ binaries/data/mods/public/maps/random/rmgen-common/mountain_range_builder.js @@ -0,0 +1,138 @@ +Engine.LoadLibrary("rmgen"); + +/** + * @file This class creates random mountainranges ensuring that there are enough breaks to pass between them. + * + * To determine their location, random points are connected in shortest-cycle order and then trimmed to max length. + * + * Constructor arguments: + * @param {number} "height": how tall the mountains should be, in absolute height (ie set not modify). + * @param {number} "width": How wide the mountains will be. + * @param {number} "count": How many mountain ranges to create. + * @param {number} "maxLength": The longest that any connected mountain strip is allowed to be. Use to ensure that passable gaps are created. + * @param {number} "bumpiness": (0..1) How much the height of each peak should vary. + * @param {number} "waviness": (0..1) How much the mountain ranges should deviate from a straight line. + * @param {array strings} "terrain": String or array of two or more strings specifying terrain textures to paint. + * @param {number} "tileclass": The tileclass that the mountains should be assigned. + * @param {array constraints} "constraint": List of tileclasses to avoid painting over. + */ +function MountainRangeBuilder(args) +{ + /** + * These parameters paint the mountainranges after their location was determined. + */ + var height = args.height || scaleByMapSize(60, 120); + this.mountainWidth = args.width || height/4; + + this.pathplacer = new PathPlacer(undefined, undefined, undefined, args.bumpiness || 0.4, scaleByMapSize(10, 25), args.waviness || 0.75, 0.2, 0.1); + this.painters = [ + new TerrainPainter(args.terrain), + new SmoothElevationPainter(ELEVATION_SET, height, 4 * height/this.mountainWidth, 3), + new TileClassPainter(args.tileclass) + ]; + this.constraint = args.constraint; + + /** + * Array of Vector2D locations where a mountainrange can start or end. + */ + this.vertices = g_Map.randomCoordinates(2 * (args.count || 16), false); + + /** + * Maximum length that a mountain path can have. + */ + this.maxLength = args.maxLength || fractionToTiles(0.6); + + /** + * Number of mountainranges starting or ending at the given point. + */ + this.vertexDegree = this.vertices.map(p => 0); + + /** + * 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(); + + /** + * 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() +{ + var order = sortPointsShortestCycle(this.vertices); + var length = 0; + for (let i = 0; i < order.length - 1; ++i) + { + length += this.vertices[order[i]].distanceTo(this.vertices[order[i+1]]); + if (length <= this.maxLength) + { + this.possibleEdges.push([order[i], order[i+1]]); + }else + { + length = 0; + } + } +}; + +MountainRangeBuilder.prototype.UpdateCurrentEdge = function() +{ + this.currentEdge = this.possibleEdges[this.index]; + this.currentEdgeStart = this.vertices[this.currentEdge[0]]; + this.currentEdgeEnd = this.vertices[this.currentEdge[1]]; +}; + +MountainRangeBuilder.prototype.PaintCurrentEdge = function() +{ + this.pathplacer.start = this.currentEdgeStart; + this.pathplacer.end = this.currentEdgeEnd; + this.pathplacer.width = this.mountainWidth; + + // Creating mountainrange + if (!createArea(this.pathplacer, this.painters, this.constraint)) + return false; + + return true; +}; + +/** + * This is the only function meant to be publicly accessible. + */ +MountainRangeBuilder.prototype.CreateMountainRanges = function() +{ + g_Map.log("Creating mountain ranges with " + this.possibleEdges.length + " possible edges"); + + for (let i = 0; i < this.possibleEdges.length; ++i) + { + + this.index = i; + this.UpdateCurrentEdge(); + + if (this.PaintCurrentEdge()) + { + ++this.vertexDegree[this.currentEdge[0]]; + ++this.vertexDegree[this.currentEdge[1]]; + } + } + + // Create circular mountains to connect ranges that share vertices. + for (let i = 0; i < this.vertexDegree.length; ++i) + { + if (this.vertexDegree[i] > 1) + { + createArea( + new ClumpPlacer(diskArea(this.mountainWidth / 2), 0.95, 0.6, Infinity, this.vertices[i]), + this.painters, + this.constraint); + } + } +}; \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/Constraint.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/Constraint.js +++ binaries/data/mods/public/maps/random/rmgen/Constraint.js @@ -3,6 +3,74 @@ */ /** + * The following functions are for easily creating constraints in batches. + */ + +/** + * Create an avoid constraint for the given classes by the given distances + */ +function avoidClasses(/* class1, dist1, class2, dist2, etc*/) +{ + let ar = []; + for (let i = 0; i < arguments.length/2; ++i) + ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1])); + + // Return single constraint + if (ar.length == 1) + return ar[0]; + + return new AndConstraint(ar); +} + +/** + * Create a stay constraint for the given classes by the given distances + */ +function stayClasses(/* class1, dist1, class2, dist2, etc*/) +{ + let ar = []; + for (let i = 0; i < arguments.length/2; ++i) + ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1])); + + // Return single constraint + if (ar.length == 1) + return ar[0]; + + return new AndConstraint(ar); +} + +/** + * Create a stay constraint for the given classes by the given distances + */ +function nearClasses(/* class1, dist1, class2, dist2, etc*/) +{ + let ar = []; + for (let i = 0; i < arguments.length/2; ++i) + ar.push(new NearTileClassConstraint(arguments[2*i], arguments[2*i+1])); + + // Return single constraint + if (ar.length == 1) + return ar[0]; + + return new AndConstraint(ar); +} + +/** + * Create a border constraint for the given classes by the given distances + */ +function borderClasses(/* class1, idist1, odist1, class2, idist2, odist2, etc*/) +{ + let ar = []; + for (let i = 0; i < arguments.length/3; ++i) + ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2])); + + // Return single constraint + if (ar.length == 1) + return ar[0]; + + return new AndConstraint(ar); +} + +/** * The NullConstraint is always satisfied. */ function NullConstraint() {} Index: binaries/data/mods/public/maps/random/rmgen/RandomMap.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/RandomMap.js +++ binaries/data/mods/public/maps/random/rmgen/RandomMap.js @@ -207,6 +207,19 @@ }; /** + * Returns n random points on the map. + * @param pointCount - The number of coordinates to generate. + * @param passableOnly - Should be true for entity placement and false for terrain or elevation operations. + */ +RandomMap.prototype.randomCoordinates = function(pointCount, passableOnly) +{ + let points = []; + for (let i = 0; i < pointCount; ++i) + points[i] = this.randomCoordinate(passableOnly); + return points; +} + +/** * Returns a random point on the map. * @param passableOnly - Should be true for entity placement and false for terrain or elevation operations. */ Index: binaries/data/mods/public/maps/random/rmgen/library.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/library.js +++ binaries/data/mods/public/maps/random/rmgen/library.js @@ -32,7 +32,7 @@ * Constants needed for heightmap_manipulation.js */ const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters -const MIN_HEIGHT = - SEA_LEVEL; +const MIN_HEIGHT = -SEA_LEVEL; /** * Length of one tile of the terrain grid in metres. @@ -68,12 +68,12 @@ let obstructionSize = obstruction.Static ? new Vector2D(obstruction.Static["@depth"], obstruction.Static["@width"]) : - // Used for gates, should consider the position too - obstruction.Obstructions ? - new Vector2D( - Object.keys(obstruction.Obstructions).reduce((depth, key) => Math.max(depth, +obstruction.Obstructions[key]["@depth"]), 0), - Object.keys(obstruction.Obstructions).reduce((width, key) => width + +obstruction.Obstructions[key]["@width"], 0)) : - new Vector2D(0, 0); + // Used for gates, should consider the position too + obstruction.Obstructions ? + new Vector2D( + Object.keys(obstruction.Obstructions).reduce((depth, key) => Math.max(depth, +obstruction.Obstructions[key]["@depth"]), 0), + Object.keys(obstruction.Obstructions).reduce((width, key) => width + (+obstruction.Obstructions[key]["@width"]), 0)) : + new Vector2D(0, 0); return obstructionSize.div(TERRAIN_TILE_SIZE).add(new Vector2D(2, 2).mult(margin)); } @@ -247,54 +247,6 @@ } /** - * Create an avoid constraint for the given classes by the given distances - */ -function avoidClasses(/*class1, dist1, class2, dist2, etc*/) -{ - let ar = []; - for (let i = 0; i < arguments.length/2; ++i) - ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1])); - - // Return single constraint - if (ar.length == 1) - return ar[0]; - - return new AndConstraint(ar); -} - -/** - * Create a stay constraint for the given classes by the given distances - */ -function stayClasses(/*class1, dist1, class2, dist2, etc*/) -{ - let ar = []; - for (let i = 0; i < arguments.length/2; ++i) - ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1])); - - // Return single constraint - if (ar.length == 1) - return ar[0]; - - return new AndConstraint(ar); -} - -/** - * Create a border constraint for the given classes by the given distances - */ -function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/) -{ - let ar = []; - for (let i = 0; i < arguments.length/3; ++i) - ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2])); - - // Return single constraint - if (ar.length == 1) - return ar[0]; - - return new AndConstraint(ar); -} - -/** * Returns a subset of the given heightmap. */ function extractHeightmap(heightmap, topLeft, size) Index: binaries/data/mods/public/maps/random/the_high_alps.js =================================================================== --- binaries/data/mods/public/maps/random/the_high_alps.js +++ binaries/data/mods/public/maps/random/the_high_alps.js @@ -0,0 +1,362 @@ +Engine.LoadLibrary("rmgen"); +Engine.LoadLibrary("rmgen-common"); +Engine.LoadLibrary("heightmap"); + +TILE_CENTERED_HEIGHT_MAP = true; + +const winter = randBool(); + +var tPrimary = winter ? ["alpine_snow_a", "alpine_snow_b"] : ["new_alpine_grass_a", "new_alpine_grass_mossy"]; +var tForestFloor = winter ? "alpine_cliff_snow" : "alpine_forrestfloor"; +var tCliff = ["alpine_cliff", "alpine_mountainside"]; +var tDecoration = winter ? ["polar_snow_a"] : ["alpine_dirt_grass_50", "new_alpine_grass_mossy"]; +var tSnowLine = ["path a"]; +var tSnowLineTrans = ["polar_snow_glacial", "alpine_snow_b", "alpine_snow_a"]; +var tSnowCaps = ["polar_snow_glacial"]; +var tFarmland = winter ? ["polar_ice_snow", "polar_snow_a"] : "temp_farmland"; +var tRoad = "temp_road_broken"; +var tLakeBed = "alpine_shore_rocks"; +var tShore = winter ? "alpine_shore_rocks_icy" : "alpine_shore_rocks_grass_50"; + +var oPine = "gaia/flora_tree_pine"; +var oAleppoPine = "gaia/flora_tree_aleppo_pine"; +var oWinterPine = "gaia/flora_tree_pine_w"; +var oBerryBush = "gaia/flora_bush_berry"; +var oSheep = "gaia/fauna_sheep"; +var oDeer = "gaia/fauna_deer"; +var oRabbit = "gaia/fauna_rabbit"; +var oBear = "gaia/fauna_bear"; +var oFish = "gaia/fauna_fish"; +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"; + + +var heightLand = 3; +var heightLake = -5; +var heightOffsetBump = scaleByMapSize(10, 75); +var heightMountain = scaleByMapSize(60, 120); +var snowlineHeight = heightMountain * 0.6; + +const pForest = [tForestFloor + TERRAIN_SEPARATOR + oPine, tForestFloor]; +const pForestSnow = [tForestFloor + TERRAIN_SEPARATOR + oWinterPine, tForestFloor]; + +var g_Map = new RandomMap(heightLand, tPrimary); + +const numPlayers = getNumPlayers(); + +var clPlayer = g_Map.createTileClass(); +var clFarm = g_Map.createTileClass(); +var clHill = g_Map.createTileClass(); +var clWater = g_Map.createTileClass(); +var clForest = g_Map.createTileClass(); +var clDeco = g_Map.createTileClass(); +var clRock = g_Map.createTileClass(); +var clMetal = g_Map.createTileClass(); +var clFood = g_Map.createTileClass(); +var clBaseResource = g_Map.createTileClass(); + +// Setup environment settings +g_Map.log("Configuring environment settings"); +setWaterType("clap"); +setWaterColor(0.713726, 0.854902, 0.803922); +setWaterTint(0.913725, 0.976471, 0.996078); +setWaterWaviness(3.29102); +setWaterMurkiness(0.674805); +setPPEffect("hdr"); + +var env = randIntInclusive(0, 2); +if (env == 0) +{ + g_Map.log("Using Lighting Set Morning"); + setSkySet("cirrus"); + setSunColor(1, 0.886275, 0.556863); + setSunElevation(0.469398); + setSunRotation(-1.42353); + setTerrainAmbientColor(0.329412, 0.419608, 0.501961); + setUnitsAmbientColor(0.439216, 0.521569, 0.556863); + setFogColor(0.8, 0.8, 0.894118); + setFogFactor(0.4); + setFogThickness(0.2); + setPPBloom(0.35); +} +else if (env == 1) +{ + g_Map.log("Using lighting set Afternoon"); + setSkySet("cloudless"); + setSunColor(1, 0.921569, 0.682353); + setSunElevation(0.670645); + setSunRotation(0.416702); + setTerrainAmbientColor(0.329412, 0.419608, 0.501961); + setUnitsAmbientColor(0.439216, 0.521569, 0.556863); + setFogColor(0.8, 0.8, 0.894118); + setFogFactor(0.3); + setFogThickness(0.2); + setPPBloom(0.25); +} +else +{ + g_Map.log("Using Lighting set Evening"); + setSkySet("sunset"); + setSunColor(1.2652, 0.873236, 0.486234); + setSunElevation(0.378635); + setSunRotation(1.10222); + setTerrainAmbientColor(0.329412, 0.419608, 0.501961); + setUnitsAmbientColor(0.439216, 0.521569, 0.556863); + setFogColor(0.839216, 0.780392, 0.729412); + setFogFactor(0.3); + setFogThickness(0.22); + setPPBloom(0.35); +} + +Engine.SetProgress(10); + +var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35)); + +placePlayerBases({ + "PlayerPlacement": [playerIDs, playerPosition], + "PlayerTileClass": clPlayer, + "BaseResourceClass": clBaseResource, + "CityPatch": { + "outerTerrain": tPrimary, + "innerTerrain": tRoad + }, + "Chicken": { + "template": oSheep + }, + "Berries": { + "template": oBerryBush + }, + "Mines": { + "types": [ + { "template": oMetalLarge }, + { "template": oStoneLarge } + ] + }, + "Trees": { + "template": winter ? oWinterPine : oPine + }, +}); +Engine.SetProgress(20); + +new MountainRangeBuilder({ + "height": heightMountain, + "width": heightMountain * 0.25, + "maxLength": fractionToTiles(0.6), + "count": 16, + "bumpiness": 0.4, + "waviness": 0.75, + "terrain": tCliff, + "tileclass": clHill, + "constraint": avoidClasses(clPlayer, scaleByMapSize(15, 30)) +}).CreateMountainRanges(); + +Engine.SetProgress(35); + +g_Map.log("Creating lakes"); +var numLakes = randIntInclusive(0, Math.round(scaleByMapSize(1, 7))); +if (numLakes > 0) + createAreas( + new ChainPlacer(1, Math.floor(scaleByMapSize(4, 8)), Math.floor(scaleByMapSize(30, 120)), 0.7), + [ + new LayeredPainter([tShore, tLakeBed], [1]), + new SmoothElevationPainter(ELEVATION_SET, heightLake, 5), + new TileClassPainter(clWater) + ], + avoidClasses(clPlayer, 20, clWater, 30, clHill, 7), + numLakes, + 1); + +createMountains(tCliff, avoidClasses(clPlayer, 15, clHill, 4, clWater, 4), clHill, scaleByMapSize(80, 320), Math.floor(scaleByMapSize(50, 90)), Math.floor(scaleByMapSize(4, 8)), Math.floor(scaleByMapSize(8, 18)), Math.floor(scaleByMapSize(4, 8))); + +Engine.SetProgress(40); + +// Paint the snow caps. +paintTerrainBasedOnHeight(snowlineHeight - 7, snowlineHeight, 3, tSnowLine); +paintTerrainBasedOnHeight(snowlineHeight, snowlineHeight + 5, 3, tSnowLineTrans); +paintTerrainBasedOnHeight(snowlineHeight + 5, Infinity, 3, tSnowCaps); + +g_Map.log("Creating bumps"); +createAreas( + new ClumpPlacer(scaleByMapSize(75, 300), 0.3, 0.06, Infinity), + new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump * 2, scaleByMapSize(35, 150)), + avoidClasses(clPlayer, 10, clWater, 2), + scaleByMapSize(50, 100)); + +createAreas( + new ClumpPlacer(scaleByMapSize(50, 200), 0.3, 0.06, Infinity), + new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, scaleByMapSize(25, 100)), + avoidClasses(clPlayer, 10, clWater, 2), + scaleByMapSize(100, 200)); + +Engine.SetProgress(50); + +g_Map.log("Creating texture variation"); + +// Decorate the base terrain. +createAreas( + new ChainPlacer(fractionToTiles(0.005), fractionToTiles(0.015), scaleByMapSize(7, 25), 0.75), + [ + new TerrainPainter(tDecoration), + new TileClassPainter(clDeco) + ], + avoidClasses(clPlayer, 10, clHill, 0, clWater, 0, clFarm, 0), + randIntInclusive(scaleByMapSize(10, 30), scaleByMapSize(60, 120))); + +// Decorate random areas with farmland or ice. +createAreas( + new ChainPlacer(4, 6, 10, 0.35), + [ + new TerrainPainter(tFarmland), + new TileClassPainter(clFarm) + ], + avoidClasses(clPlayer, 15, clHill, 5, clFarm, 50, clWater, 10), + randIntInclusive(scaleByMapSize(2, 4), scaleByMapSize(4, 8))); +Engine.SetProgress(60); + +g_Map.log("Creating forests"); +var [forestTrees, stragglerTrees] = getTreeCounts(750, 4000, 0.7); + +// Create forests +if (winter) +{ + createForests( + [tPrimary, tForestFloor, tForestFloor, pForestSnow, pForestSnow], + [avoidClasses(clPlayer, 20, clForest, 3, clWater, 2, clHill, 0, clFarm, 3), nearClasses(clHill, 6)], + clForest, + forestTrees); + + createStragglerTrees( + [oWinterPine], + [avoidClasses(clForest, 2, clHill, 1, clPlayer, 12, clWater, 3, clFarm, 3), nearClasses(clForest, 6)], + clForest, + stragglerTrees); +} +else +{ + createForests( + [tPrimary, tForestFloor, tForestFloor, pForest, pForest], + [avoidClasses(clPlayer, 20, clForest, 3, clWater, 2, clHill, 0, clFarm, 3), nearClasses(clHill, 6)], + clForest, + forestTrees); + + createStragglerTrees( + [oPine, oAleppoPine], + [avoidClasses(clForest, 2, clHill, 1, clPlayer, 12, clWater, 3, clFarm, 3), nearClasses(clForest, 6)], + clForest, + stragglerTrees); +} + +Engine.SetProgress(65); + +g_Map.log("Creating stone mines"); +createMines( + [ + [new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)] + ], + avoidClasses(clWater, 3, clForest, 1, clPlayer, 25, clRock, 25, clHill, 3, clFarm, 5), + clRock); + +g_Map.log("Creating metal mines"); +createMines( + [ + [new SimpleObject(oMetalLarge, 1, 1, 0, 4)] + ], + avoidClasses(clWater, 3, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 3, clFarm, 5), + clMetal +); +Engine.SetProgress(70); + +g_Map.log("Creating decorations"); +createDecoration( + [ + [new SimpleObject(aRockMedium, 1, 3, 3, 6)], + [new SimpleObject(aRockLarge, 1, 2, 2, 4), new SimpleObject(aRockMedium, 1, 3, 2, 6)], + ], + [ + scaleByMapSize(20, 300), + scaleByMapSize(10, 150), + ], + avoidClasses(clWater, 0, clForest, 0, clPlayer, 15, clHill, 1, clFarm, 1)); + +if (!winter) + 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, 1, 3), new SimpleObject(aBushSmall, 2, 4, 2, 4)] + ], + [ + scaleByMapSize(20, 300), + scaleByMapSize(20, 300), + scaleByMapSize(20, 300) + ], + avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFarm, 1, clDeco, 2)); + +Engine.SetProgress(75); + +g_Map.log("Creating food"); +createFood( + [ + [new SimpleObject(oDeer, 5, 7, 0, 4)], + [new SimpleObject(oRabbit, 2, 3, 0, 2)] + ], + [ + scaleByMapSize(8, 24), + scaleByMapSize(8, 24) + ], + avoidClasses(clWater, 3, clForest, 0, clPlayer, 25, clHill, 3, clFood, 10), + clFood); + +createFood( + [ + [new SimpleObject(oBear, 1, 1, 0, 4)] + ], + [ + scaleByMapSize(1, 7) + ], + avoidClasses(clWater, 3, clForest, 0, clPlayer, 25, clHill, 3), + clFood); + +createFood( + [ + [new SimpleObject(oBerryBush, 4, 6, 1, 4)] + ], + [ + scaleByMapSize(2, 6) * randIntInclusive(1, 4) * numPlayers + ], + avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 2, clFood, 10, clFarm, 1), + clFood); + +createFood( + [ + winter ? [new SimpleObject(oRabbit, 3, 5, 1, 2)] : [new SimpleObject(oSheep, 1, 1, 2, 3)] + ], + [ + scaleByMapSize(50, 200) + ], + [nearClasses(clFarm, 5)], + clFood); + +createFood( + [ + [new SimpleObject(oFish, 1, 3, 4, 6)] + ], + [ + scaleByMapSize(50, 400) * numLakes + ], + [avoidClasses(clFood, 4), stayClasses(clWater, 5)], + clFood); + +Engine.SetProgress(85); + +placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2)); + +g_Map.ExportMap(); Index: binaries/data/mods/public/maps/random/the_high_alps.json =================================================================== --- binaries/data/mods/public/maps/random/the_high_alps.json +++ binaries/data/mods/public/maps/random/the_high_alps.json @@ -0,0 +1,9 @@ +{ + "settings" : { + "Name" : "The High Alps", + "Script" : "the_high_alps.js", + "Description" : "High in the mountains of northern Europe, amongst cloistered valleys dotted with serene lakes.", + "Preview" : "high_alps.png", + "CircularMap" : true + } +}