/
UltraEnvy/
UltraEnvy/area/classes/
UltraEnvy/backup/
UltraEnvy/log/
UltraEnvy/newdocs/
UltraEnvy/newdocs/MobProg2.1/
UltraEnvy/newdocs/OLC11/
UltraEnvy/newdocs/OLC11/area/
UltraEnvy/newdocs/OLC11/doc/
UltraEnvy/newdocs/OLC11/options/
UltraEnvy/player/
UltraEnvy/src/Contributions/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, David   *
 *  Love, Guilherme 'Willie' Arnold, and Mitchell Tse.                     *
 *                                                                         *
 *  EnvyMud 2.0 improvements copyright (C) 1995 by Michael Quan and        *
 *  Mitchell Tse.                                                          *
 *                                                                         *
 *  EnvyMud 2.2 improvements copyright (C) 1996, 1997 by Michael Quan.     *
 *                                                                         *
 *  In order to use any part of this Envy Diku Mud, you must comply with   *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  In particular, you may not remove either of these copyright notices.   *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

#if defined( macintosh )
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "merc.h"

#if !defined( macintosh )
extern  int     _filbuf	        args( (FILE *) );
#endif

#if !defined( ultrix ) && !defined( apollo )
#include <memory.h>
#endif



#if defined(KEY)
#undef KEY
#endif

#define KEY( literal, field, value )                \
                if ( !str_cmp( word, literal ) )    \
                {                                   \
                    field  = value;                 \
                    fMatch = TRUE;                  \
                    break;                          \
                }

#define SKEY( string, field )                       \
                if ( !str_cmp( word, string ) )     \
                {                                   \
                    free_string( field );           \
                  field = fread_string( fp, &stat); \
                    fMatch = TRUE;                  \
                    break;                          \
                }



/*
 * Globals.
 */

/* The social table. New socials contributed by Katrina and Binky */
SOC_INDEX_DATA *	soc_index_hash	[ MAX_WORD_HASH ];

/* Class table (see class files in area/classes) */
struct  class_type *    class_table     [ MAX_CLASS ];

/* Titles (see class files in area/classes) */ 
char *title_table 			[ MAX_CLASS ][ MAX_LEVEL+1 ][ 2 ];




/*
 * New code for loading classes from file.
 */
bool fread_class( char *filename )
{
    FILE   *fp;
    char   *word;
    char    buf  [ MAX_STRING_LENGTH ];
    bool    fMatch;
    int     stat;
    struct  class_type *class;
    int     number;
    int     tlev = 0;

    sprintf( buf, "%s%s", CLASS_DIR, filename );
    if ( !( fp = fopen( buf, "r" ) ) )
    {
        perror( buf );
        return FALSE;
    }

    word = feof( fp ) ? "End" : fread_word( fp, &stat );
    if ( !str_cmp( word, "Class" ) )
	number = fread_number( fp, &stat );
    else
	number = -1;	/* reports any error in case "Class N" doesn't exist */

    if ( number < 0 || number >= MAX_CLASS )
    {
	sprintf( buf, "Fread_class: bad class  '%s'.", filename );
	bug ( buf, 0 );
	return FALSE;
    }

    class = alloc_mem ( sizeof( CLASS_TYPE ) );

    class->filename = str_dup( filename );

    word = feof( fp ) ? "End" : fread_word( fp, &stat );
    if ( !str_cmp( word, "Name" ) )
	class->who_name = fread_string( fp, &stat );

    for ( ; ; )
    {
	word   = feof( fp ) ? "End" : fread_word( fp, &stat );
	fMatch = FALSE;

	switch ( UPPER( word[0] ) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'A':
            KEY( "AtrPrm", class->attr_prime,
            	fread_number( fp, &stat ) );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		fclose( fp );
		class_table[number] = class;
		return TRUE;
	    }
	    break;

	case 'G':
            KEY( "Guild", class->guild,
		fread_number( fp, &stat ) );
	    break;

	case 'H':
            KEY( "Hpmin", class->hp_min,
		fread_number( fp, &stat ) );
            KEY( "Hpmax", class->hp_max,
		fread_number( fp, &stat ) );
	    break;

	case 'M':
            KEY( "Mana", class->fMana, fread_number( fp, &stat ) );
	    break;

	case 'S':
            KEY( "SkillAdept", class->skill_adept,
		fread_number( fp, &stat ) );

	    if ( !str_cmp( word, "Skll" ) )
	    {
		int sn;
		int value;

		sn    = skill_lookup( fread_word( fp, &stat ) );
		value = fread_number( fp, &stat );
		if ( sn < 0 )
		    bug( "Fread_class: unknown skill.", 0 );
		else
		    skill_table[sn].skill_level[number] = value;
		fMatch = TRUE;
	    }

	    break;

	case 'T':
	    KEY( "Thac0", class->thac0_00,
		fread_number( fp, &stat ) );
	    KEY( "Thac47", class->thac0_47,
		fread_number( fp, &stat ) );

	    if ( !str_cmp( word, "Title" ) )
	    {
		if ( tlev <= MAX_LEVEL )
		{
                    title_table[number][tlev][0] = fread_string( fp, &stat );
                    title_table[number][tlev][1] = fread_string( fp, &stat );
		    ++tlev;
		}
		else
		    bug( "Fread_class: too many titles.", 0 );
		fMatch = TRUE;
	    }

	    break;

	case 'W':
	    KEY( "Weapon", class->weapon,
		fread_number( fp, &stat ) );
	    break;
	}

	if ( !fMatch )
	{
            sprintf( buf, "load_class_file: no match: %s", word );
            bug( buf, 0 );
	}
    }

    return FALSE;
}


/*
 * Load in all the class files.
 */ 
void load_classes()
{
    FILE *fpList;
    char *filename;
    char  fname		[ MAX_STRING_LENGTH ];
    char  classlist	[ MAX_STRING_LENGTH ];
    char  buf		[ MAX_STRING_LENGTH ];
    int   stat;
    int   sn;
    int   class;
    
    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if ( !skill_table[sn].name )
	    break;

	for ( class = 0; class < MAX_CLASS; class++ )
	    skill_table[sn].skill_level[class] = L_APP;	
    }

    sprintf( classlist, "%s%s", CLASS_DIR, CLASS_LIST );
    if ( !( fpList = fopen( classlist, "r" ) ) )
    {
        perror( classlist );
        exit( 1 );
    }

    for ( ; ; )
    {
        filename = feof( fpList ) ? "$" : fread_word( fpList, &stat );
	strcpy( fname, filename );
        if ( fname[0] == '$' )
          break;

        if ( !fread_class( fname ) )
        {
          sprintf( buf, "Cannot load class file: %s", fname );
          bug( buf, 0 );
        }
    }
    fclose( fpList );

    return;
}


void fwrite_class( int num )
{
    FILE                    *fp;
    const struct class_type *class = class_table[num];
    char                     buf	[ MAX_STRING_LENGTH ];
    char                     filename	[ MAX_INPUT_LENGTH ];
    int                      level;
    int                      sn;

    strcpy ( filename, class->filename );

    if ( !( fp = fopen( filename, "w" ) ) )
    {
        sprintf( buf, "Cannot open: %s for writing", filename );
        bug( buf, 0 );
        return;
    }

    fprintf( fp, "Name        %s~\n",        class->who_name	);
    fprintf( fp, "Class       %d\n",         num		);
    fprintf( fp, "Attrprime   %d\n",         class->attr_prime	);
    fprintf( fp, "Weapon      %d\n",         class->weapon	);
    fprintf( fp, "Guild       %d\n",         class->guild	);
    fprintf( fp, "Skilladept  %d\n",         class->skill_adept	);
    fprintf( fp, "Thac0       %d\n",         class->thac0_00	);
    fprintf( fp, "Thac47      %d\n",         class->thac0_47	);
    fprintf( fp, "Hpmin       %d\n",         class->hp_min	);
    fprintf( fp, "Hpmax       %d\n",         class->hp_max	);
    fprintf( fp, "Mana        %d\n",         class->fMana	);

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if ( !skill_table[sn].name )
	    break;

        if ( ( level = skill_table[sn].skill_level[num] ) < LEVEL_IMMORTAL )
          fprintf( fp, "Skll        '%s' %d\n", skill_table[sn].name, level );
    }

    for ( level = 0; level <= MAX_LEVEL; level++ )
        fprintf( fp, "Title\n%s~\n%s~\n",
                title_table[num][level][0], title_table[num][level][1] );
    fprintf( fp, "End\n" );
    fclose( fp );

    return;
}


void save_classes()
{
    int class;

    for ( class = 0; class < MAX_CLASS; class++ )
      fwrite_class( class );

    return;
}



/*
 * Add a social to the social index table                       - Thoric
 * Hashed and insert sorted (SMAUG code)
 */
void add_social( SOC_INDEX_DATA *social )
{
    SOC_INDEX_DATA *tmp;
    SOC_INDEX_DATA *prev;
    int             hash;
    int             x;

    if ( !social )
    {
	bug( "Add_social: NULL social", 0 );
	return;
    }

    if ( !social->name )
    {
	bug( "Add_social: NULL social->name", 0 );
	return;
    }

    if ( !social->char_no_arg )
    {
	bug( "Add_social: NULL social->char_no_arg", 0 );
	return;
    }

    /* make sure the name is all lowercase */
    for ( x = 0; social->name[x] != '\0'; x++ )
	social->name[x] = LOWER( social->name[x] );

    if ( social->name[0] < 'a' || social->name[0] > 'z' )
	hash = 0;
    else
	hash = ( social->name[0] - 'a' ) + 1;

    if ( !( prev = tmp = soc_index_hash[hash] ) )
    {
	social->next = soc_index_hash[hash];
	soc_index_hash[hash] = social;
	return;
    }

    for ( ; tmp; tmp = tmp->next )
    {
	if ( !( x = strcmp( social->name, tmp->name ) ) )
	{
	    bug( "Add_social: trying to add duplicate name to bucket %d", hash);
	    free_social( social );
	    return;
	}
	else
	if ( x < 0 )
	{
	    if ( tmp == soc_index_hash[hash] )
	    {
		social->next = soc_index_hash[hash];
		soc_index_hash[hash] = social;
		return;
	    }
	    prev->next = social;
	    social->next = tmp;
	    return;
	}
	prev = tmp;
    }

    /* add to end */
    prev->next = social;
    social->next = NULL;
    return;
}


/*
 * Save the social_table_tables to disk (cut & pasted from SMAUG - Zen)
 */
void save_socials()
{
    FILE           *fpout;
    SOC_INDEX_DATA *social;
    int             x;

    if ( !( fpout = fopen( SOCIAL_FILE, "w" ) ) )
    {
	bug( "Cannot open SOCIALS.TXT for writting", 0 );
	perror( SOCIAL_FILE );
	return;
    }

    for ( x = 0; x < 27; x++ )
    {
	for ( social = soc_index_hash[x]; social; social = social->next )
	{
	    if ( !social->name || social->name[0] == '\0' )
	    {
		bug( "Save_socials: blank social in hash bucket %d", x );
		continue;
	    }
	    fprintf( fpout, "#SOCIAL\n" );
	    fprintf( fpout, "Name        %s~\n",	social->name );
	    if ( social->char_no_arg )
		fprintf( fpout, "CharNoArg   %s~\n",	social->char_no_arg );
	    else
	        bug( "Save_socials: NULL char_no_arg in hash bucket %d", x );
	    if ( social->others_no_arg )
		fprintf( fpout, "OthersNoArg %s~\n",	social->others_no_arg );
	    if ( social->char_found )
		fprintf( fpout, "CharFound   %s~\n",	social->char_found );
	    if ( social->others_found )
		fprintf( fpout, "OthersFound %s~\n",	social->others_found );
	    if ( social->vict_found )
		fprintf( fpout, "VictFound   %s~\n",	social->vict_found );
	    if ( social->char_auto )
		fprintf( fpout, "CharAuto    %s~\n",	social->char_auto );
	    if ( social->others_auto )
		fprintf( fpout, "OthersAuto  %s~\n",	social->others_auto );
	    fprintf( fpout, "End\n\n" );
	}
    }

    fprintf( fpout, "#END\n" );
    fclose( fpout );
    return;
}


/*
 * Clear a new social.
 */
void clear_social( SOC_INDEX_DATA *soc )
{
    static SOC_INDEX_DATA soc_zero;

    *soc			= soc_zero;
    soc->name			= NULL;
    soc->char_no_arg		= NULL;
    soc->others_no_arg		= NULL;
    soc->char_found		= NULL;
    soc->others_found		= NULL;
    soc->vict_found		= NULL;
    soc->char_auto		= NULL;
    soc->others_auto		= NULL;
    return;
}


/*
 * Take a social data from the free list and clean it out.
 */
SOC_INDEX_DATA *new_social()
{
    SOC_INDEX_DATA *soc;

    soc		= alloc_perm( sizeof( SOC_INDEX_DATA ) );

    clear_social( soc );

    return soc;
}


/*
 * Remove a social from it's hash index                         - Thoric
 * (SMAUG code)
 */
void unlink_social( SOC_INDEX_DATA *social )
{
    SOC_INDEX_DATA *tmp;
    SOC_INDEX_DATA *tmp_next;
    int             hash;

    if ( !social )
    {
        bug( "Unlink_social: NULL social", 0 );
        return;
    }

    if ( social->name[0] < 'a' || social->name[0] > 'z' )
        hash = 0;
    else
        hash = (social->name[0] - 'a') + 1;

    if ( social == ( tmp = soc_index_hash[hash] ) )
    {
        soc_index_hash[hash] = tmp->next;
        return;
    }

    for ( ; tmp; tmp = tmp_next )
    {
        tmp_next = tmp->next;
        if ( social == tmp_next )
        {
            tmp->next = tmp_next->next;
            return;
        }
    }

    return;
}

/*
 * Free a social structure
 */
void free_social( SOC_INDEX_DATA *social )
{
    free_string( social->name );
    free_string( social->char_no_arg );
    free_string( social->others_no_arg );
    free_string( social->char_found );
    free_string( social->others_found );
    free_string( social->vict_found );
    free_string( social->char_auto );
    free_string( social->others_auto );

    free_mem( social, sizeof( SOC_INDEX_DATA ) );
    return;
}


void fread_social( FILE *fp )
{
    char           *word;
    SOC_INDEX_DATA *social;
    char            buf [ MAX_STRING_LENGTH ];
    bool            fMatch;
    int             stat;

    social = new_social();

    for ( ; ; )
    {
	word   = feof( fp ) ? "End" : fread_word( fp, &stat );
	fMatch = FALSE;

	switch ( UPPER( word[0] ) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'C':
	    SKEY( "CharNoArg", social->char_no_arg );
	    SKEY( "CharFound", social->char_found );
	    SKEY( "CharAuto", social->char_auto );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		if ( !social->name )
		{
		    bug( "Fread_social: Name not found", 0 );
		    free_social( social );
		    return;
		}
		if ( !social->char_no_arg )
		{
		    bug( "Fread_social: CharNoArg not found", 0 );
		    free_social( social );
		    return;
		}
		add_social( social );
		return;
	    }
	    break;

	case 'N':
	    SKEY( "Name", social->name );
	    break;

	case 'O':
	    SKEY( "OthersNoArg", social->others_no_arg );
	    SKEY( "OthersFound", social->others_found );
	    SKEY( "OthersAuto",	social->others_auto );
	    break;

	case 'V':
	    SKEY( "VictFound", social->vict_found );
	    break;
	}
	
	if ( !fMatch )
	{
            sprintf( buf, "Fread_social: no match: %s", word );
	    bug( buf, 0 );
	}
    }

    return;
}


void load_socials()
{
    FILE *fp;
    int   stat;

    if ( !( fp = fopen( SOCIAL_FILE, "r" ) ) )
    {
	bug( "Cannot open SOCIALS.TXT", 0 );
	exit( 0 );
    }

    for ( ; ; )
    {
	char letter;
	char *word;

	letter = fread_letter( fp );

	if ( letter == '*' )
	{
	    fread_to_eol( fp );
	    continue;
	}

	if ( letter != '#' )
	{
	    bug( "Load_socials: # not found.", 0 );
	    break;
	}

	word = fread_word( fp, &stat );
	if ( !str_cmp( word, "SOCIAL" ) )
	{
	    fread_social( fp );
	    continue;
	}
	else
	if ( !str_cmp( word, "END" ) )
	    break;
	else
	{
	    bug( "Load_socials: bad section.", 0 );
	    continue;
	}
    }
    
    fclose( fp );
    return;
}