Index: ps/trunk/binaries/data/mods/public/maps/random/aegean_sea.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/aegean_sea.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/aegean_sea.js (revision 20429) @@ -1,362 +1,364 @@ RMS.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"; 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 tCorals1 = "medit_sea_coral_plants"; const tCorals2 = "medit_sea_coral_deep"; 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 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]; InitMap(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); 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(); var clIsland = createTileClass(); var clCorals = createTileClass(); var playerIDs = primeSortAllPlayers(); var playerPos = placePlayersRiver(); var playerX = []; var playerZ = []; for (var i = 0; i < numPlayers; i++) { playerZ[i] = playerPos[i]; playerX[i] = 0.2 + 0.6*(i%2); } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles(playerX[i]); var fz = fractionToTiles(playerZ[i]); var ix = floor(fx); var iz = floor(fz); addToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tCityPlaza, tCity], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oCarob, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aBush1, clBaseResource, radius); } RMS.SetProgress(30); paintRiver({ - "horizontal": false, "parallel": false, - "position": 0.5, + "startX": 0.5, + "startZ": 0, + "endX": 0.5, + "endZ": 1, "width": 0.35, "fadeDist": 0.025, "deviation": 0, "waterHeight": -3, "landHeight": 2, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { if (height < 0.7) addToClass(ix, iz, clWater); } }); paintTerrainBasedOnHeight(-20, 1, 0, tWater); paintTerrainBasedOnHeight(1, 2, 0, tShore); RMS.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); RMS.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); RMS.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); RMS.SetProgress(60); log("Creating undersea bumps..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5), [ new SmoothElevationPainter(ELEVATION_SET, -2.5, 3), paintClass(clCorals) ], stayClasses(clWater, 6), scaleByMapSize(10, 50) ); log("Creating islands..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(30, 80)), 0.5), [ new LayeredPainter([tWater, tShore, tHill], [2 ,1]), new SmoothElevationPainter(ELEVATION_SET, 6, 4), paintClass(clIsland) ], [avoidClasses(clPlayer, 8, clForest, 1, clIsland, 15), stayClasses (clWater, 6)], scaleByMapSize(1, 4) * numPlayers ); paintTerrainBasedOnHeight(-20, -3, 3, tSeaDepths); paintTerrainBasedOnHeight(-3, -2, 2, tCorals2); paintTerrainBasedOnHeight(-2, -1.5, 2, tCorals1); log("Creating island stone mines..."); createMines( [ [new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], [new SimpleObject(oStoneSmall, 2,5, 1,3)] ], stayClasses(clIsland, 4), clRock); log("Creating island metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], stayClasses(clIsland, 4), clMetal ); 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, 20, clRock, 10, clWater, 1, clHill, 1), clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clWater, 1, clHill, 1), clMetal ); RMS.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, 0, clHill, 1) ); RMS.SetProgress(70); createFood ( [ [new SimpleObject(oFish, 2,3, 0,2)] ], [ 3*scaleByMapSize(5,20) ], [avoidClasses(clIsland, 2, clFood, 10), stayClasses(clWater, 5)] ); 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, 8, clWater, 1, clFood, 10, clHill, 1) ); createFood ( [ [new SimpleObject(oBerryBush, 5,7, 0,4)] ], [ 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10) ); RMS.SetProgress(90); var types = [oDatePalm, oSDatePalm, oCarob, oFanPalm, oPoplar, oCypress]; createStragglerTrees( types, avoidClasses(clForest, 1, clWater, 2, clPlayer, 12, clMetal, 6, clHill, 1), clForest, stragglerTrees); createStragglerTrees( types, stayClasses(clIsland, 4), clForest, stragglerTrees * 10); setSkySet("cumulus"); setSunColor(0.866667, 0.776471, 0.486275); setWaterColor(0, 0.501961, 1); setWaterTint(0.501961, 1, 1); setWaterWaviness(4.0); 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/danubius.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/danubius.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/danubius.js (revision 20429) @@ -1,860 +1,861 @@ RMS.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 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 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; InitMap(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); 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(); // Percentage of the mapsize that the river takes up const waterWidth = 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 gX = i == 0 ? gaulCityBorderDistance : mapSize - gaulCityBorderDistance; let gZ = mapSize / 2; if (addCelticRitual) { // Don't position the meeting place at the center of the map let mLocation = randFloat(0.1, 0.4) * (randBool() ? 1 : -1); // Center of the meeting place let mX = i == 0 ? mapSize * waterWidth : mapSize * (1 - waterWidth); let mZ = gZ + mapSize * mLocation; // 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 placer = new PathPlacer( gX + gaulCityRadius * (i == 0 ? 1 : -1), gZ, mX, mZ, 4, // width 0.4, // waviness 4, // smoothness 0.2, // offset 0.05); // tapering createArea( placer, [ new LayeredPainter([tShore, tRoad, tRoad], [1, 3]), new SmoothElevationPainter(ELEVATION_SET, 5, 4), paintClass(clPath) ]); // Create the meeting place near the shoreline at the end of the path createArea( new ClumpPlacer(mRadius * mRadius * PI, 0.6, 0.3, 10, mX, mZ), [new LayeredPainter([tShore, tShore], [1]), paintClass(clPath), paintClass(clRitualPlace)], null); placeObject(mX, mZ, aCampfire, 0, randFloat(0, 2 * PI)); 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; wallStyles.celt_ritual = { "female": new WallElement("female", oFemale, PI, femaleRadius, 0, 2 * PI / femaleCount), "skirmisher": new WallElement("skirmisher", oSkirmisher, PI, maleRadius, 0, 2 * PI / maleCount), "healer": new WallElement("healer", oHealer, PI, maleRadius, 0, 2 * PI / maleCount), "fanatic": new WallElement("fanatic", oNakedFanatic, PI, maleRadius, 0, 2 * PI / maleCount), "bench": new WallElement("bench", aBench, PI/2, benchRadius, 0, 2 * PI / benchCount), "rug": new WallElement("rug", aRug, 0, rugRadius, 0, 2 * PI / rugCount), "goat": new WallElement("goat", oGoat, PI, goatRadius, 0, 2 * PI / goatCount), }; placeCustomFortress(mX, mZ, new Fortress("celt ritual females", new Array(femaleCount).fill("female")), "celt_ritual", 0, 0); placeCustomFortress(mX, mZ, new Fortress("celt ritual males", new Array(maleCount).fill(0).map(i => pickRandom(["skirmisher", "healer", "fanatic"]))), "celt_ritual", 0, 0); placeCustomFortress(mX, mZ, new Fortress("celt ritual bench", new Array(benchCount).fill("bench")), "celt_ritual", 0, 0); placeCustomFortress(mX, mZ, new Fortress("celt ritual rug", new Array(rugCount).fill("rug")), "celt_ritual", 0, 0); placeCustomFortress(mX, mZ, new Fortress("celt ritual goat", new Array(goatCount).fill("goat")), "celt_ritual", 0, 0); } placeObject(gX, gZ, oCivicCenter, 0, BUILDING_ORIENTATION + PI * 3/2 * i); // Create the city patch createArea( new ClumpPlacer(gaulCityRadius * gaulCityRadius * PI, 0.6, 0.3, 10, gX, gZ), [new LayeredPainter([tShore, tShore], [1]), paintClass(clGauls)], null); // Place palisade fortress and some city buildings // Use actors to avoid players capturing the buildings wallStyles.gaul.house = new WallElement("house", oHouse, PI, 0, 4); wallStyles.gaul.hut = new WallElement("hut", oHut, PI, 0, 4); wallStyles.gaul.longhouse = new WallElement("longhouse", oLongHouse, PI, 0, 4); wallStyles.gaul.tavern = new WallElement("tavern", oTavern, PI * 3/2, 0, 4); wallStyles.gaul.temple = new WallElement("temple", oTemple, PI * 3/2, 0, 4); wallStyles.gaul.defense_tower = new WallElement("defense_tower", mapSize >= normalMapSize ? oTower : oPalisadeTower, PI/2, 0, 4); wallStyles.gaul.palisade_tower = wallStyles.palisades.tower; // Replace stone walls with palisade walls for (let template of ["gate", "wallLong", "cornerIn", "cornerOut"]) wallStyles.gaul[template] = wallStyles.palisades[template]; let wall = [ "gate", "hut", "palisade_tower", "wallLong", "wallLong", "cornerIn", "defense_tower", "wallLong", "wallLong", "temple", "palisade_tower", "wallLong", "house", "gate", "palisade_tower", "longhouse", "wallLong", "wallLong", "cornerIn", "defense_tower", "wallLong", "tavern", "wallLong", "palisade_tower"]; wall = wall.concat(wall); placeCustomFortress(gX, gZ, new Fortress("Geto-Dacian Tribal Confederation", wall), "gaul", 0, PI); // Place spikes wallStyles.palisades.tall_spikes = new WallElement("tall_spikes", oTallSpikes, PI/2, 2); wallStyles.palisades.spikeIn = new WallElement("spikeIn", oAngleSpikes, -PI/4, 2.1, 0.7, PI/2); wallStyles.palisades.spikeMid = new WallElement("spikeIn", oAngleSpikes, -PI/2, 0.7); wallStyles.palisades.gateGap = new WallElement("gateGap", undefined, PI, 3.6); let fourSpikes = new Array(4).fill("tall_spikes"); let sixSpikes = new Array(6).fill("tall_spikes"); let spikes = [ "gateGap", "spikeMid", ...fourSpikes, "spikeIn", ...sixSpikes, "spikeMid", "gateGap", "spikeMid", ...fourSpikes, "spikeIn", ...fourSpikes, "spikeMid" ]; spikes = spikes.concat(spikes); placeCustomFortress(gX, gZ, new Fortress("spikes", spikes), "palisades", 0, PI); // Place treasure, potentially inside buildings for (let i = 0; i < gallicCCTreasureCount; ++i) placeObject( gX + randFloat(-0.8, 0.8) * gaulCityRadius, gZ + randFloat(-0.8, 0.8) * gaulCityRadius, pickRandom(oTreasures), 0, randFloat(0, 2 * PI)); } } RMS.SetProgress(10); var playerIDs = primeSortAllPlayers(); var playerPos = placePlayersRiver(); var playerX = []; var playerZ = []; for (let i = 0; i < numPlayers; ++i) { playerZ[i] = playerPos[i]; playerX[i] = 0.2 + 0.6 * (i % 2); } for (let i = 0; i < numPlayers; ++i) { let id = playerIDs[i]; log("Creating base for player " + id + "..."); let radius = scaleByMapSize(15, 25); let fx = fractionToTiles(playerX[i]); let fz = fractionToTiles(playerZ[i]); let ix = Math.floor(fx); let iz = Math.floor(fz); addToClass(ix, iz, clPlayer); // Create the city patch let cityRadius = radius / 3; createArea( new ClumpPlacer(PI * cityRadius * cityRadius, 0.6, 0.3, 10, ix, iz), new LayeredPainter([tShore, tRoad], [1]), null); placeCivDefaultEntities(fx, fz, id, { 'iberWall': false }); placeDefaultChicken(fx, fz, clBaseResource); // Create berry bushes let angle = randFloat(0, 2 * PI); let dist = 10; createObjectGroup( new SimpleGroup( [new SimpleObject(oBerryBush, 5, 5, 0, 3)], true, clBaseResource, Math.round(fx + dist * Math.cos(angle)), Math.round(fz + dist * Math.sin(angle)) ), 0); // Create metal mine dist = scaleByMapSize(9, 14); angle += randFloat(PI/4, PI/3); createObjectGroup( new SimpleGroup( [new SimpleObject(oMetalLarge, 1, 1, 0, 0)], true, clBaseResource, Math.round(fx + dist * Math.cos(angle)), Math.round(fz + dist * Math.sin(angle)) ), 0); // Create stone mines angle += randFloat(PI/3, PI/2); createObjectGroup( new SimpleGroup( [new SimpleObject(oStoneLarge, 1, 1, 0, 2)], true, clBaseResource, Math.round(fx + dist * Math.cos(angle)), Math.round(fz + dist * Math.sin(angle)) ), 0); // Create starting trees let num = 20; angle += randFloat(-PI/3, PI * 4/3); dist = randFloat(10, 14); createObjectGroup( new SimpleGroup( [new SimpleObject(oOak, num, num, 0, 5)], false, clBaseResource, Math.round(fx + dist * Math.cos(angle)), Math.round(fz + dist * Math.sin(angle)) ), 0, avoidClasses(clBaseResource, 4)); placeDefaultDecoratives(fx, fz, aBush1, clBaseResource, radius); } RMS.SetProgress(20); -log("Creating the river"); paintRiver({ - "horizontal": false, "parallel": true, - "position": 0.5, + "startX": 0.5, + "startZ": 0, + "endX": 0.5, + "endZ": 1, "width": waterWidth, "fadeDist": 0.025, "deviation": 0, "waterHeight": -3, "landHeight": 2, "meanderShort": 30, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { if (height < 0.7) addToClass(ix, iz, clWater); // Distinguish left and right shoreline if (0 < height && height < 1 && iz > ShorelineDistance && iz < mapSize - ShorelineDistance) addToClass(ix, iz, clShore[ix < mapSize / 2 ? 0 : 1]); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { if (shoreDist1 > 0) addToClass(ix, iz, clLand[0]); if (shoreDist2 < 0) addToClass(ix, iz, clLand[1]); } }); RMS.SetProgress(30); log("Creating shores..."); paintTerrainBasedOnHeight(-20, 1, 0, tWater); paintTerrainBasedOnHeight(1, 2, 0, tShore); RMS.SetProgress(35); log("Creating bumps..."); createBumps(avoidClasses(clPlayer, 6, clWater, 2, clPath, 1), scaleByMapSize(30, 300), 1, 8, 4, 0, 3); RMS.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)); RMS.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); RMS.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); RMS.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, 6, 4), paintClass(clIsland) ], [avoidClasses(clIsland, 30), stayClasses (clWater, 10)], scaleByMapSize(1, 4) * numPlayers ); RMS.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 ); RMS.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 ); RMS.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)] ); RMS.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 ); RMS.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..."); 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 ); RMS.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 ); RMS.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); RMS.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 ); RMS.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, PI/4, PI/2)], [new SimpleObject(aWheel, 2, 4, 1, 2)], [new SimpleObject(aWell, 1, 1, 0, 2)], [new SimpleObject(aWoodcord, 1, 2, 2, 2, PI/2, 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(mapSize/2, mapSize/2, clMiddle); 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 ); 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/english_channel.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/english_channel.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/english_channel.js (revision 20429) @@ -1,325 +1,327 @@ RMS.LoadLibrary("rmgen"); const tGrass = ["temp_grass", "temp_grass", "temp_grass_d"]; 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 tShore = "temp_dirt_gravel"; const tWater = "temp_dirt_gravel_b"; const oBeech = "gaia/flora_tree_euro_beech"; const oPoplar = "gaia/flora_tree_poplar"; const oApple = "gaia/flora_tree_apple"; const oOak = "gaia/flora_tree_oak"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oGoat = "gaia/fauna_goat"; const oBoar = "gaia/fauna_boar"; 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 aRockLarge = "actor|geology/stone_granite_large.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBushMedium = "actor|props/flora/bush_medit_me_lush.xml"; const aBushSmall = "actor|props/flora/bush_medit_sm_lush.xml"; const aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; const aLillies = "actor|props/flora/water_lillies.xml"; const pForestD = [tGrassDForest + TERRAIN_SEPARATOR + oBeech, tGrassDForest]; InitMap(); const numPlayers = getNumPlayers(); 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 landHeight = 3; var waterHeight = -4; var playerIDs = primeSortAllPlayers(); var playerPos = placePlayersRiver(); var playerX = []; var playerZ = []; for (var i = 0; i < numPlayers; i++) { playerZ[i] = 0.2 + 0.6*(i%2); playerX[i] = playerPos[i]; } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles(playerX[i]); var fz = fractionToTiles(playerZ[i]); var ix = round(fx); var iz = round(fz); // Setting tile class addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, [painter, paintClass(clPlayer)], null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) mAngle = randFloat(0, TWO_PI); var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oOak, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius); } RMS.SetProgress(10); paintRiver({ - "horizontal": true, "parallel": false, - "position": 0.5, + "startX": 0, + "startZ": 0.5, + "endX": 1, + "endZ": 0.5, "width": 0.25, "fadeDist": 0.01, "deviation": 0, "waterHeight": waterHeight, "landHeight": landHeight, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { placeTerrain(ix, iz, height < -1.5 ? tWater : tShore); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { setHeight(ix, iz, landHeight + 0.1); } }); RMS.SetProgress(20); createTributaryRivers( true, randIntInclusive(9, scaleByMapSize(13, 21)), scaleByMapSize(10, 20), waterHeight, [-6, -1.5], Math.PI / 5, clWater, clShallow, avoidClasses(clPlayer, 8, clBaseResource, 4)); paintTerrainBasedOnHeight(-5, 1, 1, tWater); paintTerrainBasedOnHeight(1, landHeight, 1, tShore); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); RMS.SetProgress(25); createBumps(avoidClasses(clWater, 5, clPlayer, 20)); RMS.SetProgress(30); createHills([tCliff, tCliff, tHill], avoidClasses(clPlayer, 20, clHill, 15, clWater, 5), clHill, scaleByMapSize(1, 4) * numPlayers); RMS.SetProgress(50); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); createForests( [tGrass, tGrassDForest, tGrassDForest, pForestD, pForestD], avoidClasses(clPlayer, 20, clForest, 17, clHill, 0, clWater, 6), clForest, forestTrees); RMS.SetProgress(70); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tGrass,tGrassA], tGrassB, [tGrassB,tGrassC]], [1,1], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], [tGrassPatchBlend, tGrassPatch], [1], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45), clDirt); RMS.SetProgress(80); 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, 2, clForest, 1, clPlayer, 20, clRock, 10, clHill, 2), clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], avoidClasses(clWater, 2, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 2), clMetal ); RMS.SetProgress(85); 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, -PI/8,PI/8)], [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)], [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, 1, clForest, 0, clPlayer, 0, clHill, 0) ); // create water decoration in the shallow parts createDecoration ( [[new SimpleObject(aReeds, 1,3, 0,1)], [new SimpleObject(aLillies, 1,2, 0,1)] ], [ scaleByMapSize(800, 12800), scaleByMapSize(800, 12800) ], stayClasses(clShallow, 0) ); createFood ( [ [new SimpleObject(oDeer, 5,7, 0,4)], [new SimpleObject(oGoat, 2,3, 0,2)], [new SimpleObject(oBoar, 2,3, 0,2)] ], [ 3 * numPlayers, 3 * numPlayers, 3 * numPlayers ], avoidClasses(clWater, 1, clForest, 0, clPlayer, 20, clHill, 0, clFood, 15) ); createFood ( [ [new SimpleObject(oBerryBush, 5,7, 0,4)] ], [ randIntInclusive(1, 4) * numPlayers + 2 ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10) ); createFood ( [ [new SimpleObject(oFish, 2,3, 0,2)] ], [scaleByMapSize(3, 25) * numPlayers], [avoidClasses(clFood, 6), stayClasses(clWater, 4)] ); createStragglerTrees( [oBeech, oPoplar, oApple], avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 8, clMetal, 6, clRock, 6), clForest, stragglerTrees); setSkySet("cirrus"); setWaterColor(0.114, 0.192, 0.463); setWaterTint(0.255, 0.361, 0.651); setWaterWaviness(3.0); setWaterType("ocean"); setWaterMurkiness(0.83); setFogThickness(0.35); setFogFactor(0.55); setPPEffect("hdr"); setPPSaturation(0.62); setPPContrast(0.62); setPPBloom(0.37); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/guadalquivir_river.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/guadalquivir_river.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/guadalquivir_river.js (revision 20429) @@ -1,339 +1,342 @@ RMS.LoadLibrary("rmgen"); const tGrass = ["medit_grass_field_a", "medit_grass_field_b"]; const tForestFloorC = "medit_plants_dirt"; const tForestFloorP = "medit_grass_shrubs"; const tGrassA = "medit_grass_field_b"; const tGrassB = "medit_grass_field_brown"; const tGrassC = "medit_grass_field_dry"; const tRoad = "medit_city_tile"; const tRoadWild = "medit_city_tile"; const tGrassPatch = "medit_grass_shrubs"; const tShore = "sand_grass_25"; const tWater = "medit_sand_wet"; const oPoplar = "gaia/flora_tree_poplar"; const oApple = "gaia/flora_tree_apple"; const oCarob = "gaia/flora_tree_carob"; 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_medit_quarry"; const oStoneSmall = "gaia/geology_stone_mediterranean"; const oMetalLarge = "gaia/geology_metal_mediterranean_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/water_lillies.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 pForestP = [tForestFloorP + TERRAIN_SEPARATOR + oPoplar, tForestFloorP]; const pForestC = [tForestFloorC + TERRAIN_SEPARATOR + oCarob, tForestFloorC]; InitMap(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); 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 clRiver = createTileClass(); var clShallow = createTileClass(); +var landHeight = 3; +var shoreHeight = 2; +var shallowHeight = -1.5; +var waterHeight = -3; + log("Create the continent body"); createArea( new ChainPlacer( 2, Math.floor(scaleByMapSize(5, 12)), Math.floor(scaleByMapSize(60, 700)), 1, Math.floor(fractionToTiles(0.5)), Math.floor(fractionToTiles(0.7)), 0, [Math.floor(mapSize * 0.49)]), [ new LayeredPainter([tGrass, tGrass, tGrass], [4, 2]), - new SmoothElevationPainter(ELEVATION_SET, 3, 4), + new SmoothElevationPainter(ELEVATION_SET, landHeight, 4), paintClass(clLand) ], null); var playerIDs = primeSortAllPlayers(); // place players var playerX = []; var playerZ = []; var playerAngle = []; var startAngle = 0; for (var i = 0; i < numPlayers; i++) { playerAngle[i] = startAngle - 0.23*(i+(i%2))*TWO_PI/numPlayers - (i%2)*PI/2; playerX[i] = 0.5 + 0.35*cos(playerAngle[i]); playerZ[i] = 0.7 + 0.35*sin(playerAngle[i]); } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles let fx = fractionToTiles(playerX[i]); let fz = fractionToTiles(playerZ[i]); let ix = Math.round(fx); let iz = Math.round(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id, { 'iberWall': false }); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 8; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 10; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oPoplar, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius); } - RMS.SetProgress(20); -var shallowHeight = -1.5; paintRiver({ - "horizontal": false, "parallel": true, "constraint": stayClasses(clLand, 0), - "position": 0.5, + "startX": 0.5, + "startZ": 0, + "endX": 0.5, + "endZ": 1, "width": 0.07, "fadeDist": 0.025, - "deviation": 0.005, - "waterHeight": -3, - "landHeight": 2, + "deviation": 0.0025, + "waterHeight": waterHeight, + "landHeight": shoreHeight, "meanderShort": 12, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, z) => { addToClass(ix, iz, clRiver); placeTerrain(ix, iz, tWater); - let z = iz / (mapSize + 1.0); - if (height < shallowHeight && ( z > 0.3 && z < 0.4 || z > 0.5 && z < 0.6 || z > 0.7 && z < 0.8)) { setHeight(ix, iz, shallowHeight); addToClass(ix, iz, clShallow); } } }); paintTerrainBasedOnHeight(1, 3, 0, tShore); paintTerrainBasedOnHeight(-8, 1, 2, tWater); createBumps([avoidClasses(clPlayer, 20, clRiver, 1), stayClasses(clLand, 3)]); var [forestTrees, stragglerTrees] = getTreeCounts(500, 3000, 0.7); createForests( [tGrass, tForestFloorP, tForestFloorC, pForestC, pForestP], [avoidClasses(clPlayer, 20, clForest, 17, clHill, 0, clRiver, 1), stayClasses(clLand, 7)], clForest, forestTrees); RMS.SetProgress(50); log("Creating dirt patches..."); createLayeredPatches( [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)], [[tGrass,tGrassA],[tGrassA,tGrassB], [tGrassB,tGrassC]], [1,1], [avoidClasses(clForest, 0, clHill, 0, clDirt, 3, clPlayer, 8, clRiver, 1), stayClasses(clLand, 7)], scaleByMapSize(15, 45), clDirt); log("Creating grass patches..."); createPatches( [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)], tGrassPatch, [avoidClasses(clForest, 0, clHill, 0, clDirt, 3, clPlayer, 8, clRiver, 1), stayClasses(clLand, 7)], scaleByMapSize(15, 45), clDirt); RMS.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(clForest, 1, clPlayer, 20, clRock, 10, clHill, 1, clRiver, 1), stayClasses(clLand, 5)], clRock); log("Creating metal mines..."); createMines( [ [new SimpleObject(oMetalLarge, 1,1, 0,4)] ], [avoidClasses(clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clHill, 1, clRiver, 1), stayClasses(clLand, 5)], clMetal ); RMS.SetProgress(65); 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, -PI/8,PI/8)], [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)], [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(clHill, 1, clPlayer, 1, clDirt, 1, clRiver, 1), stayClasses(clLand, 6)] ); log("Create water decoration in the shallow parts"); createDecoration ( [[new SimpleObject(aReeds, 1,3, 0,1)], [new SimpleObject(aLillies, 1,2, 0,1)] ], [ scaleByMapSize(800, 12800), scaleByMapSize(800, 12800) ], stayClasses(clShallow, 0) ); RMS.SetProgress(70); createFood ( [ [new SimpleObject(oDeer, 5,7, 0,4)], [new SimpleObject(oSheep, 2,3, 0,2)] ], [ 3 * numPlayers, 3 * numPlayers ], [avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clFood, 20, clRiver, 1), stayClasses(clLand, 3)] ); createFood ( [ [new SimpleObject(oBerryBush, 5,7, 0,4)] ], [ randIntInclusive(1, 4) * numPlayers + 2 ], [avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clFood, 10, clRiver, 1), stayClasses(clLand, 3)] ); createFood ( [ [new SimpleObject(oFish, 2,3, 0,2)] ], [ 25 * numPlayers ], avoidClasses(clLand, 2, clRiver, 1) ); RMS.SetProgress(85); createStragglerTrees( [oPoplar, oCarob, oApple], [avoidClasses(clForest, 1, clHill, 1, clPlayer, 9, clMetal, 6, clRock, 6, clRiver, 1), stayClasses(clLand, 7)], clForest, stragglerTrees); setSkySet("cumulus"); setWaterColor(0.2,0.312,0.522); setWaterTint(0.1,0.1,0.8); setWaterWaviness(4.0); setWaterType("lake"); setWaterMurkiness(0.73); 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/hyrcanian_shores.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/hyrcanian_shores.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/hyrcanian_shores.js (revision 20429) @@ -1,381 +1,383 @@ RMS.LoadLibrary("rmgen"); const tGrass = ["temp_grass_clovers"]; const tGrassPForest = "temp_plants_bog"; const tGrassDForest = "alpine_dirt_grass_50"; const tCliff = ["temp_cliff_a", "temp_cliff_b"]; const tGrassA = "temp_grass_d"; const tGrassB = "temp_grass_c"; const tGrassC = "temp_grass_clovers_2"; const tHill = ["temp_highlands", "temp_grass_long_b"]; const tRoad = "temp_road"; const tRoadWild = "temp_road_overgrown"; const tGrassPatch = "temp_grass_plants"; const tShore = "medit_sand_wet"; const tWater = "medit_sand_wet"; const oPoplar = "gaia/flora_tree_poplar"; const oPalm = "gaia/flora_tree_cretan_date_palm_short"; const oApple = "gaia/flora_tree_apple"; const oOak = "gaia/flora_tree_oak"; const oBerryBush = "gaia/flora_bush_berry"; const oDeer = "gaia/fauna_deer"; const oFish = "gaia/fauna_fish"; const oGoat = "gaia/fauna_goat"; const oBoar = "gaia/fauna_boar"; 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 aRockLarge = "actor|geology/stone_granite_large.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBushMedium = "actor|props/flora/bush_medit_me_lush.xml"; const aBushSmall = "actor|props/flora/bush_medit_sm_lush.xml"; const pForestD = [tGrassDForest + TERRAIN_SEPARATOR + oPoplar, tGrassDForest]; const pForestP = [tGrassPForest + TERRAIN_SEPARATOR + oOak, tGrassPForest]; InitMap(); const numPlayers = getNumPlayers(); 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 clSea = createTileClass(); var clHighlands = createTileClass(); var clFlatlands = createTileClass(); var playerIDs = sortAllPlayers(); for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles((i + 1) / (numPlayers + 1)); var fz = fractionToTiles(0.4 + 0.2 * (i % 2)); var ix = round(fx); var iz = round(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) mAngle = randFloat(0, TWO_PI); var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oOak, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius); } RMS.SetProgress(10); paintRiver({ - "horizontal": true, "parallel": true, - "position": 1, + "startX": 0, + "startZ": 1, + "endX": 1, + "endZ": 1, "width": 0.5, "fadeDist": 0.05, "deviation": 0, "waterHeight": -3, "landHeight": 1, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { if (height < 0) addToClass(ix, iz, clWater); if (height < -1.5) placeTerrain(ix, iz, tWater); else placeTerrain(ix, iz, tShore); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { addToClass(ix, iz, clHighlands); } }); RMS.SetProgress(20); log("Creating fish..."); for (let i = 0; i < scaleByMapSize(10, 20); ++i) createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood), 0, [stayClasses(clWater, 2), avoidClasses(clFood, 3)], numPlayers, 50); RMS.SetProgress(25); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, 4, 3), stayClasses(clHighlands, 1), scaleByMapSize(300, 600)); RMS.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, 15, 2), paintClass(clHill) ], avoidClasses(clSea, 5, clPlayer, 20, clWater, 5, clHill, 15, clHighlands, 5), scaleByMapSize(1, 4) * numPlayers); RMS.SetProgress(35); log("Creating mainland forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(500, 2500, 0.7); var types = [ [[tGrassDForest, tGrass, pForestD], [tGrassDForest, pForestD]] ]; var size = forestTrees * 1.3 / (scaleByMapSize(2,8) * numPlayers); var num = Math.floor(0.7 * 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, clWater, 3, clForest, 10, clHill, 0, clSea, 6, clBaseResource, 3), num); RMS.SetProgress(45); log("Creating highland forests..."); var types = [ [[tGrassDForest, tGrass, pForestP], [tGrassDForest, pForestP]] ]; var size = forestTrees / (scaleByMapSize(2,8) * numPlayers); var num = 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, clWater, 3, clForest, 2, clHill, 0, clSea, 6, clFlatlands, 3), num); RMS.SetProgress(70); 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, tGrassA], [tGrassA, tGrassB], [tGrassB, tGrassC]], [1, 1]), paintClass(clDirt) ], avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 4, clSea, 0), scaleByMapSize(15, 45)); RMS.SetProgress(75); 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 LayeredPainter([tGrassC, tGrassPatch], [2]), avoidClasses(clWater, 1, clForest, 0, clHill, 0, clDirt, 5, clPlayer, 6, clSea, 0, clBaseResource, 6), scaleByMapSize(15, 45)); RMS.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, 0, clForest, 1, clPlayer, 20, clRock, 10, clSea, 2, clHill, 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(clWater, 0, clForest, 1, clPlayer, 20, clRock, 10, clSea, 2, clHill, 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(clWater, 0, clForest, 1, clPlayer, 20, clMetal, 10, clRock, 5, clSea, 0, clHill, 2)], scaleByMapSize(4,16), 100 ); RMS.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, clHill, 0, clSea, 0), scaleByMapSize(16, 262), 50 ); RMS.SetProgress(90); 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, clSea, 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, 20, clHill, 0, clFood, 5, clSea, 0, clFlatlands, 0), 6 * numPlayers, 50 ); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oGoat, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 0, clFood, 20, clSea, 0), 3 * numPlayers, 50 ); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 6, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50 ); log("Creating boar..."); group = new SimpleGroup( [new SimpleObject(oBoar, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 20, clHill, 0, clFood, 20, clSea, 0, clFlatlands, 0), 2 * numPlayers, 50 ); createStragglerTrees( [oPoplar, oPalm, oApple], avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 10, clMetal, 6, clRock, 6, clSea, 1, clHighlands, 25), clForest, stragglerTrees); log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -PI/8,PI/8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0, clSea, 1), scaleByMapSize(13, 200) ); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0, clSea, 1), scaleByMapSize(13, 200) ); RMS.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, 1, clHill, 1, clPlayer, 1, clDirt, 1, clSea, 1), scaleByMapSize(13, 200), 50 ); setSkySet("cirrus"); setWaterColor(0.114, 0.192, 0.463); setWaterTint(0.255, 0.361, 0.651); setWaterWaviness(2.0); setWaterType("ocean"); setWaterMurkiness(0.83); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/kerala.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/kerala.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/kerala.js (revision 20429) @@ -1,380 +1,382 @@ RMS.LoadLibrary("rmgen"); const tGrass = ["tropic_grass_c", "tropic_grass_c", "tropic_grass_c", "tropic_grass_c", "tropic_grass_plants", "tropic_plants", "tropic_plants_b"]; const tGrassA = "tropic_plants_c"; const tGrassB = "tropic_plants_c"; const tGrassC = "tropic_grass_c"; const tForestFloor = "tropic_grass_plants"; const tCliff = ["tropic_cliff_a", "tropic_cliff_a", "tropic_cliff_a", "tropic_cliff_a_plants"]; const tPlants = "tropic_plants"; const tRoad = "tropic_citytile_a"; const tRoadWild = "tropic_citytile_plants"; const tShoreBlend = "tropic_beach_dry_plants"; const tShore = "tropic_beach_dry"; const tWater = "tropic_beach_wet"; const oTree = "gaia/flora_tree_toona"; const oPalm = "gaia/flora_tree_palm_tropic"; const oStoneLarge = "gaia/geology_stonemine_tropic_quarry"; const oStoneSmall = "gaia/geology_stone_tropic_a"; const oMetalLarge = "gaia/geology_metal_tropic_slabs"; const oFish = "gaia/fauna_fish"; const oDeer = "gaia/fauna_deer"; const oSheep = "gaia/fauna_tiger"; const oBush = "gaia/flora_bush_berry"; const aRockLarge = "actor|geology/stone_granite_large.xml"; const aRockMedium = "actor|geology/stone_granite_med.xml"; const aBush1 = "actor|props/flora/plant_tropic_a.xml"; const aBush2 = "actor|props/flora/plant_lg.xml"; const aBush3 = "actor|props/flora/plant_tropic_large.xml"; const pForestD = [tForestFloor + TERRAIN_SEPARATOR + oTree, tForestFloor]; const pForestP = [tForestFloor + TERRAIN_SEPARATOR + oPalm, tForestFloor]; InitMap(); const numPlayers = getNumPlayers(); const 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 clMountains = createTileClass(); var playerIDs = sortAllPlayers(); for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles(0.45 + 0.2 * (i % 2)); var fz = fractionToTiles((i + 1) / (numPlayers + 1)); var ix = round(fx); var iz = round(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); var group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); var hillSize = PI * radius * radius; // create starting trees var num = floor(hillSize / 60); var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(12, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oTree, num, num, 0,3)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); } RMS.SetProgress(15); const waterPos = 0.31; const mountainPos = 0.69; paintRiver({ - "horizontal": false, "parallel": false, - "position": 0, + "startX": 0, + "startZ": 0, + "endX": 0, + "endZ": 1, "width": 2 * waterPos, "fadeDist": 0.025, "deviation": 0, "waterHeight": -5, "landHeight": 3, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { addToClass(ix, iz, clWater); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { - if (ix > mountainPos * mapSize) + if (ix > fractionToTiles(mountainPos)) addToClass(ix, iz, clMountains) } }); log("Creating shores..."); for (let i = 0; i < scaleByMapSize(20, 120); ++i) createArea( new ChainPlacer( 1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 30)), 1, randIntExclusive(0.28 * mapSize, 0.34 * mapSize), randIntExclusive(0.1 * mapSize, 0.9 * mapSize)), [ new LayeredPainter([tGrass, tGrass], [2]), new SmoothElevationPainter(ELEVATION_SET, 3, 3), unPaintClass(clWater) ], null); paintTerrainBasedOnHeight(-6, 1, 1, tWater); paintTerrainBasedOnHeight(1, 2.8, 1, tShoreBlend); paintTerrainBasedOnHeight(0, 1, 1, tShore); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); RMS.SetProgress(45); log("Creating hills..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.1), [ new LayeredPainter([tCliff, tGrass], [3]), new SmoothElevationPainter(ELEVATION_SET, 25, 3), paintClass(clHill) ], [avoidClasses(clPlayer, 20, clHill, 5, clWater, 2, clBaseResource, 2), stayClasses(clMountains, 0)], scaleByMapSize(5, 40) * numPlayers); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(1000, 6000, 0.7); var types = [ [[tGrass, tGrass, tGrass, tGrass, pForestD], [tGrass, tGrass, tGrass, pForestD]], [[tGrass, tGrass, tGrass, tGrass, pForestP], [tGrass, tGrass, tGrass, pForestP]] ]; var size = forestTrees / (scaleByMapSize(3,6) * numPlayers); var num = 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, 20, clForest, 10, clHill, 0, clWater, 8), num); RMS.SetProgress(70); log("Creating grass 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([tGrassC, tGrassA, tGrassB], [2, 1]), paintClass(clDirt) ], avoidClasses(clWater, 8, clForest, 0, clHill, 0, clPlayer, 12, clDirt, 16), scaleByMapSize(20, 80)); 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([tPlants, tPlants], [1]), paintClass(clDirt) ], avoidClasses(clWater, 8, clForest, 0, clHill, 0, clPlayer, 12, clDirt, 16), scaleByMapSize(20, 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, 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 ); 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), 3*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), 3*scaleByMapSize(8, 131), 50 ); log("Creating small grass tufts..."); group = new SimpleGroup( [new SimpleObject(aBush1, 1,2, 0,1, -PI/8,PI/8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), 8 * scaleByMapSize(13, 200) ); RMS.SetProgress(90); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aBush2, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aBush1, 3,6, 1.2,2.5, -PI/8,PI/8)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), 8 * scaleByMapSize(13, 200) ); RMS.SetProgress(95); log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBush3, 1,2, 0,2), new SimpleObject(aBush2, 2,4, 0,2)] ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1), 8 * scaleByMapSize(13, 200), 50 ); RMS.SetProgress(95); createStragglerTrees( [oTree, oPalm], avoidClasses(clWater, 5, clForest, 1, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); RMS.SetProgress(75); log("Creating berry bush..."); group = new SimpleGroup( [new SimpleObject(oBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 6, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50 ); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oSheep, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 22, clHill, 1, clFood, 20), 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 ); setSunColor(0.6, 0.6, 0.6); setSunElevation(PI/ 3); setWaterColor(0.524,0.734,0.839); setWaterTint(0.369,0.765,0.745); setWaterWaviness(1.0); setWaterType("ocean"); setWaterMurkiness(0.35); setFogFactor(0.4); setFogThickness(0.2); setPPEffect("hdr"); setPPContrast(0.7); setPPSaturation(0.65); setPPBloom(0.6); setSkySet("cirrus"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/northern_lights.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/northern_lights.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/northern_lights.js (revision 20429) @@ -1,328 +1,330 @@ RMS.LoadLibrary("rmgen"); const tSnowA = ["polar_snow_b"]; const tSnowB = "polar_ice_snow"; const tSnowC = "polar_ice"; const tSnowD = "polar_snow_a"; const tForestFloor = "polar_tundra_snow"; const tCliff = "polar_snow_rocks"; const tSnowE = ["polar_snow_glacial"]; const tRoad = "new_alpine_citytile"; const tRoadWild = "new_alpine_citytile"; const tShoreBlend = "alpine_shore_rocks_icy"; const tShore = "alpine_shore_rocks"; const tWater = "alpine_shore_rocks"; const oPine = "gaia/flora_tree_pine_w"; const oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; const oStoneSmall = "gaia/geology_stone_alpine_a"; const oMetalLarge = "gaia/geology_metal_alpine_slabs"; const oFish = "gaia/fauna_fish"; const oWalrus = "gaia/fauna_walrus"; const oWolf = "gaia/fauna_wolf_snow"; const aIceberg = "actor|props/special/eyecandy/iceberg.xml"; const pForestD = [tForestFloor + TERRAIN_SEPARATOR + oPine, tForestFloor, tForestFloor]; const pForestS = [tForestFloor + TERRAIN_SEPARATOR + oPine, tForestFloor, tForestFloor, tForestFloor]; InitMap(); const numPlayers = getNumPlayers(); const 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 playerIDs = sortAllPlayers(); for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles((i + 1) / (numPlayers + 1)); var fz = fractionToTiles(0.35 + 0.2 * (i % 2)); var ix = round(fx); var iz = round(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); // create metal mine var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); var group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); var hillSize = PI * radius * radius; // create starting trees var num = floor(hillSize / 60); var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(12, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oPine, num, num, 0,3)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); } RMS.SetProgress(15); paintRiver({ - "horizontal": true, "parallel": true, - "position": 1, + "startX": 0, + "startZ": 1, + "endX": 1, + "endZ": 1, "width": 0.62, - "fadeDist": 8 / mapSize, + "fadeDist": tilesToFraction(8), "deviation": 0, "waterHeight": -5, "landHeight": 3, "meanderShort": 0, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { addToClass(ix, iz, clWater); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { if (getHeight(ix, iz) < 0.5) addToClass(ix, iz, clWater); } }); log("Creating shores..."); for (var i = 0; i < scaleByMapSize(20,120); i++) createArea( new ChainPlacer( 1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 30)), 1, randIntExclusive(0.1 * mapSize, 0.9 * mapSize), randIntExclusive(0.67 * mapSize, 0.74 * mapSize)), [ new LayeredPainter([tSnowA, tSnowA], [2]), new SmoothElevationPainter(ELEVATION_SET, 3, 3), unPaintClass(clWater) ], null); log("Creating islands..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.1), [ new LayeredPainter([tSnowA, tSnowA], [3]), new SmoothElevationPainter(ELEVATION_SET, 3, 3), unPaintClass(clWater) ], stayClasses(clWater, 7), scaleByMapSize(10, 80) ); paintTerrainBasedOnHeight(-6, 1, 1, tWater); log("Creating lakes..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(5, 7)), Math.floor(scaleByMapSize(20, 50)), 0.1), [ new LayeredPainter([tShoreBlend, tShore, tWater], [1, 1]), new SmoothElevationPainter(ELEVATION_SET, -4, 3), paintClass(clWater) ], avoidClasses(clPlayer, 20, clWater, 20), Math.round(scaleByMapSize(1, 4) * numPlayers)); paintTerrainBasedOnHeight(1, 2.8, 1, tShoreBlend); paintTileClassBasedOnHeight(-6, 0.5, 1, clWater); RMS.SetProgress(45); log("Creating hills..."); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.1), [ new LayeredPainter([tCliff, tSnowA], [3]), new SmoothElevationPainter(ELEVATION_SET, 25, 3), paintClass(clHill) ], avoidClasses(clPlayer, 20, clHill, 15, clWater, 2, clBaseResource, 2), scaleByMapSize(1, 4) * numPlayers ); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(100, 625, 0.7); var types = [ [[tSnowA, tSnowA, tSnowA, tSnowA, pForestD], [tSnowA, tSnowA, tSnowA, pForestD]], [[tSnowA, tSnowA, tSnowA, tSnowA, pForestS], [tSnowA, tSnowA, tSnowA, pForestS]] ]; var size = forestTrees / (scaleByMapSize(3,6) * numPlayers); var num = 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))), 1), [ new LayeredPainter(type, [2]), paintClass(clForest) ], avoidClasses(clPlayer, 20, clForest, 20, clHill, 0, clWater, 8), num); log("Creating iceberg..."); createObjectGroupsDeprecated( new SimpleGroup([new SimpleObject(aIceberg, 0, 2, 0, 4)], true, clRock), 0, [avoidClasses(clRock, 6), stayClasses(clWater, 4)], scaleByMapSize(4, 16), 100); RMS.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([tSnowD, tSnowB, tSnowC], [2, 1]), paintClass(clDirt) ], avoidClasses( clWater, 8, clForest, 0, clHill, 0, clPlayer, 20, clDirt, 16), scaleByMapSize(20, 80)); 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([tSnowE, tSnowE], [1]), paintClass(clDirt) ], avoidClasses( clWater, 8, clForest, 0, clHill, 0, clPlayer, 20, clDirt, 16), scaleByMapSize(20, 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, 3, clForest, 1, clPlayer, 20, clRock, 10, clHill, 1), scaleByMapSize(8,32), 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(8,32), 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(8,32), 100 ); RMS.SetProgress(95); createStragglerTrees( [oPine], avoidClasses(clWater, 5, clForest, 1, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oWalrus, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 20), 3 * numPlayers, 50 ); RMS.SetProgress(75); log("Creating sheep..."); group = new SimpleGroup( [new SimpleObject(oWolf, 2,3, 0,2)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 20), 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 ); setSunColor(0.6, 0.6, 0.6); setSunElevation(PI/ 6); setWaterColor(0.02, 0.17, 0.52); setWaterTint(0.494, 0.682, 0.808); setWaterMurkiness(0.82); setWaterWaviness(0.5); setWaterType("ocean"); setFogFactor(0.95); setFogThickness(0.09); setPPSaturation(0.28); setPPEffect("hdr"); setSkySet("fog"); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/phoenician_levant.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/phoenician_levant.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/phoenician_levant.js (revision 20429) @@ -1,376 +1,378 @@ RMS.LoadLibrary("rmgen"); //TILE_CENTERED_HEIGHT_MAP = true; const tCity = "medit_city_pavement"; const tCityPlaza = "medit_city_pavement"; const tHill = ["medit_dirt", "medit_dirt_b", "medit_dirt_c", "medit_rocks_grass", "medit_rocks_grass"]; const tMainDirt = "medit_dirt"; const tCliff = "medit_cliff_aegean"; const tForestFloor = "medit_rocks_shrubs"; const tGrass = "medit_rocks_grass"; const tRocksShrubs = "medit_rocks_shrubs"; const tRocksGrass = "medit_rocks_grass"; const tDirt = "medit_dirt_b"; const tDirtB = "medit_dirt_c"; const tShore = "medit_sand"; const tWater = "medit_sand_wet"; const oGrapeBush = "gaia/flora_bush_grapes"; 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"; // terrain + entity (for painting) const pForest = [tForestFloor + TERRAIN_SEPARATOR + oDatePalm, tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, tForestFloor + TERRAIN_SEPARATOR + oCarob, tForestFloor, tForestFloor]; InitMap(); const numPlayers = getNumPlayers(); 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(); var clIsland = createTileClass(); var playerIDs = sortAllPlayers(); for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles(0.66 + 0.2 * (i % 2)); var fz = fractionToTiles((i + 1) / (numPlayers + 1)); var ix = floor(fx); var iz = floor(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tCityPlaza, tCity], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oGrapeBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oCarob, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aBush1, clBaseResource, radius); } RMS.SetProgress(30); paintRiver({ - "horizontal": false, "parallel": true, - "position": 0, + "startX": 0, + "startZ": 0, + "endX": 0, + "endZ": 1, "width": 1, "fadeDist": 0.05, "deviation": 0, "waterHeight": -3, "landHeight": 1, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { if (height < 0) addToClass(ix, iz, clWater); if (height < -1.5) placeTerrain(ix, iz, tWater); else placeTerrain(ix, iz, tShore); } }); RMS.SetProgress(40); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2), avoidClasses(clWater, 2, clPlayer, 20), scaleByMapSize(100, 200)); 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, 15, 2), paintClass(clHill) ], avoidClasses(clPlayer, 20, clForest, 1, clHill, 15, clWater, 0), scaleByMapSize(1, 4) * numPlayers * 3); log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(500, 2500, 0.5); var num = scaleByMapSize(10,42); createAreas( new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), forestTrees / (num * Math.floor(scaleByMapSize(2, 5))), 0.5), [ new TerrainPainter([tForestFloor, pForest]), paintClass(clForest) ], avoidClasses(clPlayer, 20, clForest, 10, clWater, 1, clHill, 1, clBaseResource, 3), num, 50); RMS.SetProgress(50); log("Creating grass 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, tRocksShrubs], [tRocksShrubs, tRocksGrass], [tRocksGrass, tGrass]], [1, 1]), paintClass(clDirt) ], avoidClasses(clForest, 0, clGrass, 5, clPlayer, 10, clWater, 4, clDirt, 5, clHill, 1), scaleByMapSize(15, 45)); RMS.SetProgress(55); 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( [[tDirt, tDirtB], [tDirt, tMainDirt], [tDirtB, tMainDirt]], [1, 1]), paintClass(clDirt) ], avoidClasses(clForest, 0, clDirt, 5, clPlayer, 10, clWater, 4, clGrass, 5, clHill, 1), scaleByMapSize(15, 45)); RMS.SetProgress(60); log("Creating cyprus..."); createAreas( new ClumpPlacer(4.5 * scaleByMapSize(60, 540), 0.2, 0.1, 0.01), [ new LayeredPainter([tShore, tHill], [12]), new SmoothElevationPainter(ELEVATION_SET, 6, 8), paintClass(clIsland) ], [stayClasses (clWater, 5)], 1); log("Creating cyprus 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, stayClasses(clIsland, 9), 14 * scaleByMapSize(4,16), 100 ); log("Creating cyprus small stone mines..."); group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroupsDeprecated(group, 0, stayClasses(clIsland, 9), 14 * scaleByMapSize(4,16), 100 ); log("Creating cyprus metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroupsDeprecated(group, 0, stayClasses(clIsland, 9), 14 * scaleByMapSize(4,16), 100 ); 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(clForest, 1, clPlayer, 20, clRock, 10, clWater, 3, 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(clForest, 1, clPlayer, 20, clRock, 10, clWater, 3, 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, clWater, 3, clHill, 1), scaleByMapSize(4,16), 100 ); RMS.SetProgress(65); 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, clHill, 1), 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, 3, clPlayer, 0, clHill, 1), scaleByMapSize(40, 360), 50 ); RMS.SetProgress(70); log("Creating fish..."); group = new SimpleGroup([new SimpleObject(oFish, 1,3, 2,6)], true, clFood); createObjectGroupsDeprecated(group, 0, [avoidClasses(clIsland, 2, clFood, 10), stayClasses(clWater, 5)], 3*scaleByMapSize(5,20), 50 ); log("Creating sheeps..."); group = new SimpleGroup([new SimpleObject(oSheep, 5,7, 0,4)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 7, clWater, 3, clFood, 10, clHill, 1), scaleByMapSize(5,20), 50 ); log("Creating goats..."); group = new SimpleGroup([new SimpleObject(oGoat, 2,4, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 7, clWater, 3, clFood, 10, clHill, 1), scaleByMapSize(5,20), 50 ); log("Creating deers..."); group = new SimpleGroup([new SimpleObject(oDeer, 2,4, 0,2)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 7, clWater, 3, clFood, 10, clHill, 1), scaleByMapSize(5,20), 50 ); log("Creating grape bushes..."); group = new SimpleGroup( [new SimpleObject(oGrapeBush, 5,7, 0,4)], true, clFood ); createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 15, clHill, 1, clFood, 7), randIntInclusive(1, 4) * numPlayers + 2, 50 ); RMS.SetProgress(90); var stragglerTrees = [ [1, avoidClasses(clForest, 0, clWater, 1, clPlayer, 8, clMetal, 6, clHill, 1)], [3, stayClasses(clIsland, 9)] ]; for (let [amount, constraint] of stragglerTrees) createStragglerTrees( [oDatePalm, oSDatePalm, oCarob, oFanPalm, oPoplar, oCypress], constraint, clForest, amount * stragglerTrees); setSkySet("sunny"); setSunColor(0.917, 0.828, 0.734); setWaterColor(0.263,0.314,0.631); setWaterTint(0.133, 0.725,0.855); setWaterWaviness(2.0); setWaterType("ocean"); setWaterMurkiness(0.8); setTerrainAmbientColor(0.57, 0.58, 0.55); setUnitsAmbientColor(0.447059, 0.509804, 0.54902); setSunElevation(0.671884); setSunRotation(-0.582913); setFogFactor(0.2); setFogThickness(0.15); setFogColor(0.8, 0.7, 0.6); setPPEffect("hdr"); setPPContrast(0.53); setPPSaturation(0.47); setPPBloom(0.52); ExportMap(); Index: ps/trunk/binaries/data/mods/public/maps/random/rivers.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rivers.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/rivers.js (revision 20429) @@ -1,436 +1,352 @@ RMS.LoadLibrary("rmgen"); RMS.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]; InitMap(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapArea = getMapArea(); 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 waterHeight = -3; +var shallowHeight = -1; + initTerrain(tMainTerrain); +log("Creating central lake..."); +var centralLake = [0.5, 0.5]; createArea( new ClumpPlacer( mapArea / 100 * Math.pow(scaleByMapSize(1, 6), 1/8), 0.7, 0.1, 10, - Math.round(fractionToTiles(0.5)), - Math.round(fractionToTiles(0.5))), + fractionToTiles(centralLake[0]), + fractionToTiles(centralLake[1])), [ new LayeredPainter([tShore, tWater, tWater, tWater], [1, 4, 2]), - new SmoothElevationPainter(ELEVATION_SET, -3, 4), + new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], null); var [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement(); for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles - fx = fractionToTiles(playerX[i]); - fz = fractionToTiles(playerZ[i]); - ix = round(fx); - iz = round(fz); + var fx = fractionToTiles(playerX[i]); + var fz = fractionToTiles(playerZ[i]); + var ix = round(fx); + var iz = round(fz); addCivicCenterAreaToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oFruitBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) mAngle = randFloat(0, TWO_PI); var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oTree1, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius); } - RMS.SetProgress(20); -//init rivers -var PX = []; -var PZ = []; - -//isRiver actually tells us if two points must be joined by river -var isRiver = []; -for (let m = 0; m < numPlayers + 1; ++m) +log("Creating rivers between opponents..."); +var [riverX, riverZ] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, 0.5, ...centralLake); +for (let i = 0; i < numPlayers; ++i) { - isRiver[m] = []; - for (let n = 0; n < numPlayers + 1; ++n) - isRiver[m][n] = 0; -} + if (areAllies(playerIDs[i] - 1, playerIDs[(i + 1) % numPlayers] - 1)) + continue; -//creating the first point in the center. all others are -//connected to this one so all of our rivers join together -//in the middle of the map -var fx = fractionToTiles(0.5); -var fz = fractionToTiles(0.5); -var ix = round(fx); -var iz = round(fz); -PX[numPlayers]= fx; -PZ[numPlayers]= fz; + let shallowLocation = randFloat(0.2, 0.7); + let shallowWidth = randFloat(0.12, 0.21); -var riverAngle = []; -for (var c = 0 ; c < numPlayers ; c++) -{ - //creating other points of the river and making them - // join the point in the center of the map - riverAngle[c] = startAngle + (((2 * c + 1) / (numPlayers * 2)) * TWO_PI ); - PX[c] = round(fractionToTiles(0.5 + 0.5 * cos(riverAngle[c]))); - PZ[c] = round(fractionToTiles(0.5 + 0.5 * sin(riverAngle[c]))); - //log (playerIDs[c], ",,," ,playerIDs[0]); - //isRiver[c][numPlayers]=1; - if ((c == numPlayers-1)&&(!areAllies(playerIDs[c]-1, playerIDs[0]-1))) - isRiver[c][numPlayers]=1; - else if ((c < numPlayers-1)&&(!areAllies(playerIDs[c]-1, playerIDs[c+1]-1))) - isRiver[c][numPlayers]=1; + paintRiver({ + "parallel": true, + "startX": riverX[i], + "startZ": riverZ[i], + "endX": centralLake[0], + "endZ": centralLake[1], + "width": tilesToFraction(scaleByMapSize(10, 30)), + "fadeDist": tilesToFraction(5), + "deviation": 0, + "landHeight": getMapBaseHeight(), + "waterHeight": waterHeight, + "minHeight": waterHeight, + "meanderShort": tilesToFraction(scaleByMapSize(20, 60) * scaleByMapSize(35, 160)), + "meanderLong": 0, + "waterFunc": (ix, iz, height, riverFraction) => { + + addToClass(ix, iz, clWater); + + let isShallow = height < shallowHeight && + riverFraction > shallowLocation && + riverFraction < shallowLocation + shallowWidth; + + let newHeight = isShallow ? shallowHeight : Math.max(height, waterHeight); + + if (getHeight(ix, iz) < newHeight) + return; + + setHeight(ix, iz, newHeight); + placeTerrain(ix, iz, height >= 0 ? tShore : tWater); + + if (isShallow) + addToClass(ix, iz, clShallow); + } + }); } - -//theta is the start value for rndRiver function. seed implies -//the randomness. we must have one of these for each river we create. -//shallowpoint and shallow length define the place and size of the shallow part -var theta = []; -var seed = []; -var shallowpoint = []; -var shallowlength = []; -for (let q = 0; q < numPlayers + 1; ++q) -{ - theta[q]=randFloat(0, 1); - seed[q]=randFloat(2,3); - shallowpoint[q]=randFloat(0.2,0.7); - shallowlength[q]=randFloat(0.12,0.21); -} - -log ("Creating rivers..."); -//checking all the tiles -for (var ix = 0; ix < mapSize; ix++) - for (var iz = 0; iz < mapSize; iz++) - for (var m = 0; m < numPlayers+1; m++) - for (var n = 0; n < numPlayers+1; n++) - { - //checking if there is a river between those points - if(isRiver[m][n] == 1) - { - //very important calculations. don't change anything. results - //"dis" which is the distance to the riverline and "y" and "xm" which are - //the coordinations for the point it's image is in. - var a = PZ[m]-PZ[n]; - var b = PX[n]-PX[m]; - var c = (PZ[m]*(PX[m]-PX[n]))-(PX[m]*(PZ[m]-PZ[n])); - var dis = abs(a*ix + b*iz + c)/sqrt(a*a + b*b); - if (abs(a*ix + b*iz + c) != 0) - var alamat = (a*ix + b*iz + c)/abs(a*ix + b*iz + c); - else - var alamat = 1; - - var k = (a*ix + b*iz + c)/(a*a + b*b); - var y = iz-(b*k); - var xm = ix-(a*k); - //this calculates which "part" of the river are we in now. - //used for the function rndRiver. - var sit = sqrt((PZ[n]-y)*(PZ[n]-y)+(PX[n]-xm)*(PX[n]-xm))/sqrt((PZ[n]-PZ[m])*(PZ[n]-PZ[m])+(PX[n]-PX[m])*(PX[n]-PX[m])); - var sbms = scaleByMapSize(5,15) + alamat * ( scaleByMapSize(20, 60) * rndRiver( theta[m] + sit * 0.5 * (mapSize/64) , seed[m]) ); - if((dis < sbms)&&(y <= Math.max(PZ[m],PZ[n]))&&(y >= Math.min(PZ[m],PZ[n]))) - { - //create the deep part of the river - if (dis <= sbms-5){ - if ((sit > shallowpoint[m])&&(sit < shallowpoint[m]+shallowlength[m])) - { - //create the shallow part - var h = -1; - addToClass(ix, iz, clShallow); - } - else - var h = -3; - - var t = tWater; - addToClass(ix, iz, clWater); - } - //creating the rough edges - else if (dis <= sbms) - { - if ((sit > shallowpoint[m])&&(sit < shallowpoint[m]+shallowlength[m])) - { - if (2-(sbms-dis)<-1) - { - //checking if there is shallow water here - var h = -1; - addToClass(ix, iz, clShallow); - } - else - var h = 2-(sbms-dis); - } - else - var h = 2-(sbms-dis); - - //we must create shore lines for more beautiful terrain - if (sbms-dis<=2) - var t = tShore; - else - var t = tWater; - addToClass(ix, iz, clWater); - } - //we don't want to cause problems when river joins sea - if (getHeight(ix, iz)>h) - { - placeTerrain(ix, iz, t); - setHeight(ix, iz, h); - } - } - } - } - RMS.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); RMS.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); RMS.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 ); RMS.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, -PI/8,PI/8)], [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)], [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) ); // create water decoration in the shallow parts createDecoration ( [[new SimpleObject(aReeds, 1,3, 0,1)], [new SimpleObject(aLillies, 1,2, 0,1)] ], [ scaleByMapSize(800, 12800), scaleByMapSize(800, 12800) ], stayClasses(clShallow, 0) ); RMS.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) ); createFood ( [ [new SimpleObject(oFruitBush, 5,7, 0,4)] ], [ 3 * numPlayers ], avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clHill, 1, clFood, 10) ); createFood ( [ [new SimpleObject(oFish, 2,3, 0,2)] ], [ 25 * numPlayers ], [avoidClasses(clFood, 20), stayClasses(clWater, 6)] ); RMS.SetProgress(85); createStragglerTrees( [oTree1, oTree2, oTree4, oTree3], avoidClasses(clWater, 5, clForest, 7, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6), clForest, stragglerTrees); 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 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/gaia_terrain.js (revision 20429) @@ -1,665 +1,700 @@ /** * @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, constraint, x, z, terrain, tileClass, fcc = 0, q = []) { if (constraint instanceof Array) constraint = new AndConstraint(constraint); 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) { let s = edges.splice(state, 1); gotRet[ix][iz] = -2; let edgesLength = edges.length; 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 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 distance = Math.euclidDistance2D(ix, iz, cx, cz); 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) setHeight(ix, iz, newHeight); else if (getHeight(ix, iz) >= newHeight && getHeight(ix, iz) < newHeight + 4) setHeight(ix, iz, newHeight + 4); if (terrain !== undefined) placeTerrain(ix, iz, terrain); if (tileClass !== undefined) addToClass(ix, iz, tileClass); } } } /** * Generates a volcano mountain. Smoke and lava are optional. * * @param {number} fx - Horizontal coordinate of the center. * @param {number} fz - Horizontal coordinate of the center. * @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(fx, fz, tileClass, terrainTexture, lavaTextures, smoke, elevationType) { log("Creating volcano"); let ix = Math.round(fractionToTiles(fx)); let iz = Math.round(fractionToTiles(fz)); let baseSize = getMapArea() / scaleByMapSize(1, 8); let coherence = 0.7; let smoothness = 0.05; let failFraction = 100; let steepness = 3; let clLava = createTileClass(); let layers = [ { "clumps": 0.067, "elevation": 15, "tileClass": tileClass }, { "clumps": 0.05, "elevation": 25, "tileClass": createTileClass() }, { "clumps": 0.02, "elevation": 45, "tileClass": createTileClass() }, { "clumps": 0.011, "elevation": 62, "tileClass": createTileClass() }, { "clumps": 0.003, "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(baseSize * layers[i].clumps, coherence, smoothness, failFraction, ix, iz), [ layers[i].painter || new LayeredPainter([terrainTexture, terrainTexture], [3]), new SmoothElevationPainter(elevationType, layers[i].elevation, layers[i].steepness || steepness), paintClass(layers[i].tileClass) ], i == 0 ? null : stayClasses(layers[i - 1].tileClass, 1)); if (smoke) { let num = Math.floor(baseSize * 0.002); createObjectGroup( new SimpleGroup( [new SimpleObject("actor|particle/smoke.xml", num, num, 0, 7)], false, clLava, ix, iz), 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. + * Horizontal locations and widths (including fadeDist, meandering) are fractions of the mapsize. * * @property horizontal - Whether the river is horizontal or vertical * @property parallel - Whether the shorelines should be parallel or meander separately. - * @property position - Location of the river. Number between 0 and 1. - * @property width - Size between the two shorelines. Number between 0 and 1. + * @property position - Location of the river. + * @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 waterHeight - Ground height of the riverbed. * @proeprty landHeight - Ground height of the end of the shoreline. * @property meanderShort - Strength of frequent meanders. * @property meanderLong - Strength of less frequent meanders. - * @property waterFunc - Optional function called on water tiles, providing ix, iz, height. - * @property landFunc - Optional function called on land tiles, providing ix, iz, shoreDist1, shoreDist2. + * @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 the river"); - let theta1 = randFloat(0, 1); - let theta2 = randFloat(0, 1); + // 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 meanderShort = args.meanderShort / scaleByMapSize(35, 160); - let meanderLong = args.meanderLong / scaleByMapSize(35, 100); + 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 width and length of the shoreline. + let halfWidth = fractionToTiles(args.width / 2); + let fadeDist = fractionToTiles(args.fadeDist); + + // Describe river location in vectors. let mapSize = getMapSize(); + let vecStart = new Vector2D(args.startX, args.startZ).mult(mapSize); + let vecEnd = new Vector2D(args.endX, args.endZ).mult(mapSize); + let vecRiver = Vector2D.sub(vecStart, vecEnd); + let riverLength = vecRiver.length(); + + // Describe river boundaries. + let riverMinX = Math.min(vecStart.x, vecEnd.x); + let riverMinZ = Math.min(vecStart.y, vecEnd.y); + let riverMaxX = Math.max(vecStart.x, vecEnd.x); + let riverMaxZ = Math.max(vecStart.y, vecEnd.y); for (let ix = 0; ix < mapSize; ++ix) for (let iz = 0; iz < mapSize; ++iz) { if (args.constraint && !args.constraint.allows(ix, iz)) continue; - let x = ix / (mapSize + 1); - let z = iz / (mapSize + 1); + let vecPoint = new Vector2D(ix, iz); + + // Compute the shortest distance to the river. + let distanceToRiver = vecRiver.cross(Vector2D.sub(vecPoint, vecEnd)) / riverLength; - let coord1 = args.horizontal ? z : x; - let coord2 = args.horizontal ? x : z; + // Closest point on the river (i.e the foot of the perpendicular). + let river = Vector2D.sub(vecPoint, vecRiver.perpendicular().mult(distanceToRiver / riverLength)); + + // Only process points that actually are perpendicular with the river. + if (river.x < riverMinX || river.x > riverMaxX || + river.y < riverMinZ || river.y > riverMaxZ) + continue; - // River curve at this place - let curve1 = - meanderShort * rndRiver(theta1 + coord2 * mapSize / 128, seed1) + - meanderLong * rndRiver(theta2 + coord2 * mapSize / 256, seed2); - - let curve2 = args.parallel ? curve1 : - meanderShort * rndRiver(theta2 + coord2 * mapSize / 128, seed2) + - meanderLong * rndRiver(theta2 + coord2 * mapSize / 256, seed2); - - // Fuzz the river border - let devCoord1 = coord1 * randFloat(1 - args.deviation, 1 + args.deviation); - let devCoord2 = coord2 * randFloat(1 - args.deviation, 1 + args.deviation); + // Coordinate between 0 and 1 on the axis parallel to the river. + let riverFraction = river.distanceTo(vecStart) / riverLength; - let shoreDist1 = -devCoord1 + curve1 + args.position - args.width / 2; - let shoreDist2 = -devCoord1 + curve2 + args.position + args.width / 2; + // 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 = fractionToTiles(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 - halfWidth; + let shoreDist2 = sign * riverCurve2 + Math.abs(distanceToRiver) - deviation + halfWidth; + // Create the elevation for the water and the slopy shoreline and call the user functions. if (shoreDist1 < 0 && shoreDist2 > 0) { let height = args.waterHeight; - if (shoreDist1 > -args.fadeDist) - height += (args.landHeight - args.waterHeight) * (1 + shoreDist1 / args.fadeDist); - else if (shoreDist2 < args.fadeDist) - height += (args.landHeight - args.waterHeight) * (1 - shoreDist2 / args.fadeDist); + if (shoreDist1 > -fadeDist) + height += (args.landHeight - args.waterHeight) * (1 + shoreDist1 / fadeDist); + else if (shoreDist2 < fadeDist) + height += (args.landHeight - args.waterHeight) * (1 - shoreDist2 / fadeDist); - setHeight(ix, iz, height); + if (args.minHeight === undefined || height < args.minHeight) + setHeight(ix, iz, height); if (args.waterFunc) - args.waterFunc(ix, iz, height); + args.waterFunc(ix, iz, height, riverFraction); } else if (args.landFunc) args.landFunc(ix, iz, 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(horizontal, riverCount, riverWidth, waterHeight, 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 riverConstraint = avoidClasses(tributaryRiverTileClass, 3); if (shallowTileClass) riverConstraint = new AndConstraint([riverConstraint, avoidClasses(shallowTileClass, 2)]); for (let i = 0; i < riverCount; ++i) { // Determining tributary start point let location = randFloat(tapering, 1 - tapering); let sign = randBool() ? 1 : -1; let angle = sign * randFloat(maxAngle, 2 * Math.PI - maxAngle); let distance = sign * tapering; let searchStart = [fractionToTiles(location), fractionToTiles(0.5 + distance)]; let searchEnd = [fractionToTiles(location), fractionToTiles(0.5 - distance)]; if (!horizontal) { searchStart.reverse(); searchEnd.reverse(); } let start = getTIPIADBON(searchStart, searchEnd, heightRange, 0.5, 4); if (!start) continue; let endX = fractionToTiles(0.5 + 0.5 * Math.cos(angle)); let endZ = fractionToTiles(0.5 + 0.5 * Math.sin(angle)); // Create river if (!createArea( new PathPlacer( Math.floor(start[0]), Math.floor(start[1]), Math.floor(endX), Math.floor(endZ), riverWidth, waviness, smoothness, offset, tapering), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 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, endX, endZ), new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3), constraint); } // Create shallows if (shallowTileClass) for (let z of [0.25, 0.75]) { let m1 = [Math.round(fractionToTiles(0.2)), Math.round(fractionToTiles(z))]; let m2 = [Math.round(fractionToTiles(0.8)), Math.round(fractionToTiles(z))]; if (!horizontal) { m1.reverse(); m2.reverse(); } createShallowsPassage(...m1, ...m2, scaleByMapSize(4, 8), -2, -2, 2, shallowTileClass, undefined, waterHeight); } } /** * Create shallow water between (x1, z1) and (x2, z2) of tiles below maxHeight. */ function createShallowsPassage(x1, z1, x2, z2, width, maxHeight, shallowHeight, smooth, tileClass, terrain, riverHeight) { let a = z1 - z2; let b = x2 - x1; let distance = Math.euclidDistance2D(x1, z1, x2, z2); let mapSize = getMapSize(); for (let ix = 0; ix < mapSize; ++ix) for (let iz = 0; iz < mapSize; ++iz) { let c = a * (ix - x1) + b * (iz - z1); let my = iz - b * c / Math.square(distance); let inline = 0; let dis; if (b == 0) { dis = Math.abs(ix - x1); if (iz >= Math.min(z1, z2) && iz <= Math.max(z1, z2)) inline = 1; } else if (my >= Math.min(z1, z2) && my <= Math.max(z1, z2)) { dis = Math.abs(c) / distance; inline = 1; } if (dis > width || !inline || getHeight(ix, iz) > maxHeight) continue; if (dis > width - smooth) setHeight(ix, iz, ((width - dis) * shallowHeight + riverHeight * (smooth - width + dis)) / smooth); else if (dis <= width - smooth) setHeight(ix, iz, shallowHeight); if (tileClass !== undefined) addToClass(ix, iz, tileClass); if (terrain !== undefined) placeTerrain(ix, iz, terrain); } } /** * Creates a ramp from (x1, y1) to (x2, y2). */ function createRamp(x1, y1, x2, y2, minHeight, maxHeight, width, smoothLevel, mainTerrain, edgeTerrain, tileClass) { let halfWidth = width / 2; let x3; let y3; if (y1 == y2) { x3 = x2; y3 = y2 + halfWidth; } else { x3 = x2 + halfWidth; y3 = (x1 - x2) / (y1 - y2) * (x2 - x3) + y2; } let minBoundX = Math.max(Math.min(x1, x2) - halfWidth, 0); let minBoundY = Math.max(Math.min(y1, y2) - halfWidth, 0); let maxBoundX = Math.min(Math.max(x1, x2) + halfWidth, getMapSize()); let maxBoundY = Math.min(Math.max(y1, y2) + halfWidth, getMapSize()); for (let x = minBoundX; x < maxBoundX; ++x) for (let y = minBoundY; y < maxBoundY; ++y) { let lDist = distanceOfPointFromLine(x3, y3, x2, y2, x, y); let sDist = distanceOfPointFromLine(x1, y1, x2, y2, x, y); let rampLength = Math.euclidDistance2D(x1, y1, x2, y2); if (lDist > rampLength || sDist > halfWidth) continue; let height = ((rampLength - lDist) * maxHeight + lDist * minHeight) / rampLength; if (sDist >= halfWidth - smoothLevel) { height = (height - minHeight) * (halfWidth - sDist) / smoothLevel + minHeight; if (edgeTerrain) placeTerrain(x, y, edgeTerrain); } else if (mainTerrain) placeTerrain(x, y, mainTerrain); if (tileClass !== undefined) addToClass(x, y, tileClass); if (getHeight(Math.floor(x), Math.floor(y)) < height && height <= maxHeight) setHeight(x, y, height); } } /** * Get The Intended Point In A Direction Based On Height. * Retrieves the N'th point with a specific height in a line and returns it as a [x, y] array. * * @param startPoint - [x, y] array defining the start point * @param endPoint - [x, y] array defining the ending point * @param heightRange - [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max" * @param step - how much tile units per turn should the search go. more value means faster but less accurate * @param n - how many points to skip before ending the search. skips """n-1 points""". */ function getTIPIADBON(startPoint, endPoint, heightRange, step, n) { let X = endPoint[0] - startPoint[0]; let Y = endPoint[1] - startPoint[1]; if (!X && !Y) { error("getTIPIADBON startPoint and endPoint are identical! " + new Error().stack); return undefined; } let M = Math.sqrt(Math.square(X) + step * Math.square(Y)); let stepX = step * X / M; let stepY = step * Y / M; let y = startPoint[1]; let checked = 0; let mapSize = getMapSize(); for (let x = startPoint[0]; true; x += stepX) { let ix = Math.floor(x); let iy = Math.floor(y); if (ix < mapSize || iy < mapSize) { if (getHeight(ix, iy) <= heightRange[1] && getHeight(ix, iy) >= heightRange[0]) ++checked; if (checked >= n) return [x, y]; } y += stepY; if (y > endPoint[1] && stepY > 0 || y < endPoint[1] && stepY < 0 || x > endPoint[1] && stepX > 0 || x < endPoint[1] && stepX < 0) return undefined; } return undefined; } Index: ps/trunk/binaries/data/mods/public/maps/random/the_nile.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/the_nile.js (revision 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/the_nile.js (revision 20429) @@ -1,454 +1,455 @@ RMS.LoadLibrary("rmgen"); var tCity = "desert_city_tile"; var tCityPlaza = "desert_city_tile_plaza"; var tFineSand = "desert_sand_smooth"; var tForestFloor = "desert_forestfloor_palms"; var tGrass = "desert_dirt_rough_2"; var tGrassSand50 = "desert_sand_dunes_50"; var tGrassSand25 = "desert_dirt_rough"; var tDirt = "desert_dirt_rough"; var tDirtCracks = "desert_dirt_cracks"; var tShore = "desert_sand_wet"; var tLush = "desert_grass_a"; var tSLush = "desert_grass_a_sand"; var tSDry = "desert_plants_b"; var oBerryBush = "gaia/flora_bush_berry"; var oCamel = "gaia/fauna_camel"; var oGazelle = "gaia/fauna_gazelle"; var oGoat = "gaia/fauna_goat"; var oStoneLarge = "gaia/geology_stonemine_desert_badlands_quarry"; var oStoneSmall = "gaia/geology_stone_desert_small"; var oMetalLarge = "gaia/geology_metal_desert_slabs"; var oDatePalm = "gaia/flora_tree_date_palm"; var oSDatePalm = "gaia/flora_tree_cretan_date_palm_short"; var eObelisk = "other/obelisk"; var ePyramid = "other/pyramid_minor"; var oWood = "gaia/special_treasure_wood"; var oFood = "gaia/special_treasure_food_bin"; var aBush1 = "actor|props/flora/bush_desert_a.xml"; var aBush2 = "actor|props/flora/bush_desert_dry_a.xml"; var aBush3 = "actor|props/flora/bush_medit_sm_dry.xml"; var aBush4 = "actor|props/flora/plant_desert_a.xml"; var aDecorativeRock = "actor|geology/stone_desert_med.xml"; var aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; var aLillies = "actor|props/flora/water_lillies.xml"; var pForest = [tForestFloor + TERRAIN_SEPARATOR + oDatePalm, tForestFloor + TERRAIN_SEPARATOR + oSDatePalm, tForestFloor]; InitMap(); var mapSize = getMapSize(); var aPlants = mapSize < 256 ? "actor|props/flora/grass_tropical.xml" : "actor|props/flora/grass_tropic_field_tall.xml"; var numPlayers = getNumPlayers(); 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 clDesert = createTileClass(); var clPond = createTileClass(); var clShore = createTileClass(); var clTreasure = createTileClass(); var playerIDs = primeSortAllPlayers(); var playerPos = placePlayersRiver(); var playerX = []; var playerZ = []; for (var i = 0; i < numPlayers; i++) { playerZ[i] = playerPos[i]; playerX[i] = 0.30 + 0.4*(i%2); } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(15,25); var cliffRadius = 2; var elevation = 20; // get the x and z in tiles var fx = fractionToTiles(playerX[i]); var fz = fractionToTiles(playerZ[i]); var ix = floor(fx); var iz = floor(fz); addToClass(ix, iz, clPlayer); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tCityPlaza, tCity], [1]); createArea(placer, painter, null); placeCivDefaultEntities(fx, fz, id); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); // create metal mine var mAngle = bbAngle; while(abs(mAngle - bbAngle) < PI/3) mAngle = randFloat(0, TWO_PI); var mDist = radius - 4; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create starting trees var num = 2; var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oDatePalm, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aBush1, clBaseResource, radius); } RMS.SetProgress(30); const riverTextures = [ { "left": 0, "right": 0.04, "tileClass": tLush }, { "left": 0.04, "right": 0.06, "tileClass": tSLush }, { "left": 0.06, "right": 0.09, "tileClass": tSDry } ]; const plantFrequency = 2; - var plantID = 0; paintRiver({ - "horizontal": false, "parallel": true, - "position": 0.5, + "startX": 0.5, + "startZ": 0, + "endX": 0.5, + "endZ": 1, "width": 0.1, "fadeDist": 0.025, - "deviation": 0.005, + "deviation": 0.0025, "waterHeight": -3, "landHeight": 2, "meanderShort": 12, "meanderLong": 50, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { addToClass(ix, iz, clWater); placeTerrain(ix, iz, tShore); // Place river bushes if (height <= -0.2 || height >= 0.1) return; if (plantID % plantFrequency == 0) { plantID = 0; placeObject(ix, iz, aPlants, 0, randFloat(0, TWO_PI)); } ++plantID; }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { let x = ix / (mapSize + 1.0); if (x < 0.25 || x > 0.75) addToClass(ix, iz, clDesert); for (let riv of riverTextures) if (-shoreDist1 > -riv.right && -shoreDist1 < -riv.left || -shoreDist2 > riv.left && -shoreDist2 < riv.right) { placeTerrain(ix, iz, riv.tileClass); addToClass(ix, iz, clShore); } } }); RMS.SetProgress(40); log("Creating bumps..."); createAreas( new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1), new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2), avoidClasses(clWater, 2, clPlayer, 6), scaleByMapSize(100, 200)); log("Creating ponds..."); var numLakes = Math.round(scaleByMapSize(1, 4) * numPlayers / 2); var waterAreas = createAreas( new ClumpPlacer(scaleByMapSize(2, 5) * 50, 0.8, 0.1, 10), [ new LayeredPainter([tShore, tShore, tShore], [1, 1]), new SmoothElevationPainter(ELEVATION_SET, -7, 4), paintClass(clPond) ], avoidClasses(clPlayer, 25, clWater, 20, clPond, 10), numLakes ); log("Creating reeds..."); createObjectGroupsByAreasDeprecated( new SimpleGroup([new SimpleObject(aReeds, 1, 3, 0, 1)], true), 0, stayClasses(clPond, 1), numLakes, 100, waterAreas); log("Creating lillies..."); createObjectGroupsByAreasDeprecated( new SimpleGroup([new SimpleObject(aLillies, 1, 3, 0, 1)], true), 0, stayClasses(clPond, 1), numLakes, 100, waterAreas); waterAreas = []; log("Creating forests..."); var [forestTrees, stragglerTrees] = getTreeCounts(700, 3500, 0.5); var num = scaleByMapSize(10,30); createAreas( new ClumpPlacer(forestTrees / num, 0.15, 0.1, 0.5), [ new TerrainPainter([pForest, tForestFloor]), paintClass(clForest) ], avoidClasses(clPlayer, 19, clForest, 4, clWater, 1, clDesert, 5, clPond, 2, clBaseResource, 3), num, 50); RMS.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( [[tGrass, tGrassSand50], [tGrassSand50, tGrassSand25], [tGrassSand25, tGrass]], [1, 1]), paintClass(clDirt) ], avoidClasses(clForest, 0, clGrass, 5, clPlayer, 10, clWater, 1, clDirt, 5, clShore, 1, clPond, 1), scaleByMapSize(15, 45)); RMS.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( [[tDirt, tDirtCracks], [tDirt, tFineSand], [tDirtCracks, tFineSand]], [1, 1]), paintClass(clDirt) ], avoidClasses(clForest, 0, clDirt, 5, clPlayer, 10, clWater, 1, clGrass, 5, clShore, 1, clPond, 1), scaleByMapSize(15, 45)); RMS.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, 20, clRock, 10, clWater, 1, clPond, 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(clForest, 1, clPlayer, 20, clRock, 10, clWater, 1, clPond, 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, clWater, 1, clPond, 1), scaleByMapSize(4,16), 100 ); 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(clForest, 1, clPlayer, 20, clRock, 10, clWater, 1, clPond, 1), stayClasses(clDesert, 3)], scaleByMapSize(6,20), 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, 20, clRock, 10, clWater, 1, clPond, 1), stayClasses(clDesert, 3)], scaleByMapSize(6,20), 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, clWater, 1, clPond, 1), stayClasses(clDesert, 3)], scaleByMapSize(6,20), 100 ); RMS.SetProgress(65); 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, clPond, 1), 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, clPond, 1), scaleByMapSize(20, 180), 50 ); RMS.SetProgress(70); log("Creating gazelles..."); group = new SimpleGroup([new SimpleObject(oGazelle, 5,7, 0,4)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clWater, 1, clFood, 10, clDesert, 5, clPond, 1), 3*scaleByMapSize(5,20), 50 ); log("Creating goats..."); group = new SimpleGroup([new SimpleObject(oGoat, 2,4, 0,3)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clWater, 1, clFood, 10, clDesert, 5, clPond, 1), 3*scaleByMapSize(5,20), 50 ); log("Creating treasures..."); group = new SimpleGroup([new SimpleObject(oFood, 1,1, 0,2)], true, clTreasure); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clWater, 1, clFood, 2, clDesert, 5, clTreasure, 6, clPond, 1), 3*scaleByMapSize(5,20), 50 ); group = new SimpleGroup([new SimpleObject(oWood, 1,1, 0,2)], true, clTreasure); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clWater, 1, clFood, 2, clDesert, 5, clTreasure, 6, clPond, 1), 3*scaleByMapSize(5,20), 50 ); log("Creating camels..."); group = new SimpleGroup([new SimpleObject(oCamel, 2,4, 0,2)], true, clFood); createObjectGroupsDeprecated(group, 0, avoidClasses(clForest, 0, clPlayer, 20, clWater, 1, clFood, 10, clDesert, 5, clTreasure, 2, clPond, 1), 3*scaleByMapSize(5,20), 50 ); RMS.SetProgress(90); createStragglerTrees( [oDatePalm, oSDatePalm], avoidClasses(clForest, 0, clWater, 1, clPlayer, 20, clMetal, 6, clDesert, 1, clTreasure, 2, clPond, 1), clForest, stragglerTrees / 2); createStragglerTrees( [oDatePalm, oSDatePalm], avoidClasses(clForest, 0, clWater, 1, clPlayer, 20, clMetal, 6, clTreasure, 2), clForest, stragglerTrees / 10); createStragglerTrees( [oDatePalm, oSDatePalm], borderClasses(clPond, 1, 4), clForest, stragglerTrees); log("Creating obelisks"); group = new SimpleGroup( [new SimpleObject(eObelisk, 1,1, 0,1)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clWater, 4, clForest, 3, clPlayer, 20, clMetal, 6, clRock, 2, clPond, 4, clTreasure, 2), stayClasses(clDesert, 3)], scaleByMapSize(5, 30), 50 ); log("Creating pyramids"); group = new SimpleGroup( [new SimpleObject(ePyramid, 1,1, 0,1)], true ); createObjectGroupsDeprecated( group, 0, [avoidClasses(clWater, 7, clForest, 6, clPlayer, 20, clMetal, 5, clRock, 5, clPond, 7, clTreasure, 2), stayClasses(clDesert, 3)], scaleByMapSize(2, 6), 50 ); setSkySet("sunny"); setSunColor(0.711, 0.746, 0.574); setWaterColor(0.541,0.506,0.416); setWaterTint(0.694,0.592,0.522); setWaterMurkiness(1); setWaterWaviness(3.0); setWaterType("lake"); 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 20428) +++ ps/trunk/binaries/data/mods/public/maps/random/the_unknown/unknown_common.js (revision 20429) @@ -1,1153 +1,1166 @@ /** * @file This library is used to generate different map variations on the map Unknown, Unknown Land and Unknown Nomad. */ /** * True if city centers should be placed or false for nomad. */ var g_PlayerBases; /** * 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]; InitMap(); const numPlayers = getNumPlayers(); const mapSize = getMapSize(); const mapArea = getMapArea(); const lSize = Math.pow(scaleByMapSize(1, 6), 1/8); 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 landHeight = 3; var cliffHeight = 3.12; var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, landHeight, 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 playerX = []; var playerZ = []; var g_StartingTreasures = false; var g_IberianWalls = "walls"; function createUnknownMap() { let funcs = unknownMapFunctions.land; if (g_AllowNaval) funcs = funcs.concat(unknownMapFunctions.naval); global["unknown" + pickRandom(funcs)](); paintUnknownMapBasedOnHeight(); if (g_PlayerBases) createUnknownPlayerBases(); } /** * Chain of islands or many disconnected islands. */ function unknownArchipelago() { g_IberianWalls = "towers"; g_StartingTreasures = true; let [pIDs, islandX, islandZ] = radialPlayerPlacement(); if (g_PlayerBases) { [playerIDs, playerX, playerZ] = [pIDs, islandX, islandZ]; 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, fractionToTiles(islandX[i]), fractionToTiles(islandZ[i])), landElevationPainter, null); let type = 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), [ 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, landHeight, 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)), [ 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 SmoothElevationPainter(ELEVATION_SET, landHeight, 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), [ 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 (g_PlayerBases) { log("Ensuring player area..."); [playerIDs, playerX, playerZ] = radialPlayerPlacement(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, Math.round(fractionToTiles(playerX[i])), Math.round(fractionToTiles(playerZ[i])), 0, [Math.floor(scaleByMapSize(23, 50))]), [ landElevationPainter, paintClass(clLand) ], null); } log("Creating continent..."); createArea( new ClumpPlacer(mapArea * 0.45, 0.9, 0.09, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))), [ landElevationPainter, paintClass(clLand) ], null); if (randBool(1/3)) { log("Creating peninsula (i.e. half the map not being surrounded by water)..."); let angle = randFloat(0, 2 * Math.PI); createArea( new ClumpPlacer( mapArea * 0.45, 0.9, 0.09, 10, Math.round(fractionToTiles(0.5 + 0.25 * Math.cos(angle))), Math.round(fractionToTiles(0.5 + 0.25 * Math.sin(angle)))), [ landElevationPainter, paintClass(clLand) ], null); log("Remembering to not paint shorelines into the peninsula..."); createArea( new ClumpPlacer( mapArea * 0.3, 0.9, 0.01, 10, Math.round(fractionToTiles(0.5 + 0.35 * Math.cos(angle))), Math.round(fractionToTiles(0.5 + 0.35 * Math.sin(angle)))), paintClass(clPeninsulaSteam), null); } 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 horizontal = randBool(); + let [start, end] = centralRiverCoordinates(horizontal); paintRiver({ - "horizontal": horizontal, "parallel": false, - "position": 0.5, + "startX": tilesToFraction(start[0]), + "startZ": tilesToFraction(start[1]), + "endX": tilesToFraction(end[0]), + "endZ": tilesToFraction(end[1]), "width": randFloat(0.22, 0.3) + scaleByMapSize(0.05, 0.2), "fadeDist": 0.025, "deviation": 0, "waterHeight": waterHeight, "landHeight": landHeight, "meanderShort": 20, "meanderLong": 0, - "waterFunc": (ix, iz, height) => { + "waterFunc": (ix, iz, height, riverFraction) => { if (height < 0) addToClass(ix, iz, clWater); }, "landFunc": (ix, iz, shoreDist1, shoreDist2) => { setHeight(ix, iz, 3.1); addToClass(ix, iz, clLand); } }); if (g_PlayerBases) { playerIDs = primeSortAllPlayers(); let playerPos = placePlayersRiver(); for (let i = 0; i < numPlayers; ++i) { playerX[i] = playerPos[i]; playerZ[i] = 0.2 + 0.6 * (i % 2); } if (!horizontal) [playerX, playerZ] = [playerZ, playerX]; markPlayerArea("small"); } if (!g_AllowNaval || randBool()) { log("Creating isthmus (i.e. connecting the two riversides with a big land passage)..."); let [coord1, coord2] = centralRiverCoordinates(!horizontal); createArea( new PathPlacer( ...coord1, ...coord2, scaleByMapSize(randIntInclusive(16, 24), randIntInclusive(100, 140)), 0.5, 3 * scaleByMapSize(1, 4), 0.1, 0.01), [ landElevationPainter, paintClass(clLand), unPaintClass(clWater) ], null); } createExtensionsOrIslands(); // Don't createShoreJaggedness since it doesn't fit artistically here } /** * Creates a very small central river. */ function unknownCentralRiver() { let waterHeight = -4; initHeight(landHeight); let horizontal = randBool(); if (g_PlayerBases) { playerIDs = primeSortAllPlayers(); let playerPos = placePlayersRiver(); for (let i = 0; i < numPlayers; ++i) { playerX[i] = playerPos[i]; playerZ[i] = 0.25 + 0.5*(i%2); } if (!horizontal) [playerX, playerZ] = [playerZ, playerX]; markPlayerArea("large"); } log("Creating the main river..."); let [coord1, coord2] = centralRiverCoordinates(horizontal); 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), 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 cLocation = randFloat(0.15, 0.85); let x1 = [fractionToTiles(cLocation), fractionToTiles(0.35)]; let x2 = [fractionToTiles(cLocation), fractionToTiles(0.65)]; if (!horizontal) { x1.reverse(); x2.reverse(); } createShallowsPassage(...x1, ...x2, scaleByMapSize(4, 8), -2, -2, 2, clShallow, undefined, waterHeight); } } if (randBool(2/3)) createTributaryRivers( horizontal, 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; initHeight(landHeight); let startAngle; if (g_PlayerBases) { let playerAngle; [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement(); markPlayerArea("small"); } let mid = Math.round(fractionToTiles(0.5)); let lake = randBool(3/4); if (lake) { log("Creating lake..."); createArea( new ClumpPlacer(mapArea * 0.09 * lSize, 0.7, 0.1, 10, mid, mid), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], null); createShoreJaggedness(waterHeight, clWater, 3); } // Don't do this on nomad because the imbalances on the different islands are too drastic if (g_PlayerBases && (!lake || randBool(1/3))) { log("Creating small rivers separating players..."); let [riverX, riverZ, riverAngle] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, 0.5, 0.5, 0.5); for (let i = 0; i < numPlayers; ++i) { createArea( new PathPlacer(mid, mid, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i]), 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, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i])), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0), paintClass(clWater) ], avoidClasses(clPlayer, 5)); } log("Creating lake..."); createArea( new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], null); } if (lake && randBool()) { log("Creating small central island..."); createArea( new ClumpPlacer(mapArea * 0.006 * lSize, 0.7, 0.1, 10, mid, mid), [ landElevationPainter, paintClass(clWater) ], null); } } /** * Align players on a land strip with seas bordering on one or both sides that can hold islands. */ function unknownEdgeSeas() { let waterHeight = -4; initHeight(landHeight); let horizontal = randBool(); if (g_PlayerBases) { for (let i = 0; i < numPlayers; i++) { let playerPos1 = (i + 1) / (numPlayers + 1); let playerPos2 = 0.4 + 0.2 * (i % 2); playerX[i] = horizontal ? playerPos1 : playerPos2; playerZ[i] = horizontal ? playerPos2 : playerPos1; } // Don't place the shoreline inside the CC, but possibly into the players territory markPlayerArea("small"); } for (let location of pickRandom([["first"], ["second"], ["first", "second"]])) + { + let margin = randFloat(0, scaleByMapSize(0, 0.1)); + let positionX = location == "first" ? [0, margin] : [1 - margin, 1]; + let positionZ = [0, 1]; + + if (!horizontal) + [positionX, positionZ] = [positionZ, positionX]; + paintRiver({ - "horizontal": horizontal, "parallel": false, - "position": (location == "first" ? 0 : 1) + (location == "first" ? -1 : 1) * randFloat(0, scaleByMapSize(0, 0.1)), + "startX": positionX[0], + "startZ": positionZ[0], + "endX": positionX[1], + "endX": positionZ[1], "width": 0.62, "fadeDist": 0.015, "deviation": 0, "waterHeight": waterHeight, "landHeight": landHeight, "meanderShort": 20, "meanderLong": 0 }); + } createExtensionsOrIslands(); paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand); createShoreJaggedness(waterHeight, clLand, 7, false); } /** * Land shaped like a concrescent moon around a central lake. */ function unknownGulf() { let waterHeight = -3; initHeight(landHeight); let startAngle = randFloat(0, 2) * Math.PI; if (g_PlayerBases) { log("Determining player locations..."); for (let i = 0; i < numPlayers; ++i) { let playerAngle = startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))); playerX[i] = 0.5 + 0.35 * Math.cos(playerAngle); playerZ[i] = 0.5 + 0.35 * Math.sin(playerAngle); } markPlayerArea("large"); } let placers = [ new ClumpPlacer(mapArea * 0.08, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))), new ClumpPlacer(mapArea * 0.13 * lSize, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5 - 0.2 * Math.cos(startAngle))), Math.round(fractionToTiles(0.5 - 0.2 * Math.sin(startAngle)))), new ClumpPlacer(mapArea * 0.15 * lSize, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5 - 0.49 * Math.cos(startAngle))), Math.round(fractionToTiles(0.5 - 0.49 * Math.sin(startAngle)))), ]; for (let placer of placers) createArea( placer, [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4), paintClass(clWater) ], avoidClasses(clPlayerTerritory, scaleByMapSize(15, 25))); } /** * Mainland style with some small random lakes. */ function unknownLakes() { let waterHeight = -5; initHeight(landHeight); if (g_PlayerBases) { [playerIDs, playerX, playerZ] = radialPlayerPlacement(); 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 mountainHeight = 24; let waterHeight = -4; initHeight(landHeight); let playerAngle; let startAngle; if (g_PlayerBases) { [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement(); markPlayerArea("small"); } else startAngle = Math.random(0, 2 * Math.PI); let mid = Math.round(fractionToTiles(0.5)); let [mountainX, mountainZ] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mid, mid); let [passesX, passesZ] = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mid, mid); for (let i = 0; i < numPlayers; ++i) { log("Creating a mountain range between neighboring players..."); createArea( new PathPlacer(mid, mid, mountainX[i], mountainZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), [ new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 3), 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, mountainX[i], mountainZ[i]), new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 0), avoidClasses(clPlayer, 5)); log("Create passages between neighboring players..."); createArea( new PathPlacer( passesX[2 * i], passesZ[2 * i], passesX[2 * ((i + 1) % numPlayers)], passesZ[2 * ((i + 1) % numPlayers)], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), new SmoothElevationPainter(ELEVATION_SET, landHeight, 2), null); } if (randBool(2/5)) { log("Creating small mountain at the center to ensure the mountain ranges to be connected..."); createArea( new ClumpPlacer(mapArea * 0.03 * lSize, 0.7, 0.1, 10, mid, mid), [ new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3), paintClass(clWater) ], null); } else { log("Fill area between the paths..."); createArea( new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid), [ new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 4), paintClass(clWater) ], null); } } /** * Land enclosed by a hill that leaves small areas for civic centers and large central place. */ function unknownLowlands() { let mountainHeight = 30; log("Creating mountain that is going to separate players..."); initHeight(mountainHeight); let playerAngle; let startAngle; if (g_PlayerBases) { [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement(); markPlayerArea("small"); } else startAngle = Math.random(0, 2 * Math.PI); 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; let mid = Math.round(fractionToTiles(0.5)); let [valleyX, valleyZ] = distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mid, mid); for (let i = 0; i < valleys; ++i) createArea( new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valleyX[i], valleyZ[i]), [ new SmoothElevationPainter(ELEVATION_SET, landHeight, 2), paintClass(clLand) ], null); log("Creating the big central area..."); createArea( new ClumpPlacer(mapArea * 0.091 * lSize, 0.7, 0.1, 10, mid, mid), [ landElevationPainter, paintClass(clWater) ], null); log("Creating passes from player areas to the center..."); for (let i = 0; i < valleys; ++i) createArea( new PathPlacer(mid, mid, valleyX[i], valleyZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05), [ landElevationPainter, paintClass(clWater) ], null); } /** * No water, no hills. */ function unknownMainland() { initHeight(3); if (g_PlayerBases) { [playerIDs, playerX, playerZ] = radialPlayerPlacement(); markPlayerArea("small"); } } function centralRiverCoordinates(horizontal) { let coord1 = [0, fractionToTiles(0.5)]; let coord2 = [fractionToTiles(1), fractionToTiles(0.5)]; if (!horizontal) { coord1.reverse(); coord2.reverse(); } return [coord1, coord2]; } 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 ? landHeight : 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( Math.round(fractionToTiles(playerX[i])), Math.round(fractionToTiles(playerZ[i])), clPlayer); if (size == "large") createArea( new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])), paintClass(clPlayerTerritory), null); } } function paintUnknownMapBasedOnHeight() { paintTerrainBasedOnHeight(cliffHeight, 40, 1, tCliff); paintTerrainBasedOnHeight(3, cliffHeight, 1, tMainTerrain); paintTerrainBasedOnHeight(1, 3, 1, tShore); paintTerrainBasedOnHeight(-8, 1, 2, tWater); unPaintTileClassBasedOnHeight(0, cliffHeight, 1, clWater); unPaintTileClassBasedOnHeight(-6, 0, 1, clLand); paintTileClassBasedOnHeight(-6, 0, 1, clWater); paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand); paintTileClassBasedOnHeight(cliffHeight, 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, 2, 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, 18, 2), paintClass(clHill) ], [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)], randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9)) ); RMS.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, 0), stayClasses(clLand, 4)], num); RMS.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, 0, 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, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)], patchCount); RMS.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, 1), 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, 1), 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, 1), stayClasses(clLand, 3)], randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)), 100); RMS.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, 0), 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, 0), stayClasses(clLand, 3)], scaleByMapSize(8, 131), 50); RMS.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, 1, 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, 1, clFood, 20), stayClasses(clLand, 2)], randIntInclusive(1, 4) * numPlayers + 2, 50); RMS.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, 1, 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, 0, clFood, 20), randIntInclusive(15, 40) * numPlayers, 60); RMS.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, 1, 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)); RMS.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)); RMS.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, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)], planetm * scaleByMapSize(13, 200), 50); setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"])); setSunRotation(randFloat(0, 2 * Math.PI)); setSunElevation(Math.PI * randFloat(1/5, 1/3)); } function createUnknownPlayerBases() { for (var i = 0; i < numPlayers; ++i) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); var radius = scaleByMapSize(17,29); var shoreRadius = 4; var elevation = 3; var hillSize = PI * radius * radius; // get the x and z in tiles var fx = fractionToTiles(playerX[i]); var fz = fractionToTiles(playerZ[i]); var ix = round(fx); var iz = round(fz); placeCivDefaultEntities(fx, fz, id, { "iberWall": g_IberianWalls }); placeDefaultChicken(fx, fz, clBaseResource); // create berry bushes var bbAngle = randFloat(0, TWO_PI); var bbDist = 12; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); var group = new SimpleGroup( [new SimpleObject(oFruitBush, 5,5, 0,3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); if (g_StartingTreasures) { // create woods var bbAngle = randFloat(0, TWO_PI); var bbDist = 13; var bbX = round(fx + bbDist * cos(bbAngle)); var bbZ = round(fz + bbDist * sin(bbAngle)); group = new SimpleGroup( [new SimpleObject(oWoodTreasure, 14, 14, 0, 3)], true, clBaseResource, bbX, bbZ ); createObjectGroup(group, 0); } // create metal mine var mAngle = bbAngle; while (abs(mAngle - bbAngle) < PI/3) { mAngle = randFloat(0, TWO_PI); } var mDist = 12; var mX = round(fx + mDist * cos(mAngle)); var mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oMetalLarge, 1,1, 0,0)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); // create stone mines mAngle += randFloat(PI/8, PI/4); mX = round(fx + mDist * cos(mAngle)); mZ = round(fz + mDist * sin(mAngle)); group = new SimpleGroup( [new SimpleObject(oStoneLarge, 1,1, 0,2)], true, clBaseResource, mX, mZ ); createObjectGroup(group, 0); var hillSize = PI * radius * radius; // create starting trees var num = floor(hillSize / 100); var tAngle = randFloat(-PI/3, 4*PI/3); var tDist = randFloat(11, 13); var tX = round(fx + tDist * cos(tAngle)); var tZ = round(fz + tDist * sin(tAngle)); group = new SimpleGroup( [new SimpleObject(oTree1, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius); // create the city patch var cityRadius = radius/3; var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz); var painter = new LayeredPainter([tRoadWild, tRoad], [1]); createArea(placer, [painter], null); } }