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