Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
TerrainBehavior.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 static micropolisj.engine.TileConstants.*;
12 import micropolisj.util.Utilities;
13 
14 class TerrainBehavior extends TileBehavior {
15  final B behavior;
16 
17  TerrainBehavior(Micropolis city, B behavior) {
18  super(city);
19  this.behavior = behavior;
20  }
21 
22  static enum B {
23  FIRE, FLOOD, RADIOACTIVE, ROAD, RAIL, EXPLOSION;
24  }
25 
26  @Override
27  public void apply() {
28  switch(behavior) {
29  case FIRE:
30  doFire();
31  return;
32  case FLOOD:
33  doFlood();
34  return;
35  case RADIOACTIVE:
36  doRadioactiveTile();
37  return;
38  case ROAD:
39  doRoad();
40  return;
41  case RAIL:
42  doRail();
43  return;
44  case EXPLOSION:
45  doExplosion();
46  return;
47  default:
48  assert false;
49  }
50  }
51 
52  void doFire() {
54 
55  // one in four times
56  if(PRNG.nextInt(4) != 0) {
57  return;
58  }
59 
60  final int[] DX = {
61  0, 1, 0, -1
62  };
63  final int[] DY = {
64  -1, 0, 1, 0
65  };
66 
67  for(int dir = 0; dir < 4; dir++) {
68  if(PRNG.nextInt(8) == 0) {
69  int xtem = xpos + DX[dir];
70  int ytem = ypos + DY[dir];
71  if(!city.testBounds(xtem, ytem))
72  continue;
73 
74  int c = city.getTile(xtem, ytem);
75  if(isCombustible(c)) {
76  if(isZoneCenter(c)) {
77  city.killZone(xtem, ytem, c);
78  if(c > IZB) { // explode
79  city.makeExplosion(xtem, ytem);
80  }
81  }
82  city.setTile(xtem, ytem, (char) (FIRE + PRNG.nextInt(4)));
83  }
84  }
85  }
86 
87  int cov = city.getFireStationCoverage(xpos, ypos);
88  int rate = cov > 100 ? 1 : cov > 20 ? 2 : cov != 0 ? 3 : 10;
89 
90  if(PRNG.nextInt(rate + 1) == 0) {
91  city.setTile(xpos, ypos, (char) (RUBBLE + PRNG.nextInt(4)));
92  }
93  }
94 
98  void doFlood() {
99  final int[] DX = {
100  0, 1, 0, -1
101  };
102  final int[] DY = {
103  -1, 0, 1, 0
104  };
105 
106  if(city.floodCnt != 0) {
107  for(int z = 0; z < 4; z++) {
108  if(PRNG.nextInt(8) == 0) {
109  int xx = xpos + DX[z];
110  int yy = ypos + DY[z];
111  if(city.testBounds(xx, yy)) {
112  int t = city.getTile(xx, yy);
113  if(isCombustible(t) || t == DIRT || (t >= WOODS5 && t < FLOOD)) {
114  if(isZoneCenter(t)) {
115  city.killZone(xx, yy, t);
116  }
117  city.setTile(xx, yy, (char) (FLOOD + PRNG.nextInt(3)));
118  }
119  }
120  }
121  }
122  }
123  else {
124  if(PRNG.nextInt(16) == 0) {
125  city.setTile(xpos, ypos, DIRT);
126  }
127  }
128  }
129 
133  void doRadioactiveTile() {
134  if(PRNG.nextInt(4096) == 0) {
135  // radioactive decay
136  city.setTile(xpos, ypos, DIRT);
137  }
138  }
139 
140  static int[] TRAFFIC_DENSITY_TAB = {
141  ROADBASE, LTRFBASE, HTRFBASE
142  };
143 
147  void doRoad() {
148  int tilePlayerID = Utilities.getPlayerID(rawTile);
149  city.getPlayerInfo(tilePlayerID).roadTotal++;
150 
151  if(city.playerInfo.roadEffect < 30) {
152  // deteriorating roads
153  if(PRNG.nextInt(512) == 0) {
154  if(!isConductive(tile)) {
155  if(city.playerInfo.roadEffect < PRNG.nextInt(32)) {
156  if(isOverWater(tile))
157  city.setTile(xpos, ypos, RIVER);
158  else
159  city.setTile(xpos, ypos, (char) (RUBBLE + PRNG.nextInt(4)));
160  return;
161  }
162  }
163  }
164  }
165 
166  if(!isCombustible(tile)) // bridge
167  {
169  if(doBridge())
170  return;
171  }
172 
173  int tden;
174  if(tile < LTRFBASE)
175  tden = 0;
176  else if(tile < HTRFBASE)
177  tden = 1;
178  else {
180  tden = 2;
181  }
182 
183  int trafficDensity = city.getTrafficDensity(xpos, ypos);
184  int newLevel = trafficDensity < 64 ? 0 : trafficDensity < 192 ? 1 : 2;
185 
186  assert newLevel >= 0 && newLevel < TRAFFIC_DENSITY_TAB.length;
187 
188  if(tden != newLevel) {
189  int z = (((rawTile & LOMASK) - ROADBASE) & 15) + TRAFFIC_DENSITY_TAB[newLevel];
190  z += rawTile & ALLBITS;
191 
192  city.setTile(xpos, ypos, (char) z);
193  }
194  }
195 
199  void doRail() {
200  int tilePlayerID = Utilities.getPlayerID(rawTile);
201  city.getPlayerInfo(tilePlayerID).railTotal++;
202  city.generateTrain(xpos, ypos);
203 
204  if(city.playerInfo.roadEffect < 30) { // deteriorating rail
205  if(PRNG.nextInt(512) == 0) {
206  if(!isConductive(tile)) {
207  if(city.playerInfo.roadEffect < PRNG.nextInt(32)) {
208  if(isOverWater(tile)) {
209  city.setTile(xpos, ypos, RIVER);
210  }
211  else {
212  city.setTile(xpos, ypos, (char) (RUBBLE + PRNG.nextInt(4)));
213  }
214  }
215  }
216  }
217  }
218  }
219 
230  boolean doBridge() {
231  final int HDx[] = {
232  -2, 2, -2, -1, 0, 1, 2
233  };
234  final int HDy[] = {
235  -1, -1, 0, 0, 0, 0, 0
236  };
237  final char HBRTAB[] = {
238  HBRDG1, HBRDG3, HBRDG0, RIVER, BRWH, RIVER, HBRDG2
239  };
240  final char HBRTAB2[] = {
241  RIVER, RIVER, HBRIDGE, HBRIDGE, HBRIDGE, HBRIDGE, HBRIDGE
242  };
243 
244  final int VDx[] = {
245  0, 1, 0, 0, 0, 0, 1
246  };
247  final int VDy[] = {
248  -2, -2, -1, 0, 1, 2, 2
249  };
250  final char VBRTAB[] = {
251  VBRDG0, VBRDG1, RIVER, BRWV, RIVER, VBRDG2, VBRDG3
252  };
253  final char VBRTAB2[] = {
254  VBRIDGE, RIVER, VBRIDGE, VBRIDGE, VBRIDGE, VBRIDGE, RIVER
255  };
256 
257  if(tile == BRWV) {
258  // vertical bridge, open
259  if(PRNG.nextInt(4) == 0 && getBoatDis() > 340 / 16) {
260  // close the bridge
261  applyBridgeChange(VDx, VDy, VBRTAB, VBRTAB2);
262  }
263  return true;
264  }
265  else if(tile == BRWH) {
266  // horizontal bridge, open
267  if(PRNG.nextInt(4) == 0 && getBoatDis() > 340 / 16) {
268  // close the bridge
269  applyBridgeChange(HDx, HDy, HBRTAB, HBRTAB2);
270  }
271  return true;
272  }
273 
274  if(getBoatDis() < 300 / 16 && PRNG.nextInt(8) == 0) {
275  if((rawTile & 1) != 0) {
276  // vertical bridge
277  if(xpos < city.getWidth() - 1) {
278  // look for CHANNEL tile to right of
279  // bridge. the CHANNEL tiles are only
280  // found in the very center of the
281  // river
282  if(city.getTile(xpos + 1, ypos) == CHANNEL) {
283  // vertical bridge, open it up
284  applyBridgeChange(VDx, VDy, VBRTAB2, VBRTAB);
285  return true;
286  }
287  }
288  return false;
289  }
290  else {
291  // horizontal bridge
292  if(ypos > 0) {
293  // look for CHANNEL tile just above
294  // bridge. the CHANNEL tiles are only
295  // found in the very center of the
296  // river
297  if(city.getTile(xpos, ypos - 1) == CHANNEL) {
298  // open it up
299  applyBridgeChange(HDx, HDy, HBRTAB2, HBRTAB);
300  return true;
301  }
302  }
303  return false;
304  }
305  }
306 
307  return false;
308  }
309 
313  private void applyBridgeChange(int[] Dx, int[] Dy, char[] fromTab, char[] toTab) {
314  // FIXME- a closed bridge with traffic on it is not
315  // correctly handled by this subroutine, because the
316  // the tiles representing traffic on a bridge do not match
317  // the expected tile values of fromTab
318 
319  for(int z = 0; z < 7; z++) {
320  int x = xpos + Dx[z];
321  int y = ypos + Dy[z];
322  if(city.testBounds(x, y)) {
323  if((city.getTile(x, y) == fromTab[z]) || (city.getTile(x, y) == CHANNEL)) {
324  city.setTile(x, y, toTab[z]); // TODO: needs player id
325  }
326  }
327  }
328  }
329 
333  int getBoatDis() {
334  int dist = 99999;
335  for(Sprite s : city.sprites) {
336  if(s.isVisible() && s.kind == SpriteKind.SHI) {
337  int x = s.x / 16;
338  int y = s.y / 16;
339  int d = Math.abs(xpos - x) + Math.abs(ypos - y);
340  dist = Math.min(d, dist);
341  }
342  }
343  return dist;
344  }
345 
346  void doExplosion() {
347  // clear AniRubble
348  city.setTile(xpos, ypos, (char) (RUBBLE + PRNG.nextInt(4)));
349  }
350 }