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  * (c) Corey Keasling 2001-2004.
6  * ---------------------------------------------------------------------- */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include "lclib.h"
11 #include "common.h"
12 #include "lctypes.h"
13 #include "lin-city.h"
14 #include "engine.h"
15 #include "engglobs.h"
16 #include "cliglobs.h"
17 #include "simulate.h"
18 #include "lcintl.h"
19 #include "power.h"
20 #include "transport.h" /* for XY_IS_TRANSPORT */
21 
22 /* reset per map_power_grid run; how many different grids */
23 int grid_num = 0;
24 
25 /* grid map time stampt.  Incremented per map_power_grid run,
26    used to determine if a square has been mapped */
27 int grid_inc = 0;
28 
29 Grid * grid[MAX_GRIDS];
30 
31 /* power_time_step
32    Take the avail_power from last timestep, and move in into the
33    total_power, which will be used during this timestep on a first-come
34    first-served basis.
35 */
36 void
power_time_step()37 power_time_step ()
38 {
39     int gi;
40     int net; /* net power */
41 
42     if (grid_num == 0)
43 	return;
44 
45     for (gi = 1; gi <= grid_num; gi++) {
46 	grid[gi]->total_power = grid[gi]->avail_power -
47 		(grid[gi]->power_lines * POWER_LINE_LOSS);
48 
49 	net = (grid[gi]->total_power - grid[gi]->demand);
50 	if (net < 0)
51 	    grid[gi]->powered = -1;
52 	else if (net < (grid[gi]->avail_power / 4))
53 	    grid[gi]->powered = 0;
54 	else
55 	    grid[gi]->powered = 1;
56 
57 	grid[gi]->avail_power = 0;
58 	grid[gi]->demand = 0;
59     }
60 
61     /* Clear substation 'Here' counter */
62     for (gi = 0; gi < numof_substations; gi++)
63 	MP_INFO(substationx[gi],substationy[gi]).int_5 = 0;
64 }
65 
66 
67 void
map_power_grid()68 map_power_grid ()
69 {
70     int mapx, mapy;
71     grid_num = 0;  /* how many grids found so far */
72     grid_inc++; /* how many times have we run map_power_grid */
73 
74     for (mapx = 0; mapx < WORLD_SIDE_LEN; mapx++) {
75 	for (mapy = 0; mapy < WORLD_SIDE_LEN; mapy++) {
76 	    if (XY_IS_GRID(mapx,mapy)) {
77 		if (MP_INFO(mapx,mapy).int_7 != grid_inc) {
78 		    if (grid_num == MAX_GRIDS) {
79 			printf("You have too many power grids.  Join some of them\n");
80 			return;
81 		    }
82 		    grid[++grid_num] = (Grid *)lcalloc(sizeof(Grid));
83 		    grid[grid_num]->total_power = 0;
84 		    grid[grid_num]->power_lines = 0;
85 		    grid[grid_num]->demand = 0;
86 		    grid[grid_num]->max_power = 0;
87 
88 		    recurse_power_grid(mapx,mapy,0);
89 		}
90 	    }
91 	}
92     }
93 #ifdef DEBUG_POWER
94     printf("grid_inc: %d found %d grids\n",grid_inc, grid_num);
95 #endif
96 }
97 
98 
99 
100 /*
101 check_grid(x, y, xi, yi) - coordinates, ?i being which one to increment if we
102 need to step over transport
103 
104 Check connection to power grid things and check if they've been
105 mapped.  If they have, we perform a sanity check.  If it is a
106 power source, project the power for it and add that to our
107 total.  Now set it to our grid.  If it is a power line, return
108 1, otherwise 0. */
109 
110 int
check_grid(int x,int y,int xi,int yi)111 check_grid(int x, int y, int xi, int yi)
112 {
113   if (XY_IS_GRID(x,y) && !IS_OLD_WINDMILL(x,y)) {
114     if (GRID_CURRENT(x,y)) {
115       if (MP_INFO(x,y).int_6 != grid_num)
116 	/* XXX: This can occur if connecting to a power source at different
117 	   locations.  Need a clean way to resolve this, either connect
118 	   the two grids by treating a power source as a power line, or
119 	   let the power source be multihomed and figure out power distribution
120 	*/
121 	printf("recurse_power_grid insane: %d, %d on a different grid!\n",
122 	       x,y);
123     } else if (!IS_POWER_LINE(x,y)) {
124       if (IS_POWER_SOURCE(x,y)) {
125 	project_power(x,y);
126 	grid[grid_num]->total_power += MP_INFO(x,y).int_1;
127       }
128 
129       MP_INFO(x,y).int_6 = grid_num;
130       MP_INFO(x,y).int_7 = grid_inc;
131 
132     } else /* is a power line */
133       return 1;
134   } else if (XY_IS_TRANSPORT(x,y) || XY_IS_WATER(x,y)) { /* can we step over?*/
135     if (xi == 0 && yi == 0) /* already stepped */
136       return 0;
137     if (x+xi >= 1 && x+xi < WORLD_SIDE_LEN &&
138 	y+yi >= 1 && y+yi < WORLD_SIDE_LEN)
139       return (check_grid(x+xi,y+yi,0,0) ? 2 : 0);
140     else
141       return 0;
142   }
143 
144   return 0;
145 }
146 
147 /* Go through the power grid and figure out what is connected.  This
148 should really handle the connect_transport bit for power lines.  That
149 would help perspicuity anyway. */
150 
151 void
recurse_power_grid(int startx,int starty,int steps)152 recurse_power_grid (int startx, int starty, int steps)
153 {
154     static int level;             /* debug: levels of recursion encountered */
155     int count = steps;            /* number of steps taken - for animation */
156     short dir = -1;   /* -1 undetermined, 0 nothing left, Direction #defines */
157     int mapx = startx, mapy = starty;                     /* to move about */
158     int inc;           /* handles special case of stepping over transport */
159 
160     level++;
161 
162     if (count % POWER_MODULUS == 0)
163 	count = 0;
164 
165     /* Old windmills aren't grid connected, so they are on their own 'grid'.  We
166        ignore them in the main loop.  This case should only be reached from a
167        call from map_power_grid with a new grid_num, not from a new path in the
168        code below */
169 
170     if (IS_OLD_WINDMILL(mapx, mapy)) {
171 	MP_INFO(mapx,mapy).int_6 = grid_num;
172 	MP_INFO(mapx,mapy).int_7 = grid_inc;
173 
174 	grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
175 
176 	level--;
177 	return;
178     }
179 
180 
181     /* Crawl about the grid, finding paths and what not.  */
182 
183     while (dir != 0) {
184 
185 	/* Set to current grid */
186 
187 	/* figure out what we are on */
188 	if (IS_POWER_LINE(mapx,mapy)) {
189 	    grid[grid_num]->power_lines++;
190 	    MP_INFO(mapx,mapy).int_5 = (count++ % POWER_MODULUS);
191 	    if ((MP_TYPE(mapx,mapy) >= 1) && (MP_TYPE(mapx,mapy) <= 11))
192 		MP_TYPE(mapx,mapy) += 11;
193 
194 
195 	}
196 
197 	MP_INFO(mapx,mapy).int_6 = grid_num;
198 	MP_INFO(mapx,mapy).int_7 = grid_inc;
199 
200 
201 	/* For each direction, check map bounds, check if there is power stuff
202 	   there, then either remember to follow it later or start a new
203 	   recursion to follow the path now */
204 
205 	/* West */
206 	if (mapx >= 1) {
207 	    if ((inc = check_grid(mapx - 1, mapy, -1, 0))) {
208 		if (dir < 1) {
209 		    dir = WEST;
210 		} else {
211 		    recurse_power_grid(mapx - inc, mapy, count + 1);
212 		}
213 	    }
214 	}
215 
216 	/* North */
217 	if (mapy >= 1) {
218 	    if ((inc = check_grid(mapx, mapy - 1, 0, -1))) {
219 		if (dir < 1) {
220 		    dir = NORTH;
221 		} else {
222 		    recurse_power_grid(mapx, mapy - inc, count + 1);
223 		}
224 	    }
225 	}
226 
227 	/* East */
228 	if (mapx < WORLD_SIDE_LEN) {
229 	    if ((inc = check_grid(mapx + 1, mapy, 1, 0))) {
230 		if (dir < 1) {
231 		    dir = EAST;
232 		} else {
233 		    recurse_power_grid(mapx + inc, mapy, count + 1);
234 		}
235 	    }
236 	}
237 
238 	/* South */
239 	if (mapy < WORLD_SIDE_LEN) {
240 	    if ((inc = check_grid(mapx, mapy + 1, 0, 1))) {
241 		if (dir < 1) {
242 		    dir = SOUTH;
243 		} else {
244 		    recurse_power_grid(mapx, mapy + inc, count + 1);
245 		}
246 	    }
247 	}
248 
249 	/* Move to a new square if the chosen direction is not already mapped. */
250 	switch (dir) {
251 	case (-1):  /* Didn't find one, must not be any.  Stop looping */
252 	    {
253 		dir = 0;
254 	    } break;
255 	case WEST:
256 	    {
257 		if (mapx >= 1) {
258 		    if ((inc = check_grid(mapx - 1, mapy, -1, 0))) {
259 			mapx -= inc;
260 			dir = -1;
261 		    } else {
262 			dir = 0;
263 		    }
264 		}
265 	    } break;
266 
267 	case NORTH:
268 	    {
269 		if (mapy >= 1) {
270 		    if ((inc = check_grid(mapx, mapy - 1, 0, -1))) {
271 			mapy -= inc;
272 			dir = -1;
273 		    } else {
274 			dir = 0;
275 		    }
276 		}
277 	    } break;
278 
279 	case EAST:
280 	    {
281 		if (mapx < WORLD_SIDE_LEN) {
282 		    if ((inc = check_grid(mapx + 1, mapy, 1, 0))) {
283 			mapx += inc;
284 			dir = -1;
285 		    } else {
286 			dir = 0;
287 		    }
288 		}
289 	    } break;
290 
291 	case SOUTH:
292 	    {
293 		if (mapy < WORLD_SIDE_LEN) {
294 		    if ((inc = check_grid(mapx, mapy + 1, 0, 1))) {
295 			mapy += inc;
296 			dir = -1;
297 		    } else {
298 			dir = 0;
299 		    }
300 		}
301 	    } break;
302 	}
303     }
304 
305     level--;
306     /*  printf("exiting recurse_power_grid:level %d\n",level);*/
307 }
308 
309 /* project_power
310    Get the appropriate number from the proper variable */
311 
312 void
project_power(int mapx,int mapy)313 project_power(int mapx, int mapy)
314 {
315   switch (MP_GROUP(mapx,mapy)) {
316   case GROUP_COAL_POWER:
317     {
318       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
319     } break;
320   case GROUP_SOLAR_POWER:
321     {
322       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_3;
323     } break;
324   case GROUP_WINDMILL:
325     {
326       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
327     } break;
328   default:
329     {
330       printf("default case in project_power");
331       printf("\tMP_GROUP = %d\n",MP_GROUP(mapx,mapy));
332     } break;
333   }
334 }
335 
336 
337 /* get_power
338    get power for thing at x, y.  Don't use windmills if industry.
339    We go through a list (ugly, I know) until we find a substation in range
340    and then try and get power from it's grid.  If we can't, continue.
341 */
342 
343 int
get_power(int x,int y,int power,int block_industry)344 get_power (int x, int y, int power, int block_industry)
345 {
346 
347   int i;
348   int xi, yi;
349   int grid_tmp; /* for simplicity */
350 
351   if (numof_substations == 0)
352     return(0);
353 
354   for (i = 0; i < numof_substations; i++)
355     {
356       xi = substationx[i];
357       yi = substationy[i];
358       if (abs (xi - x) < SUBSTATION_RANGE &&
359 	  abs (yi - y) < SUBSTATION_RANGE) {
360 
361 	if (block_industry != 0 && MP_GROUP(xi, yi) == GROUP_WINDMILL)
362 	  continue;
363 
364 	grid_tmp = MP_INFO(xi,yi).int_6;
365 
366 	grid[grid_tmp]->demand += power;
367 	if (grid[grid_tmp]->total_power >= power) {
368 	  grid[grid_tmp]->total_power -= power;
369 	  MP_INFO(xi,yi).int_5 += power;
370 	  return 1;
371 	}
372 
373       }
374     }
375   return 0;
376 }
377