2 /* SCCS Id: @(#)dgn_comp.c 3.4 1996/06/22 */
3 /* Copyright (c) 1989 by Jean-Christophe Collet */
4 /* Copyright (c) 1990 by M. Stephenson */
5 /* NetHack may be freely redistributed. See license for details. */
8 * This file contains the Dungeon Compiler code
11 /* In case we're using bison in AIX. This definition must be
12 * placed before any other C-language construct in the file
13 * excluding comments and preprocessor directives (thanks IBM
14 * for this wonderful feature...).
16 * Note: some cpps barf on this 'undefined control' (#pragma).
17 * Addition of the leading space seems to prevent barfage for now,
18 * and AIX will still see the directive in its non-standard locale.
22 #pragma alloca /* keep leading space! */
29 void FDECL(yyerror, (const char *));
30 void FDECL(yywarning, (const char *));
33 int FDECL(getchain, (char *));
34 int NDECL(check_dungeon);
35 int NDECL(check_branch);
36 int NDECL(check_level);
37 void NDECL(init_dungeon);
38 void NDECL(init_branch);
39 void NDECL(init_level);
40 void NDECL(output_dgn);
42 #define Free(ptr) free((genericptr_t)ptr)
47 # define memset(addr,val,len) setmem(addr,len,val)
53 static struct couple couple;
54 static struct tmpdungeon tmpdungeon[MAXDUNGEON];
55 static struct tmplevel tmplevel[LEV_LIMIT];
56 static struct tmpbranch tmpbranch[BRANCH_LIMIT];
58 static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1;
60 extern int fatal_error;
61 extern const char *fname;
62 extern FILE *yyin, *yyout; /* from dgn_lex.c */
73 %token <i> A_DUNGEON BRANCH CHBRANCH LEVEL RNDLEVEL CHLEVEL RNDCHLEVEL
74 %token <i> UP_OR_DOWN PROTOFILE DESCRIPTION DESCRIPTOR LEVELDESC
75 %token <i> ALIGNMENT LEVALIGN ENTRY STAIR NO_UP NO_DOWN PORTAL
77 %type <i> optional_int direction branch_type bones_tag
98 dungeonline : A_DUNGEON ':' STRING bones_tag rcouple optional_int
101 Strcpy(tmpdungeon[n_dgns].name, $3);
102 tmpdungeon[n_dgns].boneschar = (char)$4;
103 tmpdungeon[n_dgns].lev.base = couple.base;
104 tmpdungeon[n_dgns].lev.rand = couple.rand;
105 tmpdungeon[n_dgns].chance = $6;
110 optional_int : /* nothing */
125 entry : ENTRY ':' INTEGER
127 tmpdungeon[n_dgns].entry_lev = $3;
134 desc : DESCRIPTION ':' DESCRIPTOR
136 if($<i>3 <= TOWN || $<i>3 >= D_ALIGN_CHAOTIC)
137 yyerror("Illegal description - ignoring!");
139 tmpdungeon[n_dgns].flags |= $<i>3 ;
141 | ALIGNMENT ':' DESCRIPTOR
143 if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
144 yyerror("Illegal alignment - ignoring!");
146 tmpdungeon[n_dgns].flags |= $<i>3 ;
150 prototype : PROTOFILE ':' STRING
152 Strcpy(tmpdungeon[n_dgns].protoname, $3);
164 level1 : LEVEL ':' STRING bones_tag '@' acouple
167 Strcpy(tmplevel[n_levs].name, $3);
168 tmplevel[n_levs].boneschar = (char)$4;
169 tmplevel[n_levs].lev.base = couple.base;
170 tmplevel[n_levs].lev.rand = couple.rand;
171 tmpdungeon[n_dgns].levels++;
174 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER
177 Strcpy(tmplevel[n_levs].name, $3);
178 tmplevel[n_levs].boneschar = (char)$4;
179 tmplevel[n_levs].lev.base = couple.base;
180 tmplevel[n_levs].lev.rand = couple.rand;
181 tmplevel[n_levs].rndlevs = $7;
182 tmpdungeon[n_dgns].levels++;
187 level2 : LEVEL ':' STRING bones_tag '@' acouple INTEGER
190 Strcpy(tmplevel[n_levs].name, $3);
191 tmplevel[n_levs].boneschar = (char)$4;
192 tmplevel[n_levs].lev.base = couple.base;
193 tmplevel[n_levs].lev.rand = couple.rand;
194 tmplevel[n_levs].chance = $7;
195 tmpdungeon[n_dgns].levels++;
198 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER
201 Strcpy(tmplevel[n_levs].name, $3);
202 tmplevel[n_levs].boneschar = (char)$4;
203 tmplevel[n_levs].lev.base = couple.base;
204 tmplevel[n_levs].lev.rand = couple.rand;
205 tmplevel[n_levs].chance = $7;
206 tmplevel[n_levs].rndlevs = $8;
207 tmpdungeon[n_dgns].levels++;
212 levdesc : LEVELDESC ':' DESCRIPTOR
214 if($<i>3 >= D_ALIGN_CHAOTIC)
215 yyerror("Illegal description - ignoring!");
217 tmplevel[n_levs].flags |= $<i>3 ;
219 | LEVALIGN ':' DESCRIPTOR
221 if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
222 yyerror("Illegal alignment - ignoring!");
224 tmplevel[n_levs].flags |= $<i>3 ;
228 chlevel1 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple
231 Strcpy(tmplevel[n_levs].name, $3);
232 tmplevel[n_levs].boneschar = (char)$4;
233 tmplevel[n_levs].chain = getchain($5);
234 tmplevel[n_levs].lev.base = couple.base;
235 tmplevel[n_levs].lev.rand = couple.rand;
236 if(!check_level()) n_levs--;
237 else tmpdungeon[n_dgns].levels++;
241 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
244 Strcpy(tmplevel[n_levs].name, $3);
245 tmplevel[n_levs].boneschar = (char)$4;
246 tmplevel[n_levs].chain = getchain($5);
247 tmplevel[n_levs].lev.base = couple.base;
248 tmplevel[n_levs].lev.rand = couple.rand;
249 tmplevel[n_levs].rndlevs = $8;
250 if(!check_level()) n_levs--;
251 else tmpdungeon[n_dgns].levels++;
257 chlevel2 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
260 Strcpy(tmplevel[n_levs].name, $3);
261 tmplevel[n_levs].boneschar = (char)$4;
262 tmplevel[n_levs].chain = getchain($5);
263 tmplevel[n_levs].lev.base = couple.base;
264 tmplevel[n_levs].lev.rand = couple.rand;
265 tmplevel[n_levs].chance = $8;
266 if(!check_level()) n_levs--;
267 else tmpdungeon[n_dgns].levels++;
271 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER
274 Strcpy(tmplevel[n_levs].name, $3);
275 tmplevel[n_levs].boneschar = (char)$4;
276 tmplevel[n_levs].chain = getchain($5);
277 tmplevel[n_levs].lev.base = couple.base;
278 tmplevel[n_levs].lev.rand = couple.rand;
279 tmplevel[n_levs].chance = $8;
280 tmplevel[n_levs].rndlevs = $9;
281 if(!check_level()) n_levs--;
282 else tmpdungeon[n_dgns].levels++;
292 branch : BRANCH ':' STRING '@' acouple branch_type direction
295 Strcpy(tmpbranch[n_brs].name, $3);
296 tmpbranch[n_brs].lev.base = couple.base;
297 tmpbranch[n_brs].lev.rand = couple.rand;
298 tmpbranch[n_brs].type = $6;
299 tmpbranch[n_brs].up = $7;
300 if(!check_branch()) n_brs--;
301 else tmpdungeon[n_dgns].branches++;
306 chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction
309 Strcpy(tmpbranch[n_brs].name, $3);
310 tmpbranch[n_brs].chain = getchain($4);
311 tmpbranch[n_brs].lev.base = couple.base;
312 tmpbranch[n_brs].lev.rand = couple.rand;
313 tmpbranch[n_brs].type = $7;
314 tmpbranch[n_brs].up = $8;
315 if(!check_branch()) n_brs--;
316 else tmpdungeon[n_dgns].branches++;
322 branch_type : /* nothing */
324 $$ = TBR_STAIR; /* two way stair */
328 $$ = TBR_STAIR; /* two way stair */
332 $$ = TBR_NO_UP; /* no up staircase */
336 $$ = TBR_NO_DOWN; /* no down staircase */
340 $$ = TBR_PORTAL; /* portal connection */
344 direction : /* nothing */
346 $$ = 0; /* defaults to down */
357 if (strlen(p) != 1) {
358 if (strcmp(p, "none") != 0)
359 yyerror("Bones marker must be a single char, or \"none\"!");
370 * (base, range) where:
372 * base is either a positive or negative integer with a value
373 * less than or equal to MAXLEVEL.
374 * base > 0 indicates the base level.
375 * base < 0 indicates reverse index (-1 == lowest level)
377 * range is the random component.
378 * if range is zero, there is no random component.
379 * if range is -1 the dungeon loader will randomize between
380 * the base and the end of the dungeon.
381 * during dungeon load, range is always *added* to the base,
382 * therefore range + base(converted) must not exceed MAXLEVEL.
384 acouple : '(' INTEGER ',' INTEGER ')'
386 if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
387 yyerror("Abs base out of dlevel range - zeroing!");
388 couple.base = couple.rand = 0;
389 } else if ($4 < -1 ||
390 (($2 < 0) ? (MAXLEVEL + $2 + $4 + 1) > MAXLEVEL :
391 ($2 + $4) > MAXLEVEL)) {
392 yyerror("Abs range out of dlevel range - zeroing!");
393 couple.base = couple.rand = 0;
404 * (base, range) where:
406 * base is either a positive or negative integer with a value
407 * less than or equal to MAXLEVEL.
408 * base > 0 indicates a forward index.
409 * base < 0 indicates a reverse index.
410 * base == 0 indicates on the parent level.
412 * range is the random component.
413 * if range is zero, there is no random component.
414 * during dungeon load, range is always *added* to the base,
415 * range + base(converted) may be very large. The dungeon
416 * loader will then correct to "between here and the top/bottom".
418 * There is no practical way of specifying "between here and the
419 * nth / nth last level".
421 rcouple : '(' INTEGER ',' INTEGER ')'
423 if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
424 yyerror("Rel base out of dlevel range - zeroing!");
425 couple.base = couple.rand = 0;
437 if(++n_dgns > MAXDUNGEON) {
438 (void) fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n",
440 (void) fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n");
445 tmpdungeon[n_dgns].lev.base = 0;
446 tmpdungeon[n_dgns].lev.rand = 0;
447 tmpdungeon[n_dgns].chance = 100;
448 Strcpy(tmpdungeon[n_dgns].name, "");
449 Strcpy(tmpdungeon[n_dgns].protoname, "");
450 tmpdungeon[n_dgns].flags = 0;
451 tmpdungeon[n_dgns].levels = 0;
452 tmpdungeon[n_dgns].branches = 0;
453 tmpdungeon[n_dgns].entry_lev = 0;
459 if(++n_levs > LEV_LIMIT) {
461 yyerror("FATAL - Too many special levels defined.");
464 tmplevel[n_levs].lev.base = 0;
465 tmplevel[n_levs].lev.rand = 0;
466 tmplevel[n_levs].chance = 100;
467 tmplevel[n_levs].rndlevs = 0;
468 tmplevel[n_levs].flags = 0;
469 Strcpy(tmplevel[n_levs].name, "");
470 tmplevel[n_levs].chain = -1;
476 if(++n_brs > BRANCH_LIMIT) {
478 yyerror("FATAL - Too many special levels defined.");
481 tmpbranch[n_brs].lev.base = 0;
482 tmpbranch[n_brs].lev.rand = 0;
483 Strcpy(tmpbranch[n_brs].name, "");
484 tmpbranch[n_brs].chain = -1;
495 for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++)
496 if(!strcmp(tmplevel[i].name, s)) return i;
498 yyerror("Can't locate the specified chain level.");
505 * Consistancy checking routines:
507 * - A dungeon must have a unique name.
508 * - A dungeon must have a originating "branch" command
509 * (except, of course, for the first dungeon).
510 * - A dungeon must have a proper depth (at least (1, 0)).
518 for(i = 0; i < n_dgns; i++)
519 if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) {
520 yyerror("Duplicate dungeon name.");
525 for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) {
526 if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break;
528 if(i >= n_brs - tmpdungeon[n_dgns].branches) {
529 yyerror("Dungeon cannot be reached.");
534 if(tmpdungeon[n_dgns].lev.base <= 0 ||
535 tmpdungeon[n_dgns].lev.rand < 0) {
536 yyerror("Invalid dungeon depth specified.");
543 * - A level must have a unique level name.
544 * - If chained, the level used as reference for the chain
545 * must be in this dungeon, must be previously defined, and
546 * the level chained from must be "non-probabilistic" (ie.
547 * have a 100% chance of existing).
556 yyerror("Level defined outside of dungeon.");
560 for(i = 0; i < n_levs; i++)
561 if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) {
562 yyerror("Duplicate level name.");
566 if(tmplevel[i].chain == -2) {
567 yyerror("Invaild level chain reference.");
569 } else if(tmplevel[i].chain != -1) { /* there is a chain */
570 /* KMH -- tmplevel[tmpbranch[i].chain].chance was in error */
571 if(tmplevel[tmplevel[i].chain].chance != 100) {
572 yyerror("Level cannot chain from a probabilistic level.");
574 } else if(tmplevel[i].chain == n_levs) {
575 yyerror("A level cannot chain to itself!");
583 * - A branch may not branch backwards - to avoid branch loops.
584 * - A branch name must be unique.
585 * (ie. You can only have one entry point to each dungeon).
586 * - If chained, the level used as reference for the chain
587 * must be in this dungeon, must be previously defined, and
588 * the level chained from must be "non-probabilistic" (ie.
589 * have a 100% chance of existing).
598 yyerror("Branch defined outside of dungeon.");
602 for(i = 0; i < n_dgns; i++)
603 if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) {
605 yyerror("Reverse branching not allowed.");
609 if(tmpbranch[i].chain == -2) {
611 yyerror("Invaild branch chain reference.");
613 } else if(tmpbranch[i].chain != -1) { /* it is chained */
615 if(tmplevel[tmpbranch[i].chain].chance != 100) {
616 yyerror("Branch cannot chain from a probabilistic level.");
624 * Output the dungon definition into a file.
626 * The file will have the following format:
628 * [ nethack version ID ]
629 * [ number of dungeons ]
630 * [ first dungeon struct ]
631 * [ levels for the first dungeon ]
633 * [ branches for the first dungeon ]
635 * [ second dungeon struct ]
642 int nd, cl = 0, nl = 0,
644 static struct version_info version_data = {
645 VERSION_NUMBER, VERSION_FEATURES,
646 VERSION_SANITY1, VERSION_SANITY2
650 yyerror("FATAL - no dungeons were defined.");
654 if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) {
655 yyerror("FATAL - output failure.");
659 (void) fwrite((char *)&n_dgns, sizeof(int), 1, yyout);
660 for (nd = 0; nd < n_dgns; nd++) {
661 (void) fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon),
664 nl += tmpdungeon[nd].levels;
666 (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel),
669 nb += tmpdungeon[nd].branches;
671 (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch),
674 /* apparently necessary for Think C 5.x, otherwise harmless */
675 (void) fflush(yyout);