// This file is part of MicropolisJ. // Copyright (C) 2013 Jason Long // Portions Copyright (C) 1989-2007 Electronic Arts Inc. // // MicropolisJ is free software; you can redistribute it and/or modify // it under the terms of the GNU GPLv3, with additional terms. // See the README file, included in this distribution, for details. package micropolisj.engine; import java.util.*; import static micropolisj.engine.TileConstants.*; /** * Contains the code for generating a random map terrain. */ public class MapGenerator { Micropolis engine; char [][] map; Random PRNG; /** * Three settings on whether to generate a new map as an island. */ static enum CreateIsland { NEVER, ALWAYS, SELDOM; // seldom == 10% of the time } CreateIsland createIsland = CreateIsland.SELDOM; public MapGenerator(Micropolis engine) { assert engine != null; this.engine = engine; this.map = engine.map; } private int getWidth() { return map[0].length; } private int getHeight() { return map.length; } /** * Generate a random map terrain. */ public void generateNewCity() { long r = Micropolis.DEFAULT_PRNG.nextLong(); generateSomeCity(r); } public void generateSomeCity(long r) { engine.totalFunds = GameLevel.getStartingFunds(engine.gameLevel); generateMap(r); engine.fireWholeMapChanged(); } /** * Level for tree creation. * If positive, this is (roughly) the number of trees to randomly place. * If negative, then the number of trees is randomly chosen. * If zero, then no trees are generated. */ int treeLevel = -1; //level for tree creation int curveLevel = -1; //level for river curviness; -1==auto, 0==none, >0==level int lakeLevel = -1; //level for lake creation; -1==auto, 0==none, >0==level void generateMap(long r) { PRNG = new Random(r); if (createIsland == CreateIsland.SELDOM) { if (PRNG.nextInt(100) < 10) //chance that island is generated { makeIsland(); return; } } if (createIsland == CreateIsland.ALWAYS) { makeNakedIsland(); } else { clearMap(); } getRandStart(); if (curveLevel != 0) { doRivers(); } if (lakeLevel != 0) { makeLakes(); } smoothRiver(); if (treeLevel != 0) { doTrees(); } } private void makeIsland() { makeNakedIsland(); smoothRiver(); doTrees(); } private int erand(int limit) { return Math.min( PRNG.nextInt(limit), PRNG.nextInt(limit) ); } private void makeNakedIsland() { final int ISLAND_RADIUS = 18; final int WORLD_X = getWidth(); final int WORLD_Y = getHeight(); for (int y = 0; y < WORLD_Y; y++) { for (int x = 0; x < WORLD_X; x++) { map[y][x] = RIVER; } } for (int y = 5; y < WORLD_Y - 5; y++) { for (int x = 5; x < WORLD_X - 5; x++) { map[y][x] = DIRT; } } for (int x = 0; x < WORLD_X - 5; x += 2) { mapX = x; mapY = erand(ISLAND_RADIUS+1); BRivPlop(); mapY = (WORLD_Y - 10) - erand(ISLAND_RADIUS+1); BRivPlop(); mapY = 0; SRivPlop(); mapY = WORLD_Y - 6; SRivPlop(); } for (int y = 0; y < WORLD_Y - 5; y += 2) { mapY = y; mapX = erand(ISLAND_RADIUS+1); BRivPlop(); mapX = (WORLD_X - 10) - erand(ISLAND_RADIUS+1); BRivPlop(); mapX = 0; SRivPlop(); mapX = (WORLD_X - 6); SRivPlop(); } } private void clearMap() { for (int y = 0; y < map.length; y++) { for (int x = 0; x < map[y].length; x++) { map[y][x] = DIRT; } } } int xStart; int yStart; int mapX; int mapY; int dir; int lastDir; private void getRandStart() { xStart = 40 + PRNG.nextInt(getWidth() - 79); yStart = 33 + PRNG.nextInt(getHeight() - 66); mapX = xStart; mapY = yStart; } private void makeLakes() { int lim1; if (lakeLevel < 0) lim1 = PRNG.nextInt(11); else lim1 = lakeLevel / 2; for (int t = 0; t < lim1; t++) { int x = PRNG.nextInt(getWidth() - 20) + 10; int y = PRNG.nextInt(getHeight() - 19) + 10; int lim2 = PRNG.nextInt(13) + 2; for (int z = 0; z < lim2; z++) { mapX = x - 6 + PRNG.nextInt(13); mapY = y - 6 + PRNG.nextInt(13); if (PRNG.nextInt(5) != 0) SRivPlop(); else BRivPlop(); } } } private void doRivers() { dir = lastDir = PRNG.nextInt(4); doBRiv(); mapX = xStart; mapY = yStart; dir = lastDir = lastDir ^ 4; doBRiv(); mapX = xStart; mapY = yStart; lastDir = PRNG.nextInt(4); doSRiv(); } private void doBRiv() { int r1, r2; if (curveLevel < 0) { r1 = 100; r2 = 200; } else { r1 = curveLevel + 10; r2 = curveLevel + 100; } while (engine.testBounds(mapX + 4, mapY + 4)) { BRivPlop(); if (PRNG.nextInt(r1+1) < 10) { dir = lastDir; } else { if (PRNG.nextInt(r2+1) > 90) { dir++; } if (PRNG.nextInt(r2+1) > 90) { dir--; } } moveMap(dir); } } private void doSRiv() { int r1, r2; if (curveLevel < 0) { r1 = 100; r2 = 200; } else { r1 = curveLevel + 10; r2 = curveLevel + 100; } while (engine.testBounds(mapX + 3, mapY + 3)) { SRivPlop(); if (PRNG.nextInt(r1+1) < 10) { dir = lastDir; } else { if (PRNG.nextInt(r2+1) > 90) { dir++; } if (PRNG.nextInt(r2+1) > 90) { dir--; } } moveMap(dir); } } static final char [][] BRMatrix = new char[][] { { 0, 0, 0, 3, 3, 3, 0, 0, 0 }, { 0, 0, 3, 2, 2, 2, 3, 0, 0 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0 }, { 3, 2, 2, 2, 2, 2, 2, 2, 3 }, { 3, 2, 2, 2, 4, 2, 2, 2, 3 }, { 3, 2, 2, 2, 2, 2, 2, 2, 3 }, { 0, 3, 2, 2, 2, 2, 2, 3, 0 }, { 0, 0, 3, 2, 2, 2, 3, 0, 0 }, { 0, 0, 0, 3, 3, 3, 0, 0, 0 } }; private void BRivPlop() { for (int x = 0; x < 9; x++) { for (int y = 0; y < 9; y++) { putOnMap(BRMatrix[y][x], x, y); } } } static final char [][] SRMatrix = new char[][] { { 0, 0, 3, 3, 0, 0 }, { 0, 3, 2, 2, 3, 0 }, { 3, 2, 2, 2, 2, 3 }, { 3, 2, 2, 2, 2, 3 }, { 0, 3, 2, 2, 3, 0 }, { 0, 0, 3, 3, 0, 0 } }; private void SRivPlop() { for (int x = 0; x < 6; x++) { for (int y = 0; y < 6; y++) { putOnMap(SRMatrix[y][x], x, y); } } } private void putOnMap(char mapChar, int xoff, int yoff) { if (mapChar == 0) return; int xloc = mapX + xoff; int yloc = mapY + yoff; if (!engine.testBounds(xloc, yloc)) return; char tmp = map[yloc][xloc]; if (tmp != DIRT) { tmp &= LOMASK; if (tmp == RIVER && mapChar != CHANNEL) return; if (tmp == CHANNEL) return; } map[yloc][xloc] = mapChar; } static final char [] REdTab = new char[] { 13 + BULLBIT, 13 + BULLBIT, 17 + BULLBIT, 15 + BULLBIT, 5 + BULLBIT, 2, 19 + BULLBIT, 17 + BULLBIT, 9 + BULLBIT, 11 + BULLBIT, 2, 13 + BULLBIT, 7 + BULLBIT, 9 + BULLBIT, 5 + BULLBIT, 2 }; private void smoothRiver() { for (int mapY = 0; mapY < map.length; mapY++) { for (int mapX = 0; mapX < map[mapY].length; mapX++) { if (map[mapY][mapX] == REDGE) { int bitindex = 0; for (int z = 0; z < 4; z++) { bitindex <<= 1; int xtem = mapX + DX[z]; int ytem = mapY + DY[z]; if (engine.testBounds(xtem, ytem) && ((map[ytem][xtem] & LOMASK) != DIRT) && (((map[ytem][xtem] & LOMASK) < WOODS_LOW) || ((map[ytem][xtem] & LOMASK) > WOODS_HIGH))) { bitindex |= 1; } } char temp = REdTab[bitindex & 15]; if ((temp != RIVER) && PRNG.nextInt(2) != 0) temp++; map[mapY][mapX] = temp; } } } } private void doTrees() { int amount; if (treeLevel < 0) { amount = PRNG.nextInt(101) + 50; } else { amount = treeLevel + 3; } for (int x = 0; x < amount; x++) { int xloc = PRNG.nextInt(getWidth()); int yloc = PRNG.nextInt(getHeight()); treeSplash(xloc, yloc); } smoothTrees(); smoothTrees(); } private void treeSplash(int xloc, int yloc) { int dis; if (treeLevel < 0) { dis = PRNG.nextInt(151) + 50; } else { dis = PRNG.nextInt(101 + (treeLevel*2)) + 50; } mapX = xloc; mapY = yloc; for (int z = 0; z < dis; z++) { int dir = PRNG.nextInt(8); moveMap(dir); if (!engine.testBounds(mapX, mapY)) return; if ((map[mapY][mapX] & LOMASK) == DIRT) { map[mapY][mapX] = WOODS + BLBNBIT; } } } static final int [] DIRECTION_TABX = new int[] { 0, 1, 1, 1, 0, -1, -1, -1 }; static final int [] DIRECTION_TABY = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 }; private void moveMap(int dir) { dir = dir & 7; mapX += DIRECTION_TABX[dir]; mapY += DIRECTION_TABY[dir]; } static final int [] DX = new int[] { -1, 0, 1, 0 }; static final int [] DY = new int[] { 0, 1, 0, -1 }; static final char [] TEdTab = new char[] { 0, 0, 0, 34, 0, 0, 36, 35, 0, 32, 0, 33, 30, 31, 29, 37 }; private void smoothTrees() { for (int mapY = 0; mapY < map.length; mapY++) { for (int mapX = 0; mapX < map[mapY].length; mapX++) { if (isTree(map[mapY][mapX])) { int bitindex = 0; for (int z = 0; z < 4; z++) { bitindex <<= 1; int xtem = mapX + DX[z]; int ytem = mapY + DY[z]; if (engine.testBounds(xtem, ytem) && isTree(map[ytem][xtem])) { bitindex |= 1; } } char temp = TEdTab[bitindex & 15]; if (temp != 0) { if (temp != WOODS) { if (((mapX + mapY) & 1) != 0) { temp -= 8; } } map[mapY][mapX] = (char)(temp + BLBNBIT); } else { map[mapY][mapX] = temp; } } } } } }