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

src/mkmap.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 // mkmap.c
00025 
00026 #include "crogue.h"
00027 #include "export.h"
00028 
00029 static const schar simpledir[4][2] = {
00030             {0,-1}, {1,0}, {0,1}, {-1,0}    // Ordered clockwise
00031     };
00032 static const schar cornerdir[4][2] = {
00033         {-1,-1},        {1,-1},
00034         
00035         {-1,1},         {1,1}
00036     };
00037 const schar dir[8][2] = {
00038         {-1,-1},    {0,-1},     {1,-1},
00039         {-1,0},                 {1,0},
00040         {-1,1},     {0,1},      {1,1}
00041     };
00042 
00043 void gen_map(int source, coord pos);
00044 void mkmap(map_gen_method *desc);
00045 void base_coat(void);
00046 sint place_room(room *r);
00047 void place_hole(void);
00048 void make_noise(ushort floor, ushort wall);
00049 void cave_generation(ushort floor, ushort wall);
00050 sint place_corridors(void);
00051 void fill_room(room *r);
00052 //static void light_room(room r);
00053 //static void place_shop(room r, int portal_x, int portal_y);
00054 void place_doors(void);
00055 //static void place_fountain(int x, int y);
00056 void place_items(void);
00057 void make_connections(void);
00058 void place_player_on_map(int source, coord pos);
00059 static void connection_fill(uint x, uint y, uint s);
00060 
00061 
00062 //{{{
00063 void gen_map(int source, coord pos)
00064 {
00065     int i;
00066     map_gen_method *desc;
00067     
00068     cleanup_items();
00069     init_items();
00070     
00071     desc = (map_gen_method*)deref_file_ptr(MAPDESC_CURRENT.genmethod);
00072     resize_map_store(desc->size_x, desc->size_y);
00073     
00074     w->plr.score += (MAPDESC_CURRENT.difficulty - 1) * LEVEL_SCORE_FACTOR;
00075     call_genericfunc(MAPDESC_CURRENT.genfunction);
00076     
00077     place_player_on_map(source, pos);
00078     
00079     for(i=0; i<MAPDESC_CURRENT.initial_population; i++)
00080         addrandommonst();
00081 }
00082 //}}}
00083 
00084 //{{{
00085 int mkmap_big_front(void) // Pass the call onto the DLL
00086 {
00087     return mkmap_big();
00088 }
00089 //}}}
00090 
00091 //{{{
00092 void base_coat(void)
00093 {
00094     map_gen_method *desc;
00095     int xi, yi;
00096     
00097     desc = (map_gen_method*)deref_file_ptr(MAPDESC_CURRENT.genmethod);
00098     
00099     rle_set_source(rle_pointer, desc->data);
00100     
00101     for(yi=0; yi<MAPSIZE_Y; yi++)
00102     for(xi=0; xi<MAPSIZE_X; xi++)
00103     {
00104         w->t[yi][xi].type = rlegetc();
00105         
00106         if(w->t[yi][xi].type == TILE_BLANK)
00107         {
00108             w->t[yi][xi].type = TILE_WALL;
00109             w->t[yi][xi].flags = TFLAG_MUTABLE;
00110         }
00111         else
00112         {
00113             w->t[yi][xi].flags = 0;
00114             if(desc->global_light)
00115                 w->t[yi][xi].flags |= TFLAG_INTRINSIC_LIGHT;
00116         }
00117         
00118         w->t[yi][xi].special = 0;
00119     }
00120     
00121     // Clear monster table
00122     for(yi=0; yi<MONSTERS_MAX; yi++) {
00123         w->m[yi].type = filelink_null;
00124         w->m[yi].energy = 0;
00125     }
00126 
00127 }
00128 //}}}
00129 //{{{
00130 // Returns 1 (success) or 0 (failure). If it fails, the map is unchanged, but
00131 // placement failed after too many attempts.
00132 // Only affects areas marked as mutable, and changes area used to non-mutable.
00133 sint place_room(room *r)
00134 {
00135     int retrycount=0;
00136     int x, y;
00137     
00138 retry:
00139     r->left = RANGE(MAPSIZE_X-6, 1);
00140     r->right = min(r->left+RANGE(8,3), MAPSIZE_X-2);
00141     r->top = RANGE(MAPSIZE_Y-4, 1);
00142     r->bottom = min(r->top+RANGE(6,3), MAPSIZE_Y-2);
00143     
00144     // Check for collisions with existing rooms
00145     for(y=r->top-1; y<=r->bottom+1; y++)
00146         for(x=r->left-1; x<=r->right+1; x++)
00147             if( !(w->t[y][x].flags & TFLAG_MUTABLE) )
00148             {
00149                 retrycount++;
00150                 if( retrycount>200 )
00151                     return 0;
00152                 else
00153                     goto retry;
00154             }
00155     
00156     for(y=r->top; y<=r->bottom; y++)
00157         for(x=r->left; x<=r->right; x++)
00158         {
00159             w->t[y][x].flags &= ~TFLAG_MUTABLE;
00160             w->t[y][x].type=TILE_FLOOR;
00161         }
00162     
00163     return 1;
00164 }
00165 //}}}
00166 //{{{
00167 void place_hole(void)
00168 {
00169     int x, y;
00170     
00171     do
00172     {
00173         x = RANGE(MAPSIZE_X-2, 1);
00174         y = RANGE(MAPSIZE_Y-2, 1);
00175     } while(!(w->t[y][x].flags & TFLAG_MUTABLE));
00176     
00177     w->t[y][x].type = TILE_FLOOR;
00178     w->t[y][x].flags &= ~TFLAG_MUTABLE;
00179 }
00180 //}}}
00181 //{{{
00182 void make_noise(ushort floor, ushort wall)
00183 {
00184     int x, y;
00185     
00186     for(y=0; y<MAPSIZE_Y; y++)
00187         for(x=0; x<MAPSIZE_X; x++)
00188             if(w->t[y][x].flags & TFLAG_MUTABLE)
00189             {
00190                 if(RANGE(100, 0) >= 45)
00191                     w->t[y][x].type = floor;
00192                 else
00193                     w->t[y][x].type = wall;
00194             }
00195 }
00196 //}}}
00197 //{{{
00198 void cave_pass(ushort floor, ushort wall, void (*effect)(ushort floor, ushort wall, int x, int y, int neighbors))
00199 {
00200     int adjcount;
00201     int i, ii;
00202     int x, y;
00203     
00204     for(y=1; y<MAPSIZE_Y-1; y++)
00205     for(x=1; x<MAPSIZE_X-1; x++)
00206     {
00207         if( !(w->t[y][x].flags & TFLAG_MUTABLE) )
00208             continue;
00209         
00210         adjcount=0;
00211         for(i=-1; i<=1; i++)
00212             for(ii=-1; ii<=1; ii++)
00213                 if( w->t[y+i][x+ii].type != floor )
00214                     adjcount++;
00215         
00216         effect(floor, wall, x, y, adjcount);
00217     }
00218 }
00219 //}}}
00220 //{{{
00221 void cb_cave_partial(ushort floor, ushort wall, int x, int y, int neighbors)
00222 {
00223     if(neighbors >= nrandom(9,0))
00224         w->t[y][x].type = wall;
00225     else
00226         w->t[y][x].type = floor;
00227 }
00228 //}}}
00229 //{{{
00230 void cave_partial_gen(ushort floor, ushort wall)
00231 {
00232     cave_pass(floor, wall, cb_cave_partial);
00233 /*  int adjcount;
00234     int i, ii;
00235     int x, y;
00236     
00237     for(y=1; y<MAPSIZE_Y-1; y++)
00238     for(x=1; x<MAPSIZE_X-1; x++)
00239     {
00240         if( !(w->t[y][x].flags & TFLAG_MUTABLE) )
00241             continue;
00242         
00243         adjcount=0;
00244         for(i=-1; i<=1; i++)
00245             for(ii=-1; ii<=1; ii++)
00246                 if( w->t[y+i][x+ii].type != floor )
00247                     adjcount++;
00248         
00249         if(adjcount >= nrandom(9,0))
00250             w->t[y][x].type = wall;
00251         else
00252             w->t[y][x].type = floor;
00253     }*/
00254 }
00255 //}}}
00256 //{{{
00257 void cb_cave(ushort floor, ushort wall, int x, int y, int neighbors)
00258 {
00259     if(neighbors >= 5)
00260         w->t[y][x].type = wall;
00261     else
00262         w->t[y][x].type = floor;
00263 }
00264 //}}}
00265 //{{{
00266 void cave_generation(ushort floor, ushort wall)
00267 {
00268     cave_pass(floor, wall, cb_cave);
00269 /*  int adjcount;
00270     int i, ii;
00271     int x, y;
00272     
00273     for(y=1; y<MAPSIZE_Y-1; y++)
00274     for(x=1; x<MAPSIZE_X-1; x++)
00275     {
00276         if( !(w->t[y][x].flags & TFLAG_MUTABLE) )
00277             continue;
00278         
00279         adjcount=0;
00280         for(i=-1; i<=1; i++)
00281             for(ii=-1; ii<=1; ii++)
00282                 if( w->t[y+i][x+ii].type != floor )
00283                     adjcount++;
00284         
00285         if(adjcount >= 5)
00286             w->t[y][x].type = wall;
00287         else
00288             w->t[y][x].type = floor;
00289     }*/
00290 }
00291 //}}}
00292 static void replace(int from, int into);
00293 //{{{
00294 sint place_corridors(void)
00295 {
00296     int num_areas=0;
00297     int state=0; // 0: in wall, non-0: in open area
00298     int x, y;
00299     int type, otype;
00300     int tries=0;
00301     
00302     // Chop map into areas for connection
00303     for(y=0; y<MAPSIZE_Y; y++)
00304     {
00305         for(x=0; x<MAPSIZE_X; x++)
00306         {
00307             if(state) {
00308                 if( !TILEDESC(w->t[y][x]).passable ) {
00309                     state=0;
00310                 } else {
00311                     w->t[y][x].special = num_areas;
00312                 }
00313             } else {
00314                 if( TILEDESC(w->t[y][x]).passable ) {
00315                     state=1;
00316                     num_areas++;
00317                     w->t[y][x].special = num_areas;
00318                 }
00319             }
00320         }
00321         state=0;
00322     }
00323     
00324     // Merge already-connected areas
00325     for(y=1; y<MAPSIZE_Y-1; y++)
00326         for(x=1; x<MAPSIZE_X-1; x++) {
00327             type = w->t[y][x].special;
00328             otype = w->t[y+1][x].special;
00329             if( type!=0 && otype!=0 && type!=otype ) {
00330                 num_areas--;
00331                 replace(otype, type);
00332             }
00333             otype = w->t[y][x+1].special;
00334             if( type!=0 && otype!=0 && type!=otype ) {
00335                 num_areas--;
00336                 replace(otype, type);
00337             }
00338         }
00339     
00340     // Make corridors
00341     do {
00342         // If a randomly-selected ROW connects two areas, make a tunnel
00343         y = RANGE(MAPSIZE_Y-2, 1);
00344         type = otype = 0;
00345         for(x=1; x<MAPSIZE_X-1; x++) {
00346             if( w->t[y][x].special) {
00347                 if(!otype)
00348                     otype = w->t[y][x].special;
00349                 else {
00350                     type = w->t[y][x].special;
00351                     if(type==otype) continue;
00352                     
00353                     for(;;x--) {
00354                         if(w->t[y][x].special == otype) break;
00355                         w->t[y][x].type = TILE_FLOOR;
00356 //                      w->t[y][x].flags &= ~TFLAG_MUTABLE;
00357                         w->t[y][x].special = type;
00358                     }
00359                     replace(type, otype);
00360                     num_areas--;
00361                     break;
00362                 }
00363             }
00364         }
00365         
00366         // If a randomly-selected COLUMN connects two areas, make a tunnel
00367         x = RANGE( MAPSIZE_X-2, 1);
00368         type = otype = 0;
00369         for(y=1; y<MAPSIZE_Y-1; y++) {
00370             if( w->t[y][x].special ) {
00371                 if(!otype)
00372                     otype = w->t[y][x].special;
00373                 else {
00374                     type = w->t[y][x].special;
00375                     if(type==otype) continue;
00376                     
00377                     for(;;y--) {
00378                         if(w->t[y][x].special == otype) break;
00379                         w->t[y][x].type = TILE_FLOOR;
00380 //                      w->t[y][x].flags &= ~TFLAG_MUTABLE;
00381                         w->t[y][x].special = type;
00382                     }
00383                     replace(type, otype);
00384                     num_areas--;
00385                     break;
00386                 }
00387             }
00388         }
00389         
00390         tries++;
00391         if(tries>750) {
00392             return 0;
00393         }
00394         
00395     } while(num_areas>1);
00396     
00397     for(y=0; y<MAPSIZE_Y; y++)
00398         for(x=0; x<MAPSIZE_X; x++)
00399         {
00400             w->t[y][x].special = 0;
00401         }
00402     
00403     return 1;
00404 }
00405 
00406 static void replace(int from, int into)
00407 {
00408     int x, y;
00409     
00410     for(y=0; y<MAPSIZE_Y; y++)
00411     for(x=0; x<MAPSIZE_X; x++)
00412         if(w->t[y][x].special == from)
00413             w->t[y][x].special = into;
00414 }
00415 //}}}
00416 //{{{
00417 void fill_room(room *r)
00418 {
00419     dll_fill_room(r);
00420 }
00421 //}}}
00422 //{{{
00423 void place_doors(void)
00424 {
00425     int x, y;
00426     
00427     // A tile becomes a door if...
00428     // No doors on adjacent tiles
00429     // Two opposing EDGE tiles are walls
00430     // Two opposing EDGE tiles are empty space
00431     // At least one CORNER tile is space
00432     // At least one CORNER tile is a wall
00433     for(y=1; y<MAPSIZE_Y-1; y++)
00434     for(x=1; x<MAPSIZE_X-1; x++)
00435     {
00436         if( !(w->t[y][x].flags & TFLAG_MUTABLE) )
00437             continue;
00438         
00439         if(make_door(x, y)) {
00440             if(RANGE(7,0)==0)   // 1 in 8 chance a door becomes secret
00441                 w->t[y][x].type = TILE_SDOOR;
00442             else
00443                 w->t[y][x].type = TILE_CDOOR;
00444         }
00445     }
00446 }
00447 //}}}
00448 //{{{
00449 int make_door(int x, int y)
00450 {
00451     int i;
00452     int result;
00453     tile *type, *otype;
00454     
00455     // A tile becomes a door if...
00456     // No doors on adjacent tiles
00457     // Two opposing EDGE tiles are walls
00458     // Two opposing EDGE tiles are empty space
00459     // At least one CORNER tile is space
00460     // At least one CORNER tile is a wall
00461     
00462     // Simplified version of first three rules above: simpledir rotation
00463     // clockwise alternates between wall and non-wall
00464     otype =  &w->t[y+simpledir[0][1]][x+simpledir[0][0]];
00465     if(TILEDESC(*otype).is_door) return 0;
00466     
00467     for(i=1; i<4; i++) {
00468         type = &w->t[y+simpledir[i][1]][x+simpledir[i][0]];
00469         if(TILEDESC(*type).is_wall == TILEDESC(*otype).is_wall || TILEDESC(*type).is_door)
00470             return 0;
00471         otype = type;
00472     }
00473     
00474     result=0;
00475     for(i=0; i<3; i++)
00476     {
00477         type = &w->t[y+cornerdir[i][1]][x+cornerdir[i][0]];
00478         if( TILEDESC(*type).passable )
00479             result|=1;
00480         if( TILEDESC(*type).is_wall )
00481             result|=2;
00482     }
00483     
00484     if(result==3)
00485         return 1;
00486     else
00487         return 0;
00488 }
00489 //}}}
00490 //{{{
00491 void place_items(void)
00492 {
00493     int yi=0, xi=0;
00494     int ii;
00495     int num_items = rrandom(MAPDESC_CURRENT.num_items);
00496     item itm;
00497     
00498     for(ii=0; ii<num_items; ii++)
00499     {
00500         do {
00501             xi = nrandom(MAPSIZE_X-2, 1);
00502             yi = nrandom(MAPSIZE_Y-2, 1);
00503         } while( !w->tiledescs[ w->t[yi][xi].type ].allow_item
00504                  || w->t[yi][xi].flags & TFLAG_ITEM );
00505         
00506         itm = randitem(w->desc.generate_item);
00507         place_item(&itm, xi, yi);
00508     }
00509     
00510 /*  for(; yi<MAPSIZE_Y; yi++)
00511     for(xi=0; xi<MAPSIZE_X; xi++)
00512     {
00513         if(w->tiledescs[w->t[yi][xi].type].allow_item &&
00514            RANGE(35,0)==0)
00515         {
00516             itm = randitem(w->desc.generate_item);
00517             place_item(&itm, xi, yi);
00518         }
00519     }*/
00520 }
00521 //}}}
00522 //{{{
00523 void make_connections(void)
00524 {
00525     int x, y, i;
00526     const spec_descriptor *specs;
00527     item itm;
00528     
00529 //  specs = (spec_descriptor*)deref_file_ptr(MAPDESC_CURRENT.special);
00530     specs = MAPDESC_CURRENT.ext;
00531     
00532     // Place connections
00533     for( i=0; i < MAPDESC_CURRENT.numspecial; i++ )
00534     {
00535         if( specs[i].location.x==0 && specs[i].location.y == 0 ) {  // Random location
00536             do {
00537                 x = RANGE(MAPSIZE_X-2, 2);
00538                 y = RANGE(MAPSIZE_Y-2, 2);
00539             } while(w->t[y][x].type != TILE_FLOOR);
00540         } else {
00541             x = specs[i].location.x;
00542             y = specs[i].location.y;
00543         }
00544         if( specs[i].type >= 0 )
00545         {
00546             w->t[y][x].type = specs[i].type;
00547             w->t[y][x].special = i;
00548         } else if( specs[i].type == MAPSPEC_ITEM ) {
00549             itm = randitem(specs[i].ext.spec_item);
00550             place_item(&itm, x, y);
00551         } else if( specs[i].type == MAPSPEC_MONSTER ) {
00552             addmonster(x, y, specs[i].ext.spec_monster);
00553         } else if( specs[i].type == MAPSPEC_CONNECT ) {
00554             connection_fill(x, y, i);
00555         }
00556     }
00557 }
00558 //}}}
00559 //{{{
00560 void place_player_on_map(int source, coord pos)
00561 {
00562     int x, y;
00563     const spec_descriptor *connections;
00564     
00565     connections = MAPDESC_CURRENT.ext;
00566     
00567     // Place the player
00568     if( pos.x || pos.y )
00569     {
00570         w->plr.x = pos.x;
00571         w->plr.y = pos.y;
00572         w->t[w->plr.y][w->plr.x].flags |= TFLAG_OCCUPIED;
00573         return;
00574     }
00575     else
00576     {
00577         for(x=0; x<MAPSIZE_X; x++)
00578             for(y=0; y<MAPSIZE_Y; y++) {
00579                 if( TILEDESC(w->t[y][x]).is_connection &&
00580                     ((mapdesc*)deref_file_ptr(MAPDESC_CURRENT.ext[w->t[y][x].special].ext.lvl.destmap))->mapindex == source )
00581                 {
00582                     w->plr.x = x;
00583                     w->plr.y = y;
00584                     w->t[y][x].flags |= TFLAG_OCCUPIED;
00585                     return;
00586                 }
00587             }
00588     }
00589     
00590     place_player_randomly();
00591 }
00592 //}}}
00593 
00594 //{{{
00595 static void connection_fill(uint x, uint y, uint s)
00596 {
00597     int xi, yi;
00598     int left, right, top, bottom;
00599     w->t[y][x].special = s;
00600     w->t[y][x].flags |= TFLAG_TEMP;
00601     
00602     left = x-1;
00603     if(left<0) left=0;
00604     right = x+1;
00605     if(right>=MAPSIZE_X) right=MAPSIZE_X-1;
00606     top = y-1;
00607     if(top<0) top=0;
00608     bottom = y+1;
00609     if(bottom>=MAPSIZE_Y) bottom=MAPSIZE_Y-1;
00610     
00611     for(yi=top; yi<=bottom; yi++)
00612     for(xi=left; xi<=right; xi++)
00613     {
00614         if(TILEDESC(w->t[yi][xi]).is_connection &&
00615                 !(w->t[yi][xi].flags & TFLAG_TEMP) )
00616             connection_fill(xi, yi, s);
00617     }
00618 }
00619 //}}}

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