muddy/area/
muddy/bin/
muddy/bin/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/corefiles/
muddy/corefiles/CVS/
muddy/doc/CVS/
muddy/doc/cvsup/
muddy/doc/cvsup/CVS/
muddy/doc/muddy/
muddy/doc/muddy/CVS/
muddy/doc/olc/CVS/
muddy/etc/
muddy/etc/CVS/
muddy/gods/
muddy/gods/CVS/
muddy/lang/CVS/
muddy/msgdb/
muddy/msgdb/CVS/
muddy/notes/
muddy/notes/CVS/
muddy/player/
muddy/races/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/compat/
muddy/src/compat/CVS/
muddy/src/compat/mkdep/
muddy/src/compat/mkdep/CVS/
muddy/src/compat/regex-win32/CVS/
muddy/src/db/CVS/
muddy/src/mudprogs/CVS/
muddy/src/olc/CVS/
muddy/tmp/CVS/
/*
 * $Id: skills.c,v 1.45 1998/12/17 21:05:44 fjoe Exp $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR  *
 *     ANATOLIA has been brought to you by ANATOLIA consortium		   *
 *	 Serdar BULUT {Chronos}		bulut@rorqual.cc.metu.edu.tr       *
 *	 Ibrahim Canpunar  {Asena}	canpunar@rorqual.cc.metu.edu.tr    *	
 *	 Murat BICER  {KIO}		mbicer@rorqual.cc.metu.edu.tr	   *	
 *	 D.Baris ACAR {Powerman}	dbacar@rorqual.cc.metu.edu.tr	   *	
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *	
 ***************************************************************************/

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

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"
#include "update.h"

#include "resource.h"

varr skills = { sizeof(SKILL_DATA), 8 };

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

int	ch_skill_nok	(CHAR_DATA *ch , int sn);

/* used to converter of prac and train */
void do_gain(CHAR_DATA *ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *tr;

	if (IS_NPC(ch))
		return;

	/* find a trainer */
	for (tr = ch->in_room->people; tr; tr = tr->next_in_room)
		if (IS_NPC(tr)
		&&  IS_SET(tr->pIndexData->act,
			   ACT_PRACTICE | ACT_TRAIN | ACT_GAIN))
			break;

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

	one_argument(argument, arg);

	if (arg[0] == '\0') {
		do_say(tr, "You may convert 10 practices into 1 train.");
		do_say(tr, "You may revert 1 train into 10 practices.");
		do_say(tr, "Simply type 'gain convert' or 'gain revert'.");
		return;
	}

	if (!str_prefix(arg, "revert")) {
		if (ch->train < 1) {
			do_tell_raw(tr, ch, "You are not yet ready.");
			return;
		}

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

	if (!str_prefix(arg, "convert")) {
		if (ch->practice < 10) {
			do_tell_raw(tr, ch, "You are not yet ready.");
			return;
		}

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

	do_tell_raw(tr, ch, "I do not understand...");
}


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

void do_spells(CHAR_DATA *ch, const char *argument)
{
	char spell_list[LEVEL_IMMORTAL+1][MAX_STRING_LENGTH];
	char spell_columns[LEVEL_IMMORTAL+1];
	int lev;
	int i;
	bool found = FALSE;
	char buf[MAX_STRING_LENGTH];
	BUFFER *output;

	if (IS_NPC(ch))
		return;
	
	/* initialize data */
	for (lev = 0; lev <= LEVEL_IMMORTAL; lev++) {
		spell_columns[lev] = 0;
		spell_list[lev][0] = '\0';
	}
	
	for (i = 0; i < ch->pcdata->learned.nused; i++) {
		PC_SKILL *ps = VARR_GET(&ch->pcdata->learned, i);
		SKILL_DATA *sk;

		if (ps->percent == 0
		||  (sk = skill_lookup(ps->sn)) == NULL
		||  sk->spell_fun == NULL)
			continue;

		found = TRUE;
		lev = skill_level(ch, ps->sn);

		if (lev > (IS_IMMORTAL(ch) ? LEVEL_IMMORTAL : LEVEL_HERO))
			continue;

		if (ch->level < lev)
			snprintf(buf, sizeof(buf), "%-18s  n/a      ",
				 sk->name);
		else
			snprintf(buf, sizeof(buf), "%-18s  %3d mana  ",
				 sk->name, mana_cost(ch, ps->sn));
			
		if (spell_list[lev][0] == '\0')
			snprintf(spell_list[lev], sizeof(spell_list[lev]),
				 "\nLevel %2d: %s", lev, buf);
		else { /* append */
			if (++spell_columns[lev] % 2 == 0)
				strnzcat(spell_list[lev], "\n          ",
					 sizeof(spell_list[lev]));
			strnzcat(spell_list[lev], buf, sizeof(spell_list[lev]));
		}
	}

	/* return results */
	
	if (!found) {
		char_puts("You know no spells.\n",ch);
		return;
	}
	
	output = buf_new(-1);
	for (lev = 0; lev <= LEVEL_IMMORTAL; lev++)
		if (spell_list[lev][0] != '\0')
			buf_add(output, spell_list[lev]);
	buf_add(output, "\n");
	page_to_char(buf_string(output), ch);
	buf_free(output);
}

void do_skills(CHAR_DATA *ch, const char *argument)
{
	char skill_list[LEVEL_IMMORTAL+1][MAX_STRING_LENGTH];
	char skill_columns[LEVEL_IMMORTAL+1];
	int lev;
	int i;
	bool found = FALSE;
	char buf[MAX_STRING_LENGTH];
	BUFFER *output;
	
	if (IS_NPC(ch))
		return;
	
	/* initialize data */
	for (lev = 0; lev <= LEVEL_IMMORTAL; lev++) {
		skill_columns[lev] = 0;
		skill_list[lev][0] = '\0';
	}
	
	for (i = 0; i < ch->pcdata->learned.nused; i++) {
		PC_SKILL *ps = VARR_GET(&ch->pcdata->learned, i);
		SKILL_DATA *sk;

		if (ps->percent == 0
		||  (sk = skill_lookup(ps->sn)) == NULL
		||  sk->spell_fun)
			continue;

		found = TRUE;
		lev = skill_level(ch, ps->sn);

		if (lev > (IS_IMMORTAL(ch) ? LEVEL_IMMORTAL : LEVEL_HERO))
			continue;

		snprintf(buf, sizeof(buf),
			 ch->level < lev ?
				"%-18s n/a      " : "%-18s %3d%%      ",
			 sk->name, ps->percent);

		if (skill_list[lev][0] == '\0')
			snprintf(skill_list[lev], sizeof(skill_list[lev]),
				 "\nLevel %2d: %s", lev, buf);
		else { /* append */
			if (++skill_columns[lev] % 2 == 0)
				strnzcat(skill_list[lev], "\n          ",
					 sizeof(skill_list[lev]));
			strnzcat(skill_list[lev], buf, sizeof(skill_list[lev]));
		}
	}
	
	/* return results */
	
	if (!found) {
		char_puts("You know no skills.\n",ch);
		return;
	}
	
	output = buf_new(-1);
	for (lev = 0; lev <= LEVEL_IMMORTAL; lev++)
		if (skill_list[lev][0] != '\0')
			buf_add(output, skill_list[lev]);
	buf_add(output, "\n");
	page_to_char(buf_string(output), ch);
	buf_free(output);
}

int base_exp(CHAR_DATA *ch)
{
	int expl;
	CLASS_DATA *cl;
	RACE_DATA *r;
	RACE_CLASS_DATA *rcl;

	if (IS_NPC(ch)
	||  (cl = class_lookup(ch->class)) == NULL
	||  (r = race_lookup(ch->pcdata->race)) == NULL
	||  !r->pcdata
	||  (rcl = race_class_lookup(r, cl->name)) == NULL)
		return 1500;

	expl = 1000 + r->pcdata->points + cl->points;
	return expl * rcl->mult/100;
}

int exp_for_level(CHAR_DATA *ch, int level)
{
	int i = base_exp(ch) * level;
	return i + i * (level-1) / 20;
}

int exp_to_level(CHAR_DATA *ch)
{ 
	return exp_for_level(ch, ch->level+1) - ch->exp;
}

/* checks for skill improvement */
void check_improve(CHAR_DATA *ch, int sn, bool success, int multiplier)
{
	PC_SKILL *ps;
	CLASS_DATA *cl;
	CLASS_SKILL *cs;
	int chance;
	int rating;

	if (IS_NPC(ch)
	||  (cl = class_lookup(ch->class)) == NULL
	||  (ps = pc_skill_lookup(ch, sn)) == NULL
	||  ps->percent == 0 || ps->percent == 100
	||  skill_level(ch, sn) > ch->level)
		return;

	if ((cs = class_skill_lookup(cl, sn)))
		rating = cs->rating;
	else
		rating = 1;

	/* check to see if the character has a chance to learn */
	chance = 10 * int_app[get_curr_stat(ch,STAT_INT)].learn;
	chance /= (multiplier *	rating * 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 - ps->percent, 95);
		if (number_percent() < chance) {
			act_nprintf(ch, NULL, NULL, TO_CHAR, POS_DEAD,
				    MSG_HAVE_BECOME_BETTER, skill_name(sn));
			ps->percent++;
			gain_exp(ch, 2 * rating);
		}
	}
	else {
		chance = URANGE(5, ps->percent / 2, 30);
		if (number_percent() < chance) {
			act_nprintf(ch, NULL, NULL, TO_CHAR, POS_DEAD,
				    MSG_LEARN_FROM_MISTAKES, skill_name(sn));
			if ((ps->percent += number_range(1, 3)) > 100)
				ps->percent = 100;
			gain_exp(ch, 2 * rating);
		}
	}
}

/*
 * simply adds sn to ch's known skills (if skill is not already known).
 */
void set_skill_raw(CHAR_DATA *ch, int sn, int percent, bool replace)
{
	PC_SKILL *ps;

	if (sn <= 0)
		return;

	if ((ps = pc_skill_lookup(ch, sn))) {
		if (replace || ps->percent < percent)
			ps->percent = percent;
		return;
	}
	ps = varr_enew(&ch->pcdata->learned);
	ps->sn = sn;
	ps->percent = percent;
	varr_qsort(&ch->pcdata->learned, cmpint);
}

/* use for adding/updating all skills available for that ch  */
void update_skills(CHAR_DATA *ch)
{
	int i;
	CLASS_DATA *cl;
	RACE_DATA *r;
	CLAN_DATA *clan;
	const char *p;

/* NPCs do not have skills */
	if (IS_NPC(ch)
	||  (cl = class_lookup(ch->class)) == NULL
	||  (r = race_lookup(ch->race)) == NULL
	||  !r->pcdata)
		return;

/* add class skills */
	for (i = 0; i < cl->skills.nused; i++) {
		CLASS_SKILL *cs = VARR_GET(&cl->skills, i);
		set_skill_raw(ch, cs->sn, 1, FALSE);
	}

/* add race skills */
	for (i = 0; i < r->pcdata->skills.nused; i++) {
		RACE_SKILL *rs = VARR_GET(&r->pcdata->skills, i);
		set_skill_raw(ch, rs->sn, 100, FALSE);
	}

	if ((p = r->pcdata->bonus_skills))
		for (;;) {
			int sn;
			char name[MAX_STRING_LENGTH];

			p = one_argument(p, name);
			if (name[0] == '\0')
				break;
		
			sn = sn_lookup(name);
			if (sn < 0)
				continue;

			set_skill_raw(ch, sn, 100, FALSE);
		}

/* add clan skills */
	if ((clan = clan_lookup(ch->clan))) {
		for (i = 0; i < clan->skills.nused; i++) {
			CLAN_SKILL *cs = VARR_GET(&clan->skills, i);
			set_skill_raw(ch, cs->sn, 1, FALSE);
		}
	}

/* remove not matched skills */
	for (i = 0; i < ch->pcdata->learned.nused; i++) {
		PC_SKILL *ps = VARR_GET(&ch->pcdata->learned, i);
		if (skill_level(ch, ps->sn) > LEVEL_HERO && !IS_IMMORTAL(ch))
			ps->percent = 0;
	}
}

void set_skill(CHAR_DATA *ch, int sn, int percent)
{
	set_skill_raw(ch, sn, percent, TRUE);
}

DO_FUN(do_glist)
{
	char arg[MAX_INPUT_LENGTH];
	int col = 0;
	flag_t group = GROUP_NONE;
	int sn;

	one_argument(argument, arg);
	
	if (arg[0] == '\0') {
		char_puts("Syntax: glist group\n"
			  "Use 'glist ?' to get the list of groups.\n", ch);
		return;
	}

	if (!str_cmp(arg, "?")) {
		show_flags(ch, skill_groups);
		return;
	}

	if (str_prefix(arg, "none")
	&&  (group = flag_value(skill_groups, arg)) == 0) {
		char_puts("That is not a valid group.\n", ch);
		do_glist(ch, str_empty);
		return;
	}

	char_printf(ch, "Now listing group '%s':\n",
		    flag_string(skill_groups, group));

	for (sn = 0; sn < skills.nused; sn++) {
		SKILL_DATA *sk = VARR_GET(&skills, sn);
		if (group == sk->group) {
			char_printf(ch, "%c%-18s",
				    pc_skill_lookup(ch, sn) ? '*' : ' ',
				    sk->name);
			if (col)
				char_puts("\n", ch);
			col = 1 - col;
		}
	}

	if (col)
		char_puts("\n", ch);
}

void do_slook(CHAR_DATA *ch, const char *argument)
{
	int sn = -1;
	char arg[MAX_INPUT_LENGTH];

	one_argument(argument,arg);
	if (arg[0] == '\0') {
		char_puts("Syntax : slook <skill | spell>\n",ch);
		return;
	}

	if (!IS_NPC(ch)) {
		PC_SKILL *ps;
		if ((ps = skill_vlookup(&ch->pcdata->learned, arg)))
			sn = ps->sn;
	}

	if (sn < 0 && (sn = sn_lookup(arg)) < 0) { 
		char_puts("That is not a spell or skill.\n",ch);
		return; 
	}

	char_printf(ch, "Skill '%s' in group '%s'.\n",
		    SKILL(sn)->name,
		    flag_string(skill_groups, SKILL(sn)->group));
}

#define PC_PRACTICER	123

void do_learn(CHAR_DATA *ch, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	int sn;
	CHAR_DATA *mob;
	int adept;
	CLASS_DATA *cl;
	CLASS_SKILL *cs;
	PC_SKILL *ps;
	SKILL_DATA *sk;
	int rating;

	if (IS_NPC(ch) || (cl = class_lookup(ch->class)) == NULL)
		return;

	if (!IS_AWAKE(ch)) {
		char_puts("In your dreams, or what?\n", ch);
		return;
	}	

	if (argument[0] == '\0') {
		char_puts("Syntax: learn <skill | spell> <player>\n", ch);
		return;
	}

	if (ch->practice <= 0) {
		char_puts("You have no practice sessions left.\n", ch);
		return;
	}

	argument = one_argument(argument,arg);

	if ((ps = skill_vlookup(&ch->pcdata->learned, arg)) == NULL
	||  ps->percent == 0
	||  skill_level(ch, sn = ps->sn) > ch->level) {
		char_puts("You can't learn that.\n", ch);
		return;
	}

	ps = pc_skill_lookup(ch, sn);

	if (sn == gsn_vampire) {
		char_puts("You can't practice that, only available "
			  "at questor.\n", ch);
		return;
	}	

	argument = one_argument(argument,arg);
		
	if ((mob = get_char_room(ch,arg)) == NULL) {
		char_puts("Your hero is not here.\n", ch);
		return;
	}
			
	if (IS_NPC(mob) || mob->level != HERO) {
		char_puts("You must find a hero, not an ordinary one.\n",
			  ch);
		return;
	}

	if (mob->status != PC_PRACTICER) {
		char_puts("Your hero doesn't want to teach you anything.\n",ch);
		return;
	}

	if (get_skill(mob, sn) < 100) {
		char_puts("Your hero doesn't know that skill enough to teach you.\n",ch);
		return;
	}

	sk = SKILL(sn);
	adept = cl->skill_adept;

	if (ps->percent >= adept) {
		char_printf(ch, "You are already learned at %s.\n",
			    sk->name);
		return;
	}

	ch->practice--;

	cs = class_skill_lookup(cl, sn);
	rating = cs ? UMAX(cs->rating, 1) : 1;
	ps->percent += int_app[get_curr_stat(ch,STAT_INT)].learn / rating;

	act("You teach $T.", mob, NULL, sk->name, TO_CHAR);
	act("$n teaches $T.", mob, NULL, sk->name, TO_ROOM);
	mob->status = 0;

	if (ps->percent < adept) {
		act("You learn $T.", ch, NULL, sk->name, TO_CHAR);
		act("$n learn $T.", ch, NULL, sk->name, TO_ROOM);
	}
	else {
		ps->percent = adept;
		act("You are now learned at $T.", ch, NULL, sk->name, TO_CHAR);
		act("$n is now learned at $T.", ch, NULL, sk->name, TO_ROOM);
	}
}

void do_teach(CHAR_DATA *ch, const char *argument)
{
	if (IS_NPC(ch) || ch->level != LEVEL_HERO) {
		char_puts("You must be a hero.\n",ch);
		return;
	}
	ch->status = PC_PRACTICER;
	char_puts("Now, you can teach youngsters your 100% skills.\n",ch);
}

const char *skill_name(int sn)
{
	SKILL_DATA *sk = varr_get(&skills, sn);
	if (sk)
		return sk->name;
	return "none";
}

/* for returning skill information */
int get_skill(CHAR_DATA *ch, int sn)
{
	int skill;
	SKILL_DATA *sk;

	if ((sk = skill_lookup(sn)) == NULL)
		return 0;

	if (!IS_NPC(ch)) {
		PC_SKILL *ps;

		if ((ps = pc_skill_lookup(ch, sn)) == NULL
		||  skill_level(ch, sn) > ch->level)
			skill = 0;
		else
			skill = ps->percent;
	}
	else {
		flag_t act = ch->pIndexData->act;
		flag_t off = ch->pIndexData->off_flags;

		/* mobiles */
		if (sk->spell_fun)
			skill = 40 + 2 * ch->level;
		else if (sn == gsn_track)
			skill = 100;
		else if ((sn == gsn_sneak || sn == gsn_hide || sn == gsn_pick)
		     &&  IS_SET(act, ACT_THIEF))
			skill = ch->level * 2 + 20;
		else if (sn == gsn_backstab
		     &&  (IS_SET(act, ACT_THIEF) ||
			  IS_SET(off, OFF_BACKSTAB)))
			skill = ch->level * 2 + 20;
		else if (sn == gsn_dual_backstab
		     &&  (IS_SET(act, ACT_THIEF) ||
			  IS_SET(off, OFF_BACKSTAB)))
			skill = ch->level + 20;
		else if ((sn == gsn_dodge && IS_SET(off, OFF_DODGE)) ||
 		         (sn == gsn_parry && IS_SET(off, OFF_PARRY)) ||
			 (sn == gsn_dirt && IS_SET(off, OFF_DIRT_KICK)))
			skill = ch->level * 2;
 		else if (sn == gsn_shield_block)
			skill = 10 + 2 * ch->level;
		else if (sn == gsn_second_attack &&
			 (IS_SET(act, ACT_WARRIOR | ACT_THIEF)))
			skill = 10 + 3 * ch->level;
		else if (sn == gsn_third_attack && IS_SET(act, ACT_WARRIOR))
			skill = 4 * ch->level - 40;
		else if (sn == gsn_fourth_attack && IS_SET(act, ACT_WARRIOR))
			skill = 4 * ch->level - 60;
		else if (sn == gsn_hand_to_hand)
			skill = 40 + 2 * ch->level;
 		else if (sn == gsn_trip && IS_SET(off, OFF_TRIP)) 
			skill = 10 + 3 * ch->level;
 		else if ((sn == gsn_bash || sn == gsn_bash_door) &&
			 IS_SET(off, OFF_BASH))
			skill = 10 + 3 * ch->level;
		else if (sn == gsn_kick && IS_SET(off, OFF_KICK))
			skill = 10 + 3 * ch->level;
		else if ((sn == gsn_critical) && IS_SET(act, ACT_WARRIOR))
			skill = ch->level;
		else if (sn == gsn_disarm &&
			 (IS_SET(off, OFF_DISARM) ||
			  IS_SET(act, ACT_WARRIOR | ACT_THIEF)))
			skill = 20 + 3 * ch->level;
		else if (sn == gsn_grip &&
			 (IS_SET(act, ACT_WARRIOR | ACT_THIEF)))
			skill = ch->level;
		else if ((sn == gsn_berserk || sn == gsn_tiger_power) &&
			 IS_SET(off, OFF_BERSERK))
			skill = 3 * ch->level;
		else if (sn == gsn_kick)
			skill = 10 + 3 * ch->level;
		else if (sn == gsn_rescue)
			skill = 40 + ch->level; 
		else if (sn == gsn_sword || sn == gsn_dagger ||
			 sn == gsn_spear || sn == gsn_mace ||
			 sn == gsn_axe || sn == gsn_flail ||
			 sn == gsn_whip || sn == gsn_polearm ||
			 sn == gsn_bow || sn == gsn_arrow || sn == gsn_lance)
			skill = 60 + 5 * ch->level / 2;
		else if (sn == gsn_crush && IS_SET(off, OFF_CRUSH))
			skill = 10 + 3 * ch->level;
		else 
			skill = 0;
	}

	if (ch->daze > 0) {
		if (sk->spell_fun)
			skill /= 2;
		else
			skill = 2 * skill / 3;
	}

	if (!IS_NPC(ch) && ch->pcdata->condition[COND_DRUNK]  > 10)
		skill = 9 * skill / 10;

	return URANGE(0, skill, 100);
}


/*
 * Lookup a skill by name.
 */
int sn_lookup(const char *name)
{
	int sn;

	if (IS_NULLSTR(name))
		return -1;

	for (sn = 0; sn < skills.nused; sn++)
		if (LOWER(name[0]) == LOWER(SKILL(sn)->name[0])
		&&  !str_prefix(name, SKILL(sn)->name))
			return sn;

	return -1;
}

int char_sn_lookup(CHAR_DATA *ch, const char *name)
{
	int i;

	if (IS_NULLSTR(name) || IS_NPC(ch))
		return -1;

	for (i = 0; i < ch->pcdata->learned.nused; i++) {
		PC_SKILL *ps = VARR_GET(&ch->pcdata->learned, i);
		SKILL_DATA *skill;

		if (ps->percent == 0
		||  (skill = skill_lookup(ps->sn)) == NULL
		||  skill_level(ch, ps->sn) > ch->level)
			continue;

		if (!str_prefix(name, skill->name))
			return ps->sn;
	}

	return -1;
}

/* for returning weapon information */
int get_weapon_sn(OBJ_DATA *wield)
{
	int sn;

	if (wield == NULL)
		return gsn_hand_to_hand;

	if (wield->pIndexData->item_type != ITEM_WEAPON)
		return 0;

	switch (wield->value[0]) {
	default :               sn = -1;		break;
	case(WEAPON_SWORD):     sn = gsn_sword;		break;
	case(WEAPON_DAGGER):    sn = gsn_dagger;	break;
	case(WEAPON_SPEAR):     sn = gsn_spear;		break;
	case(WEAPON_MACE):      sn = gsn_mace;		break;
	case(WEAPON_AXE):       sn = gsn_axe;		break;
	case(WEAPON_FLAIL):     sn = gsn_flail;		break;
	case(WEAPON_WHIP):      sn = gsn_whip;		break;
	case(WEAPON_POLEARM):   sn = gsn_polearm;	break;
	case(WEAPON_BOW):	sn = gsn_bow;		break;
	case(WEAPON_ARROW):	sn = gsn_arrow;		break;
	case(WEAPON_LANCE):	sn = gsn_lance;		break;
	}
	return sn;
}

int get_weapon_skill(CHAR_DATA *ch, int sn)
{
	 int sk;

/* -1 is exotic */
	if (sn == -1)
		sk = 3 * ch->level;
	else if (!IS_NPC(ch))
		sk = get_skill(ch, sn);
	else if (sn == gsn_hand_to_hand)
		sk = 40 + 2 * ch->level;
	else 
		sk = 40 + 5 * ch->level / 2;

	return URANGE(0, sk, 100);
} 

/*
 * Utter mystical words for an sn.
 */
void say_spell(CHAR_DATA *ch, int sn)
{
	char buf  [MAX_STRING_LENGTH];
	CHAR_DATA *rch;
	const char *pName;
	int iSyl;
	int length;
	int skill;

	struct syl_type
	{
		char *	old;
		char *	new;
	};

	static const struct syl_type syl_table[] =
	{
		{ " ",		" "		},
		{ "ar",		"abra"		},
		{ "au",		"kada"		},
		{ "bless",	"fido"		},
		{ "blind",	"nose"		},
		{ "bur",	"mosa"		},
		{ "cu",		"judi"		},
		{ "de",		"oculo"		},
		{ "en",		"unso"		},
		{ "light",	"dies"		},
		{ "lo",		"hi"		},
		{ "mor",	"zak"		},
		{ "move",	"sido"		},
		{ "ness",	"lacri"		},
		{ "ning",	"illa"		},
		{ "per",	"duda"		},
		{ "ra",		"gru"		},
		{ "fresh",	"ima"		},
		{ "re",		"candus"	},
		{ "son",	"sabru"		},
		{ "tect",	"infra"		},
		{ "tri",	"cula"		},
		{ "ven",	"nofo"		},
		{ "a", "a" }, { "b", "b" }, { "c", "q" }, { "d", "e" },
		{ "e", "z" }, { "f", "y" }, { "g", "o" }, { "h", "p" },
		{ "i", "u" }, { "j", "y" }, { "k", "t" }, { "l", "r" },
		{ "m", "w" }, { "n", "i" }, { "o", "a" }, { "p", "s" },
		{ "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" },
		{ "u", "j" }, { "v", "z" }, { "w", "x" }, { "x", "n" },
		{ "y", "l" }, { "z", "k" },
		{ str_empty, str_empty }
	};

	buf[0]	= '\0';
	for (pName = skill_name(sn); *pName != '\0'; pName += length) {
		for (iSyl = 0; (length = strlen(syl_table[iSyl].old)); iSyl++) {
			if (!str_prefix(syl_table[iSyl].old, pName)) {
				strcat(buf, syl_table[iSyl].new);
				break;
			}
		}
		if (length == 0)
			length = 1;
	}

	for (rch = ch->in_room->people; rch; rch = rch->next_in_room) {
		if (rch == ch)
			continue;

		skill = (get_skill(rch, gsn_spell_craft) * 9) / 10;
		if (skill < number_percent()) {
			act("$n utters the words, '$t'.", ch, buf, rch, TO_VICT);
			check_improve(rch, gsn_spell_craft, FALSE, 5);
		}
		else  {
			act("$n utters the words, '$t'.",
			    ch, skill_name(sn), rch, TO_VICT);
			check_improve(rch, gsn_spell_craft, TRUE, 5);
		}
	}
}

/* find min level of the skill for char */
int skill_level(CHAR_DATA *ch, int sn)
{
	int slevel = LEVEL_IMMORTAL;
	SKILL_DATA *sk;
	CLAN_DATA *clan;
	CLAN_SKILL *clan_skill;
	CLASS_DATA *cl;
	CLASS_SKILL *class_skill;
	RACE_DATA *r;
	RACE_SKILL *race_skill;

/* noone can use ill-defined skills */
/* broken chars can't use any skills */
	if (IS_NPC(ch)
	||  (sk = skill_lookup(sn)) == NULL
	||  (cl = class_lookup(ch->class)) == NULL
	||  (r = race_lookup(ch->race)) == NULL
	||  !r->pcdata)
		return slevel;

	if ((clan = clan_lookup(ch->clan))
	&&  (clan_skill = clan_skill_lookup(clan, sn)))
		slevel = UMIN(slevel, clan_skill->level);

	if ((class_skill = class_skill_lookup(cl, sn))) {
		slevel = UMIN(slevel, class_skill->level);
		if (is_name(sk->name, r->pcdata->bonus_skills))
			slevel = UMIN(slevel, 1);
	}

	if ((race_skill = race_skill_lookup(r, sn)))
		slevel = UMIN(slevel, race_skill->level);

	return slevel;
}

/*
 * assumes !IS_NPC(ch) && ch->level >= skill_level(ch, sn)
 */
int mana_cost(CHAR_DATA *ch, int sn)
{
	SKILL_DATA *sk;

	if ((sk = skill_lookup(sn)) == NULL)
		return 0;

	return UMAX(sk->min_mana, 100 / (2 + ch->level - skill_level(ch, sn)));
}

void *skill_vlookup(varr *v, const char *name)
{
	int i;

	if (IS_NULLSTR(name))
		return NULL;

	for (i = 0; i < v->nused; i++) {
		SKILL_DATA *skill;
		int *psn = (int*) VARR_GET(v, i);

		if ((skill = skill_lookup(*psn))
		&&  !str_prefix(name, skill->name))
			return psn;
	}

	return NULL;
}