1 /* ---------------------------------------------------------------------- *
2  * engine.c
3  * This file is part of lincity.
4  * Lincity is copyright (c) I J Peters 1995-1997, (c) Greg Sharp 1997-2001.
5  * ---------------------------------------------------------------------- */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include "common.h"
10 #include "lctypes.h"
11 #include "lin-city.h"
12 #include "engine.h"
13 #include "engglobs.h"
14 #include "cliglobs.h"
15 #include "simulate.h"
16 #include "lcintl.h"
17 #include "power.h"
18 #include "mouse.h"
19 #include "module_buttons.h"
20 #include "pbar.h"
21 #include "stats.h"
22 #include "screen.h"
23 #include "dialbox.h"
24 #include "modules.h"
25 #include <mps.h>
26 
27 extern int selected_type_cost;
28 
29 int
adjust_money(int value)30 adjust_money(int value)
31 {
32     total_money += value;
33     print_total_money();
34     mps_update();
35     update_pbar (PMONEY, total_money, 0);
36     refresh_pbars(); /* This could be more specific */
37     return total_money;
38 }
39 
40 int is_real_river (int x, int y);
41 
42 int
no_credit_build(int selected_group)43 no_credit_build (int selected_group)
44 {
45   if (total_money >= 0)
46     return (0);
47 
48 #ifdef GROUP_POWER_SOURCE_NO_CREDIT
49   if (selected_group == GROUP_POWER_SOURCE) {
50     return (1);
51   }
52 #endif
53 #ifdef GROUP_UNIVERSITY_NO_CREDIT
54   if (selected_group == GROUP_UNIVERSITY) {
55     return (1);
56   }
57 #endif
58 #ifdef GROUP_PARKLAND_NO_CREDIT
59   if (selected_group == GROUP_PARKLAND) {
60     return (1);
61   }
62 #endif
63 #ifdef GROUP_RECYCLE_NO_CREDIT
64   if (selected_group == GROUP_RECYCLE) {
65     return (1);
66   }
67 #endif
68 #ifdef GROUP_ROCKET
69   if (selected_group == GROUP_ROCKET) {
70     return (1);
71   }
72 #endif
73 
74   if (main_groups[selected_group].no_credit == TRUE ) {
75     return (1);
76   }
77   return (0);
78 }
79 
80 int
place_item(int x,int y,short type)81 place_item (int x, int y, short type)
82 {
83     int group;
84     int size;
85 
86     group = get_group_of_type(type);
87     if (group < 0) return -1;
88 
89     size = main_groups[group].size;
90 
91     /* You can't build because credit not available. */
92     if (no_credit_build (group) != 0) {
93 	return -1;
94     }
95 
96     /* Not enough slots in the substation array */
97 
98     switch (group) {
99     case GROUP_SUBSTATION:
100     case GROUP_WINDMILL:
101     {
102 	if (add_a_substation (x, y) == 0)
103 	    return -3;
104     } break;
105     case GROUP_PORT:
106     {
107 	if (is_real_river (x + 4, y) != 1
108 	    || is_real_river (x + 4, y + 1) != 1
109 	    || is_real_river (x + 4, y + 2) != 1
110 	    || is_real_river (x + 4, y + 3) != 1) {
111 	    return -2;
112 	}
113     } break;
114     case GROUP_COMMUNE:
115     {
116 	numof_communes++;
117     } break;
118     case GROUP_MARKET:
119     {
120 	/* Test for enough slots in the market array */
121 	if (add_a_market (x, y) == 0)
122 	    return -3;
123 	MP_INFO(x,y).flags += (FLAG_MB_FOOD | FLAG_MB_JOBS
124 			       | FLAG_MB_COAL | FLAG_MB_ORE | FLAG_MB_STEEL
125 			       | FLAG_MB_GOODS | FLAG_MS_FOOD | FLAG_MS_JOBS
126 			       | FLAG_MS_COAL | FLAG_MS_GOODS | FLAG_MS_ORE
127 			       | FLAG_MS_STEEL);
128     } break;
129     case GROUP_TIP:
130     {
131 	/* Don't build a tip if there has already been one.  If we succeed,
132 	   mark the spot permanently by "doubling" the ore reserve */
133 	int i,j;
134 	int prev_tip = 0;
135 	for (i=0;i<3;i++) {
136 	    for (j=0;j<3;j++) {
137 		if (MP_INFO(x+i,y+j).ore_reserve > ORE_RESERVE) {
138 		    prev_tip = 1;
139 		    break;
140 		}
141 	    }
142 	}
143 	if (prev_tip) {
144 	    dialog_box(red(12),3,
145 		       0,0,_("You can't build a tip here"),
146 		       0,0,_("This area was once a landfill"),
147 		       2,' ',_("OK"));
148 	    return -4;
149 	} else {
150 	    for (i=0;i<3;i++) {
151 		for (j=0;j<3;j++) {
152 		    MP_INFO(x+i,y+j).ore_reserve = ORE_RESERVE * 2;
153 		}
154 	    }
155 	}
156     } break;
157     case GROUP_OREMINE:
158     {
159 	/* Don't allow new mines on old mines or old tips */
160 	/* GCS: mines over old mines is OK if there is enough remaining
161 	        ore, as is the case when there is partial overlap. */
162 	int i,j;
163 	int prev_tip = 0;
164 	int total_ore = 0;
165 	for (i=0;i<3;i++) {
166 	    for (j=0;j<3;j++) {
167 		total_ore += MP_INFO(x+i,y+j).ore_reserve;
168 		if (MP_INFO(x+i,y+j).ore_reserve > ORE_RESERVE) {
169 		    prev_tip = 1;
170 		    break;
171 		}
172 	    }
173 	}
174 	if (prev_tip) {
175 	    dialog_box(red(12),3,
176 		       0,0,_("You can't build a mine here"),
177 		       0,0,_("This area was once a landfill"),
178 		       2,' ',_("OK"));
179 	    return -4;
180 	}
181 	if (total_ore < MIN_ORE_RESERVE_FOR_MINE) {
182 	    dialog_box(red(12),3,
183 		       0,0,_("You can't build a mine here"),
184 		       0,0,_("There is no ore left at this site"),
185 		       2,' ',_("OK"));
186 	    return -4;
187 	}
188     }
189     } /* end case */
190 
191     /* Store last_built for refund on "mistakes" */
192     last_built_x = x;
193     last_built_y = y;
194 
195     /* Make sure that the correct windmill graphic shows up */
196     if (group == GROUP_WINDMILL) {
197 	if (tech_level > MODERN_WINDMILL_TECH) {
198 	    type = CST_WINDMILL_1_R;
199 	} else {
200 	    type = CST_WINDMILL_1_W;
201 	}
202     }
203 
204     if (group == GROUP_SOLAR_POWER || group == GROUP_WINDMILL) {
205 	MP_INFO(x,y).int_2 = tech_level;
206 	let_one_through = 1;
207     }
208     else if (group == GROUP_RECYCLE || group == GROUP_COAL_POWER)
209 	MP_INFO(x,y).int_4 = tech_level;
210     else if (group == GROUP_ORGANIC_FARM)
211 	MP_INFO(x,y).int_1 = tech_level;
212     else if (group == GROUP_TRACK
213 	     || group == GROUP_ROAD
214 	     || group == GROUP_RAIL)
215 	MP_INFO(x,y).flags |= FLAG_IS_TRANSPORT;
216     else if (group == GROUP_COALMINE
217 	     || group == GROUP_OREMINE)
218 	let_one_through = 1;
219 
220     set_mappoint (x, y, type);
221 
222     update_tech_dep (x, y);
223 
224     if (group == GROUP_RIVER)
225 	connect_rivers ();
226 
227     connect_transport (x-2,y-2,x+size+1,y+size+1);
228 
229     adjust_money(-selected_module_cost);
230     map_power_grid();
231     return 0;
232 }
233 
234 int
bulldoze_item(int x,int y)235 bulldoze_item (int x, int y)
236 {
237     int g, size;
238 
239     if (MP_TYPE(x,y) == CST_USED) {
240 	/* This is considered "improper" input.  Silently ignore. */
241 	return -1;
242     }
243 
244     size = MP_SIZE(x,y);
245     g = MP_GROUP(x,y);
246 
247     if (g == GROUP_BARE) {
248 	/* Nothing to do. */
249 	return -1;
250     }
251     else if (g == GROUP_SHANTY) {
252 	fire_area (x, y);
253 	adjust_money(-GROUP_SHANTY_BUL_COST);
254     }
255     else if (g == GROUP_FIRE) {
256 	if (MP_INFO(x,y).int_2 >= FIRE_LENGTH)
257 	    return -1;  /* Can't bulldoze ? */
258 	MP_INFO(x,y).int_2 = FIRE_LENGTH + 1;
259 	MP_TYPE(x,y) = CST_FIRE_DONE1;
260 	MP_GROUP(x,y) = GROUP_BURNT;
261 	adjust_money(-GROUP_BURNT_BUL_COST);
262     }
263     else {
264 	adjust_money(-main_groups[g].bul_cost);
265 	do_bulldoze_area (CST_GREEN, x, y);
266 	if (g == GROUP_OREMINE)
267 	{
268 	    int i, j;
269 	    for (j = 0; j < 4; j++)
270 		for (i = 0; i < 4; i++)
271 		    if (MP_INFO(x + i,y + j).ore_reserve < ORE_RESERVE / 2)
272 			do_bulldoze_area (CST_WATER, x + i, y + j);
273 	}
274     }
275     return size;  /* No longer used... */
276 }
277 
278 void
init_mappoint_array(void)279 init_mappoint_array (void)
280 {
281     int x;
282     for (x = 0; x < WORLD_SIDE_LEN; x++) {
283 	mappoint_array_x[x] = x;
284 	mappoint_array_y[x] = x;
285     }
286 }
287 
288 void
shuffle_mappoint_array(void)289 shuffle_mappoint_array (void)
290 {
291   int i, x, a;
292   for (i = 0; i < SHUFFLE_MAPPOINT_COUNT; i++)
293     {
294       x = rand () % WORLD_SIDE_LEN;
295       a = mappoint_array_x[i];
296       mappoint_array_x[i] = mappoint_array_x[x];
297       mappoint_array_x[x] = a;
298       x = rand () % WORLD_SIDE_LEN;
299       a = mappoint_array_y[i];
300       mappoint_array_y[i] = mappoint_array_y[x];
301       mappoint_array_y[x] = a;
302     }
303 }
304 
305 
306 int
buy_food(int xt,int yt)307 buy_food (int xt, int yt)
308 {
309   int i = 0;
310   if (MP_GROUP(xt,yt) == GROUP_TRACK)
311     {
312       if (MP_INFO(xt,yt).int_1 < MAX_FOOD_ON_TRACK)
313 	i = MAX_FOOD_ON_TRACK - MP_INFO(xt,yt).int_1;
314     }
315   else if (MP_GROUP(xt,yt) == GROUP_ROAD)
316     {
317       if (MP_INFO(xt,yt).int_1 < MAX_FOOD_ON_ROAD)
318 	i = MAX_FOOD_ON_ROAD - MP_INFO(xt,yt).int_1;
319     }
320   else if (MP_GROUP(xt,yt) == GROUP_RAIL)
321     {
322       if (MP_INFO(xt,yt).int_1 < MAX_FOOD_ON_RAIL)
323 	i = MAX_FOOD_ON_RAIL - MP_INFO(xt,yt).int_1;
324     }
325   i = (i * PORT_IMPORT_RATE) / 1000;
326   MP_INFO(xt,yt).int_1 += i;
327   return (i * PORT_FOOD_RATE);
328 }
329 
330 int
buy_coal(int xt,int yt)331 buy_coal (int xt, int yt)
332 {
333   int i = 0;
334   if (MP_GROUP(xt,yt) == GROUP_TRACK)
335     {
336       if (MP_INFO(xt,yt).int_3 < MAX_COAL_ON_TRACK)
337 	i = MAX_COAL_ON_TRACK - MP_INFO(xt,yt).int_3;
338     }
339   else if (MP_GROUP(xt,yt) == GROUP_ROAD)
340     {
341       if (MP_INFO(xt,yt).int_3 < MAX_COAL_ON_ROAD)
342 	i = MAX_COAL_ON_ROAD - MP_INFO(xt,yt).int_3;
343     }
344   else if (MP_GROUP(xt,yt) == GROUP_RAIL)
345     {
346       if (MP_INFO(xt,yt).int_3 < MAX_COAL_ON_RAIL)
347 	i = MAX_COAL_ON_RAIL - MP_INFO(xt,yt).int_3;
348     }
349   i = (i * PORT_IMPORT_RATE) / 1000;
350   MP_INFO(xt,yt).int_3 += i;
351   return (i * PORT_COAL_RATE);
352 }
353 
354 int
buy_ore(int xt,int yt)355 buy_ore (int xt, int yt)
356 {
357   int i = 0;
358   if (MP_GROUP(xt,yt) == GROUP_TRACK)
359     {
360       if (MP_INFO(xt,yt).int_5 < MAX_ORE_ON_TRACK)
361 	i = MAX_ORE_ON_TRACK - MP_INFO(xt,yt).int_5;
362     }
363   else if (MP_GROUP(xt,yt) == GROUP_ROAD)
364     {
365       if (MP_INFO(xt,yt).int_5 < MAX_ORE_ON_ROAD)
366 	i = MAX_ORE_ON_ROAD - MP_INFO(xt,yt).int_5;
367     }
368   else if (MP_GROUP(xt,yt) == GROUP_RAIL)
369     {
370       if (MP_INFO(xt,yt).int_5 < MAX_ORE_ON_RAIL)
371 	i = MAX_ORE_ON_RAIL - MP_INFO(xt,yt).int_5;
372     }
373   i = (i * PORT_IMPORT_RATE) / 1000;
374   MP_INFO(xt,yt).int_5 += i;
375   return (i * PORT_ORE_RATE);
376 }
377 
378 int
buy_goods(int xt,int yt)379 buy_goods (int xt, int yt)
380 {
381   int i = 0;
382   if (MP_GROUP(xt,yt) == GROUP_TRACK)
383     {
384       if (MP_INFO(xt,yt).int_4 < MAX_GOODS_ON_TRACK)
385 	i = MAX_GOODS_ON_TRACK - MP_INFO(xt,yt).int_4;
386     }
387   else if (MP_GROUP(xt,yt) == GROUP_ROAD)
388     {
389       if (MP_INFO(xt,yt).int_4 < MAX_GOODS_ON_ROAD)
390 	i = MAX_GOODS_ON_ROAD - MP_INFO(xt,yt).int_4;
391     }
392   else if (MP_GROUP(xt,yt) == GROUP_RAIL)
393     {
394       if (MP_INFO(xt,yt).int_4 < MAX_GOODS_ON_RAIL)
395 	i = MAX_GOODS_ON_RAIL - MP_INFO(xt,yt).int_4;
396     }
397   i = (i * PORT_IMPORT_RATE) / 1000;
398   MP_INFO(xt,yt).int_4 += i;
399   return (i * PORT_GOODS_RATE);
400 }
401 
402 
403 int
buy_steel(int xt,int yt)404 buy_steel (int xt, int yt)
405 {
406   int i = 0;
407   if (MP_GROUP(xt,yt) == GROUP_TRACK)
408     {
409       if (MP_INFO(xt,yt).int_6 < MAX_STEEL_ON_TRACK)
410 	i = MAX_STEEL_ON_TRACK - MP_INFO(xt,yt).int_6;
411     }
412   else if (MP_GROUP(xt,yt) == GROUP_ROAD)
413     {
414       if (MP_INFO(xt,yt).int_6 < MAX_STEEL_ON_ROAD)
415 	i = MAX_STEEL_ON_ROAD - MP_INFO(xt,yt).int_6;
416     }
417   else if (MP_GROUP(xt,yt) == GROUP_RAIL)
418     {
419       if (MP_INFO(xt,yt).int_6 < MAX_STEEL_ON_RAIL)
420 	i = MAX_STEEL_ON_RAIL - MP_INFO(xt,yt).int_6;
421     }
422   i = (i * PORT_IMPORT_RATE) / 1000;
423   MP_INFO(xt,yt).int_6 += i;
424   return (i * PORT_STEEL_RATE);
425 }
426 
427 int
sell_food(int xt,int yt)428 sell_food (int xt, int yt)
429 {
430   int i = 0;
431   i = (MP_INFO(xt,yt).int_1 * PORT_EXPORT_RATE) / 1000;
432   MP_INFO(xt,yt).int_1 -= i;
433   return (i * PORT_FOOD_RATE);
434 }
435 
436 int
sell_coal(int xt,int yt)437 sell_coal (int xt, int yt)
438 {
439   int i = 0;
440   i = (MP_INFO(xt,yt).int_3 * PORT_EXPORT_RATE) / 1000;
441   MP_INFO(xt,yt).int_3 -= i;
442   return (i * PORT_COAL_RATE);
443 }
444 
445 int
sell_ore(int xt,int yt)446 sell_ore (int xt, int yt)
447 {
448   int i = 0;
449   i = (MP_INFO(xt,yt).int_5 * PORT_EXPORT_RATE) / 1000;
450   MP_INFO(xt,yt).int_5 -= i;
451   return (i * PORT_ORE_RATE);
452 }
453 
454 int
sell_goods(int xt,int yt)455 sell_goods (int xt, int yt)
456 {
457   int i = 0;
458   i = (MP_INFO(xt,yt).int_4 * PORT_EXPORT_RATE) / 1000;
459   MP_INFO(xt,yt).int_4 -= i;
460   return (i * PORT_GOODS_RATE);
461 }
462 
463 int
sell_steel(int xt,int yt)464 sell_steel (int xt, int yt)
465 {
466   int i = 0;
467   i = (MP_INFO(xt,yt).int_6 * PORT_EXPORT_RATE) / 1000;
468   MP_INFO(xt,yt).int_6 -= i;
469   return (i * PORT_STEEL_RATE);
470 }
471 
472 void
do_pollution()473 do_pollution ()
474 {
475   int x, p;
476   int* pol = &map.pollution[0][0];
477 
478   /* Kill pollution from top edge of map */
479   do {
480     if (*pol > 0)
481       *pol /= POL_DIV;
482   } while (++pol < &map.pollution[1][0]);
483 
484 
485   x= 1;
486   do
487     {
488       /* Kill some pollution from left edge of map */
489       if (*pol++ > 0)
490         *(pol-1) /= POL_DIV;
491       do {
492         if (*pol > 10) {
493 	  p = *pol / 16;
494 	  *pol -= p;
495 	  switch ( rand() % 11)
496 	    {         /* prevailing wind is *from* SW ie right down */
497 	    case 0:
498 	    case 1: /* up */
499 	    case 2:
500 	      *(pol - 1) += p;
501 	      break;
502 	    case 3:
503 	    case 4: /* right */
504 	    case 5:
505 	      *(pol + WORLD_SIDE_LEN) += p;
506 	      break;
507 	    case 6: /* down */
508 	    case 7:
509 	      *(pol + 1) += p;
510 	      break;
511 	    case 8: /* left */
512 	    case 9:
513 	      *(pol - WORLD_SIDE_LEN) += p;
514 	      break;
515 	    case 10:
516 	      *pol += p- 2;
517 	      break;
518 	    }
519 	}
520       } while (++pol < &map.pollution[x][WORLD_SIDE_LEN-1]);
521       /* Kill some pollution from right edge of map */
522       if (*pol > 0)
523         *pol /= POL_DIV;
524       ++x;
525     }
526   while (++pol < &map.pollution[WORLD_SIDE_LEN-1][0]);
527 
528   /* Kill pollution from bottom edge of map */
529   do {
530     if (*pol > 0)
531       *pol /= POL_DIV;
532   } while (++pol < &map.pollution[WORLD_SIDE_LEN][0]);
533 }
534 
535 
536 /* XXX: remove_people is only used by rocket_pad, perhaps it should go there */
537 
538 void
remove_people(int num)539 remove_people (int num)
540 {
541 #if defined (commentout)
542   int x, y, f;
543   time_t t;
544   f = 1;
545   t = time (0);
546   while (f && (num > 0)) {
547       for (y = 0; y < WORLD_SIDE_LEN; y++)
548 	for (x = 0; x < WORLD_SIDE_LEN; x++)
549 	  if (MP_GROUP_IS_RESIDENCE(x,y) && MP_INFO(x,y).population > 0)
550 	    {
551 	      MP_INFO(x,y).population--;
552 	      // f = 1;
553 	      f |= (MP_INFO(x,y).population > 0);
554 	      num--;
555 	      total_evacuated++;
556 	    }
557   }
558   while (num > 0 && people_pool > 0) {
559       num--;
560       total_evacuated++;
561       people_pool--;
562   }
563 #endif
564 
565   int x, y;
566   /* reset housed population so that we can display it correctly */
567   housed_population = 1;
568   while (housed_population && (num > 0)) {
569       housed_population = 0;
570       for (y = 0; y < WORLD_SIDE_LEN; y++)
571 	for (x = 0; x < WORLD_SIDE_LEN; x++)
572 	  if (MP_GROUP_IS_RESIDENCE(x,y) && MP_INFO(x,y).population > 0) {
573 	      MP_INFO(x,y).population--;
574 	      housed_population += MP_INFO(x,y).population;
575 	      num--;
576 	      total_evacuated++;
577 	  }
578   }
579   while (num > 0 && people_pool > 0) {
580       num--;
581       total_evacuated++;
582       people_pool--;
583   }
584 
585   refresh_population_text ();
586 
587 #if defined (commentout)
588 /* last ship wasn't full so everyone has gone. */
589   if (num > 0)
590     {
591       if (t > HOF_START && t < HOF_STOP)
592 	ok_dial_box ("launch-gone-mail.mes", GOOD, 0L);
593       else
594 	ok_dial_box ("launch-gone.mes", GOOD, 0L);
595       housed_population = 0;
596     }
597 #endif
598 
599   /* Note that the previous test was inaccurate.  There could be
600      exactly 1000 people left. */
601   if (!housed_population && !people_pool) {
602     ok_dial_box ("launch-gone.mes", GOOD, 0L);
603   }
604 }
605 
606 
607 
608 void
clear_fire_health_and_cricket_cover(void)609 clear_fire_health_and_cricket_cover (void)
610 {
611   int x, y, m;
612   m = 0xffffffff - (FLAG_FIRE_COVER | FLAG_HEALTH_COVER
613 		    | FLAG_CRICKET_COVER);
614   for (y = 0; y < WORLD_SIDE_LEN; y++)
615     for (x = 0; x < WORLD_SIDE_LEN; x++)
616       MP_INFO(x,y).flags &= m;
617   /* Wow... chache misses or what! */
618 }
619 
620 void
do_fire_health_and_cricket_cover(void)621 do_fire_health_and_cricket_cover (void)
622 {
623   int x, y;
624   for (y = 0; y < WORLD_SIDE_LEN; y++)
625     for (x = 0; x < WORLD_SIDE_LEN; x++)
626       {
627 	/*  The next few lines need changing to test for */
628 	/*  the group if these areas are animated. */
629 
630 	if (MP_GROUP(x,y) == GROUP_FIRESTATION)
631 	  do_fire_cover (x, y);
632 	else if (MP_TYPE(x,y) == CST_HEALTH)
633 	  do_health_cover (x, y);
634 	else if (MP_GROUP(x,y) == GROUP_CRICKET)
635 	  do_cricket_cover (x, y);
636       }
637 }
638 
639 
640 
641 void
do_random_fire(int x,int y,int pwarning)642 do_random_fire (int x, int y, int pwarning)	/* well random if x=y=-1 */
643 {
644   int xx, yy;
645   if (x == -1 && y == -1)
646     {
647       x = rand () % WORLD_SIDE_LEN;
648       y = rand () % WORLD_SIDE_LEN;
649     }
650   else
651     {
652       if (x < 0 || x >= WORLD_SIDE_LEN || y < 0 || y >= WORLD_SIDE_LEN)
653 	return;
654     }
655   if (MP_TYPE(x,y) == CST_USED)
656     {
657       xx = MP_INFO(x,y).int_1;
658       yy = MP_INFO(x,y).int_2;
659       x = xx;
660       y = yy;
661     }
662   xx = rand () % 100;
663   if (xx >= (main_groups[MP_GROUP(x,y)].fire_chance))
664     return;
665   if ((MP_INFO(x,y).flags & FLAG_FIRE_COVER) != 0)
666     return;
667   if (pwarning)
668     {
669       if (MP_GROUP(x,y) == GROUP_POWER_LINE)
670 	ok_dial_box ("fire.mes", BAD, _("It's at a power line."));
671       else if (MP_GROUP(x,y) == GROUP_SOLAR_POWER)
672 	ok_dial_box ("fire.mes", BAD, _("It's at a solar power station."));
673       else if (MP_GROUP(x,y) == GROUP_SUBSTATION)
674 	ok_dial_box ("fire.mes", BAD, _("It's at a substation."));
675       else if (MP_GROUP_IS_RESIDENCE(x,y))
676 	ok_dial_box ("fire.mes", BAD, _("It's at a residential area."));
677       else if (MP_GROUP(x,y) == GROUP_ORGANIC_FARM)
678 	ok_dial_box ("fire.mes", BAD, _("It's at a farm."));
679       else if (MP_GROUP(x,y) == GROUP_MARKET)
680 	ok_dial_box ("fire.mes", BAD, _("It's at a market."));
681       else if (MP_GROUP(x,y) == GROUP_TRACK)
682 	ok_dial_box ("fire.mes", BAD, _("It's at a track."));
683       else if (MP_GROUP(x,y) == GROUP_COALMINE)
684 	ok_dial_box ("fire.mes", BAD, _("It's at a coal mine."));
685       else if (MP_GROUP(x,y) == GROUP_RAIL)
686 	ok_dial_box ("fire.mes", BAD, _("It's at a railway."));
687       else if (MP_GROUP(x,y) == GROUP_COAL_POWER)
688 	ok_dial_box ("fire.mes", BAD, _("It's at a coal power station."));
689       else if (MP_GROUP(x,y) == GROUP_ROAD)
690 	ok_dial_box ("fire.mes", BAD, _("It's at a road."));
691       else if (MP_GROUP(x,y) == GROUP_INDUSTRY_L)
692 	ok_dial_box ("fire.mes", BAD, _("It's at light industry."));
693       else if (MP_GROUP(x,y) == GROUP_UNIVERSITY)
694 	ok_dial_box ("fire.mes", BAD, _("It's at a university."));
695       else if (MP_GROUP(x,y) == GROUP_COMMUNE)
696 	ok_dial_box ("fire.mes", BAD, _("It's at a commune."));
697       else if (MP_GROUP(x,y) == GROUP_TIP)
698 	ok_dial_box ("fire.mes", BAD, _("It's at a tip."));
699       else if (MP_GROUP(x,y) == GROUP_PORT)
700 	ok_dial_box ("fire.mes", BAD, _("It's at a port."));
701       else if (MP_GROUP(x,y) == GROUP_INDUSTRY_H)
702 	ok_dial_box ("fire.mes", BAD, _("It's at a steel works."));
703       else if (MP_GROUP(x,y) == GROUP_RECYCLE)
704 	ok_dial_box ("fire.mes", BAD, _("It's at a recycle centre."));
705       else if (MP_GROUP(x,y) == GROUP_HEALTH)
706 	ok_dial_box ("fire.mes", BAD, _("It's at a health centre."));
707       else if (MP_GROUP(x,y) == GROUP_ROCKET)
708 	ok_dial_box ("fire.mes", BAD, _("It's at a rocket site."));
709       else if (MP_GROUP(x,y) == GROUP_WINDMILL)
710 	ok_dial_box ("fire.mes", BAD, _("It's at a windmill."));
711       else if (MP_GROUP(x,y) == GROUP_SCHOOL)
712 	ok_dial_box ("fire.mes", BAD, _("It's at a school."));
713       else if (MP_GROUP(x,y) == GROUP_BLACKSMITH)
714 	ok_dial_box ("fire.mes", BAD, _("It's at a blacksmith."));
715       else if (MP_GROUP(x,y) == GROUP_MILL)
716 	ok_dial_box ("fire.mes", BAD, _("It's at a mill."));
717       else if (MP_GROUP(x,y) == GROUP_POTTERY)
718 	ok_dial_box ("fire.mes", BAD, _("It's at a pottery."));
719       else if (MP_GROUP(x,y) == GROUP_FIRESTATION)
720 	ok_dial_box ("fire.mes", BAD, _("It's at a fire station!!!."));
721       else if (MP_GROUP(x,y) == GROUP_CRICKET)
722 	ok_dial_box ("fire.mes", BAD, _("It's at a cricket pitch!!!."));
723       else if (MP_GROUP(x,y) == GROUP_SHANTY)
724 	ok_dial_box ("fire.mes", BAD, _("It's at a shanty town."));
725       else
726 	ok_dial_box ("fire.mes", BAD, _("UNKNOWN!"));
727     }
728   fire_area (x, y);
729 }
730 
731 /*
732    // spiral round from startx,starty until we hit something of group group.
733    // return the x y coords encoded as x+y*WORLD_SIDE_LEN
734    // return -1 if we don't find one.
735  */
736 int
spiral_find_group(int startx,int starty,int group)737 spiral_find_group (int startx, int starty, int group)
738 {
739   int i, j, x, y;
740   x = startx;
741   y = starty;
742   /* let's just do a complete spiral for now, work out the bounds later */
743   for (i = 1; i < (WORLD_SIDE_LEN + WORLD_SIDE_LEN); i++)
744     {
745       for (j = 0; j < i; j++)
746 	{
747 	  x--;
748 	  if (x > 0 && x < WORLD_SIDE_LEN && y > 0 && y < WORLD_SIDE_LEN)
749 	    if (MP_GROUP(x,y) == group)
750 	      return (x + y * WORLD_SIDE_LEN);
751 	}
752       for (j = 0; j < i; j++)
753 	{
754 	  y--;
755 	  if (x > 0 && x < WORLD_SIDE_LEN && y > 0 && y < WORLD_SIDE_LEN)
756 	    if (MP_GROUP(x,y) == group)
757 	      return (x + y * WORLD_SIDE_LEN);
758 	}
759       i++;
760       for (j = 0; j < i; j++)
761 	{
762 	  x++;
763 	  if (x > 0 && x < WORLD_SIDE_LEN && y > 0 && y < WORLD_SIDE_LEN)
764 	    if (MP_GROUP(x,y) == group)
765 	      return (x + y * WORLD_SIDE_LEN);
766 	}
767       for (j = 0; j < i; j++)
768 	{
769 	  y++;
770 	  if (x > 0 && x < WORLD_SIDE_LEN && y > 0 && y < WORLD_SIDE_LEN)
771 	    if (MP_GROUP(x,y) == group)
772 	      return (x + y * WORLD_SIDE_LEN);
773 	}
774     }
775   return (-1);
776 }
777 
778 /*
779    // spiral round from startx,starty until we hit a 2x2 space.
780    // return the x y coords encoded as x+y*WORLD_SIDE_LEN
781    // return -1 if we don't find one.
782  */
783 int
spiral_find_2x2(int startx,int starty)784 spiral_find_2x2 (int startx, int starty)
785 {
786   int i, j, x, y;
787   x = startx;
788   y = starty;
789   /* let's just do a complete spiral for now, work out the bounds later */
790   for (i = 1; i < (WORLD_SIDE_LEN + WORLD_SIDE_LEN); i++)
791     {
792       for (j = 0; j < i; j++)
793 	{
794 	  x--;
795 	  if (x > 1 && x < WORLD_SIDE_LEN - 2 && y > 1
796 	      && y < WORLD_SIDE_LEN - 2)
797 	    if (MP_TYPE(x,y) == CST_GREEN
798 		&& MP_TYPE(x + 1,y) == CST_GREEN
799 		&& MP_TYPE(x,y + 1) == CST_GREEN
800 		&& MP_TYPE(x + 1,y + 1) == CST_GREEN)
801 	      return (x + y * WORLD_SIDE_LEN);
802 	}
803       for (j = 0; j < i; j++)
804 	{
805 	  y--;
806 	  if (x > 1 && x < WORLD_SIDE_LEN - 2 && y > 1
807 	      && y < WORLD_SIDE_LEN - 2)
808 	    if (MP_TYPE(x,y) == CST_GREEN
809 		&& MP_TYPE(x + 1,y) == CST_GREEN
810 		&& MP_TYPE(x,y + 1) == CST_GREEN
811 		&& MP_TYPE(x + 1,y + 1) == CST_GREEN)
812 	      return (x + y * WORLD_SIDE_LEN);
813 	}
814       i++;
815       for (j = 0; j < i; j++)
816 	{
817 	  x++;
818 	  if (x > 1 && x < WORLD_SIDE_LEN - 2 && y > 1
819 	      && y < WORLD_SIDE_LEN - 2)
820 	    if (MP_TYPE(x,y) == CST_GREEN
821 		&& MP_TYPE(x + 1,y) == CST_GREEN
822 		&& MP_TYPE(x,y + 1) == CST_GREEN
823 		&& MP_TYPE(x + 1,y + 1) == CST_GREEN)
824 	      return (x + y * WORLD_SIDE_LEN);
825 	}
826       for (j = 0; j < i; j++)
827 	{
828 	  y++;
829 	  if (x > 1 && x < WORLD_SIDE_LEN - 2 && y > 1
830 	      && y < WORLD_SIDE_LEN - 2)
831 	    if (MP_TYPE(x,y) == CST_GREEN
832 		&& MP_TYPE(x + 1,y) == CST_GREEN
833 		&& MP_TYPE(x,y + 1) == CST_GREEN
834 		&& MP_TYPE(x + 1,y + 1) == CST_GREEN)
835 	      return (x + y * WORLD_SIDE_LEN);
836 	}
837     }
838   return (-1);
839 }
840 
841 
842 
843 void
do_bulldoze_area(short fill,int xx,int yy)844 do_bulldoze_area (short fill, int xx, int yy)
845 {
846   int size, x, y;
847   if (MP_TYPE(xx,yy) == CST_USED)
848     {
849       x = MP_INFO(xx,yy).int_1;
850       y = MP_INFO(xx,yy).int_2;
851     }
852   else
853     {
854       x = xx;
855       y = yy;
856     }
857   size = MP_SIZE(x,y);
858   if (MP_GROUP(x,y) == GROUP_SUBSTATION
859       || MP_GROUP(x,y) == GROUP_WINDMILL)
860     remove_a_substation (x, y);
861   else if (MP_GROUP(x,y) == GROUP_MARKET)
862     remove_a_market (x, y);
863   else if (MP_GROUP(x,y) == GROUP_SHANTY)
864     numof_shanties--;
865   else if (MP_GROUP(x,y) == GROUP_COMMUNE)
866     numof_communes--;
867 
868   people_pool += MP_INFO(x,y).population;
869   clear_mappoint (fill, x, y);
870   if (size > 1)			/* do size 2 */
871     {
872       clear_mappoint (fill, x + 1, y);
873       clear_mappoint (fill, x, y + 1);
874       clear_mappoint (fill, x + 1, y + 1);
875     }
876   if (size > 2)			/* do size 3 */
877     {
878       clear_mappoint (fill, x + 2, y);
879       clear_mappoint (fill, x + 2, y + 1);
880       clear_mappoint (fill, x + 2, y + 2);
881       clear_mappoint (fill, x, y + 2);
882       clear_mappoint (fill, x + 1, y + 2);
883     }
884   if (size > 3)			/* do size 4 */
885     {
886       clear_mappoint (fill, x + 3, y);
887       clear_mappoint (fill, x + 3, y + 1);
888       clear_mappoint (fill, x + 3, y + 2);
889       clear_mappoint (fill, x + 3, y + 3);
890       clear_mappoint (fill, x, y + 3);
891       clear_mappoint (fill, x + 1, y + 3);
892       clear_mappoint (fill, x + 2, y + 3);
893     }
894 }
895 
896 
897 void
update_tech_dep(int x,int y)898 update_tech_dep (int x, int y)
899 {
900   switch (MP_GROUP(x,y))
901     {
902     case (GROUP_ORGANIC_FARM):
903       MP_INFO(x,y).int_7 = ((double) MP_INFO(x,y).int_1
904 			      * ORGANIC_FARM_FOOD_OUTPUT) / MAX_TECH_LEVEL;
905       break;
906     case (GROUP_WINDMILL):
907 #ifdef OLD_POWER_CODE
908       MP_INFO(x,y).int_5 = WINDMILL_POWER
909 #else
910       MP_INFO(x,y).int_1 = WINDMILL_POWER
911 #endif
912 	+ (((double) MP_INFO(x,y).int_2 * WINDMILL_POWER) / MAX_TECH_LEVEL);
913       break;
914     case (GROUP_COAL_POWER):
915 #ifdef OLD_POWER_CODE
916       MP_INFO(x,y).int_5 = POWERS_COAL_OUTPUT
917 #else
918       MP_INFO(x,y).int_1 = POWERS_COAL_OUTPUT
919 #endif
920 	+ (((double) MP_INFO(x,y).int_4 * POWERS_COAL_OUTPUT)
921 	   / MAX_TECH_LEVEL);
922       break;
923     case (GROUP_SOLAR_POWER):
924       MP_INFO(x,y).int_3 = POWERS_SOLAR_OUTPUT
925 	+ (((double) MP_INFO(x,y).int_2 * POWERS_SOLAR_OUTPUT)
926 	   / MAX_TECH_LEVEL);
927       break;
928     }
929 }
930 
931 void
connect_rivers(void)932 connect_rivers (void)
933 {
934   int x, y, count;
935   count = 1;
936   while (count > 0)
937     {
938       count = 0;
939       for (y = 0; y < WORLD_SIDE_LEN; y++)
940 	for (x = 0; x < WORLD_SIDE_LEN; x++)
941 	  {
942 	    if (is_real_river (x, y) == 1)
943 	      {
944 		if (is_real_river (x - 1, y) == -1)
945 		  {
946 		    MP_INFO(x - 1,y).flags |= FLAG_IS_RIVER;
947 		    count++;
948 		  }
949 		if (is_real_river (x, y - 1) == -1)
950 		  {
951 		    MP_INFO(x,y - 1).flags |= FLAG_IS_RIVER;
952 		    count++;
953 		  }
954 		if (is_real_river (x + 1, y) == -1)
955 		  {
956 		    MP_INFO(x + 1,y).flags |= FLAG_IS_RIVER;
957 		    count++;
958 		  }
959 		if (is_real_river (x, y + 1) == -1)
960 		  {
961 		    MP_INFO(x,y + 1).flags |= FLAG_IS_RIVER;
962 		    count++;
963 		  }
964 	      }
965 	  }
966     }
967 }
968 
969 int
is_real_river(int x,int y)970 is_real_river (int x, int y)
971 {
972   /* returns zero if not water at all or if out of bounds. */
973   if (x < 0 || x >= WORLD_SIDE_LEN || y < 0 || y >= WORLD_SIDE_LEN)
974     return (0);
975   if (MP_GROUP(x,y) != GROUP_WATER)
976     return (0);
977   if (MP_INFO(x,y).flags & FLAG_IS_RIVER)
978     return (1);
979   return (-1);
980 }
981 
982 /* Feature: coal survey should vary in price and accuracy with technology */
983 void
do_coal_survey(void)984 do_coal_survey (void)
985 {
986     if (coal_survey_done == 0) {
987 	adjust_money(-1000000);
988 	coal_survey_done = 1;
989     }
990 }
991