/*****************************************************************************
* 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 );
}