LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/color/
LOP/councils/
LOP/deity/
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, 2009 by: the LoP team.                          *
 *****************************************************************************/

#include <stdio.h>
#include <string.h>
#include "h/mud.h"

int top_social;

/* Only does the insert when changing name or adding a new one */
void add_social( SOCIALTYPE *social, bool hinsert )
{
   int x;
   SOCIALTYPE *tmp, *prev;

   if( !social )
   {
      bug( "%s: NULL social", __FUNCTION__ );
      return;
   }

   if( !social->name )
   {
      bug( "%s: NULL social->name", __FUNCTION__ );
      return;
   }

   if( !social->char_no_arg )
   {
      bug( "%s: NULL social->char_no_arg", __FUNCTION__ );
      return;
   }

   for( x = 0; social->name[x] != '\0'; x++ )
      social->name[x] = LOWER( social->name[x] );

   top_social++;
   HASH_LINK( social, social_index, tmp, prev, hinsert );
}

SOCIALTYPE *find_social( const char *command, bool exact )
{
   SOCIALTYPE *social;
   int hash;

   hash = LOWER( command[0] ) % 126;

   for( social = social_index[hash]; social; social = social->next )
   {
      if( ( !exact && !str_prefix( command, social->name ) )
      || ( exact && !str_cmp( command, social->name ) ) )
         return social;
   }
   return NULL;
}

bool check_social( CHAR_DATA *ch, const char *command, char *argument )
{
   char arg[MIL];
   CHAR_DATA *victim = NULL;
   SOCIALTYPE *social;
   bool gsocial = false;
   bool pmobtrigger = MOBtrigger;

   if( !( social = find_social( command, false ) ) )
      return false;

   if( !is_npc( ch ) && xIS_SET( ch->act, PLR_NO_EMOTE ) )
   {
      send_to_char( "You're anti-social!\r\n", ch );
      return true;
   }

   switch( ch->position )
   {
      case POS_DEAD:
         send_to_char( "Lie still; you're DEAD.\r\n", ch );
         return true;

      case POS_INCAP:
      case POS_MORTAL:
         send_to_char( "You're hurt far too bad for that.\r\n", ch );
         return true;

      case POS_STUNNED:
         send_to_char( "You're too stunned to do that.\r\n", ch );
         return true;

      case POS_SLEEPING:
         /*
          * I just know this is the path to a 12" 'if' statement.  :(
          * But two players asked for it already!  -- Furey
          */
         if( !str_cmp( social->name, "snore" ) )
            break;
         send_to_char( "In your dreams, or what?\r\n", ch );
         return true;

   }

   one_argument( argument, arg );
   victim = NULL;

   if( arg != NULL && arg[0] != '\0' )
   {
      if( !( victim = get_char_room( ch, arg ) ) )
      {
         if( ( victim = get_char_world( ch, arg ) ) )
            gsocial = true;
      }
   }

   if( gsocial )
      MOBtrigger = false;
   if( victim && ch && is_ignoring( victim, ch ) )
   {
      set_char_color( AT_IGNORE, ch );
      if( is_immortal( ch ) )
         ch_printf( ch, "%s is trying to ignore you.\r\n", victim->name );
      else
         ch_printf( ch, "%s is ignoring you.\r\n", victim->name );
   }
   if( arg == NULL || arg[0] == '\0' )
   {
      act_printf( AT_SOCIAL, ch, NULL, victim, TO_ROOM, "%s%s", gsocial ? "[From afar] " : "", social->others_no_arg );
      act_printf( AT_SOCIAL, ch, NULL, victim, TO_CHAR, "%s%s", gsocial ? "[From afar] " : "", social->char_no_arg );
   }
   else if( !victim )
      send_to_char( "They aren't here.\r\n", ch );
   else if( victim == ch )
   {
      act_printf( AT_SOCIAL, ch, NULL, victim, gsocial ? TO_OTHERS : TO_ROOM, "%s%s", gsocial ? "[From afar] " : "", social->others_auto );
      act_printf( AT_SOCIAL, ch, NULL, victim, TO_CHAR, "%s%s", gsocial ? "[From afar] " : "", social->char_auto );
   }
   else
   {
      act_printf( AT_SOCIAL, ch, NULL, victim, gsocial ? TO_OTHERS : TO_NOTVICT, "%s%s", gsocial ? "[From afar] " : "", social->others_found );
      act_printf( AT_SOCIAL, ch, NULL, victim, TO_CHAR, "%s%s", gsocial ? "[From afar] " : "", social->char_found );
      act_printf( AT_SOCIAL, ch, NULL, victim, TO_VICT, "%s%s", gsocial ? "[From afar] " : "", social->vict_found );

      /*
       * Make mobs do random socials back
       * Also removed being drawn into a fight for socials
       */
      if( !is_npc( ch ) && is_npc( victim ) && !IS_AFFECTED( victim, AFF_CHARM ) && is_awake( victim )
      && !HAS_PROG( victim->pIndexData, ACT_PROG ) )
      {
         bool found = false;
         SOCIALTYPE *resocial, *resocial_next;
         int hash;

         /* Preform a random social */
         for( hash = 0; hash < 126; hash++ )
         {
            for( resocial = social_index[hash]; resocial; resocial = resocial_next )
            {
               resocial_next = resocial->next;
               /* Continue as long as percent is not equal to 50 */
               if( number_percent( ) != 50 )
                  continue;
               /* If it doesn't have some of the data continue */
               if( !resocial->others_found || !resocial->char_found || !resocial->vict_found )
                 continue;
               /* Send the social data */
               act_printf( AT_SOCIAL, victim, NULL, ch, gsocial ? TO_OTHERS : TO_NOTVICT, "%s%s", gsocial ? "[From afar] " : "", resocial->others_found );
               act_printf( AT_SOCIAL, victim, NULL, ch, TO_CHAR, "%s%s", gsocial ? "[From afar] " : "", resocial->char_found );
               act_printf( AT_SOCIAL, victim, NULL, ch, TO_VICT, "%s%s", gsocial ? "[From afar] " : "", resocial->vict_found );
               found = true;
               break;
            }
            /* If one was found and used break */
            if( found )
              break;
         }

         /* If found is false then do the same social back */
         if( !found )
         {
            if( social->others_found )
               act_printf( AT_SOCIAL, victim, NULL, ch, gsocial ? TO_OTHERS : TO_NOTVICT, "%s%s", gsocial ? "[From afar] " : "", social->others_found );
            if( social->char_found )
               act_printf( AT_SOCIAL, victim, NULL, ch, TO_CHAR, "%s%s", gsocial ? "[From afar] " : "", social->char_found );
            if( social->vict_found )
               act_printf( AT_SOCIAL, victim, NULL, ch, TO_VICT, "%s%s", gsocial ? "[From afar] " : "", social->vict_found );
         }
      }
   }

   if( gsocial )
      MOBtrigger = pmobtrigger;

   return true;
}

SOCIALTYPE *new_social( void )
{
   SOCIALTYPE *social = NULL;

   CREATE( social, SOCIALTYPE, 1 );
   if( !social )
   {
      bug( "%s: social is NULL after CREATE.", __FUNCTION__ );
      return NULL;
   }
   social->name = NULL;
   social->char_no_arg = NULL;
   social->char_found = NULL;
   social->char_auto = NULL;
   social->others_no_arg = NULL;
   social->others_found = NULL;
   social->others_auto = NULL;
   social->vict_found = NULL;
   return social;
}

void free_social( SOCIALTYPE *social )
{
   if( !social )
      return;
   STRFREE( social->name );
   STRFREE( social->char_no_arg );
   STRFREE( social->others_no_arg );
   STRFREE( social->char_found );
   STRFREE( social->others_found );
   STRFREE( social->vict_found );
   STRFREE( social->char_auto );
   STRFREE( social->others_auto );
   DISPOSE( social );
   top_social--;
}

void fread_social( FILE *fp )
{
   const char *word;
   bool fMatch;
   SOCIALTYPE *social;

   social = new_social( );

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

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

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

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

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

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

         case 'V':
            KEY( "VictFound", social->vict_found, fread_string( fp ) );
            break;
      }

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

void load_socials( void )
{
   FILE *fp;

   top_social = 0;

   if( !( fp = fopen( SOCIAL_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, (%c) found instead.", __FUNCTION__, letter );
         break;
      }

      word = fread_word( fp );
      if( !str_cmp( word, "SOCIAL" ) )
      {
         fread_social( fp );
         continue;
      }
      else if( !str_cmp( word, "END" ) )
         break;
      else
      {
         bug( "%s: bad section (%s).", __FUNCTION__, word );
         continue;
      }
   }
   fclose( fp );
   fp = NULL;
}

void save_socials( bool autosave )
{
   FILE *fp;
   SOCIALTYPE *social;
   int x;
   bool found = false;

   if( autosave && !sysdata.autosavesocials )
      return;

   if( !( fp = fopen( SOCIAL_FILE, "w" ) ) )
   {
      bug( "%s: Can't open %s for writting", __FUNCTION__, SOCIAL_FILE );
      perror( SOCIAL_FILE );
      return;
   }

   for( x = 0; x < 126; x++ )
   {
      for( social = social_index[x]; social; social = social->next )
      {
         if( !social->name || social->name[0] == '\0' )
         {
            bug( "%s: blank social in hash bucket %d", __FUNCTION__, x );
            continue;
         }
         if( !social->char_no_arg || social->char_no_arg[0] == '\0' )
         {
            bug( "%s: social with NULL char_no_arg in hash bucket %d", __FUNCTION__, x );
            continue;
         }
         found = true;
         fprintf( fp, "#SOCIAL\n" );
         fprintf( fp, "Name        %s~\n", social->name );
         if( social->char_no_arg )
            fprintf( fp, "CharNoArg   %s~\n", social->char_no_arg );
         if( social->others_no_arg )
            fprintf( fp, "OthersNoArg %s~\n", social->others_no_arg );
         if( social->char_found )
            fprintf( fp, "CharFound   %s~\n", social->char_found );
         if( social->others_found )
            fprintf( fp, "OthersFound %s~\n", social->others_found );
         if( social->vict_found )
            fprintf( fp, "VictFound   %s~\n", social->vict_found );
         if( social->char_auto )
            fprintf( fp, "CharAuto    %s~\n", social->char_auto );
         if( social->others_auto )
            fprintf( fp, "OthersAuto  %s~\n", social->others_auto );
         fprintf( fp, "End\n\n" );
      }
   }
   fprintf( fp, "#END\n" );
   fclose( fp );
   fp = NULL;
   if( !found )
      remove_file( SOCIAL_FILE );
}

void free_socials( void )
{
   SOCIALTYPE *social, *social_next;
   int hash;

   for( hash = 0; hash < 126; hash++ )
   {
      for( social = social_index[hash]; social; social = social_next )
      {
         social_next = social->next;
         free_social( social );
      }
   }
}

void unlink_social( SOCIALTYPE *social )
{
   SOCIALTYPE *tmp;

   if( !social )
   {
      bug( "%s: NULL social", __FUNCTION__ );
      return;
   }

   if( !social->name || social->name[0] == '\0' )
   {
      bug( "%s: social with invalid name", __FUNCTION__ );
      return;
   }

   HASH_UNLINK( social, social_index, tmp );
}

CMDF( do_sedit )
{
   SOCIALTYPE *social;
   char arg1[MIL], arg2[MIL];

   set_char_color( AT_SOCIAL, ch );

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

   if( arg1 == NULL || arg1[0] == '\0' )
   {
      if( get_trust( ch ) >= PERM_HEAD )
         send_to_char( "Usage: sedit <save>\r\n", ch );
      send_to_char( "Usage: sedit <social> [field]\r\n", ch );
      send_to_char( "Fields:\r\n   ", ch );
      if( get_trust( ch ) >= PERM_HEAD )
         send_to_char( "name    delete  ", ch );
      send_to_char( "create  cnoarg  onoarg  cfound  ofound  vfound  cauto  oauto\r\n", ch );
      send_to_char( "   raise   lower   list\r\n", ch );
      return;
   }

   if( get_trust( ch ) >= PERM_HEAD && !str_cmp( arg1, "save" ) )
   {
      save_socials( false );
      send_to_char( "Saved.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "create" ) )
   {
      if( ( social = find_social( arg1, true ) ) )
      {
         send_to_char( "That social already exists!\r\n", ch );
         return;
      }
      if( !( social = new_social( ) ) )
         return;
      social->name = STRALLOC( arg1 );
      snprintf( arg2, sizeof( arg2 ), "You %s.", arg1 );
      social->char_no_arg = STRALLOC( arg2 );
      add_social( social, true );
      save_socials( true );
      send_to_char( "Social added.\r\n", ch );
      return;
   }

   if( !( social = find_social( arg1, false ) ) )
   {
      send_to_char( "Social not found.\r\n", ch );
      return;
   }

   if( arg2 == NULL || arg2[0] == '\0' || !str_cmp( arg2, "show" ) )
   {
      ch_printf( ch, "Social: %s\r\n", social->name ? social->name : "(Not Set)" );
      ch_printf( ch, "CNoArg: %s\r\n", social->char_no_arg ? social->char_no_arg : "(Not Set)" );
      ch_printf( ch, "ONoArg: %s\r\n", social->others_no_arg ? social->others_no_arg : "(Not Set)" );
      ch_printf( ch, "CFound: %s\r\n", social->char_found ? social->char_found : "(Not Set)" );
      ch_printf( ch, "OFound: %s\r\n", social->others_found ? social->others_found : "(Not Set)" );
      ch_printf( ch, "VFound: %s\r\n", social->vict_found ? social->vict_found : "(Not Set)" );
      ch_printf( ch, "CAuto : %s\r\n", social->char_auto ? social->char_auto : "(Not Set)" );
      ch_printf( ch, "OAuto : %s\r\n", social->others_auto ? social->others_auto : "(Not Set)" );
      return;
   }

   if( get_trust( ch ) >= PERM_HEAD && !str_cmp( arg2, "delete" ) )
   {
      unlink_social( social );
      free_social( social );
      save_socials( true );
      send_to_char( "Deleted.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "cnoarg" ) )
   {
      if( !argument || argument[0] == '\0' || !str_cmp( argument, "clear" ) )
      {
         send_to_char( "You can't clear this field.  It must have a message.\r\n", ch );
         return;
      }
      STRSET( social->char_no_arg, argument );
      save_socials( true );
      send_to_char( "Cnoarg set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "onoarg" ) )
   {
      STRSET( social->others_no_arg, argument );
      save_socials( true );
      send_to_char( "Onoarg set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "cfound" ) )
   {
      STRSET( social->char_found, argument );
      save_socials( true );
      send_to_char( "Cfound set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "ofound" ) )
   {
      STRSET( social->others_found, argument );
      save_socials( true );
      send_to_char( "Ofound set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "vfound" ) )
   {
      STRSET( social->vict_found, argument );
      save_socials( true );
      send_to_char( "Vfound set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "cauto" ) )
   {
      STRSET( social->char_auto, argument );
      save_socials( true );
      send_to_char( "Cauto set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "oauto" ) )
   {
      STRSET( social->others_auto, argument );
      save_socials( true );
      send_to_char( "Oauto set.\r\n", ch );
      return;
   }

   if( !str_cmp( arg2, "raise" ) )
   {
      SOCIALTYPE *tmp, *tmp_next;
      int hash = social->name[0] % 126;

      if( ( tmp = social_index[hash] ) == social )
      {
         send_to_char( "That social is already at the top.\r\n", ch );
         return;
      }
      if( tmp->next == social )
      {
         social_index[hash] = social;
         tmp_next = tmp->next;
         tmp->next = social->next;
         social->next = tmp;
         save_socials( true );
         ch_printf( ch, "Moved %s above %s.\r\n", social->name, social->next->name );
         return;
      }
      for( ; tmp; tmp = tmp->next )
      {
         tmp_next = tmp->next;
         if( tmp_next->next == social )
         {
            tmp->next = social;
            tmp_next->next = social->next;
            social->next = tmp_next;
            save_socials( true );
            ch_printf( ch, "Moved %s above %s.\r\n", social->name, tmp_next->name );
            return;
         }
      }
      send_to_char( "ERROR -- Not Found!\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "lower" ) )
   {
      SOCIALTYPE *tmp, *tmp_next;
      int hash = social->name[0] % 126;

      if( !social->next )
      {
         send_to_char( "That social is already at the bottom.\r\n", ch );
         return;
      }
      tmp = social_index[hash];
      if( tmp == social )
      {
         tmp_next = tmp->next;
         social_index[hash] = social->next;
         social->next = tmp_next->next;
         tmp_next->next = social;
         save_socials( true );
         ch_printf( ch, "Moved %s below %s.\r\n", social->name, tmp_next->name );
         return;
      }
      for( ; tmp; tmp = tmp->next )
      {
         if( tmp->next == social )
         {
            tmp_next = social->next;
            tmp->next = tmp_next;
            social->next = tmp_next->next;
            tmp_next->next = social;
            save_socials( true );
            ch_printf( ch, "Moved %s below %s.\r\n", social->name, tmp_next->name );
            return;
         }
      }
      send_to_char( "ERROR -- Not Found!\r\n", ch );
      return;
   }
   if( !str_cmp( arg2, "list" ) )
   {
      SOCIALTYPE *tmp;
      int hash = social->name[0] % 126;
      int col = 0;

      pager_printf( ch, "Priority placement for [%s]:\r\n", social->name );
      for( tmp = social_index[hash]; tmp; tmp = tmp->next )
      {
         pager_printf( ch, "  %s%-12s", tmp == social ? "&[green]" : "&[plain]", tmp->name );
         if( ++col == 6 )
         {
            send_to_pager( "\r\n", ch );
            col = 0;
         }
      }
      if( col != 0 )
         send_to_pager( "\r\n", ch );
      return;
   }
   if( get_trust( ch ) >= PERM_HEAD && !str_cmp( arg2, "name" ) )
   {
      bool relocate;
      SOCIALTYPE *checksocial;

      one_argument( argument, arg1 );
      if( arg1[0] == '\0' )
      {
         send_to_char( "Can't clear name field!\r\n", ch );
         return;
      }
      if( ( checksocial = find_social( arg1, true ) ) )
      {
         ch_printf( ch, "There is already a social named %s.\r\n", arg1 );
         return;
      }
      if( arg1[0] != social->name[0] )
      {
         unlink_social( social );
         relocate = true;
      }
      else
         relocate = false;
      STRSET( social->name, arg1 );
      if( relocate )
         add_social( social, true );
      save_socials( true );
      send_to_char( "Done.\r\n", ch );
      return;
   }
   do_sedit( ch, (char *)"" );
}

CMDF( do_socials )
{
   int iHash, col = 0, cnt = 0;
   SOCIALTYPE *social;
   char arg[MIL];

   argument = one_argument( argument, arg );
   set_pager_color( AT_PLAIN, ch );
   for( iHash = 0; iHash < 126; iHash++ )
   {
      for( social = social_index[iHash]; social; social = social->next )
      {
         if( !social || !social->name )
            continue;
         if( arg != NULL && arg[0] != '\0' && str_prefix( arg, social->name ) )
            continue;
         cnt++;
         pager_printf( ch, "%-12s", social->name );
         if( ++col == 6 )
         {
            send_to_pager( "\r\n", ch );
            col = 0;
         }
      }
   }
   if( col != 0 )
      send_to_pager( "\r\n", ch );
   if( cnt == 0 )
   {
      if( arg != NULL && arg[0] != '\0' )
         pager_printf( ch, "&WNo social found under &C%s&W.&D\r\n", arg );
      else
         send_to_pager( "&WNo socials found at all.&D\r\n", ch );
   }
   else
   {
      if( arg != NULL && arg[0] != '\0' )
         pager_printf( ch, "&C%d &Wsocials found under &C%s&W.&D\r\n", cnt, arg );
      else
         pager_printf( ch, "&C%d &Wsocials found in all.&D\r\n", cnt );
   }
}