2013-02-14 21:02:42 +00:00
|
|
|
// 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.*;
|
|
|
|
|
2013-02-18 01:56:47 +00:00
|
|
|
/**
|
|
|
|
* Contains the code for generating a random map terrain.
|
|
|
|
*/
|
2013-02-14 21:02:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-02-18 01:56:47 +00:00
|
|
|
/**
|
|
|
|
* Generate a random map terrain.
|
|
|
|
*/
|
2013-02-14 21:02:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|