/*****************************************************************************
* 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. *
*---------------------------------------------------------------------------*
* High Scores *
*****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "h/mud.h"
#define HIGHSCORE_FILE SYSTEM_DIR "highscores.dat"
typedef struct highscore_data HIGHSCORE_DATA;
struct highscore_data
{
HIGHSCORE_DATA *next, *prev;
char *name;
int value;
int pos;
};
typedef struct hightable_data HIGHTABLE_DATA;
struct hightable_data
{
HIGHTABLE_DATA *next, *prev;
HIGHSCORE_DATA *first_highscore, *last_highscore;
char *name;
int max;
bool reversed; /* Should it be a reversed table - Lowest is best */
};
HIGHTABLE_DATA *first_hightable, *last_hightable;
short show_highscore_count( HIGHTABLE_DATA *table )
{
HIGHSCORE_DATA *score;
short count = 0;
if( table )
for( score = table->first_highscore; score; score = score->next )
count++;
return count;
}
void save_highscore( void )
{
HIGHTABLE_DATA *table;
HIGHSCORE_DATA *score;
FILE *fp;
if( !first_hightable )
{
remove_file( HIGHSCORE_FILE );
return;
}
if( !( fp = fopen( HIGHSCORE_FILE, "w" ) ) )
{
bug( "%s: Can't open %s for writing.", __FUNCTION__, HIGHSCORE_FILE );
perror( HIGHSCORE_FILE );
return;
}
for( table = first_hightable; table; table = table->next )
{
if( !table->name )
continue;
fprintf( fp, "Name %s~\n", table->name );
fprintf( fp, "Max %d\n", table->max );
if( table->reversed )
fprintf( fp, "%s\n", "Reversed" );
for( score = table->first_highscore; score; score = score->next )
fprintf( fp, "HighScore %s~ %d\r\n", score->name, score->value );
}
fprintf( fp, "%s", "End\n" );
fclose( fp );
fp = NULL;
}
void free_highscore( HIGHSCORE_DATA *score )
{
if( !score )
return;
STRFREE( score->name );
DISPOSE( score );
}
void free_hightable( HIGHTABLE_DATA *table )
{
HIGHSCORE_DATA *score, *score_next;
if( !table )
return;
STRFREE( table->name );
for( score = table->first_highscore; score; score = score_next )
{
score_next = score->next;
UNLINK( score, table->first_highscore, table->last_highscore, next, prev );
free_highscore( score );
}
DISPOSE( table );
}
void free_hightables( void )
{
HIGHTABLE_DATA *table, *table_next;
for( table = first_hightable; table; table = table_next )
{
table_next = table->next;
UNLINK( table, first_hightable, last_hightable, next, prev );
free_hightable( table );
}
}
void trim_highscore( HIGHTABLE_DATA *table )
{
HIGHSCORE_DATA *score, *score_next;
int count = 0;
for( score = table->first_highscore; score; score = score_next )
{
score_next = score->next;
score->pos = ++count;
if( count >= table->max )
{
UNLINK( score, table->first_highscore, table->last_highscore, next, prev );
free_highscore( score );
}
}
}
void link_highscore( HIGHTABLE_DATA *table, HIGHSCORE_DATA *nscore, bool silent )
{
HIGHSCORE_DATA *score, *score_next;
char buf[MSL];
int count = 0;
for( score = table->first_highscore; score; score = score_next )
{
score_next = score->next;
++count;
if( ( table->reversed && nscore->value < score->value )
|| ( !table->reversed && nscore->value > score->value ) )
{
if( !silent && nscore->pos > count )
{
snprintf( buf, sizeof( buf ), "%s has obtained position %d on highscore table %s.",
nscore->name, count, table->name );
to_channel( buf, "highscore", PERM_ALL );
}
else if( !silent && nscore->pos < count )
{
snprintf( buf, sizeof( buf ), "%s has dropped to position %d on highscore table %s.",
nscore->name, count, table->name );
to_channel( buf, "highscore", PERM_ALL );
}
INSERT( nscore, score, table->first_highscore, next, prev );
trim_highscore( table );
return;
}
}
if( ++count < table->max )
{
if( !silent && nscore->pos > count )
{
snprintf( buf, sizeof( buf ), "%s has obtained position %d on highscore table %s.",
nscore->name, count, table->name );
to_channel( buf, "highscore", PERM_ALL );
}
else if( !silent && nscore->pos < count )
{
snprintf( buf, sizeof( buf ), "%s has dropped to position %d on highscore table %s.",
nscore->name, count, table->name );
to_channel( buf, "highscore", PERM_ALL );
}
LINK( nscore, table->first_highscore, table->last_highscore, next, prev );
trim_highscore( table );
return;
}
free_highscore( nscore );
trim_highscore( table );
}
HIGHTABLE_DATA *add_hightable( char *name )
{
HIGHTABLE_DATA *table;
if( !name || name[0] == '\0' )
return NULL;
CREATE( table, HIGHTABLE_DATA, 1 );
STRSET( table->name, name );
table->first_highscore = NULL;
table->last_highscore = NULL;
table->max = 20;
table->reversed = false;
LINK( table, first_hightable, last_hightable, next, prev );
return table;
}
void add_highscore( HIGHTABLE_DATA *table, char *name, int value, int silent )
{
HIGHSCORE_DATA *score;
if( !table || !name )
return;
CREATE( score, HIGHSCORE_DATA, 1 );
STRSET( score->name, name );
score->value = value;
score->pos = ( table->max + 1 );
link_highscore( table, score, silent );
}
void load_highscores( void )
{
FILE *fp;
HIGHTABLE_DATA *table = NULL;
char *name;
char *sname;
int value;
first_hightable = last_hightable = NULL;
if( !( fp = fopen( HIGHSCORE_FILE, "r" ) ) )
return;
for( ;; )
{
char *word;
if( feof( fp ) )
break;
word = fread_word( fp );
if( word[0] == EOF )
break;
if( !str_cmp( word, "Name" ) )
{
name = fread_flagstring( fp );
if( ( table = add_hightable( name ) ) )
{
table->max = 20;
table->reversed = false;
}
continue;
}
else if( !str_cmp( word, "Max" ) )
{
value = fread_number( fp );
if( !table )
continue;
table->max = value;
}
else if( !str_cmp( word, "Reversed" ) )
{
if( !table )
continue;
table->reversed = true;
}
else if( !str_cmp( word, "HighScore" ) )
{
sname = fread_flagstring( fp );
value = fread_number( fp );
if( !table )
continue;
if( valid_pfile( sname ) )
add_highscore( table, sname, value, true );
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;
}
HIGHTABLE_DATA *find_hightable( const char *name )
{
HIGHTABLE_DATA *table;
for( table = first_hightable; table; table = table->next )
if( !str_cmp( table->name, name ) )
return table;
return NULL;
}
HIGHSCORE_DATA *find_highscore( HIGHTABLE_DATA *table, const char *name )
{
HIGHSCORE_DATA *score;
for( score = table->first_highscore; score; score = score->next )
if( !str_cmp( score->name, name ) )
return score;
return NULL;
}
void check_highscores( HIGHTABLE_DATA *table, HIGHSCORE_DATA *score )
{
if( !table || !score )
return;
if( ( score->prev && score->value > score->prev->value )
|| ( score->next && score->value < score->next->value ) )
{
UNLINK( score, table->first_highscore, table->last_highscore, next, prev );
link_highscore( table, score, false );
}
}
void update_highscore( CHAR_DATA *ch )
{
HIGHTABLE_DATA *table;
HIGHSCORE_DATA *score;
if( !ch || is_npc( ch ) || is_immortal( ch ) )
return;
if( ( table = find_hightable( "Gold" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->mgold;
check_highscores( table, score );
}
else if( ch->mgold > 0 )
add_highscore( table, ch->name, ch->mgold, false );
}
if( ( table = find_hightable( "SudokuWins" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->swins;
check_highscores( table, score );
}
else if( ch->pcdata->swins > 0 )
add_highscore( table, ch->name, ch->pcdata->swins, false );
}
if( ( table = find_hightable( "MKills" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->mkills;
check_highscores( table, score );
}
else if( ch->pcdata->mkills > 0 )
add_highscore( table, ch->name, ch->pcdata->mkills, false );
}
if( ( table = find_hightable( "MDeaths" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->mdeaths;
check_highscores( table, score );
}
else if( ch->pcdata->mdeaths > 0 )
add_highscore( table, ch->name, ch->pcdata->mdeaths, false );
}
if( ( table = find_hightable( "PKills" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->pkills;
check_highscores( table, score );
}
else if( ch->pcdata->pkills > 0 )
add_highscore( table, ch->name, ch->pcdata->pkills, false );
}
if( ( table = find_hightable( "PDeaths" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->pdeaths;
check_highscores( table, score );
}
else if( ch->pcdata->pdeaths > 0 )
add_highscore( table, ch->name, ch->pcdata->pdeaths, false );
}
if( ( table = find_hightable( "QCompleted" ) ) )
{
if( ( score = find_highscore( table, ch->name ) ) )
{
score->value = ch->pcdata->questcompleted;
check_highscores( table, score );
}
else if( ch->pcdata->questcompleted > 0 )
add_highscore( table, ch->name, ch->pcdata->questcompleted, false );
}
save_highscore( );
}
CMDF( do_highscore )
{
HIGHSCORE_DATA *score;
HIGHTABLE_DATA *table;
int cnt = 0, count = 0;
set_char_color( AT_HIGHSCORE, ch );
if( !argument || argument[0] == '\0' )
{
for( table = first_hightable; table; table = table->next )
{
count++;
ch_printf( ch, "&[hscore2]%20s&[hscore][&[hscore2]%2d&[hscore]]", table->name, show_highscore_count( table ) );
if( ++cnt >= 3 )
{
cnt = 0;
send_to_char( "\r\n", ch );
}
}
if( cnt != 0 )
send_to_char( "\r\n", ch );
if( !count )
send_to_char( "There are currently no highscore tables.\r\n", ch );
return;
}
if( !( table = find_hightable( argument ) ) )
{
send_to_char( "No such highscore table to look at.\r\n", ch );
send_to_char( "Checking highscore table to see if it matches someone in the lists.\r\n", ch );
count = 0;
for( table = first_hightable; table; table = table->next )
{
cnt = 0;
for( score = table->first_highscore; score; score = score->next )
{
cnt++;
if( str_cmp( score->name, argument ) )
continue;
count++;
ch_printf( ch, "&[hscore2]%s &[hscore]is in &[hscore2]%3d &[hscore]on highscore table &[hscore2]%s&[hscore].\r\n", score->name, cnt, table->name );
}
}
if( !count )
send_to_char( "Sorry it doesn't look like anyone matching that name is on the list.\r\n", ch );
return;
}
ch_printf( ch, "&[hscore]HighScore Table: &[hscore2]%s&[hscore].\r\n", table->name );
for( score = table->first_highscore; score; score = score->next )
ch_printf( ch, "&[hscore2]%3d&[hscore]> &[hscore2]%20s %10d\r\n", ++cnt, score->name, score->value );
if( !cnt )
send_to_char( "No one is currently on this highscore table.\r\n", ch );
}
CMDF( do_clearhightable )
{
HIGHTABLE_DATA *table;
HIGHSCORE_DATA *score, *score_next;
set_char_color( AT_HIGHSCORE, ch );
if( !argument || argument[0] == '\0' )
{
send_to_char( "Usage: clearhightable <name>\r\n", ch );
return;
}
if( !( table = find_hightable( argument ) ) )
{
send_to_char( "No such highscore table to clear.\r\n", ch );
return;
}
for( score = table->first_highscore; score; score = score_next )
{
score_next = score->next;
UNLINK( score, table->first_highscore, table->last_highscore, next, prev );
free_highscore( score );
}
save_highscore( );
send_to_char( "HighScore table cleared.\r\n", ch );
}
CMDF( do_makehightable )
{
HIGHTABLE_DATA *table;
set_char_color( AT_HIGHSCORE, ch );
if( !argument || argument[0] == '\0' )
{
send_to_char( "Usage: makehightable <name>\r\n", ch );
return;
}
if( !( table = add_hightable( argument ) ) )
{
send_to_char( "HighScore table wasn't created.\r\n", ch );
return;
}
save_highscore( );
send_to_char( "HighScore table created.\r\n", ch );
}
CMDF( do_sethightable )
{
HIGHTABLE_DATA *table, *new_table;
char arg[MIL];
set_char_color( AT_HIGHSCORE, ch );
if( !argument || argument[0] == '\0' )
{
send_to_char( "Usage: sethightable <table> max/name <setting>\r\n", ch );
send_to_char( "Usage: sethightable <table> reversed\r\n", ch );
return;
}
argument = one_argument( argument, arg );
if( !( table = find_hightable( arg ) ) )
{
send_to_char( "No such highscore table to set.\r\n", ch );
return;
}
argument = one_argument( argument, arg );
if( !str_cmp( arg, "max" ) )
{
short value;
if( !argument || argument[0] == '\0' )
{
send_to_char( "You need to specify a value between 1 and 100.\r\n", ch );
return;
}
value = atoi( argument );
if( value < 1 || value > 100 )
{
send_to_char( "You need to specify a value between 1 and 100.\r\n", ch );
return;
}
table->max = URANGE( 1, atoi( argument ), 100 );
save_highscore( );
ch_printf( ch, "The max for that table has been set to %d.\r\n", table->max );
return;
}
if( !str_cmp( arg, "reversed" ) )
{
table->reversed = !table->reversed;
save_highscore( );
if( table->reversed )
send_to_char( "That table will use reversed linking now.\r\n", ch );
else
send_to_char( "That table will use normal linking now.\r\n", ch );
return;
}
if( !str_cmp( arg, "name" ) )
{
if( !argument || argument[0] == '\0' )
{
send_to_char( "You can't set a hightable name to nothing.\r\n", ch );
return;
}
if( ( new_table = find_hightable( argument ) ) )
{
send_to_char( "There is already a hightable useing that name.\r\n", ch );
return;
}
STRSET( table->name, argument );
save_highscore( );
send_to_char( "The hightable name has been changed.\r\n", ch );
return;
}
}
CMDF( do_deletehightable )
{
HIGHTABLE_DATA *table;
set_char_color( AT_HIGHSCORE, ch );
if( !argument || argument[0] == '\0' )
{
send_to_char( "Usage: deletehightable <table>\r\n", ch );
return;
}
if( !( table = find_hightable( argument ) ) )
{
send_to_char( "No such highscore table to delete.\r\n", ch );
return;
}
UNLINK( table, first_hightable, last_hightable, next, prev );
free_hightable( table );
save_highscore( );
send_to_char( "HighScore table deleted.\r\n", ch );
}