Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
MapGenerator.java
Go to the documentation of this file.
1 // This file is part of MicropolisJ.
2 // Copyright (C) 2013 Jason Long
3 // Portions Copyright (C) 1989-2007 Electronic Arts Inc.
4 //
5 // MicropolisJ is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU GPLv3, with additional terms.
7 // See the README file, included in this distribution, for details.
8 
9 package micropolisj.engine;
10 
11 import java.util.*;
12 
13 import static micropolisj.engine.TileConstants.*;
14 
18 public class MapGenerator
19 {
20  Micropolis engine;
21  char [][] map;
22  Random PRNG;
23 
27  static enum CreateIsland
28  {
29  NEVER,
30  ALWAYS,
31  SELDOM; // seldom == 10% of the time
32  }
33  CreateIsland createIsland = CreateIsland.SELDOM;
34 
35  public MapGenerator(Micropolis engine)
36  {
37  assert engine != null;
38  this.engine = engine;
39  this.map = engine.map;
40  }
41 
42  private int getWidth()
43  {
44  return map[0].length;
45  }
46 
47  private int getHeight()
48  {
49  return map.length;
50  }
51 
55  public void generateNewCity()
56  {
57  long r = Micropolis.DEFAULT_PRNG.nextLong();
59  }
60 
61  public void generateSomeCity(long r)
62  {
63  generateMap(r);
64  engine.fireWholeMapChanged();
65  }
66 
73  int treeLevel = -1; //level for tree creation
74 
75  int curveLevel = -1; //level for river curviness; -1==auto, 0==none, >0==level
76 
77  int lakeLevel = -1; //level for lake creation; -1==auto, 0==none, >0==level
78 
79  void generateMap(long r)
80  {
81  PRNG = new Random(r);
82 
83  if (createIsland == CreateIsland.SELDOM)
84  {
85  if (PRNG.nextInt(100) < 10) //chance that island is generated
86  {
87  makeIsland();
88  return;
89  }
90  }
91 
92  if (createIsland == CreateIsland.ALWAYS)
93  {
95  }
96  else
97  {
98  clearMap();
99  }
100 
101  getRandStart();
102 
103  if (curveLevel != 0)
104  {
105  doRivers();
106  }
107 
108  if (lakeLevel != 0)
109  {
110  makeLakes();
111  }
112 
113  smoothRiver();
114 
115  if (treeLevel != 0)
116  {
117  doTrees();
118  }
119  }
120 
121  private void makeIsland()
122  {
123  makeNakedIsland();
124  smoothRiver();
125  doTrees();
126  }
127 
128  private int erand(int limit)
129  {
130  return Math.min(
131  PRNG.nextInt(limit),
132  PRNG.nextInt(limit)
133  );
134  }
135 
136  private void makeNakedIsland()
137  {
138  final int ISLAND_RADIUS = 18;
139  final int WORLD_X = getWidth();
140  final int WORLD_Y = getHeight();
141 
142  for (int y = 0; y < WORLD_Y; y++)
143  {
144  for (int x = 0; x < WORLD_X; x++)
145  {
146  map[y][x] = RIVER;
147  }
148  }
149 
150  for (int y = 5; y < WORLD_Y - 5; y++)
151  {
152  for (int x = 5; x < WORLD_X - 5; x++)
153  {
154  map[y][x] = DIRT;
155  }
156  }
157 
158  for (int x = 0; x < WORLD_X - 5; x += 2)
159  {
160  mapX = x;
161  mapY = erand(ISLAND_RADIUS+1);
162  BRivPlop();
163  mapY = (WORLD_Y - 10) - erand(ISLAND_RADIUS+1);
164  BRivPlop();
165  mapY = 0;
166  SRivPlop();
167  mapY = WORLD_Y - 6;
168  SRivPlop();
169  }
170 
171  for (int y = 0; y < WORLD_Y - 5; y += 2)
172  {
173  mapY = y;
174  mapX = erand(ISLAND_RADIUS+1);
175  BRivPlop();
176  mapX = (WORLD_X - 10) - erand(ISLAND_RADIUS+1);
177  BRivPlop();
178  mapX = 0;
179  SRivPlop();
180  mapX = (WORLD_X - 6);
181  SRivPlop();
182  }
183  }
184 
185  private void clearMap()
186  {
187  for (int y = 0; y < map.length; y++)
188  {
189  for (int x = 0; x < map[y].length; x++)
190  {
191  map[y][x] = DIRT;
192  }
193  }
194  }
195 
196  int xStart;
197  int yStart;
198  int mapX;
199  int mapY;
200  int dir;
201  int lastDir;
202 
203  private void getRandStart()
204  {
205  xStart = 40 + PRNG.nextInt(getWidth() - 79);
206  yStart = 33 + PRNG.nextInt(getHeight() - 66);
207 
208  mapX = xStart;
209  mapY = yStart;
210  }
211 
212  private void makeLakes()
213  {
214  int lim1;
215  if (lakeLevel < 0)
216  lim1 = PRNG.nextInt(11);
217  else
218  lim1 = lakeLevel / 2;
219 
220  for (int t = 0; t < lim1; t++)
221  {
222  int x = PRNG.nextInt(getWidth() - 20) + 10;
223  int y = PRNG.nextInt(getHeight() - 19) + 10;
224  int lim2 = PRNG.nextInt(13) + 2;
225 
226  for (int z = 0; z < lim2; z++)
227  {
228  mapX = x - 6 + PRNG.nextInt(13);
229  mapY = y - 6 + PRNG.nextInt(13);
230 
231  if (PRNG.nextInt(5) != 0)
232  SRivPlop();
233  else
234  BRivPlop();
235  }
236  }
237  }
238 
239  private void doRivers()
240  {
241  dir = lastDir = PRNG.nextInt(4);
242  doBRiv();
243 
244  mapX = xStart;
245  mapY = yStart;
246  dir = lastDir = lastDir ^ 4;
247  doBRiv();
248 
249  mapX = xStart;
250  mapY = yStart;
251  lastDir = PRNG.nextInt(4);
252  doSRiv();
253  }
254 
255  private void doBRiv()
256  {
257  int r1, r2;
258  if (curveLevel < 0)
259  {
260  r1 = 100;
261  r2 = 200;
262  }
263  else
264  {
265  r1 = curveLevel + 10;
266  r2 = curveLevel + 100;
267  }
268 
269  while (engine.testBounds(mapX + 4, mapY + 4))
270  {
271  BRivPlop();
272  if (PRNG.nextInt(r1+1) < 10)
273  {
274  dir = lastDir;
275  }
276  else
277  {
278  if (PRNG.nextInt(r2+1) > 90)
279  {
280  dir++;
281  }
282  if (PRNG.nextInt(r2+1) > 90)
283  {
284  dir--;
285  }
286  }
287  moveMap(dir);
288  }
289  }
290 
291  private void doSRiv()
292  {
293  int r1, r2;
294  if (curveLevel < 0)
295  {
296  r1 = 100;
297  r2 = 200;
298  }
299  else
300  {
301  r1 = curveLevel + 10;
302  r2 = curveLevel + 100;
303  }
304 
305  while (engine.testBounds(mapX + 3, mapY + 3))
306  {
307  SRivPlop();
308  if (PRNG.nextInt(r1+1) < 10)
309  {
310  dir = lastDir;
311  }
312  else
313  {
314  if (PRNG.nextInt(r2+1) > 90)
315  {
316  dir++;
317  }
318  if (PRNG.nextInt(r2+1) > 90)
319  {
320  dir--;
321  }
322  }
323  moveMap(dir);
324  }
325  }
326 
327  static final char [][] BRMatrix = new char[][] {
328  { 0, 0, 0, 3, 3, 3, 0, 0, 0 },
329  { 0, 0, 3, 2, 2, 2, 3, 0, 0 },
330  { 0, 3, 2, 2, 2, 2, 2, 3, 0 },
331  { 3, 2, 2, 2, 2, 2, 2, 2, 3 },
332  { 3, 2, 2, 2, 4, 2, 2, 2, 3 },
333  { 3, 2, 2, 2, 2, 2, 2, 2, 3 },
334  { 0, 3, 2, 2, 2, 2, 2, 3, 0 },
335  { 0, 0, 3, 2, 2, 2, 3, 0, 0 },
336  { 0, 0, 0, 3, 3, 3, 0, 0, 0 }
337  };
338 
339  private void BRivPlop()
340  {
341  for (int x = 0; x < 9; x++)
342  {
343  for (int y = 0; y < 9; y++)
344  {
345  putOnMap(BRMatrix[y][x], x, y);
346  }
347  }
348  }
349 
350  static final char [][] SRMatrix = new char[][] {
351  { 0, 0, 3, 3, 0, 0 },
352  { 0, 3, 2, 2, 3, 0 },
353  { 3, 2, 2, 2, 2, 3 },
354  { 3, 2, 2, 2, 2, 3 },
355  { 0, 3, 2, 2, 3, 0 },
356  { 0, 0, 3, 3, 0, 0 }
357  };
358 
359  private void SRivPlop()
360  {
361  for (int x = 0; x < 6; x++)
362  {
363  for (int y = 0; y < 6; y++)
364  {
365  putOnMap(SRMatrix[y][x], x, y);
366  }
367  }
368  }
369 
370  private void putOnMap(char mapChar, int xoff, int yoff)
371  {
372  if (mapChar == 0)
373  return;
374 
375  int xloc = mapX + xoff;
376  int yloc = mapY + yoff;
377 
378  if (!engine.testBounds(xloc, yloc))
379  return;
380 
381  char tmp = map[yloc][xloc];
382  if (tmp != DIRT)
383  {
384  tmp &= LOMASK;
385  if (tmp == RIVER && mapChar != CHANNEL)
386  return;
387  if (tmp == CHANNEL)
388  return;
389  }
390  map[yloc][xloc] = mapChar;
391  }
392 
393  static final char [] REdTab = new char[] {
394  RIVEDGE + 8, RIVEDGE + 8, RIVEDGE + 12, RIVEDGE + 10,
395  RIVEDGE + 0, RIVER, RIVEDGE + 14, RIVEDGE + 12,
396  RIVEDGE + 4, RIVEDGE + 6, RIVER, RIVEDGE + 8,
397  RIVEDGE + 2, RIVEDGE + 4, RIVEDGE + 0, RIVER
398  };
399 
400  private void smoothRiver()
401  {
402  for (int mapY = 0; mapY < map.length; mapY++)
403  {
404  for (int mapX = 0; mapX < map[mapY].length; mapX++)
405  {
406  if (map[mapY][mapX] == REDGE)
407  {
408  int bitindex = 0;
409 
410  for (int z = 0; z < 4; z++)
411  {
412  bitindex <<= 1;
413  int xtem = mapX + DX[z];
414  int ytem = mapY + DY[z];
415  if (engine.testBounds(xtem, ytem) &&
416  ((map[ytem][xtem] & LOMASK) != DIRT) &&
417  (((map[ytem][xtem] & LOMASK) < WOODS_LOW) ||
418  ((map[ytem][xtem] & LOMASK) > WOODS_HIGH)))
419  {
420  bitindex |= 1;
421  }
422  }
423 
424  char temp = REdTab[bitindex & 15];
425  if ((temp != RIVER) && PRNG.nextInt(2) != 0)
426  temp++;
427  map[mapY][mapX] = temp;
428  }
429  }
430  }
431  }
432 
433  private void doTrees()
434  {
435  int amount;
436 
437  if (treeLevel < 0)
438  {
439  amount = PRNG.nextInt(101) + 50;
440  }
441  else
442  {
443  amount = treeLevel + 3;
444  }
445 
446  for (int x = 0; x < amount; x++)
447  {
448  int xloc = PRNG.nextInt(getWidth());
449  int yloc = PRNG.nextInt(getHeight());
450  treeSplash(xloc, yloc);
451  }
452 
453  smoothTrees();
454  smoothTrees();
455  }
456 
457  private void treeSplash(int xloc, int yloc)
458  {
459  int dis;
460  if (treeLevel < 0)
461  {
462  dis = PRNG.nextInt(151) + 50;
463  }
464  else
465  {
466  dis = PRNG.nextInt(101 + (treeLevel*2)) + 50;
467  }
468 
469  mapX = xloc;
470  mapY = yloc;
471 
472  for (int z = 0; z < dis; z++)
473  {
474  int dir = PRNG.nextInt(8);
475  moveMap(dir);
476 
477  if (!engine.testBounds(mapX, mapY))
478  return;
479 
480  if ((map[mapY][mapX] & LOMASK) == DIRT)
481  {
482  map[mapY][mapX] = WOODS;
483  }
484  }
485  }
486 
487  static final int [] DIRECTION_TABX = new int[] { 0, 1, 1, 1, 0, -1, -1, -1 };
488  static final int [] DIRECTION_TABY = new int[] { -1, -1, 0, 1, 1, 1, 0, -1 };
489  private void moveMap(int dir)
490  {
491  dir = dir & 7;
492  mapX += DIRECTION_TABX[dir];
493  mapY += DIRECTION_TABY[dir];
494  }
495 
496  static final int [] DX = new int[] { -1, 0, 1, 0 };
497  static final int [] DY = new int[] { 0, 1, 0, -1 };
498  static final char [] TEdTab = new char[] {
499  0, 0, 0, 34,
500  0, 0, 36, 35,
501  0, 32, 0, 33,
502  30, 31, 29, 37
503  };
504 
505  private void smoothTrees()
506  {
507  for (int mapY = 0; mapY < map.length; mapY++)
508  {
509  for (int mapX = 0; mapX < map[mapY].length; mapX++)
510  {
511  if (isTree(map[mapY][mapX]))
512  {
513  int bitindex = 0;
514  for (int z = 0; z < 4; z++)
515  {
516  bitindex <<= 1;
517  int xtem = mapX + DX[z];
518  int ytem = mapY + DY[z];
519  if (engine.testBounds(xtem, ytem) &&
520  isTree(map[ytem][xtem]))
521  {
522  bitindex |= 1;
523  }
524  }
525  char temp = TEdTab[bitindex & 15];
526  if (temp != 0)
527  {
528  if (temp != WOODS)
529  {
530  if (((mapX + mapY) & 1) != 0)
531  {
532  temp -= 8;
533  }
534  }
535  map[mapY][mapX] = temp;
536  }
537  else
538  {
539  map[mapY][mapX] = temp;
540  }
541  }
542  }
543  }
544  }
545 
546 }