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.                                *
 *---------------------------------------------------------------------------*
 *                              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;
};

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 );
      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 )
{
   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( nscore->value > score->value )
      {
         if( 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( 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( 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( 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;
   LINK( table, first_hightable, last_hightable, next, prev );
   return table;
}

void add_highscore( HIGHTABLE_DATA *table, char *name, int value )
{
   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 );
}

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;
         continue;
      }
      else if( !str_cmp( word, "Max" ) )
      {
         value = fread_number( fp );
         if( !table )
            continue;
         table->max = value;
      }
      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 );
         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( 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, 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 );
   }
}

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

   argument = capitalize( argument );
   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_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 );
}