1 /* terragen.c: Terrain generator
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.
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.
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/>.
21 * ADDITIONAL TERMS per GNU GPL Section 7
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.
29 * Any propagation or conveyance of this program must include this
30 * copyright notice and these terms.
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.
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.
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
63 /* Micropolis simulator code. Copyright 1988, 1989 Maxis, Will Wright */
64 /* modified by Paul Schmidt 10-89 to implement terrain editor quickly... - rax */
66 #include "..\sim\sim.h"
80 #define WATER_LOW 2 /* range for water */
82 #define WOODS_LOW 21 /* range for woods */
85 static int XStart
, YStart
;
86 static int Dir
, LastDir
;
88 /* trash values for GRand() */
89 static int GRanArray
[5] = { 1018,4521,202,419,3 };
91 extern int treeLevel
; /* level for tree creation (terra.c) */
92 extern int lakeLevel
; /* level for lake creation (terra.c) */
93 extern int curvLevel
; /* level for river curviness (terra.c) */
99 for (x
=0; x
<WORLD_X
; x
++)
100 for (y
=0; y
<WORLD_Y
; y
++)
104 rax_WaterEdges() /* set water edges */
106 register int x
,y
; /* temporary counters */
108 for(x
=0;x
< WORLD_X
;x
++) {
109 for(y
=0;y
< WORLD_Y
;y
++) {
110 if((Map
[x
][y
]&LOMASK
) >= WATER_LOW
&& (Map
[x
][y
]&LOMASK
) <= WATER_HIGH
) { /* if water */
112 if((Map
[x
-1][y
]&LOMASK
) < WATER_LOW
|| (Map
[x
-1][y
]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
117 if((Map
[x
+1][y
]&LOMASK
) < WATER_LOW
|| (Map
[x
+1][y
]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
122 if((Map
[x
][y
-1]&LOMASK
) < WATER_LOW
|| (Map
[x
][y
-1]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
127 if((Map
[x
][y
+1]&LOMASK
) < WATER_LOW
|| (Map
[x
][y
+1]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
129 Map
[x
][y
]=REDGE
; /* set river edge */
136 for(x
=0;x
< WORLD_X
;x
++) {
137 for(y
=0;y
< WORLD_Y
;y
++) {
138 if((Map
[x
][y
]&LOMASK
) != CHANNEL
&& (Map
[x
][y
]&LOMASK
) >= WATER_LOW
&& (Map
[x
][y
]&LOMASK
) <= WATER_HIGH
) { /* if water which is not a channel */
140 if((Map
[x
-1][y
]&LOMASK
) < WATER_LOW
|| (Map
[x
-1][y
]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
145 if((Map
[x
+1][y
]&LOMASK
) < WATER_LOW
|| (Map
[x
+1][y
]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
150 if((Map
[x
][y
-1]&LOMASK
) < WATER_LOW
|| (Map
[x
][y
-1]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
155 if((Map
[x
][y
+1]&LOMASK
) < WATER_LOW
|| (Map
[x
][y
+1]&LOMASK
) > WATER_HIGH
) { /* if nearest object is not water */
159 Map
[x
][y
]=RIVER
; /* make it a river */
163 for(x
=0;x
< WORLD_X
;x
++) {
164 for(y
=0;y
< WORLD_Y
;y
++) {
165 if((Map
[x
][y
]&LOMASK
) >= WOODS_LOW
&& (Map
[x
][y
]&LOMASK
) <= WOODS_HIGH
) { /* if woods */
167 if(Map
[x
-1][y
] == RIVER
|| Map
[x
-1][y
] == CHANNEL
) { /* if nearest object is water */
168 Map
[x
][y
]=REDGE
; /* make it water's edge */
173 if(Map
[x
+1][y
] == RIVER
|| Map
[x
+1][y
] == CHANNEL
) { /* if nearest object is water */
174 Map
[x
][y
]=REDGE
; /* make it water's edge */
179 if(Map
[x
][y
-1] == RIVER
|| Map
[x
][y
-1] == CHANNEL
) { /* if nearest object is water */
180 Map
[x
][y
]=REDGE
; /* make it water's edge */
185 if(Map
[x
][y
+1] == RIVER
|| Map
[x
][y
+1] == CHANNEL
) { /* if nearest object is water */
186 Map
[x
][y
]=REDGE
; /* make it water's edge */
201 for (x
=0; x
<WORLD_X
; x
++)
202 for (y
=0; y
<WORLD_Y
; y
++)
204 for (x
=5; x
<WORLD_X
-5; x
++)
205 for (y
=5; y
<WORLD_Y
-5; y
++)
207 for (x
=0; x
<WORLD_X
-5; x
+=2) {
209 MapY
= rax_EGRand(RADIUS
);
211 MapY
= 90-rax_EGRand(RADIUS
);
218 for (y
=0; y
<WORLD_Y
-5; y
+=2) {
220 MapX
= rax_EGRand(RADIUS
);
222 MapX
= 110-rax_EGRand(RADIUS
);
233 int Lim1
, Lim2
, t
, z
;
236 /* Lim1 = rax_GRand(10); /**/
238 for (t
= 0; t
< Lim1
; t
++) {
239 x
= rax_GRand(99) + 10;
240 y
= rax_GRand(80) + 10;
241 Lim2
= rax_GRand(12)+2;
242 for (z
= 0; z
< Lim2
; z
++) {
243 MapX
= x
- 6 + rax_GRand(12);
244 MapY
= y
- 6 + rax_GRand(12);
245 if (rax_GRand(4)) rax_SRivPlop();
253 XStart
= 40 + rax_GRand(40);
254 YStart
= 33 + rax_GRand(33);
263 for(x
=0;x
< (treeLevel
*3);x
++) {
266 rax_TreeSplash(xloc
,yloc
);
272 rax_TreeSplash(xloc
,yloc
)
276 register int xoff
, yoff
,z
;
278 Dis
= rax_GRand(100+(treeLevel
*2))+50;
281 for (z
=0; z
<Dis
; z
++) {
284 if (!(rax_TestBounds(MapX
,MapY
))) return;
285 if (Map
[MapX
][MapY
] == 0) Map
[MapX
][MapY
] = WOODS
+ BLN
;
291 static int DX
[4] = {-1, 0, 1, 0};
292 static int DY
[4] = { 0, 1, 0,-1};
293 static int REdTab
[16] = { 13+BL
,13+BL
,17+BL
,15+BL
,5+BL
,2,19+BL
,
294 17+BL
,9+BL
,11+BL
,2,13+BL
,7+BL
,9+BL
,5+BL
,2};
295 int bitindex
, z
,Xtem
,Ytem
;
296 register int temp
,MapX
,MapY
;
298 for (MapX
= 0; MapX
< WORLD_X
; MapX
++)
299 for (MapY
= 0; MapY
< WORLD_Y
; MapY
++)
300 if (Map
[MapX
][MapY
] == REDGE
) {
302 for (z
=0; z
<4; z
++) {
303 bitindex
= bitindex
<< 1;
306 if (rax_TestBounds(Xtem
, Ytem
))
307 /* if(Map[Xtem][Ytem]) bitindex++; /* original code */
308 if((Map
[Xtem
][Ytem
]&LOMASK
) && ((Map
[Xtem
][Ytem
]&LOMASK
) < WOODS_LOW
|| (Map
[Xtem
][Ytem
]&LOMASK
) > WOODS_HIGH
)) bitindex
++; /* new code - rax */
310 temp
= REdTab
[bitindex
& 15];
311 if ((temp
!= 2) && (rax_GRand(1))) temp
++;
312 Map
[MapX
][MapY
] = temp
;
316 IsTree(cell
) /* return TRUE or FALSE if cell value is a tree cell */
319 if((cell
&LOMASK
) >= WOODS_LOW
&& (cell
&LOMASK
) <= WOODS_HIGH
) return TRUE
; else return FALSE
;
324 static int DX
[4] = {-1, 0, 1, 0};
325 static int DY
[4] = { 0, 1, 0,-1};
326 static int TEdTab
[16] = {0,0,0,34,0,0,36,35,0,32,0,33,30,31,29,37};
327 int bitindex
, z
,Xtem
,Ytem
;
328 register int temp
,MapX
,MapY
;
330 for (MapX
= 0; MapX
< WORLD_X
; MapX
++)
331 for (MapY
= 0; MapY
< WORLD_Y
; MapY
++)
332 if (IsTree(Map
[MapX
][MapY
])) {
334 for (z
=0; z
<4; z
++) {
335 bitindex
= bitindex
<< 1;
338 if (rax_TestBounds(Xtem
, Ytem
))
339 if(IsTree(Map
[Xtem
][Ytem
])) bitindex
++;
342 temp
= TEdTab
[bitindex
& 15];
347 Map
[MapX
][MapY
] = temp
+BLN
;
349 else Map
[MapX
][MapY
] = temp
;
355 LastDir
= rax_GRand(3);
360 LastDir
= LastDir
^ 4;
365 LastDir
= rax_GRand(3);
371 while(rax_TestBounds (MapX
+4, MapY
+4)) {
373 if(rax_GRand(curvLevel
+10) < 10) {
376 if(rax_GRand(curvLevel
+100) > 90) {
379 if(rax_GRand(curvLevel
+100) > 90) {
389 while(rax_TestBounds (MapX
+3, MapY
+3)) {
391 if(rax_GRand(curvLevel
+10) < 10) {
394 if(rax_GRand(curvLevel
+100) > 90) {
397 if(rax_GRand(curvLevel
+100) > 90) {
408 static int DirTab
[2][8] ={ { 0, 1, 1, 1, 0, -1, -1, -1},
409 {-1,-1, 0, 1, 1, 1, 0, -1} };
411 MapX
+= DirTab
[0][dir
];
412 MapY
+= DirTab
[1][dir
];
420 static int BRMatrix
[9][9] ={
429 {0,0,0,3,3,3,0,0,0} };
434 rax_PutOnMap (BRMatrix
[y
][x
], x
, y
);
439 static int SRMatrix
[6][6] ={
450 rax_PutOnMap (SRMatrix
[y
][x
], x
, y
);
453 rax_PutOnMap (Mchar
, Xoff
, Yoff
)
454 int Mchar
, Xoff
, Yoff
;
456 register int Xloc
, Yloc
, temp
;
458 if (Mchar
== 0) return;
461 if (rax_TestBounds (Xloc
, Yloc
) == FALSE
) return (FALSE
);
462 if (temp
= Map
[Xloc
][Yloc
]) {
465 if (Mchar
!= CHANNEL
)
467 if (temp
== CHANNEL
) return (FALSE
);
469 Map
[Xloc
][Yloc
] = Mchar
;
475 if ((( x
>= 0) && (x
< WORLD_X
)) && (( y
>= 0) && (y
< WORLD_Y
)))
489 if (z
< x
) return(z
);
493 #define RANMASK 32767
495 rax_GRand(range
) /* stupid but works */
498 register x
, newv
, divisor
;
500 divisor
= RANMASK
/ (range
+1);
503 newv
+= (GRanArray
[x
] = GRanArray
[x
-1]);
505 x
= (newv
& RANMASK
) / divisor
;
506 if (x
> range
) return(range
);