Micropolis
scan.cpp
Go to the documentation of this file.
1 /* scan.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 
77 
78 
79 #include "micropolis.h"
80 
81 
83 
90 static void smoothStationMap(MapShort8 *map)
91 {
92  short x, y, edge;
93  MapShort8 tempMap(*map);
94 
95  for (x = 0; x < tempMap.MAP_W; x++) {
96  for (y = 0; y < tempMap.MAP_H; y++) {
97  edge = 0;
98  if (x > 0) {
99  edge += tempMap.get(x - 1, y);
100  }
101  if (x < tempMap.MAP_W - 1) {
102  edge += tempMap.get(x + 1, y);
103  }
104  if (y > 0) {
105  edge += tempMap.get(x, y - 1);
106  }
107  if (y < tempMap.MAP_H - 1) {
108  edge += tempMap.get(x, y + 1);
109  }
110  edge = tempMap.get(x, y) + edge / 4;
111  map->set(x, y, edge / 2);
112  }
113  }
114 }
115 
121 {
125 
127 
130 }
131 
132 
137 {
138  /* sets: populationDensityMap, , , comRateMap */
139  tempMap1.clear();
140  Quad Xtot = 0;
141  Quad Ytot = 0;
142  Quad Ztot = 0;
143  for (int x = 0; x < WORLD_W; x++) {
144  for (int y = 0; y < WORLD_H; y++) {
145  MapValue mapValue = map[x][y];
146  if (mapValue & ZONEBIT) {
147  MapTile mapTile = mapValue & LOMASK;
148  int pop = getPopulationDensity(Position(x, y), mapTile) * 8;
149  pop = min(pop, 254);
150 
151  tempMap1.worldSet(x, y, (Byte)pop);
152  Xtot += x;
153  Ytot += y;
154  Ztot++;
155  }
156  }
157  }
158 
159  doSmooth1(); // tempMap1 -> tempMap2
160  doSmooth2(); // tempMap2 -> tempMap1
161  doSmooth1(); // tempMap1 -> tempMap2
162 
165 
166  // Copy tempMap2 to populationDensityMap, multiplying by 2
167  Byte *srcMap = tempMap2.getBase();
168  Byte *destMap = populationDensityMap.getBase();
169  for (int i = 0; i < tempMap2.MAP_W * tempMap2.MAP_H; i++) {
170  destMap[i] = srcMap[i] * 2;
171  }
172 
173  computeComRateMap(); /* Compute the comRateMap */
174 
175 
176  // Compute new city center
177  if (Ztot > 0) { /* Find Center of Mass for City */
178  cityCenterX = (short)(Xtot / Ztot);
179  cityCenterY = (short)(Ytot / Ztot);
180  } else {
181  cityCenterX = WORLD_W / 2; /* if pop==0 center of map is city center */
182  cityCenterY = WORLD_H / 2;
183  }
184 
185  // Set flags for updated maps
189 }
190 
191 
199 {
200  int pop;
201 
202  if (tile == FREEZ) {
203  pop = doFreePop(pos);
204  return pop;
205  }
206 
207  if (tile < COMBASE) {
208  pop = getResZonePop(tile);
209  return pop;
210  }
211 
212  if (tile < INDBASE) {
213  pop = getComZonePop(tile) * 8;
214  return pop;
215  }
216 
217  if (tile < PORTBASE) {
218  pop = getIndZonePop(tile) * 8;
219  return pop;
220  }
221 
222  return 0;
223 }
224 
225 
226 /* comefrom: simulate SpecialInit */
227 void Micropolis::pollutionTerrainLandValueScan()
228 {
229  /* Does pollution, terrain, land value */
230  Quad ptot, LVtot;
231  int x, y, z, dis;
232  int pollutionLevel, loc, worldX, worldY, Mx, My, pnum, LVnum, pmax;
233 
234  // tempMap3 is a map of development density, smoothed into terrainMap.
235  tempMap3.clear();
236 
237  LVtot = 0;
238  LVnum = 0;
239 
240  for (x = 0; x < landValueMap.MAP_W; x++) {
241  for (y = 0; y < landValueMap.MAP_H; y++) {
242  pollutionLevel = 0;
243  bool landValueFlag = false;
244  worldX = x * 2;
245  worldY = y * 2;
246 
247  for (Mx = worldX; Mx <= worldX + 1; Mx++) {
248  for (My = worldY; My <= worldY + 1; My++) {
249  loc = (map[Mx][My] & LOMASK);
250  if (loc) {
251  if (loc < RUBBLE) {
252  // Increment terrain memory.
253  Byte value = tempMap3.get(x >>1, y >>1);
254  tempMap3.set(x >>1, y >>1, value + 15);
255  continue;
256  }
257  pollutionLevel += getPollutionValue(loc);
258  if (loc >= ROADBASE) {
259  landValueFlag = true;
260  }
261  }
262  }
263  }
264 
265 /* XXX ??? This might have to do with the radiation tile returning -40.
266  if (pollutionLevel < 0) {
267  pollutionLevel = 250;
268  }
269 */
270 
271  pollutionLevel = min(pollutionLevel, 255);
272  tempMap1.set(x, y, pollutionLevel);
273 
274  if (landValueFlag) { /* LandValue Equation */
275  dis = 34 - getCityCenterDistance(worldX, worldY) / 2;
276  dis = dis <<2;
277  dis += terrainDensityMap.get(x >>1, y >>1);
278  dis -= pollutionDensityMap.get(x, y);
279  if (crimeRateMap.get(x, y) > 190) {
280  dis -= 20;
281  }
282  dis = clamp(dis, 1, 250);
283  landValueMap.set(x, y, dis);
284  LVtot += dis;
285  LVnum++;
286  } else {
287  landValueMap.set(x, y, 0);
288  }
289  }
290  }
291 
292  if (LVnum > 0) {
293  landValueAverage = (short)(LVtot / LVnum);
294  } else {
295  landValueAverage = 0;
296  }
297 
298  doSmooth1(); // tempMap1 -> tempMap2
299  doSmooth2(); // tempMap2 -> tempMap1
300 
301  pmax = 0;
302  pnum = 0;
303  ptot = 0;
304 
305  for (x = 0; x < WORLD_W; x += pollutionDensityMap.MAP_BLOCKSIZE) {
306  for (y = 0; y < WORLD_H; y += pollutionDensityMap.MAP_BLOCKSIZE) {
307  z = tempMap1.worldGet(x, y);
308  pollutionDensityMap.worldSet(x, y, z);
309 
310  if (z) { /* get pollute average */
311  pnum++;
312  ptot += z;
313  /* find max pol for monster */
314  if (z > pmax || (z == pmax && (getRandom16() & 3) == 0)) {
315  pmax = z;
316  pollutionMaxX = x;
317  pollutionMaxY = y;
318  }
319  }
320  }
321  }
322  if (pnum) {
323  pollutionAverage = (short)(ptot / pnum);
324  } else {
325  pollutionAverage = 0;
326  }
327 
328  smoothTerrain();
329 
333 }
334 
335 
342 {
343  if (loc < POWERBASE) {
344 
345  if (loc >= HTRFBASE) {
346  return /* 25 */ 75; /* heavy traf */
347  }
348 
349  if (loc >= LTRFBASE) {
350  return /* 10 */ 50; /* light traf */
351  }
352 
353  if (loc < ROADBASE) {
354 
355  if (loc > FIREBASE) {
356  return /* 60 */ 90;
357  }
358 
359  /* XXX: Why negative pollution from radiation? */
360  if (loc >= RADTILE) {
361  return /* -40 */ 255; /* radioactivity */
362  }
363 
364  }
365  return 0;
366  }
367 
368  if (loc <= LASTIND) {
369  return 0;
370  }
371 
372  if (loc < PORTBASE) {
373  return 50; /* Ind */
374  }
375 
376  if (loc <= LASTPOWERPLANT) {
377  return /* 60 */ 100; /* prt, aprt, cpp */
378  }
379 
380  return 0;
381 }
382 
383 
393 {
394  int xDis, yDis;
395 
396  if (x > cityCenterX) {
397  xDis = x - cityCenterX;
398  } else {
399  xDis = cityCenterX - x;
400  }
401 
402  if (y > cityCenterY) {
403  yDis = y - cityCenterY;
404  } else {
405  yDis = cityCenterY - y;
406  }
407 
408  return min(xDis + yDis, 64);
409 }
410 
411 
414 {
418 
419  Quad totz = 0;
420  int numz = 0;
421  int cmax = 0;
422 
423  for (int x = 0; x < WORLD_W; x += crimeRateMap.MAP_BLOCKSIZE) {
424  for (int y = 0; y < WORLD_H; y += crimeRateMap.MAP_BLOCKSIZE) {
425  int z = landValueMap.worldGet(x, y);
426  if (z > 0) {
427  ++numz;
428  z = 128 - z;
429  z += populationDensityMap.worldGet(x, y);
430  z = min(z, 300);
431  z -= policeStationMap.worldGet(x, y);
432  z = clamp(z, 0, 250);
433  crimeRateMap.worldSet(x, y, (Byte)z);
434  totz += z;
435 
436  // Update new crime hot-spot
437  if (z > cmax || (z == cmax && (getRandom16() & 3) == 0)) {
438  cmax = z;
439  crimeMaxX = x;
440  crimeMaxY = y;
441  }
442 
443  } else {
444  crimeRateMap.worldSet(x, y, 0);
445  }
446  }
447  }
448 
449  if (numz > 0) {
450  crimeAverage = (short)(totz / numz);
451  } else {
452  crimeAverage = 0;
453  }
454 
456 
460 }
461 
462 
463 /* comefrom: pollutionTerrainLandValueScan */
464 void Micropolis::smoothTerrain()
465 {
466  if (donDither & 1) {
467  int x, y = 0, dir = 1;
468  unsigned z = 0;
469 
470  for (x = 0; x < terrainDensityMap.MAP_W; x++) {
471  for (; y != terrainDensityMap.MAP_H && y != -1; y += dir) {
472  z +=
473  tempMap3.get((x == 0) ? x : (x - 1), y) +
474  tempMap3.get((x == (terrainDensityMap.MAP_W - 1)) ? x : (x + 1), y) +
475  tempMap3.get(x, (y == 0) ? (0) : (y - 1)) +
476  tempMap3.get(x, (y == (terrainDensityMap.MAP_H - 1)) ? y : (y + 1)) +
477  (tempMap3.get(x, y) <<2);
478  Byte val = (Byte)(z / 8);
479  terrainDensityMap.set(x, y, val);
480  z &= 0x7;
481  }
482  dir = -dir;
483  y += dir;
484  }
485  } else {
486  short x, y;
487 
488  for (x = 0; x < terrainDensityMap.MAP_W; x++) {
489  for (y = 0; y < terrainDensityMap.MAP_H; y++) {
490  unsigned z = 0;
491  if (x > 0) {
492  z += tempMap3.get(x - 1, y);
493  }
494  if (x < (terrainDensityMap.MAP_W - 1)) {
495  z += tempMap3.get(x + 1, y);
496  }
497  if (y > 0) {
498  z += tempMap3.get(x, y - 1);
499  }
500  if (y < (terrainDensityMap.MAP_H - 1)) {
501  z += tempMap3.get(x, y + 1);
502  }
503  Byte val = (Byte)(z / 4 + tempMap3.get(x, y)) / 2;
504  terrainDensityMap.set(x, y, val);
505  }
506  }
507  }
508 }
509 
516 static void smoothDitherMap(const MapByte2 &srcMap,
517  MapByte2 *destMap,
518  bool ditherFlag)
519 {
520  if (ditherFlag) {
521  int x, y = 0, z = 0, dir = 1;
522 
523  for (x = 0; x < srcMap.MAP_W; x++) {
524  for (; y != srcMap.MAP_H && y != -1; y += dir) {
525  z +=
526  srcMap.get((x == 0) ? x : (x - 1), y) +
527  srcMap.get((x == srcMap.MAP_W - 1) ? x : (x + 1), y) +
528  srcMap.get(x, (y == 0) ? (0) : (y - 1)) +
529  srcMap.get(x, (y == (srcMap.MAP_H - 1)) ? y : (y + 1)) +
530  srcMap.get(x, y);
531  Byte val = (Byte)(z / 4);
532  destMap->set(x, y, val);
533  z &= 3;
534  }
535  dir = -dir;
536  y += dir;
537  }
538  } else {
539  short x, y, z;
540 
541  for (x = 0; x < srcMap.MAP_W; x++) {
542  for (y = 0; y < srcMap.MAP_H; y++) {
543  z = 0;
544  if (x > 0) {
545  z += srcMap.get(x - 1, y);
546  }
547  if (x < srcMap.MAP_W - 1) {
548  z += srcMap.get(x + 1, y);
549  }
550  if (y > 0) {
551  z += srcMap.get(x, y - 1);
552  }
553  if (y < (srcMap.MAP_H - 1)) {
554  z += srcMap.get(x, y + 1);
555  }
556  z = (z + srcMap.get(x, y)) >>2;
557  if (z > 255) {
558  z = 255;
559  }
560  destMap->set(x, y, (Byte)z);
561  }
562  }
563  }
564 }
565 
566 
567 /* Smooth Micropolis::tempMap1 to Micropolis::tempMap2 */
568 void Micropolis::doSmooth1()
569 {
571 }
572 
573 
574 /* Smooth Micropolis::tempMap2 to Micropolis::tempMap1 */
575 void Micropolis::doSmooth2()
576 {
578 }
579 
580 
586 {
587  short x, y, z;
588 
589  for (x = 0; x < comRateMap.MAP_W; x++) {
590  for (y = 0; y < comRateMap.MAP_H; y++) {
591  z = (short)(getCityCenterDistance(x * 8,y * 8) / 2); // 0..32
592  z = z * 4; // 0..128
593  z = 64 - z; // 64..-64
594  comRateMap.set(x, y, z);
595  }
596  }
597 }
598 
599 
DATA worldGet(int x, int y) const
Definition: map_type.h:316
const int MAP_BLOCKSIZE
Definition: map_type.h:121
void set(int x, int y, DATA val)
Definition: map_type.h:248
const int MAP_W
Number of clusters in horizontal direction.
Definition: map_type.h:122
void clear()
Definition: map_type.h:222
const int MAP_H
Number of clusters in vertical direction.
Definition: map_type.h:123
DATA * getBase()
Definition: map_type.h:233
DATA get(int x, int y) const
Definition: map_type.h:265
void worldSet(int x, int y, DATA val)
Definition: map_type.h:297
short crimeMaxX
X coordinate of most criminal area. Not used.
Definition: micropolis.h:2050
MapShort8 policeStationEffectMap
Definition: micropolis.h:1322
short getResZonePop(MapTile mapTile)
Definition: zone.cpp:709
short doFreePop(const Position &pos)
Definition: zone.cpp:407
short crimeAverage
Definition: micropolis.h:1063
short getIndZonePop(MapTile tile)
Definition: zone.cpp:999
MapByte2 crimeRateMap
Crime rate map.
Definition: micropolis.h:1248
MapByte4 terrainDensityMap
Definition: micropolis.h:1255
short newMapFlags[MAP_TYPE_COUNT]
Definition: micropolis.h:2042
MapShort8 comRateMap
Definition: micropolis.h:1330
short cityCenterY
Y coordinate of city center.
Definition: micropolis.h:2045
MapShort8 fireStationEffectMap
Definition: micropolis.h:1306
short pollutionMaxY
Y coordinate of most polluted area.
Definition: micropolis.h:2048
void fireAnalysis()
Definition: scan.cpp:120
int getRandom16()
Definition: random.cpp:130
MapByte4 tempMap3
Definition: micropolis.h:1276
short pollutionMaxX
X coordinate of most polluted area.
Definition: micropolis.h:2047
short pollutionAverage
Definition: micropolis.h:1072
Quad donDither
Definition: micropolis.h:2056
int getPollutionValue(int loc)
Definition: scan.cpp:341
void computeComRateMap()
Definition: scan.cpp:585
short getComZonePop(MapTile tile)
Definition: zone.cpp:864
MapShort8 fireStationMap
Definition: micropolis.h:1301
short cityCenterX
X coordinate of city center.
Definition: micropolis.h:2044
short landValueAverage
Definition: micropolis.h:1080
unsigned short * map[WORLD_W]
Definition: micropolis.h:1120
void crimeScan()
Definition: scan.cpp:413
MapShort8 policeStationMap
Definition: micropolis.h:1317
MapByte2 pollutionDensityMap
Pollution density map.
Definition: micropolis.h:1246
MapByte2 tempMap1
Definition: micropolis.h:1262
int getCityCenterDistance(int x, int y)
Definition: scan.cpp:392
int getPopulationDensity(const Position &pos, MapTile tile)
Definition: scan.cpp:198
MapByte2 populationDensityMap
Population density map.
Definition: micropolis.h:1244
MapByte2 landValueMap
Land value map.
Definition: micropolis.h:1247
MapByte2 tempMap2
Definition: micropolis.h:1269
short crimeMaxY
Y coordinate of most criminal area. Not used.
Definition: micropolis.h:2051
void populationDensityScan()
Definition: scan.cpp:136
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.
@ MAP_TYPE_POLLUTION
Pollution.
Definition: micropolis.h:316
@ MAP_TYPE_POPULATION_DENSITY
Population density.
Definition: micropolis.h:313
@ MAP_TYPE_DYNAMIC
Dynamic filter.
Definition: micropolis.h:321
@ MAP_TYPE_LAND_VALUE
Land value.
Definition: micropolis.h:318
@ MAP_TYPE_FIRE_RADIUS
Fire station coverage radius.
Definition: micropolis.h:319
@ MAP_TYPE_CRIME
Crime rate.
Definition: micropolis.h:317
@ MAP_TYPE_RATE_OF_GROWTH
Rate of growth.
Definition: micropolis.h:314
@ MAP_TYPE_POLICE_RADIUS
Police station coverage radius.
Definition: micropolis.h:320
@ LASTPOWERPLANT
Last tile of coal power plant.
Definition: micropolis.h:581
@ HTRFBASE
First tile with high traffic.
Definition: micropolis.h:452
@ PORTBASE
Top-left tile of the seaport.
Definition: micropolis.h:567
@ RADTILE
Radio-active contaminated tile.
Definition: micropolis.h:415
@ LTRFBASE
First tile with low traffic.
Definition: micropolis.h:443
@ INDBASE
Top-left tile of empty industrial zone.
Definition: micropolis.h:536
@ LASTIND
Last tile of empty industrial zone.
Definition: micropolis.h:538
static T min(const T a, const T b)
Definition: micropolis.h:783
static T clamp(const T val, const T lower, const T upper)
Definition: micropolis.h:808
static void smoothStationMap(MapShort8 *map)
Definition: scan.cpp:90
static void smoothDitherMap(const MapByte2 &srcMap, MapByte2 *destMap, bool ditherFlag)
Definition: scan.cpp:516
unsigned short MapTile
Definition: tool.h:108
@ LOMASK
Mask for the Tiles part of the tile.
Definition: tool.h:129
@ ZONEBIT
bit 10, tile is the center tile of the zone.
Definition: tool.h:125
unsigned short MapValue
Definition: tool.h:94