2 /* SCCS Id: @(#)lev_yacc.c 3.4 2000/01/17 */
3 /* Copyright (c) 1989 by Jean-Christophe Collet */
4 /* NetHack may be freely redistributed. See license for details. */
7 * This file contains the Level Compiler code
8 * It may handle special mazes & special room-levels
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.
21 #pragma alloca /* keep leading space! */
27 #define MAX_REGISTERS 10
29 /* many types of things are put in chars for transference to NetHack.
30 * since some systems will use signed chars, limit everybody to the
31 * same number for portability.
33 #define MAX_OF_TYPE 128
36 (type *) memset((genericptr_t)alloc(sizeof(type)), 0, sizeof(type))
37 #define NewTab(type, size) (type **) alloc(sizeof(type *) * size)
38 #define Free(ptr) free((genericptr_t)ptr)
40 extern void FDECL(yyerror, (const char *));
41 extern void FDECL(yywarning, (const char *));
42 extern int NDECL(yylex);
45 extern int FDECL(get_floor_type, (CHAR_P));
46 extern int FDECL(get_room_type, (char *));
47 extern int FDECL(get_trap_type, (char *));
48 extern int FDECL(get_monster_id, (char *,CHAR_P));
49 extern int FDECL(get_object_id, (char *,CHAR_P));
50 extern boolean FDECL(check_monster_char, (CHAR_P));
51 extern boolean FDECL(check_object_char, (CHAR_P));
52 extern char FDECL(what_map_char, (CHAR_P));
53 extern void FDECL(scan_map, (char *));
54 extern void NDECL(wallify_map);
55 extern boolean NDECL(check_subrooms);
56 extern void FDECL(check_coord, (int,int,const char *));
57 extern void NDECL(store_part);
58 extern void NDECL(store_room);
59 extern boolean FDECL(write_level_file, (char *,splev *,specialmaze *));
60 extern void FDECL(free_rooms, (splev *));
70 } current_coord, current_align;
81 digpos *tmpdig[MAX_OF_TYPE];
82 region *tmpreg[MAX_OF_TYPE];
83 lev_region *tmplreg[MAX_OF_TYPE];
84 door *tmpdoor[MAX_OF_TYPE];
85 drawbridge *tmpdb[MAX_OF_TYPE];
86 walk *tmpwalk[MAX_OF_TYPE];
88 room_door *tmprdoor[MAX_OF_TYPE];
89 trap *tmptrap[MAX_OF_TYPE];
90 monster *tmpmonst[MAX_OF_TYPE];
91 object *tmpobj[MAX_OF_TYPE];
92 altar *tmpaltar[MAX_OF_TYPE];
93 lad *tmplad[MAX_OF_TYPE];
94 stair *tmpstair[MAX_OF_TYPE];
95 gold *tmpgold[MAX_OF_TYPE];
96 engraving *tmpengraving[MAX_OF_TYPE];
97 fountain *tmpfountain[MAX_OF_TYPE];
98 sink *tmpsink[MAX_OF_TYPE];
99 pool *tmppool[MAX_OF_TYPE];
101 mazepart *tmppart[10];
102 room *tmproom[MAXNROFROOMS*2];
103 corridor *tmpcor[MAX_OF_TYPE];
105 static specialmaze maze;
106 static splev special_lev;
107 static lev_init init_lev;
109 static char olist[MAX_REGISTERS], mlist[MAX_REGISTERS];
110 static struct coord plist[MAX_REGISTERS];
112 int n_olist = 0, n_mlist = 0, n_plist = 0;
114 unsigned int nlreg = 0, nreg = 0, ndoor = 0, ntrap = 0, nmons = 0, nobj = 0;
115 unsigned int ndb = 0, nwalk = 0, npart = 0, ndig = 0, nlad = 0, nstair = 0;
116 unsigned int naltar = 0, ncorridor = 0, nrooms = 0, ngold = 0, nengraving = 0;
117 unsigned int nfountain = 0, npool = 0, nsink = 0, npass = 0;
119 static int lev_flags = 0;
121 unsigned int max_x_map, max_y_map;
123 static xchar in_room;
125 extern int fatal_error;
126 extern int want_warnings;
127 extern const char *fname;
143 %token <i> CHAR INTEGER BOOLEAN PERCENT
144 %token <i> MESSAGE_ID MAZE_ID LEVEL_ID LEV_INIT_ID GEOMETRY_ID NOMAP_ID
145 %token <i> OBJECT_ID COBJECT_ID MONSTER_ID TRAP_ID DOOR_ID DRAWBRIDGE_ID
146 %token <i> MAZEWALK_ID WALLIFY_ID REGION_ID FILLING
147 %token <i> RANDOM_OBJECTS_ID RANDOM_MONSTERS_ID RANDOM_PLACES_ID
148 %token <i> ALTAR_ID LADDER_ID STAIR_ID NON_DIGGABLE_ID NON_PASSWALL_ID ROOM_ID
149 %token <i> PORTAL_ID TELEPRT_ID BRANCH_ID LEV CHANCE_ID
150 %token <i> CORRIDOR_ID GOLD_ID ENGRAVING_ID FOUNTAIN_ID POOL_ID SINK_ID NONE
151 %token <i> RAND_CORRIDOR_ID DOOR_STATE LIGHT_STATE CURSE_TYPE ENGRAVING_TYPE
152 %token <i> DIRECTION RANDOM_TYPE O_REGISTER M_REGISTER P_REGISTER A_REGISTER
153 %token <i> ALIGNMENT LEFT_OR_RIGHT CENTER TOP_OR_BOT ALTAR_TYPE UP_OR_DOWN
154 %token <i> SUBROOM_ID NAME_ID FLAGS_ID FLAG_TYPE MON_ATTITUDE MON_ALERTNESS
155 %token <i> MON_APPEARANCE
157 %token <i> ',' ':' '(' ')' '[' ']'
158 %token <map> STRING MAP_ID
159 %type <i> h_justif v_justif trap_name room_type door_state light_state
160 %type <i> alignment altar_type a_register roomfill filling door_pos
161 %type <i> door_wall walled secret amount chance
162 %type <i> engraving_type flags flag_list prefilled lev_region lev_init
163 %type <i> monster monster_c m_register object object_c o_register
164 %type <map> string maze_def level_def m_name o_name
165 %type <corpos> corr_spec
181 maze_level : maze_def flags lev_init messages regions
185 if (fatal_error > 0) {
186 (void) fprintf(stderr,
187 "%s : %d errors detected. No output created!\n",
191 (void) memcpy((genericptr_t)&(maze.init_lev),
192 (genericptr_t)&(init_lev),
194 maze.numpart = npart;
195 maze.parts = NewTab(mazepart, npart);
197 maze.parts[i] = tmppart[i];
198 if (!write_level_file($1, (splev *)0, &maze)) {
199 yyerror("Can't write output file!!");
208 room_level : level_def flags lev_init messages rreg_init rooms corridors_def
212 if (fatal_error > 0) {
213 (void) fprintf(stderr,
214 "%s : %d errors detected. No output created!\n",
217 special_lev.flags = (long) $2;
219 (genericptr_t)&(special_lev.init_lev),
220 (genericptr_t)&(init_lev),
222 special_lev.nroom = nrooms;
223 special_lev.rooms = NewTab(room, nrooms);
224 for(i=0; i<nrooms; i++)
225 special_lev.rooms[i] = tmproom[i];
226 special_lev.ncorr = ncorridor;
227 special_lev.corrs = NewTab(corridor, ncorridor);
228 for(i=0; i<ncorridor; i++)
229 special_lev.corrs[i] = tmpcor[i];
230 if (check_subrooms()) {
231 if (!write_level_file($1, &special_lev,
233 yyerror("Can't write output file!!");
237 free_rooms(&special_lev);
245 level_def : LEVEL_ID ':' string
248 yyerror("Invalid dot ('.') in level name.");
249 if ((int) strlen($3) > 8)
250 yyerror("Level names limited to 8 characters.");
252 special_lev.nrmonst = special_lev.nrobjects = 0;
253 n_mlist = n_olist = 0;
257 lev_init : /* nothing */
259 /* in case we're processing multiple files,
260 explicitly clear any stale settings */
261 (void) memset((genericptr_t) &init_lev, 0,
263 init_lev.init_present = FALSE;
266 | LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled
268 init_lev.init_present = TRUE;
269 init_lev.fg = what_map_char((char) $3);
270 if (init_lev.fg == INVALID_TYPE)
271 yyerror("Invalid foreground type.");
272 init_lev.bg = what_map_char((char) $5);
273 if (init_lev.bg == INVALID_TYPE)
274 yyerror("Invalid background type.");
275 init_lev.smoothed = $7;
276 init_lev.joined = $9;
277 if (init_lev.joined &&
278 init_lev.fg != CORR && init_lev.fg != ROOM)
279 yyerror("Invalid foreground type for joined map.");
281 init_lev.walled = $13;
290 flags : /* nothing */
294 | FLAGS_ID ':' flag_list
297 lev_flags = 0; /* clear for next user */
301 flag_list : FLAG_TYPE ',' flag_list
311 messages : /* nothing */
315 message : MESSAGE_ID ':' STRING
319 i = (int) strlen($3) + 1;
320 j = (int) strlen(tmpmessage);
322 yyerror("Message string too long (>256 characters)");
324 if (j) tmpmessage[j++] = '\n';
325 (void) strncpy(tmpmessage+j, $3, i - 1);
326 tmpmessage[j + i - 1] = 0;
332 rreg_init : /* nothing */
333 | rreg_init init_rreg
336 init_rreg : RANDOM_OBJECTS_ID ':' object_list
338 if(special_lev.nrobjects) {
339 yyerror("Object registers already initialized!");
341 special_lev.nrobjects = n_olist;
342 special_lev.robjects = (char *) alloc(n_olist);
343 (void) memcpy((genericptr_t)special_lev.robjects,
344 (genericptr_t)olist, n_olist);
347 | RANDOM_MONSTERS_ID ':' monster_list
349 if(special_lev.nrmonst) {
350 yyerror("Monster registers already initialized!");
352 special_lev.nrmonst = n_mlist;
353 special_lev.rmonst = (char *) alloc(n_mlist);
354 (void) memcpy((genericptr_t)special_lev.rmonst,
355 (genericptr_t)mlist, n_mlist);
360 rooms : /* Nothing - dummy room for use with INIT_MAP */
362 tmproom[nrooms] = New(room);
363 tmproom[nrooms]->name = (char *) 0;
364 tmproom[nrooms]->parent = (char *) 0;
365 tmproom[nrooms]->rtype = 0;
366 tmproom[nrooms]->rlit = 0;
367 tmproom[nrooms]->xalign = ERR;
368 tmproom[nrooms]->yalign = ERR;
369 tmproom[nrooms]->x = 0;
370 tmproom[nrooms]->y = 0;
371 tmproom[nrooms]->w = 2;
372 tmproom[nrooms]->h = 2;
382 corridors_def : random_corridors
386 random_corridors: RAND_CORRIDOR_ID
388 tmpcor[0] = New(corridor);
389 tmpcor[0]->src.room = -1;
394 corridors : /* nothing */
398 corridor : CORRIDOR_ID ':' corr_spec ',' corr_spec
400 tmpcor[ncorridor] = New(corridor);
401 tmpcor[ncorridor]->src.room = $3.room;
402 tmpcor[ncorridor]->src.wall = $3.wall;
403 tmpcor[ncorridor]->src.door = $3.door;
404 tmpcor[ncorridor]->dest.room = $5.room;
405 tmpcor[ncorridor]->dest.wall = $5.wall;
406 tmpcor[ncorridor]->dest.door = $5.door;
408 if (ncorridor >= MAX_OF_TYPE) {
409 yyerror("Too many corridors in level!");
413 | CORRIDOR_ID ':' corr_spec ',' INTEGER
415 tmpcor[ncorridor] = New(corridor);
416 tmpcor[ncorridor]->src.room = $3.room;
417 tmpcor[ncorridor]->src.wall = $3.wall;
418 tmpcor[ncorridor]->src.door = $3.door;
419 tmpcor[ncorridor]->dest.room = -1;
420 tmpcor[ncorridor]->dest.wall = $5;
422 if (ncorridor >= MAX_OF_TYPE) {
423 yyerror("Too many corridors in level!");
429 corr_spec : '(' INTEGER ',' DIRECTION ',' door_pos ')'
431 if ((unsigned) $2 >= nrooms)
432 yyerror("Wrong room number!");
439 aroom : room_def room_details
443 | subroom_def room_details
449 subroom_def : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill
451 tmproom[nrooms] = New(room);
452 tmproom[nrooms]->parent = $11;
453 tmproom[nrooms]->name = (char *) 0;
454 tmproom[nrooms]->rtype = $3;
455 tmproom[nrooms]->rlit = $5;
456 tmproom[nrooms]->filled = $12;
457 tmproom[nrooms]->xalign = ERR;
458 tmproom[nrooms]->yalign = ERR;
459 tmproom[nrooms]->x = current_coord.x;
460 tmproom[nrooms]->y = current_coord.y;
461 tmproom[nrooms]->w = current_size.width;
462 tmproom[nrooms]->h = current_size.height;
467 room_def : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill
469 tmproom[nrooms] = New(room);
470 tmproom[nrooms]->name = (char *) 0;
471 tmproom[nrooms]->parent = (char *) 0;
472 tmproom[nrooms]->rtype = $3;
473 tmproom[nrooms]->rlit = $5;
474 tmproom[nrooms]->filled = $12;
475 tmproom[nrooms]->xalign = current_align.x;
476 tmproom[nrooms]->yalign = current_align.y;
477 tmproom[nrooms]->x = current_coord.x;
478 tmproom[nrooms]->y = current_coord.y;
479 tmproom[nrooms]->w = current_size.width;
480 tmproom[nrooms]->h = current_size.height;
485 roomfill : /* nothing */
495 room_pos : '(' INTEGER ',' INTEGER ')'
497 if ( $2 < 1 || $2 > 5 ||
499 yyerror("Room position should be between 1 & 5!");
501 current_coord.x = $2;
502 current_coord.y = $4;
507 current_coord.x = current_coord.y = ERR;
511 subroom_pos : '(' INTEGER ',' INTEGER ')'
513 if ( $2 < 0 || $4 < 0) {
514 yyerror("Invalid subroom position !");
516 current_coord.x = $2;
517 current_coord.y = $4;
522 current_coord.x = current_coord.y = ERR;
526 room_align : '(' h_justif ',' v_justif ')'
528 current_align.x = $2;
529 current_align.y = $4;
533 current_align.x = current_align.y = ERR;
537 room_size : '(' INTEGER ',' INTEGER ')'
539 current_size.width = $2;
540 current_size.height = $4;
544 current_size.height = current_size.width = ERR;
548 room_details : /* nothing */
549 | room_details room_detail
552 room_detail : room_name
567 room_name : NAME_ID ':' string
569 if (tmproom[nrooms]->name)
570 yyerror("This room already has a name!");
572 tmproom[nrooms]->name = $3;
576 room_chance : CHANCE_ID ':' INTEGER
578 if (tmproom[nrooms]->chance)
579 yyerror("This room already assigned a chance!");
580 else if (tmproom[nrooms]->rtype == OROOM)
581 yyerror("Only typed rooms can have a chance!");
582 else if ($3 < 1 || $3 > 99)
583 yyerror("The chance is supposed to be percentile.");
585 tmproom[nrooms]->chance = $3;
589 room_door : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos
591 /* ERR means random here */
592 if ($7 == ERR && $9 != ERR) {
593 yyerror("If the door wall is random, so must be its pos!");
595 tmprdoor[ndoor] = New(room_door);
596 tmprdoor[ndoor]->secret = $3;
597 tmprdoor[ndoor]->mask = $5;
598 tmprdoor[ndoor]->wall = $7;
599 tmprdoor[ndoor]->pos = $9;
601 if (ndoor >= MAX_OF_TYPE) {
602 yyerror("Too many doors in room!");
613 door_wall : DIRECTION
621 maze_def : MAZE_ID ':' string ',' filling
623 maze.filling = (schar) $5;
625 yyerror("Invalid dot ('.') in level name.");
626 if ((int) strlen($3) > 8)
627 yyerror("Level names limited to 8 characters.");
630 n_plist = n_mlist = n_olist = 0;
636 $$ = get_floor_type((char)$1);
648 aregion : map_definition reg_init map_details
654 map_definition : NOMAP_ID
656 tmppart[npart] = New(mazepart);
657 tmppart[npart]->halign = 1;
658 tmppart[npart]->valign = 1;
659 tmppart[npart]->nrobjects = 0;
660 tmppart[npart]->nloc = 0;
661 tmppart[npart]->nrmonst = 0;
662 tmppart[npart]->xsize = 1;
663 tmppart[npart]->ysize = 1;
664 tmppart[npart]->map = (char **) alloc(sizeof(char *));
665 tmppart[npart]->map[0] = (char *) alloc(1);
666 tmppart[npart]->map[0][0] = STONE;
670 | map_geometry MAP_ID
672 tmppart[npart] = New(mazepart);
673 tmppart[npart]->halign = $<i>1 % 10;
674 tmppart[npart]->valign = $<i>1 / 10;
675 tmppart[npart]->nrobjects = 0;
676 tmppart[npart]->nloc = 0;
677 tmppart[npart]->nrmonst = 0;
683 map_geometry : GEOMETRY_ID ':' h_justif ',' v_justif
685 $<i>$ = $<i>3 + ($<i>5 * 10);
689 h_justif : LEFT_OR_RIGHT
693 v_justif : TOP_OR_BOT
697 reg_init : /* nothing */
701 init_reg : RANDOM_OBJECTS_ID ':' object_list
703 if (tmppart[npart]->nrobjects) {
704 yyerror("Object registers already initialized!");
706 tmppart[npart]->robjects = (char *)alloc(n_olist);
707 (void) memcpy((genericptr_t)tmppart[npart]->robjects,
708 (genericptr_t)olist, n_olist);
709 tmppart[npart]->nrobjects = n_olist;
712 | RANDOM_PLACES_ID ':' place_list
714 if (tmppart[npart]->nloc) {
715 yyerror("Location registers already initialized!");
718 tmppart[npart]->rloc_x = (char *) alloc(n_plist);
719 tmppart[npart]->rloc_y = (char *) alloc(n_plist);
720 for(i=0;i<n_plist;i++) {
721 tmppart[npart]->rloc_x[i] = plist[i].x;
722 tmppart[npart]->rloc_y[i] = plist[i].y;
724 tmppart[npart]->nloc = n_plist;
727 | RANDOM_MONSTERS_ID ':' monster_list
729 if (tmppart[npart]->nrmonst) {
730 yyerror("Monster registers already initialized!");
732 tmppart[npart]->rmonst = (char *) alloc(n_mlist);
733 (void) memcpy((genericptr_t)tmppart[npart]->rmonst,
734 (genericptr_t)mlist, n_mlist);
735 tmppart[npart]->nrmonst = n_mlist;
742 if (n_olist < MAX_REGISTERS)
743 olist[n_olist++] = $<i>1;
745 yyerror("Object list too long!");
747 | object ',' object_list
749 if (n_olist < MAX_REGISTERS)
750 olist[n_olist++] = $<i>1;
752 yyerror("Object list too long!");
756 monster_list : monster
758 if (n_mlist < MAX_REGISTERS)
759 mlist[n_mlist++] = $<i>1;
761 yyerror("Monster list too long!");
763 | monster ',' monster_list
765 if (n_mlist < MAX_REGISTERS)
766 mlist[n_mlist++] = $<i>1;
768 yyerror("Monster list too long!");
774 if (n_plist < MAX_REGISTERS)
775 plist[n_plist++] = current_coord;
777 yyerror("Location list too long!");
781 if (n_plist < MAX_REGISTERS)
782 plist[n_plist++] = current_coord;
784 yyerror("Location list too long!");
789 map_details : /* nothing */
790 | map_details map_detail
793 map_detail : monster_detail
815 monster_detail : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate
817 tmpmonst[nmons] = New(monster);
818 tmpmonst[nmons]->x = current_coord.x;
819 tmpmonst[nmons]->y = current_coord.y;
820 tmpmonst[nmons]->class = $<i>4;
821 tmpmonst[nmons]->peaceful = -1; /* no override */
822 tmpmonst[nmons]->asleep = -1;
823 tmpmonst[nmons]->align = - MAX_REGISTERS - 2;
824 tmpmonst[nmons]->name.str = 0;
825 tmpmonst[nmons]->appear = 0;
826 tmpmonst[nmons]->appear_as.str = 0;
827 tmpmonst[nmons]->chance = $2;
828 tmpmonst[nmons]->id = NON_PM;
830 check_coord(current_coord.x, current_coord.y,
833 int token = get_monster_id($6, (char) $<i>4);
836 "Invalid monster name! Making random monster.");
838 tmpmonst[nmons]->id = token;
844 if (++nmons >= MAX_OF_TYPE) {
845 yyerror("Too many monsters in room or mazepart!");
851 monster_infos : /* nothing */
852 | monster_infos monster_info
855 monster_info : ',' string
857 tmpmonst[nmons]->name.str = $2;
861 tmpmonst[nmons]->peaceful = $<i>2;
865 tmpmonst[nmons]->asleep = $<i>2;
869 tmpmonst[nmons]->align = $<i>2;
871 | ',' MON_APPEARANCE string
873 tmpmonst[nmons]->appear = $<i>2;
874 tmpmonst[nmons]->appear_as.str = $3;
878 object_detail : OBJECT_ID object_desc
881 | COBJECT_ID object_desc
883 /* 1: is contents of preceeding object with 2 */
884 /* 2: is a container */
886 tmpobj[nobj-1]->containment = 2;
890 object_desc : chance ':' object_c ',' o_name
892 tmpobj[nobj] = New(object);
893 tmpobj[nobj]->class = $<i>3;
894 tmpobj[nobj]->corpsenm = NON_PM;
895 tmpobj[nobj]->curse_state = -1;
896 tmpobj[nobj]->name.str = 0;
897 tmpobj[nobj]->chance = $1;
898 tmpobj[nobj]->id = -1;
900 int token = get_object_id($5, $<i>3);
903 "Illegal object name! Making random object.");
905 tmpobj[nobj]->id = token;
909 ',' object_where object_infos
911 if (++nobj >= MAX_OF_TYPE) {
912 yyerror("Too many objects in room or mazepart!");
918 object_where : coordinate
920 tmpobj[nobj]->containment = 0;
921 tmpobj[nobj]->x = current_coord.x;
922 tmpobj[nobj]->y = current_coord.y;
924 check_coord(current_coord.x, current_coord.y,
929 tmpobj[nobj]->containment = 1;
930 /* random coordinate, will be overridden anyway */
931 tmpobj[nobj]->x = -MAX_REGISTERS-1;
932 tmpobj[nobj]->y = -MAX_REGISTERS-1;
936 object_infos : /* nothing */
938 tmpobj[nobj]->spe = -127;
939 /* Note below: we're trying to make as many of these optional as
940 * possible. We clearly can't make curse_state, enchantment, and
941 * monster_id _all_ optional, since ",random" would be ambiguous.
942 * We can't even just make enchantment mandatory, since if we do that
943 * alone, ",random" requires too much lookahead to parse.
946 | ',' curse_state ',' monster_id ',' enchantment optional_name
949 | ',' curse_state ',' enchantment optional_name
952 | ',' monster_id ',' enchantment optional_name
957 curse_state : RANDOM_TYPE
959 tmpobj[nobj]->curse_state = -1;
963 tmpobj[nobj]->curse_state = $1;
969 int token = get_monster_id($1, (char)0);
970 if (token == ERR) /* "random" */
971 tmpobj[nobj]->corpsenm = NON_PM - 1;
973 tmpobj[nobj]->corpsenm = token;
978 enchantment : RANDOM_TYPE
980 tmpobj[nobj]->spe = -127;
984 tmpobj[nobj]->spe = $1;
988 optional_name : /* nothing */
994 tmpobj[nobj]->name.str = $2;
998 door_detail : DOOR_ID ':' door_state ',' coordinate
1000 tmpdoor[ndoor] = New(door);
1001 tmpdoor[ndoor]->x = current_coord.x;
1002 tmpdoor[ndoor]->y = current_coord.y;
1003 tmpdoor[ndoor]->mask = $<i>3;
1004 if(current_coord.x >= 0 && current_coord.y >= 0 &&
1005 tmpmap[current_coord.y][current_coord.x] != DOOR &&
1006 tmpmap[current_coord.y][current_coord.x] != SDOOR)
1007 yyerror("Door decl doesn't match the map");
1009 if (ndoor >= MAX_OF_TYPE) {
1010 yyerror("Too many doors in mazepart!");
1016 trap_detail : TRAP_ID chance ':' trap_name ',' coordinate
1018 tmptrap[ntrap] = New(trap);
1019 tmptrap[ntrap]->x = current_coord.x;
1020 tmptrap[ntrap]->y = current_coord.y;
1021 tmptrap[ntrap]->type = $<i>4;
1022 tmptrap[ntrap]->chance = $2;
1024 check_coord(current_coord.x, current_coord.y,
1026 if (++ntrap >= MAX_OF_TYPE) {
1027 yyerror("Too many traps in room or mazepart!");
1033 drawbridge_detail: DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state
1037 tmpdb[ndb] = New(drawbridge);
1038 x = tmpdb[ndb]->x = current_coord.x;
1039 y = tmpdb[ndb]->y = current_coord.y;
1040 /* convert dir from a DIRECTION to a DB_DIR */
1043 case W_NORTH: dir = DB_NORTH; y--; break;
1044 case W_SOUTH: dir = DB_SOUTH; y++; break;
1045 case W_EAST: dir = DB_EAST; x++; break;
1046 case W_WEST: dir = DB_WEST; x--; break;
1048 yyerror("Invalid drawbridge direction");
1051 tmpdb[ndb]->dir = dir;
1052 if (current_coord.x >= 0 && current_coord.y >= 0 &&
1053 !IS_WALL(tmpmap[y][x])) {
1056 "Wall needed for drawbridge (%02d, %02d)",
1057 current_coord.x, current_coord.y);
1061 if ( $<i>7 == D_ISOPEN )
1062 tmpdb[ndb]->db_open = 1;
1063 else if ( $<i>7 == D_CLOSED )
1064 tmpdb[ndb]->db_open = 0;
1066 yyerror("A drawbridge can only be open or closed!");
1068 if (ndb >= MAX_OF_TYPE) {
1069 yyerror("Too many drawbridges in mazepart!");
1075 mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION
1077 tmpwalk[nwalk] = New(walk);
1078 tmpwalk[nwalk]->x = current_coord.x;
1079 tmpwalk[nwalk]->y = current_coord.y;
1080 tmpwalk[nwalk]->dir = $5;
1082 if (nwalk >= MAX_OF_TYPE) {
1083 yyerror("Too many mazewalks in mazepart!");
1089 wallify_detail : WALLIFY_ID
1095 ladder_detail : LADDER_ID ':' coordinate ',' UP_OR_DOWN
1097 tmplad[nlad] = New(lad);
1098 tmplad[nlad]->x = current_coord.x;
1099 tmplad[nlad]->y = current_coord.y;
1100 tmplad[nlad]->up = $<i>5;
1102 check_coord(current_coord.x, current_coord.y,
1105 if (nlad >= MAX_OF_TYPE) {
1106 yyerror("Too many ladders in mazepart!");
1112 stair_detail : STAIR_ID ':' coordinate ',' UP_OR_DOWN
1114 tmpstair[nstair] = New(stair);
1115 tmpstair[nstair]->x = current_coord.x;
1116 tmpstair[nstair]->y = current_coord.y;
1117 tmpstair[nstair]->up = $<i>5;
1119 check_coord(current_coord.x, current_coord.y,
1122 if (nstair >= MAX_OF_TYPE) {
1123 yyerror("Too many stairs in room or mazepart!");
1129 stair_region : STAIR_ID ':' lev_region
1131 tmplreg[nlreg] = New(lev_region);
1132 tmplreg[nlreg]->in_islev = $3;
1133 tmplreg[nlreg]->inarea.x1 = current_region.x1;
1134 tmplreg[nlreg]->inarea.y1 = current_region.y1;
1135 tmplreg[nlreg]->inarea.x2 = current_region.x2;
1136 tmplreg[nlreg]->inarea.y2 = current_region.y2;
1138 ',' lev_region ',' UP_OR_DOWN
1140 tmplreg[nlreg]->del_islev = $6;
1141 tmplreg[nlreg]->delarea.x1 = current_region.x1;
1142 tmplreg[nlreg]->delarea.y1 = current_region.y1;
1143 tmplreg[nlreg]->delarea.x2 = current_region.x2;
1144 tmplreg[nlreg]->delarea.y2 = current_region.y2;
1146 tmplreg[nlreg]->rtype = LR_UPSTAIR;
1148 tmplreg[nlreg]->rtype = LR_DOWNSTAIR;
1149 tmplreg[nlreg]->rname.str = 0;
1151 if (nlreg >= MAX_OF_TYPE) {
1152 yyerror("Too many levregions in mazepart!");
1158 portal_region : PORTAL_ID ':' lev_region
1160 tmplreg[nlreg] = New(lev_region);
1161 tmplreg[nlreg]->in_islev = $3;
1162 tmplreg[nlreg]->inarea.x1 = current_region.x1;
1163 tmplreg[nlreg]->inarea.y1 = current_region.y1;
1164 tmplreg[nlreg]->inarea.x2 = current_region.x2;
1165 tmplreg[nlreg]->inarea.y2 = current_region.y2;
1167 ',' lev_region ',' string
1169 tmplreg[nlreg]->del_islev = $6;
1170 tmplreg[nlreg]->delarea.x1 = current_region.x1;
1171 tmplreg[nlreg]->delarea.y1 = current_region.y1;
1172 tmplreg[nlreg]->delarea.x2 = current_region.x2;
1173 tmplreg[nlreg]->delarea.y2 = current_region.y2;
1174 tmplreg[nlreg]->rtype = LR_PORTAL;
1175 tmplreg[nlreg]->rname.str = $8;
1177 if (nlreg >= MAX_OF_TYPE) {
1178 yyerror("Too many levregions in mazepart!");
1184 teleprt_region : TELEPRT_ID ':' lev_region
1186 tmplreg[nlreg] = New(lev_region);
1187 tmplreg[nlreg]->in_islev = $3;
1188 tmplreg[nlreg]->inarea.x1 = current_region.x1;
1189 tmplreg[nlreg]->inarea.y1 = current_region.y1;
1190 tmplreg[nlreg]->inarea.x2 = current_region.x2;
1191 tmplreg[nlreg]->inarea.y2 = current_region.y2;
1195 tmplreg[nlreg]->del_islev = $6;
1196 tmplreg[nlreg]->delarea.x1 = current_region.x1;
1197 tmplreg[nlreg]->delarea.y1 = current_region.y1;
1198 tmplreg[nlreg]->delarea.x2 = current_region.x2;
1199 tmplreg[nlreg]->delarea.y2 = current_region.y2;
1204 case -1: tmplreg[nlreg]->rtype = LR_TELE; break;
1205 case 0: tmplreg[nlreg]->rtype = LR_DOWNTELE; break;
1206 case 1: tmplreg[nlreg]->rtype = LR_UPTELE; break;
1208 tmplreg[nlreg]->rname.str = 0;
1210 if (nlreg >= MAX_OF_TYPE) {
1211 yyerror("Too many levregions in mazepart!");
1217 branch_region : BRANCH_ID ':' lev_region
1219 tmplreg[nlreg] = New(lev_region);
1220 tmplreg[nlreg]->in_islev = $3;
1221 tmplreg[nlreg]->inarea.x1 = current_region.x1;
1222 tmplreg[nlreg]->inarea.y1 = current_region.y1;
1223 tmplreg[nlreg]->inarea.x2 = current_region.x2;
1224 tmplreg[nlreg]->inarea.y2 = current_region.y2;
1228 tmplreg[nlreg]->del_islev = $6;
1229 tmplreg[nlreg]->delarea.x1 = current_region.x1;
1230 tmplreg[nlreg]->delarea.y1 = current_region.y1;
1231 tmplreg[nlreg]->delarea.x2 = current_region.x2;
1232 tmplreg[nlreg]->delarea.y2 = current_region.y2;
1233 tmplreg[nlreg]->rtype = LR_BRANCH;
1234 tmplreg[nlreg]->rname.str = 0;
1236 if (nlreg >= MAX_OF_TYPE) {
1237 yyerror("Too many levregions in mazepart!");
1243 teleprt_detail : /* empty */
1257 | LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
1259 /* This series of if statements is a hack for MSC 5.1. It seems that its
1260 tiny little brain cannot compile if these are all one big if statement. */
1261 if ($3 <= 0 || $3 >= COLNO)
1262 yyerror("Region out of level range!");
1263 else if ($5 < 0 || $5 >= ROWNO)
1264 yyerror("Region out of level range!");
1265 else if ($7 <= 0 || $7 >= COLNO)
1266 yyerror("Region out of level range!");
1267 else if ($9 < 0 || $9 >= ROWNO)
1268 yyerror("Region out of level range!");
1269 current_region.x1 = $3;
1270 current_region.y1 = $5;
1271 current_region.x2 = $7;
1272 current_region.y2 = $9;
1277 fountain_detail : FOUNTAIN_ID ':' coordinate
1279 tmpfountain[nfountain] = New(fountain);
1280 tmpfountain[nfountain]->x = current_coord.x;
1281 tmpfountain[nfountain]->y = current_coord.y;
1283 check_coord(current_coord.x, current_coord.y,
1286 if (nfountain >= MAX_OF_TYPE) {
1287 yyerror("Too many fountains in room or mazepart!");
1293 sink_detail : SINK_ID ':' coordinate
1295 tmpsink[nsink] = New(sink);
1296 tmpsink[nsink]->x = current_coord.x;
1297 tmpsink[nsink]->y = current_coord.y;
1299 if (nsink >= MAX_OF_TYPE) {
1300 yyerror("Too many sinks in room!");
1306 pool_detail : POOL_ID ':' coordinate
1308 tmppool[npool] = New(pool);
1309 tmppool[npool]->x = current_coord.x;
1310 tmppool[npool]->y = current_coord.y;
1312 if (npool >= MAX_OF_TYPE) {
1313 yyerror("Too many pools in room!");
1319 diggable_detail : NON_DIGGABLE_ID ':' region
1321 tmpdig[ndig] = New(digpos);
1322 tmpdig[ndig]->x1 = current_region.x1;
1323 tmpdig[ndig]->y1 = current_region.y1;
1324 tmpdig[ndig]->x2 = current_region.x2;
1325 tmpdig[ndig]->y2 = current_region.y2;
1327 if (ndig >= MAX_OF_TYPE) {
1328 yyerror("Too many diggables in mazepart!");
1334 passwall_detail : NON_PASSWALL_ID ':' region
1336 tmppass[npass] = New(digpos);
1337 tmppass[npass]->x1 = current_region.x1;
1338 tmppass[npass]->y1 = current_region.y1;
1339 tmppass[npass]->x2 = current_region.x2;
1340 tmppass[npass]->y2 = current_region.y2;
1343 yyerror("Too many passwalls in mazepart!");
1349 region_detail : REGION_ID ':' region ',' light_state ',' room_type prefilled
1351 tmpreg[nreg] = New(region);
1352 tmpreg[nreg]->x1 = current_region.x1;
1353 tmpreg[nreg]->y1 = current_region.y1;
1354 tmpreg[nreg]->x2 = current_region.x2;
1355 tmpreg[nreg]->y2 = current_region.y2;
1356 tmpreg[nreg]->rlit = $<i>5;
1357 tmpreg[nreg]->rtype = $<i>7;
1358 if($<i>8 & 1) tmpreg[nreg]->rtype += MAXRTYPE+1;
1359 tmpreg[nreg]->rirreg = (($<i>8 & 2) != 0);
1360 if(current_region.x1 > current_region.x2 ||
1361 current_region.y1 > current_region.y2)
1362 yyerror("Region start > end!");
1363 if(tmpreg[nreg]->rtype == VAULT &&
1364 (tmpreg[nreg]->rirreg ||
1365 (tmpreg[nreg]->x2 - tmpreg[nreg]->x1 != 1) ||
1366 (tmpreg[nreg]->y2 - tmpreg[nreg]->y1 != 1)))
1367 yyerror("Vaults must be exactly 2x2!");
1368 if(want_warnings && !tmpreg[nreg]->rirreg &&
1369 current_region.x1 > 0 && current_region.y1 > 0 &&
1370 current_region.x2 < (int)max_x_map &&
1371 current_region.y2 < (int)max_y_map) {
1372 /* check for walls in the room */
1374 register int x, y, nrock = 0;
1376 for(y=current_region.y1; y<=current_region.y2; y++)
1377 for(x=current_region.x1;
1378 x<=current_region.x2; x++)
1379 if(IS_ROCK(tmpmap[y][x]) ||
1380 IS_DOOR(tmpmap[y][x])) nrock++;
1383 "Rock in room (%02d,%02d,%02d,%02d)?!",
1384 current_region.x1, current_region.y1,
1385 current_region.x2, current_region.y2);
1389 !IS_ROCK(tmpmap[current_region.y1-1][current_region.x1-1]) ||
1390 !IS_ROCK(tmpmap[current_region.y2+1][current_region.x1-1]) ||
1391 !IS_ROCK(tmpmap[current_region.y1-1][current_region.x2+1]) ||
1392 !IS_ROCK(tmpmap[current_region.y2+1][current_region.x2+1])) {
1394 "NonRock edge in room (%02d,%02d,%02d,%02d)?!",
1395 current_region.x1, current_region.y1,
1396 current_region.x2, current_region.y2);
1399 } else if(tmpreg[nreg]->rirreg &&
1400 !IS_ROOM(tmpmap[current_region.y1][current_region.x1])) {
1403 "Rock in irregular room (%02d,%02d)?!",
1404 current_region.x1, current_region.y1);
1408 if (nreg >= MAX_OF_TYPE) {
1409 yyerror("Too many regions in mazepart!");
1415 altar_detail : ALTAR_ID ':' coordinate ',' alignment ',' altar_type
1417 tmpaltar[naltar] = New(altar);
1418 tmpaltar[naltar]->x = current_coord.x;
1419 tmpaltar[naltar]->y = current_coord.y;
1420 tmpaltar[naltar]->align = $<i>5;
1421 tmpaltar[naltar]->shrine = $<i>7;
1423 check_coord(current_coord.x, current_coord.y,
1426 if (naltar >= MAX_OF_TYPE) {
1427 yyerror("Too many altars in room or mazepart!");
1433 gold_detail : GOLD_ID ':' amount ',' coordinate
1435 tmpgold[ngold] = New(gold);
1436 tmpgold[ngold]->x = current_coord.x;
1437 tmpgold[ngold]->y = current_coord.y;
1438 tmpgold[ngold]->amount = $<i>3;
1440 check_coord(current_coord.x, current_coord.y,
1443 if (ngold >= MAX_OF_TYPE) {
1444 yyerror("Too many golds in room or mazepart!");
1450 engraving_detail: ENGRAVING_ID ':' coordinate ',' engraving_type ',' string
1452 tmpengraving[nengraving] = New(engraving);
1453 tmpengraving[nengraving]->x = current_coord.x;
1454 tmpengraving[nengraving]->y = current_coord.y;
1455 tmpengraving[nengraving]->engr.str = $7;
1456 tmpengraving[nengraving]->etype = $<i>5;
1458 check_coord(current_coord.x, current_coord.y,
1461 if (nengraving >= MAX_OF_TYPE) {
1462 yyerror("Too many engravings in room or mazepart!");
1471 $<i>$ = - MAX_REGISTERS - 1;
1479 $<i>$ = - MAX_REGISTERS - 1;
1500 int token = get_trap_type($1);
1502 yyerror("Unknown trap type!");
1511 int token = get_room_type($1);
1513 yywarning("Unknown room type! Making ordinary room...");
1522 prefilled : /* empty */
1530 | ',' FILLING ',' BOOLEAN
1532 $<i>$ = $2 + ($4 << 1);
1540 current_coord.x = current_coord.y = -MAX_REGISTERS-1;
1544 door_state : DOOR_STATE
1548 light_state : LIGHT_STATE
1552 alignment : ALIGNMENT
1556 $<i>$ = - MAX_REGISTERS - 1;
1560 altar_type : ALTAR_TYPE
1564 p_register : P_REGISTER '[' INTEGER ']'
1566 if ( $3 >= MAX_REGISTERS )
1567 yyerror("Register Index overflow!");
1569 current_coord.x = current_coord.y = - $3 - 1;
1573 o_register : O_REGISTER '[' INTEGER ']'
1575 if ( $3 >= MAX_REGISTERS )
1576 yyerror("Register Index overflow!");
1582 m_register : M_REGISTER '[' INTEGER ']'
1584 if ( $3 >= MAX_REGISTERS )
1585 yyerror("Register Index overflow!");
1591 a_register : A_REGISTER '[' INTEGER ']'
1594 yyerror("Register Index overflow!");
1605 if (check_monster_char((char) $1))
1608 yyerror("Unknown monster class!");
1617 if (check_object_char(c))
1620 yyerror("Unknown char class!");
1633 chance : /* empty */
1635 $$ = 100; /* default is 100% */
1639 if ($1 <= 0 || $1 > 100)
1640 yyerror("Expected percentile chance.");
1645 engraving_type : ENGRAVING_TYPE
1649 coord : '(' INTEGER ',' INTEGER ')'
1651 if (!in_room && !init_lev.init_present &&
1652 ($2 < 0 || $2 > (int)max_x_map ||
1653 $4 < 0 || $4 > (int)max_y_map))
1654 yyerror("Coordinates out of map range!");
1655 current_coord.x = $2;
1656 current_coord.y = $4;
1660 region : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
1662 /* This series of if statements is a hack for MSC 5.1. It seems that its
1663 tiny little brain cannot compile if these are all one big if statement. */
1664 if ($2 < 0 || $2 > (int)max_x_map)
1665 yyerror("Region out of map range!");
1666 else if ($4 < 0 || $4 > (int)max_y_map)
1667 yyerror("Region out of map range!");
1668 else if ($6 < 0 || $6 > (int)max_x_map)
1669 yyerror("Region out of map range!");
1670 else if ($8 < 0 || $8 > (int)max_y_map)
1671 yyerror("Region out of map range!");
1672 current_region.x1 = $2;
1673 current_region.y1 = $4;
1674 current_region.x2 = $6;
1675 current_region.y2 = $8;