%{ /* SCCS Id: @(#)dgn_comp.c 3.4 1996/06/22 */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* Copyright (c) 1990 by M. Stephenson */ /* NetHack may be freely redistributed. See license for details. */ /* * This file contains the Dungeon Compiler code */ /* In case we're using bison in AIX. This definition must be * placed before any other C-language construct in the file * excluding comments and preprocessor directives (thanks IBM * for this wonderful feature...). * * Note: some cpps barf on this 'undefined control' (#pragma). * Addition of the leading space seems to prevent barfage for now, * and AIX will still see the directive in its non-standard locale. */ #ifdef _AIX #pragma alloca /* keep leading space! */ #endif #include "config.h" #include "date.h" #include "dgn_file.h" void FDECL(yyerror, (const char *)); void FDECL(yywarning, (const char *)); int NDECL(yylex); int NDECL(yyparse); int FDECL(getchain, (char *)); int NDECL(check_dungeon); int NDECL(check_branch); int NDECL(check_level); void NDECL(init_dungeon); void NDECL(init_branch); void NDECL(init_level); void NDECL(output_dgn); #define Free(ptr) free((genericptr_t)ptr) #ifdef AMIGA # undef printf #ifndef LATTICE # define memset(addr,val,len) setmem(addr,len,val) #endif #endif #define ERR (-1) static struct couple couple; static struct tmpdungeon tmpdungeon[MAXDUNGEON]; static struct tmplevel tmplevel[LEV_LIMIT]; static struct tmpbranch tmpbranch[BRANCH_LIMIT]; static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1; extern int fatal_error; extern const char *fname; extern FILE *yyin, *yyout; /* from dgn_lex.c */ %} %union { int i; char* str; } %token INTEGER %token A_DUNGEON BRANCH CHBRANCH LEVEL RNDLEVEL CHLEVEL RNDCHLEVEL %token UP_OR_DOWN PROTOFILE DESCRIPTION DESCRIPTOR LEVELDESC %token ALIGNMENT LEVALIGN ENTRY STAIR NO_UP NO_DOWN PORTAL %token STRING %type optional_int direction branch_type bones_tag %start file %% file : /* nothing */ | dungeons { output_dgn(); } ; dungeons : dungeon | dungeons dungeon ; dungeon : dungeonline | dungeondesc | branches | levels ; dungeonline : A_DUNGEON ':' STRING bones_tag rcouple optional_int { init_dungeon(); Strcpy(tmpdungeon[n_dgns].name, $3); tmpdungeon[n_dgns].boneschar = (char)$4; tmpdungeon[n_dgns].lev.base = couple.base; tmpdungeon[n_dgns].lev.rand = couple.rand; tmpdungeon[n_dgns].chance = $6; Free($3); } ; optional_int : /* nothing */ { $$ = 0; } | INTEGER { $$ = $1; } ; dungeondesc : entry | descriptions | prototype ; entry : ENTRY ':' INTEGER { tmpdungeon[n_dgns].entry_lev = $3; } ; descriptions : desc ; desc : DESCRIPTION ':' DESCRIPTOR { if($3 <= TOWN || $3 >= D_ALIGN_CHAOTIC) yyerror("Illegal description - ignoring!"); else tmpdungeon[n_dgns].flags |= $3 ; } | ALIGNMENT ':' DESCRIPTOR { if($3 && $3 < D_ALIGN_CHAOTIC) yyerror("Illegal alignment - ignoring!"); else tmpdungeon[n_dgns].flags |= $3 ; } ; prototype : PROTOFILE ':' STRING { Strcpy(tmpdungeon[n_dgns].protoname, $3); Free($3); } ; levels : level1 | level2 | levdesc | chlevel1 | chlevel2 ; level1 : LEVEL ':' STRING bones_tag '@' acouple { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmpdungeon[n_dgns].levels++; Free($3); } | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].rndlevs = $7; tmpdungeon[n_dgns].levels++; Free($3); } ; level2 : LEVEL ':' STRING bones_tag '@' acouple INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].chance = $7; tmpdungeon[n_dgns].levels++; Free($3); } | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].chance = $7; tmplevel[n_levs].rndlevs = $8; tmpdungeon[n_dgns].levels++; Free($3); } ; levdesc : LEVELDESC ':' DESCRIPTOR { if($3 >= D_ALIGN_CHAOTIC) yyerror("Illegal description - ignoring!"); else tmplevel[n_levs].flags |= $3 ; } | LEVALIGN ':' DESCRIPTOR { if($3 && $3 < D_ALIGN_CHAOTIC) yyerror("Illegal alignment - ignoring!"); else tmplevel[n_levs].flags |= $3 ; } ; chlevel1 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].chain = getchain($5); tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; if(!check_level()) n_levs--; else tmpdungeon[n_dgns].levels++; Free($3); Free($5); } | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].chain = getchain($5); tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].rndlevs = $8; if(!check_level()) n_levs--; else tmpdungeon[n_dgns].levels++; Free($3); Free($5); } ; chlevel2 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].chain = getchain($5); tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].chance = $8; if(!check_level()) n_levs--; else tmpdungeon[n_dgns].levels++; Free($3); Free($5); } | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER { init_level(); Strcpy(tmplevel[n_levs].name, $3); tmplevel[n_levs].boneschar = (char)$4; tmplevel[n_levs].chain = getchain($5); tmplevel[n_levs].lev.base = couple.base; tmplevel[n_levs].lev.rand = couple.rand; tmplevel[n_levs].chance = $8; tmplevel[n_levs].rndlevs = $9; if(!check_level()) n_levs--; else tmpdungeon[n_dgns].levels++; Free($3); Free($5); } ; branches : branch | chbranch ; branch : BRANCH ':' STRING '@' acouple branch_type direction { init_branch(); Strcpy(tmpbranch[n_brs].name, $3); tmpbranch[n_brs].lev.base = couple.base; tmpbranch[n_brs].lev.rand = couple.rand; tmpbranch[n_brs].type = $6; tmpbranch[n_brs].up = $7; if(!check_branch()) n_brs--; else tmpdungeon[n_dgns].branches++; Free($3); } ; chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction { init_branch(); Strcpy(tmpbranch[n_brs].name, $3); tmpbranch[n_brs].chain = getchain($4); tmpbranch[n_brs].lev.base = couple.base; tmpbranch[n_brs].lev.rand = couple.rand; tmpbranch[n_brs].type = $7; tmpbranch[n_brs].up = $8; if(!check_branch()) n_brs--; else tmpdungeon[n_dgns].branches++; Free($3); Free($4); } ; branch_type : /* nothing */ { $$ = TBR_STAIR; /* two way stair */ } | STAIR { $$ = TBR_STAIR; /* two way stair */ } | NO_UP { $$ = TBR_NO_UP; /* no up staircase */ } | NO_DOWN { $$ = TBR_NO_DOWN; /* no down staircase */ } | PORTAL { $$ = TBR_PORTAL; /* portal connection */ } ; direction : /* nothing */ { $$ = 0; /* defaults to down */ } | UP_OR_DOWN { $$ = $1; } ; bones_tag : STRING { char *p = $1; if (strlen(p) != 1) { if (strcmp(p, "none") != 0) yyerror("Bones marker must be a single char, or \"none\"!"); *p = '\0'; } $$ = *p; Free(p); } ; /* * acouple rules: * * (base, range) where: * * base is either a positive or negative integer with a value * less than or equal to MAXLEVEL. * base > 0 indicates the base level. * base < 0 indicates reverse index (-1 == lowest level) * * range is the random component. * if range is zero, there is no random component. * if range is -1 the dungeon loader will randomize between * the base and the end of the dungeon. * during dungeon load, range is always *added* to the base, * therefore range + base(converted) must not exceed MAXLEVEL. */ acouple : '(' INTEGER ',' INTEGER ')' { if ($2 < -MAXLEVEL || $2 > MAXLEVEL) { yyerror("Abs base out of dlevel range - zeroing!"); couple.base = couple.rand = 0; } else if ($4 < -1 || (($2 < 0) ? (MAXLEVEL + $2 + $4 + 1) > MAXLEVEL : ($2 + $4) > MAXLEVEL)) { yyerror("Abs range out of dlevel range - zeroing!"); couple.base = couple.rand = 0; } else { couple.base = $2; couple.rand = $4; } } ; /* * rcouple rules: * * (base, range) where: * * base is either a positive or negative integer with a value * less than or equal to MAXLEVEL. * base > 0 indicates a forward index. * base < 0 indicates a reverse index. * base == 0 indicates on the parent level. * * range is the random component. * if range is zero, there is no random component. * during dungeon load, range is always *added* to the base, * range + base(converted) may be very large. The dungeon * loader will then correct to "between here and the top/bottom". * * There is no practical way of specifying "between here and the * nth / nth last level". */ rcouple : '(' INTEGER ',' INTEGER ')' { if ($2 < -MAXLEVEL || $2 > MAXLEVEL) { yyerror("Rel base out of dlevel range - zeroing!"); couple.base = couple.rand = 0; } else { couple.base = $2; couple.rand = $4; } } ; %% void init_dungeon() { if(++n_dgns > MAXDUNGEON) { (void) fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n", MAXDUNGEON); (void) fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n"); exit(EXIT_FAILURE); } in_dungeon = 1; tmpdungeon[n_dgns].lev.base = 0; tmpdungeon[n_dgns].lev.rand = 0; tmpdungeon[n_dgns].chance = 100; Strcpy(tmpdungeon[n_dgns].name, ""); Strcpy(tmpdungeon[n_dgns].protoname, ""); tmpdungeon[n_dgns].flags = 0; tmpdungeon[n_dgns].levels = 0; tmpdungeon[n_dgns].branches = 0; tmpdungeon[n_dgns].entry_lev = 0; } void init_level() { if(++n_levs > LEV_LIMIT) { yyerror("FATAL - Too many special levels defined."); exit(EXIT_FAILURE); } tmplevel[n_levs].lev.base = 0; tmplevel[n_levs].lev.rand = 0; tmplevel[n_levs].chance = 100; tmplevel[n_levs].rndlevs = 0; tmplevel[n_levs].flags = 0; Strcpy(tmplevel[n_levs].name, ""); tmplevel[n_levs].chain = -1; } void init_branch() { if(++n_brs > BRANCH_LIMIT) { yyerror("FATAL - Too many special levels defined."); exit(EXIT_FAILURE); } tmpbranch[n_brs].lev.base = 0; tmpbranch[n_brs].lev.rand = 0; Strcpy(tmpbranch[n_brs].name, ""); tmpbranch[n_brs].chain = -1; } int getchain(s) char *s; { int i; if(strlen(s)) { for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++) if(!strcmp(tmplevel[i].name, s)) return i; yyerror("Can't locate the specified chain level."); return(-2); } return(-1); } /* * Consistancy checking routines: * * - A dungeon must have a unique name. * - A dungeon must have a originating "branch" command * (except, of course, for the first dungeon). * - A dungeon must have a proper depth (at least (1, 0)). */ int check_dungeon() { int i; for(i = 0; i < n_dgns; i++) if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) { yyerror("Duplicate dungeon name."); return(0); } if(n_dgns) for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) { if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break; if(i >= n_brs - tmpdungeon[n_dgns].branches) { yyerror("Dungeon cannot be reached."); return(0); } } if(tmpdungeon[n_dgns].lev.base <= 0 || tmpdungeon[n_dgns].lev.rand < 0) { yyerror("Invalid dungeon depth specified."); return(0); } return(1); /* OK */ } /* * - A level must have a unique level name. * - If chained, the level used as reference for the chain * must be in this dungeon, must be previously defined, and * the level chained from must be "non-probabilistic" (ie. * have a 100% chance of existing). */ int check_level() { int i; if(!in_dungeon) { yyerror("Level defined outside of dungeon."); return(0); } for(i = 0; i < n_levs; i++) if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) { yyerror("Duplicate level name."); return(0); } if(tmplevel[i].chain == -2) { yyerror("Invaild level chain reference."); return(0); } else if(tmplevel[i].chain != -1) { /* there is a chain */ /* KMH -- tmplevel[tmpbranch[i].chain].chance was in error */ if(tmplevel[tmplevel[i].chain].chance != 100) { yyerror("Level cannot chain from a probabilistic level."); return(0); } else if(tmplevel[i].chain == n_levs) { yyerror("A level cannot chain to itself!"); return(0); } } return(1); /* OK */ } /* * - A branch may not branch backwards - to avoid branch loops. * - A branch name must be unique. * (ie. You can only have one entry point to each dungeon). * - If chained, the level used as reference for the chain * must be in this dungeon, must be previously defined, and * the level chained from must be "non-probabilistic" (ie. * have a 100% chance of existing). */ int check_branch() { int i; if(!in_dungeon) { yyerror("Branch defined outside of dungeon."); return(0); } for(i = 0; i < n_dgns; i++) if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) { yyerror("Reverse branching not allowed."); return(0); } if(tmpbranch[i].chain == -2) { yyerror("Invaild branch chain reference."); return(0); } else if(tmpbranch[i].chain != -1) { /* it is chained */ if(tmplevel[tmpbranch[i].chain].chance != 100) { yyerror("Branch cannot chain from a probabilistic level."); return(0); } } return(1); /* OK */ } /* * Output the dungon definition into a file. * * The file will have the following format: * * [ nethack version ID ] * [ number of dungeons ] * [ first dungeon struct ] * [ levels for the first dungeon ] * ... * [ branches for the first dungeon ] * ... * [ second dungeon struct ] * ... */ void output_dgn() { int nd, cl = 0, nl = 0, cb = 0, nb = 0; static struct version_info version_data = { VERSION_NUMBER, VERSION_FEATURES, VERSION_SANITY1, VERSION_SANITY2 }; if(++n_dgns <= 0) { yyerror("FATAL - no dungeons were defined."); exit(EXIT_FAILURE); } if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) { yyerror("FATAL - output failure."); exit(EXIT_FAILURE); } (void) fwrite((char *)&n_dgns, sizeof(int), 1, yyout); for (nd = 0; nd < n_dgns; nd++) { (void) fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon), 1, yyout); nl += tmpdungeon[nd].levels; for(; cl < nl; cl++) (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel), 1, yyout); nb += tmpdungeon[nd].branches; for(; cb < nb; cb++) (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch), 1, yyout); } /* apparently necessary for Think C 5.x, otherwise harmless */ (void) fflush(yyout); } /*dgn_comp.y*/