Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

src/light.c

Go to the documentation of this file.
00001 /* {{{
00002  * CalcRogue, a roguelike game for PCs, calculators and PDAs
00003  * Copyright (C) 2003 Jim Babcock
00004  * 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  * }}} */
00019 // light.c
00026 
00027 #include "crogue.h"
00028 
00029 #define MAX_LIGHT_RADIUS 15
00030 #define MAX_LIGHT_RADIUS_SQUARED 225
00031 
00032 static void calc_light_tile(int x, int y);
00033 
00034 //{{{
00035 void calc_light(void)
00036 /*
00037  * As you can see, this function is *ugly*. That's because the problem domain
00038  * is a little ugly, and because it's speed critical.
00039  */
00040 {
00041     int xi, yi;
00042     int min_x, min_y;
00043     int max_x, max_y;
00044     
00045     min_x = w->plr.x - MAX_LIGHT_RADIUS;
00046     if(min_x < 0) min_x = 0;
00047     max_x = w->plr.x + MAX_LIGHT_RADIUS;
00048     if(max_x >= MAPSIZE_X) max_x = MAPSIZE_X - 1;
00049     min_y = w->plr.y - MAX_LIGHT_RADIUS;
00050     if(min_y < 0) min_y = 0;
00051     max_y = w->plr.y + MAX_LIGHT_RADIUS;
00052     if(max_y >= MAPSIZE_Y) max_y = MAPSIZE_Y - 1;
00053     
00054     for(yi=min_y; yi<=max_y; yi++)
00055     for(xi=min_x; xi<=max_x; xi++)
00056     {
00057         calc_light_tile(xi, yi);
00058     }
00059 }
00060 //}}}
00061 //{{{
00062 static void calc_light_tile(int x, int y)
00063 {
00064     int distance;
00065     int is_in_range;
00066     int change;
00067     
00068     distance = distancesquare(x, y, w->plr.x, w->plr.y);
00069     
00070     if(distance <= w->plr.extrinsic[STAT_LIGHTRADIUS])
00071         is_in_range = 1;
00072     else
00073         is_in_range = 0;
00074     
00075     // Find lit tiles to unlight
00076     if(w->t[y][x].flags & TFLAG_LIT)
00077     {
00078         if(w->t[y][x].flags & TFLAG_INTRINSIC_LIGHT)
00079         {
00080             if( distance > MAX_LIGHT_RADIUS_SQUARED ||
00081                 !tracevision(x, y, w->plr.x, w->plr.y) )
00082             {
00083                 w->t[y][x].flags &= ~TFLAG_LIT;
00084                 draw_tile(x, y);
00085             }
00086         } else {
00087             if( !is_in_range ||
00088                 !tracevision(x, y, w->plr.x, w->plr.y) )
00089             {
00090                 w->t[y][x].flags &= ~TFLAG_LIT;
00091                 draw_tile(x, y);
00092             }
00093         }
00094     }
00095     // Find unlit tiles to light
00096     else if(!(w->t[y][x].flags & TFLAG_LIT))
00097     {
00098         change = 0;
00099         
00100         if(w->t[y][x].flags & TFLAG_INTRINSIC_LIGHT)
00101         {
00102             if(distance > MAX_LIGHT_RADIUS_SQUARED) return;
00103             
00104             if(TILEDESC(w->t[y][x]).transparent) {
00105                 change = tracevision(x, y, w->plr.x, w->plr.y);
00106             } else if(is_in_range) {
00107                 change = tracevision(x, y, w->plr.x, w->plr.y);
00108             } else {
00109                 int xd=x, yd=y;
00110                 
00111                 if(w->plr.x > x)      xd++;
00112                 else if(w->plr.x < x) xd--;
00113                 if(w->plr.y > y)      yd++;
00114                 else if(w->plr.y < y) yd--;
00115                 
00116                 if(w->t[yd][xd].flags & TFLAG_INTRINSIC_LIGHT)
00117                     change = tracevision(x, y, w->plr.x, w->plr.y);
00118             }
00119         }
00120         else
00121         {
00122             if(is_in_range && tracevision(x, y, w->plr.x, w->plr.y))
00123                 change = 1;
00124         }
00125         if(change)
00126         {
00127             w->t[y][x].flags |= (TFLAG_LIT | TFLAG_EXPLORED);
00128             draw_tile(x, y);
00129         }
00130     }
00131 }
00132 //}}}
00133 //{{{
00134 ushort tracevision(sshort x1, sshort y1, sshort x2, sshort y2)
00135 /*
00136  * Check whether the straight path from (x1,y1) to (x2,y2) is blocked (but
00137  * with a special case when (x1,y1) is an opaque tile itself).
00138  *
00139  * This is the most speed-critical function in the entire project. Kluge with
00140  * care. Also, pay attention to the sizes of struct tile and struct tiledesc
00141  * - if they're not powers of 2, this function will be much slower than it
00142  * should be due to extra address calculations.
00143  */
00144 {
00145     sshort xdist, ydist;
00146     sshort xi, yi;
00147     sshort xdir, ydir;
00148     
00149     xdir=(x1<x2)?1 : (x1==x2)?0:-1;
00150     ydir=(y1<y2)?1 : (y1==y2)?0:-1;
00151     
00152     // Hack: If tracing to a non-transparent tile (such as a wall), move one
00153     // tile towards the player. This solves the corner and edge problems
00154     // without introducing any noticeable false positives
00155     if( !w->tiledescs[w->t[y1][x1].type].transparent ) {
00156         x1 += xdir;
00157         y1 += ydir;
00158     }
00159     
00160     xdist=abs(x1-x2); ydist=abs(y1-y2);
00161     xi=(ydist/2); yi=(xdist/2);
00162     
00163     while(x1!=x2 || y1!=y2)
00164     {
00165         if( !w->tiledescs[w->t[y1][x1].type].transparent )
00166             return 0;
00167         
00168         xi+=xdist; yi+=ydist;
00169         if(xi>=ydist) {
00170             x1 += xdir;
00171             xi -= ydist;
00172         }
00173         if(yi>=xdist) {
00174             y1 += ydir;
00175             yi -= xdist;
00176         }
00177     }
00178     
00179     return 1;
00180 }
00181 //}}}
00182 //{{{
00183 void illuminate(sint x, sint y, sint radiussquared)
00184 /*
00185  * Mark all tiles within sqrt(radiussquared) of (x,y) which can see (x,y) as
00186  * intrinsically lit. Used for lighting up rooms.
00187  */
00188 {
00189     int i, j;
00190     
00191     for(i=0; i<MAPSIZE_Y; i++)
00192     for(j=0; j<MAPSIZE_X; j++)
00193     {
00194         if( (x-j)*(x-j)+(y-i)*(y-i) <= radiussquared && tracevision(j,i,x,y) )
00195             w->t[i][j].flags |= TFLAG_INTRINSIC_LIGHT;
00196     }
00197 }
00198 //}}}
00199 

Generated on Thu May 20 13:12:10 2004 for CalcRogue by doxygen 1.3.6