Engine.LoadLibrary("rmgen");
Engine.LoadLibrary("rmgen-common");
Engine.LoadLibrary("rmbiome");

setSelectedBiome();

const tMainTerrain = g_Terrains.mainTerrain;
const tForestFloor1 = g_Terrains.forestFloor1;
const tForestFloor2 = g_Terrains.forestFloor2;
const tCliff = g_Terrains.cliff;
const tTier1Terrain = g_Terrains.tier1Terrain;
const tTier2Terrain = g_Terrains.tier2Terrain;
const tTier3Terrain = g_Terrains.tier3Terrain;
const tHill = g_Terrains.hill;
const tRoad = g_Terrains.road;
const tRoadWild = g_Terrains.roadWild;
const tTier4Terrain = g_Terrains.tier4Terrain;

const oTree1 = g_Gaia.tree1;
const oTree2 = g_Gaia.tree2;
const oTree3 = g_Gaia.tree3;
const oTree4 = g_Gaia.tree4;
const oTree5 = g_Gaia.tree5;
const oFruitBush = g_Gaia.fruitBush;
const oFish = g_Gaia.fish;
const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal;
const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal;
const oStoneLarge = g_Gaia.stoneLarge;
const oStoneSmall = g_Gaia.stoneSmall;
const oMetalLarge = g_Gaia.metalLarge;

const aGrass = g_Decoratives.grass;
const aGrassShort = g_Decoratives.grassShort;
const aRockLarge = g_Decoratives.rockLarge;
const aRockMedium = g_Decoratives.rockMedium;
const aBushMedium = g_Decoratives.bushMedium;
const aBushSmall = g_Decoratives.bushSmall;

Engine.SetProgress(10);

const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2];
const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1];

const heightLand = 3;

var g_Map = new RandomMap(heightLand, tMainTerrain);

const numPlayers = getNumPlayers();

var clPlayer = g_Map.createTileClass();
var clHill = g_Map.createTileClass();
var clForest = g_Map.createTileClass();
var clDirt = g_Map.createTileClass();
var clRock = g_Map.createTileClass();
var clMetal = g_Map.createTileClass();
var clFood = g_Map.createTileClass();
var clBaseResource = g_Map.createTileClass();
var clWater = g_Map.createTileClass();
var clCliff = g_Map.createTileClass();


function getTangentPointCirclePointLine(circlePos,circleRad,point){

	var x1 = circlePos.x;
	var y1 = circlePos.y;
	var r1 = circleRad;
	var x2 = point.x;
	var y2 = point.y;
	var r2 = Math.sqrt( Math.pow( x1-x2,2 ) + Math.pow( y1-y2,2 ) - r1*r1 );;
	var centerdx = x1-x2;
	var centerdy = y1-y2;
	var R = Math.sqrt(centerdx * centerdx + centerdy * centerdy);
	var R2 = R*R;
	var R4 = Math.pow(R,4);
	var a = (r1*r1-r2*r2)/(2*R2);
	var r2r2 = (r1*r1-r2*r2);
	var c = Math.sqrt(2*(r1*r1+r2*r2)/R2 - (r2r2*r2r2)/R4-1);

	var fx = (x1+x2)/2 + a*(x2-x1);
	var gx = c*(y2-y1)/2;
	var ix1 = fx+gx;
	var ix2 = fx-gx;

	var fy = (y1+y2)/2 + a*(y2-y1);
	var gy = c*(x1-x2)/2;
	var iy1 = fy+gy;
	var iy2 = fy-gy;
	return [ new Vector2D(ix1, iy1), new Vector2D(ix2, iy2) ];
}

Engine.SetProgress(20);

createBumps(avoidClasses(clPlayer, 20));

g_Map.placeEntityPassable("undeletable|disableGarrisonHolder|structures/brit_wonder",0,new Vector2D(g_Map.size/2,g_Map.size/2), 0.0);


/*g_Map.placeEntityPassable("units/brit_catafalque",0,new Vector2D(g_Map.size/2,g_Map.size/2), 0);

for (var i = 0 ; i < 5; i++ )
{	var rad = i*2.0*Math.PI/5;
	var pos = new Vector2D( g_Map.size/2 + 2*Math.cos(rad) , g_Map.size/2 + 2*Math.sin(rad) );
	g_Map.placeEntityPassable("other/palisades_angle_spike",0, pos, rad - Math.PI/2);
}*/



var n = numPlayers;
var h = 10.0*n/8.0;
var offh = 24;
var step = 0.1
var r = 18;
var start = 0.0;
var end = Math.PI*scaleByMapSize(1.0,2.0);
var m = n*1.1;
var maxRad = (r + m*end);



var hlist = [];



var R = g_Map.size/2.0 - ( r + m*end) ;
for (var t = 0; t < n; t++ ) {
	var offset = 2*Math.PI*t/n;
	var cx = g_Map.size/2 + g_Map.size/2*Math.cos(end+offset);
	var cy = g_Map.size/2 + g_Map.size/2*Math.sin(end+offset);

	for (var i = 0 ; i < 2*Math.PI/4.0; i += 0.1/R ) {

		var x = parseInt( cx + R*Math.cos(Math.PI+end+offset-i) )
		var y = parseInt( cy + R*Math.sin(Math.PI+end+offset-i) )
		var px = x;
		var py = y;
		var s = 3;
		for (var dx = -s; dx <= s; dx++) {
			for (var dy = -s; dy <= s; dy++) {
				px = x+dx;
				py = y+dy;
				var z = offh;
				var pos = new Vector2D(px,py);
				if(!g_Map.validHeight(pos)){ continue;}

				g_Map.setHeight( pos, z );
				
			}
		}
		for (var qr = -5; qr <= 20; qr++) {
			px = parseInt( cx + (R-qr-m)*Math.cos(Math.PI+end+offset-i) )
			py = parseInt( cy + (R-qr-m)*Math.sin(Math.PI+end+offset-i) )
			var pos = new Vector2D(px,py);
			if(!g_Map.validHeight(pos)){ continue;}
			if(i < 2*Math.PI/4.0*0.65 ){
				g_Map.setHeight(pos, Math.min(offh,-(1-qr/20.0)*offh*3)*(i/(2*Math.PI/4.0)) + 3 )
			}else{
				g_Map.setHeight(pos, Math.min(offh,(1-qr/20.0)*offh+3) )
			}
		}
		for (var qr = -3; qr <= 20; qr++) {
			px = parseInt( cx + (R+qr)*Math.cos(Math.PI+end+offset-i) )
			py = parseInt( cy + (R+qr)*Math.sin(Math.PI+end+offset-i) )
			var pos = new Vector2D(px,py);
			if(!g_Map.validHeight(pos)){ continue;}
			g_Map.setHeight(pos, Math.min(offh,(1-qr/20.0)*offh)+4 )
		}
	}
}

Engine.SetProgress(30);

var spiralEndPos = [];

for (var t = 0; t < n; t++ ) {
	var offset = 2*Math.PI*t/n;
	for (var i = -2*Math.PI/n*0.5 ; i < end; i += 1.0/(r+i) ) {
		var x = parseInt( g_Map.size/2 + (r + m*i)*Math.cos(i+offset) );
		var y = parseInt( g_Map.size/2 + (r + m*i)*Math.sin(i+offset) );
		var px = x;
		var py = y;
		var s = 3;
		for (var dx = -s; dx <= s; dx++) {
			for (var dy = -s; dy <= s; dy++) {
				px = x+dx;
				py = y+dy;
				var z = Math.max(Math.min(h,h*(1.0-i/end)),0.0) + offh;
				var pos = new Vector2D(px,py);
				g_Map.setHeight( pos, z );
				hlist.push( [ pos ,z] );
				
			}
		}
	}
	var tempPos = new Vector2D( parseInt( g_Map.size/2 + (r + m*end)*Math.cos(end-2*offset) ),  parseInt( g_Map.size/2 + (r + m*end)*Math.sin(end-2*offset) ) );
	spiralEndPos.push(tempPos);
}

Engine.SetProgress(40);

createArea(
	new ClumpPlacer( diskArea(r), 0.5, 0.4, Infinity, new Vector2D(g_Map.size/2,g_Map.size/2) ),
	[new SmoothElevationPainter(ELEVATION_SET, h + offh, 2)],
	null );


for (var i = 0; i < hlist.length; i++) {
	var pos = hlist[i][0];
	var z = hlist[i][1];
	var nz = g_Map.getHeight(pos);
	if( z < nz ){
		g_Map.setHeight(pos,z);
	}
}


Engine.SetProgress(50);

var stonesGroup = new SimpleGroup( [
	new SimpleObject("actor|geology/gray1.xml",1,2,1,2),
	new SimpleObject("actor|geology/gray_rock1.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland1.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland1_moss.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland2.xml",1,2,1,3),
	new SimpleObject("actor|geology/highland2_moss.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland3.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland_c.xml",1,2,1,2),
	new SimpleObject("actor|geology/highland_d.xml",1,2,1,3),
	new SimpleObject("actor|geology/highland_e.xml",1,2,1,2)] );



function playerPlacementCircleCustom(radius, startingAngle = undefined, center = undefined)
{
	let startAngle = end - 2*Math.PI/getNumPlayers()*scaleByMapSize(0.3,-0.4)
	
	let [playerPosition, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius*scaleByMapSize(1,1.4) , center || g_Map.getCenter());
	return [sortAllPlayers(), playerPosition.map(p => p.round()), playerAngle, startAngle];
}

Engine.SetProgress(55);

var civsPlacement = playerPlacementCircleCustom(fractionToTiles(0.37)*scaleByMapSize(1.2,0.6))
var civsPos = civsPlacement[1];

placePlayerBases({
	"PlayerPlacement": civsPlacement,
	"PlayerTileClass": clPlayer,
	"Walls" : false,
	"BaseResourceClass": clBaseResource,
	"CityPatch": {
		"outerTerrain": tRoadWild,
		"innerTerrain": tRoad
	},
	"Chicken": {
	},
	"Berries": {
		"template": oFruitBush
	},
	"Mines": {
		"types": [
			{ "template": oMetalLarge },
			{ "template": oStoneLarge }
		]
	},
	"Trees": {
		"template": oTree1,
		"count": 25
	},
	"Decoratives": {
		"template": aGrassShort
	}
});

Engine.SetProgress(60);


	var pathAreas = []

	var slopeSpiralArea = new createArea(
		new MapBoundsPlacer(),
		[new TileClassPainter(clCliff)],
		[new SlopeConstraint(3.2, Infinity)]
	);
	pathAreas.push(slopeSpiralArea)

	for (var i = 0; i < getNumPlayers() ; i++) {

		var i2 = (i==0) ? getNumPlayers()-1 : i-1;

		var tanPos = new getTangentPointCirclePointLine( new Vector2D(g_Map.size/2,g_Map.size/2) , maxRad - m/2   , civsPos[i] );
		tanPos = tanPos[0];

		var pathArea = new createArea(
			new PathPlacer(	civsPos[i], tanPos, m*scaleByMapSize(0.5,1.2),0.1,1,0.5,0,1000),
			[new TerrainPainter(tRoadWild)],
			[new HeightConstraint(2,30)]
		);
		pathAreas.push(pathArea)
	}


var areaOutCircle = new createArea(
	new ClumpPlacer( diskArea(r+end*(m-1)/1.7), 1 , 1 , Infinity, new Vector2D(g_Map.size/2,g_Map.size/2) ),
	[],
	null );
var areaInCircle = new createArea(
	new ClumpPlacer( diskArea(r-9), 1 , 0 , Infinity, new Vector2D(g_Map.size/2,g_Map.size/2) ),
	[],
	null );

g_Map.log("Creating forest");
	var [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1));

createForests(
	[tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
	[ avoidClasses(clForest,0), new StayAreasConstraint([areaOutCircle]), new AvoidAreasConstraint([areaInCircle]) ],
	clForest,
	forestTrees*0.2);

var areaOutCircle2 = createArea(
	new ClumpPlacer( diskArea(r+end*(m-1)/1.1), 1 , 1 , Infinity, new Vector2D(g_Map.size/2,g_Map.size/2) ),
	[],
	null
);


createStragglerTrees(
[oTree1, oTree2, oTree4, oTree3],
[avoidClasses(clForest, 8, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6, clFood, 1), new StayAreasConstraint([areaOutCircle2]) ,new AvoidAreasConstraint(pathAreas),new AvoidAreasConstraint([areaInCircle])],
clForest,
stragglerTrees);

if(true)
{	


	
	createForests(
	 [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
	 [avoidClasses(clPlayer, 20,clForest,0), new HeightConstraint(1,16), new AvoidAreasConstraint(pathAreas) ],
	 clForest,
	 forestTrees);


	Engine.SetProgress(65);

	g_Map.log("Creating dirt patches");
	createLayeredPatches(
	 [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)],
	 [[tMainTerrain,tTier1Terrain],[tTier1Terrain,tTier2Terrain], [tTier2Terrain,tTier3Terrain]],
	 [1, 1],
	 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12),new HeightConstraint(-2,25),new AvoidAreasConstraint(pathAreas)],
	 scaleByMapSize(15, 45),
	 clDirt);

	Engine.SetProgress(70);

	g_Map.log("Creating grass patches");
	createPatches(
	 [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)],
	 tTier4Terrain,
	 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 12),new HeightConstraint(-2,85), new AvoidAreasConstraint(pathAreas)],
	 scaleByMapSize(15, 45),
	 clDirt);

	createArea(
		new MapBoundsPlacer(),
		new TerrainPainter(g_Terrains.cliff),
		[	new SlopeConstraint(3.2, Infinity)
		]);

	createObjectGroupsByAreas( stonesGroup, 0, [], 150, 10, [slopeSpiralArea] );

	Engine.SetProgress(75);
	g_Map.log("Creating stone mines");
	createMines(
		[
			[new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)],
			[new SimpleObject(oStoneSmall, 2, 5, 1, 3, 0, 2 * Math.PI, 1)]
		],
		[avoidClasses(clForest, 1, clPlayer, 15, clRock, 3, clHill, 1),new HeightConstraint(1,17),new AvoidAreasConstraint(pathAreas)],
		clRock);

	g_Map.log("Creating metal mines");
	createMines(
	 [
	  [new SimpleObject(oMetalLarge, 1,4, 4,7)]
	 ],
	 [avoidClasses(clForest, 1, clPlayer, 15, clMetal, 5, clRock, 3, clHill, 1),new HeightConstraint(1,17),new AvoidAreasConstraint(pathAreas)],
	 clMetal,
	 10
	);


	Engine.SetProgress(80);




	var planetm = 1;

	if (currentBiome() == "generic/tropic")
		planetm = 8;

	createDecoration(
		[
			[new SimpleObject(aRockMedium, 1, 3, 0, 1)],
			[new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)],
			[new SimpleObject(aGrassShort, 1, 2, 0, 1)],
			[new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3,6, 1.2, 2.5)],
			[new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]
		],
		[
			scaleByMapSize(16, 262),
			scaleByMapSize(8, 131),
			planetm * scaleByMapSize(13, 200),
			planetm * scaleByMapSize(13, 200),
			planetm * scaleByMapSize(13, 200)
		],
		[avoidClasses(clForest, 0, clPlayer, 0, clHill, 0),new HeightConstraint(1,17),new  AvoidAreasConstraint(pathAreas)]
		);

	Engine.SetProgress(85);

	createFood(
		[
			[new SimpleObject(oMainHuntableAnimal, 3, 4, 0, 25)],
			[new SimpleObject(oSecondaryHuntableAnimal, 3,5, 0, 25)]
		],
		[
			50 * numPlayers,
			50 * numPlayers
		],
		[avoidClasses(clForest, 0, clPlayer, 10, clHill, 1, clMetal, 4, clRock, 4, clFood, 20),new HeightConstraint(1,15)],
		clFood);

	Engine.SetProgress(90);

	createFood(
		[
			[new SimpleObject(oFruitBush, 3, 4, 0, 4)]
		],
		[
			3 * numPlayers
		],
		[avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clMetal, 4, clRock, 4, clFood, 10),new HeightConstraint(1,15)],
		clFood);

	Engine.SetProgress(95);

	createStragglerTrees(
		[oTree1, oTree2, oTree4, oTree3],
		[avoidClasses(clForest, 8, clHill, 1, clPlayer, 12, clMetal, 6, clRock, 6, clFood, 1),new HeightConstraint(1,15),new AvoidAreasConstraint(pathAreas)],
		clForest,
		stragglerTrees);


	var areaWater = new createArea(
		new MapBoundsPlacer(),
		[],
		[new HeightConstraint(-Infinity,-1)]
	);

	createObjectGroups(
	new SimpleGroup([new SimpleObject(oFish, 3, 4, 2, 3)], true, clFood),
	0,
	[new HeightConstraint(-Infinity,-1), new AvoidAreasConstraint([slopeSpiralArea])],
	scaleByMapSize(3, 10)*numPlayers,
	50);
}




placePlayersNomad(clPlayer, [avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2),new HeightConstraint(1,17)]);
Engine.SetProgress(100);

g_Map.ExportMap();


