drm/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 Rom24/doc/rom.license			   *
***************************************************************************/

/*************************************************************************** 
*       ROT 1.4 is copyright 1996-1997 by Russ Walsh                       * 
*       By using this code, you have agreed to follow the terms of the     * 
*       ROT license, in the file doc/rot.license                           * 
***************************************************************************/

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

/* used to get new skills */
CH_CMD ( do_gain )
{
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    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;
    }

    one_argument ( argument, arg );

    if ( arg[0] == '\0' )
    {
        do_say ( trainer, "{aPardon me?{x" );
        return;
    }

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

        col = 0;

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

        for ( gn = 0; gn < MAX_GROUP; gn++ )
        {
            if ( group_table[gn].name == NULL )
                break;

            if ( !ch->pcdata->group_known[gn] &&
                 group_table[gn].rating[ch->class] > 0 )
            {
                sprintf ( buf, "{c%-18s {W%-5d ", group_table[gn].name,
                          group_table[gn].rating[ch->class] );
                send_to_char ( buf, ch );
                if ( ++col % 3 == 0 )
                    send_to_char ( "{x\n\r", ch );
            }
        }
        if ( col % 3 != 0 )
            send_to_char ( "\n\r", ch );

        send_to_char ( "\n\r", ch );

        col = 0;

        sprintf ( buf,
                  "{D%-5s {c%-16s {W%-5s {D%-5s {c%-16s {W%-5s {D%-5s {c%-16s {W%-5s{x\n\r",
                  "level", "skill", "cost", "level", "skill", "cost", "level",
                  "skill", "cost" );
        send_to_char ( buf, ch );

        for ( sn = 0; sn < MAX_SKILL; sn++ )
        {
            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 )
            {
                sprintf ( buf, "{w[{D%3d{w] {c%-18s {W%-3d{x ",
                          skill_table[sn].skill_level[ch->class],
                          skill_table[sn].name,
                          skill_table[sn].rating[ch->class] );
                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 -= 6;
        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 += 6;
        return;
    }

    if ( !str_prefix ( arg, "points" ) )
    {
        if ( ch->train < 2 )
        {
            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;
        }

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

        ch->train -= 2;
        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 ( group_table[gn].rating[ch->class] <= 0 )
        {
            act ( "$N tells you '{aThat 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 '{aYou 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 );
        ch->train -= group_table[gn].rating[ch->class];
        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 ( 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;
        }

        /* 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->class];
        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) */

CH_CMD ( do_spells )
{
    char spell_list[LEVEL_ANCIENT][MAX_STRING_LENGTH];
    char spell_columns[LEVEL_ANCIENT];
    int sn, lev, mana;
    bool found = FALSE;
    char buf[MAX_STRING_LENGTH];

    if ( IS_NPC ( ch ) )
        return;

    /* initilize data */
    for ( lev = 0; lev < LEVEL_ANCIENT; lev++ )
    {
        spell_columns[lev] = 0;
        spell_list[lev][0] = '\0';
    }

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
        if ( skill_table[sn].name == NULL )
            break;

        if ( skill_table[sn].skill_level[ch->class] < LEVEL_ANCIENT &&
             skill_table[sn].spell_fun != spell_null &&
             ch->pcdata->learned[sn] > 0 )
        {
            found = TRUE;
            lev = skill_table[sn].skill_level[ch->class];
            if ( ch->level < lev )
                sprintf ( buf, "{W%-19s{g  n/a      ", skill_table[sn].name );
            else
            {
                mana =
                    UMAX ( skill_table[sn].min_mana,
                           100 / ( 2 + ch->level - lev ) );
                sprintf ( buf, "{W%-18s {g %3d {xmana  ",
                          skill_table[sn].name, mana );
            }

            if ( spell_list[lev][0] == '\0' )
                sprintf ( spell_list[lev], "\n\r{WLevel %4d: {g%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_ANCIENT; lev++ )
        if ( spell_list[lev][0] != '\0' )
            send_to_char ( spell_list[lev], ch );
    send_to_char ( "\n\r", ch );
}

CH_CMD ( do_skills )
{
    char skill_list[LEVEL_ANCIENT][MAX_STRING_LENGTH];
    char skill_columns[LEVEL_ANCIENT];
    int sn, lev;
    bool found = FALSE;
    char buf[MAX_STRING_LENGTH];

    if ( IS_NPC ( ch ) )
        return;

    /* initilize data */
    for ( lev = 0; lev < LEVEL_ANCIENT; lev++ )
    {
        skill_columns[lev] = 0;
        skill_list[lev][0] = '\0';
    }

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
        if ( skill_table[sn].name == NULL )
            break;

        if ( skill_table[sn].skill_level[ch->class] < LEVEL_ANCIENT &&
             skill_table[sn].spell_fun == spell_null &&
             ch->pcdata->learned[sn] > 0 )
        {
            found = TRUE;
            lev = skill_table[sn].skill_level[ch->class];
            if ( ch->level < lev )
                sprintf ( buf, "{W%-16s {gn/a{x      ",
                          skill_table[sn].name );
            else
                sprintf ( buf, "{W%-16s {g%3d%%{x      ",
                          skill_table[sn].name, ch->pcdata->learned[sn] );

            if ( skill_list[lev][0] == '\0' )
                sprintf ( skill_list[lev], "\n\r{WLevel %3d: {g%s{x", 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_ANCIENT; 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, "{c%-18s {W%-5s {c%-18s {W%-5s {c%-18s {W%-5s{x\n\r",
              "group", "cp", "group", "cp", "group", "cp" );
    send_to_char ( buf, ch );

    for ( gn = 0; gn < MAX_GROUP; gn++ )
    {
        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 )
        {
            sprintf ( buf, "{c%-18s {W%-5d{x ", group_table[gn].name,
                      group_table[gn].rating[ch->class] );
            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, "{c%-18s {W%-2s {c%-18s {W%-2s {c%-18s {W%-2s{x\n\r",
              "skill", "cp", "skill", "cp", "skill", "cp" );
    send_to_char ( buf, ch );

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
        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 )
        {
            sprintf ( buf, "{c%-18s {W%-2d{x ", skill_table[sn].name,
                      skill_table[sn].rating[ch->class] );
            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, "{WCreation points: {R%d{x\n\r", ch->pcdata->points );
    send_to_char ( buf, ch );
    sprintf ( buf, "{WExperience per level: {B%ld{x\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, "%-18s %-5s %-18s %-5s %-18s %-5s{x", "group", "cp",
              "group", "cp", "group", "cp\n\r" );
    send_to_char ( buf, ch );

    for ( gn = 0; gn < MAX_GROUP; gn++ )
    {
        if ( group_table[gn].name == NULL )
            break;

        if ( ch->gen_data->group_chosen[gn] &&
             group_table[gn].rating[ch->class] > 0 )
        {
            sprintf ( buf, "%-18s %-5d{x ", group_table[gn].name,
                      group_table[gn].rating[ch->class] );
            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, "{c%-18s {W%-2s {c%-18s {W%-2s {c%-18s {W%-2s", "skill",
              "cp", "skill", "cp", "skill", "cp\n\r" );
    send_to_char ( buf, ch );

    for ( sn = 0; sn < MAX_SKILL; sn++ )
    {
        if ( skill_table[sn].name == NULL )
            break;

        if ( ch->gen_data->skill_chosen[sn] &&
             skill_table[sn].rating[ch->class] > 0 )
        {
            sprintf ( buf, "{c%-18s {W%-2d ", skill_table[sn].name,
                      skill_table[sn].rating[ch->class] );
            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, "{WCreation points: {R%d{x\n\r",
              ch->gen_data->points_chosen );
    send_to_char ( buf, ch );
    sprintf ( buf, "{WExperience per level: {B%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 )
        return 1000 * pc_race_table[ch->race].class_mult[ch->class] / 100;

    /* 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;

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

/* this procedure handles the input parsing for the skill generator */
bool parse_gen_groups ( CHAR_DATA * ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    char buf[100];
    int gn, sn, i;

    if ( argument[0] == '\0' )
        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 )
        {
            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 )
            {
                send_to_char ( "That group is not available.\n\r", ch );
                return TRUE;
            }
            if ( exp_per_level
                 ( ch,
                   ch->gen_data->points_chosen +
                   group_table[gn].rating[ch->class] ) > 500000 )
            {
                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;
            ch->gen_data->points_chosen += group_table[gn].rating[ch->class];
            gn_add ( ch, gn );
            ch->pcdata->points += group_table[gn].rating[ch->class];
            return TRUE;
        }

        sn = skill_lookup ( argument );
        if ( sn != -1 )
        {
            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 ||
                 skill_table[sn].spell_fun != spell_null )
            {
                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;
            ch->gen_data->points_chosen += skill_table[sn].rating[ch->class];
            ch->pcdata->learned[sn] = 1;
            ch->pcdata->points += skill_table[sn].rating[ch->class];
            return TRUE;
        }

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

    if ( !str_cmp ( 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;
            ch->gen_data->points_chosen -= group_table[gn].rating[ch->class];
            gn_remove ( ch, gn );
            for ( i = 0; i < MAX_GROUP; i++ )
            {
                if ( ch->gen_data->group_chosen[gn] )
                    gn_add ( ch, gn );
            }
            ch->pcdata->points -= group_table[gn].rating[ch->class];
            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;
            ch->gen_data->points_chosen -= skill_table[sn].rating[ch->class];
            ch->pcdata->learned[sn] = 0;
            ch->pcdata->points -= skill_table[sn].rating[ch->class];
            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 );
//        send_to_char("Sorry that is currently a buggy choice, but will be fixed soon.\n\r",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 */
CH_CMD ( do_groups )
{
    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*/
CH_CMD ( do_class )
{
    char buf[100];
    int gn, sn, tn, col;

    if ( IS_NPC ( ch ) )
        return;

    col = 0;

    if ( argument[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 );
        return;
    }

    if ( !str_cmp ( argument, "all" ) ) /* show all groups */
    {
        send_to_char ( "Groups available to your class:\n\r", ch );
        send_to_char ( "-------------------------------\n\r", ch );
        for ( gn = 0; gn < MAX_GROUP; gn++ )
        {
            if ( group_table[gn].name == NULL )
                break;
            if ( group_table[gn].rating[ch->class] > 0 )
            {
                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 'class all' for a full listing.\n\r", ch );
        return;
    }

    send_to_char ( "Spells available in this group:\n\r", ch );
    send_to_char ( "-------------------------------\n\r", ch );
    send_to_char
        ( "{BLevel{x-{GSpell{x--------------- {BLevel{x-{GSpell{x---------------\n\r",
          ch );
    for ( sn = 0; sn < MAX_IN_GROUP; sn++ )
    {
        if ( group_table[gn].spells[sn] == NULL )
            break;
        if ( ( tn = spell_avail ( ch, group_table[gn].spells[sn] ) ) >= 0 )
        {
            sprintf ( buf, "{B%-5d {G%-20s ",
                      skill_table[tn].skill_level[ch->class],
                      group_table[gn].spells[sn] );
            send_to_char ( buf, ch );
            if ( ++col % 2 == 0 )
                send_to_char ( "{x\n\r", ch );
        }
    }
    if ( col % 2 != 0 )
        send_to_char ( "{x\n\r", ch );
}

int spell_avail ( CHAR_DATA * ch, const char *name )
{
    /* checks to see if a spell is available to a 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_ANCIENT )
                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];

    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 )
        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;
    chance /= ( multiplier * skill_table[sn].rating[ch->class] * 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 );
            ch->pcdata->learned[sn]++;
            gain_exp ( ch, 2 * skill_table[sn].rating[ch->class] );
        }
    }

    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 );
            ch->pcdata->learned[sn] += number_range ( 1, 3 );
            ch->pcdata->learned[sn] = UMIN ( ch->pcdata->learned[sn], 100 );
            gain_exp ( ch, 2 * skill_table[sn].rating[ch->class] );
        }
    }
}

/* 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 )
                ch->pcdata->points += skill_table[sn].rating[ch->class];
        }
        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 )
                ch->pcdata->points += group_table[gn].rating[ch->class];
        }
        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 */
    }
}

CH_CMD ( do_showskill )
{
    char buf[MAX_STRING_LENGTH];
    int skill, class = 0;
    int i;

    strcpy ( buf, "" );
    /*argument = one_argument(argument,arg1); */

    if ( argument[0] == '\0' )
    {
        send_to_char ( "Syntax: showskill [skill/spell]\n\r", ch );
        return;
    }

    if ( ( skill = skill_lookup ( argument ) ) == -1 )
    {
        send_to_char ( "Skill not found.\n\r", ch );
        return;
    }

    sprintf ( buf, "{xClasses and levels for %s{x:\n\r",
              skill_table[skill].name );
    send_to_char ( buf, ch );
    /* send_to_char("{CClass       Level Gain{x\n\r",ch); */
    /* send_to_char("{C----------- ----- ----{x\n\r",ch); */
    i = 0;
    for ( class = 0; class < MAX_CLASS; class++ )
    {
        if ( skill_table[skill].skill_level[class] > LEVEL_ANCIENT )
            continue;
        i++;
        /*sprintf(buf,"{m%-11s {c%5d {m%4d{x\n\r",class_table[class].name,
           skill_table[skill].skill_level[class],
           skill_table[skill].rating[class]); */
        sprintf ( buf, "{C%-13s{x: {c%3d  {x", class_table[class].name,
                  skill_table[skill].skill_level[class] );
        buf[2] = UPPER ( buf[2] );
        send_to_char ( buf, ch );
        if ( i == 3 )
        {
            send_to_char ( "\n\r", ch );
            i = 0;
        }
    }
    return;
}

CH_CMD ( do_showclass )
{
    char arg1[MAX_INPUT_LENGTH];
    char buf[MAX_STRING_LENGTH];

    int class, level, skill;

    int i;

    strcpy ( buf, "" );
    argument = one_argument ( argument, arg1 );

    if ( arg1[0] == '\0' )
    {
        send_to_char ( "Syntax: showclass [class]\n\r", ch );
        return;
    }
    if ( ( class = class_lookup ( arg1 ) ) == -1 )
    {
        send_to_char ( "Class not found.\n\r", ch );
        return;
    }

    sprintf ( buf, "{xSpells/skills for %s:{x\n\r", class_table[class].name );
    send_to_char ( buf, ch );
    i = 0;
    for ( level = 1; level <= LEVEL_ANCIENT; level++ )
    {
        for ( skill = 0; skill < MAX_SKILL; skill++ )
        {
            if ( skill_table[skill].skill_level[class] != level )
                continue;
            i++;
            sprintf ( buf, "{CLevel %3d: {x: {c%-20s{x  ", level,
                      skill_table[skill].name );
            send_to_char ( buf, ch );
            if ( i == 2 )
            {
                send_to_char ( "\n\r", ch );
                i = 0;
            }
        }
    }
}