/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* Copyright (c) M. Stephenson, 1990, 1991.			  */
/* Copyright (c) Dean Luick, 1990.				  */
/* NetHack may be freely redistributed.  See license for details. */
/* makedefs.c - NetHack version 3.1 */

#define	MAKEDEFS_C	/* use to conditionally include file sections */
/* #define DEBUG /* uncomment for debugging info */

#include "config.h"
#include "permonst.h"
#include "objclass.h"
#include "monsym.h"
#include "artilist.h"

#include "mondata.h"

/* version information */
#ifdef SHORT_FILENAMES
#include "patchlev.h"
#else
#include "patchlevel.h"
#endif

#ifdef MAC
# ifdef applec	/* Means the MPW compiler, I hope */
#  define MPWTOOL
#  include <CursorCtl.h>
#  include <string.h>
#  include <ctype.h>
# else		/* MAC without MPWTOOL */
#  define MACsansMPWTOOL
# endif
#endif /* MAC */

#ifndef MPWTOOL
# define SpinCursor(x)
#endif

#define Fprintf	(void) fprintf
#define Fclose	(void) fclose
#define Unlink	(void) unlink
#if !defined(AMIGA) || defined(AZTEC_C)
#define rewind(fp) fseek((fp),0L,SEEK_SET)	/* guarantee a return value */
#endif

#ifdef NULL
#undef NULL
#endif
#define NULL	((char *)0)

#if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
static	const char	SCCS_Id[] = "@(#)makedefs.c\t3.1\t93/05/15";
#endif

#ifdef MICRO
# undef	exit
extern void FDECL(exit, (int));
#endif

#define WRMODE  "w+"
#define RDMODE  "r"
/* the quest.dat file is binary, while everything else is text... */
#if (defined(MICRO) && !defined(AMIGA)) || defined(THINK_C)
# define WRBMODE "w+b"
#else
# define WRBMODE "w+"
#endif

#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifndef SEEK_END
# define SEEK_END 2
#endif

	/* names of files to be generated */
#define DATE_FILE	"date.h"
#define MONST_FILE	"pm.h"
#define ONAME_FILE	"onames.h"
#define OPTIONS_FILE	"options"
#define ORACLE_FILE	"oracles"
#define DATA_FILE	"data"
#define RUMOR_FILE	"rumors"
#define DGN_I_FILE	"dungeon.def"
#define DGN_O_FILE	"dungeon.pdf"
#define MON_STR_C	"monstr.c"
#define QTXT_I_FILE	"quest.txt"
#define QTXT_O_FILE	"quest.dat"
#define VIS_TAB_H	"vis_tab.h"
#define VIS_TAB_C	"vis_tab.c"
	/* locations for those files */
#ifdef AMIGA
# define INCLUDE_TEMPLATE	"Incl:t.%s"
# define SOURCE_TEMPLATE	"NHS:%s"
# define DATA_TEMPLATE		"Dat:%s"
#else
# ifdef MAC
#   define INCLUDE_TEMPLATE	":include:%s"
#   define SOURCE_TEMPLATE	":src:%s"
#   define DATA_TEMPLATE	":dat:%s"
# else /* MAC */
#   define INCLUDE_TEMPLATE	"../include/%s"
#   define SOURCE_TEMPLATE	"../src/%s"
#   define DATA_TEMPLATE	"../dat/%s"
# endif /* MAC */
#endif	/* AMIGA */

static const char
    *Dont_Edit_Code =
	"/* This source file is generated by 'makedefs'.  Do not edit. */\n",
    *Dont_Edit_Data =
	"#\tThis data file is generated by 'makedefs'.  Do not edit. \n";

static struct {
	long	incarnation;
	long	features;
} version;

/* definitions used for vision tables */
#define TEST_WIDTH  COLNO
#define TEST_HEIGHT ROWNO
#define BLOCK_WIDTH (TEST_WIDTH + 10)
#define BLOCK_HEIGHT TEST_HEIGHT	/* don't need extra spaces */
#define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT)
#define MAX_COL (BLOCK_WIDTH + TEST_WIDTH)
/* Use this as an out-of-bound value in the close table.  */
#define CLOSE_OFF_TABLE_STRING "99,"	/* for the close table */
#define FAR_OFF_TABLE_STRING "0xff,"	/* for the far table */

#define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
#ifdef VISION_TABLES
static char xclear[MAX_ROW][MAX_COL];
#endif
/*-end of vision defs-*/

static char	in_line[256], filename[30];

#ifdef MACsansMPWTOOL
void FDECL(macstart, (void));
int FDECL(main, (void));
#else
int FDECL(main, (int, char **));
#endif
int FDECL(do_makedefs, (int, char **));
void NDECL(do_objs);
void NDECL(do_data);
void NDECL(do_dungeon);
void NDECL(do_date);
void NDECL(do_options);
void NDECL(do_monstr);
void NDECL(do_permonst);
void NDECL(do_questtxt);
void NDECL(do_rumors);
void NDECL(do_oracles);
void NDECL(do_vision);

extern void NDECL(monst_init);		/* monst.c */
extern void NDECL(objects_init);	/* objects.c */

static void NDECL(make_version);
static char *FDECL(xcrypt, (const char *));
static int FDECL(check_control, (char *));
static char *FDECL(without_control, (char *));
static boolean FDECL(d_filter, (char *));
static boolean FDECL(h_filter, (char *));
static boolean FDECL(ranged_attk,(struct permonst*));
static int FDECL(mstrength,(struct permonst *));

#ifdef MULDGN
static boolean FDECL(qt_comment, (char *));
static boolean FDECL(qt_control, (char *));
static int FDECL(get_hdr, (CHAR_P));
static boolean FDECL(known_id, (CHAR_P));
static boolean FDECL(new_id, (CHAR_P));
static boolean FDECL(known_msg, (CHAR_P, char *));
static void FDECL(new_msg, (char *));
static void FDECL(do_qt_control, (char *));
static void FDECL(do_qt_text, (char *));
static void NDECL(adjust_qt_hdrs);
static void NDECL(put_qt_hdrs);
#endif

#ifdef VISION_TABLES
static void NDECL(H_close_gen);
static void NDECL(H_far_gen);
static void NDECL(C_close_gen);
static void NDECL(C_far_gen);
static int FDECL(clear_path, (int,int,int,int));
#endif

static char *FDECL(tmpdup, (const char *));
static char *FDECL(limit, (char *,int));

/* input, output, tmp */

static FILE *ifp, *ofp, *tfp;

#ifdef MACsansMPWTOOL
char mac_opt;

void
macstart()
{
	static char buf[100];
	static char *ptr = NULL;

again :
	if (!ptr || !*ptr) {
		Fprintf(stderr, "Options: otdemvpqrhz\n");
		buf[0] = 0;
		fgets(buf, 100, stdin);
		ptr = buf;
	}

	do {
		mac_opt = *ptr++;
	} while (mac_opt && isspace(mac_opt));

	if (!mac_opt) {
		Fprintf(stderr, "Makedefs done.\n");
		exit(0);
	}
}
#endif /* MAC */


int
#ifdef MACsansMPWTOOL
main(void)
{
	int argc;
	char **argv;
#else /* ! MAC */
main(argc, argv)
int	argc;
char	*argv[];
{
#endif /* MAC */
	/* Note:  these initializers don't do anything except guarantee that
		we're linked properly.
	*/
	monst_init();
	objects_init();

	/* construct the current version number */
	make_version();

#ifdef MACsansMPWTOOL
	while (1) {
		macstart();
		do_makedefs(argc, argv);
	}
#else
	if (do_makedefs(argc, argv))
		exit(1);
#endif
#ifndef VMS
	return 0;
#else
	return 1;       /* vms success */
#endif /*VMS*/
}

int
do_makedefs(arrgc, arrgv)
int	arrgc;
char	*arrgv[];
{
#ifdef MACsansMPWTOOL
	if (1) {
		Fprintf(stderr, "makedefs -%c\n", mac_opt);
		switch (mac_opt) {
#else /* !MAC */
	if (arrgc == 2) {
	    char *option = arrgv[1];
	    switch (option[1]) {
#endif /* MAC */
		case 'o':
		case 'O':	do_objs();
				break;
		case 't':			/* this may go away... */
		case 'T':	Fprintf(stderr,	"`-t' option is obsolete.\n");
				break;
		case 'd':
		case 'D':	do_data();
				break;
		case 'e':
		case 'E':	do_dungeon();
				break;
		case 'm':
		case 'M':	do_monstr();
				break;
		case 'v':
		case 'V':	do_date();
				do_options();
				break;
		case 'p':
		case 'P':	do_permonst();
				break;
		case 'q':
		case 'Q':	do_questtxt();
				break;
		case 'r':
		case 'R':	do_rumors();
				break;
		case 'h':
		case 'H':	do_oracles();
				break;
		case 'z':
		case 'Z':	do_vision();
				break;

		default:
				Fprintf(stderr,	"Unknown option '%c'.\n",
#ifdef MACsansMPWTOOL
					mac_opt
#else /* MAC */
					option[1]
#endif /* MAC */
					);
				(void) fflush(stderr);
				return(1);
	    }
	    return 0;
	} else {
	    Fprintf(stderr, "Bad arg count (%d).\n", arrgc-1);
	    (void) fflush(stderr);
	    return 1;
	}
}


/* trivial text encryption routine which can't be broken with `tr' */
static
char *xcrypt(str)
const char *str;
{				/* duplicated in src/hacklib.c */
	static char buf[BUFSZ];
	register const char *p;
	register char *q;
	register int bitmask;

	for (bitmask = 1, p = str, q = buf; *p; q++) {
		*q = *p++;
		if (*q & (32|64)) *q ^= bitmask;
		if ((bitmask <<= 1) >= 32) bitmask = 1;
	}
	*q = '\0';
	return buf;
}

void
do_rumors()
{
	char	infile[30];
	long	true_rumor_size;

	Sprintf(filename, DATA_TEMPLATE, RUMOR_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}
	Fprintf(ofp,Dont_Edit_Data);

	Strcat(strcpy(infile, filename), ".tru");
	if (!(ifp = fopen(infile, RDMODE))) {
		perror(infile);
		Fclose(ofp);
		Unlink(filename);	/* kill empty output file */
		exit(1);
	}

	/* get size of true rumors file */
#ifndef VMS
	(void) fseek(ifp, 0L, SEEK_END);
	true_rumor_size = ftell(ifp);
#else
	/* seek+tell is only valid for stream format files; since rumors.%%%
	   might be in record format, count the actual data bytes instead.
	 */
	true_rumor_size = 0;
	while (fgets(in_line,sizeof(in_line),ifp) != NULL)
		true_rumor_size += strlen(in_line);	/* includes newline */
#endif /* VMS */
	Fprintf(ofp,"%06lx\n", true_rumor_size);
	(void) fseek(ifp, 0L, SEEK_SET);

	/* copy true rumors */
	while(fgets(in_line,sizeof(in_line),ifp) != NULL)
		(void) fputs(xcrypt(in_line), ofp);

	Fclose(ifp);
	Strcat(strcpy(infile, filename), ".fal");
	if (!(ifp = fopen(infile, RDMODE))) {
		perror(infile);
		Fclose(ofp);
		Unlink(filename);	/* kill incomplete output file */
		exit(1);
	}

	/* copy false rumors */
	while(fgets(in_line,sizeof(in_line),ifp) != NULL)
		(void) fputs(xcrypt(in_line), ofp);

	Fclose(ifp);
	Fclose(ofp);
	return;
}

static void
make_version()
{
	/*
	 * integer version number
	 */
	version.incarnation = ((long)VERSION_MAJOR << 24) |
				((long)VERSION_MINOR << 16) |
				((long)PATCHLEVEL << 8) |
				((long)EDITLEVEL);
	/*
	 * encoded feature list
	 * Note:  if any of these magic numbers are changed or reassigned,
	 * EDITLEVEL in patchlevel.h should be incremented at the same time.
	 * The actual values have no special meaning, and the category
	 * groupings are just for convenience.
	 */
	version.features = 0
		/* levels and/or topology (0..4) */
#ifdef MULDGN
			| (1L <<  0)
#endif
#ifdef REINCARNATION
			| (1L <<  1)
#endif
#ifdef SINKS
			| (1L <<  2)
#endif
		/* monsters (5..9) */
#ifdef ARMY
			| (1L <<  5)
#endif
#ifdef KOPS
			| (1L <<  6)
#endif
#ifdef MAIL
			| (1L <<  7)
#endif
		/* objects (10..14) */
#ifdef TOURIST
			| (1L << 10)
#endif
#ifdef TUTTI_FRUTTI
			| (1L << 11)
#endif
#ifdef WALKIES
			| (1L << 12)
#endif
		/* flag bits and/or other global variables (15..26) */
#ifdef MUSE
			| (1L << 15)
#endif
#ifdef POLYSELF
			| (1L << 16)
#endif
#ifdef TEXTCOLOR
			| (1L << 17)
#endif
#ifdef INSURANCE
			| (1L << 18)
#endif
#ifdef ELBERETH
			| (1L << 19)
#endif
#ifdef EXP_ON_BOTL
			| (1L << 20)
#endif
#ifdef SCORE_ON_BOTL
			| (1L << 21)
#endif
		/* data format [COMPRESS excluded] (27..31) */
#ifdef ZEROCOMP
			| (1L << 27)
#endif
#ifdef RLECOMP
			| (1L << 28)
#endif
			;
	return;
}

void
do_date()
{
	long	clocktim;
	char	cbuf[30], *c;

	Sprintf(filename, INCLUDE_TEMPLATE, DATE_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}
	Fprintf(ofp,"/*\tSCCS Id: @(#)date.h\t3.1\t92/04/07 */\n\n");
	Fprintf(ofp,Dont_Edit_Code);

#ifdef KR1ED
	(void) time(&clocktim);
	Strcpy(cbuf, ctime(&clocktim));
#else
	(void) time((time_t *)&clocktim);
	Strcpy(cbuf, ctime((time_t *)&clocktim));
#endif
	for(c = cbuf; *c != '\n'; c++);	*c = 0; /* strip off the '\n' */
	Fprintf(ofp,"#define BUILD_DATE \"%s\"\n", cbuf);
	Fprintf(ofp,"#define BUILD_TIME (%ldL)\n", clocktim);
	Fprintf(ofp,"\n");
	Fprintf(ofp,"#define VERSION_NUMBER 0x%08lxL\n", version.incarnation);
	Fprintf(ofp,"#define VERSION_FEATURES 0x%08lxL\n", version.features);
	Fprintf(ofp,"\n");
#ifdef AMIGA
	{
	struct tm *tm = localtime((time_t *) &clocktim);
	Fprintf(ofp,"#ifdef AMIGA\n");
	Fprintf(ofp,"const char amiga_version_string[] = ");
	Fprintf(ofp,"\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\";\n",
		VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
		tm->tm_mday, tm->tm_mon+1, tm->tm_year);
	Fprintf(ofp,"#endif\n");
	}
#endif
	Fclose(ofp);
	return;
}

static const char *build_opts[] = {
#ifdef AMIGA_WBENCH
		"Amiga WorkBench support",
#endif
#ifdef ANSI_DEFAULT
		"ANSI default terminal",
#endif
#ifdef ARMY
		"armies",
#endif
#ifdef TEXTCOLOR
		"color",
#endif
#ifdef COM_COMPL
		"command line completion",
#endif
#ifdef COMPRESS
		"data file compression",
#endif
#ifdef WIZARD
		"debug mode",
#endif
#ifdef ELBERETH
		"Elbereth",
#endif
#ifdef EXP_ON_BOTL
		"experience points on status line",
#endif
#ifdef EXPLORE_MODE
		"explore mode",
#endif
#ifdef MFLOPPY
		"floppy drive support",
#endif
#ifdef TUTTI_FRUTTI
		"fruit names",
#endif
#ifdef INSURANCE
		"insurance against game crashes",
#endif
#ifdef KOPS
		"Keystone Kops",
#endif
#ifdef WALKIES
		"leashes",
#endif
#ifdef LOGFILE
		"log file",
#endif
#ifdef MAIL
		"mail daemon",
#endif
#ifdef MUSE
		"monster item use",
#endif
#ifdef GNUDOS
		"MSDOS protected mode",
#endif
#ifdef NEWS
		"news file",
#endif
#ifdef OVERLAY
		"overlays",
#endif
#ifdef MULDGN
		"quest dungeon",
#endif
#ifdef REDO
		"redo command",
#endif
#ifdef REINCARNATION
		"rogue level",
#endif
#ifdef SCORE_ON_BOTL
		"score on status line",
#endif
#ifdef CLIPPING
		"screen clipping",
#endif
#ifdef NO_TERMS
# ifdef MAC
		"screen control via mactty",
# endif
# ifdef SCREEN_BIOS
		"screen control via BIOS",
# endif
# ifdef SCREEN_DJGPPFAST
		"screen control via DJGPP fast",
# endif
# ifdef WIN32CON
		"screen control via WIN32 console I/O",
# endif
#endif
#ifdef SEDUCE
		"seduction",
#endif
#ifdef POLYSELF
		"self-polymorph",
#endif
#ifdef SHELL
		"shell command",
#endif
#ifdef SINKS
		"sinks",
#endif
#ifdef SOUNDS
		"sounds",
#endif
#ifdef SUSPEND
		"suspend command",
#endif
#ifdef TERMINFO
		"terminal info library",
#else
# if defined(TERMLIB) || (!defined(MICRO) && defined(TTY_GRAPHICS))
		"terminal capability library",
# endif
#endif
#ifdef TOURIST
		"tourists",
#endif
#ifdef VISION_TABLES
		"vision tables",
#endif
#ifdef WALLIFIED_MAZE
		"walled mazes",
#endif
#ifdef ZEROCOMP
		"zero-compressed save files",
#endif
		"basic NetHack features"
	};

static const char *window_opts[] = {
#ifdef TTY_GRAPHICS
		"traditional tty-based graphics",
#endif
#ifdef X11_GRAPHICS
		"X11",
#endif
#ifdef MAC
		"Mac",
#endif
#ifdef AMIGA_INTUITION
		"Amiga Intuition",
#endif
		NULL
	};

void
do_options()
{
	register int i, length;
	register const char *str, *indent = "    ";

	Sprintf(filename, DATA_TEMPLATE, OPTIONS_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}

	Fprintf(ofp,
#ifdef BETA
		"\n    NetHack version %d.%d.%d [beta]\n",
#else
		"\n    NetHack version %d.%d.%d\n",
#endif
		VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);

	Fprintf(ofp,"\nOptions compiled into this edition:\n");

	length = COLNO + 1;	/* force 1st item onto new line */
	for (i = 0; i < SIZE(build_opts); i++) {
	    str = build_opts[i];
	    if (length + strlen(str) > COLNO - 5)
		Fprintf(ofp,"\n%s", indent),  length = strlen(indent);
	    else
		Fprintf(ofp," "),  length++;
	    Fprintf(ofp,"%s", str),  length += strlen(str);
	    Fprintf(ofp,(i < SIZE(build_opts) - 1) ? "," : "."),  length++;
	}

	Fprintf(ofp,"\n\nSupported windowing systems:\n");

	length = COLNO + 1;	/* force 1st item onto new line */
	for (i = 0; i < SIZE(window_opts) - 1; i++) {
	    str = window_opts[i];
	    if (length + strlen(str) > COLNO - 5)
		Fprintf(ofp,"\n%s", indent),  length = strlen(indent);
	    else
		Fprintf(ofp," "),  length++;
	    Fprintf(ofp,"%s", str),  length += strlen(str);
	    Fprintf(ofp, ","),  length++;
	}
	Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS);
	Fprintf(ofp,"\n\n");

	Fclose(ofp);
	return;
}

/* routine to decide whether to discard something from data.base */
static boolean
d_filter(line)
    char *line;
{
    if (*line == '#') return TRUE;	/* ignore comment lines */
#ifndef ARMY
  { static int ignore_army = 0;

    switch (ignore_army) {
      case 0:   if (!strcmp(line, "*soldier")) ignore_army = 1;
		break;		/* 0 => not in army related data */
      case 1:   if (*line <= ' ') ignore_army = 2;
		break;		/* 1 => in army name list */
      case 2:   if (*line > ' ')  ignore_army = 0;
		break;		/* 2 => in army descriptive text */
    }
    if (ignore_army) return TRUE;
  }
#endif
    return FALSE;
}

   /*
    *
	New format (v3.1) of 'data' file which allows much faster lookups [pr]
"do not edit"		first record is a comment line
01234567		hexadecimal formatted offset to text area
name-a			first name of interest
123,4			offset to name's text, and number of lines for it
name-b			next name of interest
name-c			multiple names which share same description also
456,7			share a single offset,count line
.			sentinel to mark end of names
789,0			dummy record containing offset,count of EOF
text-a			4 lines of descriptive text for name-a
text-a			at file position 0x01234567L + 123L
text-a
text-a
text-b/text-c		7 lines of text for names-b and -c
text-b/text-c		at fseek(0x01234567L + 456L)
...
    *
    */

void
do_data()
{
	char	infile[30], tempfile[30];
	boolean ok;
	long	txt_offset;
	unsigned entry_cnt, line_cnt;

	Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
	Sprintf(filename, DATA_TEMPLATE, DATA_FILE);
	Strcat(strcpy(infile, filename),
#ifdef SHORT_FILENAMES
		".bas"
#else
		".base"
#endif
		);
	if (!(ifp = fopen(infile, RDMODE))) {		/* data.base */
		perror(infile);
		exit(1);
	}
	if (!(ofp = fopen(filename, WRMODE))) {		/* data */
		perror(filename);
		Fclose(ifp);
		exit(1);
	}
	if (!(tfp = fopen(tempfile, WRMODE))) {		/* database.tmp */
		perror(tempfile);
		Fclose(ifp);
		Fclose(ofp);
		Unlink(filename);
		exit(1);
	}

	/* output a dummy header record; we'll rewind and overwrite it later */
	Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);

	entry_cnt = line_cnt = 0;
	/* read through the input file and split it into two sections */
	while (fgets(in_line, sizeof in_line, ifp)) {
	    if (d_filter(in_line)) continue;
	    if (*in_line > ' ') {	/* got an entry name */
		/* first finish previous entry */
		if (line_cnt)  Fprintf(ofp, "%d\n", line_cnt),  line_cnt = 0;
		/* output the entry name */
		(void) fputs(in_line, ofp);
		entry_cnt++;		/* update number of entries */
	    } else if (entry_cnt) {	/* got some descriptive text */
		/* update previous entry with current text offset */
		if (!line_cnt)  Fprintf(ofp, "%ld,", ftell(tfp));
		/* save the text line in the scratch file */
		(void) fputs(in_line, tfp);
		line_cnt++;		/* update line counter */
	    }
	}
	/* output an end marker and then record the current position */
	if (line_cnt)  Fprintf(ofp, "%d\n", line_cnt);
	Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
	txt_offset = ftell(ofp);
	Fclose(ifp);		/* all done with original input file */

	/* reprocess the scratch file; 1st format an error msg, just in case */
	Sprintf(in_line, "rewind of \"%s\"", tempfile);
	if (rewind(tfp) != 0)  goto dead_data;
	/* copy all lines of text from the scratch file into the output file */
	while (fgets(in_line, sizeof in_line, tfp))
	    (void) fputs(in_line, ofp);

	/* finished with scratch file */
	Fclose(tfp);
	Unlink(tempfile);	/* remove it */

	/* update the first record of the output file; prepare error msg 1st */
	Sprintf(in_line, "rewind of \"%s\"", filename);
	ok = (rewind(ofp) == 0);
	if (ok) {
	   Sprintf(in_line, "header rewrite of \"%s\"", filename);
	   ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, txt_offset) >= 0);
	}
	if (!ok) {
dead_data:  perror(in_line);	/* report the problem */
	    /* close and kill the aborted output file, then give up */
	    Fclose(ofp);
	    Unlink(filename);
	    exit(1);
	}

	/* all done */
	Fclose(ofp);

	return;
}

/* routine to decide whether to discard something from oracles.txt */
static boolean
h_filter(line)
    char *line;
{
    static boolean skip = FALSE;
    char tag[sizeof in_line];

    SpinCursor(3);

    if (*line == '#') return TRUE;	/* ignore comment lines */
    if (sscanf(line, "----- %s", tag) == 1) {
	skip = FALSE;
#ifndef SINKS
	if (!strcmp(tag, "SINKS")) skip = TRUE;
#endif
#ifndef ELBERETH
	if (!strcmp(tag, "ELBERETH")) skip = TRUE;
#endif
    } else if (skip && !strncmp(line, "-----", 5))
	skip = FALSE;
    return skip;
}

static const char *special_oracle[] = {
	"\"...it is rather disconcerting to be confronted with the",
	"following theorem from [Baker, Gill, and Solovay, 1975].",
	"",
	"Theorem 7.18  There exist recursive languages A and B such that",
	"  (1)  P(A) == NP(A), and",
	"  (2)  P(B) != NP(B)",
	"",
	"This provides impressive evidence that the techniques that are",
	"currently available will not suffice for proving that P != NP or          ",
	"that P == NP.\"  [Garey and Johnson, p. 185.]"
};

/*
   The oracle file consists of a "do not edit" comment, a decimal count N
   and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
   records, separated by "---" lines.  The first oracle is a special case.
   The input data contains just those multi-line records, separated by
   "-----" lines.
 */

void
do_oracles()
{
	char	infile[30], tempfile[30];
	boolean in_oracle, ok;
	long	txt_offset, offset, fpos;
	unsigned oracle_cnt;
	register int i;

	Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
	Sprintf(filename, DATA_TEMPLATE, ORACLE_FILE);
	Strcat(strcpy(infile, filename), ".txt");
	if (!(ifp = fopen(infile, RDMODE))) {
		perror(infile);
		exit(1);
	}
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		Fclose(ifp);
		exit(1);
	}
	if (!(tfp = fopen(tempfile, WRMODE))) {		/* oracles.tmp */
		perror(tempfile);
		Fclose(ifp);
		Fclose(ofp);
		Unlink(filename);
		exit(1);
	}

	/* output a dummy header record; we'll rewind and overwrite it later */
	Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);

	/* handle special oracle; it must come first */
	(void) fputs("---\n", tfp);
	Fprintf(ofp, "%05lx\n", ftell(tfp));  /* start pos of special oracle */
	for (i = 0; i < SIZE(special_oracle); i++) {
	    (void) fputs(xcrypt(special_oracle[i]), tfp);
	    (void) fputc('\n', tfp);
	}
	SpinCursor(3);

	oracle_cnt = 1;
	(void) fputs("---\n", tfp);
	Fprintf(ofp, "%05lx\n", ftell(tfp));	/* start pos of first oracle */
	in_oracle = FALSE;

	while (fgets(in_line, sizeof in_line, ifp)) {
	    SpinCursor(3);

	    if (h_filter(in_line)) continue;
	    if (!strncmp(in_line, "-----", 5)) {
		if (!in_oracle) continue;
		in_oracle = FALSE;
		oracle_cnt++;
		(void) fputs("---\n", tfp);
		Fprintf(ofp, "%05lx\n", ftell(tfp));
		/* start pos of this oracle */
	    } else {
		in_oracle = TRUE;
		(void) fputs(xcrypt(in_line), tfp);
	    }
	}

	if (in_oracle) {	/* need to terminate last oracle */
	    oracle_cnt++;
	    (void) fputs("---\n", tfp);
	    Fprintf(ofp, "%05lx\n", ftell(tfp));	/* eof position */
	}

	/* record the current position */
	txt_offset = ftell(ofp);
	Fclose(ifp);		/* all done with original input file */

	/* reprocess the scratch file; 1st format an error msg, just in case */
	Sprintf(in_line, "rewind of \"%s\"", tempfile);
	if (rewind(tfp) != 0)  goto dead_data;
	/* copy all lines of text from the scratch file into the output file */
	while (fgets(in_line, sizeof in_line, tfp))
	    (void) fputs(in_line, ofp);

	/* finished with scratch file */
	Fclose(tfp);
	Unlink(tempfile);	/* remove it */

	/* update the first record of the output file; prepare error msg 1st */
	Sprintf(in_line, "rewind of \"%s\"", filename);
	ok = (rewind(ofp) == 0);
	if (ok) {
	    Sprintf(in_line, "header rewrite of \"%s\"", filename);
	    ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, (int)oracle_cnt) >=0);
	}
	if (ok) {
	    Sprintf(in_line, "data rewrite of \"%s\"", filename);
	    for (i = 0; i <= oracle_cnt; i++) {
#ifndef VMS	/* alpha/vms v1.0; this fflush seems to confuse ftell */
		if (!(ok = (fflush(ofp) == 0))) break;
#endif
		if (!(ok = (fpos = ftell(ofp)) >= 0)) break;
		if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break;
		if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1))) break;
		if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break;
		if (!(ok = (fprintf(ofp, "%05lx\n", offset + txt_offset) >= 0)))
		    break;
	    }
	}
	if (!ok) {
dead_data:  perror(in_line);	/* report the problem */
	    /* close and kill the aborted output file, then give up */
	    Fclose(ofp);
	    Unlink(filename);
	    exit(1);
	}

	/* all done */
	Fclose(ofp);

	return;
}


static	struct deflist {

	const char	*defname;
	boolean	true_or_false;
} deflist[] = {
#ifdef REINCARNATION
	      {	"REINCARNATION", TRUE },
#else
	      {	"REINCARNATION", FALSE },
#endif
#ifdef MULDGN
	      {	"MULDGN", TRUE },
#else
	      {	"MULDGN", FALSE },
#endif
	      { 0, 0 } };

static int
check_control(s)
	char	*s;
{
	int	i;

	if(s[0] != '%') return(-1);

	for(i = 0; deflist[i].defname; i++)
	    if(!strncmp(deflist[i].defname, s+1, strlen(deflist[i].defname)))
		return(i);

	return(-1);
}

static char *
without_control(s)
	char *s;
{
	return(s + 1 + strlen(deflist[check_control(in_line)].defname));
}

void
do_dungeon()
{
	int rcnt = 0;

	Sprintf(filename, DATA_TEMPLATE, DGN_I_FILE);
	if (!(ifp = fopen(filename, RDMODE))) {
		perror(filename);
		exit(1);
	}
	Sprintf(filename, DATA_TEMPLATE, DGN_O_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}
	Fprintf(ofp,Dont_Edit_Data);

	while(fgets(in_line,sizeof(in_line),ifp) != NULL) {

	    SpinCursor(3);

	    rcnt++;
	    if(in_line[0] == '#') continue;	/* discard comments */
recheck:
	    if(in_line[0] == '%') {
		int i = check_control(in_line);
		if(i >= 0) {
		    if(!deflist[i].true_or_false)  {
			while(fgets(in_line,sizeof(in_line),ifp))
			    if(check_control(in_line) != i) goto recheck;
		    } else
			(void) fputs(without_control(in_line),ofp);
		} else {
		    Fprintf(stderr, "Unknown control option '%s' in file %s at line %d.\n",
			    in_line, DGN_I_FILE, rcnt);
		    exit(1);
		}
	    } else
		(void) fputs(in_line,ofp);
	}
	Fclose(ifp);
	Fclose(ofp);

	return;
}

static boolean
ranged_attk(ptr)	/* returns TRUE if monster can attack at range */
	register struct permonst *ptr;
{
	register int	i, j;
	register int atk_mask = (1<<AT_BREA) | (1<<AT_SPIT) | (1<<AT_GAZE);

	for(i = 0; i < NATTK; i++) {
	    if((j=ptr->mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1<<j)))
		return TRUE;
	}

	return(FALSE);
}

/* This routine is designed to return an integer value which represents
 * an approximation of monster strength.  It uses a similar method of
 * determination as "experience()" to arrive at the strength.
 */
static int
mstrength(ptr)
struct permonst *ptr;
{
	int	i, tmp2, n, tmp = ptr->mlevel;

	if(tmp > 49)		/* special fixed hp monster */
	    tmp = 2*(tmp - 6) / 4;

/*	For creation in groups */
	n = (!!(ptr->geno & G_SGROUP));
	n += (!!(ptr->geno & G_LGROUP)) << 1;
	n += (!!(ptr->geno & G_VLGROUP)) << 2;

/*	For ranged attacks */
	if (ranged_attk(ptr)) n++;

/*	For higher ac values */
	n += (ptr->ac < 4);
	n += (ptr->ac < 0);

/*	For very fast monsters */
	n += (ptr->mmove >= 18);

/*	For each attack and "special" attack */
	for(i = 0; i < NATTK; i++) {

	    tmp2 = ptr->mattk[i].aatyp;
	    n += (tmp2 > 0);
	    n += (tmp2 == AT_MAGC);
	    n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
	}

/*	For each "special" damage type */
	for(i = 0; i < NATTK; i++) {

	    tmp2 = ptr->mattk[i].adtyp;
	    if((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
			|| (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO)
#ifdef POLYSELF
					|| (tmp2 == AD_WERE)
#endif
								) n += 2;
	    else if (strcmp(ptr->mname, "grid bug")) n += (tmp2 != AD_PHYS);
	    n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
	}
	/* tom's nasties */
	if (extra_nasty(ptr)) n += 5;
/*	Leprechauns are special cases.  They have many hit dice so they
	can hit and are hard to kill, but they don't really do much damage. */
	if (!strcmp(ptr->mname, "leprechaun")) n -= 2;

/*	Finally, adjust the monster level  0 <= n <= 24 (approx.) */
	if(n == 0) tmp--;
	else if(n >= 6) tmp += ( n / 2 );
	else tmp += ( n / 3 + 1);

	return((tmp >= 0) ? tmp : 0);
}

void
do_monstr()
{
    register struct permonst *ptr;
    register int i, j;

    /*
     * create the source file, "monstr.c"
     */
    Sprintf(filename, SOURCE_TEMPLATE, MON_STR_C);
    if (!(ofp = fopen(filename, WRMODE))) {
	perror(filename);
	exit(1);
    }
    Fprintf(ofp,Dont_Edit_Code);
    Fprintf(ofp,"#include \"config.h\"\n");
    Fprintf(ofp,"\nint monstr[] = {\n");
    for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) {

	SpinCursor(3);

	i = mstrength(ptr);
	Fprintf(ofp,"%2d,%c", i, (++j & 15) ? ' ' : '\n');
    }
    /* might want to insert a final 0 entry here instead of just newline */
    Fprintf(ofp,"%s};\n", (j & 15) ? "\n" : "");

    Fprintf(ofp,"\nvoid NDECL(monstr_init);\n");
    Fprintf(ofp,"\nvoid\n");
    Fprintf(ofp,"monstr_init()\n");
    Fprintf(ofp,"{\n");
    Fprintf(ofp,"    return;\n");
    Fprintf(ofp,"}\n");
    Fprintf(ofp,"\n/*monstr.c*/\n");

    Fclose(ofp);
    return;
}

void
do_permonst()
{
	int	i;
	char	*c, *nam;

	Sprintf(filename, INCLUDE_TEMPLATE, MONST_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}
	Fprintf(ofp,"/*\tSCCS Id: @(#)pm.h\t3.1\t92/01/04 */\n\n");
	Fprintf(ofp,Dont_Edit_Code);
	Fprintf(ofp,"#ifndef PM_H\n#define PM_H\n");

	for(i = 0; mons[i].mlet; i++) {

		SpinCursor(3);

		Fprintf(ofp,"\n#define\tPM_");
		if (mons[i].mlet == S_HUMAN &&
				!strncmp(mons[i].mname, "were", 4))
		    Fprintf(ofp, "HUMAN_");
		for (nam = c = tmpdup(mons[i].mname); *c; c++)
		    if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A');
		    else if (*c < 'A' || *c > 'Z') *c = '_';
		Fprintf(ofp,"%s\t%d", nam, i);
	}
	Fprintf(ofp,"\n\n#define\tNUMMONS\t%d\n", i);
	Fprintf(ofp,"\n#endif /* PM_H */\n");
	Fclose(ofp);
	return;
}

#ifdef MULDGN
/*	Start of Quest text file processing. */
#include "qtext.h"

static struct qthdr	qt_hdr;
static struct msghdr	msg_hdr[N_HDR];
static struct qtmsg	*curr_msg;

static int	qt_line;

static boolean	in_msg;
#define	NO_MSG	1	/* strlen of a null line returned by fgets() */

static boolean
qt_comment(s)

	char *s;
{
	if(s[0] == '#') return(TRUE);
	return(!in_msg  && strlen(s) == NO_MSG);
}

static boolean
qt_control(s)

	char *s;
{
	return(s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
}

static int
get_hdr(c)

	char c;
{
	int	i;

	for(i = 0; i < qt_hdr.n_hdr; i++)
	    if(c == qt_hdr.id[i]) return (++i);

	return(0);
}

static boolean
known_id(c)

	char c;
{
	return(get_hdr(c) > 0);
}

static boolean
new_id(c)

	char c;
{
	if(qt_hdr.n_hdr >= N_HDR) {

	    Fprintf(stderr, OUT_OF_HEADERS, qt_line);
	    return(FALSE);
	}

	qt_hdr.id[qt_hdr.n_hdr] = c;
	msg_hdr[qt_hdr.n_hdr].n_msg = 0;
	qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
	return(TRUE);
}

static boolean
known_msg(c, s)

	char c, *s;
{
	int i = get_hdr(c) - 1,
	    j, n = atoi(s);

	for(j = 0; j < msg_hdr[i].n_msg; j++)
	    if(msg_hdr[i].qt_msg[j].msgnum == n) return(TRUE);

	return(FALSE);
}


static void
new_msg(s)
char *s;
{
	struct	qtmsg	*qt_msg;
	int	i = get_hdr(s[4]) - 1;

	if(msg_hdr[i].n_msg >= N_MSG) {
		Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
	} else {
		qt_msg = &(msg_hdr[i].qt_msg[msg_hdr[i].n_msg++]);
		qt_msg->msgnum = atoi(s+5);
		qt_msg->delivery = s[2];
		qt_msg->offset = qt_msg->size = 0L;

		curr_msg = qt_msg;
	}
}

static void
do_qt_control(s)

	char *s;
{
	switch(s[1]) {

	    case 'C':	if(in_msg) {
			    Fprintf(stderr, CREC_IN_MSG, qt_line);
			    break;
			} else {
			    in_msg = TRUE;
			    if(!known_id(s[4]))
				if(!new_id(s[4])) break;
			    if(known_msg(s[4],&s[5]))
				Fprintf(stderr, DUP_MSG, qt_line);
			    else new_msg(s);
			}
			break;

	    case 'E':	if(!in_msg) {
			    Fprintf(stderr, END_NOT_IN_MSG, qt_line);
			    break;
			} else in_msg = FALSE;
			break;

	    default:	Fprintf(stderr, UNREC_CREC, qt_line);
			break;
	}
}

static void
do_qt_text(s)

	char *s;
{
	curr_msg->size += strlen(s);
}

static void
adjust_qt_hdrs() {

	int	i, j;
	long	count = 0L,
		hdr_offset = sizeof(int) +
			     (sizeof(char) + sizeof(long)) * qt_hdr.n_hdr;

	for(i = 0; i < qt_hdr.n_hdr; i++) {

	    qt_hdr.offset[i] = hdr_offset;
	    hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
	}

	for(i = 0; i < qt_hdr.n_hdr; i++)
	    for(j = 0; j < msg_hdr[i].n_msg; j++) {

		msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
		count += msg_hdr[i].qt_msg[j].size;
	    }
}

static void
put_qt_hdrs() {

	int	i;

	/*
	 *	The main header record.
	 */
#ifdef DEBUG
	Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
#endif
	(void) fwrite(&(qt_hdr.n_hdr), sizeof(int), 1, ofp);
	(void) fwrite(&(qt_hdr.id[0]), sizeof(char), qt_hdr.n_hdr, ofp);
	(void) fwrite(&(qt_hdr.offset[0]), sizeof(long), qt_hdr.n_hdr, ofp);
#ifdef DEBUG
	for(i = 0; i < qt_hdr.n_hdr; i++)
		Fprintf(stderr, "%c @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);

	Fprintf(stderr, "\n");
#endif

	/*
	 *	The individual class headers.
	 */
	for(i = 0; i < qt_hdr.n_hdr; i++) {

#ifdef DEBUG
	    Fprintf(stderr, "%ld: %c header info.\n", ftell(ofp),
		    qt_hdr.id[i]);
#endif
	    (void) fwrite(&(msg_hdr[i].n_msg), sizeof(int), 1, ofp);
	    (void) fwrite(&(msg_hdr[i].qt_msg[0]), sizeof(struct qtmsg),
		   msg_hdr[i].n_msg, ofp);
#ifdef DEBUG
	    { int j;
	      for(j = 0; j < msg_hdr[i].n_msg; j++)
		Fprintf(stderr, "msg %d @ %ld (%ld)\n",
			msg_hdr[i].qt_msg[j].msgnum,
			msg_hdr[i].qt_msg[j].offset,
			msg_hdr[i].qt_msg[j].size);
	    }
#endif
	}
}

void
do_questtxt()
{
	Sprintf(filename, DATA_TEMPLATE, QTXT_I_FILE);
	if(!(ifp = fopen(filename, RDMODE))) {
		perror(filename);
		exit(1);
	}

	Sprintf(filename, DATA_TEMPLATE, QTXT_O_FILE);
	if(!(ofp = fopen(filename, WRBMODE))) {
		perror(filename);
		Fclose(ifp);
		exit(1);
	}

	qt_hdr.n_hdr = 0;
	qt_line = 0;
	in_msg = FALSE;

	while(fgets(in_line, 80, ifp) != NULL) {

	    SpinCursor (3);

	    qt_line++;
	    if(qt_control(in_line)) do_qt_control(in_line);
	    else if(qt_comment(in_line)) continue;
	    else		    do_qt_text(in_line);
	}

	(void) rewind(ifp);
	in_msg = FALSE;
	adjust_qt_hdrs(); put_qt_hdrs();
	while(fgets(in_line, 80, ifp) != NULL) {

		if(qt_control(in_line)) {
		    in_msg = (in_line[1] == 'C');
		    continue;
		} else if(qt_comment(in_line)) continue;
#ifdef DEBUG
		Fprintf(stderr, "%ld: %s", ftell(stdout), in_line);
#endif
		(void) fputs(xcrypt(in_line), ofp);
	}
	Fclose(ifp);
	Fclose(ofp);
	return;
}
#else	/* not MULDGN */

void
do_questtxt()
{
	Fprintf(stderr, "makedefs: `-q' option ignored.\n");
	/* create an empty file to satisfy `make' */
	Sprintf(filename, DATA_TEMPLATE, QTXT_O_FILE);
	ofp = fopen(filename, WRBMODE);
	Fclose(ofp);
	return;
}

#endif /* MULDGN */

static	char	temp[32];

static char *
limit(name,pref)	/* limit a name to 30 characters length */
char	*name;
int	pref;
{
	(void) strncpy(temp, name, pref ? 26 : 30);
	temp[pref ? 26 : 30] = 0;
	return temp;
}

void
do_objs()
{
	int i, sum = 0;
	char *c, *objnam;
	int nspell = 0;
	int prefix = 0;
	char class = '\0';
	boolean	sumerr = FALSE;

	Sprintf(filename, INCLUDE_TEMPLATE, ONAME_FILE);
	if (!(ofp = fopen(filename, WRMODE))) {
		perror(filename);
		exit(1);
	}
	Fprintf(ofp,"/*\tSCCS Id: @(#)onames.h\t3.1\t92/11/01 */\n\n");
	Fprintf(ofp,Dont_Edit_Code);
	Fprintf(ofp,"#ifndef ONAMES_H\n#define ONAMES_H\n\n");

	for(i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
		SpinCursor(3);

		objects[i].oc_name_idx = objects[i].oc_descr_idx = i;	/* init */
		if (!(objnam = tmpdup(OBJ_NAME(objects[i])))) continue;

		/* make sure probabilities add up to 1000 */
		if(objects[i].oc_class != class) {
			if (sum && sum != 1000) {
			    Fprintf(stderr, "prob error for class %d (%d%%)",
				    class, sum);
			    (void) fflush(stderr);
			    sumerr = TRUE;
			}
			class = objects[i].oc_class;
			sum = 0;
		}

		for (c = objnam; *c; c++)
		    if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A');
		    else if (*c < 'A' || *c > 'Z') *c = '_';

		switch (class) {
		    case WAND_CLASS:
			Fprintf(ofp,"#define\tWAN_"); prefix = 1; break;
		    case RING_CLASS:
			Fprintf(ofp,"#define\tRIN_"); prefix = 1; break;
		    case POTION_CLASS:
			Fprintf(ofp,"#define\tPOT_"); prefix = 1; break;
		    case SPBOOK_CLASS:
			Fprintf(ofp,"#define\tSPE_"); prefix = 1; nspell++; break;
		    case SCROLL_CLASS:
			Fprintf(ofp,"#define\tSCR_"); prefix = 1; break;
		    case AMULET_CLASS:
			/* avoid trouble with stupid C preprocessors */
			Fprintf(ofp,"#define\t");
			if(objects[i].oc_material == PLASTIC) {
			    Fprintf(ofp,"FAKE_AMULET_OF_YENDOR\t%d\n", i);
			    prefix = -1;
			    break;
			}
			break;
		    case GEM_CLASS:
			/* avoid trouble with stupid C preprocessors */
			if(objects[i].oc_material == GLASS) {
			    Fprintf(ofp,"/* #define\t%s\t%d */\n",
							objnam, i);
			    prefix = -1;
			    break;
			}
		    default:
			Fprintf(ofp,"#define\t");
		}
		if (prefix >= 0)
			Fprintf(ofp,"%s\t%d\n", limit(objnam, prefix), i);
		prefix = 0;

		sum += objects[i].oc_prob;
	}

	/* check last set of probabilities */
	if (sum && sum != 1000) {
	    Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
	    (void) fflush(stderr);
	    sumerr = TRUE;
	}

	Fprintf(ofp,"#define\tLAST_GEM\t(JADE)\n");
	Fprintf(ofp,"#define\tMAXSPELL\t%d\n", nspell+1);
	Fprintf(ofp,"#define\tNROFOBJECTS\t%d\n", i-1);

	Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");

	for (i = 1; artifact_names[i]; i++) {
		SpinCursor(3);

		for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
		    if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A');
		    else if (*c < 'A' || *c > 'Z') *c = '_';

		if (!strncmp(objnam, "THE_", 4))
			objnam += 4;
#ifdef TOURIST
		/* fudge _platinum_ YENDORIAN EXPRESS CARD */
		if (!strncmp(objnam, "PLATINUM_", 9))
			objnam += 9;
#endif
		Fprintf(ofp,"#define\tART_%s\t%d\n", limit(objnam, 1), i);
	}

	Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i-1);
	Fprintf(ofp,"\n#endif /* ONAMES_H */\n");
	Fclose(ofp);
	if (sumerr) exit(1);
	return;
}

static char *
tmpdup(str)
const char *str;
{
	static char buf[128];

	if (!str) return (char *)0;
	(void)strncpy(buf, str, 127);
	return buf;
}


/*
 * macros used to control vision algorithms:
 *      VISION_TABLES => generate tables
 *      BRACES        => table elements should be enclosed in "{ }"
 */

void
do_vision()
{
#ifdef VISION_TABLES
    int i, j;

    /* Everything is clear.  xclear may be malloc'ed.
     * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
     */
    for (i = 0; i < MAX_ROW; i++)
	for (j = 0; j < MAX_COL; j++)
	    if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
		xclear[i][j] = '\000';
	    else
		xclear[i][j] = '\001';
#endif /* VISION_TABLES */

    SpinCursor(3);

    /*
     * create the include file, "vis_tab.h"
     */
    Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
    if (!(ofp = fopen(filename, WRMODE))) {
	perror(filename);
	exit(1);
    }
    Fprintf(ofp,Dont_Edit_Code);
    Fprintf(ofp,"#ifdef VISION_TABLES\n");
#ifdef VISION_TABLES
    H_close_gen();
    H_far_gen();
#endif /* VISION_TABLES */
    Fprintf(ofp,"\n#endif /* VISION_TABLES */\n");
    Fclose(ofp);

    SpinCursor(3);

    /*
     * create the source file, "vis_tab.c"
     */
    Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C);
    if (!(ofp = fopen(filename, WRMODE))) {
	perror(filename);
	Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H);
	Unlink(filename);
	exit(1);
    }
    Fprintf(ofp,Dont_Edit_Code);
    Fprintf(ofp,"#include \"config.h\"\n");
    Fprintf(ofp,"#ifdef VISION_TABLES\n");
    Fprintf(ofp,"#include \"vis_tab.h\"\n");

    SpinCursor(3);

#ifdef VISION_TABLES
    C_close_gen();
    C_far_gen();
    Fprintf(ofp,"\nvoid vis_tab_init() { return; }\n");
#endif /* VISION_TABLES */

    SpinCursor(3);

    Fprintf(ofp,"\n#endif /* VISION_TABLES */\n");
    Fprintf(ofp,"\n/*vis_tab.c*/\n");

    Fclose(ofp);
    return;
}

#ifdef VISION_TABLES

/*--------------  vision tables  --------------*\
 *
 *  Generate the close and far tables.  This is done by setting up a
 *  fake dungeon and moving our source to different positions relative
 *  to a block and finding the first/last visible position.  The fake
 *  dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
 *  by BLOCK_WIDTH) is blocked.  Then we move the source around relative
 *  to the corner of the block.  For each new position of the source
 *  we check positions on rows "kittycorner" from the source.  We check
 *  positions until they are either in sight or out of sight (depends on
 *  which table we are generating).  The picture below shows the setup
 *  for the generation of the close table.  The generation of the far
 *  table would switch the quadrants of the '@' and the "Check rows
 *  here".
 *
 *
 *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
 *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
 *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
 *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
 *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
 *  ...............................
 *  ...............................
 *  .........@.....................
 *  ...............................
 *
 *      Table generation figure (close_table).  The 'X's are blocked points.
 *      The 'B' is a special blocked point.  The '@' is the source.  The ','s
 *      are the target area.  The '.' are just open areas.
 *
 *
 *  Example usage of close_table[][][].
 *
 *  The table is as follows:
 *
 *      dy = |row of '@' - row of 'B'|  - 1
 *      dx = |col of '@' - col of 'B'|
 *
 *  The first indices are the deltas from the source '@' and the block 'B'.
 *  You must check for the value inside the abs value bars being zero.  If
 *  so then the block is on the same row and you don't need to do a table
 *  lookup.  The last value:
 *
 *      dcy = |row of block - row to be checked|
 *
 *  Is the value of the first visible spot on the check row from the
 *  block column.  So
 *
 *  first visible col = close_table[dy][dx][dcy] + col of 'B'
 *
\*--------------  vision tables  --------------*/

static void
H_close_gen()
{
    Fprintf(ofp,"\n/* Close */\n");
    Fprintf(ofp,"#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
	    TEST_HEIGHT-1);
    Fprintf(ofp,"#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
	    TEST_WIDTH);
    Fprintf(ofp,"#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
	    TEST_HEIGHT);
    Fprintf(ofp,"typedef struct {\n");
    Fprintf(ofp,"    unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
    Fprintf(ofp,"} close2d;\n");
    Fprintf(ofp,"extern close2d close_table[CLOSE_MAX_SB_DY];\n");
    return;
}

static void
H_far_gen()
{
    Fprintf(ofp,"\n/* Far */\n");
    Fprintf(ofp,"#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
	    TEST_HEIGHT);
    Fprintf(ofp,"#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
	    TEST_WIDTH-1);
    Fprintf(ofp,"#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
	    TEST_HEIGHT-1);
    Fprintf(ofp,"typedef struct {\n");
    Fprintf(ofp,"    unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
    Fprintf(ofp,"} far2d;\n");
    Fprintf(ofp,"extern far2d far_table[FAR_MAX_SB_DY];\n");
    return;
}

# ifdef BRACES
#  define L_BRACE "{"
#  define R_BRACE "},"
# else
#  define L_BRACE ""
#  define R_BRACE ""
# endif /* BRACES */

static void
C_close_gen()
{
    int i,dx,dy;
    int src_row, src_col;	/* source */
    int block_row, block_col;	/* block */
    int this_row;
    int no_more;

    block_row = BLOCK_HEIGHT-1;
    block_col = BLOCK_WIDTH-1;

    Fprintf(ofp,"\n#ifndef FAR_TABLE_ONLY\n");
    Fprintf(ofp,"\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
#ifndef no_vision_progress
    Fprintf(stderr,"\nclose:");
#endif

    for (dy = 1; dy < TEST_HEIGHT; dy++) {
	src_row = block_row + dy;
	Fprintf(ofp,"/* DY = %2d (- 1)*/\n  {\n",dy);
#ifndef no_vision_progress
	Fprintf(stderr," %2d",dy),  (void)fflush(stderr);
#endif
	for (dx = 0; dx < TEST_WIDTH; dx++) {
	    src_col = block_col - dx;
	    Fprintf(ofp,"  /*%2d*/  %s",dx, L_BRACE);

	    no_more = 0;
	    for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
		if (no_more) {
		    Fprintf(ofp,CLOSE_OFF_TABLE_STRING);
		    continue;
		}

		SpinCursor(3);

		/* Find the first column that we can see. */
		for (i = block_col+1; i < MAX_COL; i++) {

		    if (clear_path(src_row,src_col,block_row-this_row,i))
			break;
		}

		if (i == MAX_COL) no_more = 1;
		Fprintf(ofp,"%2d,",i-block_col);
	    }
	    Fprintf(ofp,"%s\n", R_BRACE);
	}
	Fprintf(ofp,"  },\n");
    }

    Fprintf(ofp,"}; /* close_table[] */\n");		/* closing brace for table */
    Fprintf(ofp,"#endif /* !FAR_TABLE_ONLY */\n");
#ifndef no_vision_progress
    Fprintf(stderr,"\n");
#endif
    return;
}

static void
C_far_gen()
{
    int i,dx,dy;
    int src_row, src_col;	/* source */
    int block_row, block_col;	/* block */
    int this_row;

    block_row = BLOCK_HEIGHT-1;
    block_col = BLOCK_WIDTH-1;

    Fprintf(ofp,"\n#ifndef CLOSE_TABLE_ONLY\n");
    Fprintf(ofp,"\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
#ifndef no_vision_progress
    Fprintf(stderr,"\n_far_:");
#endif

    for (dy = 0; dy < TEST_HEIGHT; dy++) {
	src_row = block_row - dy;
	Fprintf(ofp,"/* DY = %2d */\n  {\n",dy);
#ifndef no_vision_progress
	Fprintf(stderr," %2d",dy),  (void)fflush(stderr);
#endif
	for (dx = 1; dx < TEST_WIDTH; dx++) {
	    src_col = block_col + dx;
	    Fprintf(ofp,"  /*%2d(-1)*/ %s",dx, L_BRACE);

	    for (this_row = block_row+1; this_row < block_row+TEST_HEIGHT;
								this_row++) {
		/* Find first col that we can see. */
		for (i = 0; i <= block_col; i++) {

		    SpinCursor(3);

		    if (clear_path(src_row,src_col,this_row,i)) break;
		}

		if (block_col-i < 0)
		    Fprintf(ofp,FAR_OFF_TABLE_STRING);
		else
		    Fprintf(ofp,"%2d,",block_col-i);
	    }
	    Fprintf(ofp,"%s\n", R_BRACE);
	}
	Fprintf(ofp,"  },\n");
    }

    Fprintf(ofp,"}; /* far_table[] */\n");	/* closing brace for table */
    Fprintf(ofp,"#endif /* !CLOSE_TABLE_ONLY */\n");
#ifndef no_vision_progress
    Fprintf(stderr,"\n");
#endif
    return;
}

/*
 *  "Draw" a line from the hero to the given location.  Stop of we hit a
 *  wall.
 *
 *  Generalized integer Bresenham's algorithm (fast line drawing) for
 *  all quadrants.  From _Procedural Elements for Computer Graphics_, by
 *  David F. Rogers.  McGraw-Hill, 1985.
 *
 *  I have tried a little bit of optimization by pulling compares out of
 *  the inner loops.
 *
 *  NOTE:  This had better *not* be called from a position on the
 *  same row as the hero.
 */
static int
clear_path(you_row,you_col,y2,x2)
    int you_row, you_col, y2, x2;
{
    int dx, dy, s1, s2;
    register int i, error, x, y, dxs, dys;

    x  = you_col;		y  = you_row;
    dx = abs(x2-you_col);	dy = abs(y2-you_row);
    s1 = sign(x2-you_col);	s2 = sign(y2-you_row);

    if (s1 == 0) {	/* same column */
	if (s2 == 1) {	/* below (larger y2 value) */
	    for (i = you_row+1; i < y2; i++)
		if (!xclear[i][you_col]) return 0;
	} else {	/* above (smaller y2 value) */
	    for (i = y2+1; i < you_row; i++)
		if (!xclear[i][you_col]) return 0;
	}
	return 1;
    }

    /*
     *  Lines at 0 and 90 degrees have been weeded out.
     */
    if (dy > dx) {
	error = dx; dx = dy; dy = error;	/* swap the values */
	dxs = dx << 1;		/* save the shifted values */
	dys = dy << 1;
	error = dys - dx;	/* NOTE: error is used as a temporary above */

	for (i = 0; i < dx; i++) {
	    if (!xclear[y][x]) return 0;	/* plot point */

	    while (error >= 0) {
		x += s1;
		error -= dxs;
	    }
	    y += s2;
	    error += dys;
	}
    } else {
	dxs = dx << 1;		/* save the shifted values */
	dys = dy << 1;
	error = dys - dx;

	for (i = 0; i < dx; i++) {
	    if (!xclear[y][x]) return 0;	/* plot point */

	    while (error >= 0) {
		y += s2;
		error -= dxs;
	    }
	    x += s1;
	    error += dys;
	}
    }
    return 1;
}
#endif /* VISION_TABLES */

/*makedefs.c*/
