Jump to content

Maze map


Cuervo
 Share

Recommended Posts

Hi,

For the last few days I have worked on a maze map. Unfortunately I currently don't have much time to work on it, but it is already quite playable, so I want to showcase it here.

Screenshots:

(The screenshots are pretty large!)

2 players

4 players

8 players

The complexity depends on the size

Tiny map with 8 players...

From above

Civic center macro shot

Oasis in the center

Another civic center

Old screenshots:

Early stage

Different trees, bushes

Source code:

maze.json:

{	"settings" : {		"Name" : "Maze",		"Script" : "maze.js",		"Description" : "A nice map with a maze. Works good for up to 4 players, more players cause some imbalance. Tiny map is ridiculous, small map is still very easy.",		"BaseTerrain" : "tropic_beach_volcanic",		"BaseHeight" : 0,		"Keywords": [],		"CircularMap": false,		"XXXXXX" : "Optionally define other things here, like we would for a scenario"	}}

maze.js:

/** * Maze map for 0 A.D.: *  * As this map is optimized for up to 4 players, 4 mazes are created on this square map. *  - It would be relatively simple to modifiy this map to be circular. -  *  * For reading this code, a monospaced font is recommended, as in the comments some explanations are in ASCII-Art. *  *  * Map: * I use X-Y-coordinates rather than X-Z-coordinates in this code: *  * Y * ^ * | * +--> X *  * The map is divided in 4 smaller squares, like this: * C---E---B * | 2 | 3 | * G---X---H * | 0 | 1 | * A---F---D *  * In each square a maze is drawn (maze[nr]). * The X represents the oasis in the center of the map. * The letters A to H represent the starting positions of the players (please note, that the order of the players is randomized.) *  * The architecture of the maze generation forces walls to be between the 4 squares with the mazes, but guarantees, that each player can reach the center of the map. * However, when playing with more than 4 players, civic centers for the players E to H in the map destroy the walls, which can force the players in the corners to move through enemy territory if they want to reach the center of the map. *  * There are some things which should be fixed or done, they are marked with FIXME or TODO (as that causes highlighting in my editor). *  * Mazes: * The mazes generated by the algorithm are "perfect", which means, there are no loops possible and every cell in the maze is reachable. In all deadlocks are resources like stone, metal or wood, and also in random cells. *  * Start area: * The start conditions of the players differ a bit from other maps. All resources are initially available and placed in a special arrangement. *  * Known Problems: * => At some places the code is not very optimized resulting in "slow" map generation (still very fast, so it's not really bad) * => The pathfinder algorithm of the main game solves the maze. So if you send a unit to the center of the map, even if you can not see it, it will automatically find the right way instantly. This is very bad. *   => Maybe this can be fixed by using units as walls! * => The map is not fun to play if it is really large, because the distances are huge, and to gain resources you have to build many civic centers. Tip: Build a civic center in the center, but be quick! * => The borders of the players can cause some minor graphic issues when hitting a maze wall - this bug is caused by 0 A.D., not the map. * => Some quite important constants are hardcoded in the script and should be defined at the top. * => Maybe too many resources are placed in the mazes. * => The tree-selection (oTree1 and oTree2) is not perfect yet. *  * Some things that might not be clear at the first glance are commented. */RMS.LoadLibrary("rmgen");RMS.SetProgress(5);const tMainTerrain = "tropic_beach_volcanic";const tCliff = "alpine_cliff";const tTempCliff = "temp_cliff_c";// Gaia entitiesconst oTree1 = "gaia/flora_tree_pine";const oTree2 = "gaia/flora_tree_cypress";const oBerryBush = "gaia/flora_bush_berry";const oStoneLarge = "gaia/geology_stonemine_alpine_quarry";const oStoneSmall = "gaia/geology_stone_alpine_a";const oMetalLarge = "gaia/geology_metal_alpine_slabs";const oMetalSmall = "gaia/geology_metal_alpine";const oMazeMultiResources = [tMainTerrain + TERRAIN_SEPARATOR + oTree1, tMainTerrain + TERRAIN_SEPARATOR + oTree2, tMainTerrain];const oMazeSingleResources = [tMainTerrain + TERRAIN_SEPARATOR + oStoneLarge, tMainTerrain + TERRAIN_SEPARATOR + oMetalLarge];// Decorative propsconst aGrass = "grass1_spring_fancy";const aCoral = "ocean_medit_coral";const aPlayer = "road_roman";const aOasis = "alpine_grass";const aOasisTrees = [aOasis + TERRAIN_SEPARATOR + oTree1, aOasis + TERRAIN_SEPARATOR + oTree2, aOasis];const aPlayerBaseTrees = oTree1;const aPlayerBaseBushes = oBerryBush;// Initialize maplog("Initializing map...");InitMap();const numPlayers = getNumPlayers();const mapSize = getMapSize();const mapArea = getMapArea();const mSHalf = round(mapSize/2); // The half size of the map, used to calculate the center and the size of the mazeconst WALL_HEIGHT = 10; // The height of the walls of the maze.const BUILDING_ANGLE = -PI/4;const BASE_RESOURCE_DISTANCE = 10; // The distance the start resources are away from the civic center/* * The width and height of half the maze. Must be an integer. *  * Maybe this needs to be optimized for different map sizes (TODO) * Currently possible map sizes: * => Tiny: 128x128 * => Small: 192x192 * => Medium: 256x256 * => Normal: 320x320 * => Large: 384x384 * => Very large: 448x448 * => Giant: 512x512 */var MAZE_HALFSIZE = round(mapSize / 30);const PLAYER_BORDERDISTANCE = 22; // The size of the start area of the players and the size of the "oasis" in the center of the mapconst PLAYER_FROM_BORDERS = 5; // The distance of the start area of the players from the borderconst OASIS_CIRCLEELEMENTS = 5; // The amount of metal and stone mines in the oasis + 1 metal mine in the exace centerlog("Filling map with initial terrain...");for (var ix = 0; ix < mapSize; ix++) {	for (var iz = 0; iz < mapSize; iz++) {		var x = ix / (mapSize + 1.0);		var z = iz / (mapSize + 1.0);				placeTerrain(ix, iz, tMainTerrain);	}}/** * Draws a line on the terrain with the specified height and, if requested, the specified terrain. This function uses Bresenham's line algorithm. *  * @param x0 The Start-X-coordinate of the line. * @param y0 The Start-Y-coordinate of the line. * @param x1 The End-X-coordinate of the line. * @param y1 The End-Y-coordinate of the line. * @param height The height of the line. * @param terrain The terrain of the line. Can be undefined. *  * @return Nothing. */function bline(x0, y0, x1, y1, height, terrain) {  x0 = round(x0);  y0 = round(y0);  x1 = round(x1);  y1 = round(y1);    var dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;  var dy = Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;   var err = (dx>dy ? dx : -dy)/2;   while (true) {    if (vCFT(mapSize, x0, y0)) {      setHeight(x0, y0, height);    }        if (terrain !== undefined) {      if (vCFT(mapSize, x0, y0)) {	placeTerrain(x0, y0, terrain);      }            if (vCFT(mapSize, (x0 - 1), (y0 - 1))) {	placeTerrain((x0 - 1), (y0 - 1), terrain);      }    }        if (x0 === x1 && y0 === y1) break;    var e2 = err;    if (e2 > -dx) { err -= dy; x0 += sx; }    if (e2 < dy) { err += dx; y0 += sy; }  }}/** * This function draws a rect on the map with the specified height and, if requested, the specified terrain. *  * @param x The X-coordinate of the rect. * @param y The Y-coordinate of the rect. * @param w The width of the rect. * @param h The height of the rect. * @param height The hight of the rect on the map. * @param terrain The terrain the rect should be painted with. Can be undefined. *  * @return Nothing. */function mapRect(x, y, w, h, height, terrain) {  x = round(x);  y = round(y);  w = round(w);  h = round(h);    for (var i = x; i <= (x + w); i++) {    for (var j = y; j <= (y + h); j++) {      if (vCFT(mapSize, i, j)) {	setHeight(i, j, height);      }    }  }    if (terrain !== undefined) {    paintTerrain(x, y, w, h, terrain, mapSize);  }}function paintTerrain(x, y, w, h, terrain, mapSize) {  if (terrain != undefined) {    for (var i = x; i <= (x + w); i++) {      for (var j = y; j <= (y + h); j++) {	if (vCFT(mapSize, i, j)) {	  placeTerrain(i, j, terrain);	}      }    }  }}// validCoordinatesForTerrainfunction vCFT(mapSize, x, y) {  if (x >= 0 && y >= 0 && x < mapSize && y < mapSize) {    return true;  }    return false;}/** * Creates a Maze with the specified parameters. *  * @param width The width of the maze * @param heigth The height of the maze *  * @return A maze or null, if the maze could not be created. */function createMaze(width, height) {  var cellStack = [];  var totalCells = (width * height);  if (totalCells < 1) {    error("createMaze: Invalid maze dimensions.");    return null;  }  var cellWalls = [];  for (var i = 0; i < width; i++) {    cellWalls[i] = [];  }      var currentCell = {"x": 0, "y": 0};  var visitedCells = 1;  cellWalls[currentCell.x][currentCell.y] = makeBitmask(1, 1, 1, 1, 1);    while (visitedCells < totalCells) {    var validNeighbors = getValidNeighbors(cellWalls, currentCell.x, currentCell.y, width, height);        if (validNeighbors[0] > 0) {      var toCell = randInt(1, 5);            while (validNeighbors[toCell].x === (-1)) {	toCell = randInt(1, 5);      }            /*       * Cell-Bits in cellWalls[][]:       *        *      ---       *     | 2 |       *  ---+---+---       * | 1 |   | 3 |       *  ---+---+---       *     | 0 |       *      ---       *        * Bits 0-3: Walls to the direction are present (1/True = Walls, 0/False = No Walls)       * Bit 4: Is deadlock (needed for placing resources in all deadlocks)       *        * cellWalls[X][Y] = 0b11111       *                     ^^^^^       *            Bit nr:  01234       */            var bitmaskCurrent = makeBitmask(1, 1, 1, 1, 1);      var bitmaskNext = makeBitmask(1, 1, 1, 1, 1);            if (toCell == 1) {bitmaskCurrent = makeBitmask(0, 1, 1, 1, 0); bitmaskNext = makeBitmask(1, 1, 0, 1, 1);};      if (toCell == 2) {bitmaskCurrent = makeBitmask(1, 0, 1, 1, 0); bitmaskNext = makeBitmask(1, 1, 1, 0, 1);};      if (toCell == 3) {bitmaskCurrent = makeBitmask(1, 1, 0, 1, 0); bitmaskNext = makeBitmask(0, 1, 1, 1, 1);};      if (toCell == 4) {bitmaskCurrent = makeBitmask(1, 1, 1, 0, 0); bitmaskNext = makeBitmask(1, 0, 1, 1, 1);};            cellWalls[currentCell.x][currentCell.y] &= bitmaskCurrent;      cellWalls[validNeighbors[toCell].x][validNeighbors[toCell].y] = makeBitmask(1, 1, 1, 1, 1) & bitmaskNext;            cellStack.push(currentCell);      currentCell = {"x": validNeighbors[toCell].x, "y": validNeighbors[toCell].y};            visitedCells++;    } else {      currentCell = cellStack.pop();    }  }      return {w: width, h: height, walls: cellWalls};}function getValidNeighbors(cellWalls, x, y, w, h) {  var data = [];  var foundNeighbors = 0;    if (y-1 >= 0) {    if (cellWalls[x][y-1] == undefined) {      data.push({"x": x, "y": (y-1)});      foundNeighbors++;    } else {      data.push({"x": (-1), "y": (-1)});    }  } else {    data.push({"x": (-1), "y": (-1)});  }    if (x-1 >= 0) {    if (cellWalls[x-1][y] == undefined) {      data.push({"x": (x-1), "y": y});      foundNeighbors++;    } else {      data.push({"x": (-1), "y": (-1)});    }  } else {    data.push({"x": (-1), "y": (-1)});  }    if (y+1 < h) {    if (cellWalls[x][y+1] == undefined) {      data.push({"x": x, "y": (y+1)});      foundNeighbors++;    } else {      data.push({"x": (-1), "y": (-1)});    }  } else {    data.push({"x": (-1), "y": (-1)});  }    if (x+1 < w) {    if (cellWalls[x+1][y] == undefined) {      data.push({"x": (x+1), "y": y});      foundNeighbors++;    } else {      data.push({"x": (-1), "y": (-1)});    }  } else {    data.push({"x": (-1), "y": (-1)});  }    data.push({"x": (-1), "y": (-1)}); // Push last invalid cell, which is nonexistent, otherwise off-by-one-error - FIXME  data.unshift(foundNeighbors);  return data;}function drawMaze(m, x, y, width, height, yHeight, terrain) {  x = round(x);  y = round(y);  width = round(width);  height = round(height);  yHeight = round(yHeight);    var mX = (width / m.w); // Scale factor  var mY = (height / m.h); // Scale factor    // Yes, we set every wall twice - this doesn't make the map load much slower at the moment... Maybe a FIXME if maps can become much larger  for (var iX = 0; iX < m.w; iX++) {    for (var iY = 0; iY < m.h; iY++) {      var bits = m.walls[iX][iY];                  // Check if bits are set using AND      if ((bits & makeBitmask(1, 0, 0, 0, 0)) > 0) {	bline(x + (iX * mX), y + (iY * mY), x + ((iX + 1) * mX), y + (iY * mY), yHeight, terrain);      }            if ((bits & makeBitmask(0, 1, 0, 0, 0)) > 0) {	bline(x + (iX * mX), y + (iY * mY), x + (iX * mX), y + ((iY + 1) * mY), yHeight, terrain);      }            if ((bits & makeBitmask(0, 0, 1, 0, 0)) > 0) {	bline(x + (iX * mX), y + ((iY + 1) * mY), x + ((iX + 1) * mX), y + ((iY + 1) * mY), yHeight, terrain);      }            if ((bits & makeBitmask(0, 0, 0, 1, 0)) > 0) {	bline(x + ((iX + 1) * mX), y + (iY * mY), x + ((iX + 1) * mX), y + ((iY + 1) * mY), yHeight, terrain);      }            // If this is a dead end, create at least some resources      // Also, create them randomly      // But just if they are not on the outer paths, as that may cause AI malfunctions      if (((bits & makeBitmask(0, 0, 0, 0, 1)) > 0 || randInt(0, 100) > 75) && (iX > 0 || x != 0) && (iY > 0 || y != 0) && (iX < (m.w - 1) || x == 0) && (iY < (m.h - 1) || y == 0)) {	if (randInt(0, 10) > 4) {	  // Single resources (Stone, Metal)	  var mazePainter = new TerrainPainter(oMazeSingleResources);	  var toX = round(x + ((iX + 0.5) * mX) - randInt(0, 1));	  var toY = round(y + ((iY + 0.5) * mY) - randInt(0, 1));	  var mazePlacer = new RectPlacer(toX, toY, (toX + 1), (toY + 1));	  	  createArea(mazePlacer, mazePainter);	} else {	  // Multiple Resources (Trees)	  var mazePainter = new TerrainPainter(oMazeMultiResources);	  var size = chooseRand(2, 3);	  var toX1 = round(x + ((iX + 0.5) * mX) - size);	  var toY1 = round(y + ((iY + 0.5) * mY) - size);	  var toX2 = round(x + ((iX + 0.5) * mX) + size);	  var toY2 = round(y + ((iY + 0.5) * mY) + size);	  var mazePlacer = new RectPlacer(toX1, toY1, toX2, toY2);	  	  createArea(mazePlacer, mazePainter);	}      }    }  }}/** * Create a step in a circle of objects. *  * @param fx The X-coordinate of the center of the circle. * @param fy The Y-coordinate of the center of the circle. * @param angle The angle of the current step. * @param object The object to place. * @param addToDistance The radius of the circle. *  * @return Nothing. */function placeCircle(fx, fy, angle, object, addToDistance) {  var x = (fx + cos(angle) * addToDistance);  var y = (fy + sin(angle) * addToDistance);  placeObject(x, y, object, 0, angle);}/** * Creates a bitmask as an integer from the specified bits. *  * @param bit0-bitN The bits. *  * @return The bitmask. */function makeBitmask() {  var nMask = 0, nFlag = 0, nLen = arguments.length > 32 ? 32 : arguments.length;  for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);  return nMask;}// Randomize player ordervar playerIDs = [];for (var i = 0; i < numPlayers; i++) {	playerIDs.push(i+1);}playerIDs = sortPlayers(playerIDs);var playerAngle = new Array(numPlayers);log("Creating mazes...");RMS.SetProgress(15);// Create big maze using four small mazes// Maybe this could be done in a for-loop (TODO)var maze = new Array(4);log(" => Maze 0/3");maze[0] = createMaze(MAZE_HALFSIZE, MAZE_HALFSIZE);RMS.SetProgress(20);log(" => Maze 1/3");maze[1] = createMaze(MAZE_HALFSIZE, MAZE_HALFSIZE);RMS.SetProgress(25);log(" => Maze 2/3");maze[2] = createMaze(MAZE_HALFSIZE, MAZE_HALFSIZE);RMS.SetProgress(30);log(" => Maze 3/3");maze[3] = createMaze(MAZE_HALFSIZE, MAZE_HALFSIZE);RMS.SetProgress(35);// This could also be done in a for-loop, position can be found out either by if/switch, or, more elegant, by reading the bits from the iterator (TODO)log("Drawing mazes on the map...");// Draw the mazes on the maplog(" => Maze 0/3");drawMaze(maze[0], 0, 0, mSHalf, mSHalf, WALL_HEIGHT, tCliff);RMS.SetProgress(40);log(" => Maze 1/3");drawMaze(maze[1], mSHalf, 0, mSHalf, mSHalf, WALL_HEIGHT, tCliff);RMS.SetProgress(45);log(" => Maze 2/3");drawMaze(maze[2], 0, mSHalf, mSHalf, mSHalf, WALL_HEIGHT, tCliff);RMS.SetProgress(50);log(" => Maze 3/3");drawMaze(maze[3], mSHalf, mSHalf, mSHalf, mSHalf, WALL_HEIGHT, tCliff);RMS.SetProgress(55);log("Creating oasis in the center of the map...");mapRect((mSHalf - PLAYER_BORDERDISTANCE - 4), (mSHalf - PLAYER_BORDERDISTANCE - 4), (PLAYER_BORDERDISTANCE * 2 + 8), (PLAYER_BORDERDISTANCE * 2 + 8), 1, tMainTerrain);mapRect((mSHalf - PLAYER_BORDERDISTANCE - 2), (mSHalf - PLAYER_BORDERDISTANCE - 2), (PLAYER_BORDERDISTANCE * 2 + 4), (PLAYER_BORDERDISTANCE * 2 + 4), 1, aCoral);mapRect((mSHalf - PLAYER_BORDERDISTANCE), (mSHalf - PLAYER_BORDERDISTANCE), (PLAYER_BORDERDISTANCE * 2), (PLAYER_BORDERDISTANCE * 2), 2, aOasis);var oasisCircleDone = 0;for (var r = 0; r <= (2 * PI); r = (r + (PI * 2 / OASIS_CIRCLEELEMENTS))) {  if (oasisCircleDone < OASIS_CIRCLEELEMENTS) {    placeCircle(mSHalf, mSHalf, r, oStoneLarge, 8);    placeCircle(mSHalf, mSHalf, r, oMetalLarge, 14);  }    oasisCircleDone++;}placeObject(mSHalf, mSHalf, oMetalLarge, 0, 0); // Place metal mine in the center of the map// This could also be done in a loop (TODO)var oasisTreePainter = new TerrainPainter(aOasisTrees);var oasisEdges = [(mSHalf - PLAYER_BORDERDISTANCE + 2), (mSHalf + PLAYER_BORDERDISTANCE - 2)];var oasisTreePlacer1 = new PathPlacer(oasisEdges[0], oasisEdges[0], oasisEdges[1], oasisEdges[0], 3, 0, 1, 0, 0);var oasisTreePlacer2 = new PathPlacer(oasisEdges[0], oasisEdges[0], oasisEdges[0], oasisEdges[1], 3, 0, 1, 0, 0);var oasisTreePlacer3 = new PathPlacer(oasisEdges[0], oasisEdges[1], oasisEdges[1], oasisEdges[1], 3, 0, 1, 0, 0);var oasisTreePlacer4 = new PathPlacer(oasisEdges[1], oasisEdges[1], oasisEdges[1], oasisEdges[0], 3, 0, 1, 0, 0);createArea(oasisTreePlacer1, oasisTreePainter);createArea(oasisTreePlacer2, oasisTreePainter);createArea(oasisTreePlacer3, oasisTreePainter);createArea(oasisTreePlacer4, oasisTreePainter);log("Creating stuff for players...");// Create playersvar startAngle = 0; // randFloat() * 2 * PI;for (var i=0; i < numPlayers; i++) {	var id = playerIDs[i];	log(" => Player " + id);		var fx = 0;	var fy = 0;		switch(i) {	  case 0:	    fx = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    fy = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    playerAngle[i] = (5 / 4 * PI);	    break;	  case 1:	    fx = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    fy = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    playerAngle[i] = (1 / 4 * PI);	    break;	  case 2:	    fx = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    fy = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    playerAngle[i] = (3 / 4 * PI);	    break;	  case 3:	    fx = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    fy = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    playerAngle[i] = (7 / 4 * PI);	    break;	  case 4:	    fx = mSHalf;	    fy = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    playerAngle[i] = (2 / 4 * PI);	    break;	  case 5:	    fx = mSHalf;	    fy = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    playerAngle[i] = (6 / 4 * PI);	    break;	  case 6:	    fx = PLAYER_BORDERDISTANCE + PLAYER_FROM_BORDERS;	    fy = mSHalf;	    playerAngle[i] = (4 / 4 * PI);	    break;	  case 7:	    fx = mapSize - PLAYER_BORDERDISTANCE - PLAYER_FROM_BORDERS;	    fy = mSHalf;	    playerAngle[i] = (0 / 4 * PI);	    break;	  default:	    error("Unsupported player count! This map only supports up to 8 Players! Remaining players were not created.");	    break;	}		if (fx != 0 && fy != 0) {	  // Remove maze too near to the player - yes, it is sad, but it has to be..	  paintTerrain((fx - PLAYER_BORDERDISTANCE - 6), (fy - PLAYER_BORDERDISTANCE - 6), (PLAYER_BORDERDISTANCE * 2 + 12), (PLAYER_BORDERDISTANCE * 2 + 12), tCliff);	  mapRect((fx - PLAYER_BORDERDISTANCE - 4), (fy - PLAYER_BORDERDISTANCE - 4), (PLAYER_BORDERDISTANCE * 2 + 8), (PLAYER_BORDERDISTANCE * 2 + 8), 1, tCliff);	  mapRect((fx - PLAYER_BORDERDISTANCE - 2), (fy - PLAYER_BORDERDISTANCE - 2), (PLAYER_BORDERDISTANCE * 2 + 4), (PLAYER_BORDERDISTANCE * 2 + 4), 1, tTempCliff);	  mapRect((fx - PLAYER_BORDERDISTANCE - 1), (fy - PLAYER_BORDERDISTANCE - 1), (PLAYER_BORDERDISTANCE * 2 + 2), (PLAYER_BORDERDISTANCE * 2 + 2), 1, aCoral);	  mapRect((fx - PLAYER_BORDERDISTANCE + 2), (fy - PLAYER_BORDERDISTANCE + 2), (PLAYER_BORDERDISTANCE * 2 - 4), (PLAYER_BORDERDISTANCE * 2 - 4), 2, aPlayer);	  	  // Create the base resources of the player	  placeCircle(fx, fy, (playerAngle[i] + PI / 4), oMetalSmall, BASE_RESOURCE_DISTANCE);	  placeCircle(fx, fy, (playerAngle[i] + PI / 4), oStoneSmall, BASE_RESOURCE_DISTANCE + 3);	  placeCircle(fx, fy, (playerAngle[i] - PI / 4), oMetalSmall, BASE_RESOURCE_DISTANCE);	  placeCircle(fx, fy, (playerAngle[i] - PI / 4), oStoneSmall, BASE_RESOURCE_DISTANCE + 3);	  	  for (var r = (5 * (playerAngle[i] + PI / 2)); r < (5 * (playerAngle[i] + (6 / 4 * PI))); r++) {	    placeCircle(fx, fy, (r / 5), aPlayerBaseBushes, BASE_RESOURCE_DISTANCE);	    placeCircle(fx, fy, (r / 5), aPlayerBaseTrees, BASE_RESOURCE_DISTANCE + 3);	  }	  	  // Create starting units	  placeCivDefaultEntities(fx, fy, id, playerAngle[i]);	}		RMS.SetProgress(round(60 + ((i / numPlayers) * 20)));}log("Setting final environment parameters...");RMS.SetProgress(90);// These parameters were experimentally determined in AtlassetSkySet("sunny");setSunColour(1, 1, 1);setSunElevation(0.67);setSunRotation(0.85);setTerrainAmbientColour(0.4, 0.4, 0.4);setUnitsAmbientColour(0.5, 0.5, 0.5);setWaterHeight(0);// Destroy outer walls of the map created by the mazes as they obstruct visibility of some things and do not look good - Also reset their terrain.bline(0, 0, mapSize, 0, 0, tMainTerrain);bline(0, mapSize, mapSize, mapSize, 0, tMainTerrain);bline(0, 0, 0, mapSize, 0, tMainTerrain);bline(mapSize, 0, mapSize, mapSize, 0, tMainTerrain);log("Warning the players if necessary...");RMS.SetProgress(95);// Generate final warningsif (numPlayers > 4) {  warn("Playing this map with more than 4 players results in some imbalance.");    if (mapSize < 256) {    warn("With " + numPlayers + " players this map is best played in medium size or above.");  }} else {  if (mapSize < 192) {    warn("With " + numPlayers + " players this map is best played in small size or above.");  }}log("Exporting map data...");ExportMap();

Feel free to edit the map and do whatever you want with it. The code is somewhat well-commented.

Edited by Cuervo
  • Like 2
Link to comment
Share on other sites

I like it.

I'm not so sure about the playability but I think you know of this yourself.

Something similar could be used to place roads in a city map (I have thought about making wall_builder.js capable of adding branches for this purpose but maybe that's not the best idea in the first place).

  • Like 2
Link to comment
Share on other sites

Reminds me of that Warzone2100 map, I've forgotten it's name, but it was probably just 'Maze'.

That map had many paths to get from one place to another and the walls were walkable. The fun part was that you could build your base utilizing the maze walls as indestructible walls, focusing your defensive buildings on the choke points into your territory.

When attacking other players you often took different routes from their armies, so fighting often happened near the bases and resource locations instead of in the middle of nowhere. This made it much more exciting.

Link to comment
Share on other sites

  • 1 month later...
  • 1 month later...

yeah, I remember that Warzone2100 map, but I forget the name as well - it would be really cool, but I agree that it doesn't have a lot of playability; the pathfinder would make it too easy

at one point, I thought of a maze map, but thought that a top-down view would also make it too easy, but it looks better with a more complicated maze and wider roads

Edited by SiahH
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...