Micropolis
traffic.cpp
Go to the documentation of this file.
1 /* traffic.cpp
2  *
3  * Micropolis, Unix Version. This game was released for the Unix platform
4  * in or about 1990 and has been modified for inclusion in the One Laptop
5  * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
6  * you need assistance with this program, you may contact:
7  * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or (at
12  * your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details. You should have received a
18  * copy of the GNU General Public License along with this program. If
19  * not, see <http://www.gnu.org/licenses/>.
20  *
21  * ADDITIONAL TERMS per GNU GPL Section 7
22  *
23  * No trademark or publicity rights are granted. This license does NOT
24  * give you any right, title or interest in the trademark SimCity or any
25  * other Electronic Arts trademark. You may not distribute any
26  * modification of this program using the trademark SimCity or claim any
27  * affliation or association with Electronic Arts Inc. or its employees.
28  *
29  * Any propagation or conveyance of this program must include this
30  * copyright notice and these terms.
31  *
32  * If you convey this program (or any modifications of it) and assume
33  * contractual liability for the program to recipients of it, you agree
34  * to indemnify Electronic Arts for any liability that those contractual
35  * assumptions impose on Electronic Arts.
36  *
37  * You may not misrepresent the origins of this program; modified
38  * versions of the program must be marked as such and not identified as
39  * the original program.
40  *
41  * This disclaimer supplements the one included in the General Public
42  * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
43  * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
44  * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF
45  * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS
46  * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
47  * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
48  * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
49  * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
50  * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST
51  * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
52  * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
53  * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
54  * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
55  * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
56  * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME
57  * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
58  * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
59  * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
60  * NOT APPLY TO YOU.
61  */
62 
79 
80 
81 #include "micropolis.h"
82 
83 
85 
86 
95 short Micropolis::makeTrafficAt(int x, int y, ZoneType dest)
96 {
97  Position pos;
98  pos.posX = x;
99  pos.posY = y;
100 
101  if (tryDrive(pos, dest)) { /* attempt to drive somewhere */
102  addToTrafficDensityMap(); /* if sucessful, inc trafdensity */
103  return 1; /* traffic passed */
104  }
105 
106  return 0; /* traffic failed */
107 }
108 
109 
118 short Micropolis::makeTraffic(int x, int y, ZoneType dest)
119 {
120  Position startPos;
121  startPos.posX = x;
122  startPos.posY = y;
123  return makeTraffic(startPos, dest);
124 }
125 
126 
134 short Micropolis::makeTraffic(const Position &startPos, ZoneType dest)
135 {
136  curMapStackPointer = 0; // Clear position stack
137 
138  Position pos(startPos);
139 
140 #if 0
141  if ((!getRandom(2)) && findPerimeterTelecom(pos)) {
142  /* printf("Telecom!\n"); */
143  return 1;
144  }
145 #endif
146 
147  if (findPerimeterRoad(&pos)) { /* look for road on zone perimeter */
148 
149  if (tryDrive(pos, dest)) { /* attempt to drive somewhere */
150  addToTrafficDensityMap(); /* if sucessful, inc trafdensity */
151  return 1; /* traffic passed */
152  }
153 
154  return 0; /* traffic failed */
155  } else {
156  return -1; /* no road found */
157  }
158 }
159 
160 
166 {
167  /* For each saved position of the drive */
168  while (curMapStackPointer > 0) {
169 
170  Position pos = pullPos();
171  if (pos.testBounds()) {
172 
173  MapTile tile = map[pos.posX][pos.posY] & LOMASK;
174 
175  if (tile >= ROADBASE && tile < POWERBASE) {
176  SimSprite *sprite;
177 
178  // Update traffic density.
179  int traffic = trafficDensityMap.worldGet(pos.posX, pos.posY);
180  traffic += 50;
181  traffic = min(traffic, 240);
182  trafficDensityMap.worldSet(pos.posX, pos.posY, (Byte)traffic);
183 
184  // Check for heavy traffic.
185  if (traffic >= 240 && getRandom(5) == 0) {
186 
187  trafMaxX = pos.posX;
188  trafMaxY = pos.posY;
189 
190  /* Direct helicopter towards heavy traffic */
191  sprite = getSprite(SPRITE_HELICOPTER);
192  if (sprite != NULL && sprite->control == -1) {
193 
194  sprite->destX = trafMaxX * 16;
195  sprite->destY = trafMaxY * 16;
196 
197  }
198  }
199  }
200  }
201  }
202 }
203 
204 
211 {
215 }
216 
217 
224 {
225  assert(curMapStackPointer > 0);
227  return curMapStackXY[curMapStackPointer + 1];
228 }
229 
230 
240 {
241  /* look for road on edges of zone */
242  static const short PerimX[12] = {-1, 0, 1, 2, 2, 2, 1, 0,-1,-2,-2,-2};
243  static const short PerimY[12] = {-2,-2,-2,-1, 0, 1, 2, 2, 2, 1, 0,-1};
244  short tx, ty;
245 
246  for (short z = 0; z < 12; z++) {
247 
248  tx = pos->posX + PerimX[z];
249  ty = pos->posY + PerimY[z];
250 
251  if (testBounds(tx, ty)) {
252 
253  if (roadTest(map[tx][ty])) {
254 
255  pos->posX = tx;
256  pos->posY = ty;
257 
258  return true;
259  }
260  }
261  }
262 
263  return false;
264 }
265 
266 
275 {
276  /* look for telecom on edges of zone */
277  static const short PerimX[12] = {-1, 0, 1, 2, 2, 2, 1, 0,-1,-2,-2,-2};
278  static const short PerimY[12] = {-2,-2,-2,-1, 0, 1, 2, 2, 2, 1, 0,-1};
279  short tx, ty, tile;
280 
281  for (short z = 0; z < 12; z++) {
282 
283  tx = pos.posX + PerimX[z];
284  ty = pos.posX + PerimY[z];
285 
286  if (testBounds(tx, ty)) {
287 
288  tile = map[tx][ty] & LOMASK;
289  if (tile >= TELEBASE && tile <= TELELAST) {
290  return true;
291  }
292  }
293  }
294 
295  return false;
296 }
297 
298 
309 bool Micropolis::tryDrive(const Position &startPos, ZoneType destZone)
310 {
311  Direction2 dirLast = DIR2_INVALID;
312  Position drivePos(startPos);
313 
314  /* Maximum distance to try */
315  for (short dist = 0; dist < MAX_TRAFFIC_DISTANCE; dist++) {
316 
317  Direction2 dir = tryGo(drivePos, dirLast);
318  if (dir != DIR2_INVALID) { // we found a road
319  drivePos.move(dir);
320  dirLast = rotate180(dir);
321 
322  /* Save pos every other move.
323  * This also relates to
324  * Micropolis::trafficDensityMap::MAP_BLOCKSIZE
325  */
326  if (dist & 1) {
327  pushPos(drivePos);
328  }
329 
330  if (driveDone(drivePos, destZone)) { // if destination is reached
331  return true; /* pass */
332  }
333 
334  } else {
335 
336  if (curMapStackPointer > 0) { /* dead end, backup */
338  dist += 3;
339  } else {
340  return false; /* give up at start */
341  }
342 
343  }
344  }
345 
346  return false; /* gone MAX_TRAFFIC_DISTANCE */
347 }
348 
349 
357 {
358  Direction2 directions[4];
359 
360  // Find connections from current position.
361  Direction2 dir = DIR2_NORTH;
362  int count = 0;
363  for (int i = 0; i < 4; i++) {
364  if (dir != dirLast && roadTest(getTileFromMap(pos, dir, DIRT))) {
365  // found a road in an allowed direction
366  directions[i] = dir;
367  count++;
368  } else {
369  directions[i] = DIR2_INVALID;
370  }
371 
372  dir = rotate90(dir);
373  }
374 
375  if (count == 0) { // dead end
376  return DIR2_INVALID;
377  }
378 
379  // We have at least one way to go.
380 
381  if (count == 1) { // only one solution
382  for (int i = 0; i < 4; i++) {
383  if (directions[i] != DIR2_INVALID) {
384  return directions[i];
385  }
386  }
387  }
388 
389  // more than one choice, draw a random number.
390  int i = getRandom16() & 3;
391  while (directions[i] == DIR2_INVALID) {
392  i = (i + 1) & 3;
393  }
394  return directions[i];
395 }
396 
397 
408  Direction2 dir, MapTile defaultTile)
409 {
410  switch (dir) {
411 
412  case DIR2_NORTH:
413  if (pos.posY > 0) {
414  return map[pos.posX][pos.posY - 1] & LOMASK;
415  }
416 
417  return defaultTile;
418 
419  case DIR2_EAST:
420  if (pos.posX < WORLD_W - 1) {
421  return map[pos.posX + 1][pos.posY] & LOMASK;
422  }
423 
424  return defaultTile;
425 
426  case DIR2_SOUTH:
427  if (pos.posY < WORLD_H - 1) {
428  return map[pos.posX][pos.posY + 1] & LOMASK;
429  }
430 
431  return defaultTile;
432 
433  case DIR2_WEST:
434  if (pos.posX > 0) {
435  return map[pos.posX - 1][pos.posY] & LOMASK;
436  }
437 
438  return defaultTile;
439 
440  default:
441  return defaultTile;
442 
443  }
444 }
445 
446 
453 bool Micropolis::driveDone(const Position &pos, ZoneType destZone)
454 {
455  // FIXME: Use macros to determine the zone type: residential, commercial or industrial.
456  /* commercial, industrial, residential destinations */
457  static const MapTile targetLow[3] = {COMBASE, LHTHR, LHTHR};
458  static const MapTile targetHigh[3] = {NUCLEAR, PORT, COMBASE};
459 
460  assert(ZT_NUM_DESTINATIONS == LENGTH_OF(targetLow));
461  assert(ZT_NUM_DESTINATIONS == LENGTH_OF(targetHigh));
462 
463  MapTile l = targetLow[destZone]; // Lowest acceptable tile value
464  MapTile h = targetHigh[destZone]; // Highest acceptable tile value
465 
466  if (pos.posY > 0) {
467  MapTile z = map[pos.posX][pos.posY - 1] & LOMASK;
468  if (z >= l && z <= h) {
469  return true;
470  }
471  }
472 
473  if (pos.posX < (WORLD_W - 1)) {
474  MapTile z = map[pos.posX + 1][pos.posY] & LOMASK;
475  if (z >= l && z <= h) {
476  return true;
477  }
478  }
479 
480  if (pos.posY < (WORLD_H - 1)) {
481  MapTile z = map[pos.posX][pos.posY + 1] & LOMASK;
482  if (z >= l && z <= h) {
483  return true;
484  }
485  }
486 
487  if (pos.posX > 0) {
488  MapTile z = map[pos.posX - 1][pos.posY] & LOMASK;
489  if (z >= l && z <= h) {
490  return true;
491  }
492  }
493 
494  return false;
495 }
496 
497 
504 {
505  MapTile tile = mv & LOMASK;
506 
507  if (tile < ROADBASE || tile > LASTRAIL) {
508  return false;
509  }
510 
511  if (tile >= POWERBASE && tile < LASTPOWER) {
512  return false;
513  }
514 
515  return true;
516 }
517 
518 
DATA worldGet(int x, int y) const
Definition: map_type.h:316
void worldSet(int x, int y, DATA val)
Definition: map_type.h:297
bool driveDone(const Position &pos, ZoneType destZone)
Definition: traffic.cpp:453
Position pullPos()
Definition: traffic.cpp:223
bool findPerimeterTelecom(const Position &pos)
Definition: traffic.cpp:274
bool tryDrive(const Position &startPos, ZoneType destZone)
Definition: traffic.cpp:309
void pushPos(const Position &pos)
Definition: traffic.cpp:210
MapTile getTileFromMap(const Position &pos, Direction2 dir, MapTile defaultTile)
Definition: traffic.cpp:407
short makeTraffic(int x, int y, ZoneType dest)
Definition: traffic.cpp:118
short getRandom(short range)
Definition: random.cpp:110
Position curMapStackXY[MAX_TRAFFIC_DISTANCE+1]
Position stack.
Definition: micropolis.h:2603
int getRandom16()
Definition: random.cpp:130
short trafMaxY
Y coordinate of a position with heavy traffic.
Definition: micropolis.h:2606
short trafMaxX
X coordinate of a position with heavy traffic.
Definition: micropolis.h:2605
SimSprite * getSprite(int type)
Definition: sprite.cpp:338
unsigned short * map[WORLD_W]
Definition: micropolis.h:1120
short makeTrafficAt(int x, int y, ZoneType dest)
Definition: traffic.cpp:95
Direction2 tryGo(const Position &pos, Direction2 dirLast)
Definition: traffic.cpp:356
MapByte2 trafficDensityMap
Traffic density map.
Definition: micropolis.h:1245
static bool testBounds(int wx, int wy)
Definition: micropolis.h:2378
void addToTrafficDensityMap()
Definition: traffic.cpp:165
short curMapStackPointer
Definition: micropolis.h:2602
bool roadTest(MapValue tile)
Definition: traffic.cpp:503
bool findPerimeterRoad(Position *pos)
Definition: traffic.cpp:239
int posY
Vertical coordnate of the position.
Definition: position.h:169
int posX
Horizontal coordinate of the position.
Definition: position.h:168
bool move(Direction2 dir)
Definition: position.cpp:161
bool testBounds()
Definition: position.h:177
int destX
Destination X coordinate of the sprite.
Definition: micropolis.h:901
int destY
Destination Y coordinate of the sprite.
Definition: micropolis.h:902
static const int WORLD_H
Definition: map_type.h:95
static const int WORLD_W
Definition: map_type.h:90
Header file for Micropolis game engine.
ZoneType
Definition: micropolis.h:717
@ ZT_NUM_DESTINATIONS
Number of available zones.
Definition: micropolis.h:722
@ NUCLEAR
'Center' tile nuclear power plant.
Definition: micropolis.h:604
@ PORT
Center tile of the seaport.
Definition: micropolis.h:568
@ DIRT
Clear tile.
Definition: micropolis.h:382
static T min(const T a, const T b)
Definition: micropolis.h:783
@ SPRITE_HELICOPTER
Helicopter sprite.
Definition: micropolis.h:332
#define LENGTH_OF(array)
Definition: micropolis.h:842
static const int MAX_TRAFFIC_DISTANCE
Definition: micropolis.h:248
static Direction2 rotate180(Direction2 dir)
Definition: position.h:147
Direction2
Definition: position.h:86
@ DIR2_WEST
Direction pointing west.
Definition: position.h:94
@ DIR2_INVALID
Invalid direction.
Definition: position.h:87
@ DIR2_SOUTH
Direction pointing south.
Definition: position.h:92
@ DIR2_EAST
Direction pointing east.
Definition: position.h:90
@ DIR2_NORTH
Direction pointing north.
Definition: position.h:88
static Direction2 rotate90(Direction2 dir)
Definition: position.h:137
unsigned short MapTile
Definition: tool.h:108
@ LOMASK
Mask for the Tiles part of the tile.
Definition: tool.h:129
unsigned short MapValue
Definition: tool.h:94