/* UNIX Moria Version 5.x source/main.c: initialization, main() function and
   main loop Copyright (c) 1989-91 James E. Wilson, Robert A. Koeneke

This software may be copied and distributed for educational, research, and not
   for profit purposes provided that this copyright and statement are included
   in all such copies. */


/* Original copyright message follows. */

/* Moria Version 4.8	COPYRIGHT (c) Robert Alan Koeneke		 */
/* */
/* I lovingly dedicate this game to hackers and adventurers	 */
/* everywhere...							 */
/* */
/* */
/* Designer and Programmer : Robert Alan Koeneke			 */
/* University of Oklahoma		 */
/* */
/* Assistant Programmers	 : Jimmey Wayne Todd			 */
/* University of Oklahoma		 */
/* */
/* Gary D. McAdoo			 */
/* University of Oklahoma		 */
/* */
/* UNIX Port		 : James E. Wilson			 */
/* UC Berkeley				 */
/* wilson@kithrup.com			 */
/* */
/* MSDOS Port		 : Don Kneller				 */
/* 1349 - 10th ave			 */
/* San Francisco, CA 94122		 */
/* kneller@cgl.ucsf.EDU			 */
/* ...ucbvax!ucsfcgl!kneller		 */
/* kneller@ucsf-cgl.BITNET		 */
/* */
/* BRUCE Moria		 : Christopher Stuart			 */
/* Monash University			 */
/* Melbourne, Victoria, AUSTRALIA	 */
/* cjs@moncsbruce.oz			 */
/* */
/* Amiga Port              : Corey Gehman                          */
/* Clemson University                    */
/* cg377170@eng.clemson.edu              */
/* */
/* Moria may be copied and modified freely as long as the above	 */
/* credits are retained.	No one who-so-ever may sell or market	 */
/* this software in any form without the expressed written consent */
/* of the author Robert Alan Koeneke.				 */
/* */

#ifdef __TURBOC__
#include	<io.h>
#include	<stdio.h>
#include	<stdlib.h>
#endif							/* __TURBOC__ */

#include "config.h"
#include "constant.h"
#include "types.h"
#include "externs.h"

#ifndef USG
#include <sys/types.h>
#include <sys/param.h>
#endif

#ifdef USG
#ifndef ATARIST_MWC
#include <string.h>
#else
#include "string.h"
#endif
#else
#include <strings.h>
#endif

#include <ctype.h>

#ifdef Pyramid
#include <sys/time.h>
#else
#include <time.h>
#endif

#ifndef VMS
#ifndef MAC
#ifndef GEMDOS
#ifndef AMIGA
long time();

#endif
#endif
char *getenv();

#endif
#endif

#ifndef MAC
#ifndef AMIGA
#ifdef USG
#if !defined(MSDOS) && !defined(ATARIST_TC)
unsigned short getuid(), getgid();

#endif
#else
#ifndef SECURE
#ifdef BSD4_3
uid_t getuid(), getgid();

#else							/* other BSD versions */
int getuid(), getgid();

#endif
#endif
#endif
#endif
#endif

#ifndef VMS
#ifndef MAC
#if defined(ultrix) || defined(USG)
void perror();

#endif
#endif
#endif

#ifndef VMS
#ifndef MAC
#ifdef USG
void exit();

#endif
#endif
#endif

/* #if defined(atarist) && defined(__GNUC__) long _stksize = 64*1024; #endif */

#ifdef ATARIST_MWC
long _stksize = 18000;			/* (SAJ) for MWC	 */

#endif

#ifdef __TURBOC__
unsigned _stklen = 0x3fff;		/* increase stack from 4K to 16K */

#endif

#if defined(LINT_ARGS)
static void char_inven_init(void);
static void init_m_level(void);
static void init_t_level(void);

#if (COST_ADJ != 100)
static void price_adjust(void);

#endif
#else
static void char_inven_init();
static void init_m_level();
static void init_t_level();

#if (COST_ADJ != 100)
static void price_adjust();

#endif
#endif

/* Initialize, restore, and get the ball rolling.	-RAK-	 */
#ifdef MAC
/* This is just a subroutine for the Mac version */
/* only options passed in are -orn */
/* save file name is never passed */
int 
moria_main(argc, argv)
	int argc;
	char *argv[];

#else
int 
main(argc, argv)
	int argc;
	char *argv[];

#endif
{
	int32u seed;
	int generate;
	int result;

#ifndef MAC
	char *p;

#endif
	int new_game = FALSE;
	int force_rogue_like = FALSE;
	int force_keys_to;

	/* default command set defined in config.h file */
	rogue_like_commands = ROGUE_LIKE;


	direct_more = FALSE;
	direct_more_time = 10;

#ifdef SECURE
	Authenticate();
#endif

#ifdef MSDOS
	msdos_init();				/* find out where everything is */
#endif

	/* call this routine to grab a file pointer to the highscore file */
	/* and prepare things to relinquish setuid privileges */
	init_scorefile();

#ifndef SECURE
#if !defined(MSDOS) && !defined(ATARIST_MWC) && !defined(MAC)
#if !defined(AMIGA) && !defined(ATARIST_TC)
#if !defined(atarist)
	if (0 != setuid(getuid())){
		perror("Can't set permissions correctly!  Setuid call failed.\n");
		exit(0);
	}
	if (0 != setgid(getgid())){
		perror("Can't set permissions correctly!  Setgid call failed.\n");
		exit(0);
	}
#endif
#endif
#endif
#endif

	/* use curses */
	init_curses();

#ifdef VMS
	/* Bizarre, but yes this really is needed to make moria work correctly
	   under VMS.  */
	restore_screen();
#endif

	/* catch those nasty signals */
	/* must come after init_curses as some of the signal handlers use curses */
	init_signals();

	seed = 0;					/* let wizard specify rng seed */
	/* check for user interface option */
	for (--argc, ++argv; argc > 0 && argv[0][0] == '-'; --argc, ++argv)
		switch (argv[0][1]) {
			case 'N':
			case 'n':
				new_game = TRUE;
				break;
			case 'O':
			case 'o':
				/* rogue_like_commands may be set in get_char(), so delay this
				   until after read savefile if any */
				force_rogue_like = TRUE;
				force_keys_to = FALSE;
				break;
			case 'R':
			case 'r':
				force_rogue_like = TRUE;
				force_keys_to = TRUE;
				break;
#ifndef MAC
			case 'S':
				display_scores(TRUE);
				exit_game();
			case 's':
				display_scores(FALSE);
				exit_game();
			case 'W':
			case 'w':
				to_be_wizard = TRUE;

				if (isdigit((int) argv[0][2]))
					seed = atoi(&argv[0][2]);
				break;
			default:
				(void) printf("Usage: moria [-norsw] [savefile]\n");
				exit_game();
#endif
		}

#ifndef MAC
	/* Check operating hours			 */
	/* If not wizard  No_Control_Y	       */
	read_times();
#endif

	/* Some necessary initializations		 */
	/* all made into constants or initialized in variables.c */

#if (COST_ADJ != 100)
	price_adjust();
#endif

	/* Grab a random seed from the clock		 */
	init_seeds(seed);

	/* Init monster and treasure levels for allocate */
	init_m_level();
	init_t_level();

	/* Init the store inventories			 */
	store_init();

#ifndef MAC
	/* On Mac, if -n is passed, no savefile is used */
	/* If -n is not passed, the calling routine will know savefile name, hence,
	   this code is not necessary */

	/* Auto-restart of saved file */
	if (argv[0] != CNIL)
		(void) strcpy(savefile, argv[0]);
	else if ((p = getenv("MORIA_SAV")) != CNIL)
		(void) strcpy(savefile, p);
	else if ((p = getenv("HOME")) != CNIL)
#if defined(ATARIST_MWC) || defined(ATARIST_TC)
		(void) sprintf(savefile, "%s\\%s", p, MORIA_SAV);
#else
#ifdef VMS
		(void) sprintf(savefile, "%s%s", p, MORIA_SAV);
#else
		(void) sprintf(savefile, "%s/%s", p, MORIA_SAV);
#endif
#endif
	else
		(void) strcpy(savefile, MORIA_SAV);
#endif

	/* This restoration of a saved character may get ONLY the monster memory.
	   In this case, get_char returns false. It may also resurrect a dead
	   character (if you are the wizard). In this case, it returns true, but
	   also sets the parameter "generate" to true, as it does not recover any
	   cave details. */

	result = FALSE;
#ifdef MAC
	if ((new_game == FALSE) && get_char(&generate))
#else
	if ((new_game == FALSE) && !access(savefile, 0) && get_char(&generate))
#endif
		result = TRUE;

	/* enter wizard mode before showing the character display, but must wait
	   until after get_char in case it was just a resurrection */
	if (to_be_wizard)
		if (!enter_wiz_mode())
			exit_game();

	if (result) {
		change_name();

		/* could be restoring a dead character after a signal or HANGUP */
		if (py.misc.chp < 0)
			death = TRUE;
	} else {					/* Create character	   */
		create_character();
#ifdef MAC
		birth_date = time((time_t *) 0);
#else
		birth_date = time((long *) 0);
#endif
		char_inven_init();
		py.flags.food = 7500;
		py.flags.food_digested = 2;
		if (class[py.misc.pclass].spell == MAGE) {		/* Magic realm   */
			clear_screen();		/* makes spell list easier to read */
			calc_spells(A_INT);
			calc_mana(A_INT);
		} else if (class[py.misc.pclass].spell == PRIEST) {		/* Clerical realm */
			calc_spells(A_WIS);
			clear_screen();		/* force out the 'learn prayer' message */
			calc_mana(A_WIS);
		}
		/* prevent ^c quit from entering score into scoreboard, and prevent
		   signal from creating panic save until this point, all info needed
		   for save file is now valid */
		character_generated = 1;
		generate = TRUE;
	}

	if (force_rogue_like)
		rogue_like_commands = force_keys_to;

	magic_init();

	/* Begin the game				 */
	clear_screen();
	prt_stat_block();
	if (generate)
		generate_cave();

	/* Loop till dead, or exit			 */
	while (!death) {
		dungeon();				/* Dungeon logic */

#ifndef MAC
		/* check for eof here, see inkey() in io.c */
		/* eof can occur if the process gets a HANGUP signal */
		if (eof_flag) {
			(void) strcpy(died_from, "(end of input: saved)");
			if (!save_char()){
				(void) strcpy(died_from, "unexpected eof");
			}
			/* should not reach here, by if we do, this guarantees exit */
			death = TRUE;
		}
#endif

		if (!death)
			generate_cave();	/* New level	 */
	}

	exit_game();				/* Character gets buried. */
	/* should never reach here, but just in case */
	return (0);
}

/* Init players with some belongings			-RAK-	 */
static void 
char_inven_init()
{
	register int i, j;
	inven_type inven_init;

	/* this is needed for bash to work right, it can't hurt anyway */
	for (i = 0; i < INVEN_ARRAY_SIZE; i++)
		invcopy(&inventory[i], OBJ_NOTHING);

	for (i = 0; i < 5; i++) {
		j = player_init[py.misc.pclass][i];
		invcopy(&inven_init, j);
		/* this makes it known2 and known1 */
		store_bought(&inven_init);
		/* must set this bit to display tohit/todam for stiletto */
		if (inven_init.tval == TV_SWORD)
			inven_init.ident |= ID_SHOW_HITDAM;
		(void) inven_carry(&inven_init);
	}

	/* wierd place for it, but why not? */
	for (i = 0; i < 32; i++)
		spell_order[i] = 99;
}


/* Initializes M_LEVEL array for use with PLACE_MONSTER	-RAK-	 */
static void 
init_m_level()
{
	register int i, k;

	for (i = 0; i <= MAX_MONS_LEVEL; i++)
		m_level[i] = 0;

	k = MAX_CREATURES - WIN_MON_TOT;
	for (i = 0; i < k; i++)
		m_level[c_list[i].level]++;

	for (i = 1; i <= MAX_MONS_LEVEL; i++)
#ifdef AMIGA					/* fix a stupid MANX Aztec C 5.0 bug again */
		m_level[i] = m_level[i] + m_level[i - 1];
#else
		m_level[i] += m_level[i - 1];
#endif
}


/* Initializes T_LEVEL array for use with PLACE_OBJECT	-RAK-	 */
static void 
init_t_level()
{
	register int i, l;
	int tmp[MAX_OBJ_LEVEL + 1];

	for (i = 0; i <= MAX_OBJ_LEVEL; i++)
		t_level[i] = 0;
	for (i = 0; i < MAX_DUNGEON_OBJ; i++)
		t_level[object_list[i].level]++;
	for (i = 1; i <= MAX_OBJ_LEVEL; i++)
#ifdef AMIGA					/* fix a stupid MANX Aztec C 5.0 bug again */
		t_level[i] = t_level[i] + t_level[i - 1];
#else
		t_level[i] += t_level[i - 1];
#endif

	/* now produce an array with object indexes sorted by level, by using the
	   info in t_level, this is an O(n) sort! */
	/* this is not a stable sort, but that does not matter */
	for (i = 0; i <= MAX_OBJ_LEVEL; i++)
		tmp[i] = 1;
	for (i = 0; i < MAX_DUNGEON_OBJ; i++) {
		l = object_list[i].level;
		sorted_objects[t_level[l] - tmp[l]] = i;
		tmp[l]++;
	}
}


#if (COST_ADJ != 100)
/* Adjust prices of objects				-RAK-	 */
static void 
price_adjust()
{
	register int i;

	/* round half-way cases up */
	for (i = 0; i < MAX_OBJECTS; i++)
		object_list[i].cost = ((object_list[i].cost * COST_ADJ) + 50) / 100;
}

#endif
