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 <stdio.h>
#include <string.h>
#include <math.h>
#include "h/mud.h"

/* Calculate roughly how much experience a character is worth */
double get_exp_worth( CHAR_DATA *ch )
{
   double wexp;
   int ulevel = urange( 1, ch->level, MAX_LEVEL );

   wexp = ( ulevel * ulevel * ulevel );
   return durange( MIN_EXP_WORTH, wexp, MAX_EXP_WORTH );
}

double exp_level( CHAR_DATA *ch, int level )
{
   double lvl;

   lvl = durange( 1, level, MAX_LEVEL );
   return ( 20 + ( lvl * lvl * lvl ) );
}

double xp_compute( CHAR_DATA *gch, CHAR_DATA *victim )
{
   int ldiff;
   double xp;

   ldiff = ( gch->level - victim->level );

   /* if there is more then a 10 level difference either way return 0 */
   if( ldiff > 10 || ldiff < -10 )
      return 0;

   xp = get_exp_worth( victim );

   /* This will take the level difference and increase or decrease exp accordingly */
   if( xp > 0 && ldiff != 0 )
   {
      if( ldiff < 0 )
      {
         ldiff = ( 0 - ldiff );
         xp *= ldiff;
      }
      else
         xp /= ldiff;
   }

   if( !is_npc( gch ) && is_npc( victim ) )
   {
      int times = times_killed( gch, victim );

      if( times >= 20 )
         xp = 0;
      else if( times )
      {
         xp = ( xp * ( 20 - times ) ) / 20;
         if( times > 15 )
            xp /= 3;
         else if( times > 10 )
            xp /= 1.5;
      }
   }

   /* No more then half the exp you needed to level gained off 1 kill */
   return durange( 0, xp, ( exp_level( gch, gch->level ) / 2 ) );
}

void update_level( CHAR_DATA *ch )
{
   MCLASS_DATA *mclass;
   int mcount = 0, mlevel = 0, oldlevel = 0;

   if( !ch || is_npc( ch ) )
      return;
   for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
   {
      mcount++;
      mlevel += mclass->level;
   }
   if( mcount && mlevel )
   {
      oldlevel = ch->level;
      ch->level = ( mlevel / mcount );

      if( ch->level > ch->perm_stats[STAT_CHA] )
         ch->perm_stats[STAT_CHA] = ch->level; /* Perm charisma should always be equal to level, but if higher let it be */
      if( ch->level > oldlevel )
      {
         /* Chance of stat increases */
         if( number_percent( ) > 60 )
            ch->max_hit = UMAX( ch->max_hit, ( ch->max_hit + number_range( 4, 12 ) ) );
         if( number_percent( ) > 60 )
            ch->max_move = UMAX( ch->max_move, ( ch->max_move + number_range( 2, 8 ) ) );
         if( number_percent( ) > 60 )
            ch->max_mana = UMAX( ch->max_mana, ( ch->max_mana + number_range( 2, 8 ) ) );
      }
   }
}

/* Advancement stuff. */
void advance_level( CHAR_DATA *ch )
{
   if( !ch || is_npc( ch ) )
      return;
   ch->practice += 1;

   update_level( ch );

   /* Restore them if needed */
   if( ch->hit < ch->max_hit )
      ch->hit = ch->max_hit;
   if( ch->mana < ch->max_mana )
      ch->mana = ch->max_mana;
   if( ch->move < ch->max_move )
      ch->move = ch->max_move;
}

void gain_exp( CHAR_DATA *ch, double gain )
{
   MCLASS_DATA *mclass;
   double modgain, oldexp = 0.0, gained = 0.0, umodgain, increase;
   short upercent = 0;
   bool nauthed = false;

   if( !ch )
      return;

   if( gain > 0 )
      modgain = ( gain * sysdata.expmulti );
   else
      modgain = gain;

   /* Handle npcs and anyone with no class */
   if( !ch->pcdata || !ch->pcdata->first_mclass )
   {
      if( ch->level >= MAX_LEVEL )
      {
         ch->exp = 0.0;
         return;
      }
      umodgain = dumin( modgain, exp_level( ch, ch->level ) );
      oldexp = ch->exp;
      increase = umodgain;
      upercent = 100;
      ch->exp = dumax( 0, ch->exp + ( int ) increase );
      gained += ( ch->exp - oldexp );
      if( !is_npc( ch ) && not_authed( ch ) )
      {
         if( ch->exp >= exp_level( ch, ch->level ) )
         {
            ch->exp = ( exp_level( ch, ch->level ) - 1 );
            nauthed = true;
         }
      }
      else
      {
         if( ch->level < MAX_LEVEL && ch->exp >= exp_level( ch, ( ch->level + 1 ) ) )
         {
            set_char_color( AT_WHITE + AT_BLINK, ch );
            ch->level += 1;
            ch->exp -= exp_level( ch, ch->level );
            if( gained != 0 )
            {
               ch_printf( ch, "You %s %s experience points.\r\n", gained > 0 ? "received" : "lost", double_punct( fabs( gained ) ) );
               gained = 0.0;
            }
            ch_printf( ch, "You have obtained level %d!\r\n", ch->level );
            if( ch->level >= MAX_LEVEL )
               ch->exp = 0.0;
            advance_level( ch );
            set_char_color( AT_PLAIN, ch );
         }
      }
   }
   else
   {
      for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
      {
         if( mclass->level >= MAX_LEVEL )
         {
            mclass->exp = 0.0;
            continue;
         }
         umodgain = dumin( modgain, exp_level( ch, mclass->level ) );
         oldexp = mclass->exp;

         upercent += mclass->cpercent;
         if( modgain > 0 )
            increase = ( ( umodgain * mclass->cpercent ) / 100 );
         else
            increase = umodgain;

         mclass->exp = dumax( 0, ( mclass->exp + ( int ) increase ) );
         gained += ( mclass->exp - oldexp );
         if( not_authed( ch ) )
         {
            if( mclass->exp >= exp_level( ch, mclass->level ) )
            {
               mclass->exp = ( exp_level( ch, mclass->level ) - 1 );
               nauthed = true;
            }
         }
         else
         {
            if( mclass->level < MAX_LEVEL && mclass->exp >= exp_level( ch, ( mclass->level + 1 ) ) )
            {
               set_char_color( AT_WHITE + AT_BLINK, ch );
               mclass->level += 1;
               mclass->exp -= exp_level( ch, mclass->level );
               if( gained != 0 )
               {
                  ch_printf( ch, "You %s %s experience points.\r\n", gained > 0 ? "received" : "lost", double_punct( fabs( gained ) ) );
                  gained = 0.0;
               }
               ch_printf( ch, "You have obtained level %d in %s!\r\n", mclass->level, dis_class_name( mclass->wclass ) );
               if( mclass->level >= MAX_LEVEL )
                  mclass->exp = 0.0;
               advance_level( ch );
               set_char_color( AT_PLAIN, ch );
            }
         }
      }
   }

   if( nauthed )
   {
      send_to_char( "You can't ascend to a higher level until you are authorized.\r\n", ch );
      return;
   }

   if( upercent == 0 && gain > 0 )
      send_to_char( "You can't gain any exp because all of your class percent gains are set to 0.\r\n", ch );

   if( gained != 0 )
      ch_printf( ch, "You %s %s experience points.\r\n", gained > 0 ? "received" : "lost", double_punct( fabs( gained ) ) );
}

/* Display your current exp, level, and surrounding level exp requirements - Thoric */
CMDF( do_level )
{
   MCLASS_DATA *mclass;
   const char *s1, *s2;

   s1 = color_str( AT_SCORE, ch );
   s2 = color_str( AT_SCORE2, ch );

   ch_printf( ch, "%s.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.\r\n", s1 );
   for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
   {
      ch_printf( ch, "%s|%s%12.12s%s|%s%3d%s|", s1, s2, dis_class_name( mclass->wclass ), s1, s2, mclass->level, s1 );
      if( mclass->level < MAX_LEVEL )
      {
         ch_printf( ch, "%sCurrent: %s%12s%s|", s1, s2, double_punct( mclass->exp ), s1 );
         ch_printf( ch, "%sStill Need: %s%12s%s|%s%3d%s|", s1, s2, double_punct( exp_level( ch, mclass->level + 1 ) - mclass->exp ), s1,
            s2, mclass->cpercent, s1 );
      }
      else
         send_to_char( "                                                  |", ch );
      send_to_char( "\r\n", ch );
   }
   ch_printf( ch, "%s.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.\r\n", s1 );
}

CMDF( do_classpercent )
{
   MCLASS_DATA *mclass, *tmclass = NULL;
   char arg[MSL];
   int tmpcount = 0, mcount = 0;

   if( !ch || is_npc( ch ) )
      return;
   argument = one_argument( argument, arg );
   if( arg == NULL || arg[0] == '\0' || !argument || argument[0] == '\0' || !is_number( argument ) )
   {
      send_to_char( "Usage: classpercent <class> <percent>\r\n", ch );
      return;
   }
   if( ( tmpcount = atoi( argument ) ) < 0 || tmpcount > 100 )
   {
      send_to_char( "A valid percent is 0 to 100.\r\n", ch );
      return;
   }
   for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
   {
      if( !str_cmp( dis_class_name( mclass->wclass ), arg ) )
      {
         mcount = mclass->cpercent;
         mclass->cpercent = tmpcount;
         tmclass = mclass;
         break;
      }
   }
   if( !tmclass )
   {
      send_to_char( "No such class to change the percent on.\r\n", ch );
      return;
   }
   tmpcount = 0;
   for( mclass = ch->pcdata->first_mclass; mclass; mclass = mclass->next )
   {
      tmpcount += mclass->cpercent;
      if( tmpcount < 0 || tmpcount > 100 )
      {
         tmclass->cpercent = mcount; /* Set it back to what it was */
         send_to_char( "Sorry, but the percents on the classes can't go over 100% combined.\r\n", ch );
         return;
      }
   }
   ch_printf( ch, "%s percent has been set to %d.\r\n", dis_class_name( tmclass->wclass ), tmclass->cpercent );
}