/* Ratings Snippet by Aurora (EternalEmpress@LostProphecy.com)
 * Installation:
 * 
 * in mud.h find:
 * struct	pc_data
 * {
 * 
 * At the end of this structure add:
 *     int			score;	/* current player score */
 * 
 * Then find:
 * void	update_aris	args( ( CHAR_DATA *ch) );
 * And add:
 * void	calc_score	args( ( CHAR_DATA *ch) );
 * 
 * Then find:
 * DECLARE_DO_FUN(	do_rank	        );
 * and add:
 * DECLARE_DO_FUN(	do_ratings	        );
 * 
 * 
 * Next In act_comm.c find:
 *     update_aris(ch);     /* update char affects and RIS */
 * And after it add:
 *     calc_score(ch);     /* update char ratings score */
 * 
 * 
 * In tables.c you'll need to hunt out 
 *	if ( !str_cmp( name, "do_rank" ))		return do_rank;	
 * and add:
 * 	if ( !str_cmp( name, "do_ratings" ))		return do_ratings;	
 * Then:
 *     if ( skill == do_rank )		return "do_rank";
 * and add:
 *     if ( skill == do_ratings )		return "do_ratings";
 * 
 * Finally you will need to add this file to your makefile. Add it as:
 * ratings.o in the O_FILES and ratings.c in the C_FILES
 * 
 * Once all changes are made you will require a make clean or quits will crash you.
 * After rebooting use cedit to add ratings: cedit ratings add do_ratings
 * Whenever a player (not imm) typs save they're rating will adjust.
 * 
 * Copyright: This file retains no copyright. I release it into public domain.
 * You can give me credit for it if you want, or not if you dont. 
 */


#include "mud.h"

typedef struct	score_data		SCORE_DATA;
struct score_data
{
	char 		*name;
	int 		score;
	SCORE_DATA 	*next;
	SCORE_DATA 	*prev;
};

SCORE_DATA * first_score;
SCORE_DATA * last_score;
#define MAX_SCORES 10  /* variable for easy adjustment of how many to display */

/* local functions */
void	replace_score	args( ( CHAR_DATA *ch) );
bool	valid_player	args( ( char *name) );
void	fix_score_length	args( ( ) );
int	count_scores	args( ( ) );

/* This is the function that actually tallies a persons score and sets it up
to add it to the list, be sure to add it to do_save so that saving will adjust
the players scores */

void calc_score(CHAR_DATA *ch)
{
   int score=0;

   if (IS_NPC(ch)) /* Mobiles dont have pcdata */
    return;   

   if (IS_IMMORTAL(ch)) /* Dont want to clutter your table with imms. */
    return;   

   /* level formula */
   score += ch->level; /*  adds 1 to 50 */

   score += ch->hitroll; 
   score += ch->damroll;
   score -= ch->armor;  
/* note this subtracts ac values. This is only a penalty if they have a positive ac,
which is considered a bad thing. -300 is the av ac generally... If you subtract 
negative 300 from a value thats the equivalent of +300, this one in particular you'll 
be needing to adjust to suit your own mud and desires */

   score += ch->mod_str/5; /* (Adds 1 to 5) */
   score += ch->mod_int/5;
   score += ch->mod_wis/5;
   score += ch->mod_dex/5;
   score += ch->mod_con/5;
   score += ch->mod_cha/5;
   score += ch->mod_lck/5;

   score += ch->gold; 
/* This could theoretically may return a waaay high score. 
instead it would be best to assign an amount based on ranges such as:

  if ( ch->gold > 1000 && ch->gold < 10000)
   score += 5;
  else if ( ch->gold > 10000 && ch->gold < 100000)
   score += 10;
  else if ( ch->gold > 100000 && ch->gold < 1000000)
   score += 15;
  else if (ch->gold > 1000000)
   score += 20;
*/

 /* Now that we've calculated the score its time to set it */
  ch->pcdata->score = score;
  replace_score(ch);  /* use the data we just collected */
  return;
}

/* The actual insertion of a new score */

void add_score( CHAR_DATA *ch)
{
 int value = ch->pcdata->score;
 SCORE_DATA *score, *newscore;
 int i;

 if (IS_NPC(ch))
  return;

	for( i=1, score = first_score ; i <= MAX_SCORES ; score = score->next, i++ )
	{
		if( !score)
		/* there are empty slots at end of list, add there */
		{
			CREATE( newscore, SCORE_DATA, 1 );
			newscore->name = STRALLOC( ch->name );
			newscore->score = value;
			LINK( newscore, first_score, last_score, next, prev );
			break;
		}
/* This section inserts the higher value into the higher slot */
		else if( value > score->score )
		{
			CREATE( newscore, SCORE_DATA, 1 );
			newscore->name = STRALLOC( ch->name );
			newscore->score = value;
			INSERT( newscore, score, first_score, next, prev );
			break;
		}
	}

 fix_score_length();
 return;
}

/* This is used to determine ensure an existing score is removed before a new one
is added, so that you dont have repeats and so if a persons score drops, its actually
changed instead of staying forever at its highest point */

void replace_score( CHAR_DATA *ch)
{
 SCORE_DATA *score;

 if (IS_NPC(ch))
  return;

        for ( score = first_score; score; score = score->next )
         if( !str_cmp( ch->name, score->name ) )
          break;

   if (!score || (score->name != ch->name))
   {
     add_score(ch);
     return;
   }

   UNLINK( score, first_score, last_score, next, prev );
   STRFREE( score->name );
   DISPOSE( score );

   add_score(ch);
   return;
}

/* This will make sure it stays at only the max you want to display 
by checking to see how many total there are and cutting off the last one until
there are only the amount you want, default is 10 */

void fix_score_length( )
{
  SCORE_DATA *score;
  sh_int x;

  x = count_scores();

	while( x > MAX_SCORES )
	{
		score = last_score;
		UNLINK( score, first_score, last_score, next, prev );
		STRFREE( score->name );
		DISPOSE( score );
                x = count_scores();
	}
}

/* very simple function to count how many scores are in memory */

int count_scores()
{
  SCORE_DATA *score;
  sh_int x=0;

        for ( score= first_score; score; score= score->next )
         ++x;
 return x;
         
}

/* the actual command to see whos where */

void do_ratings( CHAR_DATA *ch, char *argument )
{
    SCORE_DATA *score;
    sh_int counter=1;

    ch_printf( ch, "     The Top 10 Players.\n\r");
    ch_printf( ch, "      Name        Score\n\r");
    ch_printf( ch, "-------------------------------\n\r");
        for ( score= first_score; score; score= score->next )
        {
          ch_printf( ch, "[%2d] %-12s %-32d\n\r", counter, score->name, score->score);
          ++counter;
        }
    ch_printf( ch, "-------------------------------\n\r");
    return;
}


/*
 *  Additions to make persist over reboots.
 * in db.c look for: void	load_corpses	args( ( void ) );   and add:
 * void    load_scores     args( ( void ) );
 *
 * Then find: init_area_weather();
 * and add load_scores();  after it.
 * This will setup the routines to load you scores.dat when the mud opens.
 *
 * To make this section work you will need to place a call to save_scores()
 * just after fix_score_length in add_score, ie:
 * fix_score_length();
 * save_scores();
 *
 */

#define SCORES_FILE	SYSTEM_DIR "scores.dat"  /* File to store the scores */
/* local function for save_scores */
void	save_scores	args( ( ) );

    

void load_scores( )
{
   FILE *fp;
   SCORE_DATA *score;
   

   if( ( fp = fopen( SCORES_FILE, "r" ) ) == NULL )
   {
      bug( "Cannot open scores.dat for reading", 0 );
      return;
   }
   
   for( ; ; )
   {
      char *word;
      
      word = fread_word( fp );
      
      if( !str_cmp( word, "Score" ) )
      {
         CREATE( score, SCORE_DATA, 1 );
         score->score = fread_number( fp );
         score->name = fread_string( fp );
         LINK( score, first_score, last_score, next, prev );
         continue;
      }
         
      if( !str_cmp( word, "End" ) )
      {
         fclose( fp );
         return;
      }
  }
   
}

void save_scores( )
{
   SCORE_DATA *score;
   FILE		*fp;
   
   if ( ( fp = fopen( SCORES_FILE, "w" ) ) == NULL )
   {
      bug( "Cannot open scores.dat for writing", 0 );
      perror( SCORES_FILE );
      return;
   }

   for( score = first_score; score; score = score->next )
    if (!valid_player(score->name))
     continue;
    else
     fprintf( fp, "Score %d %s~\n", score->score, score->name );

   fprintf( fp, "End\n\n" );
   fclose( fp );
}

bool valid_player( char *name)
{
   char strsave[MAX_INPUT_LENGTH];
   FILE *fp = NULL;

   sprintf( strsave, "%s%c/%s", PLAYER_DIR, tolower(name[0]), capitalize( name ) );
   if ( ( fp = fopen( strsave, "r" ) ) != NULL ) ;
   else /* pfile gone */
   return FALSE;   

   sprintf( strsave, "%s%s", GOD_DIR, capitalize( name) );
   if ( ( fp = fopen( strsave, "r" ) ) != NULL ) /* Uhoh its an imm */
    return FALSE;   
   else ;
 
   return TRUE;
}

