OSDN Git Service

fix sokoban defs
[nethackexpress/trunk.git] / util / lev_comp.y
1 %{
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. */
5
6 /*
7  * This file contains the Level Compiler code
8  * It may handle special mazes & special room-levels
9  */
10
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...).
15  *
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.
19  */
20 #ifdef _AIX
21  #pragma alloca         /* keep leading space! */
22 #endif
23
24 #include "hack.h"
25 #include "sp_lev.h"
26
27 #define MAX_REGISTERS   10
28 #define ERR             (-1)
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.
32  */
33 #define MAX_OF_TYPE     128
34
35 #define New(type)               \
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)
39
40 extern void FDECL(yyerror, (const char *));
41 extern void FDECL(yywarning, (const char *));
42 extern int NDECL(yylex);
43 int NDECL(yyparse);
44
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 *));
61
62 static struct reg {
63         int x1, y1;
64         int x2, y2;
65 }               current_region;
66
67 static struct coord {
68         int x;
69         int y;
70 }               current_coord, current_align;
71
72 static struct size {
73         int height;
74         int width;
75 }               current_size;
76
77 char tmpmessage[256];
78 digpos *tmppass[32];
79 char *tmpmap[ROWNO];
80
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];
87
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];
100
101 mazepart *tmppart[10];
102 room *tmproom[MAXNROFROOMS*2];
103 corridor *tmpcor[MAX_OF_TYPE];
104
105 static specialmaze maze;
106 static splev special_lev;
107 static lev_init init_lev;
108
109 static char olist[MAX_REGISTERS], mlist[MAX_REGISTERS];
110 static struct coord plist[MAX_REGISTERS];
111
112 int n_olist = 0, n_mlist = 0, n_plist = 0;
113
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;
118
119 static int lev_flags = 0;
120
121 unsigned int max_x_map, max_y_map;
122
123 static xchar in_room;
124
125 extern int fatal_error;
126 extern int want_warnings;
127 extern const char *fname;
128
129 %}
130
131 %union
132 {
133         int     i;
134         char*   map;
135         struct {
136                 xchar room;
137                 xchar wall;
138                 xchar door;
139         } corpos;
140 }
141
142
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
156 %token  <i> CONTAINED
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
166 %start  file
167
168 %%
169 file            : /* nothing */
170                 | levels
171                 ;
172
173 levels          : level
174                 | level levels
175                 ;
176
177 level           : maze_level
178                 | room_level
179                 ;
180
181 maze_level      : maze_def flags lev_init messages regions
182                   {
183                         unsigned i;
184
185                         if (fatal_error > 0) {
186                                 (void) fprintf(stderr,
187                                 "%s : %d errors detected. No output created!\n",
188                                         fname, fatal_error);
189                         } else {
190                                 maze.flags = $2;
191                                 (void) memcpy((genericptr_t)&(maze.init_lev),
192                                                 (genericptr_t)&(init_lev),
193                                                 sizeof(lev_init));
194                                 maze.numpart = npart;
195                                 maze.parts = NewTab(mazepart, npart);
196                                 for(i=0;i<npart;i++)
197                                     maze.parts[i] = tmppart[i];
198                                 if (!write_level_file($1, (splev *)0, &maze)) {
199                                         yyerror("Can't write output file!!");
200                                         exit(EXIT_FAILURE);
201                                 }
202                                 npart = 0;
203                         }
204                         Free($1);
205                   }
206                 ;
207
208 room_level      : level_def flags lev_init messages rreg_init rooms corridors_def
209                   {
210                         unsigned i;
211
212                         if (fatal_error > 0) {
213                             (void) fprintf(stderr,
214                               "%s : %d errors detected. No output created!\n",
215                                         fname, fatal_error);
216                         } else {
217                                 special_lev.flags = (long) $2;
218                                 (void) memcpy(
219                                         (genericptr_t)&(special_lev.init_lev),
220                                         (genericptr_t)&(init_lev),
221                                         sizeof(lev_init));
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,
232                                                           (specialmaze *)0)) {
233                                         yyerror("Can't write output file!!");
234                                         exit(EXIT_FAILURE);
235                                     }
236                                 }
237                                 free_rooms(&special_lev);
238                                 nrooms = 0;
239                                 ncorridor = 0;
240                         }
241                         Free($1);
242                   }
243                 ;
244
245 level_def       : LEVEL_ID ':' string
246                   {
247                         if (index($3, '.'))
248                             yyerror("Invalid dot ('.') in level name.");
249                         if ((int) strlen($3) > 8)
250                             yyerror("Level names limited to 8 characters.");
251                         $$ = $3;
252                         special_lev.nrmonst = special_lev.nrobjects = 0;
253                         n_mlist = n_olist = 0;
254                   }
255                 ;
256
257 lev_init        : /* nothing */
258                   {
259                         /* in case we're processing multiple files,
260                            explicitly clear any stale settings */
261                         (void) memset((genericptr_t) &init_lev, 0,
262                                         sizeof init_lev);
263                         init_lev.init_present = FALSE;
264                         $$ = 0;
265                   }
266                 | LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled
267                   {
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.");
280                         init_lev.lit = $11;
281                         init_lev.walled = $13;
282                         $$ = 1;
283                   }
284                 ;
285
286 walled          : BOOLEAN
287                 | RANDOM_TYPE
288                 ;
289
290 flags           : /* nothing */
291                   {
292                         $$ = 0;
293                   }
294                 | FLAGS_ID ':' flag_list
295                   {
296                         $$ = lev_flags;
297                         lev_flags = 0;  /* clear for next user */
298                   }
299                 ;
300
301 flag_list       : FLAG_TYPE ',' flag_list
302                   {
303                         lev_flags |= $1;
304                   }
305                 | FLAG_TYPE
306                   {
307                         lev_flags |= $1;
308                   }
309                 ;
310
311 messages        : /* nothing */
312                 | message messages
313                 ;
314
315 message         : MESSAGE_ID ':' STRING
316                   {
317                         int i, j;
318
319                         i = (int) strlen($3) + 1;
320                         j = (int) strlen(tmpmessage);
321                         if (i + j > 255) {
322                            yyerror("Message string too long (>256 characters)");
323                         } else {
324                             if (j) tmpmessage[j++] = '\n';
325                             (void) strncpy(tmpmessage+j, $3, i - 1);
326                             tmpmessage[j + i - 1] = 0;
327                         }
328                         Free($3);
329                   }
330                 ;
331
332 rreg_init       : /* nothing */
333                 | rreg_init init_rreg
334                 ;
335
336 init_rreg       : RANDOM_OBJECTS_ID ':' object_list
337                   {
338                         if(special_lev.nrobjects) {
339                             yyerror("Object registers already initialized!");
340                         } else {
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);
345                         }
346                   }
347                 | RANDOM_MONSTERS_ID ':' monster_list
348                   {
349                         if(special_lev.nrmonst) {
350                             yyerror("Monster registers already initialized!");
351                         } else {
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);
356                           }
357                   }
358                 ;
359
360 rooms           : /* Nothing  -  dummy room for use with INIT_MAP */
361                   {
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;
373                         in_room = 1;
374                   }
375                 | roomlist
376                 ;
377
378 roomlist        : aroom
379                 | aroom roomlist
380                 ;
381
382 corridors_def   : random_corridors
383                 | corridors
384                 ;
385
386 random_corridors: RAND_CORRIDOR_ID
387                   {
388                         tmpcor[0] = New(corridor);
389                         tmpcor[0]->src.room = -1;
390                         ncorridor = 1;
391                   }
392                 ;
393
394 corridors       : /* nothing */
395                 | corridors corridor
396                 ;
397
398 corridor        : CORRIDOR_ID ':' corr_spec ',' corr_spec
399                   {
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;
407                         ncorridor++;
408                         if (ncorridor >= MAX_OF_TYPE) {
409                                 yyerror("Too many corridors in level!");
410                                 ncorridor--;
411                         }
412                   }
413                 | CORRIDOR_ID ':' corr_spec ',' INTEGER
414                   {
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;
421                         ncorridor++;
422                         if (ncorridor >= MAX_OF_TYPE) {
423                                 yyerror("Too many corridors in level!");
424                                 ncorridor--;
425                         }
426                   }
427                 ;
428
429 corr_spec       : '(' INTEGER ',' DIRECTION ',' door_pos ')'
430                   {
431                         if ((unsigned) $2 >= nrooms)
432                             yyerror("Wrong room number!");
433                         $$.room = $2;
434                         $$.wall = $4;
435                         $$.door = $6;
436                   }
437                 ;
438
439 aroom           : room_def room_details
440                   {
441                         store_room();
442                   }
443                 | subroom_def room_details
444                   {
445                         store_room();
446                   }
447                 ;
448
449 subroom_def     : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill
450                   {
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;
463                         in_room = 1;
464                   }
465                 ;
466
467 room_def        : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill
468                   {
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;
481                         in_room = 1;
482                   }
483                 ;
484
485 roomfill        : /* nothing */
486                   {
487                         $$ = 1;
488                   }
489                 | ',' BOOLEAN
490                   {
491                         $$ = $2;
492                   }
493                 ;
494
495 room_pos        : '(' INTEGER ',' INTEGER ')'
496                   {
497                         if ( $2 < 1 || $2 > 5 ||
498                             $4 < 1 || $4 > 5 ) {
499                             yyerror("Room position should be between 1 & 5!");
500                         } else {
501                             current_coord.x = $2;
502                             current_coord.y = $4;
503                         }
504                   }
505                 | RANDOM_TYPE
506                   {
507                         current_coord.x = current_coord.y = ERR;
508                   }
509                 ;
510
511 subroom_pos     : '(' INTEGER ',' INTEGER ')'
512                   {
513                         if ( $2 < 0 || $4 < 0) {
514                             yyerror("Invalid subroom position !");
515                         } else {
516                             current_coord.x = $2;
517                             current_coord.y = $4;
518                         }
519                   }
520                 | RANDOM_TYPE
521                   {
522                         current_coord.x = current_coord.y = ERR;
523                   }
524                 ;
525
526 room_align      : '(' h_justif ',' v_justif ')'
527                   {
528                         current_align.x = $2;
529                         current_align.y = $4;
530                   }
531                 | RANDOM_TYPE
532                   {
533                         current_align.x = current_align.y = ERR;
534                   }
535                 ;
536
537 room_size       : '(' INTEGER ',' INTEGER ')'
538                   {
539                         current_size.width = $2;
540                         current_size.height = $4;
541                   }
542                 | RANDOM_TYPE
543                   {
544                         current_size.height = current_size.width = ERR;
545                   }
546                 ;
547
548 room_details    : /* nothing */
549                 | room_details room_detail
550                 ;
551
552 room_detail     : room_name
553                 | room_chance
554                 | room_door
555                 | monster_detail
556                 | object_detail
557                 | trap_detail
558                 | altar_detail
559                 | fountain_detail
560                 | sink_detail
561                 | pool_detail
562                 | gold_detail
563                 | engraving_detail
564                 | stair_detail
565                 ;
566
567 room_name       : NAME_ID ':' string
568                   {
569                         if (tmproom[nrooms]->name)
570                             yyerror("This room already has a name!");
571                         else
572                             tmproom[nrooms]->name = $3;
573                   }
574                 ;
575
576 room_chance     : CHANCE_ID ':' INTEGER
577                    {
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.");
584                         else
585                             tmproom[nrooms]->chance = $3;
586                    }
587                 ;
588
589 room_door       : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos
590                   {
591                         /* ERR means random here */
592                         if ($7 == ERR && $9 != ERR) {
593                      yyerror("If the door wall is random, so must be its pos!");
594                         } else {
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;
600                             ndoor++;
601                             if (ndoor >= MAX_OF_TYPE) {
602                                     yyerror("Too many doors in room!");
603                                     ndoor--;
604                             }
605                         }
606                   }
607                 ;
608
609 secret          : BOOLEAN
610                 | RANDOM_TYPE
611                 ;
612
613 door_wall       : DIRECTION
614                 | RANDOM_TYPE
615                 ;
616
617 door_pos        : INTEGER
618                 | RANDOM_TYPE
619                 ;
620
621 maze_def        : MAZE_ID ':' string ',' filling
622                   {
623                         maze.filling = (schar) $5;
624                         if (index($3, '.'))
625                             yyerror("Invalid dot ('.') in level name.");
626                         if ((int) strlen($3) > 8)
627                             yyerror("Level names limited to 8 characters.");
628                         $$ = $3;
629                         in_room = 0;
630                         n_plist = n_mlist = n_olist = 0;
631                   }
632                 ;
633
634 filling         : CHAR
635                   {
636                         $$ = get_floor_type((char)$1);
637                   }
638                 | RANDOM_TYPE
639                   {
640                         $$ = -1;
641                   }
642                 ;
643
644 regions         : aregion
645                 | aregion regions
646                 ;
647
648 aregion         : map_definition reg_init map_details
649                   {
650                         store_part();
651                   }
652                 ;
653
654 map_definition  : NOMAP_ID
655                   {
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;
667                         max_x_map = COLNO-1;
668                         max_y_map = ROWNO;
669                   }
670                 | map_geometry MAP_ID
671                   {
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;
678                         scan_map($2);
679                         Free($2);
680                   }
681                 ;
682
683 map_geometry    : GEOMETRY_ID ':' h_justif ',' v_justif
684                   {
685                         $<i>$ = $<i>3 + ($<i>5 * 10);
686                   }
687                 ;
688
689 h_justif        : LEFT_OR_RIGHT
690                 | CENTER
691                 ;
692
693 v_justif        : TOP_OR_BOT
694                 | CENTER
695                 ;
696
697 reg_init        : /* nothing */
698                 | reg_init init_reg
699                 ;
700
701 init_reg        : RANDOM_OBJECTS_ID ':' object_list
702                   {
703                         if (tmppart[npart]->nrobjects) {
704                             yyerror("Object registers already initialized!");
705                         } else {
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;
710                         }
711                   }
712                 | RANDOM_PLACES_ID ':' place_list
713                   {
714                         if (tmppart[npart]->nloc) {
715                             yyerror("Location registers already initialized!");
716                         } else {
717                             register int i;
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;
723                             }
724                             tmppart[npart]->nloc = n_plist;
725                         }
726                   }
727                 | RANDOM_MONSTERS_ID ':' monster_list
728                   {
729                         if (tmppart[npart]->nrmonst) {
730                             yyerror("Monster registers already initialized!");
731                         } else {
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;
736                         }
737                   }
738                 ;
739
740 object_list     : object
741                   {
742                         if (n_olist < MAX_REGISTERS)
743                             olist[n_olist++] = $<i>1;
744                         else
745                             yyerror("Object list too long!");
746                   }
747                 | object ',' object_list
748                   {
749                         if (n_olist < MAX_REGISTERS)
750                             olist[n_olist++] = $<i>1;
751                         else
752                             yyerror("Object list too long!");
753                   }
754                 ;
755
756 monster_list    : monster
757                   {
758                         if (n_mlist < MAX_REGISTERS)
759                             mlist[n_mlist++] = $<i>1;
760                         else
761                             yyerror("Monster list too long!");
762                   }
763                 | monster ',' monster_list
764                   {
765                         if (n_mlist < MAX_REGISTERS)
766                             mlist[n_mlist++] = $<i>1;
767                         else
768                             yyerror("Monster list too long!");
769                   }
770                 ;
771
772 place_list      : place
773                   {
774                         if (n_plist < MAX_REGISTERS)
775                             plist[n_plist++] = current_coord;
776                         else
777                             yyerror("Location list too long!");
778                   }
779                 | place
780                   {
781                         if (n_plist < MAX_REGISTERS)
782                             plist[n_plist++] = current_coord;
783                         else
784                             yyerror("Location list too long!");
785                   }
786                  ',' place_list
787                 ;
788
789 map_details     : /* nothing */
790                 | map_details map_detail
791                 ;
792
793 map_detail      : monster_detail
794                 | object_detail
795                 | door_detail
796                 | trap_detail
797                 | drawbridge_detail
798                 | region_detail
799                 | stair_region
800                 | portal_region
801                 | teleprt_region
802                 | branch_region
803                 | altar_detail
804                 | fountain_detail
805                 | mazewalk_detail
806                 | wallify_detail
807                 | ladder_detail
808                 | stair_detail
809                 | gold_detail
810                 | engraving_detail
811                 | diggable_detail
812                 | passwall_detail
813                 ;
814
815 monster_detail  : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate
816                   {
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;
829                         if (!in_room)
830                             check_coord(current_coord.x, current_coord.y,
831                                         "Monster");
832                         if ($6) {
833                             int token = get_monster_id($6, (char) $<i>4);
834                             if (token == ERR)
835                                 yywarning(
836                               "Invalid monster name!  Making random monster.");
837                             else
838                                 tmpmonst[nmons]->id = token;
839                             Free($6);
840                         }
841                   }
842                  monster_infos
843                   {
844                         if (++nmons >= MAX_OF_TYPE) {
845                             yyerror("Too many monsters in room or mazepart!");
846                             nmons--;
847                         }
848                   }
849                 ;
850
851 monster_infos   : /* nothing */
852                 | monster_infos monster_info
853                 ;
854
855 monster_info    : ',' string
856                   {
857                         tmpmonst[nmons]->name.str = $2;
858                   }
859                 | ',' MON_ATTITUDE
860                   {
861                         tmpmonst[nmons]->peaceful = $<i>2;
862                   }
863                 | ',' MON_ALERTNESS
864                   {
865                         tmpmonst[nmons]->asleep = $<i>2;
866                   }
867                 | ',' alignment
868                   {
869                         tmpmonst[nmons]->align = $<i>2;
870                   }
871                 | ',' MON_APPEARANCE string
872                   {
873                         tmpmonst[nmons]->appear = $<i>2;
874                         tmpmonst[nmons]->appear_as.str = $3;
875                   }
876                 ;
877
878 object_detail   : OBJECT_ID object_desc
879                   {
880                   }
881                 | COBJECT_ID object_desc
882                   {
883                         /* 1: is contents of preceeding object with 2 */
884                         /* 2: is a container */
885                         /* 0: neither */
886                         tmpobj[nobj-1]->containment = 2;
887                   }
888                 ;
889
890 object_desc     : chance ':' object_c ',' o_name
891                   {
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;
899                         if ($5) {
900                             int token = get_object_id($5, $<i>3);
901                             if (token == ERR)
902                                 yywarning(
903                                 "Illegal object name!  Making random object.");
904                              else
905                                 tmpobj[nobj]->id = token;
906                             Free($5);
907                         }
908                   }
909                  ',' object_where object_infos
910                   {
911                         if (++nobj >= MAX_OF_TYPE) {
912                             yyerror("Too many objects in room or mazepart!");
913                             nobj--;
914                         }
915                   }
916                 ;
917
918 object_where    : coordinate
919                   {
920                         tmpobj[nobj]->containment = 0;
921                         tmpobj[nobj]->x = current_coord.x;
922                         tmpobj[nobj]->y = current_coord.y;
923                         if (!in_room)
924                             check_coord(current_coord.x, current_coord.y,
925                                         "Object");
926                   }
927                 | CONTAINED
928                   {
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;
933                   }
934                 ;
935
936 object_infos    : /* nothing */
937                   {
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.
944          */
945                   }
946                 | ',' curse_state ',' monster_id ',' enchantment optional_name
947                   {
948                   }
949                 | ',' curse_state ',' enchantment optional_name
950                   {
951                   }
952                 | ',' monster_id ',' enchantment optional_name
953                   {
954                   }
955                 ;
956
957 curse_state     : RANDOM_TYPE
958                   {
959                         tmpobj[nobj]->curse_state = -1;
960                   }
961                 | CURSE_TYPE
962                   {
963                         tmpobj[nobj]->curse_state = $1;
964                   }
965                 ;
966
967 monster_id      : STRING
968                   {
969                         int token = get_monster_id($1, (char)0);
970                         if (token == ERR)       /* "random" */
971                             tmpobj[nobj]->corpsenm = NON_PM - 1;
972                         else
973                             tmpobj[nobj]->corpsenm = token;
974                         Free($1);
975                   }
976                 ;
977
978 enchantment     : RANDOM_TYPE
979                   {
980                         tmpobj[nobj]->spe = -127;
981                   }
982                 | INTEGER
983                   {
984                         tmpobj[nobj]->spe = $1;
985                   }
986                 ;
987
988 optional_name   : /* nothing */
989                 | ',' NONE
990                   {
991                   }
992                 | ',' STRING
993                   {
994                         tmpobj[nobj]->name.str = $2;
995                   }
996                 ;
997
998 door_detail     : DOOR_ID ':' door_state ',' coordinate
999                   {
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");
1008                         ndoor++;
1009                         if (ndoor >= MAX_OF_TYPE) {
1010                                 yyerror("Too many doors in mazepart!");
1011                                 ndoor--;
1012                         }
1013                   }
1014                 ;
1015
1016 trap_detail     : TRAP_ID chance ':' trap_name ',' coordinate
1017                   {
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;
1023                         if (!in_room)
1024                             check_coord(current_coord.x, current_coord.y,
1025                                         "Trap");
1026                         if (++ntrap >= MAX_OF_TYPE) {
1027                                 yyerror("Too many traps in room or mazepart!");
1028                                 ntrap--;
1029                         }
1030                   }
1031                 ;
1032
1033 drawbridge_detail: DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state
1034                    {
1035                         int x, y, dir;
1036
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 */
1041                         dir = $5;
1042                         switch(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;
1047                         default:
1048                             yyerror("Invalid drawbridge direction");
1049                             break;
1050                         }
1051                         tmpdb[ndb]->dir = dir;
1052                         if (current_coord.x >= 0 && current_coord.y >= 0 &&
1053                             !IS_WALL(tmpmap[y][x])) {
1054                             char ebuf[60];
1055                             Sprintf(ebuf,
1056                                     "Wall needed for drawbridge (%02d, %02d)",
1057                                     current_coord.x, current_coord.y);
1058                             yyerror(ebuf);
1059                         }
1060
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;
1065                         else
1066                             yyerror("A drawbridge can only be open or closed!");
1067                         ndb++;
1068                         if (ndb >= MAX_OF_TYPE) {
1069                                 yyerror("Too many drawbridges in mazepart!");
1070                                 ndb--;
1071                         }
1072                    }
1073                 ;
1074
1075 mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION
1076                   {
1077                         tmpwalk[nwalk] = New(walk);
1078                         tmpwalk[nwalk]->x = current_coord.x;
1079                         tmpwalk[nwalk]->y = current_coord.y;
1080                         tmpwalk[nwalk]->dir = $5;
1081                         nwalk++;
1082                         if (nwalk >= MAX_OF_TYPE) {
1083                                 yyerror("Too many mazewalks in mazepart!");
1084                                 nwalk--;
1085                         }
1086                   }
1087                 ;
1088
1089 wallify_detail  : WALLIFY_ID
1090                   {
1091                         wallify_map();
1092                   }
1093                 ;
1094
1095 ladder_detail   : LADDER_ID ':' coordinate ',' UP_OR_DOWN
1096                   {
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;
1101                         if (!in_room)
1102                             check_coord(current_coord.x, current_coord.y,
1103                                         "Ladder");
1104                         nlad++;
1105                         if (nlad >= MAX_OF_TYPE) {
1106                                 yyerror("Too many ladders in mazepart!");
1107                                 nlad--;
1108                         }
1109                   }
1110                 ;
1111
1112 stair_detail    : STAIR_ID ':' coordinate ',' UP_OR_DOWN
1113                   {
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;
1118                         if (!in_room)
1119                             check_coord(current_coord.x, current_coord.y,
1120                                         "Stairway");
1121                         nstair++;
1122                         if (nstair >= MAX_OF_TYPE) {
1123                                 yyerror("Too many stairs in room or mazepart!");
1124                                 nstair--;
1125                         }
1126                   }
1127                 ;
1128
1129 stair_region    : STAIR_ID ':' lev_region
1130                   {
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;
1137                   }
1138                  ',' lev_region ',' UP_OR_DOWN
1139                   {
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;
1145                         if($8)
1146                             tmplreg[nlreg]->rtype = LR_UPSTAIR;
1147                         else
1148                             tmplreg[nlreg]->rtype = LR_DOWNSTAIR;
1149                         tmplreg[nlreg]->rname.str = 0;
1150                         nlreg++;
1151                         if (nlreg >= MAX_OF_TYPE) {
1152                                 yyerror("Too many levregions in mazepart!");
1153                                 nlreg--;
1154                         }
1155                   }
1156                 ;
1157
1158 portal_region   : PORTAL_ID ':' lev_region
1159                   {
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;
1166                   }
1167                  ',' lev_region ',' string
1168                   {
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;
1176                         nlreg++;
1177                         if (nlreg >= MAX_OF_TYPE) {
1178                                 yyerror("Too many levregions in mazepart!");
1179                                 nlreg--;
1180                         }
1181                   }
1182                 ;
1183
1184 teleprt_region  : TELEPRT_ID ':' lev_region
1185                   {
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;
1192                   }
1193                  ',' lev_region
1194                   {
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;
1200                   }
1201                 teleprt_detail
1202                   {
1203                         switch($<i>8) {
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;
1207                         }
1208                         tmplreg[nlreg]->rname.str = 0;
1209                         nlreg++;
1210                         if (nlreg >= MAX_OF_TYPE) {
1211                                 yyerror("Too many levregions in mazepart!");
1212                                 nlreg--;
1213                         }
1214                   }
1215                 ;
1216
1217 branch_region   : BRANCH_ID ':' lev_region
1218                   {
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;
1225                   }
1226                  ',' lev_region
1227                   {
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;
1235                         nlreg++;
1236                         if (nlreg >= MAX_OF_TYPE) {
1237                                 yyerror("Too many levregions in mazepart!");
1238                                 nlreg--;
1239                         }
1240                   }
1241                 ;
1242
1243 teleprt_detail  : /* empty */
1244                   {
1245                         $<i>$ = -1;
1246                   }
1247                 | ',' UP_OR_DOWN
1248                   {
1249                         $<i>$ = $2;
1250                   }
1251                 ;
1252
1253 lev_region      : region
1254                   {
1255                         $$ = 0;
1256                   }
1257                 | LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
1258                   {
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;
1273                         $$ = 1;
1274                   }
1275                 ;
1276
1277 fountain_detail : FOUNTAIN_ID ':' coordinate
1278                   {
1279                         tmpfountain[nfountain] = New(fountain);
1280                         tmpfountain[nfountain]->x = current_coord.x;
1281                         tmpfountain[nfountain]->y = current_coord.y;
1282                         if (!in_room)
1283                             check_coord(current_coord.x, current_coord.y,
1284                                         "Fountain");
1285                         nfountain++;
1286                         if (nfountain >= MAX_OF_TYPE) {
1287                             yyerror("Too many fountains in room or mazepart!");
1288                             nfountain--;
1289                         }
1290                   }
1291                 ;
1292
1293 sink_detail : SINK_ID ':' coordinate
1294                   {
1295                         tmpsink[nsink] = New(sink);
1296                         tmpsink[nsink]->x = current_coord.x;
1297                         tmpsink[nsink]->y = current_coord.y;
1298                         nsink++;
1299                         if (nsink >= MAX_OF_TYPE) {
1300                                 yyerror("Too many sinks in room!");
1301                                 nsink--;
1302                         }
1303                   }
1304                 ;
1305
1306 pool_detail : POOL_ID ':' coordinate
1307                   {
1308                         tmppool[npool] = New(pool);
1309                         tmppool[npool]->x = current_coord.x;
1310                         tmppool[npool]->y = current_coord.y;
1311                         npool++;
1312                         if (npool >= MAX_OF_TYPE) {
1313                                 yyerror("Too many pools in room!");
1314                                 npool--;
1315                         }
1316                   }
1317                 ;
1318
1319 diggable_detail : NON_DIGGABLE_ID ':' region
1320                   {
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;
1326                         ndig++;
1327                         if (ndig >= MAX_OF_TYPE) {
1328                                 yyerror("Too many diggables in mazepart!");
1329                                 ndig--;
1330                         }
1331                   }
1332                 ;
1333
1334 passwall_detail : NON_PASSWALL_ID ':' region
1335                   {
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;
1341                         npass++;
1342                         if (npass >= 32) {
1343                                 yyerror("Too many passwalls in mazepart!");
1344                                 npass--;
1345                         }
1346                   }
1347                 ;
1348
1349 region_detail   : REGION_ID ':' region ',' light_state ',' room_type prefilled
1350                   {
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 */
1373                             char ebuf[60];
1374                             register int x, y, nrock = 0;
1375
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++;
1381                             if(nrock) {
1382                                 Sprintf(ebuf,
1383                                         "Rock in room (%02d,%02d,%02d,%02d)?!",
1384                                         current_region.x1, current_region.y1,
1385                                         current_region.x2, current_region.y2);
1386                                 yywarning(ebuf);
1387                             }
1388                             if (
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])) {
1393                                 Sprintf(ebuf,
1394                                 "NonRock edge in room (%02d,%02d,%02d,%02d)?!",
1395                                         current_region.x1, current_region.y1,
1396                                         current_region.x2, current_region.y2);
1397                                 yywarning(ebuf);
1398                             }
1399                         } else if(tmpreg[nreg]->rirreg &&
1400                 !IS_ROOM(tmpmap[current_region.y1][current_region.x1])) {
1401                             char ebuf[60];
1402                             Sprintf(ebuf,
1403                                     "Rock in irregular room (%02d,%02d)?!",
1404                                     current_region.x1, current_region.y1);
1405                             yyerror(ebuf);
1406                         }
1407                         nreg++;
1408                         if (nreg >= MAX_OF_TYPE) {
1409                                 yyerror("Too many regions in mazepart!");
1410                                 nreg--;
1411                         }
1412                   }
1413                 ;
1414
1415 altar_detail    : ALTAR_ID ':' coordinate ',' alignment ',' altar_type
1416                   {
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;
1422                         if (!in_room)
1423                             check_coord(current_coord.x, current_coord.y,
1424                                         "Altar");
1425                         naltar++;
1426                         if (naltar >= MAX_OF_TYPE) {
1427                                 yyerror("Too many altars in room or mazepart!");
1428                                 naltar--;
1429                         }
1430                   }
1431                 ;
1432
1433 gold_detail     : GOLD_ID ':' amount ',' coordinate
1434                   {
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;
1439                         if (!in_room)
1440                             check_coord(current_coord.x, current_coord.y,
1441                                         "Gold");
1442                         ngold++;
1443                         if (ngold >= MAX_OF_TYPE) {
1444                                 yyerror("Too many golds in room or mazepart!");
1445                                 ngold--;
1446                         }
1447                   }
1448                 ;
1449
1450 engraving_detail: ENGRAVING_ID ':' coordinate ',' engraving_type ',' string
1451                   {
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;
1457                         if (!in_room)
1458                             check_coord(current_coord.x, current_coord.y,
1459                                         "Engraving");
1460                         nengraving++;
1461                         if (nengraving >= MAX_OF_TYPE) {
1462                             yyerror("Too many engravings in room or mazepart!");
1463                             nengraving--;
1464                         }
1465                   }
1466                 ;
1467
1468 monster_c       : monster
1469                 | RANDOM_TYPE
1470                   {
1471                         $<i>$ = - MAX_REGISTERS - 1;
1472                   }
1473                 | m_register
1474                 ;
1475
1476 object_c        : object
1477                 | RANDOM_TYPE
1478                   {
1479                         $<i>$ = - MAX_REGISTERS - 1;
1480                   }
1481                 | o_register
1482                 ;
1483
1484 m_name          : string
1485                 | RANDOM_TYPE
1486                   {
1487                         $$ = (char *) 0;
1488                   }
1489                 ;
1490
1491 o_name          : string
1492                 | RANDOM_TYPE
1493                   {
1494                         $$ = (char *) 0;
1495                   }
1496                 ;
1497
1498 trap_name       : string
1499                   {
1500                         int token = get_trap_type($1);
1501                         if (token == ERR)
1502                                 yyerror("Unknown trap type!");
1503                         $<i>$ = token;
1504                         Free($1);
1505                   }
1506                 | RANDOM_TYPE
1507                 ;
1508
1509 room_type       : string
1510                   {
1511                         int token = get_room_type($1);
1512                         if (token == ERR) {
1513                                 yywarning("Unknown room type!  Making ordinary room...");
1514                                 $<i>$ = OROOM;
1515                         } else
1516                                 $<i>$ = token;
1517                         Free($1);
1518                   }
1519                 | RANDOM_TYPE
1520                 ;
1521
1522 prefilled       : /* empty */
1523                   {
1524                         $<i>$ = 0;
1525                   }
1526                 | ',' FILLING
1527                   {
1528                         $<i>$ = $2;
1529                   }
1530                 | ',' FILLING ',' BOOLEAN
1531                   {
1532                         $<i>$ = $2 + ($4 << 1);
1533                   }
1534                 ;
1535
1536 coordinate      : coord
1537                 | p_register
1538                 | RANDOM_TYPE
1539                   {
1540                         current_coord.x = current_coord.y = -MAX_REGISTERS-1;
1541                   }
1542                 ;
1543
1544 door_state      : DOOR_STATE
1545                 | RANDOM_TYPE
1546                 ;
1547
1548 light_state     : LIGHT_STATE
1549                 | RANDOM_TYPE
1550                 ;
1551
1552 alignment       : ALIGNMENT
1553                 | a_register
1554                 | RANDOM_TYPE
1555                   {
1556                         $<i>$ = - MAX_REGISTERS - 1;
1557                   }
1558                 ;
1559
1560 altar_type      : ALTAR_TYPE
1561                 | RANDOM_TYPE
1562                 ;
1563
1564 p_register      : P_REGISTER '[' INTEGER ']'
1565                   {
1566                         if ( $3 >= MAX_REGISTERS )
1567                                 yyerror("Register Index overflow!");
1568                         else
1569                                 current_coord.x = current_coord.y = - $3 - 1;
1570                   }
1571                 ;
1572
1573 o_register      : O_REGISTER '[' INTEGER ']'
1574                   {
1575                         if ( $3 >= MAX_REGISTERS )
1576                                 yyerror("Register Index overflow!");
1577                         else
1578                                 $<i>$ = - $3 - 1;
1579                   }
1580                 ;
1581
1582 m_register      : M_REGISTER '[' INTEGER ']'
1583                   {
1584                         if ( $3 >= MAX_REGISTERS )
1585                                 yyerror("Register Index overflow!");
1586                         else
1587                                 $<i>$ = - $3 - 1;
1588                   }
1589                 ;
1590
1591 a_register      : A_REGISTER '[' INTEGER ']'
1592                   {
1593                         if ( $3 >= 3 )
1594                                 yyerror("Register Index overflow!");
1595                         else
1596                                 $<i>$ = - $3 - 1;
1597                   }
1598                 ;
1599
1600 place           : coord
1601                 ;
1602
1603 monster         : CHAR
1604                   {
1605                         if (check_monster_char((char) $1))
1606                                 $<i>$ = $1 ;
1607                         else {
1608                                 yyerror("Unknown monster class!");
1609                                 $<i>$ = ERR;
1610                         }
1611                   }
1612                 ;
1613
1614 object          : CHAR
1615                   {
1616                         char c = $1;
1617                         if (check_object_char(c))
1618                                 $<i>$ = c;
1619                         else {
1620                                 yyerror("Unknown char class!");
1621                                 $<i>$ = ERR;
1622                         }
1623                   }
1624                 ;
1625
1626 string          : STRING
1627                 ;
1628
1629 amount          : INTEGER
1630                 | RANDOM_TYPE
1631                 ;
1632
1633 chance          : /* empty */
1634                   {
1635                         $$ = 100;       /* default is 100% */
1636                   }
1637                 | PERCENT
1638                   {
1639                         if ($1 <= 0 || $1 > 100)
1640                             yyerror("Expected percentile chance.");
1641                         $$ = $1;
1642                   }
1643                 ;
1644
1645 engraving_type  : ENGRAVING_TYPE
1646                 | RANDOM_TYPE
1647                 ;
1648
1649 coord           : '(' INTEGER ',' INTEGER ')'
1650                   {
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;
1657                   }
1658                 ;
1659
1660 region          : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
1661                   {
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;
1676                   }
1677                 ;
1678
1679 %%
1680
1681 /*lev_comp.y*/