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