swr2.0/
swr2.0/area/
swr2.0/boards/
swr2.0/clans/
swr2.0/doc/
swr2.0/planets/
swr2.0/spacecraft/
#include <stdio.h>
#include <string.h>
#include "mud.h"

#define BFS_ERROR	   -1
#define BFS_ALREADY_THERE  -2
#define BFS_NO_PATH	   -3
#define BFS_MARK         BV01

#define TRACK_THROUGH_DOORS

bool mob_snipe( CHAR_DATA * ch, CHAR_DATA * victim );
ch_ret one_hit( CHAR_DATA * ch, CHAR_DATA * victim, int dt );

/* You can define or not define TRACK_THOUGH_DOORS, above, depending on
   whether or not you want track to find paths which lead through closed
   or hidden doors.
   */

struct bfs_queue_struct
{
  ROOM_INDEX_DATA *room;
  char dir;
  struct bfs_queue_struct *next;
};

static struct bfs_queue_struct *queue_head = NULL,
			       *queue_tail = NULL, *room_queue = NULL;

/* Utility macros */
#define MARK(room)	(SET_BIT(	(room)->room_flags, BFS_MARK) )
#define UNMARK(room)	(REMOVE_BIT(	(room)->room_flags, BFS_MARK) )
#define IS_MARKED(room)	(IS_SET(	(room)->room_flags, BFS_MARK) )

static ROOM_INDEX_DATA *toroom( ROOM_INDEX_DATA * room, short door )
{
  return ( get_exit( room, door )->to_room );
}

static bool valid_edge( ROOM_INDEX_DATA * room, short door )
{
  EXIT_DATA *pexit = get_exit( room, door );
  ROOM_INDEX_DATA *to_room = NULL;

  if( pexit && ( to_room = pexit->to_room ) != NULL
#ifndef TRACK_THROUGH_DOORS
      && !IS_SET( pexit->exit_info, EX_CLOSED )
#endif
      && !IS_MARKED( to_room ) )
    return TRUE;
  else
    return FALSE;
}

static void bfs_enqueue( ROOM_INDEX_DATA * room, char dir )
{
  struct bfs_queue_struct *curr = NULL;

  CREATE( curr, struct bfs_queue_struct, 1 );
  curr->room = room;
  curr->dir = dir;
  curr->next = NULL;

  if( queue_tail )
  {
    queue_tail->next = curr;
    queue_tail = curr;
  }
  else
    queue_head = queue_tail = curr;
}

static void bfs_dequeue( void )
{
  struct bfs_queue_struct *curr = queue_head;

  if( !( queue_head = queue_head->next ) )
    queue_tail = NULL;

  DISPOSE( curr );
}

static void bfs_clear_queue( void )
{
  while( queue_head )
    bfs_dequeue();
}

static void room_enqueue( ROOM_INDEX_DATA * room )
{
  struct bfs_queue_struct *curr = NULL;

  CREATE( curr, struct bfs_queue_struct, 1 );
  curr->room = room;
  curr->next = room_queue;

  room_queue = curr;
}

static void clean_room_queue( void )
{
  struct bfs_queue_struct *curr = NULL, *curr_next = NULL;

  for( curr = room_queue; curr; curr = curr_next )
  {
    UNMARK( curr->room );
    curr_next = curr->next;
    DISPOSE( curr );
  }

  room_queue = NULL;
}


static int find_first_step( ROOM_INDEX_DATA * src,
			    ROOM_INDEX_DATA * target, int maxdist )
{
  int curr_dir = 0, count = 0;

  if( !src || !target )
  {
    bug( "Illegal value passed to find_first_step (track.c)", 0 );
    return BFS_ERROR;
  }

  if( src == target )
    return BFS_ALREADY_THERE;

  if( src->area != target->area )
    return BFS_NO_PATH;

  room_enqueue( src );
  MARK( src );

  /* first, enqueue the first steps, saving which direction we're going. */
  for( curr_dir = 0; curr_dir < 10; curr_dir++ )
    if( valid_edge( src, curr_dir ) )
    {
      MARK( toroom( src, curr_dir ) );
      room_enqueue( toroom( src, curr_dir ) );
      bfs_enqueue( toroom( src, curr_dir ), curr_dir );
    }

  count = 0;
  while( queue_head )
  {
    if( ++count > maxdist )
    {
      bfs_clear_queue();
      clean_room_queue();
      return BFS_NO_PATH;
    }

    if( queue_head->room == target )
    {
      curr_dir = queue_head->dir;
      bfs_clear_queue();
      clean_room_queue();
      return curr_dir;
    }
    else
    {
      for( curr_dir = 0; curr_dir < 10; curr_dir++ )
	if( valid_edge( queue_head->room, curr_dir ) )
	{
	  MARK( toroom( queue_head->room, curr_dir ) );
	  room_enqueue( toroom( queue_head->room, curr_dir ) );
	  bfs_enqueue( toroom( queue_head->room, curr_dir ),
	      queue_head->dir );
	}
      bfs_dequeue();
    }
  }
  clean_room_queue();

  return BFS_NO_PATH;
}


void do_track( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *vict = NULL;
  char arg[MAX_INPUT_LENGTH];
  char buf[MAX_STRING_LENGTH];
  int dir = 0, maxdist = 0;

  if( !character_skill_level( ch, gsn_track ) )
  {
    send_to_char( "You do not know of this skill yet.\r\n", ch );
    return;
  }

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

  WAIT_STATE( ch, skill_table[gsn_track]->beats );

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

  maxdist = 100 + ch->top_level * 30;

  if( !IS_NPC( ch ) )
    maxdist = ( maxdist * character_skill_level( ch, gsn_track ) ) / 100;

  dir = find_first_step( ch->in_room, vict->in_room, maxdist );
  switch ( dir )
  {
    case BFS_ERROR:
      send_to_char( "Hmm... something seems to be wrong.\r\n", ch );
      break;
    case BFS_ALREADY_THERE:
      send_to_char( "You're already in the same room!\r\n", ch );
      break;
    case BFS_NO_PATH:
      snprintf( buf, MAX_STRING_LENGTH, "%s",
		"You can't sense a trail from here.\r\n" );
      send_to_char( buf, ch );
      learn_from_failure( ch, gsn_track );
      break;
    default:
      ch_printf( ch, "You sense a trail %s from here...\r\n", dir_name[dir] );
      learn_from_success( ch, gsn_track );
      break;
  }
}


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

  if( victim == NULL )
  {
    bug( "Found_prey: null victim", 0 );
    return;
  }

  if( ch == NULL )
  {
    bug( "Found_prey: null ch", 0 );
    return;
  }

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

  snprintf( victname, MAX_STRING_LENGTH, "%s",
	    IS_NPC( victim ) ? victim->short_descr : victim->name );

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

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

  act( AT_ACTION, "$n lunges at $N from out of nowhere!", ch, NULL, victim,
      TO_NOTVICT );
  act( AT_ACTION, "You lunge at $N catching $M off guard!", ch, NULL, victim,
      TO_CHAR );
  act( AT_ACTION, "$n lunges at you from out of nowhere!", ch, NULL, victim,
      TO_VICT );

  stop_hunting( ch );
  set_fighting( ch, victim );
  multi_hit( ch, victim, TYPE_UNDEFINED );
  return;
}

void hunt_victim( CHAR_DATA * ch )
{
  bool found = FALSE;
  CHAR_DATA *tmp = NULL;
  short ret = 0;

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

  /* make sure the char still exists */
  for( found = FALSE, tmp = first_char; tmp && !found; tmp = tmp->next )
    if( ch->hunting->who == tmp )
      found = TRUE;

  if( !found )
  {
    char buf[MAX_STRING_LENGTH];
    snprintf( buf, MAX_STRING_LENGTH, "%s", "Damn! My prey is gone!" );
    do_say( ch, buf );
    stop_hunting( ch );
    return;
  }

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

  /* hunting with snipe */
  {
    OBJ_DATA *wield;

    wield = get_eq_char( ch, WEAR_WIELD );
    if( wield != NULL && wield->value[3] == WEAPON_BLASTER )
    {
      if( mob_snipe( ch, ch->hunting->who ) == TRUE )
	return;
    }
    else if( !IS_SET( ch->act, ACT_DROID ) )
      do_hide( ch, STRLIT_EMPTY );
  }

  ret = find_first_step( ch->in_room, ch->hunting->who->in_room, 5000 );
  if( ret == BFS_NO_PATH )
  {
    EXIT_DATA *pexit = NULL;
    int attempt = 0;

    for( attempt = 0; attempt < 25; attempt++ )
    {
      ret = number_door();
      if( ( pexit = get_exit( ch->in_room, ret ) ) == NULL
	  || !pexit->to_room
	  || IS_SET( pexit->exit_info, EX_CLOSED )
	  || IS_SET( pexit->to_room->room_flags, ROOM_NO_MOB ) )
	continue;
    }
  }
  if( ret < 0 )
  {
    char buf[MAX_STRING_LENGTH];
    snprintf( buf, MAX_STRING_LENGTH, "%s", "Damn! Lost my prey!" );
    do_say( ch, buf );
    stop_hunting( ch );
    return;
  }
  else
  {
    move_char( ch, get_exit( ch->in_room, ret ), FALSE );
    if( char_died( ch ) )
      return;
    if( !ch->hunting )
    {
      char buf[MAX_STRING_LENGTH];

      if( !ch->in_room )
      {
	snprintf( buf, MAX_STRING_LENGTH,
	    "Hunt_victim: no ch->in_room! Mob #%ld, 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;
      }

      snprintf( buf, MAX_STRING_LENGTH, "%s", "Damn! Lost my prey!" );
      do_say( ch, buf );
      return;
    }
    if( ch->in_room == ch->hunting->who->in_room )
      found_prey( ch, ch->hunting->who );
    return;
  }
}

bool mob_snipe( CHAR_DATA * ch, CHAR_DATA * victim )
{
  short dir = 0, dist = 0;
  const short max_dist = 3;
  EXIT_DATA *pexit = NULL;
  ROOM_INDEX_DATA *was_in_room = NULL;
  ROOM_INDEX_DATA *to_room = NULL;
  char buf[MAX_STRING_LENGTH];
  bool pfound = FALSE;

  if( !ch->in_room || !victim->in_room )
    return FALSE;

  if( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
    return FALSE;

  for( dir = 0; dir <= 10; dir++ )
  {
    if( ( pexit = get_exit( ch->in_room, dir ) ) == NULL )
      continue;

    if( IS_SET( pexit->exit_info, EX_CLOSED ) )
      continue;

    was_in_room = ch->in_room;

    for( dist = 0; dist <= max_dist; dist++ )
    {
      if( IS_SET( pexit->exit_info, EX_CLOSED ) )
	break;

      if( !pexit->to_room )
	break;

      to_room = pexit->to_room;

      char_from_room( ch );
      char_to_room( ch, to_room );

      if( ch->in_room == victim->in_room )
      {
	pfound = TRUE;
	break;
      }

      if( ( pexit = get_exit( ch->in_room, dir ) ) == NULL )
	break;
    }

    char_from_room( ch );
    char_to_room( ch, was_in_room );

    if( !pfound )
    {
      char_from_room( ch );
      char_to_room( ch, was_in_room );
      continue;
    }

    if( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) )
      return FALSE;

    if( is_safe( ch, victim ) )
      return FALSE;

    if( IS_AFFECTED( ch, AFF_CHARM ) && ch->master == victim )
      return FALSE;

    if( ch->position == POS_FIGHTING )
      return FALSE;

    switch ( dir )
    {
      case DIR_NORTH:
      case DIR_EAST:
	dir += 2;
	break;
      case DIR_SOUTH:
      case DIR_WEST:
	dir -= 2;
	break;
      case DIR_UP:
      case DIR_NORTHWEST:
	dir += 1;
	break;
      case DIR_DOWN:
      case DIR_SOUTHEAST:
	dir -= 1;
	break;
      case DIR_NORTHEAST:
	dir += 3;
	break;
      case DIR_SOUTHWEST:
	dir -= 3;
	break;
    }

    char_from_room( ch );
    char_to_room( ch, victim->in_room );

    snprintf( buf, MAX_STRING_LENGTH,
	      "A blaster shot fires at you from the %s.", dir_name[dir] );
    act( AT_ACTION, buf, victim, NULL, ch, TO_CHAR );
    act( AT_ACTION, "You fire at $N.", ch, NULL, victim, TO_CHAR );
    snprintf( buf, MAX_STRING_LENGTH,
	      "A blaster shot fires at $N from the %s.", dir_name[dir] );
    act( AT_ACTION, buf, ch, NULL, victim, TO_NOTVICT );

    one_hit( ch, victim, TYPE_UNDEFINED );

    if( char_died( ch ) )
      return TRUE;

    stop_fighting( ch, TRUE );

    if( victim && !char_died( victim ) && victim->hit < 0 )
    {
      stop_hunting( ch );
      stop_hating( ch );
    }

    char_from_room( ch );
    char_to_room( ch, was_in_room );

    return TRUE;
  }

  return FALSE;
}