R2b5/deity/
R2b5/gods/
R2b5/log/
R2b5/player/
/***************************************************************************
*   Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*   Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                          *
*   Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
*   Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                          *
*   In order to use any part of this Merc Diku Mud, you must comply with   *
*   both the original Diku license in 'license.doc' as well the Merc       *
*   license in 'license.txt'.  In particular, you may not remove either of *
*   these copyright notices.                                               *
*                                                                          *
*   Much time and thought has gone into this software and you are          *
*   benefitting.  We hope that you share your changes too.  What goes      *
*   around, comes around.                                                  *
***************************************************************************/
/***************************************************************************
*       ROM 2.4 is copyright 1993-1995 Russ Taylor                         *
*       ROM 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)                                  *
*       By using this code, you have agreed to follow the terms of the     *
*       ROM license, in the file 'rom.license'                             *
***************************************************************************/
/***************************************************************************
*       ROT 2.0 is copyright 1996-1999 by Russ Walsh                       *
*       By using this code, you have agreed to follow the terms of the     *
*       ROT license, in the file 'rot.license'                             *
***************************************************************************/

#if defined(macintosh)
#include <types.h>
#include <time.h>
#else
	#include <sys/types.h>
	#if defined(WIN32)
		#include <time.h>
	#else
		#include <sys/time.h>
	#endif
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"
#include "magic.h"

/* command procedures needed */
DECLARE_DO_FUN(do_groups	);
DECLARE_DO_FUN(do_help		);
DECLARE_DO_FUN(do_say		);


/* used to get new skills */
void do_gain(CHAR_DATA *ch, char *argument)
{
    char buf[MSL];
    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 != NULL; 
	  trainer = trainer->next_in_room)
	if (IS_NPC(trainer) && IS_SET(trainer->act,ACT_GAIN))
	    break;

    if (trainer == NULL || !can_see(ch,trainer))
    {
	send_to_char("You can't do that here.\n\r",ch);
	return;
    }

    if (ch->spirit)
    {
	send_to_char( "Spirits cannot gain.\n\r", ch);
	return;
    }

    one_argument(argument,arg);

    if (arg[0] == '\0')
    {
	do_say(trainer,"{aPardon me?{x");
	send_to_char("{aI offer the following gain options:{x\n\r",ch);
	send_to_char("{R   list     {aList the groups and skills you can gain.{x\n\r", ch);
	send_to_char("{R   convert  {aConverts 5 practices to 1 train.{x\n\r", ch);
	send_to_char("{R   study    {aConverts 1 train to 6 practices.{x\n\r", ch);
	send_to_char("{R   quest    {aConverts 3 quest points to 1 train.{x\n\r", ch);
	send_to_char("{R   points   {aGain creation points.{x\n\r", ch);
	send_to_char("{R   <{rname{R>   {aGain skill or group <name>{x\n\r\n\r", ch);
	return;
    }

    if (!str_prefix(arg,"list"))
    {
	int col;
	
	col = 0;

	sprintf(buf, "%-18s %-5s %-18s %-5s %-18s %-5s\n\r",
	             "group","cost","group","cost","group","cost");
	send_to_char(buf,ch);

	for (gn = 0; gn < MAX_GROUP; gn++)
	{
	    bool nfound = FALSE;
	    int tmpskill = 999;

	    if (group_table[gn].name == NULL)
		break;

	    if (!ch->pcdata->group_known[gn]
	    &&  group_table[gn].rating[ch->class] > 0)
	    {
		nfound = TRUE;
		sprintf(buf,"{R%-18s %-5d {x",
		    group_table[gn].name,group_table[gn].rating[ch->class]);
		tmpskill = group_table[gn].rating[ch->class];
	    }
	    if ( (ch->pcdata->tier == 2)
	    && !ch->pcdata->group_known[gn]
	    &&  group_table[gn].rating[ch->clasb] > 0)
	    {
		sprintf(buf,"{B%-18s %-5d {x",
		    group_table[gn].name,group_table[gn].rating[ch->clasb]);
		if (nfound)
		{
		    sprintf(buf,"{G%-18s %-5d {x",
			group_table[gn].name,
			UMIN(tmpskill, group_table[gn].rating[ch->clasb]) );
		}
		nfound = TRUE;
	    }
	    if (nfound)
	    {
		send_to_char(buf,ch);
		if (++col % 3 == 0)
		    send_to_char("\n\r",ch);
	    }
	}
	if (col % 3 != 0)
	    send_to_char("\n\r",ch);
	
	send_to_char("\n\r",ch);		

	col = 0;

        sprintf(buf, "{M%-18s {Y%-5s {M%-18s {Y%-5s {M%-18s {Y%-5s{x\n\r",
                     "skill","cost","skill","cost","skill","cost");
        send_to_char(buf,ch);
 
        for (sn = 0; sn < MAX_SKILL; sn++)
        {
	    bool nfound = FALSE;
	    int tmpskill = 999;

            if (skill_table[sn].name == NULL)
                break;
 
            if (!ch->pcdata->learned[sn]
            &&  skill_table[sn].rating[ch->class] > 0
	    &&  skill_table[sn].spell_fun == spell_null)
            {
		nfound = TRUE;
                sprintf(buf,"{R%-18s %-5d {x",
                    skill_table[sn].name,skill_table[sn].rating[ch->class]);
		tmpskill = skill_table[sn].rating[ch->class];
            }
	    if ( (ch->pcdata->tier == 2)
	    &&  !ch->pcdata->learned[sn]
            &&  skill_table[sn].rating[ch->clasb] > 0
	    &&  skill_table[sn].spell_fun == spell_null)
            {
                sprintf(buf,"{B%-18s %-5d {x",
                    skill_table[sn].name,skill_table[sn].rating[ch->clasb]);
		if (nfound)
		{
		    sprintf(buf,"{G%-18s %-5d {x",
			skill_table[sn].name,
			UMIN(tmpskill, skill_table[sn].rating[ch->clasb]) );
		}
		nfound = TRUE;
            }
	    if (nfound)
	    {
                send_to_char(buf,ch);
                if (++col % 3 == 0)
                    send_to_char("\n\r",ch);
	    }
        }
        if (col % 3 != 0)
            send_to_char("\n\r",ch);
	return;
    }

    if (!str_prefix(arg,"convert"))
    {
	if (ch->practice < 6)
	{
	    act("$N tells you '{aYou are not yet ready.{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

	act("$N helps you apply your practice to training",
		ch,NULL,trainer,TO_CHAR);
	ch->practice -= 5;
	ch->train +=1 ;
	return;
    }

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

        act("$N helps you apply your training to practice",
                ch,NULL,trainer,TO_CHAR);
        ch->train -= 1;
	ch->practice += 5;
	return;
    }

    if (!str_prefix(arg,"quest"))
    {
	if (ch->aqps < 3)
	{
	    act("$N tells you '{aYou are not yet ready.{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

	act("$N helps you apply your quest points to training",
		ch,NULL,trainer,TO_CHAR);
	ch->aqps -= 3;
	ch->train += 1;
	return;
    }

    if (!str_prefix(arg,"points"))
    {

	if (!IS_NPC(ch) && IS_SET(ch->act, PLR_LQUEST))
	{
	    act("$N tells you '{aYou may not gain points while on a level quest.{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

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

	if (ch->pcdata->points <= 40)
	{
	    act("$N tells you '{aThere would be no point in that.{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

	if (((ch->level + 1) * exp_per_level(ch,ch->pcdata->points) - ch->exp) >= 5000)
	{
	    sprintf(buf, "Go work off some of that exp first %s.",ch->name);
	    do_say(trainer,buf);
	    return;
	}

	act("$N trains you, and you feel more at ease with your skills.",
	    ch,NULL,trainer,TO_CHAR);

	ch->train -= 1;
	ch->pcdata->points -= 1;
	ch->exp = (long)exp_per_level(ch,ch->pcdata->points) * ch->level;
	return;
    }

    /* else add a group/skill */

    gn = group_lookup(argument);
    if (gn > 0)
    {
	if (ch->pcdata->group_known[gn])
	{
	    act("$N tells you '{aYou already know that group!{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

	if (ch->pcdata->tier >= 2)
	{
	    if ( ( group_table[gn].rating[ch->class] <= 0 ) && ( group_table[gn].rating[ch->clasb] <= 0 ) ) {
		act("$N tells you '{MThat group is beyond your powers.{x'", ch,NULL,trainer,TO_CHAR);
		return;
	    }

	    if (ch->train < group_table[gn].rating[ch->class]) {
		act("$N tells you '{WYou are not yet ready for that group.{x'", ch,NULL,trainer,TO_CHAR);
		return;
	    }
	} 
	else {
	    if ( ( group_table[gn].rating[ch->class] <= 0 ) &&   ( group_table[gn].rating[ch->clasb] <= 0 ) ) {
		act("$N tells you '{mThat group is beyond your powers.{x'", ch,NULL,trainer,TO_CHAR);
		return;
	    }

	    if ( ( ch->train < group_table[gn].rating[ch->class] ) &&   ( ch->train < group_table[gn].rating[ch->clasb] ) ) {
		act("$N tells you '{wYou are not yet ready for that group.{x'", ch,NULL,trainer,TO_CHAR);
		return;
	    }
	}

	/* add the group */
	gn_add(ch,gn);
	act("$N trains you in the art of $t",
	    ch,group_table[gn].name,trainer,TO_CHAR);
	if (ch->pcdata->tier >= 2)
	{
	    ch->train -= group_table[gn].rating[ch->class];
	} else {
	    int tmptrain = 999;

	    if (group_table[gn].rating[ch->class] > 0)
		tmptrain = group_table[gn].rating[ch->class];
	    if (group_table[gn].rating[ch->clasb] > 0)
		tmptrain = UMIN(tmptrain, group_table[gn].rating[ch->clasb]);
	    ch->train -= tmptrain;
	}
	return;
    }

    sn = skill_lookup(argument);
    if (sn > -1)
    {
	if (skill_table[sn].spell_fun != spell_null)
	{
	    act("$N tells you '{aYou must learn the full group.{x'",
		ch,NULL,trainer,TO_CHAR);
	    return;
	}

        if (ch->pcdata->learned[sn])
        {
            act("$N tells you '{aYou already know that skill!{x'",
                ch,NULL,trainer,TO_CHAR);
            return;
        }
 
	if (ch->pcdata->tier != 2)
	{
	    if (skill_table[sn].rating[ch->class] <= 0)
	    {
		act("$N tells you '{aThat skill is beyond your powers.{x'",
		    ch,NULL,trainer,TO_CHAR);
		return;
	    }
 
	    if (ch->train < skill_table[sn].rating[ch->class])
	    {
		act("$N tells you '{aYou are not yet ready for that skill.{x'",
		    ch,NULL,trainer,TO_CHAR);
		return;
	    }
	} else {
	    if ( ( skill_table[sn].rating[ch->class] <= 0 )
	    &&   ( skill_table[sn].rating[ch->clasb] <= 0 ) )
	    {
		act("$N tells you '{aThat skill is beyond your powers.{x'",
		    ch,NULL,trainer,TO_CHAR);
		return;
	    }
 
	    if ( ( ch->train < skill_table[sn].rating[ch->class] )
	    &&   ( ch->train < skill_table[sn].rating[ch->clasb] ) )
	    {
		act("$N tells you '{aYou are not yet ready for that skill.{x'",
		    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);

	if (ch->pcdata->tier >= 2)
	{
	    ch->train -= skill_table[sn].rating[ch->class];
	} else {
	    int tmptrain = 999;;

	    if (skill_table[sn].rating[ch->class] > 0)
		tmptrain = skill_table[sn].rating[ch->class];
	    if (skill_table[sn].rating[ch->clasb] > 0)
		tmptrain = UMIN(tmptrain, skill_table[sn].rating[ch->clasb]);
	    ch->train -= tmptrain;
	}

        return;
    }

    act("$N tells you '{aI do not understand...{x'",ch,NULL,trainer,TO_CHAR);
}
    



/* RT spells and skills show the players spells (or skills) */

void do_spells(CHAR_DATA *ch, char *argument)
{
    char spell_list[LEVEL_HERO][MSL];
    char spell_columns[LEVEL_HERO];
    int sn,lev,mana;
    bool found = FALSE;
    bool nfound = FALSE;
    char buf[MSL];

    if (IS_NPC(ch))
      return;

    /* initilize data */
    for (lev = 0; lev < LEVEL_HERO; lev++)
    {
	spell_columns[lev] = 0;
	spell_list[lev][0] = '\0';
    }
 
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
      if (skill_table[sn].name == NULL)
        break;

      nfound = FALSE;

      if (skill_table[sn].skill_level[ch->class] < LEVEL_HERO &&
	  skill_table[sn].spell_fun != spell_null &&
          ch->pcdata->learned[sn] > 0)
      {
	nfound = TRUE;
	found = TRUE;
	lev = skill_table[sn].skill_level[ch->class];
	if (ch->level < lev)
	  sprintf(buf,"{R%-18s  n/a      {x", skill_table[sn].name);
	else
	{
	  mana = UMAX(skill_table[sn].min_mana, 100/(2 + ch->level - lev));
	  sprintf(buf,"{R%-18s  %3d mana  {x",skill_table[sn].name,mana);
	}
      }
      if (ch->pcdata->tier == 2)
      {
	if (skill_table[sn].skill_level[ch->clasb] < LEVEL_HERO
	&& skill_table[sn].spell_fun != spell_null
	&& ch->pcdata->learned[sn] > 0)
	{
	    int nlev;

	    if (!nfound)
		lev = 999;
	    found = TRUE;
	    nlev = skill_table[sn].skill_level[ch->clasb];
	    if (ch->level < nlev && nlev < lev)
		{
		    sprintf(buf,"{B%-18s  n/a      {x", skill_table[sn].name);
	    	} 
		else if (nlev < lev)
		{
	 mana = UMAX(skill_table[sn].min_mana,100/(2+ch->level-nlev));
	    sprintf(buf,"{B%-18s  %3d mana  {x",skill_table[sn].name,mana);
	    	}
	    if (nfound)
	    {
		if (ch->level < nlev)
		{
		    if (nlev < lev)
		    {
			sprintf(buf,"{G%-18s  n/a      {x", skill_table[sn].name);
		    }
		} else {
		    if (nlev < lev)
		    {
			 mana = UMAX(skill_table[sn].min_mana, 100/(2 + ch->level - nlev));
			sprintf(buf,"{G%-18s  %3d mana  {x",skill_table[sn].name,mana);
		    }
		}
	    }
	    if (nlev < lev)
		lev = nlev;
	    nfound = TRUE;
	}
      }
      if (nfound)
      {
	if (spell_list[lev][0] == '\0')
	  sprintf(spell_list[lev],"\n\r{MLevel %2d: {x%s",lev,buf);
        else /* append */
	{
	  if ( ++spell_columns[lev] % 2 == 0)
            strcat(spell_list[lev],"\n\r          ");
	  strcat(spell_list[lev],buf);
        }
      }
    }

    /* return results */
 
    if (!found)
    {
      send_to_char("You know no spells.\n\r",ch);
      return;
    }
    
    for (lev = 0; lev < LEVEL_HERO; lev++)
      if (spell_list[lev][0] != '\0')
	send_to_char(spell_list[lev],ch);
    send_to_char("\n\r",ch);
}


void do_bskills(CHAR_DATA *ch, char *argument)
{
    char skill_list[LEVEL_HERO][MSL];
    char skill_columns[LEVEL_HERO];
    int sn,lev;
    bool found = FALSE;
    bool nfound = FALSE;
    char buf[MSL];
 
    if (IS_NPC(ch))
      return;
 
    /* initilize data */
    for (lev = 0; lev < LEVEL_HERO; lev++)
    {
        skill_columns[lev] = 0;
        skill_list[lev][0] = '\0';
    }
 
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
      if (skill_table[sn].name == NULL )
        break;

      nfound = FALSE;
 
      if (skill_table[sn].skill_level[ch->clasb] < LEVEL_HERO &&
	  skill_table[sn].spell_fun == spell_null &&
	  ch->pcdata->learned[sn] > 0)
      {
        found = TRUE;
        nfound = TRUE;
       lev = skill_table[sn].skill_level[ch->clasb];
        if (ch->level < lev)
          sprintf(buf,"{R%-18s n/a      {x", skill_table[sn].name);
        else
          sprintf(buf,"{R%-18s %3d%%      {x",skill_table[sn].name,
					 ch->pcdata->learned[sn]);
      }

      if (ch->pcdata->tier == 2)
      {
	if (skill_table[sn].skill_level[ch->clasb] < LEVEL_HERO &&
	    skill_table[sn].spell_fun == spell_null &&
	    ch->pcdata->learned[sn] > 0)
	{
	    int nlev;

	    if (!nfound)
		lev = 999;
	    found = TRUE;
	    nlev = skill_table[sn].skill_level[ch->clasb];
	    if (ch->level < nlev)
	    {
		    sprintf(buf,"{B%-18s n/a      {x", skill_table[sn].name);
	    } else {
		    sprintf(buf,"{B%-18s %3d%%      {x",skill_table[sn].name,
			ch->pcdata->learned[sn]);
	    }
	    if (nlev < lev)
		lev = nlev;
	    if (nfound)
	    {
		if (ch->level < lev)
		{
			sprintf(buf,"{G%-18s n/a      {x", skill_table[sn].name);
		} else {
			sprintf(buf,"{G%-18s %3d%%      {x",skill_table[sn].name,
			    ch->pcdata->learned[sn]);
		}
	    }
	    nfound = TRUE;
	}
      }
      if (nfound)
      {
        if (skill_list[lev][0] == '\0')
          sprintf(skill_list[lev],"\n\r{MLevel %2d: {x%s",lev,buf);
        else /* append */
        {
          if ( ++skill_columns[lev] % 2 == 0)
            strcat(skill_list[lev],"\n\r          ");
          strcat(skill_list[lev],buf);
        }
      }
    }
 
    /* return results */
 
    if (!found)
    {
      send_to_char("You know no skills.\n\r",ch);
      return;
    }
 
    for (lev = 0; lev < LEVEL_HERO; lev++)
      if (skill_list[lev][0] != '\0')
        send_to_char(skill_list[lev],ch);
    send_to_char("\n\r",ch);
}

void do_skills(CHAR_DATA *ch, char *argument)
{
    char skill_list[LEVEL_HERO][MSL];
    char skill_columns[LEVEL_HERO];
    int sn,lev;
    bool found = FALSE;
    bool nfound = FALSE;
    char buf[MSL];
 
    if (IS_NPC(ch))
      return;
 
    /* initilize data */
    for (lev = 0; lev < LEVEL_HERO; lev++)
    {
        skill_columns[lev] = 0;
        skill_list[lev][0] = '\0';
    }
 
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
      if (skill_table[sn].name == NULL )
        break;

      nfound = FALSE;
 
      if (skill_table[sn].skill_level[ch->clasb] < LEVEL_HERO &&
	  skill_table[sn].spell_fun == spell_null &&
	  ch->pcdata->learned[sn] > 0)
      {
        found = TRUE;
        nfound = TRUE;
       lev = skill_table[sn].skill_level[ch->clasb];
        if (ch->level < lev)
          sprintf(buf,"{R%-18s n/a      {x", skill_table[sn].name);
        else
          sprintf(buf,"{R%-18s %3d%%      {x",skill_table[sn].name,
					 ch->pcdata->learned[sn]);
      }

      if (ch->pcdata->tier == 2)
      {
	if (skill_table[sn].skill_level[ch->clasb] < LEVEL_HERO &&
	    skill_table[sn].spell_fun == spell_null &&
	    ch->pcdata->learned[sn] > 0)
	{
	    int nlev;

	    if (!nfound)
		lev = 999;
	    found = TRUE;
	    nlev = skill_table[sn].skill_level[ch->clasb];
	    if (ch->level < nlev)
	    {
		    sprintf(buf,"{B%-18s n/a      {x", skill_table[sn].name);
	    } else {
		    sprintf(buf,"{B%-18s %3d%%      {x",skill_table[sn].name,
			ch->pcdata->learned[sn]);
	    }
	    if (nlev < lev)
		lev = nlev;
	    if (nfound)
	    {
		if (ch->level < lev)
		{
			sprintf(buf,"{G%-18s n/a      {x", skill_table[sn].name);
		} else {
			sprintf(buf,"{G%-18s %3d%%      {x",skill_table[sn].name,
			    ch->pcdata->learned[sn]);
		}
	    }
	    nfound = TRUE;
	}
      }
      if (nfound)
      {
        if (skill_list[lev][0] == '\0')
          sprintf(skill_list[lev],"\n\r{MLevel %2d: {x%s",lev,buf);
        else /* append */
        {
          if ( ++skill_columns[lev] % 2 == 0)
            strcat(skill_list[lev],"\n\r          ");
          strcat(skill_list[lev],buf);
        }
      }
    }
 
    /* return results */
 
    if (!found)
    {
      send_to_char("You know no skills.\n\r",ch);
      return;
    }
 
    for (lev = 0; lev < LEVEL_HERO; lev++)
      if (skill_list[lev][0] != '\0')
        send_to_char(skill_list[lev],ch);
    send_to_char("\n\r",ch);
}


/* shows skills, groups and costs (only if not bought) */
void list_group_costs(CHAR_DATA *ch)
{
    char buf[100];
    int gn,sn,col;

    if (IS_NPC(ch))
	return;

    col = 0;

    sprintf(buf,"{M%-18s {Y%-5s {M%-18s {Y%-5s {M%-18s {Y%-5s{x\n\r",
	"group","cp","group","cp","group","cp");
    send_to_char(buf,ch);

    for (gn = 0; gn < MAX_GROUP; gn++)
    {
	bool found = FALSE;
	int tmpcost = 999;

	if (group_table[gn].name == NULL)
	    break;

        if (!ch->gen_data->group_chosen[gn] 
	&&  !ch->pcdata->group_known[gn]
	&&  group_table[gn].rating[ch->class] > 0)
	{
	    found = TRUE;
	    sprintf(buf,"{R%-18s %-5d {x",group_table[gn].name,
		group_table[gn].rating[ch->class]);
	    tmpcost = group_table[gn].rating[ch->class];
	}
	if (ch->pcdata->tier == 2)
	{
	    if (!ch->gen_data->group_chosen[gn] 
	    &&  !ch->pcdata->group_known[gn]
	    &&  group_table[gn].rating[ch->clasb] > 0)
	    {
		sprintf(buf,"{B%-18s %-5d {x",group_table[gn].name,
			group_table[gn].rating[ch->clasb]);
		if (group_table[gn].rating[ch->clasb] < tmpcost)
		{
		    tmpcost = group_table[gn].rating[ch->clasb];
		}
		if (found)
		{
		    sprintf(buf,"{G%-18s %-5d {x",group_table[gn].name,
			tmpcost);
		}
		found = TRUE;
	    }
	}
	if (found)
	{
	    send_to_char(buf,ch);
	    if (++col % 3 == 0)
		send_to_char("\n\r",ch);
	}
    }
    if ( col % 3 != 0 )
        send_to_char( "\n\r", ch );
    send_to_char("\n\r",ch);

    col = 0;
 
    sprintf(buf,"{M%-18s {Y%-5s {M%-18s {Y%-5s {M%-18s {Y%-5s{x\n\r",
	"skill","cp","skill","cp","skill","cp");
    send_to_char(buf,ch);
 
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
	bool found = FALSE;
	int tmpcost = 999;

        if (skill_table[sn].name == NULL)
            break;
 
        if (!ch->gen_data->skill_chosen[sn] 
	&&  ch->pcdata->learned[sn] == 0
	&&  skill_table[sn].spell_fun == spell_null
	&&  skill_table[sn].rating[ch->class] > 0)
        {
	    found = TRUE;
            sprintf(buf,"{R%-18s %-5d {x",skill_table[sn].name,
                                    skill_table[sn].rating[ch->class]);
	    tmpcost = skill_table[sn].rating[ch->class];
	}
	if (ch->pcdata->tier == 2)
	{
	    if (!ch->gen_data->skill_chosen[sn] 
	    &&  ch->pcdata->learned[sn] == 0
	    &&  skill_table[sn].spell_fun == spell_null
	    &&  skill_table[sn].rating[ch->clasb] > 0)
            {
		sprintf(buf,"{B%-18s %-5d {x",skill_table[sn].name,
			skill_table[sn].rating[ch->clasb]);
		if (skill_table[sn].rating[ch->clasb] < tmpcost)
		{
		    tmpcost = skill_table[sn].rating[ch->clasb];
		}
		if (found)
		{
		    sprintf(buf,"{G%-18s %-5d {x",skill_table[sn].name,
			tmpcost);
		}
		found = TRUE;
	    }
	}
	if (found)
	{
            send_to_char(buf,ch);
            if (++col % 3 == 0)
                send_to_char("\n\r",ch);
        }
    }
    if ( col % 3 != 0 )
        send_to_char( "\n\r", ch );
    send_to_char("\n\r",ch);

    sprintf(buf,"Creation points: %d\n\r",ch->pcdata->points);
    send_to_char(buf,ch);
    sprintf(buf,"Experience per level: %ld\n\r",
	    exp_per_level(ch,ch->gen_data->points_chosen));
    send_to_char(buf,ch);
    return;
}


void list_group_chosen(CHAR_DATA *ch)
{
    char buf[100];
    int gn,sn,col;
 
    if (IS_NPC(ch))
        return;
 
    col = 0;
 
    sprintf(buf,"{M%-18s {Y%-5s {M%-18s {Y%-5s {M%-18s {Y%-5s{x\n\r",
	"group","cp","group","cp","group","cp");
    send_to_char(buf,ch);
 
    for (gn = 0; gn < MAX_GROUP; gn++)
    {
	bool found = FALSE;

        if (group_table[gn].name == NULL)
            break;
 
        if (ch->gen_data->group_chosen[gn] 
	&&  group_table[gn].rating[ch->class] > 0)
        {
	    found = TRUE;
            sprintf(buf,"{R%-18s %-5d {x",group_table[gn].name,
                                    group_table[gn].rating[ch->class]);
        }
	if (ch->pcdata->tier == 2)
	{
	    if (ch->gen_data->group_chosen[gn] 
	    &&  group_table[gn].rating[ch->clasb] > 0)
	    {
		sprintf(buf,"{B%-18s %-5d {x",group_table[gn].name,
                                    group_table[gn].rating[ch->clasb]);
		if (found)
		{
		    sprintf(buf,"{G%-18s %-5d {x",group_table[gn].name,
                    UMIN(group_table[gn].rating[ch->class], group_table[gn].rating[ch->clasb]));
		}
		found = TRUE;
	    }
	}
	if (found)
	{
            send_to_char(buf,ch);
            if (++col % 3 == 0)
                send_to_char("\n\r",ch);
        }

    }
    if ( col % 3 != 0 )
        send_to_char( "\n\r", ch );
    send_to_char("\n\r",ch);
 
    col = 0;
 
    sprintf(buf,"{M%-18s {Y%-5s {M%-18s {Y%-5s {M%-18s {Y%-5s{x\n\r",
	"skill","cp","skill","cp","skill","cp");
    send_to_char(buf,ch);
 
    for (sn = 0; sn < MAX_SKILL; sn++)
    {
	bool found = FALSE;

        if (skill_table[sn].name == NULL)
            break;
 
        if (ch->gen_data->skill_chosen[sn] 
	&&  skill_table[sn].rating[ch->class] > 0)
        {
	    found = TRUE;
            sprintf(buf,"{R%-18s %-5d {x",skill_table[sn].name,
                                    skill_table[sn].rating[ch->class]);
	}
	if (ch->pcdata->tier == 2)
	{
	    if (ch->gen_data->skill_chosen[sn] 
	    &&  skill_table[sn].rating[ch->clasb] > 0)
	    {
		sprintf(buf,"{B%-18s %-5d {x",skill_table[sn].name,
                                    skill_table[sn].rating[ch->clasb]);
	    }
	    if (found)
	    {
		sprintf(buf,"{G%-18s %-5d {x",skill_table[sn].name,
                                    skill_table[sn].rating[ch->clasb]);
	    }
	    found = TRUE;
	}
	if (found)
	{
            send_to_char(buf,ch);
            if (++col % 3 == 0)
                send_to_char("\n\r",ch);
        }
    }
    if ( col % 3 != 0 )
        send_to_char( "\n\r", ch );
    send_to_char("\n\r",ch);
 
    sprintf(buf,"{MCreation points: {C%d{x\n\r",ch->gen_data->points_chosen);
    send_to_char(buf,ch);
    sprintf(buf,"{MExperience per level: {C%ld{x\n\r",
	    exp_per_level(ch,ch->gen_data->points_chosen));
    send_to_char(buf,ch);
    return;
}

long exp_per_level(CHAR_DATA *ch, int points)
{
    long expl,inc;

    if (IS_NPC(ch))
	return 1000; 

    expl = 1000;
    inc = 500;

    if (points < 40)
    {
	if (ch->pcdata->tier != 2)
	{
	    return 1000 * pc_race_table[ch->race].class_mult[ch->class]/100;
	} else {
	    long rpoint;

	    rpoint = ( ( ( 1000 * pc_race_table[ch->race].class_mult[ch->class]/100 )
		   +     ( 1000 * pc_race_table[ch->race].class_mult[ch->class]/100 )
		   +     ( 1000 * pc_race_table[ch->race].class_mult[ch->clasb]/100 ) )
		   /       3 );
	    return (long)rpoint;
	}
    }

    /* processing */
    points -= 40;

    while (points > 9)
    {
	expl += (long)inc;
        points -= 10;
        if (points > 9)
	{
	    expl += (long)inc;
	    inc *= 2;
	    points -= 10;
	}
    }

    expl += (long)points * (long)inc / 10;  

    if (ch->pcdata->tier != 2)
    {
	return (long)expl * (long)pc_race_table[ch->race].class_mult[ch->class]/100;
    } else {
	long rpoint;

	rpoint	= ( ( ( (long)expl * (long)pc_race_table[ch->race].class_mult[ch->class]/100 )
		+     ( (long)expl * (long)pc_race_table[ch->race].class_mult[ch->class]/100 )
		+     ( (long)expl * (long)pc_race_table[ch->race].class_mult[ch->clasb]/100 ) )
		/       3 );
	return (long)rpoint;
    }
}

/* this procedure handles the input parsing for the skill generator */
bool parse_gen_groups(CHAR_DATA *ch,char *argument)
{
    char arg[MIL];
    char buf[100];
    int gn,sn,i;
 
    if (argument[0] == '\0')
	return FALSE;

    if (IS_NPC(ch))
	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 (argument[0] == '\0')
	{
	    send_to_char("You must provide a skill name.\n\r",ch);
	    return TRUE;
	}

	gn = group_lookup(argument);
	if (gn != -1)
	{
	    bool found = FALSE;

	    if (ch->gen_data->group_chosen[gn]
	    ||  ch->pcdata->group_known[gn])
	    {
		send_to_char("You already know that group!\n\r",ch);
		return TRUE;
	    }

	    if (group_table[gn].rating[ch->class] < 1)
	    {
		found = TRUE;
	    }
	    if (ch->pcdata->tier == 2)
		if (group_table[gn].rating[ch->clasb] > 0)
		    found = FALSE;
	    if (found)
	    {
	  	send_to_char("That group is not available.\n\r",ch);
	 	return TRUE;
	    }
	    found = FALSE;
	    if (exp_per_level(ch,ch->gen_data->points_chosen+group_table[gn].rating[ch->class]) > 500000)
	    {
		found = TRUE;
		send_to_char("That will take you over the Exp/Level limit.\n\r",ch);
		return TRUE;
	    }
	    if (ch->pcdata->tier == 2)
		if (exp_per_level(ch,ch->gen_data->points_chosen+group_table[gn].rating[ch->clasb]) <= 500000)
		    found = FALSE;
	    if (found)
	    {
		send_to_char("That will take you over the Exp/Level limit.\n\r",ch);
		return TRUE;
	    }

	    sprintf(buf,"%s group added\n\r",group_table[gn].name);
	    send_to_char(buf,ch);
	    ch->gen_data->group_chosen[gn] = TRUE;
	    if (ch->pcdata->tier != 2)
	    {
		ch->gen_data->points_chosen += group_table[gn].rating[ch->class];
	    } else {
		if (group_table[gn].rating[ch->class] < 1)
		{
		    ch->gen_data->points_chosen += group_table[gn].rating[ch->clasb];
		} else if (group_table[gn].rating[ch->clasb] < 1)
		{
		    ch->gen_data->points_chosen += group_table[gn].rating[ch->class];
		} else {
		    ch->gen_data->points_chosen += UMIN(
			group_table[gn].rating[ch->class],
			group_table[gn].rating[ch->clasb]);
		}
	    }
	    gn_add(ch,gn);
	    if (ch->pcdata->tier != 2)
	    {
		ch->pcdata->points += group_table[gn].rating[ch->class];
	    } else {
		if (group_table[gn].rating[ch->class] < 1)
		{
		    ch->pcdata->points += group_table[gn].rating[ch->clasb];
		} else if (group_table[gn].rating[ch->clasb] < 1)
		{
		    ch->pcdata->points += group_table[gn].rating[ch->class];
		} else {
		    ch->pcdata->points += UMIN(
			group_table[gn].rating[ch->class],
			group_table[gn].rating[ch->clasb]);
		}
	    }
	    return TRUE;
	}

	sn = skill_lookup(argument);
	if (sn != -1)
	{
	    bool found = FALSE;

	    if (ch->gen_data->skill_chosen[sn]
	    ||  ch->pcdata->learned[sn] > 0)
	    {
		send_to_char("You already know that skill!\n\r",ch);
		return TRUE;
	    }

	    if (skill_table[sn].rating[ch->class] < 1)
		found = TRUE;
	    if (ch->pcdata->tier == 2)
		if (skill_table[sn].rating[ch->class] > 0)
		    found = FALSE;
	    if (skill_table[sn].spell_fun != spell_null)
		found = TRUE;
	    if (found)
	    {
		send_to_char("That skill is not available.\n\r",ch);
		return TRUE;
	    }

	    sprintf(buf, "%s skill added\n\r",skill_table[sn].name);
	    send_to_char(buf,ch);
	    ch->gen_data->skill_chosen[sn] = TRUE;
	    if (ch->pcdata->tier != 2)
	    {
		ch->gen_data->points_chosen += skill_table[sn].rating[ch->class];
	    } else {
		if (skill_table[sn].rating[ch->class] < 1)
		{
		    ch->gen_data->points_chosen += skill_table[sn].rating[ch->clasb];
		} else if (skill_table[sn].rating[ch->clasb] < 1)
		{
		    ch->gen_data->points_chosen += skill_table[sn].rating[ch->class];
		} else {
		    ch->gen_data->points_chosen += UMIN(
			skill_table[sn].rating[ch->class],
			skill_table[sn].rating[ch->clasb]);
		}
	    }
	    ch->pcdata->learned[sn] = 1;
	    if (ch->pcdata->tier != 2)
	    {
		ch->pcdata->points += skill_table[sn].rating[ch->class];
	    } else {
		if (skill_table[sn].rating[ch->class] < 1)
		{
		    ch->pcdata->points += skill_table[sn].rating[ch->clasb];
		} else if (skill_table[sn].rating[ch->clasb] < 1)
		{
		    ch->pcdata->points += skill_table[sn].rating[ch->class];
		} else {
		    ch->pcdata->points += UMIN(
			skill_table[sn].rating[ch->class],
			skill_table[sn].rating[ch->clasb]);
		}
	    }
	    return TRUE;
	}

	send_to_char("No skills or groups by that name...\n\r",ch);
	return TRUE;
    }

    if (!strcmp(arg,"drop"))
    {
	if (argument[0] == '\0')
  	{
	    send_to_char("You must provide a skill to drop.\n\r",ch);
	    return TRUE;
	}

	gn = group_lookup(argument);
	if (gn != -1 && ch->gen_data->group_chosen[gn])
	{
	    send_to_char("Group dropped.\n\r",ch);
	    ch->gen_data->group_chosen[gn] = FALSE;
	    if (ch->pcdata->tier != 2)
	    {
		ch->gen_data->points_chosen -= group_table[gn].rating[ch->class];
	    } else {
		if (group_table[gn].rating[ch->class] < 1)
		{
		    ch->gen_data->points_chosen -= group_table[gn].rating[ch->clasb];
		} else if (group_table[gn].rating[ch->clasb] < 1)
		{
		    ch->gen_data->points_chosen -= group_table[gn].rating[ch->class];
		} else {
		    ch->gen_data->points_chosen -= UMIN(
			group_table[gn].rating[ch->class],
			group_table[gn].rating[ch->clasb]);
		}
	    }
	    gn_remove(ch,gn);
	    for (i = 0; i < MAX_GROUP; i++)
	    {
		if (ch->gen_data->group_chosen[gn])
		    gn_add(ch,gn);
	    }
	    if (ch->pcdata->tier != 2)
	    {
		ch->pcdata->points -= group_table[gn].rating[ch->class];
	    } else {
		if (group_table[gn].rating[ch->class] < 1)
		{
		    ch->pcdata->points -= group_table[gn].rating[ch->clasb];
		} else if (group_table[gn].rating[ch->clasb] < 1)
		{
		    ch->pcdata->points -= group_table[gn].rating[ch->class];
		} else {
		    ch->pcdata->points -= UMIN(
			group_table[gn].rating[ch->class],
			group_table[gn].rating[ch->clasb]);
		}
	    }
	    return TRUE;
	}

	sn = skill_lookup(argument);
	if (sn != -1 && ch->gen_data->skill_chosen[sn])
	{
	    send_to_char("Skill dropped.\n\r",ch);
	    ch->gen_data->skill_chosen[sn] = FALSE;
	    if (ch->pcdata->tier != 2)
	    {
		ch->gen_data->points_chosen -= skill_table[sn].rating[ch->class];
	    } else {
		if (skill_table[sn].rating[ch->class] < 1)
		{
		    ch->gen_data->points_chosen -= skill_table[sn].rating[ch->clasb];
		} else if (skill_table[sn].rating[ch->clasb] < 1)
		{
		    ch->gen_data->points_chosen -= skill_table[sn].rating[ch->class];
		} else {
		    ch->gen_data->points_chosen -= UMIN(
			skill_table[sn].rating[ch->class],
			skill_table[sn].rating[ch->clasb]);
		}
	    }
	    ch->pcdata->learned[sn] = 0;
	    if (ch->pcdata->tier != 2)
	    {
		ch->pcdata->points -= skill_table[sn].rating[ch->class];
	    } else {
		if (skill_table[sn].rating[ch->class] < 1)
		{
		    ch->pcdata->points -= skill_table[sn].rating[ch->clasb];
		} else if (skill_table[sn].rating[ch->clasb] < 1)
		{
		    ch->pcdata->points -= skill_table[sn].rating[ch->class];
		} else {
		    ch->pcdata->points -= UMIN(
			skill_table[sn].rating[ch->class],
			skill_table[sn].rating[ch->clasb]);
		}
	    }
	    return TRUE;
	}

	send_to_char("You haven't bought any such skill or group.\n\r",ch);
	return TRUE;
    }

    if (!str_prefix(arg,"premise"))
    {
	do_help(ch,"premise");
	return TRUE;
    }

    if (!str_prefix(arg,"list"))
    {
	list_group_costs(ch);
	return TRUE;
    }

    if (!str_prefix(arg,"learned"))
    {
	list_group_chosen(ch);
	return TRUE;
    }

    if (!str_prefix(arg,"info"))
    {
	do_groups(ch,argument);
	return TRUE;
    }

    return FALSE;
}
        

/* shows all groups, or the sub-members of a group */
void do_groups(CHAR_DATA *ch, char *argument)
{
    char buf[100];
    int gn,sn,col;

    if (IS_NPC(ch))
	return;

    col = 0;

    send_to_char("Use the class command (help class) for a listing\n\r",ch);
    send_to_char("more in tune with your current character.\n\r\n\r",ch);

    if (argument[0] == '\0')
    {   /* show all groups */
	
	for (gn = 0; gn < MAX_GROUP; gn++)
        {
	    if (group_table[gn].name == NULL)
		break;
	    if (ch->pcdata->group_known[gn])
	    {
		sprintf(buf,"%-20s ",group_table[gn].name);
		send_to_char(buf,ch);
		if (++col % 3 == 0)
		    send_to_char("\n\r",ch);
	    }
        }
        if ( col % 3 != 0 )
            send_to_char( "\n\r", ch );
        sprintf(buf,"Creation points: %d\n\r",ch->pcdata->points);
	send_to_char(buf,ch);
	return;
     }

     if (!str_cmp(argument,"all"))    /* show all groups */
     {
        for (gn = 0; gn < MAX_GROUP; gn++)
        {
            if (group_table[gn].name == NULL)
                break;
	    sprintf(buf,"%-20s ",group_table[gn].name);
            send_to_char(buf,ch);
	    if (++col % 3 == 0)
            	send_to_char("\n\r",ch);
        }
        if ( col % 3 != 0 )
            send_to_char( "\n\r", ch );
	return;
     }
	
     
     /* show the sub-members of a group */
     gn = group_lookup(argument);
     if (gn == -1)
     {
	send_to_char("No group of that name exist.\n\r",ch);
	send_to_char(
	    "Type 'groups all' or 'info all' for a full listing.\n\r",ch);
	return;
     }

     for (sn = 0; sn < MAX_IN_GROUP; sn++)
     {
	if (group_table[gn].spells[sn] == NULL)
	    break;
	sprintf(buf,"%-20s ",group_table[gn].spells[sn]);
	send_to_char(buf,ch);
	if (++col % 3 == 0)
	    send_to_char("\n\r",ch);
     }
    if ( col % 3 != 0 )
        send_to_char( "\n\r", ch );
}

/* shows all groups, or the sub-members of a group available to your class*/
void do_class(CHAR_DATA *ch, char *argument)
{
    char arg[MIL];
    char buf[MSL];
    int gn,sn,tn,col,class,clasb;
    bool found = FALSE;

    if (IS_NPC(ch))
	return;

    col = 0;
    argument = one_argument(argument,arg);
    class = class_lookup(arg);
    if (class != -1)
	argument = one_argument(argument,arg);

    clasb = -1;
    if (arg[0] == '\0')
    {   /* show all groups */

	send_to_char( "Groups you currently have:\n\r", ch );
	send_to_char( "--------------------------\n\r", ch );
	for (gn = 0; gn < MAX_GROUP; gn++)
        {
	    if (group_table[gn].name == NULL)
		break;
	    if (ch->pcdata->group_known[gn])
	    {
		sprintf(buf,"%-20s ",group_table[gn].name);
		send_to_char(buf,ch);
		if (++col % 3 == 0)
		    send_to_char("\n\r",ch);
	    }
        }
        if ( col % 3 != 0 )
            send_to_char( "\n\r", ch );
        sprintf(buf,"Creation points: %d\n\r",ch->pcdata->points);
	send_to_char(buf,ch);
	buf[0] = '\0';
	return;
     }

     if (!str_cmp(arg,"all"))    /* show all groups */
     {
	if (class == -1)
	{
	    send_to_char( "{MGroups available to your character:{x\n\r", ch );
	    class = ch->class;
	    if (ch->pcdata->tier == 2)
		clasb = ch->clasb;
	}
	else
	    send_to_char( "{M  Groups available to this class:{x\n\r", ch );
	send_to_char( "-----------------------------------\n\r", ch );
        for (gn = 0; gn < MAX_GROUP; gn++)
        {
	    found = FALSE;

            if (group_table[gn].name == NULL)
                break;
	    if (group_table[gn].rating[class] > 0)
	    {
		found = TRUE;
		sprintf(buf,"{R%-20s {x",group_table[gn].name);
	    }
	    if ( ( ch->pcdata->tier == 2 ) && ( clasb != -1 ) )
	    {
		if (group_table[gn].rating[clasb] > 0)
		{
		    sprintf(buf,"{B%-20s {x",group_table[gn].name);
		    if (found)
		    {
			sprintf(buf,"{G%-20s {x",group_table[gn].name);
		    }
		    found = TRUE;
		}
	    }
	    if (found)
	    {
		send_to_char(buf,ch);
		if (++col % 3 == 0)
		    send_to_char("\n\r",ch);
	    }
        }
        if ( col % 3 != 0 )
            send_to_char( "\n\r", ch );
	buf[0] = '\0';
	return;
     }

     if (!str_cmp(arg,"skill"))    /* show all skills */
     {
	if (class == -1)
	{
	    send_to_char( "{MSkills available to your character:{x\n\r", ch );
	    class = ch->class;
	    if (ch->pcdata->tier == 2)
		clasb = ch->clasb;
	}
	else
	    send_to_char( "{M  Skills available to this class:{x\n\r", ch );
	send_to_char( "-----------------------------------\n\r", ch );
	send_to_char( "{MLevel{x-{YSkill{x--------------- {YLevel{x-{YSkill{x---------------\n\r", ch );
        for (sn = 0; sn < MAX_SKILL; sn++)
        {
	    found = FALSE;

            if (skill_table[sn].name == NULL)
                break;
 
            if (skill_table[sn].rating[class] > 0)
            {
		found = TRUE;
		sprintf(buf,"{R%-5d %-20s {x",
                    skill_table[sn].skill_level[class],skill_table[sn].name);
	    }
	    if ( ( ch->pcdata->tier == 2 ) && ( clasb != -1 ) )
	    {
		if (skill_table[sn].rating[clasb] > 0 )
		{
		    sprintf(buf,"{B%-5d %-20s {x",
			skill_table[sn].skill_level[clasb],skill_table[sn].name);
		    if (found)
		    {
			sprintf(buf,"{G%-5d %-20s {x",
			    UMIN(skill_table[sn].skill_level[class],
				 skill_table[sn].skill_level[clasb]),
			    skill_table[sn].name);
		    }
		    found = TRUE;
		}
	    }
	    if (skill_table[sn].spell_fun != spell_null)
		found = FALSE;
	    if (found)
	    {
                send_to_char(buf,ch);
		if (++col % 2 == 0)
		    send_to_char("\n\r",ch);
            }
        }
        if (col % 2 != 0)
            send_to_char("\n\r",ch);
	buf[0] = '\0';
	return;
     }

     /* show the sub-members of a group */
     gn = group_lookup(arg);
     if (gn == -1)
     {
	send_to_char("No group of that name exists.\n\r",ch);
	send_to_char("Type 'class all' for a full listing.\n\r",ch);
	return;
     }
     if ( (ch->pcdata->tier != 2) || (class != -1) )
     {
	if (class == -1)
	    class = ch->class;
	if (group_table[gn].rating[class] < 1)
	{
	    send_to_char("This class does not get that group.\n\r",ch);
	    send_to_char("Type 'class all' for a full listing.\n\r",ch);
	    return;
	}
     } else if (class != -1)
     {
	if (group_table[gn].rating[class] < 1)
	{
	    send_to_char("This class does not get that group.\n\r",ch);
	    send_to_char("Type 'class all' for a full listing.\n\r",ch);
	    return;
	}
     } else {
	class = ch->class;
	clasb = ch->clasb;
	if ( ( group_table[gn].rating[class] < 1 ) 
	&&   ( group_table[gn].rating[clasb] < 1 ) )
	{
	    send_to_char("This character does not get that group.\n\r",ch);
	    send_to_char("Type 'class all' for a full listing.\n\r",ch);
	    return;
	}
     }

    send_to_char( "{MSpells available in this group:{x\n\r", ch );
    send_to_char( "-------------------------------\n\r", ch );
    send_to_char( "{MLevel{x-{YSpell{x--------------- {MLevel{x-{YSpell{x---------------\n\r", ch );
    if (class == -1)
    {
	class = ch->class;
	if (ch->pcdata->tier == 2)
	    clasb = ch->clasb;
    }
    for (sn = 0; sn < MAX_IN_GROUP; sn++)
    {
	found = FALSE;
	if (group_table[gn].spells[sn] == NULL)
	    break;
	if ( ( tn = spell_avail_class( ch, group_table[gn].spells[sn] ) ) >= 0)
	{
	    found = TRUE;
	    sprintf(buf,"{R%-5d %-20s {x",
		skill_table[tn].skill_level[class], group_table[gn].spells[sn]);
	}
	if (ch->pcdata->tier == 2)
	{
	    if ( ( tn = spell_avail_clasb( ch, group_table[gn].spells[sn] ) ) >= 0)
	    {
		sprintf(buf,"{B%-5d %-20s {x",
		    skill_table[tn].skill_level[clasb], group_table[gn].spells[sn]);
	    }
	    if (found)
	    {
		sprintf(buf,"{G%-5d %-20s {x",
		    UMIN(skill_table[tn].skill_level[class],
			 skill_table[tn].skill_level[clasb]),
		    group_table[gn].spells[sn]);
	    }
	    found = TRUE;
	}
	if (found)
	{
	    send_to_char(buf,ch);
	    if (++col % 2 == 0)
		send_to_char("\n\r",ch);
	}
    }
    buf[0] = '\0';
    if ( col % 2 != 0 )
	send_to_char( "\n\r", ch );
}

int spell_avail( CHAR_DATA *ch, const char *name )
{
    /* checks to see if a spell is available to either class */
    int sn, found = -1;

    if (IS_NPC(ch))
	return skill_lookup(name);

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if (skill_table[sn].name == NULL)
	    break;
	if (LOWER(name[0]) == LOWER(skill_table[sn].name[0])
	&&  !str_prefix(name,skill_table[sn].name))
	{
	    if (skill_table[sn].skill_level[ch->class] <= LEVEL_HERO)
		    return sn;
	    if (ch->pcdata->tier == 2)
	    {
		if (skill_table[sn].skill_level[ch->clasb] <= LEVEL_HERO)
		    return sn;
	    }
	}
    }
    return found;
}
int spell_avail_class( CHAR_DATA *ch, const char *name )
{
    /* checks to see if a spell is available to a primary class */
    int sn, found = -1;

    if (IS_NPC(ch))
	return skill_lookup(name);

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if (skill_table[sn].name == NULL)
	    break;
	if (LOWER(name[0]) == LOWER(skill_table[sn].name[0])
	&&  !str_prefix(name,skill_table[sn].name))
	{
	    if (skill_table[sn].skill_level[ch->class] <= LEVEL_HERO)
		    return sn;
	}
    }
    return found;
}
int spell_avail_clasb( CHAR_DATA *ch, const char *name )
{
    /* checks to see if a spell is available to a secondary class */
    int sn, found = -1;

    if (IS_NPC(ch))
	return skill_lookup(name);

    if (ch->pcdata->tier != 2)
	return found;

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
	if (skill_table[sn].name == NULL)
	    break;
	if (LOWER(name[0]) == LOWER(skill_table[sn].name[0])
	&&  !str_prefix(name,skill_table[sn].name))
	{
	    if (skill_table[sn].skill_level[ch->clasb] <= LEVEL_HERO)
		    return sn;
	}
    }
    return found;
}

/* checks for skill improvement */
void check_improve( CHAR_DATA *ch, int sn, bool success, int multiplier )
{
    int chance;
    char buf[100];
    bool found = FALSE;

    if (IS_NPC(ch))
	return;

    if (ch->level < skill_table[sn].skill_level[ch->class]
    ||  skill_table[sn].rating[ch->class] == 0
    ||  ch->pcdata->learned[sn] == 0
    ||  ch->pcdata->learned[sn] == 100)
    {
	found = TRUE;
    }
    if (ch->pcdata->tier != 2)
    {
	if (found)
	    return;   /* skill is not known */
    } else if (found) {
	if (ch->level < skill_table[sn].skill_level[ch->clasb]
	||  skill_table[sn].rating[ch->clasb] == 0
	||  ch->pcdata->learned[sn] == 0
	||  ch->pcdata->learned[sn] == 100)
	{
	    return;	/* skill is not known */
	}
    }

    /* check to see if the character has a chance to learn */
    chance = 10 * int_app[get_curr_stat(ch,STAT_INT)].learn;
    if (ch->pcdata->tier != 2)
    {
	chance /= (		multiplier
			*	skill_table[sn].rating[ch->class] 
			*	4);
    } else {
	if (skill_table[sn].rating[ch->clasb] < 1)
	{
	    chance /= (		multiplier
			    *	skill_table[sn].rating[ch->class]
			    *	4);
	} else if (skill_table[sn].rating[ch->class] < 1)
	{
	    chance /= (		multiplier
			    *	skill_table[sn].rating[ch->clasb]
			    *	4);
	} else
	{
	    chance /= (		multiplier
			    *	UMIN(skill_table[sn].rating[ch->class],
				     skill_table[sn].rating[ch->clasb])
			    *	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)
	{
	    sprintf(buf,"You have become better at %s!\n\r",
		    skill_table[sn].name);
	    send_to_char(buf,ch);
	if ( global_prq ){
	    ch->pcdata->learned[sn]++;
	    ch->pcdata->learned[sn]++;
	    ch->pcdata->learned[sn]++;
	    ch->pcdata->learned[sn]++;
	} else {
	    ch->pcdata->learned[sn]++;
	}
	    if (!IS_SET(ch->act, PLR_LQUEST))
	    {
		if (ch->pcdata->tier != 2)
		{
		    gain_exp(ch,2 * skill_table[sn].rating[ch->class]);
		} else
		{
		    if (skill_table[sn].rating[ch->clasb] < 1)
		    {
			gain_exp(ch,2 * skill_table[sn].rating[ch->class]);
		    } else if (skill_table[sn].rating[ch->class] < 1)
		    {
			gain_exp(ch,2 * skill_table[sn].rating[ch->clasb]);
		    } else
		    {
			gain_exp(ch,2 * UMIN(skill_table[sn].rating[ch->class],
					 skill_table[sn].rating[ch->clasb]));
		    }
		}
	    }
	}
    }

    else
    {
	chance = URANGE(5,ch->pcdata->learned[sn]/2,30);
	if (number_percent() < chance)
	{
	    sprintf(buf,
		"You learn from your mistakes, and your %s skill improves.\n\r",
		skill_table[sn].name);
	    send_to_char(buf,ch);
	if ( global_prq ){
	    ch->pcdata->learned[sn] += number_range(8,16);
	    ch->pcdata->learned[sn] = UMIN(ch->pcdata->learned[sn],100);
	} else {
	    ch->pcdata->learned[sn] += number_range(1,3);
	    ch->pcdata->learned[sn] = UMIN(ch->pcdata->learned[sn],100);
	}
	    if (!IS_SET(ch->act, PLR_LQUEST))
	    {
		if (ch->pcdata->tier != 2)
		{
		    gain_exp(ch,2 * skill_table[sn].rating[ch->class]);
		} else
		{
		    if (skill_table[sn].rating[ch->clasb] < 1)
		    {
			gain_exp(ch,2 * skill_table[sn].rating[ch->class]);
		    } else if (skill_table[sn].rating[ch->class] < 1)
		    {
			gain_exp(ch,2 * skill_table[sn].rating[ch->clasb]);
		    } else
		    {
			gain_exp(ch,2 * UMIN(skill_table[sn].rating[ch->class],
					 skill_table[sn].rating[ch->clasb]));
		    }
		}
	    }
	}
    }
}

/* returns a group index number given the name */
int group_lookup( const char *name )
{
    int gn;
 
    for ( gn = 0; gn < MAX_GROUP; gn++ )
    {
        if ( group_table[gn].name == NULL )
            break;
        if ( LOWER(name[0]) == LOWER(group_table[gn].name[0])
        &&   !str_prefix( name, group_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;
    
    ch->pcdata->group_known[gn] = TRUE;
    for ( i = 0; i < MAX_IN_GROUP; i++)
    {
        if (group_table[gn].spells[i] == NULL)
            break;
        group_add(ch,group_table[gn].spells[i],FALSE);
    }
}

/* recusively removes a group given its number -- uses group_remove */
void gn_remove( CHAR_DATA *ch, int gn)
{
    int i;

    ch->pcdata->group_known[gn] = FALSE;

    for ( i = 0; i < MAX_IN_GROUP; i ++)
    {
	if (group_table[gn].spells[i] == NULL)
	    break;
	group_remove(ch,group_table[gn].spells[i]);
    }
}
	
/* use for processing a skill or group for addition  */
void group_add( CHAR_DATA *ch, const char *name, bool deduct)
{
    int sn,gn;

    if (IS_NPC(ch)) /* NPCs do not have skills */
	return;

    sn = skill_lookup(name);

    if (sn != -1)
    {
	if (ch->pcdata->learned[sn] == 0) /* i.e. not known */
	{
	    ch->pcdata->learned[sn] = 1;
	    if (deduct)
	    {
		if (ch->pcdata->tier != 2)
		{
		    ch->pcdata->points += skill_table[sn].rating[ch->class];
		} else
		{
		    if (skill_table[sn].rating[ch->clasb] < 1)
		    {
			ch->pcdata->points += skill_table[sn].rating[ch->class];
		    } else if (skill_table[sn].rating[ch->class] < 1)
		    {
			ch->pcdata->points += skill_table[sn].rating[ch->clasb];
		    } else
		    {
			ch->pcdata->points += UMIN(skill_table[sn].rating[ch->class],
						   skill_table[sn].rating[ch->clasb]);
		    }
		}
	    }

	}
	return;
    }
	
    /* now check groups */

    gn = group_lookup(name);

    if (gn != -1)
    {
	if (ch->pcdata->group_known[gn] == FALSE)  
	{
	    ch->pcdata->group_known[gn] = TRUE;
	    if (deduct)
	    {
		if (ch->pcdata->tier != 2)
		{
		    ch->pcdata->points += group_table[gn].rating[ch->class];
		} else
		{
		    if (group_table[gn].rating[ch->clasb] < 1)
		    {
			ch->pcdata->points += group_table[gn].rating[ch->class];
		    } else if (group_table[gn].rating[ch->class] < 1)
		    {
			ch->pcdata->points += group_table[gn].rating[ch->clasb];
		    } else
		    {
			ch->pcdata->points += UMIN(group_table[gn].rating[ch->class],
						   group_table[gn].rating[ch->clasb]);
		    }
		}
	    }
	}
	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 groups */
 
    gn = group_lookup(name);
 
    if (gn != -1 && ch->pcdata->group_known[gn] == TRUE)
    {
	ch->pcdata->group_known[gn] = FALSE;
	gn_remove(ch,gn);  /* be sure to call gn_add on all remaining groups */
    }
}