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 <ctype.h>
#include <stdarg.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "h/mud.h"
#include "h/trivia.h"

UTRIVIA_DATA *utrivia;

TRIVIA_DATA *first_trivia, *last_trivia;
int top_trivia;
int top_answers;
int currentquestion;

void free_one_answer( ANSWER_DATA *answer )
{
   STRFREE( answer->answer );
   DISPOSE( answer );
}

void free_answers( TRIVIA_DATA *trivia )
{
   ANSWER_DATA *answer, *next_answer;

   for( answer = trivia->first_answer; answer; answer = next_answer )
   {
      next_answer = answer->next;
      UNLINK( answer, trivia->first_answer, trivia->last_answer, next, prev );
      top_answers--;
      free_one_answer( answer );
   }
}

void free_trivia( TRIVIA_DATA *trivia )
{
   STRFREE( trivia->question );
   free_answers( trivia );
   DISPOSE( trivia );
}

void remove_trivia( TRIVIA_DATA *trivia )
{
   UNLINK( trivia, first_trivia, last_trivia, next, prev );
   top_trivia--;
}

void free_all_trivias( void )
{
   TRIVIA_DATA *trivia, *trivia_next;

   for( trivia = first_trivia; trivia; trivia = trivia_next )
   {
      trivia_next = trivia->next;
      remove_trivia( trivia );
      free_trivia( trivia );
   }
}

void add_trivia( TRIVIA_DATA *trivia )
{
   LINK( trivia, first_trivia, last_trivia, next, prev );
   top_trivia++;
}

/* Look to see if this is an answer to the question */
bool check_answer( TRIVIA_DATA *trivia, char *argument )
{
   ANSWER_DATA *answer;

   if( !trivia )
      return false;

   for( answer = trivia->first_answer; answer; answer = answer->next )
   {
      if( !str_cmp( answer->answer, argument ) )
         return true;
   }

   return false;
}

char *get_hint( char *str )
{
   static char newstr[MSL];
   int i, j;

   if( !str || str[0] == '\0' )
      return (char *)"";

   for( i = j = 0; str[i] != '\0'; i++ )
   {
      if( str[i] != '\r' && str[i] != '\n' )
      {
         if( str[i] == ' ' )
            newstr[j++] = str[i];
         else
            newstr[j++] = '*';
      }
   }
   newstr[j] = '\0';
   return newstr;
}

void give_hint( CHAR_DATA *ch, TRIVIA_DATA *trivia )
{
   ANSWER_DATA *answer;

   if( !trivia || !ch )
      return;

   if( !trivia->first_answer )
   {
      send_to_char( "Sorry but there doesn't seem to be any answer for that question.", ch );
      return;
   }

   for( answer = trivia->first_answer; answer; answer = answer->next )
      ch_printf( ch, "%s%s", answer->prev ? " OR " : "Hint: ", get_hint( answer->answer ) );

   send_to_char( "\r\n", ch );
}

void save_trivia( void )
{
   TRIVIA_DATA *trivia;
   ANSWER_DATA *answer;
   FILE *fp;

   if( !first_trivia )
   {
      remove_file( TRIVIA_FILE );
      return;
   }

   if( !( fp = fopen( TRIVIA_FILE, "w" ) ) )
      return;
   for( trivia = first_trivia; trivia; trivia = trivia->next )
   {
      fprintf( fp, "%s", "#TRIVIA\n" );
      if( trivia->question )
         fprintf( fp, "Question  %s~\n", strip_cr( trivia->question ) );
      fprintf( fp, "Reward    %d\n", trivia->reward );
      for( answer = trivia->first_answer; answer; answer = answer->next )
         fprintf( fp, "Answer    %s~\n", strip_cr( answer->answer ) );
      fprintf( fp, "%s", "End\n\n" );
   }
   fprintf( fp, "%s", "#END\n" );
   fclose( fp );
   fp = NULL;
}

TRIVIA_DATA *new_trivia( void )
{
   TRIVIA_DATA *trivia = NULL;

   CREATE( trivia, TRIVIA_DATA, 1 );
   if( !trivia )
   {
      bug( "%s: trivia is NULL after CREATE.", __FUNCTION__ );
      return NULL;
   }

   trivia->question = NULL;
   trivia->reward = 0;
   trivia->first_answer = trivia->last_answer = NULL;
   return trivia;
}

void fread_trivia( FILE *fp )
{
   TRIVIA_DATA *trivia;
   ANSWER_DATA *answer;
   const char *word;
   bool fMatch;

   trivia = new_trivia( );

   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_trivia( trivia );
               return;
	    }
	    break;

         case 'A':
            if( !str_cmp( word, "Answer" ) )
            {
               CREATE( answer, ANSWER_DATA, 1 );
               if( !answer )
                  fread_flagstring( fp );
               else
               {
                  answer->answer = fread_string( fp );
                  LINK( answer, trivia->first_answer, trivia->last_answer, next, prev );
                  top_answers++;
               }
               fMatch = true;
               break;
            }
            break;

         case 'Q':
            KEY( "Question", trivia->question, fread_string( fp ) );
            break;

         case 'R':
            KEY( "Reward", trivia->reward, fread_number( fp ) );
            break;
      }

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

void load_trivia( void )
{
   FILE *fp;

   top_trivia = 0;
   top_answers = 0;
   first_trivia = last_trivia = NULL;
   utrivia = NULL;

   if( !( fp = fopen( TRIVIA_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, "TRIVIA" ) )
      {
         fread_trivia( 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;
}

/* Find a specific answer for a trivia */
ANSWER_DATA *find_answer( CHAR_DATA *ch, TRIVIA_DATA *trivia, int num )
{
   ANSWER_DATA *answer;
   int count = 0;

   if( !ch || !trivia )
      return NULL;
   for( answer = trivia->first_answer; answer; answer = answer->next )
   {
      if( ++count == num )
         return answer;
   }
   return NULL;
}

/* Find a specific trivia entry using a number */
TRIVIA_DATA *find_trivia( int num )
{
   TRIVIA_DATA *trivia;
   int cnt = 0;

   for( trivia = first_trivia; trivia; trivia = trivia->next )
   {
      if( ++cnt == num )
         return trivia;
   }
   return NULL;
}

void show_trivia( CHAR_DATA *ch, TRIVIA_DATA *trivia, int count )
{
   ANSWER_DATA *answer;
   int cnt = 0;

   ch_printf( ch, "&C[&W%4d&C] &CQuestion: &W%s\r\n", count, trivia->question );
   ch_printf( ch, "&CReward: &W%d&D\r\n", trivia->reward );
   for( answer = trivia->first_answer; answer; answer = answer->next )
      ch_printf( ch, "&C[&W%4d&C] &CAnswer: &W%s&D\r\n", ++cnt, answer->answer ? answer->answer : "(Nothing set for the answer)" );
}

void send_to_trivia( const char *argument )
{
   to_channel( argument, "Trivia", PERM_ALL );
}

void trivia_printf( const char *fmt, ... )
{
   char buf[MSL * 2];
   va_list args;

   va_start( args, fmt );
   vsnprintf( buf, sizeof( buf ), fmt, args );
   va_end( args );

   send_to_trivia( buf );
}

void stop_trivia( void )
{
   int cnt;

   if( !utrivia || !utrivia->running )
      return;
   trivia_printf( "&wThe &W%s &gT&Gr&Wiv&Gi&ga &wContest is now Closed!&D", sysdata.mud_name );
   utrivia->running = false;
   utrivia->trivia = NULL;
   utrivia->timer = 0;
   for( cnt = 0; cnt < MAX_QUESTIONS; cnt++ )
      utrivia->qasked[cnt] = -1;
   DISPOSE( utrivia );
}

void trivia_question( void )
{
   TRIVIA_DATA *trivia;
   int cnt = 0, check, tried = 0;
   bool qused = false;

   if( !utrivia || !utrivia->running)
      return;

   /* Limited to MAX_QUESTIONS */
   if( currentquestion >= MAX_QUESTIONS )
      return;

   for( trivia = first_trivia; trivia; trivia = trivia->next )
   {
      qused = false;

      /* Don't let it run in a loop */
      if( ++tried >= 1000 )
      {
         stop_trivia( );
         return;
      }

      /* Get a random question */
      cnt = number_range( 1, top_trivia );

      /* Check to see if the question has been used recently */
      for( check = 0; check < MAX_QUESTIONS; check++ )
      {
         if( cnt == utrivia->qasked[check] )
         {
            qused = true;
            break;
         }
      }

      if( qused )
         continue;

      if( !( utrivia->trivia = find_trivia( cnt ) ) )
         continue;

      trivia_printf( "&wThis question is worth &W%d &wtrivia points.&D", utrivia->trivia->reward);
      trivia_printf( "&W%s&D", utrivia->trivia->question);
      utrivia->timer = number_range( 30, 60 );
      trivia_printf( "&wYou have &W%d &wseconds to answer.&D", utrivia->timer );
      utrivia->qasked[currentquestion++] = cnt;
      return;
   }
}

void trivia_update( void )
{
   if( !utrivia || !utrivia->running )
      return;

   if( currentquestion >= MAX_QUESTIONS || currentquestion > top_trivia )
   {
      stop_trivia( );
      return;
   }

   if( !utrivia->trivia )
      trivia_question( );

   if( utrivia->timer <= 0 && !utrivia->trivia )
   {
      stop_trivia( );
      return;
   }

   if( utrivia->timer <= 0 && utrivia->trivia )
   {
      send_to_trivia( "&wTime ran out for this Trivia Question with no winner!&D" );
      utrivia->trivia = NULL;
      utrivia->timer = 0;
      trivia_question( );
   }
   if( utrivia->timer >= 1 )
      utrivia->timer--;
}

CMDF( do_guess )
{
   if( !ch || is_npc( ch ) )
      return;
   if( !utrivia || !utrivia->running )
   {
      send_to_char( "Trivia isn't currently running\r\n", ch );
      return;
   }

   if( utrivia->trivia && ( argument == NULL || argument[0] == '\0' ) )
   {
      show_trivia( ch, utrivia->trivia, currentquestion );
      return;
   }

   if( !utrivia->trivia )
   {
      send_to_char( "There currently isn't any question to guess the answer to.\r\n", ch );
      return;
   }

   /* Not a correct answer */
   if( !( check_answer( utrivia->trivia, argument ) ) )
   {
      if( !str_cmp( argument, "hint" ) )
      {
         give_hint( ch, utrivia->trivia );
         return;
      }
      trivia_printf( "&W%s &Runcorrectly &wanswered the question!&D", ch->name );
   }
   else /* Correct answer */
   {
      trivia_printf( "&W%s &Ccorrectly &wanswered the question!&D", ch->name );
      utrivia->trivia = NULL;
      utrivia->timer = 0;
      trivia_question();
   }
}

CMDF( do_trivia )
{
   TRIVIA_DATA *trivia;
   ANSWER_DATA *answer;
   int count = 0, displayed = 0;
   char arg[MIL];

   if( is_npc( ch ) )
   {
      send_to_char( "NPCs have no reason to do trivia.\r\n", ch );
      return;
   }

   if( !ch->desc )
   {
      send_to_char( "You have no descriptor.\r\n", ch );
      return;
   }

   set_pager_color( AT_HELP, ch );
   argument = one_argument( argument, arg );

   /* Add a new trivia */
   if( arg != NULL && arg[0] != '\0' && !str_cmp( arg, "add" ) )
   {
      if( argument == NULL || argument[0] == '\0' )
      {
         send_to_char( "You can't create a trivia with no question.\r\n", ch );
         return;
      }
      if( !( trivia = new_trivia( ) ) )
         return;
      STRSET( trivia->question, argument );
      add_trivia( trivia );
      save_trivia( );
      send_to_char( "Your new trivia has been added.\r\n", ch );
      return;
   }

   if( arg != NULL && arg[0] != '\0' && !str_cmp( arg, "start" ) )
   {
      if( utrivia && utrivia->running )
      {
         send_to_char( "Trivia is already running.\r\n", ch );
         return;
      }

      CREATE( utrivia, UTRIVIA_DATA, 1 );
      if( !utrivia )
      {
         bug( "%s: utrivia still NULL after CREATE", __FUNCTION__ );
         return;
      }
      utrivia->running = true;
      utrivia->trivia = NULL;
      utrivia->timer = 0;
      trivia_printf( "&wThe &W%s &gT&Gr&Wiv&Gi&ga &wContest is now Open!&D", sysdata.mud_name );
      for( count = 0; count < MAX_QUESTIONS; count++)
         utrivia->qasked[count] = -1;
      currentquestion = 0;
      trivia_question();
      return;
   }

   if( arg != NULL && arg[0] != '\0' && !str_cmp( arg, "stop" ) )
   {
      stop_trivia( );
      return;
   }

   /* Show all Questions and what number to use */
   if( arg == NULL || arg[0] == '\0' || !is_number( arg ) )
   {
      for( trivia = first_trivia; trivia; trivia = trivia->next )
      {
         ch_printf( ch, "&C[&W%4d&C] &CQuestion: &W%s\r\n", ++count, trivia->question );

         /* Limit the max it shows to 100? */
         if( ++displayed == 100 )
            break;
      }

      if( displayed == 0 )
         send_to_char( "No trivia to show.\r\n", ch );
      return;
   }

   count = atoi( arg );
   if( !( trivia = find_trivia( count ) ) )
   {
      send_to_char( "No such trivia to modify.\r\n", ch );
      return;
   }

   argument = one_argument( argument, arg );
   if( arg == NULL || arg[0] == '\0' || !is_immortal( ch ) )
   {
      show_trivia( ch, trivia, count );
      return;
   }

   /* Remove a trivia entry */
   if( !str_cmp( arg, "remove" ) )
   {
      remove_trivia( trivia );
      free_trivia( trivia );
      save_trivia( );
      send_to_char( "That trivia was removed and the trivia table was saved.\r\n", ch );
      return;
   }

   /* Add/Edit answers to the trivia */
   if( !str_cmp( arg, "answer" ) )
   {
      argument = one_argument( argument, arg );
      if( !str_cmp( arg, "add" ) )
      {
         if( argument == NULL || argument[0] == '\0' )
         {
            send_to_char( "You can't add an empty answer.\r\n", ch );
            return;
         }
         CREATE( answer, ANSWER_DATA, 1 );
         STRSET( answer->answer, argument );
         LINK( answer, trivia->first_answer, trivia->last_answer, next, prev );
         top_answers++;
         save_trivia( );
         send_to_char( "That answer has been added.\r\n", ch );
         return;
      }

      count = atoi( arg );
      if( !( answer = find_answer( ch, trivia, count ) ) )
      {
         send_to_char( "No such answer to modify.\r\n", ch );
         return;
      }

      argument = one_argument( argument, arg );

      if( !str_cmp( arg, "edit" ) )
      {
         if( argument == NULL || argument[0] == '\0' )
         {
            send_to_char( "You can't edit an answer to be empty.\r\n", ch );
            return;
         }

         STRSET( answer->answer, argument );
         save_trivia( );
         send_to_char( "That answer has been modified.\r\n", ch );
         return;
      }
      else if( !str_cmp( arg, "remove" ) )
      {
         UNLINK( answer, trivia->first_answer, trivia->last_answer, next, prev );
         top_answers--;
         free_one_answer( answer );
         save_trivia( );
         send_to_char( "That answer has been removed.\r\n", ch );
         return;
      }
      send_to_char( "Usage: trivia answer add <answer>\r\n", ch );
      send_to_char( "Usage: trivia answer <#> remove\r\n", ch );
      send_to_char( "Usage: trivia answer <#> edit <answer>\r\n", ch );
      return;
   }

   /* Edit a trivia question */
   if( !str_cmp( arg, "edit" ) )
   {
      if( argument == NULL || argument[0] == '\0' )
      {
         send_to_char( "You can't change a trivia to have no question.\r\n", ch );
         return;
      }
      STRSET( trivia->question, argument );
      save_trivia( );
      send_to_char( "The trivia question has been changed.\r\n", ch );
      return;
   }

   if( !str_cmp( arg, "reward" ) )
   {
      trivia->reward = UMAX( 0, atoi( argument ) );
      save_trivia( );
      ch_printf( ch, "Reward on that trivia is now %d.\r\n", trivia->reward );
      return;
   }

   /* Handle displaying it again if the right argument wasnt given */
   show_trivia( ch, trivia, count );
}