Files
nethack/util/lev_main.c
Pasi Kallinen c266d05680 Minor special level and compiler tweakage
Fix allowed map characters.
Make some predefined MAPs blend in better with randomly
generated parts.
2015-04-10 16:47:32 +03:00

1528 lines
34 KiB
C

/* NetHack 3.5 lev_main.c $NHDT-Date: 1428655166 2015/04/10 08:39:26 $ $NHDT-Branch: master $:$NHDT-Revision: 1.34 $ */
/* NetHack 3.5 lev_main.c $Date: 2012/01/12 04:48:12 $ $Revision: 1.20 $ */
/* SCCS Id: @(#)lev_main.c 3.5 2007/01/17 */
/* Copyright (c) 1989 by Jean-Christophe Collet */
/* NetHack may be freely redistributed. See license for details. */
/*
* This file contains the main function for the parser
* and some useful functions needed by yacc
*/
#define SPEC_LEV /* for USE_OLDARGS (sp_lev.h) and for MPW (macconf.h) */
#define NEED_VARARGS
#include "hack.h"
#include "date.h"
#include "sp_lev.h"
#ifdef STRICT_REF_DEF
#include "tcap.h"
#endif
#include <ctype.h>
#ifdef MAC
# if defined(__SC__) || defined(__MRC__)
# define MPWTOOL
# define PREFIX ":dungeon:" /* place output files here */
# include <CursorCtl.h>
# else
# if !defined(__MACH__)
# define PREFIX ":lib:" /* place output files here */
# endif
# endif
#endif
#ifdef WIN_CE
#define PREFIX "\\nethack\\dat\\"
#endif
#ifndef MPWTOOL
# define SpinCursor(x)
#endif
#if defined(AMIGA) && defined(DLB)
# define PREFIX "NH:slib/"
#endif
#ifndef O_WRONLY
#include <fcntl.h>
#endif
#ifndef O_CREAT /* some older BSD systems do not define O_CREAT in <fcntl.h> */
#include <sys/file.h>
#endif
#ifndef O_BINARY /* used for micros, no-op for others */
# define O_BINARY 0
#endif
#if defined(MICRO) || defined(WIN32)
# define OMASK FCMASK
#else
# define OMASK 0644
#endif
#define ERR (-1)
#define NewTab(type, size) (type **) alloc(sizeof(type *) * size)
#define Free(ptr) if(ptr) free((genericptr_t) (ptr))
/* write() returns a signed value but its size argument is unsigned;
types might be int and unsigned or ssize_t and size_t; casting
to long should be safe for all permutations (even if size_t is
bigger, since the values involved won't be too big for long) */
#define Write(fd, item, size) if ((long)write(fd, (genericptr_t)(item), size) \
!= (long)(size)) return FALSE;
#if defined(__BORLANDC__) && !defined(_WIN32)
extern unsigned _stklen = STKSIZ;
#endif
#define MAX_ERRORS 25
extern int NDECL(yyparse);
extern void FDECL(init_yyin, (FILE *));
extern void FDECL(init_yyout, (FILE *));
int FDECL(main, (int, char **));
void FDECL(yyerror, (const char *));
void FDECL(yywarning, (const char *));
int NDECL(yywrap);
int FDECL(get_floor_type, (CHAR_P));
int FDECL(get_room_type, (char *));
int FDECL(get_trap_type, (char *));
int FDECL(get_monster_id, (char *,CHAR_P));
int FDECL(get_object_id, (char *,CHAR_P));
boolean FDECL(check_monster_char, (CHAR_P));
boolean FDECL(check_object_char, (CHAR_P));
char FDECL(what_map_char, (CHAR_P));
void FDECL(scan_map, (char *, sp_lev *));
boolean NDECL(check_subrooms);
boolean FDECL(write_level_file, (char *,sp_lev *));
struct lc_funcdefs *FDECL(funcdef_new,(long,char *));
void FDECL(funcdef_free_all,(struct lc_funcdefs *));
struct lc_funcdefs *FDECL(funcdef_defined,(struct lc_funcdefs *,char *, int));
struct lc_vardefs *FDECL(vardef_new,(long,char *));
void FDECL(vardef_free_all,(struct lc_vardefs *));
struct lc_vardefs *FDECL(vardef_defined,(struct lc_vardefs *,char *, int));
void FDECL(splev_add_from, (sp_lev *, sp_lev *));
extern void NDECL(monst_init);
extern void NDECL(objects_init);
extern void NDECL(decl_init);
void FDECL(add_opcode, (sp_lev *, int, genericptr_t));
static boolean FDECL(write_common_data, (int));
static boolean FDECL(write_maze, (int,sp_lev *));
static void NDECL(init_obj_classes);
static int FDECL(case_insensitive_comp, (const char *, const char *));
void VDECL(lc_pline, (const char *, ...));
void VDECL(lc_error, (const char *, ...));
void VDECL(lc_warning, (const char *, ...));
char * FDECL(decode_parm_chr, (CHAR_P));
char * FDECL(decode_parm_str, (char *));
struct opvar * FDECL(set_opvar_int, (struct opvar *, long));
struct opvar * FDECL(set_opvar_coord, (struct opvar *, long));
struct opvar * FDECL(set_opvar_region, (struct opvar *, long));
struct opvar * FDECL(set_opvar_mapchar, (struct opvar *, long));
struct opvar * FDECL(set_opvar_monst, (struct opvar *, long));
struct opvar * FDECL(set_opvar_obj, (struct opvar *, long));
struct opvar * FDECL(set_opvar_str, (struct opvar *, const char *));
struct opvar * FDECL(set_opvar_var, (struct opvar *, const char *));
void VDECL(add_opvars, (sp_lev *, const char *, ...));
void NDECL(break_stmt_start);
void FDECL(break_stmt_end, (sp_lev *));
void FDECL(break_stmt_new, (sp_lev *, long));
char *FDECL(funcdef_paramtypes, (struct lc_funcdefs *));
const char *FDECL(spovar2str, (long));
void FDECL(vardef_used, (struct lc_vardefs *, char *));
void FDECL(check_vardef_type, (struct lc_vardefs *, char *, long));
struct lc_vardefs * FDECL(add_vardef_type, (struct lc_vardefs *, char *, long));
int FDECL(reverse_jmp_opcode, (int));
struct opvar * FDECL(opvar_clone, (struct opvar *));
void FDECL(start_level_def, (sp_lev **, char *));
static struct {
const char *name;
int type;
} trap_types[] = {
{ "arrow", ARROW_TRAP },
{ "dart", DART_TRAP },
{ "falling rock", ROCKTRAP },
{ "board", SQKY_BOARD },
{ "bear", BEAR_TRAP },
{ "land mine", LANDMINE },
{ "rolling boulder", ROLLING_BOULDER_TRAP },
{ "sleep gas", SLP_GAS_TRAP },
{ "rust", RUST_TRAP },
{ "fire", FIRE_TRAP },
{ "pit", PIT },
{ "spiked pit", SPIKED_PIT },
{ "hole", HOLE },
{ "trap door", TRAPDOOR },
{ "teleport", TELEP_TRAP },
{ "level teleport", LEVEL_TELEP },
{ "magic portal", MAGIC_PORTAL },
{ "web", WEB },
{ "statue", STATUE_TRAP },
{ "magic", MAGIC_TRAP },
{ "anti magic", ANTI_MAGIC },
{ "polymorph", POLY_TRAP },
{ 0, 0 }
};
static struct {
const char *name;
int type;
} room_types[] = {
/* for historical reasons, room types are not contiguous numbers */
/* (type 1 is skipped) */
{ "ordinary", OROOM },
{ "throne", COURT },
{ "swamp", SWAMP },
{ "vault", VAULT },
{ "beehive", BEEHIVE },
{ "morgue", MORGUE },
{ "barracks", BARRACKS },
{ "zoo", ZOO },
{ "delphi", DELPHI },
{ "temple", TEMPLE },
{ "anthole", ANTHOLE },
{ "cocknest", COCKNEST },
{ "leprehall", LEPREHALL },
{ "shop", SHOPBASE },
{ "armor shop", ARMORSHOP },
{ "scroll shop", SCROLLSHOP },
{ "potion shop", POTIONSHOP },
{ "weapon shop", WEAPONSHOP },
{ "food shop", FOODSHOP },
{ "ring shop", RINGSHOP },
{ "wand shop", WANDSHOP },
{ "tool shop", TOOLSHOP },
{ "book shop", BOOKSHOP },
{ "health food shop", FODDERSHOP },
{ "candle shop", CANDLESHOP },
{ 0, 0 }
};
const char *fname = "(stdin)";
int fatal_error = 0;
int got_errors = 0;
int be_verbose = 0;
int fname_counter = 1;
#ifdef FLEX23_BUG
/* Flex 2.3 bug work around; not needed for 2.3.6 or later */
int yy_more_len = 0;
#endif
extern unsigned int max_x_map, max_y_map;
extern int nh_line_number;
extern int token_start_pos;
extern char curr_token[512];
struct lc_vardefs *variable_definitions = NULL;
struct lc_funcdefs *function_definitions = NULL;
extern int allow_break_statements;
extern struct lc_breakdef *break_list;
int
main(argc, argv)
int argc;
char **argv;
{
FILE *fin;
int i;
boolean errors_encountered = FALSE;
#if defined(MAC) && (defined(THINK_C) || defined(__MWERKS__))
static char *mac_argv[] = { "lev_comp", /* dummy argv[0] */
":dat:Arch.des",
":dat:Barb.des",
":dat:Caveman.des",
":dat:Healer.des",
":dat:Knight.des",
":dat:Monk.des",
":dat:Priest.des",
":dat:Ranger.des",
":dat:Rogue.des",
":dat:Samurai.des",
":dat:Tourist.des",
":dat:Valkyrie.des",
":dat:Wizard.des",
":dat:bigroom.des",
":dat:castle.des",
":dat:endgame.des",
":dat:gehennom.des",
":dat:knox.des",
":dat:medusa.des",
":dat:mines.des",
":dat:oracle.des",
":dat:sokoban.des",
":dat:tower.des",
":dat:yendor.des"
};
argc = SIZE(mac_argv);
argv = mac_argv;
#endif
/* Note: these initializers don't do anything except guarantee that
we're linked properly.
*/
monst_init();
objects_init();
decl_init();
/* this one does something... */
init_obj_classes();
init_yyout(stdout);
if (argc == 1) { /* Read standard input */
init_yyin(stdin);
(void) yyparse();
if (fatal_error > 0) {
errors_encountered = TRUE;
}
} else { /* Otherwise every argument is a filename */
for(i=1; i<argc; i++) {
fname = argv[i];
if(!strcmp(fname, "-v")) {
be_verbose++;
continue;
}
fin = freopen(fname, "r", stdin);
if (!fin) {
lc_pline("Can't open \"%s\" for input.\n", fname);
perror(fname);
errors_encountered = TRUE;
} else {
fname_counter = 1;
init_yyin(fin);
(void) yyparse();
nh_line_number = 1;
if (fatal_error > 0 || got_errors > 0) {
errors_encountered = TRUE;
fatal_error = 0;
}
}
}
}
exit(errors_encountered ? EXIT_FAILURE : EXIT_SUCCESS);
/*NOTREACHED*/
return 0;
}
/*
* Each time the parser detects an error, it uses this function.
* Here we take count of the errors. To continue farther than
* MAX_ERRORS wouldn't be reasonable.
* Assume that explicit calls from lev_comp.y have the 1st letter
* capitalized, to allow printing of the line containing the start of
* the current declaration, instead of the beginning of the next declaration.
*/
void
yyerror(s)
const char *s;
{
char *e = ((char *)s + strlen(s) - 1);
(void) fprintf(stderr, "%s: line %d, pos %d: %s",
fname, nh_line_number,
token_start_pos - (int)strlen(curr_token), s);
if (*e != '.' && *e != '!')
(void) fprintf(stderr, " at \"%s\"", curr_token);
(void) fprintf(stderr, "\n");
if (++fatal_error > MAX_ERRORS) {
(void) fprintf(stderr,"Too many errors, good bye!\n");
exit(EXIT_FAILURE);
}
}
/*
* Just display a warning (that is : a non fatal error)
*/
void
yywarning(s)
const char *s;
{
(void) fprintf(stderr, "%s: line %d : WARNING : %s\n",
fname, nh_line_number, s);
}
/*
* Stub needed for lex interface.
*/
int
yywrap()
{
return 1;
}
/*
* lc_pline(): lev_comp version of pline(), stripped down version of
* core's pline(), with convoluted handling for variadic arguments to
* support <stdarg.h>, <varargs.h>, and neither of the above.
*
* Using state for message/warning/error mode simplifies calling interface.
*/
#define LC_PLINE_MESSAGE 0
#define LC_PLINE_WARNING 1
#define LC_PLINE_ERROR 2
static int lc_pline_mode = LC_PLINE_MESSAGE;
#if defined(USE_STDARG) || defined(USE_VARARGS)
static void FDECL(lc_vpline, (const char *, va_list));
void
lc_pline VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, char *);
lc_vpline(line, VA_ARGS);
VA_END();
}
# ifdef USE_STDARG
static void
lc_vpline(const char *line, va_list the_args) {
# else
static void
lc_vpline(line, the_args) const char *line; va_list the_args; {
# endif
#else /* USE_STDARG | USE_VARARG */
#define lc_vpline lc_pline
void
lc_pline VA_DECL(const char *, line)
#endif /* USE_STDARG | USE_VARARG */
char pbuf[3*BUFSZ];
static char nomsg[] = "(no message)";
/* Do NOT use VA_START and VA_END in here... see above */
if (!line || !*line) line = nomsg; /* shouldn't happen */
if (index(line, '%')) {
Vsprintf(pbuf, line, VA_ARGS);
pbuf[BUFSZ-1] = '\0'; /* truncate if long */
line = pbuf;
}
switch (lc_pline_mode) {
case LC_PLINE_ERROR: yyerror(line); break;
case LC_PLINE_WARNING: yywarning(line); break;
default: (void)fprintf(stderr, "%s\n", line); break;
}
lc_pline_mode = LC_PLINE_MESSAGE; /* reset to default */
return;
}
/*VARARGS1*/
void
lc_error VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, const char *);
lc_pline_mode = LC_PLINE_ERROR;
lc_vpline(line, VA_ARGS);
VA_END();
return;
}
/*VARARGS1*/
void
lc_warning VA_DECL(const char *, line)
VA_START(line);
VA_INIT(line, const char *);
lc_pline_mode = LC_PLINE_WARNING;
lc_vpline(line, VA_ARGS);
VA_END();
return;
}
char *
decode_parm_chr(chr)
char chr;
{
static char buf[32];
switch (chr) {
default: Strcpy(buf, "unknown"); break;
case 'i': Strcpy(buf, "int"); break;
case 'r': Strcpy(buf, "region"); break;
case 's': Strcpy(buf, "str"); break;
case 'O': Strcpy(buf, "obj"); break;
case 'c': Strcpy(buf, "coord"); break;
case ' ': Strcpy(buf, "nothing"); break;
case 'm': Strcpy(buf, "mapchar"); break;
case 'M': Strcpy(buf, "monster"); break;
}
return buf;
}
char *
decode_parm_str(str)
char *str;
{
static char tmpbuf[1024];
char *p = str;
tmpbuf[0] = '\0';
if (str) {
for ( ; *p; p++) {
Strcat(tmpbuf, decode_parm_chr(*p));
if (*(p + 1)) Strcat(tmpbuf, ", ");
}
}
return tmpbuf;
}
struct opvar *
set_opvar_int(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_INT;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_coord(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_COORD;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_region(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_REGION;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_mapchar(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_MAPCHAR;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_monst(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_MONST;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_obj(ov, val)
struct opvar *ov;
long val;
{
if (ov) {
ov->spovartyp = SPOVAR_OBJ;
ov->vardata.l = val;
}
return ov;
}
struct opvar *
set_opvar_str(ov, val)
struct opvar *ov;
const char *val;
{
if (ov) {
ov->spovartyp = SPOVAR_STRING;
ov->vardata.str = (val) ? strdup(val) : NULL;
}
return ov;
}
struct opvar *
set_opvar_var(ov, val)
struct opvar *ov;
const char *val;
{
if (ov) {
ov->spovartyp = SPOVAR_VARIABLE;
ov->vardata.str = (val) ? strdup(val) : NULL;
}
return ov;
}
#define New(type) \
(type *) memset((genericptr_t)alloc(sizeof(type)), 0, sizeof(type))
#if defined(USE_STDARG) || defined(USE_VARARGS)
static void FDECL(vadd_opvars, (sp_lev *, const char *, va_list));
void
add_opvars VA_DECL2(sp_lev *, sp, const char *, fmt)
VA_START(fmt);
VA_INIT(fmt, char *);
vadd_opvars(sp, fmt, VA_ARGS);
VA_END();
}
# ifdef USE_STDARG
static void
vadd_opvars(sp_lev *sp, const char *fmt, va_list the_args) {
# else
static void
vadd_opvars(sp, fmt, the_args) sp_lev *sp; const char *fmt; va_list the_args; {
# endif
#else /* USE_STDARG | USE_VARARG */
#define vadd_opvars add_opvars
void
add_opvars VA_DECL2(sp_lev *, sp, const char *, fmt)
#endif /* USE_STDARG | USE_VARARG */
const char *p, *lp;
long la;
/* Do NOT use VA_START and VA_END in here... see above */
for(p = fmt; *p != '\0'; p++) {
switch(*p) {
case ' ': break;
case 'i': /* integer */
{
struct opvar *ov = New(struct opvar);
set_opvar_int(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'c': /* coordinate */
{
struct opvar *ov = New(struct opvar);
set_opvar_coord(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'r': /* region */
{
struct opvar *ov = New(struct opvar);
set_opvar_region(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'm': /* mapchar */
{
struct opvar *ov = New(struct opvar);
set_opvar_mapchar(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'M': /* monster */
{
struct opvar *ov = New(struct opvar);
set_opvar_monst(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'O': /* object */
{
struct opvar *ov = New(struct opvar);
set_opvar_obj(ov, VA_NEXT(la, long));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 's': /* string */
{
struct opvar *ov = New(struct opvar);
set_opvar_str(ov, VA_NEXT(lp, const char *));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'v': /* variable */
{
struct opvar *ov = New(struct opvar);
set_opvar_var(ov, VA_NEXT(lp, const char *));
add_opcode(sp, SPO_PUSH, ov);
break;
}
case 'o': /* opcode */
{
long i = VA_NEXT(la, int);
if (i < 0 || i >= MAX_SP_OPCODES)
lc_pline("add_opvars: unknown opcode '%ld'.", i);
add_opcode(sp, i, NULL);
break;
}
default:
lc_pline("add_opvars: illegal format character '%c'.", *p);
break;
}
}
return;
}
void
break_stmt_start()
{
allow_break_statements++;
}
void
break_stmt_end(splev)
sp_lev *splev;
{
struct lc_breakdef *tmp = break_list;
struct lc_breakdef *prv = NULL;
while (tmp) {
if (tmp->break_depth == allow_break_statements) {
struct lc_breakdef *nxt = tmp->next;
set_opvar_int(tmp->breakpoint, splev->n_opcodes - tmp->breakpoint->vardata.l-1);
tmp->next = NULL;
Free(tmp);
if (!prv) break_list = NULL;
else prv->next = nxt;
tmp = nxt;
} else {
prv = tmp;
tmp = tmp->next;
}
}
allow_break_statements--;
}
void
break_stmt_new(splev,i)
sp_lev *splev;
long i;
{
struct lc_breakdef *tmp = New(struct lc_breakdef);
tmp->breakpoint = New(struct opvar);
tmp->break_depth = allow_break_statements;
tmp->next = break_list;
break_list = tmp;
set_opvar_int(tmp->breakpoint, i);
add_opcode(splev, SPO_PUSH, tmp->breakpoint);
add_opcode(splev, SPO_JMP, NULL);
}
struct lc_funcdefs *
funcdef_new(addr, name)
long addr;
char *name;
{
struct lc_funcdefs *f = New(struct lc_funcdefs);
if (!f) {
lc_error("Could not alloc function definition for '%s'.", name);
return NULL;
}
f->next = NULL;
f->addr = addr;
f->name = strdup(name);
f->n_called = 0;
f->n_params = 0;
f->params = NULL;
f->code.opcodes = NULL;
f->code.n_opcodes = 0;
return f;
}
void
funcdef_free_all(fchain)
struct lc_funcdefs *fchain;
{
struct lc_funcdefs *tmp = fchain;
struct lc_funcdefs *nxt;
struct lc_funcdefs_parm *tmpparam;
while (tmp) {
nxt = tmp->next;
Free(tmp->name);
while (tmp->params) {
tmpparam = tmp->params->next;
Free(tmp->params->name);
tmp->params = tmpparam;
}
/* FIXME: free tmp->code */
Free(tmp);
tmp = nxt;
}
}
char *
funcdef_paramtypes(f)
struct lc_funcdefs *f;
{
int i = 0;
struct lc_funcdefs_parm *fp = f->params;
char *tmp = (char *)alloc((f->n_params) + 1);
if (!tmp) return NULL;
while (fp) {
tmp[i++] = fp->parmtype;
fp = fp->next;
}
tmp[i] = '\0';
return tmp;
}
struct lc_funcdefs *
funcdef_defined(f, name, casesense)
struct lc_funcdefs *f;
char *name;
int casesense;
{
while (f) {
if (casesense) {
if (!strcmp(name, f->name)) return f;
} else {
if (!case_insensitive_comp(name, f->name)) return f;
}
f = f->next;
}
return NULL;
}
struct lc_vardefs *
vardef_new(typ, name)
long typ;
char *name;
{
struct lc_vardefs *f = New(struct lc_vardefs);
if (!f) {
lc_error("Could not alloc variable definition for '%s'.", name);
return NULL;
}
f->next = NULL;
f->var_type = typ;
f->name = strdup(name);
f->n_used = 0;
return f;
}
void
vardef_free_all(fchain)
struct lc_vardefs *fchain;
{
struct lc_vardefs *tmp = fchain;
struct lc_vardefs *nxt;
while (tmp) {
if (be_verbose && (tmp->n_used == 0))
lc_warning("Unused variable '%s'", tmp->name);
nxt = tmp->next;
Free(tmp->name);
Free(tmp);
tmp = nxt;
}
}
struct lc_vardefs *
vardef_defined(f, name, casesense)
struct lc_vardefs *f;
char *name;
int casesense;
{
while (f) {
if (casesense) {
if (!strcmp(name, f->name)) return f;
} else {
if (!case_insensitive_comp(name, f->name)) return f;
}
f = f->next;
}
return NULL;
}
const char *
spovar2str(spovar)
long spovar;
{
static int togl = 0;
static char buf[2][128];
const char *n = NULL;
int is_array = (spovar & SPOVAR_ARRAY);
spovar &= ~SPOVAR_ARRAY;
switch (spovar) {
default: lc_error("spovar2str(%ld)", spovar); break;
case SPOVAR_INT: n = "integer"; break;
case SPOVAR_STRING: n = "string"; break;
case SPOVAR_VARIABLE: n = "variable"; break;
case SPOVAR_COORD: n = "coordinate"; break;
case SPOVAR_REGION: n = "region"; break;
case SPOVAR_MAPCHAR: n = "mapchar"; break;
case SPOVAR_MONST: n = "monster"; break;
case SPOVAR_OBJ: n = "object"; break;
}
togl = ((togl + 1) % 2);
snprintf(buf[togl], 127, "%s%s", n, (is_array ? " array" : ""));
return buf[togl];
}
void
vardef_used(vd, varname)
struct lc_vardefs *vd;
char *varname;
{
struct lc_vardefs *tmp;
if ((tmp = vardef_defined(vd, varname, 1))) tmp->n_used++;
}
void
check_vardef_type(vd, varname, vartype)
struct lc_vardefs *vd;
char *varname;
long vartype;
{
struct lc_vardefs *tmp;
if ((tmp = vardef_defined(vd, varname, 1))) {
if (tmp->var_type != vartype)
lc_error("Trying to use variable '%s' as %s, when it is %s.",
varname, spovar2str(vartype), spovar2str(tmp->var_type));
} else lc_error("Variable '%s' not defined.", varname);
}
struct lc_vardefs *
add_vardef_type(vd, varname, vartype)
struct lc_vardefs *vd;
char *varname;
long vartype;
{
struct lc_vardefs *tmp;
if ((tmp = vardef_defined(vd, varname, 1))) {
if (tmp->var_type != vartype)
lc_error("Trying to redefine variable '%s' as %s, when it is %s.",
varname, spovar2str(vartype), spovar2str(tmp->var_type));
} else {
tmp = vardef_new(vartype, varname);
tmp->next = vd;
return tmp;
}
return vd;
}
int
reverse_jmp_opcode(opcode)
int opcode;
{
switch (opcode) {
case SPO_JE: return SPO_JNE;
case SPO_JNE: return SPO_JE;
case SPO_JL: return SPO_JGE;
case SPO_JG: return SPO_JLE;
case SPO_JLE: return SPO_JG;
case SPO_JGE: return SPO_JL;
default: lc_error("Cannot reverse comparison jmp opcode %d.", opcode); return SPO_NULL;
}
}
/* basically copied from src/sp_lev.c */
struct opvar *
opvar_clone(ov)
struct opvar *ov;
{
if (ov) {
struct opvar *tmpov = (struct opvar *)alloc(sizeof(struct opvar));
if (!tmpov) panic("could not alloc opvar struct");
switch (ov->spovartyp) {
case SPOVAR_COORD:
case SPOVAR_REGION:
case SPOVAR_MAPCHAR:
case SPOVAR_MONST:
case SPOVAR_OBJ:
case SPOVAR_INT:
{
tmpov->spovartyp = ov->spovartyp;
tmpov->vardata.l = ov->vardata.l;
}
break;
case SPOVAR_VARIABLE:
case SPOVAR_STRING:
{
int len = strlen(ov->vardata.str);
tmpov->spovartyp = ov->spovartyp;
tmpov->vardata.str = (char *)alloc(len+1);
(void)memcpy((genericptr_t)tmpov->vardata.str,
(genericptr_t)ov->vardata.str, len);
tmpov->vardata.str[len] = '\0';
}
break;
default:
{
lc_error("Unknown opvar_clone value type (%d)!", ov->spovartyp);
}
}
return tmpov;
}
return NULL;
}
void
splev_add_from(splev, from_splev)
sp_lev *splev;
sp_lev *from_splev;
{
int i;
if (splev && from_splev)
for (i = 0; i < from_splev->n_opcodes; i++)
add_opcode(splev, from_splev->opcodes[i].opcode, opvar_clone(from_splev->opcodes[i].opdat));
}
void
start_level_def(splev, ldfname)
sp_lev **splev;
char *ldfname;
{
struct lc_funcdefs *f;
if (index(ldfname, '.'))
lc_error("Invalid dot ('.') in level name '%s'.", ldfname);
if ((int) strlen(ldfname) > 14)
lc_error("Level names limited to 14 characters ('%s').", ldfname);
f = function_definitions;
while (f) {
f->n_called = 0;
f = f->next;
}
*splev = (sp_lev *)alloc(sizeof(sp_lev));
(*splev)->n_opcodes = 0;
(*splev)->opcodes = NULL;
vardef_free_all(variable_definitions);
variable_definitions = NULL;
}
/*
* Find the type of floor, knowing its char representation.
*/
int
get_floor_type(c)
char c;
{
int val;
SpinCursor(3);
val = what_map_char(c);
if(val == INVALID_TYPE) {
val = ERR;
yywarning("Invalid fill character in MAZE declaration");
}
return val;
}
/*
* Find the type of a room in the table, knowing its name.
*/
int
get_room_type(s)
char *s;
{
register int i;
SpinCursor(3);
for(i=0; room_types[i].name; i++)
if (!strcmp(s, room_types[i].name))
return ((int) room_types[i].type);
return ERR;
}
/*
* Find the type of a trap in the table, knowing its name.
*/
int
get_trap_type(s)
char *s;
{
register int i;
SpinCursor(3);
for (i=0; trap_types[i].name; i++)
if(!strcmp(s,trap_types[i].name))
return trap_types[i].type;
return ERR;
}
/*
* Find the index of a monster in the table, knowing its name.
*/
int
get_monster_id(s, c)
char *s;
char c;
{
register int i, class;
SpinCursor(3);
class = c ? def_char_to_monclass(c) : 0;
if (class == MAXMCLASSES) return ERR;
for (i = LOW_PM; i < NUMMONS; i++)
if (!class || class == mons[i].mlet)
if (!strcmp(s, mons[i].mname)) return i;
/* didn't find it; lets try case insensitive search */
for (i = LOW_PM; i < NUMMONS; i++)
if (!class || class == mons[i].mlet)
if (!case_insensitive_comp(s, mons[i].mname)) {
if (be_verbose)
lc_warning("Monster type \"%s\" matches \"%s\".", s, mons[i].mname);
return i;
}
return ERR;
}
/*
* Find the index of an object in the table, knowing its name.
*/
int
get_object_id(s, c)
char *s;
char c; /* class */
{
int i, class;
const char *objname;
SpinCursor(3);
class = (c > 0) ? def_char_to_objclass(c) : 0;
if (class == MAXOCLASSES) return ERR;
for (i = class ? bases[class] : 0; i < NUM_OBJECTS; i++) {
if (class && objects[i].oc_class != class) break;
objname = obj_descr[i].oc_name;
if (objname && !strcmp(s, objname))
return i;
}
for (i = class ? bases[class] : 0; i < NUM_OBJECTS; i++) {
if (class && objects[i].oc_class != class) break;
objname = obj_descr[i].oc_name;
if (objname && !case_insensitive_comp(s, objname)) {
if (be_verbose)
lc_warning("Object type \"%s\" matches \"%s\".", s, objname);
return i;
}
}
return ERR;
}
static void
init_obj_classes()
{
int i, class, prev_class;
prev_class = -1;
for (i = 0; i < NUM_OBJECTS; i++) {
class = objects[i].oc_class;
if (class != prev_class) {
bases[class] = i;
prev_class = class;
}
}
}
/*
* Is the character 'c' a valid monster class ?
*/
boolean
check_monster_char(c)
char c;
{
return (def_char_to_monclass(c) != MAXMCLASSES);
}
/*
* Is the character 'c' a valid object class ?
*/
boolean
check_object_char(c)
char c;
{
return (def_char_to_objclass(c) != MAXOCLASSES);
}
/*
* Convert .des map letter into floor type.
*/
char
what_map_char(c)
char c;
{
SpinCursor(3);
switch(c) {
case ' ' : return(STONE);
case '#' : return(CORR);
case '.' : return(ROOM);
case '-' : return(HWALL);
case '|' : return(VWALL);
case '+' : return(DOOR);
case 'A' : return(AIR);
case 'B' : return(CROSSWALL); /* hack: boundary location */
case 'C' : return(CLOUD);
case 'S' : return(SDOOR);
case 'H' : return(SCORR);
case '{' : return(FOUNTAIN);
case '\\' : return(THRONE);
case 'K' : return(SINK);
case '}' : return(MOAT);
case 'P' : return(POOL);
case 'L' : return(LAVAPOOL);
case 'I' : return(ICE);
case 'W' : return(WATER);
case 'T' : return (TREE);
case 'F' : return (IRONBARS); /* Fe = iron */
case 'x' : return(MAX_TYPE); /* "see-through" */
}
return(INVALID_TYPE);
}
void
add_opcode(sp, opc, dat)
sp_lev *sp;
int opc;
genericptr_t dat;
{
long nop = sp->n_opcodes;
_opcode *tmp;
if ((opc < 0) || (opc >= MAX_SP_OPCODES))
lc_error("Unknown opcode '%d'", opc);
tmp = (_opcode *)alloc(sizeof(_opcode)*(nop+1));
if (sp->opcodes && nop) {
(void) memcpy(tmp, sp->opcodes, sizeof(_opcode)*nop);
free(sp->opcodes);
} else if (!tmp)
lc_error("Could not alloc opcode space");
sp->opcodes = tmp;
sp->opcodes[nop].opcode = opc;
sp->opcodes[nop].opdat = dat;
sp->n_opcodes++;
}
/*
* Yep! LEX gives us the map in a raw mode.
* Just analyze it here.
*/
void
scan_map(map, sp)
char *map;
sp_lev *sp;
{
register int i, len;
register char *s1, *s2;
int max_len = 0;
int max_hig = 0;
char *tmpmap[ROWNO];
int dx,dy;
char *mbuf;
/* First, strip out digits 0-9 (line numbering) */
for (s1 = s2 = map; *s1; s1++)
if (*s1 < '0' || *s1 > '9')
*s2++ = *s1;
*s2 = '\0';
/* Second, find the max width of the map */
s1 = map;
while (s1 && *s1) {
s2 = index(s1, '\n');
if (s2) {
len = (int) (s2 - s1);
s1 = s2 + 1;
} else {
len = (int) strlen(s1);
s1 = (char *) 0;
}
if (len > max_len) max_len = len;
}
/* Then parse it now */
while (map && *map) {
tmpmap[max_hig] = (char *) alloc(max_len);
s1 = index(map, '\n');
if (s1) {
len = (int) (s1 - map);
s1++;
} else {
len = (int) strlen(map);
s1 = map + len;
}
for(i=0; i<len; i++)
if((tmpmap[max_hig][i] = what_map_char(map[i])) == INVALID_TYPE) {
lc_warning("Invalid character '%c' @ (%d, %d) - replacing with stone", map[i], max_hig, i);
tmpmap[max_hig][i] = STONE;
}
while(i < max_len)
tmpmap[max_hig][i++] = STONE;
map = s1;
max_hig++;
}
/* Memorize boundaries */
max_x_map = max_len - 1;
max_y_map = max_hig - 1;
if(max_len > MAP_X_LIM || max_hig > MAP_Y_LIM) {
lc_error("Map too large at (%d x %d), max is (%d x %d)", max_len, max_hig, MAP_X_LIM, MAP_Y_LIM);
}
mbuf = (char *) alloc(((max_hig-1) * max_len) + (max_len-1) + 2);
for (dy = 0; dy < max_hig; dy++)
for (dx = 0; dx < max_len; dx++)
mbuf[(dy * max_len) + dx] = (tmpmap[dy][dx] + 1);
mbuf[((max_hig-1) * max_len) + (max_len-1) + 1] = '\0';
add_opvars(sp, "siio", VA_PASS4(mbuf, max_hig, max_len, SPO_MAP));
for (dy = 0; dy < max_hig; dy++)
Free(tmpmap[dy]);
Free(mbuf);
}
/*
* Output some info common to all special levels.
*/
static boolean
write_common_data(fd)
int fd;
{
static struct version_info version_data = {
VERSION_NUMBER, VERSION_FEATURES,
VERSION_SANITY1, VERSION_SANITY2, VERSION_SANITY3
};
Write(fd, &version_data, sizeof version_data);
return TRUE;
}
/*
* Here we write the sp_lev structure in the specified file (fd).
* Also, we have to free the memory allocated via alloc().
*/
static boolean
write_maze(fd, maze)
int fd;
sp_lev *maze;
{
int i;
if (!write_common_data(fd))
return FALSE;
Write(fd, &(maze->n_opcodes), sizeof(maze->n_opcodes));
for (i = 0; i < maze->n_opcodes; i++) {
_opcode tmpo = maze->opcodes[i];
Write(fd, &(tmpo.opcode), sizeof(tmpo.opcode));
if (tmpo.opcode < SPO_NULL || tmpo.opcode >= MAX_SP_OPCODES)
panic("write_maze: unknown opcode (%d).", tmpo.opcode);
if (tmpo.opcode == SPO_PUSH) {
genericptr_t opdat = tmpo.opdat;
if (opdat) {
struct opvar *ov = (struct opvar *)opdat;
int size;
Write(fd, &(ov->spovartyp), sizeof(ov->spovartyp));
switch (ov->spovartyp) {
case SPOVAR_NULL: break;
case SPOVAR_COORD:
case SPOVAR_REGION:
case SPOVAR_MAPCHAR:
case SPOVAR_MONST:
case SPOVAR_OBJ:
case SPOVAR_INT:
Write(fd, &(ov->vardata.l), sizeof(ov->vardata.l));
break;
case SPOVAR_VARIABLE:
case SPOVAR_STRING:
if (ov->vardata.str)
size = strlen(ov->vardata.str);
else size = 0;
Write(fd, &size, sizeof(size));
if (size) {
Write(fd, ov->vardata.str, size);
Free(ov->vardata.str);
}
break;
default: panic("write_maze: unknown data type (%d).", ov->spovartyp);
}
} else panic("write_maze: PUSH with no data.");
} else {
/* sanity check */
genericptr_t opdat = tmpo.opdat;
if (opdat)
panic("write_maze: opcode (%d) has data.", tmpo.opcode);
}
Free(tmpo.opdat);
}
/* clear the struct for next user */
Free(maze->opcodes);
maze->opcodes = NULL;
/*(void) memset((genericptr_t) &maze->init_lev, 0, sizeof maze->init_lev);*/
return TRUE;
}
/*
* Open and write maze or rooms file, based on which pointer is non-null.
* Return TRUE on success, FALSE on failure.
*/
boolean
write_level_file(filename, lvl)
char *filename;
sp_lev *lvl;
{
int fout;
char lbuf[60];
lbuf[0] = '\0';
#ifdef PREFIX
Strcat(lbuf, PREFIX);
#endif
Strcat(lbuf, filename);
Strcat(lbuf, LEV_EXT);
#if defined(MAC) && (defined(__SC__) || defined(__MRC__))
fout = open(lbuf, O_WRONLY|O_CREAT|O_BINARY);
#else
fout = open(lbuf, O_WRONLY|O_CREAT|O_BINARY, OMASK);
#endif
if (fout < 0) return FALSE;
if (!lvl) panic("write_level_file");
if (be_verbose)
fprintf(stdout, "File: '%s', opcodes: %ld\n", lbuf, lvl->n_opcodes);
if (!write_maze(fout, lvl))
return FALSE;
(void) close(fout);
return TRUE;
}
static int
case_insensitive_comp(s1, s2)
const char *s1;
const char *s2;
{
unsigned char u1, u2;
for ( ; ; s1++, s2++) {
u1 = tolower((unsigned char) *s1);
u2 = tolower((unsigned char) *s2);
if ((u1 == '\0') || (u1 != u2)) {
break;
}
}
return u1-u2;
}
#ifdef STRICT_REF_DEF
/*
* Any globals declared in hack.h and descendents which aren't defined
* in the modules linked into lev_comp should be defined here. These
* definitions can be dummies: their sizes shouldn't matter as long as
* as their types are correct; actual values are irrelevant.
*/
#define ARBITRARY_SIZE 1
/* attrib.c */
struct attribs attrmax, attrmin;
/* files.c */
const char *configfile;
char lock[ARBITRARY_SIZE];
char SAVEF[ARBITRARY_SIZE];
# ifdef MICRO
char SAVEP[ARBITRARY_SIZE];
# endif
/* termcap.c */
struct tc_lcl_data tc_lcl_data;
# ifdef TEXTCOLOR
# ifdef TOS
const char *hilites[CLR_MAX];
# else
char NEARDATA *hilites[CLR_MAX];
# endif
# endif
/* trap.c */
const char *traps[TRAPNUM];
/* window.c */
# ifdef HANGUPHANDLING
volatile
# endif
struct window_procs windowprocs;
/* xxxtty.c */
# ifdef DEFINE_OSPEED
short ospeed;
# endif
# ifndef STRNCMPI
char
lowc(c) /* force 'c' into lowercase */
char c;
{
return((char)(('A' <= c && c <= 'Z') ? (c | 040) : c));
}
int
strncmpi(s1, s2, n) /* case insensitive counted string comparison */
register const char *s1, *s2;
register int n; /*(should probably be size_t, which is usually unsigned)*/
{ /*{ aka strncasecmp }*/
register char t1, t2;
while (n--) {
if (!*s2) return (*s1 != 0); /* s1 >= s2 */
else if (!*s1) return -1; /* s1 < s2 */
t1 = lowc(*s1++);
t2 = lowc(*s2++);
if (t1 != t2) return (t1 > t2) ? 1 : -1;
}
return 0; /* s1 == s2 */
}
# endif /* STRNCMPI */
#endif /* STRICT_REF_DEF */
/*lev_main.c*/