dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// skills.cpp - gain, train, prac etc
/***************************************************************************
 * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt                    *
 * >> A number of people have contributed to the Dawn codebase, with the   *
 *    majority of code written by Michael Garratt - www.dawnoftime.org     *
 * >> To use this source code, you must fully comply with all the licenses *
 *    in licenses.txt... In particular, you may not remove this copyright  *
 *    notice.                                                              *
 ***************************************************************************
 * >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer,       *
 *    Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe.   *
 * >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael       *
 *    Chastain, Michael Quan, and Mitchell Tse.                            *
 * >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to   *
 *    you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com),         *
 *    Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) *
 * >> Oblivion 1.2 is copyright 1996 Wes Wagner                            *
 **************************************************************************/
#include "include.h" // dawn standard includes

#include "magic.h"
#include "d2magsys.h"
#include "math.h"
#include "nanny.h"

// command procedures needed
DECLARE_DO_FUN(do_skillgroups);
DECLARE_DO_FUN(do_help		);
DECLARE_DO_FUN(do_say		);
DECLARE_DO_FUN(do_spinfo	);
DECLARE_DO_FUN(do_cspinfo	);
DECLARE_DO_FUN(do_cskinfo	);

/**************************************************************************/
// used to get new skills
void do_gain(char_data *ch, char *argument)
{
    char arg[MIL];
    char_data *trainer;
    int gn = 0, sn = 0;
	
    if (IS_NPC(ch))
		return;
	
    // find a trainer
    for ( trainer = ch->in_room->people; trainer; trainer = trainer->next_in_room)
	{
		if (IS_NPC(trainer) && IS_SET(trainer->act,ACT_GAIN))
			break;
	}

	if (trainer == NULL || !can_see(ch,trainer))
	{
		ch->println( "You can't do that here." );
		return;
	}
	
	one_argument(argument,arg);
	
	if (arg[0] == '\0')
	{
		do_say(trainer,"Pardon me?");
		return;
	}
	
	if (!str_prefix(arg,"list"))
	{
		int col;
		
		col = 0;
		

		ch->printlnf("%-28s %-5s    %-28s %-5s",
			"skillgroup","cost","skillgroup","cost");
				
		// groups up below
		for (gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
		{
			if (!ch->pcdata->skillgroup_known[gn]
				&&  IS_SKILLGROUP_SELECTABLE_FOR_CHAR(gn, ch))
			{
				ch->printf("%s%-28s %-5d    ",
					skillgroup_table[gn].rating[ch->clss]>ch->train?"`S":"`x",
					skillgroup_table[gn].name, 
					skillgroup_table[gn].rating[ch->clss]);
				if (++col % 2 == 0){
					ch->print_blank_lines(1);
				}
			}
		}
		if (col % 2 != 0)
		{
			ch->println("");
		}
		// skillgroups up above
		
		ch->println("`x");
		
		// skills below
		col = 0;   
		ch->print("`?`#`Yskill `^=============="
			"`Ycost`^==`Ylevel`^=======");
		ch->println("`Yskill`^==============`Ycost" 
			"`^==`Ylevel`x");
		for (sn = 0; sn < MAX_SKILL; sn++)
		{
			if (IS_NULLSTR(skill_table[sn].name)){
				break;
			}

			if(IS_SET(skill_table[sn].flags,SKFLAGS_NO_GAIN)){
				continue;
			}
			
			if( IS_SET(skill_table[sn].flags,SKFLAGS_USE_RACE_RESTRICTIONS)
				&& !IS_SETn(skill_table[sn].race_restrict_n, ch->race))
			{
				continue;
			}
			
			if (!ch->pcdata->learned[sn]
				&&  skill_table[sn].rating[ch->clss] > 0
				&&  skill_table[sn].skill_level[ch->clss]< LEVEL_IMMORTAL
				&&  skill_table[sn].skill_level[ch->clss]>0
				&&  skill_table[sn].spell_fun == spell_null)
			{
				ch->printf("%s%-18s %3d  %4d         ",
					skill_table[sn].rating[ch->clss]>ch->train?"`S":"`x",
					skill_table[sn].name,
					skill_table[sn].rating[ch->clss],
					skill_table[sn].skill_level[ch->clss]);
				if (++col % 2 == 0)
					ch->print_blank_lines(1);
			}
		}
		
		if (col % 2 != 0){
			ch->println("`x");
		}else{
			ch->print("`x");
		}
		return;
		// skills above
	}// gain list
	
	if ( ch->position < POS_STANDING )
	{
		ch->println( "You need to stand first." );
		return;
		
	}
	
	
	if (!str_prefix(arg,"convert"))
	{
		if (ch->practice < 10)
		{
			act("$N tells you 'You are not yet ready.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		act("$N helps you apply your practice to training",
			ch,NULL,trainer,TO_CHAR);
		ch->practice -= 10;
		ch->train++;
		return;
	}

	if (!str_prefix(arg,"revert"))
	{
		if (ch->train < 1)
		{
			act("$N tells you 'You are not yet ready.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}

		if(ch->level<10)
		{
			ch->println("You can't revert trains before level 10.");
			return;
		}
		
		act("$N helps you apply your practice to training",
			ch,NULL,trainer,TO_CHAR);
		ch->practice+=10;
		ch->train--;
		return;
	}
	
	if (!str_prefix(arg,"points"))
	{
		if (ch->train < 2)
		{
			act("$N tells you 'You are not yet ready.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (ch->pcdata->points <= 40)
		{
			act("$N tells you 'There would be no point in that.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		act("$N trains you, and you feel more at ease with your skills.",
			ch,NULL,trainer,TO_CHAR);
		
		// record how much xp they have beyond their current level
		int beyond=UMAX(0, ch->exp - (exp_per_level(ch,ch->pcdata->points) * ch->level));
		// reduce their creation points
		ch->train -= 2;
		ch->pcdata->points -= 1;
		ch->exp = exp_per_level(ch,ch->pcdata->points) * ch->level;
		// give them back the xp they already had on that given level
		if(beyond){
			gain_exp(ch, beyond);
		}
		return;
	}
	
	/* else add a group/skill */
	
	gn = skillgroup_lookup(argument);
	if (gn > 0)
	{
		if (ch->pcdata->skillgroup_known[gn])
		{
			act("$N tells you 'You already know that skillgroup!'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (skillgroup_table[gn].rating[ch->clss] <= 0)
		{
			act("$N tells you 'That skillgroup is beyond your powers.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (ch->train < skillgroup_table[gn].rating[ch->clss])
		{
			act("$N tells you 'You are not yet ready for that skillgroup.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}

		if (!IS_SKILLGROUP_SELECTABLE_FOR_CHAR(gn, ch)){
			ch->printlnf("%s tells you 'You aren't ready for the skillgroup '%s' at this point in time.'",
				PERS(trainer, ch), skillgroup_table[gn].name);
			return;
		}
		
		// add the group 
		gn_add(ch,gn);
		act("$N trains you in the art of $t",
			ch,skillgroup_table[gn].name,trainer,TO_CHAR);
		ch->train -= skillgroup_table[gn].rating[ch->clss];
		set_char_magic_bits(ch); // fix up their magic bits
		return;
	}
	
	sn = skill_lookup(argument);
	if (sn > -1)
	{
		if (skill_table[sn].spell_fun && skill_table[sn].spell_fun != spell_null)
		{
			act("$N tells you 'You must learn the full group.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if( IS_SET(skill_table[sn].flags,SKFLAGS_USE_RACE_RESTRICTIONS)
			&& !IS_SETn(skill_table[sn].race_restrict_n, ch->race))
		{
			act("$N tells you 'Your race can't learn that skill.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}

		if(IS_SET(skill_table[sn].flags,SKFLAGS_NO_GAIN)){
			act("$N tells you 'That skill can't be gained sorry.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (ch->pcdata->learned[sn])
		{
			act("$N tells you 'You already know that skill!'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (skill_table[sn].rating[ch->clss] <= 0
			|| skill_table[sn].skill_level[ch->clss] >= LEVEL_IMMORTAL
			|| skill_table[sn].skill_level[ch->clss]<=0)
		{
			act("$N tells you 'That skill is beyond your powers.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		if (ch->train < skill_table[sn].rating[ch->clss]*1)
		{
			act("$N tells you 'You are not yet ready for that skill.'",
				ch,NULL,trainer,TO_CHAR);
			return;
		}
		
		// add the skill 
		ch->pcdata->learned[sn] = 1;
		act("$N trains you in the art of $t",
			ch,skill_table[sn].name,trainer,TO_CHAR);
		ch->train -= skill_table[sn].rating[ch->clss]*1;
		set_char_magic_bits(ch); // fix up their magic bits
		return;
	}
	
	act("$N tells you 'I do not understand...'",ch,NULL,trainer,TO_CHAR);
}

/**************************************************************************/
char * creation_titlebar(char *fmt, ...);
/**************************************************************************/
void do_skills(char_data *ch, char *argument)
{
	char *skill_list[MAX_LEVEL+2];
    unsigned char skill_columns[MAX_LEVEL+2];
	char linebuf[MSL];
    int sn,lev;
    char arg[MIL];
    char_data *victim;
    char buf[MSL];
    BUFFER *output;
	int top_level=0;
    bool found = false;
	
	if (IS_UNSWITCHED_MOB(ch)){
		return;
	}
	
    one_argument( argument, arg );
	
    if (IS_IMMORTAL(ch) && !IS_NULLSTR(arg))
    {
		if ( ( victim = get_char_world( ch, arg ) ) == NULL )
		{		
			ch->println( "They aren't here." );
			return;
		}
    }
	else
	{
		victim=ch;
	}
	
    if (IS_NPC(victim))
	{
		ch->println( "Mobs dont have skill percentages as such." );
		return;
	}
	
	
    output = new_buf();
	{
		if(ch->desc && ch->desc->connected_state!=CON_PLAYING){
			if(ch==victim){
				sprintf(buf,"`=j%s", creation_titlebar("SKILLS"));
			}else{
				sprintf(buf,"`=j%s", creation_titlebar("SKILLS FOR %s", uppercase(victim->name)));
			}
		}else{
			if(ch==victim){
				sprintf(buf,"`=t%s", makef_titlebar("SKILLS"));
			}else{
				sprintf(buf,"`=t%s", makef_titlebar("SKILLS FOR %s", uppercase(victim->name)));
			}
		}

		buf[str_len(buf)-2]= '\0';
		strcat(buf,"`x");
	}
	add_buf( output, buf);

	if (ch!=victim)
	{
		sprintf(buf,"\r\nSkills for %s (Level: %d, Race: %s, Class: %s)",
			victim->name, 
			victim->level, 
			race_table[victim->race]->name, 
			class_table[victim->clss].name);
		add_buf( output, buf);
	}
	
    // initialize data
    for (lev = 0; lev < MAX_LEVEL+2; lev++){
        skill_columns[lev] = 0;
        skill_list[lev]= str_dup("");
    }
	
	if (IS_IMMORTAL(ch)){
		top_level= ABSOLUTE_MAX_LEVEL;
	}else{
		top_level= LEVEL_HERO;
	}
	
	bool specially_granted_skills=false;
    for (sn = 0; !IS_NULLSTR(skill_table[sn].name) ; sn++)
    {
		if (skill_table[sn].spell_fun == spell_null 
			&&	(
					(victim->pcdata->learned[sn]==101)
				  || 
					(skill_table[sn].skill_level[victim->clss]<=top_level
					&& skill_table[sn].skill_level[victim->clss]>0
					&& victim->pcdata->learned[sn] > 0)
				)
			)
		{
			if(victim->pcdata->learned[sn]==101 
				&& skill_table[sn].skill_level[victim->clss]>LEVEL_HERO)
			{
				specially_granted_skills=true;
			}
			
			found = true;
			lev = skill_table[sn].skill_level[victim->clss];
			if(IS_SET(skill_table[sn].flags,SKFLAGS_NEW_IMPROVE_SYSTEM)){
				if (victim->level < lev){
					sprintf(buf,"%-18s  n/a              ", capitalize(skill_table[sn].name));
				}else{
					sprintf(buf,"%-18s %3d%%(%3d%%,%3d%%)  ",capitalize(skill_table[sn].name),
						victim->pcdata->learned[sn], 
						skill_table[sn].learn_scale_percent[victim->clss],
						(skill_table[sn].learn_scale_percent[victim->clss]==0?
								100:skill_table[sn].learn_scale_percent[ch->clss]));
				}
			}else{
				if (victim->level < lev && victim->pcdata->learned[sn]!=101){
					if(ch->level<lev){
						sprintf(buf,"%-18s  n/a              ", capitalize(skill_table[sn].name));
					}else{
						sprintf(buf,"%-18s  n/a(%3d%%)       ", 
							capitalize(skill_table[sn].name),
							victim->pcdata->learned[sn]);
					}
				}else{
					sprintf(buf,"%-18s %3d%%             ",capitalize(skill_table[sn].name),
						victim->pcdata->learned[sn]);
				}
			}
			
			if (IS_NULLSTR(skill_list[lev])){
				sprintf(linebuf,"\r\n Level %2d: %s",lev,buf);
				replace_string(skill_list[lev], linebuf);				
			}else{ // append
				if ( ++skill_columns[lev] % 2 == 0){
					sprintf(linebuf,"%s\r\n           ",skill_list[lev]);
					replace_string(skill_list[lev], linebuf);
				}else{
					// trim off the extra spaces
					strcpy(linebuf, skill_list[lev]);
					linebuf[str_len(linebuf)-6]='\0';
					replace_string(skill_list[lev], linebuf);
				}
				sprintf(linebuf,"%s%s",skill_list[lev],buf);
				replace_string(skill_list[lev], linebuf);
			}
		}
    }

	if (IS_IMMORTAL(ch) || specially_granted_skills){
		top_level= ABSOLUTE_MAX_LEVEL;
	}else{
		top_level= LEVEL_HERO;
	}
	
	
    // return results
    if (!found){
		if (ch==victim){
			ch->println( "You know no skills." );
		}else{
			ch->println( "They know no skills." );
		}
    }else{
		for (lev = 1; lev < top_level; lev++){
			if(lev==LEVEL_IMMORTAL){
				if(IS_IMMORTAL(ch)){
					sprintf(buf, "\r\n\r\n%s", makef_titlebar("SKILLS DISABLED FOR CHARACTER UNLESS GRANTED BELOW HERE"));
				}else{
					sprintf(buf, "\r\n\r\n%s", makef_titlebar("SPECIALLY GRANTED SKILLS"));
				}
				buf[str_len(buf)-2]= '\0';
				strcat(buf,"`x");				
				add_buf( output, buf);
			}
			if (!IS_NULLSTR(skill_list[lev])){
				add_buf( output, skill_list[lev] );
			}
		}
		add_buf( output, "\r\n");
		ch->sendpage(buf_string(output));
		free_buf(output);
	}

	// deallocate all memory used
	for (lev = 0; lev < MAX_LEVEL+2; lev++){
        free_string(skill_list[lev]);
    }
	return;
}

/****************************************************************************/
// Kal - Dec 00
void do_cskinfo(char_data *ch, char * argument)
{
	bool found = false;
	char buf[MSL], clss_name[MSL];
	int sn, count, lev;
	BUFFER *output;

	int clss_no;

	if (IS_NULLSTR(argument))
	{
		ch->wrapln( "CSKINFO - Class Skill Info - at what levels "
			"a particular class gets a skill and for how many trains.");
		ch->println( "Syntax: CSKINFO <class>" );
		return;
	}

	argument = one_argument (argument, clss_name);

	for (clss_no = 0; !IS_NULLSTR(class_table[clss_no].name); clss_no++)
	{
		if (!str_cmp(clss_name, class_table[clss_no].short_name) ||
            !str_prefix(clss_name,class_table[clss_no].name))
		{
			found = true;
			break;
		}
	}

	if ( !found )
	{
		ch->printlnf( "No class named '%s' exists. Use the 3-letter WHO names (Mag, Spf etc.)", clss_name);
		do_cskinfo(ch, "");
		return;
	}

	// setup a buffer for info to be displayed
	output	= new_buf();
	count	= 0;

	sprintf(buf,"Class Skill Info - all skills for `B%ss`x\r\n", class_table[clss_no].name);
	add_buf( output, buf);
	add_buf( output, "==Skill==========Level [Trains]==========");
	add_buf( output, "==Skill==========Level [Trains]==\r\n");

	for (lev=1; lev<=LEVEL_HERO; lev++)
	{      
		for (sn = 0; !IS_NULLSTR(skill_table[sn].name) ; sn++)
		{
			if ( skill_table[sn].skill_level[clss_no]==lev
				&& skill_table[sn].spell_fun == spell_null )
			{
				count++;
				sprintf(buf, "  %-20.20s %3d [%2d]", capitalize(skill_table[sn].name), 
					lev, skill_table[sn].rating[clss_no]);
				add_buf( output, buf);
				if(count%2==0){
					add_buf( output, "\r\n"); 
				}else{
					add_buf( output, "          "); 
				}
			}
		}
	}	
	sprintf(buf, "%s  %d skill%s total.", 
		count%2==1?"\r\n":"",
		count, 
		count==1?"":"s");
	add_buf( output, buf); 

	ch->sendpage(buf_string(output));
	free_buf(output);
	return;
}
/**************************************************************************/
// shows skills, groups and costs (only if not bought) 
void list_group_costs(char_data *ch)
{
    if (IS_NPC(ch))
        return;
	
    int gn,sn,col;
	
    col = 0;
    ch->print(" `g===`MSkillgroup`g==================`xCP`g====");
    ch->println("====`MSkillgroup`g==================`xCP`g====");

    for (gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
    {
        if (!ch->gen_data->skillgroup_chosen[gn] 
			&&  !ch->pcdata->skillgroup_known[gn]
			&&  IS_SKILLGROUP_SELECTABLE_FOR_CHAR(gn, ch))
        {
			ch->printf("   `S[`M%-28.28s`x%2d`S]`x   ",
				skillgroup_table[gn].name, 
				skillgroup_table[gn].rating[ch->clss]);
            if (++col % 2 == 0){
				ch->println("");
			}
        }
    }
    if ( col % 2 != 0 ){
        ch->println( "" );
	}
	ch->println( "" );
	
    col = 0;
	
    ch->print(" `g=`YSkill`g===========`xCP`g`BLevel`g=");
    ch->print("`g=`YSkill`g===========`xCP`g`BLevel`g=");
    ch->println("`g=`YSkill`g===========`xCP`g`BLevel`g=");
	
    for (sn = 0; !IS_NULLSTR(skill_table[sn].name); sn++)
	{
		
		// race restrictions
		// race restrictions
		if( IS_SET(skill_table[sn].flags,SKFLAGS_USE_RACE_RESTRICTIONS)
				&& !IS_SETn(skill_table[sn].race_restrict_n, ch->race))
		{
			continue;
		}
		
		// no_gain
		if(IS_SET(skill_table[sn].flags,SKFLAGS_NO_GAIN))
		{
			continue;
		}

        if (!ch->gen_data->skill_chosen[sn] 
			&&  ch->pcdata->learned[sn] == 0
			&&  skill_table[sn].spell_fun == spell_null
			&&  skill_table[sn].rating[ch->clss] > 0
			&&  skill_table[sn].skill_level[ch->clss] < LEVEL_IMMORTAL
			&&  skill_table[sn].skill_level[ch->clss]>0)
        {
            ch->printf(" `S[`Y%-16.16s`x%2d`B<%2d>`S]`x",
				skill_table[sn].name,  
                skill_table[sn].rating[ch->clss],
                skill_table[sn].skill_level[ch->clss]);
            if (++col % 3 == 0)
                ch->print( "\r\n" );
        }
    }
    if ( col % 3 != 0 )
        ch->print( "\r\n" );

    ch->println( "`x\r\n" );
	
    ch->printlnf( "Creation points (CP): %3d    Experience per level (XP): %d",
		ch->gen_data->points_chosen, exp_per_level(ch,ch->gen_data->points_chosen));
	
    return;
	
}
/**************************************************************************/
void list_group_chosen(char_data *ch)
{
    int gn,sn,col;
	
    col = 0;
    ch->print(" `g===`MSkillgroup`g==================`xCP`g====");
    ch->println("====`MSkillgroup`g==================`xCP`g====");

    for (gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
    {
        if (ch->gen_data->skillgroup_chosen[gn])
        {
			ch->printf("   `S[`M%-28.28s`x%2d`S]`x   ",
				skillgroup_table[gn].name, 
				skillgroup_table[gn].rating[ch->clss]);
            if (++col % 2 == 0){
				ch->println("");
			}
        }
    }
    if ( col % 2 != 0 ){
        ch->println( "" );
	}
	ch->println( "" );

	
	col=0;
    ch->print(" `g=`YSkill`g===========`xCP`g`BLevel`g=");
    ch->print("`g=`YSkill`g===========`xCP`g`BLevel`g=");
    ch->println("`g=`YSkill`g===========`xCP`g`BLevel`g=");
	
    for (sn = 0; !IS_NULLSTR(skill_table[sn].name); sn++)
    {
        if (ch->gen_data->skill_chosen[sn] 
			&&  skill_table[sn].rating[ch->clss] > 0)
        {
            ch->printf(" `S[`Y%-16.16s`x%2d`B<%2d>`S]`x",
				skill_table[sn].name,  
                skill_table[sn].rating[ch->clss],
                skill_table[sn].skill_level[ch->clss]);
            if (++col % 3 == 0)
                ch->print( "\r\n" );
        }
    }
    if ( col % 3 != 0 )
        ch->print( "" );

    ch->println( "`x\r\n" );
	
    ch->printlnf( "Creation points (CP): %3d    Experience per level (XP): %d",
		ch->gen_data->points_chosen, exp_per_level(ch,ch->gen_data->points_chosen));
    return;
}
/**************************************************************************/
int exp_per_level(char_data *ch, int points)
{
    int expl,inc;
	
    if (IS_NPC(ch)){
		return 1000;
	}
	
    expl = 1000;
    inc = 500;
	
	
	if(race_table[ch->race]->class_exp[ch->clss]<1000){ // safety check
		ch->println("BUG: exp_per_level for your race class combination is below 1000... please report this to the immortals!");
		return 5000; 
	}
	
    if (points < 40){
        return race_table[ch->race]->class_exp[ch->clss];
	}
	
    // processing 
    points -= 40;
	
    while (points > 9)
    {
		expl += inc;
		points -= 10;
		if (points > 9)
		{
			expl += inc;
			inc *= 2;
			points -= 10;
		}
    }
    expl += points * inc / 10;  
	
    return UMAX(1000, expl * race_table[ch->race]->class_exp[ch->clss]/1000);
}
/**************************************************************************/
// this procedure handles the input parsing for the skill generator
bool parse_gen_groups(char_data *ch,char *argument)
{
    char arg[MIL];
    int gn,sn,i;
	
    if (IS_NULLSTR(argument)){
		return false;
	}
	
    argument = one_argument(argument,arg);
	
    if (!str_prefix(arg,"help"))
    {
		if (argument[0] == '\0')
		{
			do_help(ch,"group help");
			return true;
		}
		
        do_help(ch,argument);
		return true;
    }
	
    if (!str_prefix(arg,"add"))
    {
		if (IS_NULLSTR(argument))
		{
			ch->println( "You must provide a skill name." );
			return true;
		}
		
		// can't take all the skills to over load the size on an int
		if (ch->pcdata->points>150)
		{
			ch->println( "There is a limit of 150 creation points..." );
			ch->println( "you must remove other skills/groups before adding any more." );
			return true;
		}
		
		gn = skillgroup_lookup(argument);
		if (gn != -1)
		{
			if (ch->gen_data->skillgroup_chosen[gn]
				||  ch->pcdata->skillgroup_known[gn])
			{
				ch->printlnf("You already the skillgroup '%s'.",  
					skillgroup_table[gn].name);
				return true;
			}
			
			if (  !IS_SKILLGROUP_SELECTABLE_FOR_CHAR(gn, ch))
			{
				ch->printlnf( "The skillgroup '%s' is not available to you.", 
					skillgroup_table[gn].name );
				return true;
			}

			// can't take all the skills to over load the size on an int
			if (ch->pcdata->points + skillgroup_table[gn].rating[ch->clss]>150)
			{
				ch->println( "There is a limit of 150 creation points..." );
				ch->println( "you must remove other skills/groups before adding this group." );
				return true;
			}
			
			ch->printlnf( "%s skillgroup added.", skillgroup_table[gn].name);
			
			ch->gen_data->skillgroup_chosen[gn] = true;
			ch->gen_data->points_chosen += skillgroup_table[gn].rating[ch->clss];
			gn_add(ch,gn);
			ch->pcdata->points += skillgroup_table[gn].rating[ch->clss];
			return true;
		}
		
		sn = skill_lookup(argument);
		if (sn != -1)
		{
			if (ch->gen_data->skill_chosen[sn]
				||  ch->pcdata->learned[sn] > 0)
			{
				ch->println( "You already know that skill!" );
				return true;
			}
			
			if ( !IS_SKILL_VALID_FOR_CLASS(sn, ch->clss)
				||  IS_SPELL(sn))
			{
				ch->println( "That skill is not available." );
				return true;
			}
			
			// race restrictions
			if( IS_SET(skill_table[sn].flags,SKFLAGS_USE_RACE_RESTRICTIONS)
				&& !IS_SETn(skill_table[sn].race_restrict_n, ch->race))
			{
				ch->println( "That skill is not available to your race." );
				return true;
			}

			if(IS_SET(skill_table[sn].flags,SKFLAGS_NO_GAIN)){
				ch->println( "That skill is not available." );
				return true;
			}

			// can't take all the skills to over load the size on an int
			if (ch->pcdata->points + skillgroup_table[gn].rating[ch->clss]>150)
			{
				ch->println( "There is a limit of 150 creation points..." );
				ch->println( "you must remove other skills/groups before adding this skill." );
				return true;
			}
			
			ch->printlnf( "%s skill added.", skill_table[sn].name);
			ch->gen_data->skill_chosen[sn] = true;
			ch->gen_data->points_chosen += skill_table[sn].rating[ch->clss];
			ch->pcdata->learned[sn] = 1;
			ch->pcdata->points += skill_table[sn].rating[ch->clss];
			return true;
		}
		
		ch->println( "No skills or groups by that name..." );
		return true;
    }
	
    if (!strcmp(arg,"drop"))
    {
		if (argument[0] == '\0')
		{
			ch->println( "You must provide a skill to drop." );
			return true;
		}
		
		gn = skillgroup_lookup(argument);
		if (gn != -1 && ch->gen_data->skillgroup_chosen[gn])
		{
			ch->println( "Skillgroup dropped." );
			ch->gen_data->skillgroup_chosen[gn] = false;
			ch->gen_data->points_chosen -= skillgroup_table[gn].rating[ch->clss];
			gn_remove(ch,gn);
			// readd all the groups to make sure we don't lose any skills
			for (i = 0; !IS_NULLSTR(skillgroup_table[i].name); i++)
			{
				if (ch->gen_data->skillgroup_chosen[gn]){
					gn_add(ch,gn);
				}
			}
			ch->pcdata->points -= skillgroup_table[gn].rating[ch->clss];
			return true;
		}
		
		sn = skill_lookup(argument);
		if (sn != -1 && ch->gen_data->skill_chosen[sn])
		{
			ch->println( "Skill dropped." );
			ch->gen_data->skill_chosen[sn] = false;
			ch->gen_data->points_chosen -= skill_table[sn].rating[ch->clss];
			ch->pcdata->learned[sn] = 0;
			ch->pcdata->points -= skill_table[sn].rating[ch->clss];
			return true;
		}
		
		ch->println( "You haven't bought any such skill or group." );
		return true;
    }
	
    if (!str_prefix(arg,"premise") || !str_prefix(arg,"explain"))
    {
		do_help(ch,"premise");
		return true;
    }
	
    if (!str_prefix(arg,"list"))
    {
		ch->titlebar("LIST OF SKILLS AND SKILLSGROUPS STILL AVAILABLE");
        list_group_costs(ch);
        return true;
    }
	
    if (!str_prefix(arg,"learned"))
    {
		ch->titlebar("LEARNED SKILLS AND SKILLGROUPS");
        list_group_chosen(ch);
        ch->println( "You already have the following skills:" );
        ch->println( "You have these skills as a mixture, of the default skills you get" );
        ch->println( "with your race and class, plus the others you have added." );
		do_skills(ch,"");
        return true;
    }
	
    if (!str_prefix(arg,"skillgroups") || !str_prefix(arg,"info"))
    {
		do_skillgroups(ch,argument);
		return true;
    }
	
    if (!str_prefix(arg,"spinfo"))
    {
		do_spinfo(ch,argument);
        return true; 
    }	
	
    if (!str_prefix(arg,"cspinfo"))
    {
		do_cspinfo(ch,argument);
        return true; 
    }

    if (!str_prefix(arg,"cskinfo"))
    {
		do_cskinfo(ch,argument);
        return true; 
    }

    return false;
}
/**************************************************************************/
// shows all skillgroups, or the sub-members of a skillgroup
void do_skillgroups(char_data *ch, char *argument)
{
    int gn,sn,col;
	
    if (IS_NPC(ch))
		return;
	
    col = 0;

    if (argument[0] == '\0')
    {   
		ch->titlebar("SKILLGROUPS YOU HAVE");
		for (gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
		{
			if (ch->pcdata->skillgroup_known[gn])
			{
				ch->printf( "%-25s ",skillgroup_table[gn].name);

				if (++col % 3 == 0)
					ch->print( "\r\n" );
			}
        }
        if ( col % 3 != 0 )
            ch->print( "\r\n" );
		ch->titlebar("");
		ch->println("To list all skillgroups type `=Cskillgroups all`x.");
		ch->println("To show the skills in a particular skillgroup type `=Cskillgroups <skgrp name>`x.");
		ch->titlebar("");
		return;
	}
	
	if (!str_cmp(argument,"all"))    // show all skillgroups
	{
		ch->titlebar("ALL SKILLGROUPS");
	    for (gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++)
	    {
			if(!IS_SET(skillgroup_table[gn].flags, SKILLGROUP_CREATION_SELECTABLE)){
				continue;
			}
			if(skillgroup_table[gn].remort){
				ch->printf( "`S%d`x%-24s ", skillgroup_table[gn].remort, skillgroup_table[gn].name );
			}else{
				ch->printf( "%-25s ", skillgroup_table[gn].name );
			}
			if (++col % 3 == 0)
				ch->print( "\r\n" );
        }
        if ( col % 3 != 0 ){
            ch->print( "\r\n" );
		}
		ch->titlebar("");
		return;
	}
	
	
	// show the sub-members of a group 
	gn = skillgroup_lookup(argument);
	if (gn == -1)
	{
		ch->printlnf( "No skillgroup of the name '%s' exist.", argument );
		ch->println( "Type 'skillgroups all' for a full listing." );
		return;
	}
	
	ch->titlebarf("SKILLS WITHIN THE SKILLGROUP '%s'", uppercase(skillgroup_table[gn].name));
	for (sn = 0; skillgroup_table[gn].skills[sn]>-1; sn++)
	{
		ch->printf( "%-25s ", skill_table[skillgroup_table[gn].skills[sn]].name);

		if (++col % 3 == 0)
			ch->print( "\r\n" );
	}
    if ( col % 3 != 0 )
        ch->print( "\r\n" );
	ch->titlebar("");
}

/**************************************************************************/
// new check improve system - Kal
// trial code, not really completed yet.
void new_check_improve( char_data *ch, int sn, bool success, int multiplier)
{
	// standard situations where you can't improve
    if (IS_NPC(ch) || sn==-1 || IS_OOC(ch) || multiplier<1) {
		logf("1start %d\r\n",multiplier);
		return;
	}

	// can't improve by spamming
#ifndef WIN32
	if (ch->desc && ch->desc->repeat>5){
		return;
	}
#endif

	// store the perthousand improve table
	static short improve_chance_lookup_table[102]; // stored in range 1 ->10000
	static bool initialise_table=true;
	if(initialise_table){ // calculate the improvement chances once, first time
		int i;
		double f;
		for(i=0; i<=100; i++){
//			f=sqrt((100.0-i)/100);
			f=(100.0-i)/100;
			f=pow(100.0,f);
			f*=100; // put in 1->10000 range - increased precision
			improve_chance_lookup_table[i]=(short)f;
			//logf("improve_chance_lookup_table[%d]=%d", i, improve_chance_lookup_table[i]);
		}
		improve_chance_lookup_table[101]=1;
		initialise_table=false;
	}

	int maxlearn=skill_table[sn].learn_scale_percent[ch->clss];
	if(maxlearn<1){
		maxlearn=65;
	}
	
    if ( (!IS_SPELL(sn) && ch->level < skill_table[sn].skill_level[ch->clss])
		||  skill_table[sn].skill_level[ch->clss]==0
		||  skill_table[sn].rating[ch->clss] == 0
		||  ch->pcdata->learned[sn] <2 // 2% required - must prac at least once to improve
		||  ch->pcdata->learned[sn] >=maxlearn)
	{
		return;  // skill is not known, or past the point of improvement
	}

	if (number_range(1,4)!=1){ // make things harder
		new_check_improve( ch, sn, success, multiplier-1);
		return;
	}
	
    // check to see if the character has a chance to learn 
    int chance= 8 * ch->modifiers[STAT_ME] + 2*ch->modifiers[STAT_IN];
    chance /= (		multiplier
		*	skill_table[sn].rating[ch->clss] 
		*	4);
    chance += ch->level*3/4;
	
    if (number_range(1,1000) > chance){
		// try again
		new_check_improve( ch, sn, success, multiplier-1);
		return;
	}
	
    // now that the character has a CHANCE to learn, see if they really have

	chance=improve_chance_lookup_table[ch->pcdata->learned[sn]];
	int max_percent=UMAX(maxlearn,
						skill_table[sn].maxprac_percent[ch->clss]);
	if(max_percent>0 && max_percent<100){
		chance=chance* max_percent/100;
		if(chance<1){
			chance=1;
		}
	}
	
	if (!success){
		chance/=2;
		chance = URANGE(1,chance,2500);
	}
	if (number_range(1,10000)> chance){
		// try again
		new_check_improve( ch, sn, success, multiplier-1);
		return;
	}

	if (success){
		ch->printlnf("*You have become better at `Y%s`x!",
			skill_table[sn].name);
		ch->pcdata->learned[sn]++;
    }else{
		ch->printlnf("*You learn from your mistakes, and your `Y%s`x skill improves.",
				skill_table[sn].name);
		ch->pcdata->learned[sn] += number_range(1,3);
		ch->pcdata->learned[sn] = (unsigned char)UMIN(ch->pcdata->learned[sn],skill_table[sn].learn_scale_percent[ch->clss]);
    }
	ch->printlnf("*Your `Y%s`x is now at %d%%!!",
			skill_table[sn].name, ch->pcdata->learned[sn]);

	// calc how far they can prac it
	int maxprac=skill_table[sn].maxprac_percent[ch->clss]?
					skill_table[sn].maxprac_percent[ch->clss]:
					50;
	
	if(!(ch->level>70 && ch->pcdata->learned[sn]<maxprac)){
		gain_exp(ch,2 * skill_table[sn].rating[ch->clss]);
	}
}
/**************************************************************************/
// checks for skill improvement 
void check_improve( char_data *ch, int sn, bool success, int multiplier )
{
	if(IS_SET(skill_table[sn].flags,SKFLAGS_NEW_IMPROVE_SYSTEM)
		|| HAS_CONFIG(ch,CONFIG_PRACSYS_TESTER))
	{
		new_check_improve(ch, sn, success, multiplier);
		return;
	}

    if (IS_NPC(ch))
		return;

	if (sn==-1){
		return;
	}

	// can't improve in ooc rooms
    if (IS_OOC(ch)) 
		return;
	
	// can't improve by spamming
	if (ch->desc && ch->desc->repeat>5)
		return;
	
    if (  (!IS_SPELL(sn) && ch->level < skill_table[sn].skill_level[ch->clss])
		|| skill_table[sn].skill_level[ch->clss]==0
		||  skill_table[sn].rating[ch->clss] == 0
		||  ch->pcdata->learned[sn] <1
		||  ch->pcdata->learned[sn] >99)
		return;  // skill is not known

    // check to see if the character has a chance to learn 
    int chance= 8 * ch->modifiers[STAT_ME] + 2*ch->modifiers[STAT_IN];
    chance /= (		multiplier
		*	skill_table[sn].rating[ch->clss] 
		*	4);
    chance += ch->level;
	
    if (number_range(1,1000) > chance)
		return;
	
    // now that the character has a CHANCE to learn, see if they really have
	if (success){
		chance = URANGE(5,100 - ch->pcdata->learned[sn], 95);
		if (number_percent() < chance)
		{
			ch->printlnf("You have become better at `Y%s`x!", skill_table[sn].name);
			ch->pcdata->learned[sn]++;
			if(!(ch->level>75 && ch->pcdata->learned[sn]<75)){
				gain_exp(ch,2 * skill_table[sn].rating[ch->clss]);
			}
		}
    }else{
		chance = URANGE(5,ch->pcdata->learned[sn]/2,30);
		if (number_percent() < chance)
		{
			ch->printf(
				"You learn from your mistakes, and your `Y%s`x skill improves.\r\n",
				skill_table[sn].name);
			ch->pcdata->learned[sn] += number_range(1,3);
			ch->pcdata->learned[sn] = UMIN(ch->pcdata->learned[sn],100);
			if(!(ch->level>70 && ch->pcdata->learned[sn]<75)){
				gain_exp(ch,2 * skill_table[sn].rating[ch->clss]);
			}
		}
    }
}
/**************************************************************************/
// returns a skillgroup index number given the exact name
int skillgroup_exact_lookup( const char *name )
{
    for( int gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++ )
    {
        if ( !str_cmp( name, skillgroup_table[gn].name ) ){
            return gn;
		}
    }
	return -1;
}
/**************************************************************************/
// returns a skillgroup index number given the name
int skillgroup_lookup( const char *name )
{
    int gn;

	// perform exact match first
	gn=skillgroup_exact_lookup(name);
	if(gn>-1){
		return gn;
	}
	
	// perform prefix match
    for( gn = 0; !IS_NULLSTR(skillgroup_table[gn].name); gn++ )
    {
        if ( LOWER(name[0]) == LOWER(skillgroup_table[gn].name[0])
			&& !str_prefix( name, skillgroup_table[gn].name ) )
            return gn;
    }
	
    return -1;
}
/**************************************************************************/
// recursively adds a group given its number -- uses group_add
void gn_add( char_data *ch, int gn)
{
    int i;
	if(IS_NPC(ch)){
		return;
	}

	if(!IS_SKILLGROUP_SELECTABLE_FOR_CHAR(gn, ch)){
		return;
	}
    
    ch->pcdata->skillgroup_known[gn] = true;

	for (i = 0; skillgroup_table[gn].skills[i]>-1; i++)
	{
		if(IS_SKILL_VALID_FOR_CLASS(skillgroup_table[gn].skills[i], ch->clss)){
			group_add(ch,skill_table[skillgroup_table[gn].skills[i]].name,false, skillgroup_table[gn].skillsvalue[i]);
		}
    }
}
/**************************************************************************/
// recusively removes a group given its number -- uses group_remove 
void gn_remove( char_data *ch, int gn)
{
    int i;
	
    ch->pcdata->skillgroup_known[gn] = false;
	
	for (i = 0; skillgroup_table[gn].skills[i]>-1; i++)
	{
		if(IS_SKILL_VALID_FOR_CLASS(skillgroup_table[gn].skills[i], ch->clss)){
			group_remove(ch,skill_table[skillgroup_table[gn].skills[i]].name);
		}
    }
}
/**************************************************************************/	
// use for processing a skill or group for addition 
void group_add( char_data *ch, const char *name, bool deduct, int percent)
{
    int sn,gn;
	
    if (IS_NPC(ch)){ // NPCs do not have skills
		return;
	}
	
	if(percent<0 || percent>101){ // safety check
		return;
	}

    sn = skill_exact_lookup(name);
	
    if (sn != -1)
    {
		if (ch->pcdata->learned[sn] < percent){
			ch->pcdata->learned[sn] = percent;
			if (deduct){
				ch->pcdata->points += skill_table[sn].rating[ch->clss]; 
			}
		}
		return;
    }
	
	// if there wasn't a skill by the name, check for a group
	gn = skillgroup_lookup(name);
	
    if (gn != -1)
    {
		if (ch->pcdata->skillgroup_known[gn] == false)  
		{
			ch->pcdata->skillgroup_known[gn] = true;
			if (deduct)
				ch->pcdata->points += skillgroup_table[gn].rating[ch->clss];
		}
		gn_add(ch,gn); // make sure all skills in the group are known
    }
}
/**************************************************************************/
// used for processing a skill or group for deletion -- no points back!
void group_remove(char_data *ch, const char *name)
{
    int sn, gn;
    
	sn = skill_lookup(name);
	
    if (sn != -1)
    {
		ch->pcdata->learned[sn] = 0;
		return;
    }
	
    // now check skillgroups
	
    gn = skillgroup_lookup(name);
	
    if (gn != -1 && ch->pcdata->skillgroup_known[gn] == true)
    {
		ch->pcdata->skillgroup_known[gn] = false;
		gn_remove(ch,gn);  // be sure to call gn_add on all remaining skillgroups
    }
}

/**************************************************************************/
// Jarren - June 1999
void do_vault( char_data *ch, char *argument )
{
	char arg[MIL];
	char_data *victim;
	int chance, accuracy;
	
	one_argument(argument,arg);
	
	if ( (chance = get_skill(ch,gsn_vault)) == 0
		||      (!IS_NPC(ch)
		&&       ch->level < skill_table[gsn_vault].skill_level[ch->clss]))
	{
		if (!IS_CONTROLLED(ch))
		{
			ch->println( "Vault? What's that?" );
			return;
		}
	}
	
	if (arg[0] == '\0')
	{
		victim = ch->fighting;
		if (!victim){
			ch->println( "But you aren't fighting anyone!" );
			return;
		}
	}else if ((victim = get_char_room(ch,arg)) == NULL){
		ch->println( "They aren't here." );
		return;
	}
	
	if (victim->position < POS_FIGHTING)
	{
		act("You'll have to let $M get back up first.",ch,NULL,victim,TO_CHAR);
		return;
	}
	
	if (victim == ch){
		ch->println( "You try to pole vault onto yourself but miss horribly." );
		return;
	}
	
	if (is_safe(ch,victim))
		return;

	if ( !can_initiate_combat( ch, victim, 1 )) return;	

    if (IS_AFFECTED(ch,AFF_CHARM) && ch->master == victim){
		act("But $N is your friend!",ch,NULL,victim,TO_CHAR);
		return;
    }
	
    // modifiers
	
    // size and weight
    chance += ch->carry_weight / 250;
    chance -= victim->carry_weight / 200;
	
    if (ch->size < victim->size){
		chance += (ch->size - victim->size) * 15;
    }else{
		chance += (ch->size - victim->size) * 10; 
	}
		
    // stats
    chance += ch->modifiers[STAT_ST];
    chance -= victim->modifiers[STAT_QU];
    chance -= GET_AC(victim,AC_BASH) /25;
    
	// speed
    if (IS_SET(ch->off_flags,OFF_FAST) || IS_AFFECTED(ch,AFF_HASTE)){
		chance += 10;
    }
	if (IS_SET(victim->off_flags,OFF_FAST) || IS_AFFECTED(victim,AFF_HASTE)){
		chance -= 30;
	}
	
    // level
    chance += (ch->level - victim->level);
	
    if (IS_CONTROLLED(ch)){
        chance*=3;
    };
	
    if (!IS_NPC(victim) && chance < get_skill(victim,gsn_dodge) )
		   chance -= 3 * (get_skill(victim,gsn_dodge) - chance);
	
    // now the attack
    if (number_percent() < chance )
    {

		accuracy = 100 - get_skill(victim,gsn_vault);
		accuracy -= ch->modifiers[STAT_PR]/10;
		accuracy -= ch->modifiers[STAT_AG]/10;
		accuracy += victim->modifiers[STAT_QU]/9;
		if(ch->pcdata){
			accuracy += ch->pcdata->tired/2;
		}

		if(accuracy < number_range(1,8))
		{
			act("$n pole vaults at you delivering a powerfull blow to your head!",ch,NULL,NULL,TO_VICT);
			act("You pole vault at $n delivering a powerfull blow to $m head!",victim,NULL,NULL,TO_CHAR);
			act("$n pole vaults at $N delivering a powerfull blow to $M head.",ch,NULL,victim,TO_NOTVICT);
			accuracy = 2;
		}else{
			act("$n pole vauls at you delivering a crushing blow to your abdomen!",ch,NULL,NULL,TO_VICT);
			act("You pole vault at $n delivering a crushing blow to $m abdomen!",victim,NULL,NULL,TO_CHAR);
			act("$n pole vaults at $N delivering a crushing blow to $M abdomen.",ch,NULL,victim,TO_NOTVICT);
			accuracy = 1;
		}
		
		check_improve(ch,gsn_vault,true,1);
		
		DAZE_STATE(victim, 3 * PULSE_VIOLENCE);
		WAIT_STATE(ch,skill_table[gsn_vault].beats);
		victim->position = POS_RESTING;
		damage(ch,victim,number_range(2,2 + accuracy * ch->size + chance/10),
			gsn_vault,	DAM_BASH,false);
		
    }else{
		damage(ch,victim,0,gsn_vault,DAM_BASH,false);
		act("You try to pole vault at $n but miss and fly right past $m!", victim,NULL,NULL,TO_CHAR);
		act("$n tries to pole vault at $N but flies right past $M.", ch,NULL,victim,TO_NOTVICT);
		act("You evade $n's pole vault, causing $m to fly right past you.", ch,NULL,NULL,TO_VICT);
		check_improve(ch,gsn_vault,false,1);
		WAIT_STATE(ch,skill_table[gsn_vault].beats * 3/2); 
    }
}
/**************************************************************************/