Main Page | Data Structures | Directories | File List | Data Fields | Globals

power.c

Go to the documentation of this file.
00001  /* ---------------------------------------------------------------------- *
00002  * engine.c
00003  * This file is part of lincity.
00004  * Lincity is copyright (c) I J Peters 1995-1997, (c) Greg Sharp 1997-2001.
00005  * Portions copyright (c) Corey Keasling 2001.
00006  * ---------------------------------------------------------------------- */
00007 
00008 #include <stdio.h>
00009 #include "lclib.h"
00010 #include "common.h"
00011 #include "lctypes.h"
00012 #include "lin-city.h"
00013 #include "engine.h"
00014 #include "engglobs.h"
00015 #include "cliglobs.h"
00016 #include "simulate.h"
00017 /*#include "sernet.h"*/
00018 /*#include "clistubs.h"*/
00019 #include "lcintl.h"
00020 #include "power.h"
00021 #include "transport.h" /* for XY_IS_TRANSPORT */
00022 
00023 /* reset per map_power_grid run; how many different grids */
00024 int grid_num = 0;
00025 
00026 /* grid map time stampt.  Incremented per map_power_grid run,
00027    used to determine if a square has been mapped */
00028 int grid_inc = 0;
00029 
00030 Grid * grid[MAX_GRIDS];
00031 
00032 #define POWER_MODULUS 25 // Controls how often we see a packet
00033 
00034 
00035 /* power_time_step 
00036    Take the avail_power from last timestep, and move in into the 
00037    total_power, which will be used during this timestep on a first-come 
00038    first-served basis.
00039 */
00040 void
00041 power_time_step () 
00042 {
00043     int gi;
00044     int net; /* net power */
00045 
00046     if (grid_num == 0)
00047         return;
00048 
00049     for (gi = 1; gi <= grid_num; gi++) {
00050         grid[gi]->total_power = grid[gi]->avail_power - 
00051                 (grid[gi]->power_lines * POWER_LINE_LOSS);
00052 
00053         net = (grid[gi]->total_power - grid[gi]->demand);
00054         if (net < 0)
00055             grid[gi]->powered = -1;
00056         else if (net < (grid[gi]->avail_power / 4))
00057             grid[gi]->powered = 0;
00058         else 
00059             grid[gi]->powered = 1;
00060 
00061         grid[gi]->avail_power = 0;
00062         grid[gi]->demand = 0;
00063     }
00064 
00065     /* Clear substation 'Here' counter */
00066     for (gi = 0; gi < numof_substations; gi++) 
00067         MP_INFO(substationx[gi],substationy[gi]).int_5 = 0;
00068 }
00069 
00070 
00071 void 
00072 map_power_grid ()
00073 {
00074     int mapx, mapy;
00075     grid_num = 0;  /* how many grids found so far */
00076     grid_inc++; /* how many times have we run map_power_grid */
00077 
00078     for (mapx = 0; mapx < WORLD_SIDE_LEN; mapx++) {
00079         for (mapy = 0; mapy < WORLD_SIDE_LEN; mapy++) {
00080             if (XY_IS_GRID(mapx,mapy)) {
00081                 if (MP_INFO(mapx,mapy).int_7 != grid_inc) {
00082                     if (grid_num == MAX_GRIDS) {
00083                         printf("You have too many power grids.  Join some of them\n");
00084                         return;
00085                     }
00086                     grid[++grid_num] = (Grid *)lcalloc(sizeof(Grid));
00087                     grid[grid_num]->total_power = 0;
00088                     grid[grid_num]->power_lines = 0;
00089                     grid[grid_num]->demand = 0;
00090                     grid[grid_num]->max_power = 0;
00091 
00092                     recurse_power_grid(mapx,mapy,0);
00093                 }
00094             }
00095         }
00096     }
00097 #ifdef DEBUG_POWER
00098     printf("grid_inc: %d found %d grids\n",grid_inc, grid_num);
00099 #endif
00100 }
00101 
00102 
00103 
00104 /* 
00105 check_grid(x, y, xi, yi) - coordinates, ?i being which one to increment if we
00106 need to step over transport
00107 
00108 Check connection to power grid things and check if they've been
00109 mapped.  If they have, we perform a sanity check.  If it is a
00110 power source, project the power for it and add that to our
00111 total.  Now set it to our grid.  If it is a power line, return
00112 1, otherwise 0. */
00113 
00114 int 
00115 check_grid(int x, int y, int xi, int yi) 
00116 {
00117   if (XY_IS_GRID(x,y) && !IS_OLD_WINDMILL(x,y)) {
00118     if (GRID_CURRENT(x,y)) {
00119       if (MP_INFO(x,y).int_6 != grid_num)
00120         /* XXX: This can occur if connecting to a power source at different
00121            locations.  Need a clean way to resolve this, either connect
00122            the two grids by treating a power source as a power line, or 
00123            let the power source be multihomed and figure out power distribution
00124         */
00125         printf("recurse_power_grid insane: %d, %d on a different grid!\n",
00126                x,y);
00127     } else if (!IS_POWER_LINE(x,y)) {
00128       if (IS_POWER_SOURCE(x,y)) {
00129         project_power(x,y); 
00130         grid[grid_num]->total_power += MP_INFO(x,y).int_1;
00131       }
00132       
00133       MP_INFO(x,y).int_6 = grid_num;
00134       MP_INFO(x,y).int_7 = grid_inc;
00135       
00136     } else /* is a power line */
00137       return 1;
00138   } else if (XY_IS_TRANSPORT(x,y) || XY_IS_WATER(x,y)) { /* can we step over?*/
00139     if (xi == 0 && yi == 0) /* already stepped */
00140       return 0;
00141     if (x+xi >= 1 && x+xi < WORLD_SIDE_LEN &&
00142         y+yi >= 1 && y+yi < WORLD_SIDE_LEN)
00143       return (check_grid(x+xi,y+yi,0,0) ? 2 : 0);
00144     else
00145       return 0;
00146   }
00147 
00148   return 0;
00149 }
00150   
00151 /* Go through the power grid and figure out what is connected.  This
00152 should really handle the connect_transport bit for power lines.  That
00153 would help perspicuity anyway. */
00154 
00155 void 
00156 recurse_power_grid (int startx, int starty, int steps) 
00157 {
00158     static int level;             /* debug: levels of recursion encountered */
00159     int count = steps;            /* number of steps taken - for animation */
00160     short dir = -1;   /* -1 undetermined, 0 nothing left, Direction #defines */
00161     int mapx = startx, mapy = starty;                     /* to move about */
00162     int inc;           /* handles special case of stepping over transport */
00163   
00164     level++;
00165   
00166     if (count % POWER_MODULUS == 0)
00167         count = 0;
00168 
00169     /* Old windmills aren't grid connected, so they are on their own 'grid'.  We
00170        ignore them in the main loop.  This case should only be reached from a 
00171        call from map_power_grid with a new grid_num, not from a new path in the
00172        code below */
00173 
00174     if (IS_OLD_WINDMILL(mapx, mapy)) {
00175         MP_INFO(mapx,mapy).int_6 = grid_num;
00176         MP_INFO(mapx,mapy).int_7 = grid_inc;
00177 
00178         grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
00179 
00180         level--;
00181         return;
00182     }
00183 
00184 
00185     /* Crawl about the grid, finding paths and what not.  */
00186 
00187     while (dir != 0) {
00188 
00189         /* Set to current grid */
00190 
00191         /* figure out what we are on */
00192         if (IS_POWER_LINE(mapx,mapy)) {
00193             grid[grid_num]->power_lines++;
00194             MP_INFO(mapx,mapy).int_5 = (count++ % POWER_MODULUS);
00195             if ((MP_TYPE(mapx,mapy) >= 1) && (MP_TYPE(mapx,mapy) <= 11))
00196                 MP_TYPE(mapx,mapy) += 11;
00197 
00198 
00199         }
00200 
00201         MP_INFO(mapx,mapy).int_6 = grid_num;
00202         MP_INFO(mapx,mapy).int_7 = grid_inc;
00203 
00204 
00205         /* For each direction, check map bounds, check if there is power stuff
00206            there, then either remember to follow it later or start a new
00207            recursion to follow the path now */
00208 
00209         /* West */
00210         if (mapx >= 1) 
00211             if (inc = check_grid(mapx - 1, mapy, -1, 0))
00212                 if (dir < 1) 
00213                     dir = WEST;
00214                 else
00215                     recurse_power_grid(mapx - inc, mapy, count + 1);
00216 
00217 
00218         /* North */
00219         if (mapy >= 1)
00220             if (inc = check_grid(mapx, mapy - 1, 0, -1))
00221                 if (dir < 1) 
00222                     dir = NORTH;
00223                 else 
00224                     recurse_power_grid(mapx, mapy - inc, count + 1);
00225 
00226 
00227         /* East */    
00228         if (mapx < WORLD_SIDE_LEN)
00229             if (inc = check_grid(mapx + 1, mapy, 1, 0))
00230                 if (dir < 1) 
00231                     dir = EAST;
00232                 else 
00233                     recurse_power_grid(mapx + inc, mapy, count + 1);
00234 
00235 
00236         /* South */    
00237         if (mapy < WORLD_SIDE_LEN)
00238             if (inc = check_grid(mapx, mapy + 1, 0, 1))
00239                 if (dir < 1) 
00240                     dir = SOUTH;
00241                 else 
00242                     recurse_power_grid(mapx, mapy + inc, count + 1);
00243 
00244 
00245         /* Move to a new square if the chosen direction is not already mapped. */
00246         switch (dir) {
00247         case (-1):  /* Didn't find one, must not be any.  Stop looping */ 
00248             {
00249                 dir = 0; 
00250             } break;
00251         case WEST: 
00252             {
00253                 if (mapx >= 1) 
00254                     if (inc = check_grid(mapx - 1, mapy, -1, 0)) {
00255                         mapx -= inc;
00256                         dir = -1;
00257                     } else 
00258                         dir = 0;
00259             } break;
00260 
00261         case NORTH:
00262             {
00263                 if (mapy >= 1)
00264                     if (inc = check_grid(mapx, mapy - 1, 0, -1)) {
00265                         mapy -= inc;
00266                         dir = -1;
00267                     } else
00268                         dir = 0;
00269             } break;
00270 
00271         case EAST:
00272             {
00273                 if (mapx < WORLD_SIDE_LEN)
00274                     if (inc = check_grid(mapx + 1, mapy, 1, 0)) {
00275                         mapx += inc;
00276                         dir = -1;
00277                     } else
00278                         dir = 0;
00279             } break;
00280 
00281         case SOUTH:
00282             { if (mapy < WORLD_SIDE_LEN)
00283                 if (inc = check_grid(mapx, mapy + 1, 0, 1)) {
00284                     mapy += inc;
00285                     dir = -1;
00286                 } else
00287                     dir = 0;
00288             } break;
00289         }
00290     }
00291 
00292     level--;
00293     /*  printf("exiting recurse_power_grid:level %d\n",level);*/
00294 }
00295 
00296 
00297 
00298 
00299 /* project_power
00300    Get the appropriate number from the proper variable */
00301 
00302 void 
00303 project_power(int mapx, int mapy) 
00304 {
00305   switch (MP_GROUP(mapx,mapy)) {
00306   case GROUP_COAL_POWER: 
00307     {
00308       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
00309     } break;
00310   case GROUP_SOLAR_POWER: 
00311     {
00312       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_3;
00313     } break;
00314   case GROUP_WINDMILL: 
00315     { 
00316       grid[grid_num]->max_power += MP_INFO(mapx,mapy).int_1;
00317     } break;
00318   default: 
00319     {
00320       printf("default case in project_power");
00321       printf("\tMP_GROUP = %d\n",MP_GROUP(mapx,mapy));
00322     } break;
00323   }
00324 }
00325 
00326 
00327 /* get_power
00328    get power for thing at x, y.  Don't use windmills if industry.
00329    We go through a list (ugly, I know) until we find a substation in range
00330    and then try and get power from it's grid.  If we can't, continue.
00331 */
00332 
00333 int 
00334 get_power (int x, int y, int power, int block_industry)
00335 {
00336 
00337   int i;
00338   int xi, yi;
00339   int grid_tmp; /* for simplicity */
00340 
00341   if (numof_substations == 0)
00342     return(0);
00343 
00344   for (i = 0; i < numof_substations; i++) 
00345     {
00346       xi = substationx[i];
00347       yi = substationy[i];
00348       if (abs (xi - x) < SUBSTATION_RANGE && 
00349           abs (yi - y) < SUBSTATION_RANGE) {
00350 
00351         if (block_industry != 0 && MP_GROUP(xi, yi) == GROUP_WINDMILL)
00352           continue;
00353 
00354         grid_tmp = MP_INFO(xi,yi).int_6;
00355 
00356         grid[grid_tmp]->demand += power;
00357         if (grid[grid_tmp]->total_power >= power) {
00358           grid[grid_tmp]->total_power -= power;
00359           MP_INFO(xi,yi).int_5 += power;
00360           return 1;
00361         }
00362         
00363       }
00364     }
00365   return 0;
00366 }
00367           
00368 
00369 /*** Substations ***/
00370 /*
00371   int_5 is the power demand at this substation
00372   int_6 is the grid its connected to
00373   int_7 is a grid timestamp
00374 */
00375 
00376 void 
00377 do_power_substation (int x, int y) 
00378 {
00379     switch(grid[MP_INFO(x,y).int_6]->powered) {
00380     case -1: {
00381         MP_TYPE(x,y) = CST_SUBSTATION_R; 
00382     } break;
00383     case 0 : {
00384         MP_TYPE(x,y) = CST_SUBSTATION_RG;
00385     } break;
00386     case 1 : {
00387         MP_TYPE(x,y) = CST_SUBSTATION_G;
00388     } break;
00389     default : {
00390         printf("Default case in do_power_substation\n");
00391     } break;
00392     }
00393 }
00394 
00395 int
00396 add_a_substation (int x, int y) /* add to substationx substationy to list */
00397 {
00398   if (numof_substations >= MAX_NUMOF_SUBSTATIONS)
00399     return (0);
00400   substationx[numof_substations] = x;
00401   substationy[numof_substations] = y;
00402   numof_substations++;
00403   return (1);
00404 }
00405 
00406 void
00407 remove_a_substation (int x, int y)
00408 {
00409   int q;
00410   for (q = 0; q < numof_substations; q++)
00411     if (substationx[q] == x && substationy[q] == y)
00412       break;
00413   for (; q < numof_substations; q++)
00414     {
00415       substationx[q] = substationx[q + 1];
00416       substationy[q] = substationy[q + 1];
00417     }
00418   numof_substations--;
00419 }
00420 
00421 void
00422 shuffle_substations (void)
00423 {
00424   int q, x, r, m;
00425   m = (numof_substations / 2) + 1;
00426   for (x = 0; x < m; x++)
00427     {
00428       r = rand () % numof_substations;
00429       if (r == x)
00430         continue;
00431       q = substationx[x];
00432       substationx[x] = substationx[r];
00433       substationx[r] = q;
00434       q = substationy[x];
00435       substationy[x] = substationy[r];
00436       substationy[r] = q;
00437     }
00438 }
00439 
00440 
00441 /*** Windmills ***/
00442 /*
00443   // int_1 is the rated capacity 
00444   // int_2 is the tech level when built
00445   // int_3 is the sail count - to choose the right sail.
00446   // int_4 is the last real time that a sail was turned
00447   // int_5 is the power produced (basically _if_ power produced)
00448   // int_6 is the grid it's on
00449   // int_7 is a timestamp for mapping
00450 */
00451 void
00452 do_windmill (int x, int y) 
00453 {
00454   int anim_tile; 
00455 
00456   if (get_jobs (x, y, WINDMILL_JOBS) != 0) {
00457     MP_INFO(x,y).int_5 = MP_INFO(x,y).int_1;
00458     grid[MP_INFO(x,y).int_6]->avail_power += MP_INFO(x,y).int_1;
00459   } else {
00460     MP_INFO(x,y).int_4 = real_time + MODERN_WINDMILL_ANIM_SPEED;
00461     return;
00462   }
00463 
00464   /* update animation */
00465   if (real_time > MP_INFO(x,y).int_4) {
00466     MP_INFO(x,y).int_3++;
00467     if (MP_INFO(x,y).int_2 < MODERN_WINDMILL_TECH) {
00468       MP_INFO(x,y).int_4 = real_time + ANTIQUE_WINDMILL_ANIM_SPEED;
00469     } else {
00470       MP_INFO(x,y).int_4 = real_time + MODERN_WINDMILL_ANIM_SPEED;
00471     }
00472   }
00473 
00474   /* figure out which tile to use */
00475   anim_tile = (MP_INFO(x,y).int_3 % 3);
00476 
00477   if (MP_INFO(x,y).int_2 < MODERN_WINDMILL_TECH)
00478     MP_TYPE(x,y) = CST_WINDMILL_1_W + anim_tile;
00479   else
00480     switch(grid[MP_INFO(x,y).int_6]->powered) 
00481       {
00482       case -1: 
00483         MP_TYPE(x,y) = CST_WINDMILL_1_R + anim_tile; 
00484         break;
00485       case 0 : 
00486         MP_TYPE(x,y) = CST_WINDMILL_1_RG + anim_tile;
00487         break;
00488       case 1 : 
00489         MP_TYPE(x,y) = CST_WINDMILL_1_G + anim_tile;
00490         break;
00491       default : 
00492         printf("Default case in do_power_substation\n");
00493         break;
00494       }      
00495 }
00496 
00497 
00498 /*** Solar Power ***/
00499 
00500 /*
00501   int_1 is the power to give away,  this must be >= POWER_LINE_CAPACITY
00502   for it to pass it to a power line. 
00503   Not added to if > POWER_LINE_CAPACITY
00504   int_2 is the tech level when it was built.
00505   int_3 is the rated output
00506   int_5 is the current output
00507   int_6 is the grid it's on
00508   int_7 is a grid mapping timestamp
00509 */
00510 
00511 void
00512 do_power_source (int x, int y)
00513 {
00514     if (get_jobs(x, y, SOLAR_POWER_JOBS)) {
00515         MP_INFO(x,y).int_5 = MP_INFO(x,y).int_3;
00516         grid[MP_INFO(x,y).int_6]->avail_power += MP_INFO(x,y).int_3;
00517     } else {
00518         MP_INFO(x,y).int_5 = 0;
00519     }
00520 }
00521 
00522 
00523 /*** Coal Power ***/
00524 /*
00525   // int_1 is the maximum possible power
00526   // int_2 is the coal at the power station
00527   // int_3 is the stored jobs... Must be an interesting warehouse
00528   // int_4 is the tech level when built
00529   // int_5 is the projected output.
00530 */
00531 
00532 
00533 void
00534 do_power_source_coal (int x, int y)
00535 {
00536 
00537     /* Need coal?  Try transport. */
00538     if (MP_INFO(x,y).int_2 < MAX_COAL_AT_POWER_STATION) {
00539 
00540         /* left side */
00541         if (XY_IS_TRANSPORT(x-1, y+1) && MP_INFO(x-1, y+1).int_3 > 0) {
00542             if (get_jobs (x, y, JOBS_LOAD_COAL) != 0)
00543             {
00544                 MP_INFO(x,y).int_2 += (MP_INFO(x-1, y+1).int_3 / 2
00545                                        + ((MP_INFO(x-1, y+1).int_3) % 2));
00546                 MP_INFO(x-1, y+1).int_3 /= 2;
00547                 MP_POL(x,y)++;
00548             }
00549         }
00550         /* top side */
00551         else if (XY_IS_TRANSPORT(x+1, y-1) && MP_INFO(x+1, y-1).int_3 > 0) {
00552             if (get_jobs (x, y, JOBS_LOAD_COAL) != 0)
00553                 MP_INFO(x,y).int_2 += (MP_INFO(x+1, y-1).int_3 / 2
00554                                        + ((MP_INFO(x+1, y-1).int_3) % 2));
00555             MP_INFO(x + 1,y - 1).int_3 /= 2;
00556             MP_POL(x,y)++;
00557         }
00558     }
00559 
00560     /* Need jobs?  get_jobs. */
00561     if ((MP_INFO(x,y).int_3 + JOBS_COALPS_GENERATE + 10)
00562         < MAX_JOBS_AT_COALPS)
00563         if (get_jobs (x, y, JOBS_COALPS_GENERATE + 10) != 0)
00564             MP_INFO(x,y).int_3 += JOBS_COALPS_GENERATE + 10;
00565 
00566     /* Generate Power */
00567     if (MP_INFO(x,y).int_2 > POWERS_COAL_OUTPUT / 500 &&
00568         MP_INFO(x,y).int_3 > JOBS_COALPS_GENERATE) 
00569     {
00570         MP_INFO(x,y).int_5 = MP_INFO(x,y).int_1;
00571         MP_INFO(x,y).int_3 -= JOBS_COALPS_GENERATE;
00572         MP_INFO(x,y).int_2 -= POWERS_COAL_OUTPUT / 500;
00573         coal_used += POWERS_COAL_OUTPUT / 500;
00574         MP_POL(x,y) += POWERS_COAL_POLLUTION;
00575         grid[MP_INFO(x,y).int_6]->avail_power += MP_INFO(x,y).int_1;
00576     }
00577 
00578     /* Animation */
00579     /* choose a graphic */
00580     if (MP_INFO(x,y).int_2 > (MAX_COAL_AT_POWER_STATION
00581                               - (MAX_COAL_AT_POWER_STATION / 5)))
00582         MP_TYPE(x,y) = CST_POWERS_COAL_FULL;
00583     else if (MP_INFO(x,y).int_2 > (MAX_COAL_AT_POWER_STATION / 2))
00584         MP_TYPE(x,y) = CST_POWERS_COAL_MED;
00585     else if (MP_INFO(x,y).int_2 > (MAX_COAL_AT_POWER_STATION / 10))
00586         MP_TYPE(x,y) = CST_POWERS_COAL_LOW;
00587     else
00588         MP_TYPE(x,y) = CST_POWERS_COAL_EMPTY;
00589 }
00590 
00591 
00592 /*** Power Lines ***/
00593 /*
00594   int_5 is animation schedule
00595   int_6 is the grid it is on
00596   int_7 is a grid timestamp
00597 */
00598 void
00599 do_power_line (int x, int y)
00600 {
00601     if (grid[MP_INFO(x,y).int_6]->powered == -1)
00602         return;
00603 
00604     switch(MP_INFO(x,y).int_5) 
00605     {
00606     case 0: 
00607         MP_INFO(x,y).int_5 = POWER_MODULUS;
00608         break;
00609     case 1:
00610         if (!(MP_TYPE(x,y) <= 11 && MP_TYPE(x,y) >= 1))
00611             break;
00612         MP_TYPE(x,y) += 11;
00613         break;
00614     case 2:
00615         if (!(MP_TYPE(x,y) >= 11 && MP_TYPE(x,y) <= 22))
00616             break;
00617         MP_TYPE(x,y) -= 11;
00618         break;
00619     }
00620 
00621     MP_INFO(x,y).int_5--;
00622 }

Generated on Sun Dec 26 11:23:26 2004 for lincity by  doxygen 1.3.9.1