bast/
bast/area/
bast/backup/
bast/clans/
bast/doc/MSP/
bast/doc/OLC11/
bast/doc/OLC11/doc/
bast/doc/OLC11/options/
bast/log/
bast/mobprogs/
bast/player/
/***************************************************************************
 *  SillyMUD Distribution V1.1b copyright (C) 1993 SillyMUD Developement.  *
 *                                                                         *
 *  Modifications by Rip in attempt to port to Merc 2.1                    *
 *  Modified by Turtle for Merc 2.2 (07-Nov-94)                            *
 *                                                                         *
 *  Got code from ftp.atinc.com:/pub/mud/outgoing/track.merc21.tar.gz      *
 *  It cointained 5 files: README, hash.c, hash.h, skills.c, and skills.h  *
 *  I combined the *.c and *.h files in this hunt.c, which should compile  *
 *  without any warnings or errors.                                        *
 *                                                                         *
 *  Modified by Zen    for Envy 2.2 (14-Oct-97)                            *
 *                                                                         *
 *  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"



extern int	             top_vnum_room;

/*
 * Structure types.
 */
typedef	struct	hash_link		HASH_LINK;
typedef	struct	hash_header		HASH_HEADER;
typedef	struct	hunting_data		HUNTING_DATA;
typedef	struct	room_q			ROOM_Q;
typedef	struct	nodes			NODES;

/*
 * Hunting parameters.
 * Make sure area_last has the last room vnum!
 */
#define IS_DIR			( get_room_index( q_head->room_nr )->exit[i] )
#define GO_OK			( !IS_SET( IS_DIR->exit_info, EX_CLOSED ) )
#define GO_OK_SMARTER		1
#define WORLD_SIZE		top_vnum_room
#define	HASH_KEY( ht, key )	((((unsigned int)(key))*17)%(ht)->table_size)


struct hash_link
{
    int			 key;
    HASH_LINK	 	*next;
    void		*data;
};

struct hash_header
{
    int			 rec_size;
    int			 table_size;
    int			 klistsize;
    int			 klistlen; 
    int			*keylist;    /* this is really lame, AMAZINGLY lame */
    HASH_LINK	       **buckets;
};

struct hunting_data
{
    char		*name;
    CHAR_DATA	       **victim;
};

struct room_q
{
    int			 room_nr;
    ROOM_Q		*next_q;
};

struct nodes
{
    int visited;
    int ancestor;
};



void init_hash_table( HASH_HEADER *ht, int rec_size, int table_size )
{
    ht->rec_size	= rec_size;
    ht->table_size	= table_size;
    ht->buckets		= (void *) calloc( sizeof( HASH_LINK ** ), table_size );
    ht->keylist		= (void *) malloc( sizeof( ht->keylist ) * ( ht->klistsize = 128 ) );
    ht->klistlen	= 0;
}

void init_world( ROOM_INDEX_DATA *room_db[] )
{
    /* zero out the world */
    memset( room_db, 0, sizeof( ROOM_INDEX_DATA * ) * WORLD_SIZE );
    return;
}

void destroy_hash_table( HASH_HEADER *ht, void ( *gman ) ( ) )
{
    HASH_LINK *scan;
    HASH_LINK *temp;
    int        i;

    for ( i = 0; i < ht->table_size; i++ )
	for ( scan = ht->buckets[i]; scan; )
	{
	    temp = scan->next;
	    ( *gman ) ( scan->data );
	    free( scan );
	    scan = temp;
	}

    free( ht->buckets );
    free( ht->keylist );
    return;
}

void _hash_enter( HASH_HEADER *ht, int key, void *data )
{
    /* precondition: there is no entry for <key> yet */
    HASH_LINK *temp;
    int        i;

    temp	= (HASH_LINK *) malloc( sizeof( HASH_LINK ) );
    temp->key	= key;
    temp->next	= ht->buckets[HASH_KEY( ht, key )];
    temp->data	= data;
    ht->buckets[HASH_KEY( ht, key )] = temp;
    if ( ht->klistlen >= ht->klistsize )
    {
	ht->keylist = (void *) realloc( ht->keylist, sizeof( *ht->keylist ) *
					 ( ht->klistsize *= 2 ) );
    }
    for ( i = ht->klistlen; i >= 0; i-- )
    {
	if ( ht->keylist[i - 1] < key )
	{
	    ht->keylist[i] = key;
	    break;
	}
	ht->keylist[i] = ht->keylist[i - 1];
    }
    ht->klistlen++;
    return;
}

ROOM_INDEX_DATA *room_find( ROOM_INDEX_DATA *room_db[], int key )
{
    return ( ( key < WORLD_SIZE && key > -1 ) ? room_db[key] : 0 );
}

void *hash_find( HASH_HEADER *ht, int key )
{
    HASH_LINK *scan;

    scan = ht->buckets[HASH_KEY( ht, key )];

    while ( scan && scan->key != key )
	scan = scan->next;

    return scan ? scan->data : NULL;
}

int room_enter( ROOM_INDEX_DATA *rb[], int key, ROOM_INDEX_DATA *rm )
{
    ROOM_INDEX_DATA *temp;

    temp = room_find( rb, key );
    if ( temp )
	return ( 0 );

    rb[key] = rm;
    return ( 1 );
}

int hash_enter( HASH_HEADER *ht, int key, void *data )
{
    void *temp;

    temp = hash_find( ht, key );
    if ( temp )
	return 0;

    _hash_enter( ht, key, data );
    return 1;
}

ROOM_INDEX_DATA *room_find_or_create( ROOM_INDEX_DATA *rb[], int key )
{
    ROOM_INDEX_DATA *rv;

    rv = room_find( rb, key );
    if ( rv )
	return rv;

    rv = (ROOM_INDEX_DATA *) malloc( sizeof( ROOM_INDEX_DATA ) );
    rb[key] = rv;

    return rv;
}

void *hash_find_or_create( HASH_HEADER *ht, int key )
{
    void *rval;

    rval = hash_find( ht, key );
    if ( rval )
	return rval;

    rval = (void *) malloc( ht->rec_size );
    _hash_enter( ht, key, rval );

    return rval;
}

int room_remove( ROOM_INDEX_DATA *rb[], int key )
{
    ROOM_INDEX_DATA *tmp;

    tmp = room_find( rb, key );
    if ( tmp )
    {
	rb[key] = 0;
	free( tmp );
    }
    return ( 0 );
}

void *hash_remove( HASH_HEADER *ht, int key )
{
    HASH_LINK **scan;

    scan = ht->buckets + HASH_KEY( ht, key );

    while ( *scan && ( *scan )->key != key )
	scan = &( *scan )->next;

    if ( *scan )
    {
	HASH_LINK *temp;
	HASH_LINK *aux;
	int        i;

	temp = ( *scan )->data;
	aux = *scan;
	*scan = aux->next;
	free( aux );

	for ( i = 0; i < ht->klistlen; i++ )
	    if ( ht->keylist[i] == key )
		break;

	if ( i < ht->klistlen )
	{
	    memmove( ht->keylist + i, ht->keylist + i + 1, ( ht->klistlen - i )
		   * sizeof( *ht->keylist ) );
	    ht->klistlen--;
	}

	return temp;
    }

    return NULL;
}

void room_iterate( ROOM_INDEX_DATA *rb[], void ( *func ) ( ), void *cdata )
{
    register int i;

    for ( i = 0; i < WORLD_SIZE; i++ )
    {
	ROOM_INDEX_DATA *temp;

	temp = room_find( rb, i );
	if ( temp )
	    ( *func ) ( i, temp, cdata );
    }
}

void hash_iterate( HASH_HEADER *ht, void ( *func ) ( ), void *cdata )
{
    int i;

    for ( i = 0; i < ht->klistlen; i++ )
    {
	void          *temp;
	register int   key;

	key = ht->keylist[i];
	temp = hash_find( ht, key );
	( *func ) ( key, temp, cdata );
	if ( ht->keylist[i] != key )	/* They must have deleted this room */
	    i--;		/* Hit this slot again. */
    }
}

int exit_ok( EXIT_DATA *pexit )
{
    ROOM_INDEX_DATA *to_room;

    if ( ( !pexit )
	|| !( to_room = pexit->to_room ) )
	return 0;

    return 1;
}

void donothing( )
{
    return;
}

int find_path( int in_room_vnum, int out_room_vnum, CHAR_DATA *ch,
	      int depth, int in_zone )
{
    ROOM_INDEX_DATA *herep;
    ROOM_INDEX_DATA *startp;
    EXIT_DATA       *exitp;
    ROOM_Q          *tmp_q;
    ROOM_Q          *q_head;
    ROOM_Q          *q_tail;
    HASH_HEADER      x_room;
    bool             thru_doors;
    long             i;
    int              tmp_room;
    int              count = 0;

    if ( depth < 0 )
    {
	thru_doors = TRUE;
	depth = -depth;
    }
    else
    {
	thru_doors = FALSE;
    }

    startp = get_room_index( in_room_vnum );

    init_hash_table( &x_room, sizeof( int ), 2048 );
    hash_enter( &x_room, in_room_vnum, (void *) - 1 );

    /* initialize queue */
    q_head = (ROOM_Q *) malloc( sizeof( ROOM_Q ) );
    q_tail = q_head;
    q_tail->room_nr = in_room_vnum;
    q_tail->next_q = 0;

    while ( q_head )
    {
	herep = get_room_index( q_head->room_nr );
	/* for each room test all directions */
	if ( herep->area == startp->area || !in_zone )
	{
	    /*
	     * only look in this zone...
	     * saves cpu time and makes world safer for players
	     */
	    for ( i = 0; i < MAX_DIR; i++ )
	    {
		exitp = herep->exit[i];
		if ( exit_ok( exitp ) && ( thru_doors ? GO_OK_SMARTER : GO_OK ) )
		{
		    /* next room */
		    tmp_room = herep->exit[i]->to_room->vnum;
		    if ( tmp_room != out_room_vnum )
		    {
			/* 
			 * shall we add room to queue ?
			 * count determines total breadth and depth
			 */
			if ( !hash_find( &x_room, tmp_room )
			    && ( count < depth ) )
			    /* && !IS_SET( RM_FLAGS(tmp_room), DEATH ) ) */
			{
			    count++;
			    /* mark room as visted and put on queue */

			    tmp_q = (ROOM_Q *) malloc( sizeof( ROOM_Q ) );
			    tmp_q->room_nr = tmp_room;
			    tmp_q->next_q = 0;
			    q_tail->next_q = tmp_q;
			    q_tail = tmp_q;

			    /* ancestor for first layer is the direction */
			    hash_enter( &x_room, tmp_room,
			    ( (long) hash_find( &x_room, q_head->room_nr ) == -1 )
				? (void *) ( i + 1 )
				: hash_find( &x_room, q_head->room_nr ) );
			}
		    }
		    else
		    {
			/* have reached our goal so free queue */
			tmp_room = q_head->room_nr;
			for ( ; q_head; q_head = tmp_q )
			{
			    tmp_q = q_head->next_q;
			    free( q_head );
			}
			/* return direction if first layer */
			if ( (long) hash_find( &x_room, tmp_room ) == -1 )
			{
			    if ( x_room.buckets )
			    {
				/* junk left over from a previous track */
				destroy_hash_table( &x_room, donothing );
			    }
			    return ( i );
			}
			else
			{
			    /* else return the ancestor */
			    long i;

			    i = (long) hash_find( &x_room, tmp_room );
			    if ( x_room.buckets )
			    {
				/* junk left over from a previous track */
				destroy_hash_table( &x_room, donothing );
			    }
			    return ( -1 + i );
			}
		    }
		}
	    }
	}

	/* free queue head and point to next entry */
	tmp_q = q_head->next_q;
	free( q_head );
	q_head = tmp_q;
    }

    /* couldn't find path */
    if ( x_room.buckets )
    {
	/* junk left over from a previous track */
	destroy_hash_table( &x_room, donothing );
    }
    return -1;
}



void found_prey( CHAR_DATA *ch, CHAR_DATA *victim )
{
     char buf		[ MAX_STRING_LENGTH ];
     char victname	[ MAX_STRING_LENGTH ];

     if ( !victim || victim->deleted )
     {
	bug( "Found_prey: null victim", 0 );
	return;
     }

     if ( !victim->in_room )
     {
        bug( "Found_prey: null victim->in_room", 0 );
        return;
     }

     sprintf( victname, IS_NPC( victim ) ? victim->short_descr : victim->name );

     if ( !can_see( ch, victim ) )
     {
        if ( number_percent( ) < 90 )
	  return;
	switch( number_bits( 2 ) )
 	{
	case 0: sprintf( buf, "Don't make me find you, %s!", victname );
		do_say( ch, buf );
	        break;
	case 1: act( "$n sniffs around the room for $N.", ch, NULL, victim, TO_NOTVICT );
		act( "You sniff around the room for $N.", ch, NULL, victim, TO_CHAR );
		act( "$n sniffs around the room for you.", ch, NULL, victim, TO_VICT );
		sprintf( buf, "I can smell your blood!" );
		do_say( ch, buf );
		break;
	case 2: sprintf( buf, "I'm going to tear %s apart!", victname );
		do_yell( ch, buf );
		break;
	case 3: do_say( ch, "Just wait until I find you...");
		break;
        }
	return;
     }

     if ( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
     {
	if ( number_percent( ) < 90 )
	  return;
	switch( number_bits( 2 ) )
	{
	case 0:	do_say( ch, "C'mon out, you coward!" );
		sprintf( buf, "%s is a bloody coward!", victname );
		do_yell( ch, buf );
		break;
	case 1: sprintf( buf, "Let's take this outside, %s", victname );
		do_say( ch, buf );
		break;
	case 2: sprintf( buf, "%s is a yellow-bellied wimp!", victname );
		do_yell( ch, buf );
		break;
	case 3: act( "$n takes a few swipes at $N.", ch, NULL, victim, TO_NOTVICT );
		act( "You try to take a few swipes $N.", ch, NULL, victim, TO_CHAR );
		act( "$n takes a few swipes at you.", ch, NULL, victim, TO_VICT );
		break;
	}
	return;
     }

     switch( number_bits( 2 ) )
     {
     case 0: sprintf( buf, "Your blood is mine, %s!", victname );
	     do_yell( ch, buf);
	     break;
     case 1: sprintf( buf, "Alas, we meet again, %s!", victname );
     	     do_say( ch, buf );
     	     break;
     case 2: sprintf( buf, "What do you want on your tombstone, %s?", victname );
     	     do_say( ch, buf );
     	     break;
     case 3: act( "$n lunges at $N from out of nowhere!", ch, NULL, victim, TO_NOTVICT );
	     act( "You lunge at $N catching $M off guard!", ch, NULL, victim, TO_CHAR );
	     act( "$n lunges at you from out of nowhere!", ch, NULL, victim, TO_VICT );
     }
     stop_hunting( ch );
     multi_hit( ch, victim, TYPE_UNDEFINED );
     return;
} 



void do_track( CHAR_DATA *ch, char *argument )
{
    char       buf [ MAX_STRING_LENGTH ];
    char       arg [ MAX_STRING_LENGTH ];
    CHAR_DATA *victim;
    int        direction;
    bool       fArea;

    if ( !IS_NPC( ch ) && !ch->pcdata->skl_lrn[gsn_track] )
    {
	send_to_char( "You do not know of this skill yet.\n\r", ch );
	return;
    }

    one_argument( argument, arg );

    if ( arg[0] == '\0' )
    {
	send_to_char( "Whom are you trying to track?\n\r", ch );
	return;
    }

    if ( ch->riding )
    {
        send_to_char( "You can't sniff a trail mounted.\n\r", ch );
        return;
    }

    /* only imps can hunt to different areas */
    fArea = ( get_trust( ch ) < L_DIR );

    if ( fArea )
	victim = get_char_area( ch, arg );
    else
	victim = get_char_world( ch, arg );

    if ( !victim )
    {
	send_to_char( "You can't find a trail of anyone like that.\n\r", ch );
	return;
    }

    if ( ch->in_room == victim->in_room )
    {
	act( "You're already in $N's room!", ch, NULL, victim, TO_CHAR );
	return;
    }

    /*
     * Deduct some movement.
     */
    if ( ch->move > 2 )
	ch->move -= 3;
    else
    {
	send_to_char( "You're too exhausted to hunt anyone!\n\r", ch );
	return;
    }

    act( "$n carefully sniffs the air.", ch, NULL, NULL, TO_ROOM );
    WAIT_STATE( ch, skills_table[gsn_track].beats );
    direction = find_path( ch->in_room->vnum, victim->in_room->vnum,
			  ch, -40000, fArea );

    if ( direction == -1 )
    {
	act( "You can't sense $N's trail from here.",
	    ch, NULL, victim, TO_CHAR );
	return;
    }

    if ( direction < 0 || direction >= MAX_DIR )
    {
	send_to_char( "Hmm... Something seems to be wrong.\n\r", ch );
	return;
    }

    /*
     * Give a random direction if the player misses the die roll.
     */
    if (   (  IS_NPC( ch ) && number_percent( ) > 75 )	/* NPC @ 25% */
	|| ( !IS_NPC( ch ) && number_percent( ) >	/* PC @ norm */
		ch->pcdata->skl_lrn[gsn_track] ) )
    {
	do
	{
	    direction = number_door( );
	}
	while (   !( ch->in_room->exit[direction] )
	       || !( ch->in_room->exit[direction]->to_room ) );
    }

    skill_practice( ch, gsn_track );

    /*
     * Display the results of the search.
     */
    sprintf( buf, "You sense $N's trail %s from here...",
	    dir_name[direction] );
    act( buf, ch, NULL, victim, TO_CHAR );

    return;
}



void hunt_victim( CHAR_DATA *ch )
{
    CHAR_DATA *tmp;
    int        dir;
    bool       found;

    if ( !ch || ch->deleted || !ch->hunting )
	return;

    /*
     * Make sure the victim still exists.
     */
    for ( found = FALSE, tmp = char_list; tmp && !found; tmp = tmp->next )
	if ( ch->hunting->who == tmp )
	    found = TRUE;

    if ( !found || !can_see( ch, ch->hunting->who ) )
    {
	do_say( ch, "Damn!  My prey is gone!!" );
	stop_hunting( ch );
	return;
    }

    if ( ch->in_room == ch->hunting->who->in_room )
    {
	if ( ch->fighting )
	    return;
	found_prey( ch, ch->hunting->who );
	return;
    }

    WAIT_STATE( ch, skills_table[gsn_track].beats );
    dir = find_path( ch->in_room->vnum, ch->hunting->who->in_room->vnum,
		    ch, -40000, TRUE );

    if ( dir < 0 || dir >= MAX_DIR )
    {
	act( "$n says 'Damn!  Lost $M!'", ch, NULL, ch->hunting->who, TO_ROOM );
	stop_hunting( ch );
	return;
    }

    /*
     * Give a random direction if the mob misses the die roll.
     */
    if ( number_percent( ) > 75 )	/* @ 25% */
    {
	do
	{
	    dir = number_door( );
	}
	while (   !( ch->in_room->exit[dir] )
	       || !( ch->in_room->exit[dir]->to_room ) );
    }


    if ( IS_SET( ch->in_room->exit[dir]->exit_info, EX_CLOSED ) )
    {
	do_open( ch, (char *) dir_name[dir] );
	return;
    }

    move_char( ch, dir );

    if ( !ch->hunting )
    {
	if ( !ch->in_room )
	{
	    char buf [ MAX_STRING_LENGTH ];

	    sprintf( buf, "Hunt_victim: no ch->in_room!  Mob #%d, name: %s.  Placing mob in limbo.",
		    ch->pIndexData->vnum, ch->name );
	    bug( buf, 0 );
	    char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) );
	    return;
	} 
	do_say( ch, "Damn!  Lost my prey!" );
	return;
    }
    if ( ch->in_room == ch->hunting->who->in_room )
	found_prey( ch, ch->hunting->who );

    return;
}