LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/classes/
LOP/color/
LOP/councils/
LOP/deity/
LOP/races/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008 by: the LoP team.                                *
 *---------------------------------------------------------------------------*
 *                           Fishing Support                                 *
 *****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "h/mud.h"

#define FNAMES_FILE  SYSTEM_DIR "fnames.dat"
typedef struct fish_data FISH_DATA;
typedef struct fname_data FNAME_DATA;

struct fname_data
{
   FNAME_DATA *next, *prev;
   char *name;
   short minweight;
   short maxweight;
};

FNAME_DATA *first_fname, *last_fname;

struct fish_data
{
   FISH_DATA *next, *prev;
   OBJ_DATA *rod;
};

FISH_DATA *first_fish, *last_fish;

void remove_fname( FNAME_DATA *fname )
{
   if( !fname )
      return;
   UNLINK( fname, first_fname, last_fname, next, prev );
}

void free_fname( FNAME_DATA *fname )
{
   if( !fname )
      return;
   STRFREE( fname->name );
   DISPOSE( fname );
}

void add_fname( FNAME_DATA *fname )
{
   FNAME_DATA *tname;
   int match;

   if( !fname )
      return;
   if( !fname->name )
   {
      free_fname( fname );
      return;
   }
   for( tname = first_fname; tname; tname = tname->next )
   {
      if( !str_cmp( tname->name, fname->name ) )
      {
         free_fname( fname );
         return;
      }
      else if( ( match = strcmp( fname->name, tname->name ) ) < 0 )
      {
         INSERT( fname, tname, first_fname, next, prev );
         break;
      }
   }
   if( !tname )
      LINK( fname, first_fname, last_fname, next, prev );
}

void free_all_fnames( void )
{
   FNAME_DATA *fname, *fname_next;

   for( fname = first_fname; fname; fname = fname_next )
   {
      fname_next = fname->next;

      remove_fname( fname );
      free_fname( fname );
   }
}

void save_fnames( void )
{
   FNAME_DATA *fname;
   FILE *fp;

   if( !first_fname )
   {
      remove_file( FNAMES_FILE );
      return;
   }

   if( !( fp = fopen( FNAMES_FILE, "w" ) ) )
      return;
   for( fname = first_fname; fname; fname = fname->next )
   {
      if( !fname->name || fname->name[0] == '\0' )
         continue;
      fprintf( fp, "%s", "#FISH\n" );
      fprintf( fp, "Name       %s~\n", fname->name );
      fprintf( fp, "MinWeight  %d\n", fname->minweight );
      fprintf( fp, "MaxWeight  %d\n", fname->maxweight );
      fprintf( fp, "%s", "End\n\n" );
   }
   fprintf( fp, "%s", "#END\n" );
   fclose( fp );
   fp = NULL;
}

void fread_fnames( FILE *fp )
{
   const char *word;
   bool fMatch;
   FNAME_DATA *fname;

   CREATE( fname, FNAME_DATA, 1 );

   for( ;; )
   {
      word = feof( fp ) ? "End" : fread_word( fp );
      fMatch = false;

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

         case 'E':
            if( !str_cmp( word, "End" ) )
            {
               add_fname( fname );
               return;
	    }
	    break;

         case 'M':
            KEY( "MinWeight", fname->minweight, fread_number( fp ) );
            KEY( "MaxWeight", fname->maxweight, fread_number( fp ) );
            break;

         case 'N':
            KEY( "Name", fname->name, fread_string( fp ) );
            break;
      }

      if( !fMatch )
      {
         bug( "%s: no match: %s", __FUNCTION__, word );
         fread_to_eol( fp );
      }
   }
   free_fname( fname );
}

void load_fnames( void )
{
   FILE *fp;

   first_fname = last_fname = NULL;

   if( !( fp = fopen( FNAMES_FILE, "r" ) ) )
      return;
   for( ;; )
   {
      char letter;
      char *word;

      letter = fread_letter( fp );
      if( letter == '*' )
      {
         fread_to_eol( fp );
         continue;
      }
      if( letter != '#' )
      {
         bug( "%s: # not found.", __FUNCTION__ );
         break;
      }
      word = fread_word( fp );
      if( !str_cmp( word, "FISH" ) )
      {
         fread_fnames( fp );
         continue;
      }
      else if( !str_cmp( word, "END" ) )
         break;
      else
      {
         bug( "%s: bad section (%s).", __FUNCTION__, word );
         fread_to_eol( fp );
         continue;
      }
   }
   fclose( fp );
   fp = NULL;
}

FNAME_DATA *get_fish_name( void )
{
   FNAME_DATA *fname, *uname = NULL;

   for( fname = first_fname; fname; fname = fname->next )
   {
      if( !fname->name )
         continue;
      if( uname && number_percent( ) < 75 )
         continue;
      uname = fname;
   }

   if( uname )
      return uname;
   return NULL;
}

FNAME_DATA *find_fname( char *name )
{
   FNAME_DATA *fname;

   if( !name || name[0] == '\0' )
      return NULL;

   for( fname = first_fname; fname; fname = fname->next )
   {
      if( !fname->name )
         continue;
      if( !str_cmp( fname->name, name ) )
         return fname;
   }
   return NULL;
}

CMDF( do_fname )
{
   FNAME_DATA *fname;
   char arg[MSL], arg2[MSL];
   short cnt = 0;
   bool found = false;

   if( !argument || argument[0] == '\0' )
   {
      for( fname = first_fname; fname; fname = fname->next )
      {
         if( !found )
         {
            ch_printf( ch, "%20s %4s %4s  %20s %4s %4s\r\n", "Fish Name", "Min", "Max", "Fish Name", "Min", "Max" );
            send_to_char( "-------------------- ---- ----  -------------------- ---- ----\r\n", ch );
         }
         ch_printf( ch, "%20s %4d %4d", fname->name, fname->minweight, fname->maxweight );
         if( ++cnt == 2 )
         {
            send_to_char( "\r\n", ch );
            cnt = 0;
         }
         else
            send_to_char( "  ", ch );
         found = true;
      }
      if( !found )
         send_to_char( "No fish names to display.\r\n", ch );
      else if( cnt != 0 )
         send_to_char( "\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );
   argument = one_argument( argument, arg2 );

   fname = find_fname( arg );

   if( !str_cmp( arg2, "create" ) )
   {
      if( fname )
      {
         send_to_char( "That fish name is already being used.\r\n", ch );
         return;
      }
      CREATE( fname, FNAME_DATA, 1 );
      STRSET( fname->name, arg );
      fname->minweight = 1;
      fname->maxweight = 5;
      add_fname( fname );
      save_fnames( );
      send_to_char( "That fish name has been added.\r\n", ch );
      return;
   }

   if( !fname )
   {
      send_to_char( "No such fish name exist.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "delete" ) )
   {
      remove_fname( fname );
      free_fname( fname );
      save_fnames( );
      send_to_char( "That fish name has been deleted.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "name" ) )
   {
      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "What fish name would you like to use for it instead.\r\n", ch );
         return;
      }
      if( find_fname( argument ) )
      {
         send_to_char( "That fish name is already being used.\r\n", ch );
         return;
      }
      STRSET( fname->name, argument );
      save_fnames( );
      send_to_char( "The fish name has been changed.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "minweight" ) )
   {
      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "What minweight would you like to set it to.\r\n", ch );
         return;
      }
      fname->minweight = UMAX( 1, atoi( argument ) );
      save_fnames( );
      send_to_char( "The minweight has been set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "maxweight" ) )
   {
      if( !argument || argument[0] == '\0' )
      {
         send_to_char( "What maxweight would you like to set it to.\r\n", ch );
         return;
      }
      fname->maxweight = UMAX( 1, atoi( argument ) );
      save_fnames( );
      send_to_char( "The maxweight has been set.\r\n", ch );
      return;
   }

   send_to_char( "Usage: fname <fname> create/delete\r\n", ch );
   send_to_char( "Usage: fname <fname> name <new fname>\r\n", ch );
   send_to_char( "Usage: fname <fname> minweight/maxweight <#>\r\n", ch );
}

void remove_fish( FISH_DATA *fish )
{
   if( !fish )
      return;
   UNLINK( fish, first_fish, last_fish, next, prev );
}

void add_fish( FISH_DATA *fish )
{
   if( !fish )
      return;
   LINK( fish, first_fish, last_fish, next, prev );
}

void free_fish( FISH_DATA *fish )
{
   if( !fish )
      return;
   fish->rod = NULL;
   DISPOSE( fish );
}

void free_all_fish( void )
{
   FISH_DATA *fish, *fish_next;

   for( fish = first_fish; fish; fish = fish_next )
   {
      fish_next = fish->next;

      remove_fish( fish );
      free_fish( fish );
   }
}

bool create_fish( OBJ_DATA *rod )
{
   FISH_DATA *fish;

   if( !rod )
      return false;
   CREATE( fish, FISH_DATA, 1 );
   if( !fish )
   {
      bug( "%s: failed to create fish.", __FUNCTION__ );
      return false;
   } 
   fish->rod = rod;
   add_fish( fish );
   return true;
}

bool catch_fish( FISH_DATA *fish )
{
   OBJ_INDEX_DATA *findex;
   OBJ_DATA *nfish;
   FNAME_DATA *fname;
   char *name, buf[MSL];

   if( !fish || !fish->rod )
      return false;
   if( ( findex = get_obj_index( OBJ_VNUM_FISH ) ) )
   {
      nfish = create_object( findex, 0 );

      if( !( fname = get_fish_name( ) ) )
      {
         name = "catfish"; /* Need some default in case lol*/
         nfish->weight = number_range( 1, 10 );
      }
      else
      {
         name = fname->name;
         nfish->weight = number_range( fname->minweight, fname->maxweight );
      }
      if( nfish->name )
         snprintf( buf, sizeof( buf ), nfish->name, name );
      else
         snprintf( buf, sizeof( buf ), "%s fish", name );
      STRSET( nfish->name, buf );

      if( nfish->short_descr )
         snprintf( buf, sizeof( buf ), nfish->short_descr, name );
      else
         snprintf( buf, sizeof( buf ), "%s", name );
      STRSET( nfish->short_descr, buf );

      if( nfish->description )
         snprintf( buf, sizeof( buf ), nfish->description, name );
      else
         snprintf( buf, sizeof( buf ), "A %s lies here on the ground.", name );
      STRSET( nfish->description, buf );

      /* Value for when you cook and eat it */
      nfish->value[0] = UMAX( 1, nfish->weight );
      nfish->value[1] = number_range( 10, 100 );
      /* It is raw after all */
      nfish->value[2] = 0;
      /* Maybe sometime make it so some fish are poisonious */
      nfish->value[3] = 0;

      obj_to_obj( nfish, fish->rod );

      if( fish->rod->carried_by )
      {
         act( AT_ACTION, "You reel in your line to find $p.", fish->rod->carried_by, nfish, NULL, TO_CHAR );
         act( AT_ACTION, "$n reels in $s line to find $p.", fish->rod->carried_by, nfish, NULL, TO_ROOM );
      }
      remove_fish( fish );
      free_fish( fish );
      return true;
   }
   else
      bug( "%s: couldn't find object vnum %d.", __FUNCTION__, OBJ_VNUM_FISH );
   return false;
}

bool stop_obj_fishing( OBJ_DATA *obj )
{
   FISH_DATA *fish, *fish_next;
   bool found = false;

   if( !obj || obj->item_type != ITEM_FISHINGPOLE )
      return false;

   for( fish = first_fish; fish; fish = fish_next )
   {
      fish_next = fish->next;

      if( fish->rod != obj )
         continue;
      remove_fish( fish );
      free_fish( fish );
      found = true;
   }
   return found;
}

bool stop_fishing( CHAR_DATA *ch )
{
   OBJ_DATA *fishingpole;

   if( !ch )
      return false;

   if( !( fishingpole = get_eq_hold( ch, ITEM_FISHINGPOLE ) ) )
      return false;

   return stop_obj_fishing( fishingpole );
}

bool is_fishing( CHAR_DATA *ch )
{
   FISH_DATA *fish;
   OBJ_DATA *fishingpole;

   if( !ch )
      return false;

   if( !( fishingpole = get_eq_hold( ch, ITEM_FISHINGPOLE ) ) )
      return false;

   separate_obj( fishingpole );

   for( fish = first_fish; fish; fish = fish->next )
   {
      if( fish->rod == fishingpole )
         return true;
   }
   return false;
}

int fish_update = 0;

void update_fishing( void )
{
   FISH_DATA *fish, *next_fish;

   if( --fish_update > 0 )
      return;

   fish_update = number_range( 80, 100 );

   for( fish = first_fish; fish; fish = next_fish )
   {
      next_fish = fish->next;

      /* If no rod no point in keeping it */
      if( !fish->rod )
      {
         remove_fish( fish );
         free_fish( fish );
         continue;
      }

      /* Baited? */
      if( !fish->rod->first_content )
         continue;

      /* Nothing happens */
      if( number_percent( ) < 75 )
         continue;

      /* Looses bait */
      if( number_percent( ) < 50 )
      {
         extract_obj( fish->rod->first_content );
         if( fish->rod->carried_by )
         {
            act( AT_ACTION, "You reel in your line to find all the bait gone.", fish->rod->carried_by, NULL, NULL, TO_CHAR );
            act( AT_ACTION, "$n reels in $s line to find all the bait gone.", fish->rod->carried_by, NULL, NULL, TO_ROOM );
         }
         remove_fish( fish );
         free_fish( fish );
         continue;
      }

      /* Catches a fish */
      /* Remove bait first, fish becomes new bait */
      extract_obj( fish->rod->first_content );
      catch_fish( fish );
   }
}

CMDF( do_fish )
{
   OBJ_DATA *fishingpole;

   if( !( fishingpole = get_eq_hold( ch, ITEM_FISHINGPOLE ) ) )
   {
      send_to_char( "You must be holding a fishing pole.\r\n", ch );
      return;
   }

   separate_obj( fishingpole );

   if( ch->in_room->sector_type != SECT_WATER_SWIM && ch->in_room->sector_type != SECT_WATER_NOSWIM )
   {
      send_to_char( "Water is needed to fish!\r\n", ch );
      return;
   }

   if( !str_cmp( argument, "stop" ) )
   {
      if( stop_fishing( ch ) )
      {
         act( AT_ACTION, "You reel in your line and stop fishing.", ch, NULL, NULL, TO_CHAR );
         act( AT_ACTION, "$n reels in $s line and stops fishing.", ch, NULL, NULL, TO_ROOM );
      }
      else
         send_to_char( "You aren't fishing.\r\n", ch );
      return;
   }

   if( is_fishing( ch ) )
   {
      send_to_char( "You are already fishing.\r\n", ch );
      return;
   }

   if( !fishingpole->first_content )
   {
      send_to_char( "That pole doesn't have any bait to attract fish.\r\n", ch );
      return;
   }

   if( create_fish( fishingpole ) )
   {
      act( AT_ACTION, "You cast your line into the water.", ch, NULL, NULL, TO_CHAR );
      act( AT_ACTION, "$n cast $s line into the water.", ch, NULL, NULL, TO_ROOM );
   }
   else
      send_to_char( "You failed to start fishing.\r\n", ch );
}

CMDF( do_bait )
{
   OBJ_DATA *fishingpole, *bait;

   if( !( fishingpole = get_eq_hold( ch, ITEM_FISHINGPOLE ) ) )
   {
      send_to_char( "You must be holding a fishing pole.\r\n", ch );
      return;
   }

   if( is_fishing( ch ) )
   {
      send_to_char( "You can't do anything to it while your fishing.\r\n", ch );
      return;
   }

   if( !argument || argument[0] == '\0' )
   {
      send_to_char( "What would you like to bait the hook with?\r\n", ch );
      return;
   }

   if( !str_cmp( argument, "remove" ) )
   {
      if( !( bait = fishingpole->first_content ) )
      {
         send_to_char( "There is nothing on the hook to remove.\r\n", ch );
         return;
      }
      obj_from_obj( bait );
      obj_to_char( bait, ch );
      act( AT_ACTION, "You remove $P from $p's hook.", ch, fishingpole, bait, TO_CHAR );
      act( AT_ACTION, "$n removes $P from $p's hook.", ch, fishingpole, bait, TO_ROOM );
      return;
   }

   if( !( bait = get_obj_carry( ch, argument ) ) )
   {
      send_to_char( "You do not have that item.\r\n", ch );
      return;
   }

   separate_obj( fishingpole );
   separate_obj( bait );

   if( fishingpole->first_content )
   {
      send_to_char( "That pole is already baited.\r\n", ch );
      return;
   }

   if( bait->item_type != ITEM_FISH )
   {
      send_to_char( "You can't bait a hook with that.\r\n", ch );
      return;
   }

   obj_from_char( bait );
   obj_to_obj( bait, fishingpole );
   save_char_obj( ch );
   act( AT_ACTION, "You bait $p's hook with $P.", ch, fishingpole, bait, TO_CHAR );
   act( AT_ACTION, "$n baits $p's hook with $P.", ch, fishingpole, bait, TO_ROOM );
}