RMS.LoadLibrary("rmgen"); // Some functions // This is the basic SmoothElevationPainter with a random component thrown in. function SemiRandomElevationPainter(elevation, blendRadius,roughness) { this.elevation = elevation; this.blendRadius = blendRadius; if (!roughness) this.roughness = 5; else this.roughness = roughness; } SemiRandomElevationPainter.prototype.checkInArea = function(areaID, x, z) { // Check given tile and its neighbors return ( (g_Map.validT(x, z) && g_Map.area[x][z] == areaID) || (g_Map.validT(x-1, z) && g_Map.area[x-1][z] == areaID) || (g_Map.validT(x, z-1) && g_Map.area[x][z-1] == areaID) || (g_Map.validT(x-1, z-1) && g_Map.area[x-1][z-1] == areaID) ); }; SemiRandomElevationPainter.prototype.paint = function(area) { var pointQ = []; var pts = area.points; var heightPts = []; var mapSize = getMapSize()+1; var saw = new Array(mapSize); var dist = new Array(mapSize); var gotHeightPt = new Array(mapSize); var newHeight = new Array(mapSize); // init typed arrays for (var i = 0; i < mapSize; ++i) { saw[i] = new Uint8Array(mapSize); // bool / uint8 dist[i] = new Uint16Array(mapSize); // uint16 gotHeightPt[i] = new Uint8Array(mapSize); // bool / uint8 newHeight[i] = new Float32Array(mapSize); // float32 } var length = pts.length; var areaID = area.getID(); // get a list of all points for (var i=0; i < length; i++) { var x = pts[i].x; var z = pts[i].z; for (var dx=-1; dx <= 2; dx++) { var nx = x+dx; for (var dz=-1; dz <= 2; dz++) { var nz = z+dz; if (g_Map.validH(nx, nz) && !gotHeightPt[nx][nz]) { gotHeightPt[nx][nz] = 1; heightPts.push(new PointXZ(nx, nz)); newHeight[nx][nz] = g_Map.height[nx][nz]; } } } } // push edge points for (var i=0; i < length; i++) { var x = pts[i].x; var z = pts[i].z; for (var dx=-1; dx <= 2; dx++) { var nx = x+dx; for (var dz=-1; dz <= 2; dz++) { var nz = z+dz; if (g_Map.validH(nx, nz) && !this.checkInArea(areaID, nx, nz) && !saw[nx][nz]) { saw[nx][nz]= 1; dist[nx][nz] = 0; pointQ.push(new PointXZ(nx, nz)); } } } } // do BFS inwards to find distances to edge while(pointQ.length) { var pt = pointQ.shift(); var px = pt.x; var pz = pt.z; var d = dist[px][pz]; // paint if in area if (g_Map.validH(px, pz) && this.checkInArea(areaID, px, pz)) { if (d <= this.blendRadius) { var a = (d-1) / this.blendRadius; newHeight[px][pz] += a*this.elevation + randFloat(-this.roughness,this.roughness); } else { // also happens when blendRadius == 0 newHeight[px][pz] += this.elevation + randFloat(-this.roughness,this.roughness); } } // enqueue neighbours for (var dx=-1; dx <= 1; dx++) { var nx = px+dx; for (var dz=-1; dz <= 1; dz++) { var nz = pz+dz; if (g_Map.validH(nx, nz) && this.checkInArea(areaID, nx, nz) && !saw[nx][nz]) { saw[nx][nz] = 1; dist[nx][nz] = d+1; pointQ.push(new PointXZ(nx, nz)); } } } } length = heightPts.length; // smooth everything out for (var i = 0; i < length; ++i) { var pt = heightPts[i]; var px = pt.x; var pz = pt.z; if (this.checkInArea(areaID, px, pz)) { var sum = 8 * newHeight[px][pz]; var count = 8; for (var dx=-1; dx <= 1; dx++) { var nx = px+dx; for (var dz=-1; dz <= 1; dz++) { var nz = pz+dz; if (g_Map.validH(nx, nz)) { sum += newHeight[nx][nz]; count++; } } } g_Map.height[px][pz] = sum/count; } } }; TILE_CENTERED_HEIGHT_MAP = true; var tGrassSpecific = ["new_alpine_grass_d","new_alpine_grass_d", "new_alpine_grass_e"]; var tGrass = ["new_alpine_grass_d", "new_alpine_grass_b", "new_alpine_grass_e"]; var tGrassMidRange = ["new_alpine_grass_b", "alpine_grass_a"]; var tGrassHighRange = ["new_alpine_grass_a", "alpine_grass_a", "alpine_grass_rocky"]; var tHighRocks = ["alpine_cliff_b", "alpine_cliff_c","alpine_cliff_c", "alpine_grass_rocky"]; var tSnowedRocks = ["alpine_cliff_b", "alpine_cliff_snow"]; var tTopSnow = ["alpine_snow_rocky","alpine_snow_a"]; var tTopSnowOnly = ["alpine_snow_a"]; var tDirtyGrass = ["new_alpine_grass_d","alpine_grass_d","alpine_grass_c", "alpine_grass_b"]; var tLushGrass = ["new_alpine_grass_a","new_alpine_grass_d"]; var tMidRangeCliffs = ["alpine_cliff_b","alpine_cliff_c"]; var tHighRangeCliffs = ["alpine_mountainside","alpine_cliff_snow" ]; var tPass = ["alpine_cliff_b", "alpine_cliff_c", "alpine_grass_rocky", "alpine_grass_rocky", "alpine_grass_rocky"]; var tSand = ["beach_c", "beach_d"]; var tWetSand = ["sand_wet_a", "sand_wet_b"]; var tSandTransition = ["beach_scrub_50_"]; var tWater = ["sand_wet_a","sand_wet_b","sand_wet_b","sand_wet_b"]; var tGrassLandForest = "alpine_forrestfloor"; var tGrassLandForest2 = "alpine_grass_d"; var tForestTransition = ["new_alpine_grass_d", "new_alpine_grass_b","alpine_grass_d"]; var tGrassDForest = "alpine_forrestfloor_snow"; var tCliff = ["alpine_cliff_a", "alpine_cliff_b"]; var tGrassA = "alpine_grass_snow_50"; var tGrassB = ["alpine_grass_snow_50", "alpine_dirt_snow"]; var tGrassC = ["alpine_snow_rocky"]; var tDirt = ["alpine_dirt_snow", "alpine_snow_a"]; var tRoad = "new_alpine_citytile"; var tRoadWild = "new_alpine_citytile"; var tShore = "alpine_shore_rocks_icy"; // gaia entities var oBeech = "gaia/flora_tree_euro_beech"; var oPine = "gaia/flora_tree_aleppo_pine"; var oBerryBush = "gaia/flora_bush_berry"; var oChicken = "gaia/fauna_chicken"; var oDeer = "gaia/fauna_deer"; var oGoat = "gaia/fauna_goat"; var oFish = "gaia/fauna_fish"; var oRabbit = "gaia/fauna_rabbit"; var oStoneLarge = "gaia/geology_stonemine_alpine_quarry"; var oStoneSmall = "gaia/geology_stone_alpine_a"; var oMetalLarge = "gaia/geology_metal_alpine_slabs"; // decorative props var aRain = "actor|particle/rain_shower.xml"; var aGrass = "actor|props/flora/grass_soft_small_tall.xml"; var aGrassShort = "actor|props/flora/grass_soft_large.xml"; var aRockLarge = "actor|geology/stone_granite_med.xml"; var aRockMedium = "actor|geology/stone_granite_med.xml"; var aReeds = "actor|props/flora/reeds_pond_lush_a.xml"; var aLillies = "actor|props/flora/water_lillies.xml"; var aBushMedium = "actor|props/flora/bush_medit_me.xml"; var aBushSmall = "actor|props/flora/bush_medit_sm.xml"; var pForestLand = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech, tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tGrassLandForest,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; var pForestLandLight = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech, tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tGrassLandForest,tGrassLandForest2,tForestTransition,tGrassLandForest2, tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition, tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; var pForestLandVeryLight = [ tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech, tForestTransition,tGrassLandForest2,tForestTransition,tForestTransition,tForestTransition, tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition, tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2]; const BUILDING_ANGlE = -PI/4; // initialize map log("Initializing map..."); InitMap(); var numPlayers = getNumPlayers(); var mapSize = getMapSize(); var mapArea = mapSize*mapSize; // create tile classes var clDirt = createTileClass(); var clLush = createTileClass(); var clRock = createTileClass(); var clMetal = createTileClass(); var clFood = createTileClass(); var clBaseResource = createTileClass(); var clSettlement = createTileClass(); var clPass = createTileClass(); var clPyrenneans = createTileClass(); var clPass = createTileClass(); var clPlayer = createTileClass(); var clHill = createTileClass(); var clForest = createTileClass(); var clWater = createTileClass(); // Initial Terrain Creation // I'll use very basic noised sinusoidal functions to give the terrain a way aspect // It looks like we can't go higher than ≈ 75. Given this I'll lower the ground var baseHeight = -6; setWaterHeight(8); // let's choose the angle of the pyreneans var MoutainAngle = randFloat(0,TWO_PI); var lololo = randFloat(-PI/12,-PI/12); // used by oceans var baseHeights = []; for (var ix = 0; ix < mapSize; ix++) { baseHeights.push([]); for (var iz = 0; iz < mapSize; iz++) { if (g_Map.validT(ix,iz) && !checkIfInClass(ix,iz,clWater)) { placeTerrain(ix, iz, tGrass); setHeight(ix,iz,baseHeight +randFloat(-1,1) + scaleByMapSize(1,3)*(cos(ix/scaleByMapSize(5,30))+sin(iz/scaleByMapSize(5,30)))); baseHeights[ix].push( baseHeight +randFloat(-1,1) + scaleByMapSize(1,3)*(cos(ix/scaleByMapSize(5,30))+sin(iz/scaleByMapSize(5,30))) ); } else baseHeights[ix].push(-100); } } // randomize player order var playerIDs = []; for (var i = 0; i < numPlayers; i++) { playerIDs.push(i+1); } playerIDs = primeSortPlayers(sortPlayers(playerIDs)); // place players // TODO: sort players by team var playerX = new Array(numPlayers); var playerZ = new Array(numPlayers); var playerAngle = new Array(numPlayers); for (var i = 0; i < numPlayers; i++) { if ( i%2 == 1) playerAngle[i] = MoutainAngle+lololo + PI/2 + i/numPlayers*(PI/3) + (1-i/numPlayers)*(-PI/3); else playerAngle[i] = MoutainAngle + lololo - PI/2 + (i+1)/numPlayers*(PI/3) + (1-(i+1)/numPlayers)*(-PI/3); playerX[i] = 0.5 + 0.35*cos(playerAngle[i]); playerZ[i] = 0.5 + 0.35*sin(playerAngle[i]); } for (var i = 0; i < numPlayers; i++) { var id = playerIDs[i]; log("Creating base for player " + id + "..."); // some constants 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]); ix = round(fx); iz = round(fz); addToClass(ix, iz, clPlayer); addToClass(ix+5, iz, clPlayer); addToClass(ix, iz+5, clPlayer); addToClass(ix-5, iz, clPlayer); addToClass(ix, iz-5, 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); // create starting units placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE); // create animals for (var j = 0; j < 2; ++j) { var aAngle = randFloat(0, TWO_PI); var aDist = 7; var aX = round(fx + aDist * cos(aAngle)); var aZ = round(fz + aDist * sin(aAngle)); var group = new SimpleGroup( [new SimpleObject(oChicken, 5,5, 0,3)], true, clBaseResource, aX, aZ ); createObjectGroup(group, 0); } // 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)); 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); 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(oPine, num, num, 0,5)], false, clBaseResource, tX, tZ ); createObjectGroup(group, 0, avoidClasses(clBaseResource,2)); // create grass tufts var num = hillSize / 250; for (var j = 0; j < num; j++) { var gAngle = randFloat(0, TWO_PI); var gDist = radius - (5 + randInt(7)); var gX = round(fx + gDist * cos(gAngle)); var gZ = round(fz + gDist * sin(gAngle)); group = new SimpleGroup( [new SimpleObject(aGrassShort, 2,5, 0,1, -PI/8,PI/8)], false, clBaseResource, gX, gZ ); createObjectGroup(group, 0); } } RMS.SetProgress(30); log ("Creating the pyreneans..."); // This is the basic orientation of the pyreneans var MountainStartX = fractionToTiles(0.5) + cos(MoutainAngle)*fractionToTiles(0.34); var MountainStartZ = fractionToTiles(0.5) + sin(MoutainAngle)*fractionToTiles(0.34); var MountainEndX = fractionToTiles(0.5) - cos(MoutainAngle)*fractionToTiles(0.34); var MountainEndZ = fractionToTiles(0.5) - sin(MoutainAngle)*fractionToTiles(0.34); var MountainHeight = scaleByMapSize(50,65); // Number of peaks var NumOfIterations = scaleByMapSize(100,1000); var randomNess = randFloat(-scaleByMapSize(1,12),scaleByMapSize(1,12)); for (var i = 0; i < NumOfIterations; i++) { RMS.SetProgress(45 * i/NumOfIterations + 30 * (1-i/NumOfIterations)); var position = i/NumOfIterations; var width = scaleByMapSize(15,55); var randHeight2 = randFloat(0,10) + MountainHeight; for (var dist = 0; dist < width*3; dist++) { var okDist = dist/3; var S1x = round((MountainStartX * (1-position) + MountainEndX*position) + randomNess*cos(position*3.14*4) + cos(MoutainAngle+PI/2)*okDist); var S1z = round((MountainStartZ * (1-position) + MountainEndZ*position) + randomNess*sin(position*3.14*4) + sin(MoutainAngle+PI/2)*okDist); var S2x = round((MountainStartX * (1-position) + MountainEndX*position) + randomNess*cos(position*3.14*4) + cos(MoutainAngle-PI/2)*okDist); var S2z = round((MountainStartZ * (1-position) + MountainEndZ*position) + randomNess*sin(position*3.14*4) + sin(MoutainAngle-PI/2)*okDist); // complicated sigmoid // Ranges is 0-1, FormX is 0-1 too. var FormX = (-2*(1-okDist/width)+1.9) - 4*(2*(1-okDist/width)-randFloat(0.9,1.1))*(2*(1-okDist/width)-randFloat(0.9,1.1))*(2*(1-okDist/width)-randFloat(0.9,1.1)); var Formula = (1/(1 + Math.exp(FormX))); // If we're too far from the border, we flatten Formula *= (0.2 - Math.max(0,abs(0.5 - position) - 0.3)) * 5; var randHeight = randFloat(-9,9) * Formula; var height = baseHeights[S1x][S1z]; setHeight(S1x,S1z, height + randHeight2 * Formula + randHeight ); var height = baseHeights[S2x][S2z]; setHeight(S2x,S2z, height + randHeight2 * Formula + randHeight ); if (getHeight(S1x,S1z) > 15) addToClass(S1x,S1z, clPyrenneans); if (getHeight(S2x,S2z) > 15) addToClass(S2x,S2z, clPyrenneans); } } // Allright now slight smoothing (decreasing with height) for (var ix = 1; ix < mapSize-1; ix++) { for (var iz = 1; iz < mapSize-1; iz++) { if (g_Map.validT(ix,iz) && checkIfInClass(ix,iz,clPyrenneans) ) { var NB = getNeighborsHeight(ix,iz); var index = 9/(1 + Math.max(0,getHeight(ix,iz)/7)); setHeight(ix,iz, (getHeight(ix,iz)*(9-index) + NB*index)/9 ); } } } RMS.SetProgress(48); // Okay so the mountains are pretty much here. // Making the passes var passWidth = scaleByMapSize(15,100) /1.8; var S1x = round((MountainStartX * (0.35) + MountainEndX*0.65) + cos(MoutainAngle+PI/2)*passWidth); var S1z = round((MountainStartZ * (0.35) + MountainEndZ*0.65) + sin(MoutainAngle+PI/2)*passWidth); var S2x = round((MountainStartX * (0.35) + MountainEndX*0.65) + cos(MoutainAngle-PI/2)*passWidth); var S2z = round((MountainStartZ * (0.35) + MountainEndZ*0.65) + sin(MoutainAngle-PI/2)*passWidth); PassMaker(S1x, S1z, S2x, S2z, 4, 7, (getHeight(S1x,S1z) + getHeight(S2x,S2z))/2.0, MountainHeight-25, 2, clPass); S1x = round((MountainStartX * (0.65) + MountainEndX*0.35) + cos(MoutainAngle+PI/2)*passWidth); S1z = round((MountainStartZ * (0.65) + MountainEndZ*0.35) + sin(MoutainAngle+PI/2)*passWidth); S2x = round((MountainStartX * (0.65) + MountainEndX*0.35) + cos(MoutainAngle-PI/2)*passWidth); S2z = round((MountainStartZ * (0.65) + MountainEndZ*0.35) + sin(MoutainAngle-PI/2)*passWidth); PassMaker(S1x, S1z, S2x, S2z, 4, 7, (getHeight(S1x,S1z) + getHeight(S2x,S2z))/2.0, MountainHeight-25, 2, clPass); RMS.SetProgress(50); // Smoothing the mountains for (var ix = 1; ix < mapSize-1; ix++) { for (var iz = 1; iz < mapSize-1; iz++) { if ( g_Map.validT(ix,iz) && checkIfInClass(ix,iz,clPyrenneans) ) { var NB = getNeighborsHeight(ix,iz); var index = 9/(1 + Math.max(0,(getHeight(ix,iz)-10)/7)); setHeight(ix,iz, (getHeight(ix,iz)*(9-index) + NB*index)/9 ); baseHeights[ix][iz] = (getHeight(ix,iz)*(9-index) + NB*index)/9; } } } log ("creating Oceans"); // ALlright for hacky reasons I can't use a smooth Elevation Painter, that wouldn't work. // I'll use a harsh one, and then smooth it out var OceanX = fractionToTiles(0.5) + cos(MoutainAngle + lololo)*fractionToTiles(0.48); var OceanZ = fractionToTiles(0.5) + sin(MoutainAngle + lololo)*fractionToTiles(0.48); var radius = fractionToTiles(0.18); var size = radius*radius*PI; var placer = new ClumpPlacer(size, 0.9, 0.05, 10, OceanX, OceanZ); var elevationPainter = new ElevationPainter(-22); createArea(placer, [paintClass(clWater),elevationPainter], null); OceanX = fractionToTiles(0.5) + cos(PI + MoutainAngle + lololo)*fractionToTiles(0.48); OceanZ = fractionToTiles(0.5) + sin(PI + MoutainAngle + lololo)*fractionToTiles(0.48); radius = fractionToTiles(0.18); size = radius*radius*PI; placer = new ClumpPlacer(size, 0.9, 0.05, 10, OceanX, OceanZ); elevationPainter = new ElevationPainter(-22); createArea(placer, [paintClass(clWater),elevationPainter], null); // Smoothing around the water, then going a bit random for (var ix = 1; ix < mapSize-1; ix++) { for (var iz = 1; iz < mapSize-1; iz++) { if ( g_Map.validT(ix,iz) && getTileClass(clWater).countInRadius(ix,iz,5,true) > 0 ) { // Allright smoothing // I'll have to hack again. var averageHeight = 0; var size = 5; if (getTileClass(clPyrenneans).countInRadius(ix,iz,1,true) > 0) size = 1; else if (getTileClass(clPyrenneans).countInRadius(ix,iz,2,true) > 0) size = 2; else if (getTileClass(clPyrenneans).countInRadius(ix,iz,3,true) > 0) size = 3; else if (getTileClass(clPyrenneans).countInRadius(ix,iz,4,true) > 0) size = 4; var todivide = 0; for (var xx = -size; xx <= size;xx++) for (var yy = -size; yy <= size;yy++) { if (g_Map.validT(ix + xx,iz + yy) && (xx != 0 || yy != 0)){ averageHeight += getHeight(ix + xx,iz + yy) / (abs(xx)+abs(yy)); todivide += 1/(abs(xx)+abs(yy)); } } averageHeight += getHeight(ix,iz)*2; averageHeight /= (todivide+2); setHeight(ix,iz, averageHeight ); //baseHeights[ix][iz] = averageHeight; } if ( g_Map.validT(ix,iz) && getTileClass(clWater).countInRadius(ix,iz,4,true) > 0 && getTileClass(clWater).countInRadius(ix,iz,4) > 0 ) setHeight(ix,iz, getHeight(ix,iz) + randFloat(-1,1)); } } RMS.SetProgress(55); //create hills log ("Creating hills..."); placer = new ClumpPlacer(scaleByMapSize(60, 120), 0.3, 0.06, 5); painter = new SemiRandomElevationPainter(7, 4,1); var terrainPainter = new TerrainPainter(tGrassSpecific); createAreas( placer, [painter,terrainPainter, paintClass(clHill)], avoidClasses(clWater, 5, clPlayer, 10, clBaseResource, 6, clPyrenneans, 2), scaleByMapSize(5, 35) ); // create forests log("Creating forests..."); var types = [ [tForestTransition,pForestLandVeryLight, pForestLandLight, pForestLand]]; var size = scaleByMapSize(40,115)*PI; var num = floor(scaleByMapSize(8,40) / types.length); for (var i = 0; i < types.length; ++i) { placer = new ClumpPlacer(size, 0.2, 0.1, 1); painter = new LayeredPainter( types[i], [scaleByMapSize(1,2),scaleByMapSize(3,6),scaleByMapSize(3,6)] ); createAreas( placer, [painter, paintClass(clForest)], avoidClasses(clPlayer, 12, clPyrenneans,0, clForest, 7, clWater, 2), num); } RMS.SetProgress(60); log("Creating lone trees..."); var num = scaleByMapSize(80,400); var group = new SimpleGroup([new SimpleObject(oPine, 1,2, 1,3),new SimpleObject(oBeech, 1,2, 1,3)], true, clForest); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 8,clPyrenneans, 1), num, 20 ); // Painting log("Painting the map"); var terrainGrass = createTerrain(tGrass); var terrainGrassMidRange = createTerrain(tGrassMidRange); var terrainGrassHighRange = createTerrain(tGrassHighRange); var terrainRocks = createTerrain(tHighRocks); var terrainRocksSnow = createTerrain(tSnowedRocks); var terrainTopSnow = createTerrain(tTopSnow); var terrainTopSnowOnly = createTerrain(tTopSnowOnly); var terrainMidRangeCliff = createTerrain(tMidRangeCliffs); var terrainHighRangeCliff = createTerrain(tHighRangeCliffs); var terrainPass = createTerrain(tPass); var terrainSand = createTerrain(tSand); var terrainWetSand = createTerrain(tWetSand); var terrainSandTransition = createTerrain(tSandTransition); var terrainWater = createTerrain(tWater); /* // first pass: who's water? for (var sandx = 0; sandx < mapSize; sandx++) for (var sandz = 0; sandz < mapSize; sandz++) if (getHeight(sandx,sandz) < 0) addToClass(sandx,sandz,clWater); */ // second pass: who's not water for (var x = 0; x < mapSize; x++) { for (var z = 0; z < mapSize; z++) { var height = getHeight(x,z); var heightDiff = getHeightDifference(x,z); if (getTileClass(clPyrenneans).countInRadius(x,z,2,true) > 0) { if (height < 6) { if (heightDiff < 5) terrainGrass.place(x,z); else terrainMidRangeCliff.place(x,z); } else if (height >= 6 && height < 18) { if (heightDiff < 8) terrainGrassMidRange.place(x,z); else terrainMidRangeCliff.place(x,z); } else if (height >= 18 && height < 30) { if (heightDiff < 8) terrainGrassHighRange.place(x,z); else terrainMidRangeCliff.place(x,z); } else if (height >= 30 && height < MountainHeight-20) { if (heightDiff < 8) terrainRocks.place(x,z); else terrainHighRangeCliff.place(x,z); } else if (height >= MountainHeight-20 && height < MountainHeight-10) { if (heightDiff < 7) terrainRocksSnow.place(x,z); else terrainHighRangeCliff.place(x,z); } else if (height >= MountainHeight-10) { if (heightDiff < 6) terrainTopSnowOnly.place(x,z); else terrainTopSnow.place(x,z); } if (height >= 30 && getTileClass(clPass).countInRadius(x,z,2,true) > 0) if (heightDiff < 5) terrainPass.place(x,z); } if (height > -14 && height <= -2 && getTileClass(clWater).countInRadius(x,z,2,true) > 0) { if (heightDiff < 2.5) terrainSand.place(x,z); else terrainMidRangeCliff.place(x,z); } else if (height > -14 && height <= 0 && getTileClass(clWater).countInRadius(x,z,3,true) > 0) { if (heightDiff < 2.5) terrainSandTransition.place(x,z); else terrainMidRangeCliff.place(x,z); } else if (height <= -14) { terrainWater.place(x,z); } } } // create dirt patches log("Creating dirt patches..."); var sizes = [scaleByMapSize(3, 20), scaleByMapSize(5, 40), scaleByMapSize(8, 60)]; for (var i = 0; i < sizes.length; i++) { placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0.5); painter = new TerrainPainter(tDirtyGrass); createAreas( placer, [painter, paintClass(clDirt)], avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45) ); } // create grass patches log("Creating grass patches..."); var sizes = [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)]; for (var i = 0; i < sizes.length; i++) { placer = new ClumpPlacer(sizes[i], 0.3, 0.06, 0.5); painter = new TerrainPainter(tLushGrass); createAreas( placer, [painter,paintClass(clLush)], avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6), scaleByMapSize(15, 45) ); } RMS.SetProgress(70); // making more in dirt areas so as to appear different log("Creating small grass tufts..."); var group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -PI/8,PI/8)] ); createObjectGroups(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0, clPyrenneans,2), scaleByMapSize(13, 200) ); createObjectGroups(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10); log("Creating large grass tufts..."); group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)] ); createObjectGroups(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0, clPyrenneans,2), scaleByMapSize(13, 200) ); createObjectGroups(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10); RMS.SetProgress(75); // create bushes log("Creating bushes..."); group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] ); createObjectGroups(group, 0, avoidClasses(clWater, 2, clPlayer, 1, clPyrenneans, 1), scaleByMapSize(13, 200), 50 ); RMS.SetProgress(80); log("Creating stone mines..."); // create large stone quarries group = new SimpleGroup([new SimpleObject(oStoneSmall, 0,2, 0,4), new SimpleObject(oStoneLarge, 1,1, 0,4)], true, clRock); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 10, clRock, 8, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); // create small stone quarries group = new SimpleGroup([new SimpleObject(oStoneSmall, 2,5, 1,3)], true, clRock); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 10, clRock, 8, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); log("Creating metal mines..."); group = new SimpleGroup([new SimpleObject(oMetalLarge, 1,1, 0,4)], true, clMetal); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 10, clMetal, 8, clRock, 5, clPyrenneans, 1), scaleByMapSize(4,16), 100 ); RMS.SetProgress(85); log("Creating small decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true ); createObjectGroups( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(16, 262), 50 ); log("Creating large decorative rocks..."); group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true ); createObjectGroups( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(8, 131), 50 ); RMS.SetProgress(90); log("Creating deer..."); group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood ); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 10, clPyrenneans, 1, clFood, 15), 3 * numPlayers, 50 ); log("Creating rabbit..."); group = new SimpleGroup( [new SimpleObject(oRabbit, 2,3, 0,2)], true, clFood ); createObjectGroups(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 10, clPyrenneans, 1, clFood,15), 3 * numPlayers, 50 ); log("Creating fish..."); group = new SimpleGroup( [new SimpleObject(oFish, 2,3, 0,2)], true, clFood ); createObjectGroups(group, 0, [avoidClasses(clFood, 15), stayClasses(clWater, 6)], 20 * numPlayers, 60 ); setSunElevation(randFloat(PI/5, PI / 3)); setSunRotation(randFloat(0, TWO_PI)); setWaterTint(0.1, 0.1, 0.3); // muddy brown setWaterReflectionTint(0.2, 0.2, 0.5); // muddy brown setWaterMurkiness(0.8); setWaterReflectionTintStrength(0.3); //var rt = randInt(1,6); //if (rt==1){ // setSkySet("stormy"); // setSunColour(0.36,0.38,0.45); // setTerrainAmbientColour(0.52,0.575,0.6); // setUnitsAmbientColour(0.52,0.575,0.6); // setSunElevation(PI/7); // setWaterTint(0.1, 0.1, 0.2); // muddy brown //} else { setSkySet("cumulus"); setSunColour(0.73,0.73,0.65); setTerrainAmbientColour(0.45,0.45,0.50); setUnitsAmbientColour(0.4,0.4,0.4); setWaterTint(0.15, 0.15, 0.3); // muddy brown setWaterReflectionTintStrength(0.15); //} // Export map data ExportMap(); function getNeighborsHeight(x1, z1) { var toCheck = [ [-1,-1], [-1,0], [-1,1], [0,1], [1,1], [1,0], [1,-1], [0,-1] ]; var height = 0; for (i in toCheck) { var xx = x1 + toCheck[i][0]; var zz = z1 + toCheck[i][1]; height += getHeight(round(xx),round(zz)); } height /= 8; return height; } // Taken from Corsica vs Sardinia with tweaks function PassMaker(x1, z1, x2, z2, startWidth, centerWidth, startElevation, centerElevation, smooth, tileclass, terrain) { var mapSize = g_Map.size; var stepNB = sqrt((x2-x1)*(x2-x1) + (z2-z1)*(z2-z1)) + 2; var startHeight = startElevation; var finishHeight = centerElevation; for (var step = 0; step <= stepNB; step+=0.5) { var ix = ((stepNB-step)*x1 + x2*step) / stepNB; var iz = ((stepNB-step)*z1 + z2*step) / stepNB; var width = (abs(step - stepNB/2.0) *startWidth + (stepNB/2 - abs(step - stepNB/2.0)) * centerWidth ) / (stepNB/2); var oldDirection = [x2-x1, z2-z1]; // let's get the perpendicular direction var direction = [ -oldDirection[1],oldDirection[0] ]; if (abs(direction[0]) > abs(direction[1])) { direction[1] = direction[1] / abs(direction[0]); if (direction[0] > 0) direction[0] = 1; else direction[0] = -1; } else { direction[0] = direction[0] / abs(direction[1]); if (direction[1] > 0) direction[1] = 1; else direction[1] = -1; } for (var po = -Math.floor(width/2.0); po <= Math.floor(width/2.0); po+=0.5) { var rx = po*direction[0]; var rz = po*direction[1]; var relativeWidth = abs(po / Math.floor(width/2)); var targetHeight = (abs(step - stepNB/2.0) *startHeight + (stepNB/2 - abs(step - stepNB/2.0)) * finishHeight ) / (stepNB/2); if (round(ix + rx) < mapSize && round(iz + rz) < mapSize && round(ix + rx) >= 0 && round(iz + rz) >= 0) { // smoothing the sides if ( abs(abs(po) - abs(Math.floor(width/2.0))) < smooth) { var localHeight = getHeight(round(ix + rx), round(iz + rz)); var localPart = smooth - abs(abs(po) - abs(Math.floor(width/2.0))); var targetHeight = (localHeight * localPart + targetHeight * (1/localPart) )/ (localPart + 1/localPart); } g_Map.setHeight(round(ix + rx), round(iz + rz), targetHeight); if (tileclass != null) addToClass(round(ix + rx), round(iz + rz), tileclass); if (terrain != null) placeTerrain(round(ix + rx), round(iz + rz), terrain); } } } } // no need for preliminary rounding function getHeightDifference(x1, z1) { x1 = round(x1); z1 = round(z1); var height = getHeight(x1,z1); if (!g_Map.validT(x1,z1)) return 0; // I wanna store the height difference with any neighbor var toCheck = [ [-1,-1], [-1,0], [-1,1], [0,1], [1,1], [1,0], [1,-1], [0,-1] ]; var diff = 0; var todiv = 0; for (i in toCheck) { var xx = round(x1 + toCheck[i][0]); var zz = round(z1 + toCheck[i][1]); if (g_Map.validT(xx,zz)) { diff += abs(getHeight(xx,zz) - height); todiv++; } } if (todiv > 0) diff /= todiv; return diff; }