Index: ps/trunk/binaries/data/mods/public/maps/random/elephantine.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/elephantine.js (revision 27908) +++ ps/trunk/binaries/data/mods/public/maps/random/elephantine.js (revision 27909) @@ -1,602 +1,600 @@ /** * Heightmap image source: * OpenStreetMap, available under Open Database Licence, www.openstreetmap.org/copyright * https://download.geofabrik.de/africa.html * * To reproduce the river image: * You need a gdal version that supports osm, see https://www.gdal.org/drv_osm.html * wget https://download.geofabrik.de/africa/egypt-latest.osm.pbf * lon=32.89; lat=24.09175; width=0.025; * lat1=$(bc <<< ";scale=5;$lat-$width/2"); lon1=$(bc <<< ";scale=5;$lon+$width/2"); lat2=$(bc <<< ";scale=5;$lat+$width/2"); lon2=$(bc <<< ";scale=5;$lon-$width/2") * rm elephantine.geojson; ogr2ogr -f GeoJSON elephantine.geojson -clipdst $lon1 $lat1 $lon2 $lat2 egypt-latest.osm.pbf -sql 'select * from multipolygons where natural="water"' * gdal_rasterize -burn 10 -ts 512 512 elephantine.geojson elephantine.tif * convert elephantine.tif -threshold 50% -negate elephantine.png * * No further changes should be applied to the image to keep it easily interchangeable. */ Engine.LoadLibrary("rmgen"); Engine.LoadLibrary("rmgen-common"); TILE_CENTERED_HEIGHT_MAP = true; const tPrimary = ["savanna_dirt_rocks_a_red", "savanna_dirt_a_red", "savanna_dirt_b_red"]; const tDirt = ["new_savanna_dirt_c", "new_savanna_dirt_d", "savanna_dirt_b_red", "savanna_dirt_plants_cracked"]; const tWater = "desert_sand_wet"; const tRoad = "savanna_tile_a_red"; const tRoadIsland = "savanna_tile_a"; const tRoadWildIsland = "savanna_dirt_rocks_a"; const tGrass = ["savanna_shrubs_a_wetseason", "alpine_grass_b_wild", "medit_shrubs_a", "steppe_grass_green_a"]; const tForestFloorLand = "savanna_forestfloor_b_red"; const tForestFloorIsland = pickRandom(tGrass); const oAcacia = "gaia/tree/acacia"; const oPalms = [ "gaia/tree/cretan_date_palm_tall", "gaia/tree/cretan_date_palm_short", "gaia/tree/palm_tropic", "gaia/tree/date_palm", "gaia/tree/senegal_date_palm", "gaia/tree/medit_fan_palm" ]; const oStoneLarge = "gaia/rock/savanna_large"; const oStoneSmall = "gaia/rock/desert_small"; const oMetalLarge = "gaia/ore/savanna_large"; const oMetalSmall = "gaia/ore/desert_small"; const oTreasure = [ "gaia/treasure/food_barrel", "gaia/treasure/food_bin", "gaia/treasure/wood", "gaia/treasure/metal", "gaia/treasure/stone" ]; const oBerryBush = "gaia/fruit/berry_05"; const oGazelle = "gaia/fauna_gazelle"; const oRhino = "gaia/fauna_rhinoceros_white"; const oWarthog = "gaia/fauna_boar"; const oGiraffe = "gaia/fauna_giraffe"; const oGiraffeInfant = "gaia/fauna_giraffe_infant"; const oElephant = "gaia/fauna_elephant_african_bush"; const oElephantInfant = "gaia/fauna_elephant_african_infant"; const oLion = "gaia/fauna_lion"; const oLioness = "gaia/fauna_lioness"; const oCrocodile = "gaia/fauna_crocodile_nile"; const oFish = "gaia/fish/tilapia"; const oHawk = "birds/buzzard"; // The main temple on elephantine was very similar looking (Greco-Roman-Egyptian): const oWonder = "structures/ptol/wonder"; const oTemples = ["structures/kush/temple_amun", "structures/kush/temple"]; const oPyramid = "structures/kush/pyramid_large"; const oTowers = new Array(2).fill("uncapturable|structures/kush/sentry_tower").concat(["uncapturable|structures/kush/defense_tower"]); const oHeroes = Engine.FindTemplates("units/kush/", true).filter(templateName => templateName.startsWith("units/kush/hero_")); const oUnits = Engine.FindTemplates("units/kush/", false).filter(templateName => templateName.startsWith("units/kush/") && oHeroes.every(heroTemplateName => heroTemplateName != templateName) && Engine.GetTemplate(templateName).Identity.VisibleClasses._string.split(" ").some(type => ["Soldier", "Healer", "Female"].indexOf(type) != -1)); const aRock = actorTemplate("geology/stone_savanna_med"); const aStatues = [ "props/structures/kushites/statue_bird", "props/structures/kushites/statue_lion", "props/structures/kushites/statue_ram" ].map(actorTemplate); const aBushesShoreline = [ ...new Array(4).fill("props/flora/ferns"), "props/flora/bush", "props/flora/bush_medit_la", "props/flora/bush_medit_la_lush", "props/flora/bush_medit_me_lush", "props/flora/bush_medit_sm", "props/flora/bush_medit_sm_lush", "props/flora/bush_tempe_la_lush" ].map(actorTemplate); const aBushesIslands = aBushesShoreline.concat(new Array(3).fill(actorTemplate("props/flora/foliagebush"))); const aBushesDesert = [ "props/flora/bush_dry_a", "props/flora/bush_medit_la_dry", "props/flora/bush_medit_me_dry", "props/flora/bush_medit_sm", "props/flora/bush_medit_sm_dry", "props/flora/bush_tempe_me_dry", "props/flora/grass_soft_dry_large_tall", "props/flora/grass_soft_dry_small_tall" ].map(actorTemplate); const pForestPalmsLand = [ tForestFloorLand, ...oPalms.map(tree => tForestFloorLand + TERRAIN_SEPARATOR + tree), tForestFloorLand]; const pForest2Land = [ tForestFloorLand, tForestFloorLand + TERRAIN_SEPARATOR + oAcacia, tForestFloorLand ]; const pForestPalmsIsland = [ tForestFloorIsland, ...oPalms.map(tree => tForestFloorIsland + TERRAIN_SEPARATOR + tree), tForestFloorIsland]; const pForest2Island = [ tForestFloorIsland, tForestFloorIsland + TERRAIN_SEPARATOR + oAcacia, tForestFloorIsland ]; -const heightScale = num => num * g_MapSettings.Size / 320; - -const heightSeaGround = heightScale(-15); -const heightWaterLevel = heightScale(0); -const heightShore = heightScale(1); -const heightOffsetPath = heightScale(-4); +const heightSeaGround = -6; +const heightWaterLevel = 0; +const heightShore = 0.5; +const heightOffsetPath = -g_MapSettings.Size / 80; const minHeight = -1; const maxHeight = 2; const g_Map = new RandomMap(0, tPrimary); const mapBounds = g_Map.getBounds(); const mapCenter = g_Map.getCenter(); const clWater = g_Map.createTileClass(); const clIsland = g_Map.createTileClass(); const clCliff = g_Map.createTileClass(); const clPlayer = g_Map.createTileClass(); const clBaseResource = g_Map.createTileClass(); const clForest = g_Map.createTileClass(); const clDirt = g_Map.createTileClass(); const clRock = g_Map.createTileClass(); const clMetal = g_Map.createTileClass(); const clFood = g_Map.createTileClass(); const clTemple = g_Map.createTileClass(); const clTower = g_Map.createTileClass(); const clStatue = g_Map.createTileClass(); const clSoldier = g_Map.createTileClass(); const clTreasure = g_Map.createTileClass(); const clPath = g_Map.createTileClass(); const riverAngle = 0.22 * Math.PI; const riverWidthBorder = fractionToTiles(0.27); const riverWidthCenter = fractionToTiles(0.35); const avoidCollisions = avoidClasses( clPlayer, 15, clWater, 1, clForest, 1, clRock, 4, clMetal, 4, clFood, 6, clPath, 1, clTemple, 11, clCliff, 0, clStatue, 2, clSoldier, 3, clTower, 2, clTreasure, 1); g_Map.LoadHeightmapImage("elephantine.png", minHeight, maxHeight); Engine.SetProgress(3); g_Map.log("Lowering sea ground"); createArea( new MapBoundsPlacer(), [ new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 4), new TileClassPainter(clWater) ], new HeightConstraint(-Infinity, heightWaterLevel)); Engine.SetProgress(6); g_Map.log("Smoothing heightmap"); createArea( new MapBoundsPlacer(), new SmoothingPainter(1, scaleByMapSize(0.1, 0.5), 1)); Engine.SetProgress(10); g_Map.log("Marking islands"); var areaIsland = createArea( new ConvexPolygonPlacer( [ new Vector2D(mapCenter.x - riverWidthBorder / 2, mapBounds.top), new Vector2D(mapCenter.x - riverWidthBorder / 2, mapBounds.bottom), new Vector2D(mapCenter.x - riverWidthCenter / 2, mapCenter.y), new Vector2D(mapCenter.x + riverWidthCenter / 2, mapCenter.y), new Vector2D(mapCenter.x + riverWidthBorder / 2, mapBounds.top), new Vector2D(mapCenter.x + riverWidthBorder / 2, mapBounds.bottom) ].map(v => v.rotateAround(riverAngle, mapCenter)), Infinity), new TileClassPainter(clIsland), avoidClasses(clPlayer, 0, clWater, 0)); Engine.SetProgress(13); g_Map.log("Painting islands"); createArea( new MapBoundsPlacer(), new TerrainPainter(tGrass), stayClasses(clIsland, 0)); Engine.SetProgress(16); g_Map.log("Painting water and shoreline"); createArea( new MapBoundsPlacer(), new TerrainPainter(tWater), new HeightConstraint(-Infinity, heightShore)); Engine.SetProgress(19); placePlayerBases({ "PlayerPlacement": playerPlacementRiver(riverAngle, fractionToTiles(0.62)), "PlayerTileClass": clPlayer, "BaseResourceClass": clBaseResource, "baseResourceConstraint": avoidClasses(clWater, 4), "Walls": "towers", "CityPatch": { "outerTerrain": tRoad, "innerTerrain": tRoad }, "StartingAnimal": { }, "Berries": { "template": oBerryBush }, "Mines": { "types": [ { "template": oMetalLarge }, { "template": oStoneLarge } ] }, "Trees": { "template": oAcacia, "count": 2 }, "Decoratives": { "template": pickRandom(aBushesDesert) } }); Engine.SetProgress(22); g_Map.log("Creating temple"); var groupTemple = createObjectGroupsByAreas( new SimpleGroup([new RandomObject(g_Map.getSize() >= 320 ? [oWonder] : oTemples, 1, 1, 0, 1, riverAngle, riverAngle)], true, clTemple), 0, stayClasses(clIsland, scaleByMapSize(10, 20)), 1, 200, [areaIsland]); Engine.SetProgress(34); g_Map.log("Creating pyramid"); var groupPyramid = createObjectGroupsByAreas( new SimpleGroup([new SimpleObject(oPyramid, 1, 1, 0, 1, riverAngle, riverAngle)], true, clTemple), 0, [stayClasses(clIsland, scaleByMapSize(10, 20)), avoidClasses(clTemple, scaleByMapSize(20, 50)), avoidCollisions], 1, 200, [areaIsland]); Engine.SetProgress(37); g_Map.log("Painting city patches"); var cityCenters = [ groupTemple[0] && groupTemple[0][0] && { "pos": Vector2D.from3D(groupTemple[0][0].position), "radius": 10 }, groupPyramid[0] && groupPyramid[0][0] && { "pos": Vector2D.from3D(groupPyramid[0][0].position), "radius": 6 },].filter(pos => !!pos); var areaCityPatch = cityCenters.map(cityCenter => createArea( new DiskPlacer(cityCenter.radius, cityCenter.pos), new LayeredPainter([tRoadWildIsland, tRoadIsland], [2]), stayClasses(clIsland, 2))); Engine.SetProgress(40); g_Map.log("Painting city path"); if (cityCenters.length == 2) createArea( new PathPlacer(cityCenters[0].pos, cityCenters[1].pos, 4, 0.3, 4, 0.2, 0.05), [ new LayeredPainter([tRoadWildIsland, tRoadIsland], [1]), new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetPath, 4), new TileClassPainter(clPath) ]); Engine.SetProgress(42); createBumps(avoidClasses(clPlayer, 10, clWater, 2, clTemple, 10, clPath, 1), scaleByMapSize(10, 500), 1, 8, 4, 0.2, 3); Engine.SetProgress(43); g_Map.log("Marking cliffs"); createArea( new MapBoundsPlacer(), new TileClassPainter(clCliff), [ avoidClasses(clWater, 2), new SlopeConstraint(2, Infinity) ]); Engine.SetProgress(44); 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)], [new SimpleObject(oStoneSmall, 2, 5, 1, 3, 0, 2 * Math.PI, 1)] ], avoidClasses(clWater, 4, clCliff, 4, clPlayer, 20, clRock, 10, clPath, 1), clRock, scaleByMapSize(6, 24)); Engine.SetProgress(45); g_Map.log("Creating metal mines"); createMines( [ [new SimpleObject(oMetalSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oMetalLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)], [new SimpleObject(oMetalSmall, 2, 5, 1, 3, 0, 2 * Math.PI, 1)] ], avoidClasses(clWater, 4, clCliff, 4, clPlayer, 20, clMetal, 10, clRock, 5, clPath, 1), clMetal, scaleByMapSize(6, 24)); Engine.SetProgress(46); g_Map.log("Creating kushite towers"); createObjectGroups( new SimpleGroup([new RandomObject(oTowers, 1, 1, 0, 1)], true, clTower), 0, [ stayClasses(clIsland, scaleByMapSize(4, 8)), new NearTileClassConstraint(clTemple, 25), avoidClasses(clTower, 12, clPlayer, 30), avoidCollisions ], scaleByMapSize(4, 12), 200); Engine.SetProgress(49); var [forestTrees, stragglerTrees] = getTreeCounts(400, 3000, 0.7); createForests( [tForestFloorLand, tForestFloorLand, tForestFloorLand, pForestPalmsLand, pForest2Land], [avoidCollisions, avoidClasses(clIsland, 0, clPlayer, 20, clForest, 18, clWater, 2)], clForest, forestTrees / 2); Engine.SetProgress(52); createForests( [tForestFloorIsland, tForestFloorIsland, tForestFloorIsland, pForestPalmsIsland, pForest2Island], [stayClasses(clIsland, 0), avoidClasses(clForest, 15, clWater, 2), avoidCollisions], clForest, forestTrees / 2); Engine.SetProgress(55); g_Map.log("Creating dirt patches"); createPatches( [scaleByMapSize(5, 15)], tDirt, avoidClasses(clWater, 0, clIsland, 0, clForest, 0, clDirt, 5, clPlayer, 12), scaleByMapSize(5, 30), clDirt); Engine.SetProgress(58); g_Map.log("Creating statues"); createObjectGroupsByAreas( new SimpleGroup([new RandomObject(aStatues, 1, 1, 0, 1)], true, clStatue), 0, [ stayClasses(clIsland, scaleByMapSize(8, 24)), new NearTileClassConstraint(clTemple, 10), avoidCollisions ], scaleByMapSize(2, 10), 400, [areaIsland]); Engine.SetProgress(61); g_Map.log("Creating treasure"); createObjectGroupsByAreas( new SimpleGroup([new RandomObject(oTreasure, 1, 2, 0, 1)], true, clTreasure), 0, avoidCollisions, scaleByMapSize(4, 10), 100, areaCityPatch); Engine.SetProgress(62); g_Map.log("Creating hero"); createObjectGroups( new SimpleGroup([new RandomObject(oHeroes, 1, 1, 0, 1)], true, clSoldier), 0, [ stayClasses(clIsland, scaleByMapSize(2, 24)), new NearTileClassConstraint(clTemple, 14), avoidCollisions ], 1, 500); Engine.SetProgress(64); g_Map.log("Creating soldiers"); createObjectGroups( new SimpleGroup([new RandomObject(oUnits, 1, 1, 0, 1)], true, clSoldier), 0, [ new StaticConstraint( [ stayClasses(clIsland, scaleByMapSize(2, 24)), new NearTileClassConstraint(clTemple, 20) ]), avoidCollisions ], scaleByMapSize(12, 60), 200); Engine.SetProgress(67); g_Map.log("Creating berries"); createObjectGroups( new SimpleGroup([new SimpleObject(oBerryBush, 3, 5, 1, 2)], true, clFood), 0, avoidCollisions, scaleByMapSize(4, 12), 250); Engine.SetProgress(70); g_Map.log("Creating rhinos"); createObjectGroups( new SimpleGroup([new SimpleObject(oRhino, 1, 1, 0, 1)], true, clFood), 0, avoidCollisions, scaleByMapSize(2, 10), 50); Engine.SetProgress(73); g_Map.log("Creating warthog"); createObjectGroups( new SimpleGroup([new SimpleObject(oWarthog, 1, 1, 0, 1)], true, clFood), 0, avoidCollisions, scaleByMapSize(2, 10), 50); Engine.SetProgress(77); g_Map.log("Creating gazelles"); createObjectGroups( new SimpleGroup([new SimpleObject(oGazelle, 5, 7, 2, 4)], true, clFood), 0, [avoidClasses(clIsland, 1), avoidCollisions], scaleByMapSize(2, 10), 50); Engine.SetProgress(80); g_Map.log("Creating giraffes"); createObjectGroups( new SimpleGroup([new SimpleObject(oGiraffe, 2, 3, 2, 4), new SimpleObject(oGiraffeInfant, 2, 3, 2, 4)], true, clFood), 0, avoidCollisions, scaleByMapSize(2, 10), 50); Engine.SetProgress(83); if (!isNomad()) { g_Map.log("Creating lions"); createObjectGroups( new SimpleGroup([new SimpleObject(oLion, 1, 2, 2, 4), new SimpleObject(oLioness, 2, 3, 2, 4)], true, clFood), 0, avoidCollisions, scaleByMapSize(2, 10), 50); Engine.SetProgress(87); } g_Map.log("Creating elephants"); createObjectGroups( new SimpleGroup([new SimpleObject(oElephant, 2, 3, 2, 4), new SimpleObject(oElephantInfant, 2, 3, 2, 4)], true, clFood), 0, avoidCollisions, scaleByMapSize(2, 10), 50); Engine.SetProgress(88); g_Map.log("Creating crocodiles"); createObjectGroups( new SimpleGroup([new SimpleObject(oCrocodile, 2, 3, 3, 5)], true, clFood), 0, [ new NearTileClassConstraint(clWater, 3), avoidCollisions ], scaleByMapSize(2, 10), 50); Engine.SetProgress(89); g_Map.log("Creating hawk"); for (let i = 0; i < scaleByMapSize(0, 2); ++i) g_Map.placeEntityAnywhere(oHawk, 0, mapCenter, randomAngle()); Engine.SetProgress(90); g_Map.log("Creating fish"); createObjectGroups( new SimpleGroup([new SimpleObject(oFish, 1, 2, 0, 1)], true, clFood), 0, [stayClasses(clWater, 8), avoidClasses(clFood, 16)], scaleByMapSize(15, 80), 50); Engine.SetProgress(91); createStragglerTrees( [oAcacia], avoidCollisions, clForest, stragglerTrees); Engine.SetProgress(93); placePlayersNomad(clPlayer, [avoidCollisions, avoidClasses(clIsland, 0)]); Engine.SetProgress(95); createDecoration( aBushesDesert.map(bush => [new SimpleObject(bush, 0, 3, 2, 4)]), aBushesDesert.map(bush => scaleByMapSize(20, 150) * randIntInclusive(1, 3)), [avoidClasses(clIsland, 0), avoidCollisions]); Engine.SetProgress(96); createDecoration( aBushesIslands.map(bush => [new SimpleObject(bush, 0, 4, 2, 4)]), aBushesIslands.map(bush => scaleByMapSize(20, 150) * randIntInclusive(1, 3)), [stayClasses(clIsland, 0), avoidCollisions]); Engine.SetProgress(97); createDecoration( [[new SimpleObject(aRock, 0, 4, 2, 4)]], [[scaleByMapSize(80, 500)]], [avoidClasses(clIsland, 0), avoidCollisions]); Engine.SetProgress(98); createDecoration( aBushesShoreline.map(bush => [new SimpleObject(bush, 0, 3, 2, 4)]), aBushesShoreline.map(bush => scaleByMapSize(200, 1000)), [new HeightConstraint(heightWaterLevel, heightShore), avoidCollisions]); Engine.SetProgress(99); g_Environment = { "SkySet": "cloudless", "SunColor": { "r": 1, "g": 0.964706, "b": 0.909804, "a": 0 }, "SunElevation": 0.908117, "SunRotation": -0.558369, "AmbientColor": { "r": 0.54902, "g": 0.419608, "b": 0.352941, "a": 0 }, "Fog": { "FogFactor": 0.00195313, "FogThickness": 0, "FogColor": { "r": 0.941176, "g": 0.917647, "b": 0.807843, "a": 0 } }, "Water": { "WaterBody": { "Type": "lake", "Color": { "r": 0.443137, "g": 0.341176, "b": 0.14902, "a": 0 }, "Tint": { "r": 0.705882, "g": 0.67451, "b": 0.454902, "a": 0 }, "Waviness": 8.4668, "Murkiness": 0.92, "WindAngle": 0.625864 } }, "Postproc": { "Brightness": 0.0234375, "Contrast": 1.09961, "Saturation": 0.828125, "Bloom": 0.142969, "PostprocEffect": "hdr" } }; g_Map.ExportMap();