/*
* RAM $Id: skills.c 64 2008-12-11 14:35:49Z quixadhal $
*/
/***************************************************************************
* 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-1998 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@hypercube.org) *
* Gabrielle Taylor (gtaylor@hypercube.org) *
* Brian Moore (zump@rom.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************/
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"
#include "strings.h"
#include "random.h"
#include "db.h"
#include "interp.h"
#include "magic.h"
#include "tables.h"
/* stuff for recycling gen_data */
GEN_DATA *gen_data_free = NULL;
GEN_DATA *new_gen_data( void )
{
static GEN_DATA gen_zero;
GEN_DATA *gen = NULL;
if ( gen_data_free == NULL )
gen = ( GEN_DATA * ) alloc_perm( sizeof( *gen ) );
else
{
gen = gen_data_free;
gen_data_free = gen_data_free->next;
}
*gen = gen_zero;
VALIDATE( gen );
return gen;
}
void free_gen_data( GEN_DATA *gen )
{
if ( !IS_VALID( gen ) )
return;
INVALIDATE( gen );
gen->next = gen_data_free;
gen_data_free = gen;
}
/* used to get new skills */
void do_gain( CHAR_DATA *ch, const char *argument )
{
char arg[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
CHAR_DATA *trainer = NULL;
int gn = -1;
int sn = -1;
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 ) )
{
ch_printf( ch, "You can't do that here.\r\n" );
return;
}
one_argument( argument, arg );
if ( arg[0] == '\0' )
{
do_function( trainer, &do_say, "Pardon me?" );
return;
}
if ( !str_prefix( arg, "list" ) )
{
int col = 0;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s\r\n",
"group", "cost", "group", "cost", "group", "cost" );
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->iclass] > 0 )
{
ch_printf( ch, "%-18s %-5d ",
group_table[gn].name, group_table[gn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "\r\n" );
col = 0;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s\r\n",
"skill", "cost", "skill", "cost", "skill", "cost" );
for ( sn = 0; sn < MAX_SKILL; sn++ )
{
if ( skill_table[sn].name == NULL )
break;
if ( !ch->pcdata->learned[sn]
&& skill_table[sn].rating[ch->iclass] > 0
&& skill_table[sn].spell_fun == spell_null )
{
ch_printf( ch, "%-18s %-5d ",
skill_table[sn].name, skill_table[sn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
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 += 1;
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 );
ch->train -= 2;
ch->pcdata->points -= 1;
ch->exp = 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 'You already know that group!'",
ch, NULL, trainer, TO_CHAR );
return;
}
if ( group_table[gn].rating[ch->iclass] <= 0 )
{
act( "$N tells you 'That group is beyond your powers.'",
ch, NULL, trainer, TO_CHAR );
return;
}
if ( ch->train < group_table[gn].rating[ch->iclass] )
{
act( "$N tells you 'You are not yet ready for that group.'",
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->iclass];
return;
}
sn = skill_lookup( argument );
if ( sn > -1 )
{
if ( skill_table[sn].spell_fun != spell_null )
{
act( "$N tells you 'You must learn the full group.'",
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->iclass] <= 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->iclass] )
{
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->iclass];
return;
}
act( "$N tells you 'I do not understand...'", ch, NULL, trainer, TO_CHAR );
}
/* RT spells and skills show the players spells (or skills) */
void do_spells( CHAR_DATA *ch, const char *argument )
{
BUFFER *buffer = NULL;
char arg[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
char spell_list[LEVEL_HERO + 1][MAX_STRING_LENGTH];
char spell_columns[LEVEL_HERO + 1];
int sn = 0;
int level = 0;
int min_lev = 1;
int max_lev = LEVEL_HERO;
int mana = 0;
bool fAll = false;
bool found = false;
char buf[MAX_STRING_LENGTH];
if ( IS_NPC( ch ) )
return;
if ( argument[0] != '\0' )
{
fAll = true;
if ( str_prefix( argument, "all" ) )
{
argument = one_argument( argument, arg );
if ( !is_number( arg ) )
{
ch_printf( ch, "Arguments must be numerical or all.\r\n" );
return;
}
max_lev = atoi( arg );
if ( max_lev < 1 || max_lev > LEVEL_HERO )
{
ch_printf( ch, "Levels must be between 1 and %d.\r\n", LEVEL_HERO );
return;
}
if ( argument[0] != '\0' )
{
argument = one_argument( argument, arg );
if ( !is_number( arg ) )
{
ch_printf( ch, "Arguments must be numerical or all.\r\n" );
return;
}
min_lev = max_lev;
max_lev = atoi( arg );
if ( max_lev < 1 || max_lev > LEVEL_HERO )
{
ch_printf( ch, "Levels must be between 1 and %d.\r\n", LEVEL_HERO );
return;
}
if ( min_lev > max_lev )
{
ch_printf( ch, "That would be silly.\r\n" );
return;
}
}
}
}
/*
* initialize data
*/
for ( level = 0; level < LEVEL_HERO + 1; level++ )
{
spell_columns[level] = 0;
spell_list[level][0] = '\0';
}
for ( sn = 0; sn < MAX_SKILL; sn++ )
{
if ( skill_table[sn].name == NULL )
break;
if ( ( level = skill_table[sn].skill_level[ch->iclass] ) < LEVEL_HERO + 1
&& ( fAll || level <= ch->level )
&& level >= min_lev && level <= max_lev
&& skill_table[sn].spell_fun != spell_null && ch->pcdata->learned[sn] > 0 )
{
found = true;
level = skill_table[sn].skill_level[ch->iclass];
if ( ch->level < level )
sprintf( buf, "%-18s n/a ", skill_table[sn].name );
else
{
mana = UMAX( skill_table[sn].min_mana, 100 / ( 2 + ch->level - level ) );
sprintf( buf, "%-18s %3d mana ", skill_table[sn].name, mana );
}
if ( spell_list[level][0] == '\0' )
sprintf( spell_list[level], "\r\nLevel %2d: %s", level, buf );
else /* append */
{
if ( ++spell_columns[level] % 2 == 0 )
strcat( spell_list[level], "\r\n " );
strcat( spell_list[level], buf );
}
}
}
/*
* return results
*/
if ( !found )
{
ch_printf( ch, "No spells found.\r\n" );
return;
}
buffer = new_buf( );
for ( level = 0; level < LEVEL_HERO + 1; level++ )
if ( spell_list[level][0] != '\0' )
add_buf( buffer, spell_list[level] );
add_buf( buffer, "\r\n" );
page_to_char( buf_string( buffer ), ch );
free_buf( buffer );
}
void do_skills( CHAR_DATA *ch, const char *argument )
{
BUFFER *buffer = NULL;
char arg[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
char skill_list[LEVEL_HERO + 1][MAX_STRING_LENGTH];
char skill_columns[LEVEL_HERO + 1];
int sn = -1;
int level = 0;
int min_lev = 1;
int max_lev = LEVEL_HERO;
bool fAll = false;
bool found = false;
char buf[MAX_STRING_LENGTH];
if ( IS_NPC( ch ) )
return;
if ( argument[0] != '\0' )
{
fAll = true;
if ( str_prefix( argument, "all" ) )
{
argument = one_argument( argument, arg );
if ( !is_number( arg ) )
{
ch_printf( ch, "Arguments must be numerical or all.\r\n" );
return;
}
max_lev = atoi( arg );
if ( max_lev < 1 || max_lev > LEVEL_HERO )
{
ch_printf( ch, "Levels must be between 1 and %d.\r\n", LEVEL_HERO );
return;
}
if ( argument[0] != '\0' )
{
argument = one_argument( argument, arg );
if ( !is_number( arg ) )
{
ch_printf( ch, "Arguments must be numerical or all.\r\n" );
return;
}
min_lev = max_lev;
max_lev = atoi( arg );
if ( max_lev < 1 || max_lev > LEVEL_HERO )
{
ch_printf( ch, "Levels must be between 1 and %d.\r\n", LEVEL_HERO );
return;
}
if ( min_lev > max_lev )
{
ch_printf( ch, "That would be silly.\r\n" );
return;
}
}
}
}
/*
* initialize data
*/
for ( level = 0; level < LEVEL_HERO + 1; level++ )
{
skill_columns[level] = 0;
skill_list[level][0] = '\0';
}
for ( sn = 0; sn < MAX_SKILL; sn++ )
{
if ( skill_table[sn].name == NULL )
break;
if ( ( level = skill_table[sn].skill_level[ch->iclass] ) < LEVEL_HERO + 1
&& ( fAll || level <= ch->level )
&& level >= min_lev && level <= max_lev
&& skill_table[sn].spell_fun == spell_null && ch->pcdata->learned[sn] > 0 )
{
found = true;
level = skill_table[sn].skill_level[ch->iclass];
if ( ch->level < level )
sprintf( buf, "%-18s n/a ", skill_table[sn].name );
else
sprintf( buf, "%-18s %3d%% ", skill_table[sn].name,
ch->pcdata->learned[sn] );
if ( skill_list[level][0] == '\0' )
sprintf( skill_list[level], "\r\nLevel %2d: %s", level, buf );
else /* append */
{
if ( ++skill_columns[level] % 2 == 0 )
strcat( skill_list[level], "\r\n " );
strcat( skill_list[level], buf );
}
}
}
/*
* return results
*/
if ( !found )
{
ch_printf( ch, "No skills found.\r\n" );
return;
}
buffer = new_buf( );
for ( level = 0; level < LEVEL_HERO + 1; level++ )
if ( skill_list[level][0] != '\0' )
add_buf( buffer, skill_list[level] );
add_buf( buffer, "\r\n" );
page_to_char( buf_string( buffer ), ch );
free_buf( buffer );
}
/* shows skills, groups and costs (only if not bought) */
void list_group_costs( CHAR_DATA *ch )
{
int gn = -1;
int sn = -1;
int col = 0;
if ( IS_NPC( ch ) )
return;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s\r\n", "group", "cp", "group", "cp",
"group", "cp" );
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->iclass] > 0 )
{
ch_printf( ch, "%-18s %-5d ", group_table[gn].name,
group_table[gn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "\r\n" );
col = 0;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s\r\n", "skill", "cp", "skill", "cp",
"skill", "cp" );
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->iclass] > 0 )
{
ch_printf( ch, "%-18s %-5d ", skill_table[sn].name,
skill_table[sn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "\r\n" );
ch_printf( ch, "Creation points: %d\r\n", ch->pcdata->points );
ch_printf( ch, "Experience per level: %d\r\n",
exp_per_level( ch, ch->gen_data->points_chosen ) );
return;
}
void list_group_chosen( CHAR_DATA *ch )
{
int gn = -1;
int sn = -1;
int col = 0;
if ( IS_NPC( ch ) )
return;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s", "group", "cp", "group", "cp",
"group", "cp\r\n" );
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->iclass] > 0 )
{
ch_printf( ch, "%-18s %-5d ", group_table[gn].name,
group_table[gn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "\r\n" );
col = 0;
ch_printf( ch, "%-18s %-5s %-18s %-5s %-18s %-5s", "skill", "cp", "skill", "cp",
"skill", "cp\r\n" );
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->iclass] > 0 )
{
ch_printf( ch, "%-18s %-5d ", skill_table[sn].name,
skill_table[sn].rating[ch->iclass] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "\r\n" );
ch_printf( ch, "Creation points: %d\r\n", ch->gen_data->points_chosen );
ch_printf( ch, "Experience per level: %d\r\n",
exp_per_level( ch, ch->gen_data->points_chosen ) );
return;
}
int exp_per_level( CHAR_DATA *ch, int points )
{
int expl = 1000;
int inc = 500;
if ( IS_NPC( ch ) )
return 1000;
if ( points < 40 )
return 1000 * ( pc_race_table[ch->race].class_mult[ch->iclass] ?
pc_race_table[ch->race].class_mult[ch->iclass] / 100 : 1 );
/*
* processing
*/
points -= 40;
while ( points > 9 )
{
expl += inc;
points -= 10;
if ( points > 9 )
{
expl += inc;
inc *= 2;
points -= 10;
}
}
expl += points * inc / 10;
return expl * pc_race_table[ch->race].class_mult[ch->iclass] / 100;
}
/* this procedure handles the input parsing for the skill generator */
bool parse_gen_groups( CHAR_DATA *ch, const char *argument )
{
char arg[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
int gn = -1;
int sn = -1;
int i = 0;
if ( argument[0] == '\0' )
return false;
argument = one_argument( argument, arg );
if ( !str_prefix( arg, "help" ) )
{
if ( argument[0] == '\0' )
{
do_function( ch, &do_help, "group help" );
return true;
}
do_function( ch, &do_help, argument );
return true;
}
if ( !str_prefix( arg, "add" ) )
{
if ( argument[0] == '\0' )
{
ch_printf( ch, "You must provide a skill name.\r\n" );
return true;
}
gn = group_lookup( argument );
if ( gn != -1 )
{
if ( ch->gen_data->group_chosen[gn] || ch->pcdata->group_known[gn] )
{
ch_printf( ch, "You already know that group!\r\n" );
return true;
}
if ( group_table[gn].rating[ch->iclass] < 1 )
{
ch_printf( ch, "That group is not available.\r\n" );
return true;
}
/*
* Close security hole
*/
if ( ch->gen_data->points_chosen + group_table[gn].rating[ch->iclass] > 300 )
{
ch_printf( ch, "You cannot take more than 300 creation points.\r\n" );
return true;
}
ch_printf( ch, "%s group added\r\n", group_table[gn].name );
ch->gen_data->group_chosen[gn] = true;
ch->gen_data->points_chosen += group_table[gn].rating[ch->iclass];
gn_add( ch, gn );
ch->pcdata->points += group_table[gn].rating[ch->iclass];
return true;
}
sn = skill_lookup( argument );
if ( sn != -1 )
{
if ( ch->gen_data->skill_chosen[sn] || ch->pcdata->learned[sn] > 0 )
{
ch_printf( ch, "You already know that skill!\r\n" );
return true;
}
if ( skill_table[sn].rating[ch->iclass] < 1
|| skill_table[sn].spell_fun != spell_null )
{
ch_printf( ch, "That skill is not available.\r\n" );
return true;
}
/*
* Close security hole
*/
if ( ch->gen_data->points_chosen + skill_table[sn].rating[ch->iclass] > 300 )
{
ch_printf( ch, "You cannot take more than 300 creation points.\r\n" );
return true;
}
ch_printf( ch, "%s skill added\r\n", skill_table[sn].name );
ch->gen_data->skill_chosen[sn] = true;
ch->gen_data->points_chosen += skill_table[sn].rating[ch->iclass];
ch->pcdata->learned[sn] = 1;
ch->pcdata->points += skill_table[sn].rating[ch->iclass];
return true;
}
ch_printf( ch, "No skills or groups by that name...\r\n" );
return true;
}
if ( !strcmp( arg, "drop" ) )
{
if ( argument[0] == '\0' )
{
ch_printf( ch, "You must provide a skill to drop.\r\n" );
return true;
}
gn = group_lookup( argument );
if ( gn != -1 && ch->gen_data->group_chosen[gn] )
{
ch_printf( ch, "Group dropped.\r\n" );
ch->gen_data->group_chosen[gn] = false;
ch->gen_data->points_chosen -= group_table[gn].rating[ch->iclass];
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->iclass];
return true;
}
sn = skill_lookup( argument );
if ( sn != -1 && ch->gen_data->skill_chosen[sn] )
{
ch_printf( ch, "Skill dropped.\r\n" );
ch->gen_data->skill_chosen[sn] = false;
ch->gen_data->points_chosen -= skill_table[sn].rating[ch->iclass];
ch->pcdata->learned[sn] = 0;
ch->pcdata->points -= skill_table[sn].rating[ch->iclass];
return true;
}
ch_printf( ch, "You haven't bought any such skill or group.\r\n" );
return true;
}
if ( !str_prefix( arg, "premise" ) )
{
do_function( ch, &do_help, "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_function( ch, &do_groups, argument );
return true;
}
return false;
}
/* shows all groups, or the sub-members of a group */
void do_groups( CHAR_DATA *ch, const char *argument )
{
int gn = -1;
int sn = -1;
int col = 0;
if ( IS_NPC( ch ) )
return;
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] )
{
ch_printf( ch, "%-20s ", group_table[gn].name );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
ch_printf( ch, "Creation points: %d\r\n", ch->pcdata->points );
return;
}
if ( !str_cmp( argument, "all" ) ) /* show all groups */
{
for ( gn = 0; gn < MAX_GROUP; gn++ )
{
if ( group_table[gn].name == NULL )
break;
ch_printf( ch, "%-20s ", group_table[gn].name );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
return;
}
/*
* show the sub-members of a group
*/
gn = group_lookup( argument );
if ( gn == -1 )
{
ch_printf( ch, "No group of that name exist.\r\n" );
ch_printf( ch, "Type 'groups all' or 'info all' for a full listing.\r\n" );
return;
}
for ( sn = 0; sn < MAX_IN_GROUP; sn++ )
{
if ( group_table[gn].spells[sn] == NULL )
break;
ch_printf( ch, "%-20s ", group_table[gn].spells[sn] );
if ( ++col % 3 == 0 )
ch_printf( ch, "\r\n" );
}
if ( col % 3 != 0 )
ch_printf( ch, "\r\n" );
}
/* checks for skill improvement */
void check_improve( CHAR_DATA *ch, int sn, bool success, int multiplier )
{
int chance = 0;
if ( IS_NPC( ch ) )
return;
if ( ch->level < skill_table[sn].skill_level[ch->iclass]
|| skill_table[sn].rating[ch->iclass] == 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->iclass] * 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_printf( ch, "You have become better at %s!\r\n", skill_table[sn].name );
ch->pcdata->learned[sn]++;
gain_exp( ch, 2 * skill_table[sn].rating[ch->iclass] );
}
}
else
{
chance = URANGE( 5, ch->pcdata->learned[sn] / 2, 30 );
if ( number_percent( ) < chance )
{
ch_printf( ch,
"You learn from your mistakes, and your %s 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 );
gain_exp( ch, 2 * skill_table[sn].rating[ch->iclass] );
}
}
}
/* returns a group index number given the name */
int group_lookup( const char *name )
{
int gn = -1;
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 = 0;
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 = 0;
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 = -1;
int gn = -1;
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->iclass];
}
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->iclass];
}
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 = -1;
int gn = -1;
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 */
}
}