zim/area/
zim/bin/
zim/clans/plists/
zim/corefiles/
zim/doc/muddy/
zim/gods/
zim/log/
zim/player/
zim/skill_tree/
zim/tmp/
/*-
 * Copyright (c) 2006 Zsuzsu <little_zsuzsu@hotmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: waffects.c 877 2006-06-26 02:16:49Z zsuzsu $
 */

/*
 * Augmented weapons, those made my hero level characters to be ueber
 * weapons and focus items (which enhance skills/spells).  These are
 * items that gain experience, and power as the hero uses them.  They
 * are meant to be loot/sac-able so that PK should keep them relatively
 * non ultra-powerful.
 *
 * by Zsuzsu
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

#include "merc.h"
#include "debug.h"
#include "db/db.h"
#include "augment.h"

flag_t augment_type[] = 
{
        { "",			TABLE_BITVAL			},

        { "weapon",		AUGMENT_TYPE_WEAPON,	TRUE	},
        { "focus",		AUGMENT_TYPE_FOCUS,	TRUE	},
        { "relic",		AUGMENT_TYPE_RELIC,	TRUE	},

        { NULL }
};

typedef struct augmentation_data AUGMENTATION_DATA;
struct augmentation_data {
	const char 	*name;
	flag32_t	type;
	int		component_vnum;
	int		gp_cost_per_level;
	int		(*augment_cost)(CHAR_DATA *ch, OBJ_DATA *obj);
	bool		(*augment_buy)(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);
};


static int augment_cost_dicenumber (CHAR_DATA *ch, OBJ_DATA *obj);
static bool augment_buy_dicenumber (CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);
static int augment_cost_dicesize (CHAR_DATA *ch, OBJ_DATA *obj);
static bool augment_buy_dicesize (CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);
static int augment_cost_holy (CHAR_DATA *ch, OBJ_DATA *obj);
static bool augment_buy_holy(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);
static int augment_cost_flaming (CHAR_DATA *ch, OBJ_DATA *obj);
static bool augment_buy_flaming(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);
static int augment_cost_hatred (CHAR_DATA *ch, OBJ_DATA *obj);
static bool augment_buy_hatred(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *component, const char *argument);

AUGMENTATION_DATA augmentation_table[] = {

	{ "dicenumber",		AUGMENT_TYPE_WEAPON,
	AUGMENT_VNUM_DICENUMBER,
	1000,
	augment_cost_dicenumber,	
	augment_buy_dicenumber
	},

	{ "dicesize",		AUGMENT_TYPE_WEAPON,
	AUGMENT_VNUM_DICESIZE,
	500,
	augment_cost_dicesize,
	augment_buy_dicesize
	},

	{ "holy",		AUGMENT_TYPE_WEAPON,
	AUGMENT_VNUM_HOLY,
	500,
	augment_cost_holy,
	augment_buy_holy
	},

	{ "flaming",		AUGMENT_TYPE_WEAPON,
	AUGMENT_VNUM_FLAMING,
	500,
	augment_cost_flaming,
	augment_buy_flaming
	},

	{ "hatred",		AUGMENT_TYPE_WEAPON,
	0,
	4000,
	augment_cost_hatred,
	augment_buy_hatred
	},

	{ NULL }
};

void show_augment_menu (CHAR_DATA *ch, OBJ_DATA *obj);
void show_augment_focus_menu (CHAR_DATA *ch, OBJ_DATA *obj);
void show_augment_weapon_menu(CHAR_DATA *ch, OBJ_DATA *obj);
void do_consecrate_commands(CHAR_DATA *ch);
void do_consecrate_ritual (CHAR_DATA *ch, OBJ_DATA *obj, const char *argument);
void do_consecrate_buy (CHAR_DATA *ch, OBJ_DATA *obj, const char *argument);

int get_augment_exp_modifier(CHAR_DATA *ch, OBJ_DATA *obj);
int augment_exp_for_level (OBJ_DATA *obj, int target_level);
int get_augment_type (OBJ_DATA *obj);
bool can_augment_obj (CHAR_DATA *ch, OBJ_DATA *obj);
OBJ_AUGMENT_DATA *obj_augment_new(void);

/* more power should mean longer casting times, so you need to buy
 * and failure rates, so you need to buy down the others to 
 * keep them in balance.
 */
void do_consecrate (CHAR_DATA *ch, const char *argument)
{
	char	arg1[MAX_INPUT_LENGTH];
	char	arg2[MAX_INPUT_LENGTH];
	OBJ_DATA *obj = NULL;

	argument = one_argument(argument, arg1, sizeof(arg1));
	argument = one_argument(argument, arg2, sizeof(arg2));

	if (IS_NPC(ch)) {
		char_puts("Silly bean!", ch);
		return;
	}

	if (arg1[0] == '\0') {
		char_puts("Consecrate what?\n", ch);
		return;
	}

	if ((obj = get_obj_wear(ch, arg1)) == NULL) {
		char_puts("You're not wearing that.\n", ch);
		return;
	}

	if (!mlstr_null(obj->owner) 
	&& !IS_OWNER(ch, obj)) {
		act("This item is bound to another soul.",
			ch, NULL, NULL, TO_CHAR);
		return;
	}

	if (arg2[0] == '\0') {
		show_augment(ch, obj, NULL);
		return;
	}
	
	if (!str_cmp(arg2, "ritual")) {
		do_consecrate_ritual(ch, obj, argument);
		return;
	} 
	else if (!str_prefix(arg2, "ritua")) {
		char_puts("If you wish to perform the ritual, spell"
			" it out.\n",ch);
		return;

	} 
	else if (!str_prefix(arg2, "menu")) {
		show_augment_weapon_menu(ch, obj);
		return;
	} 
	else if (!str_prefix(arg2, "show")) {
		show_augment(ch, obj, NULL);
		return;
	}
	else if (!str_prefix(arg2, "augment")) {
		do_consecrate_buy(ch, obj, argument);
		return;
	}
	else {
		do_consecrate_commands(ch);
		return;
	}
}

void do_consecrate_commands(CHAR_DATA *ch)
{
	char_puts("consecrate syntax: consecrate <obj> <command>\n", ch);
	char_puts("consecrate commands: ritual, show, menu, augment\n", ch);
}

/**
 * initializes the object for augumentation.
 *
 */
void do_consecrate_ritual (CHAR_DATA *ch, OBJ_DATA *obj, const char *argument)
{
	int aug_type = get_augment_type(obj);
	char	arg1[MAX_INPUT_LENGTH];
	OBJ_AUGMENT_DATA *aug = NULL;
	pcskill_t *ps;
	int sn = 0;

	argument = one_argument(argument, arg1, sizeof(arg1));

	if (!IS_SET(ch->in_room->room_flags, 
		ROOM_HEROES_ONLY | ROOM_SOLITARY | ROOM_NOMOB)) {
		char_puts("This place is not sacred enough to perform the ritual.\n", ch);
		return;
	}

	if ((ch->gold + ch->pcdata->bank_g) < AUGMENT_COST_GOLD) {
		char_printf(ch, "You don't have the resources to bind"
			" this item to your soul.\n"
			"You need at least %d gold in the bank.\n",
			AUGMENT_COST_GOLD);
		return;
	}

	if (!can_augment_obj(ch, obj))
		return;

	switch(aug_type) {
	case AUGMENT_TYPE_FOCUS:
		if (arg1[0] == '\0') {
			act("What spell do you wish to channel through $p?",
				ch, obj, NULL, TO_CHAR);
			return;
		}

		ps = (pcskill_t*) skill_vlookup(&ch->pcdata->learned, arg1);
		if (!ps || get_skill(ch, sn = ps->sn) <= 0) {
			char_puts("You can't focus magic you don't know.\n", ch);
			return;
		}

		if (SKILL(sn)->spell_fun == NULL) {
			char_puts("Only spells can be focused.\n", ch);
			return;
		}

		if (obj->pIndexData->item_type == ITEM_WAND
		|| obj->pIndexData->item_type == ITEM_STAFF) {
			if (obj->value[ITEM_WAND_SPELL] != sn) {
				act("Those energies are foreign to $p!",
					ch, obj, NULL, TO_CHAR);
				act("You cross-bind $p, which blazes bright and is {Wgone{x.",
					ch, obj, NULL, TO_CHAR);
				act("$n's $p blazes bright and is {Wgone{x.",
					ch, obj, NULL, TO_ROOM);
				extract_obj(obj, 0);
				WAIT_STATE(ch, PULSE_VIOLENCE);
				return;
			}
		}
		else if (ps->percent < 100) {
			char_puts("You must master that spell first.\n",
				ch);
			return;
		}

		break;

	case AUGMENT_TYPE_WEAPON:
		break;
	}

	/* charge char the gold of the binding cost */
	if (ch->gold >= AUGMENT_COST_GOLD)
		ch->gold -= AUGMENT_COST_GOLD;
	else {
		ch->pcdata->bank_g -= AUGMENT_COST_GOLD - ch->gold;
		ch->gold = 0;
	}

	aug = obj_augment_new();
	aug->consecration_time = current_time;
	aug->last_level = current_time;
	aug->exp_modifier = get_augment_exp_modifier(ch, obj);
	
	switch (aug_type) {
	case AUGMENT_TYPE_FOCUS:
		aug->skill_sn = sn;
		break;

	case AUGMENT_TYPE_WEAPON:
		break;
	}

	obj->augment = aug;
	obj->level = get_wear_level(ch, obj);
	obj->owner = mlstr_dup(ch->short_descr);

	act("You tap into your wealth to modify $p to your purpose.",
		ch, obj, NULL, TO_CHAR);

	act("$p comes alive as you imbune it with your spirit and bind it"
		" to your soul.", ch, obj, NULL, TO_CHAR);
	act("The effort exhausts you completely.", ch, NULL, NULL, TO_CHAR);
	act("$n focuses all $h concentration on $p, and looks exhausted.",
		ch, obj, NULL, TO_ROOM);

	ch->hit = ch->hit * 10/100;
	ch->mana = ch->mana * 10/100;
	ch->move = ch->move * 10/100;

	aug->level = 1;
	WAIT_STATE(ch, PULSE_VIOLENCE *2);
}

/*
 * show the augments on this item.
 */
void show_augment (CHAR_DATA *ch, OBJ_DATA *obj, BUFFER *output)
{
	bool free_output = FALSE;
	int i = 0;

	if (!is_augmented(obj)) {
		act("$p is not consecrated.",
			ch, obj, NULL, TO_CHAR);
		return;
	}

	if (!mlstr_null(obj->owner) 
	&& !IS_OWNER(ch, obj)
	&& !IS_IMMORTAL(ch)) {
		act("This item is bound to another soul.",
			ch, NULL, NULL, TO_CHAR);
		return;
	}

	if (!output) {
		output = buf_new(-1);
		free_output = TRUE;
	}

	buf_printf(output,
		"%s is consecrated!\n",
		mlstr_mval(obj->short_descr));

	buf_printf(output,
		"   Level:       {c%d{x\n"
		"   Exp:         {c%d{x\n"
		"   Tnl:         {c%d{x\n"
		"   Points:      {c%d{x\n",
		obj->augment->level,
		obj->augment->exp,
		augment_tnl (obj),
		obj->augment->pts);

	switch (get_augment_type(obj)) {
	case AUGMENT_TYPE_WEAPON:
		buf_printf(output,
			"   DiceNumber:  {c%d{x\n"
			"   DiceSize:    {c%d{x\n"
			"   Flags:       [{c%s{x]\n"
			"   Hatred:      [{c%s{x]\n",
			obj->augment->dicenum,
			obj->augment->dicesize,
			flag_string(weapon_type2, obj->augment->weapon_flags),
			(obj->augment->racial_hatred != RACE_NEUTRAL) 
				? race_name(obj->augment->racial_hatred) 
				: "none"
			);

		break;

	case AUGMENT_TYPE_FOCUS:
		buf_printf(output,
			"   Spell:       '{c%s{x'\n"
			"   Mastery:     {c%d{x\n"
			"   Focus:       {c%d{x\n"
			"   Mana:        {c%d{x\n"
			"   Power:       {c%d{x\n",
		 	skill_name(obj->augment->skill_sn),
			obj->augment->mastery_level,
			obj->augment->focus_level,
			obj->augment->mana_level,
			obj->augment->power_level);

		break;
	}

	if (IS_IMMORTAL(ch) && ch->level > (ML - 5)) {
		buf_printf(output,
			"   {DExpModifier: {c%d{x (IMM only info)\n",
			obj->augment->exp_modifier);
		for (i = 2; i <= 20; i++) {
			buf_printf(output, "   {DLevel %2d: {c%7d\n",
				i,
				augment_exp_for_level(obj, i));
		}
		buf_printf(output, "{x");
	}

	if (free_output) {
		send_to_char(buf_string(output), ch);
		buf_free(output);
	}
}

/**
 * buy augmentations to your item
 */
void do_consecrate_buy (CHAR_DATA *ch, OBJ_DATA *obj, const char *argument)
{
	char arg1[MAX_INPUT_LENGTH];
	int cost = 1;
	AUGMENTATION_DATA *aug = NULL;
	OBJ_INDEX_DATA *pObjIndex = NULL;
	OBJ_DATA *component = NULL;
	int gp_cost = 0;

	argument = one_argument(argument, arg1, sizeof(arg1));

	if (!obj->augment) {
		act("$p isn't consecrated.",
			ch, obj, NULL, TO_CHAR);
		return;
	}

	if (arg1[0] == '\0') {
		show_augment_menu(ch, obj);
	}

	for (aug = augmentation_table; aug->name; aug++) {
		if (!is_name(arg1, aug->name))
			continue;

		if (get_augment_type(obj) != aug->type) {
			act("You can't augment $p in that way.",
				ch, obj, NULL, TO_CHAR);
			return;
		}
		cost = aug->augment_cost(ch, obj);
		if (cost > obj->augment->pts) {
			act("$p doesn't have enough augmentation points.",
				ch, obj, NULL, TO_CHAR);
			return;
		}

		gp_cost = (obj->augment->level > 0 ? obj->augment->level : 1) 
			* aug->gp_cost_per_level;
		if ((ch->gold + ch->pcdata->bank_g) < gp_cost) {
			char_printf(ch, "You don't have the resources to"
				" afford the research necessary for that"
				" augmentation.\n"
				"You need at least {Y%d gold{x in the bank.\n",
				gp_cost);
			return;
		}

		/* required augmentation component present? */
		if (aug->component_vnum != 0
		&& !(component = has_item(ch, aug->component_vnum, -1, FALSE))) {
			pObjIndex = get_obj_index(aug->component_vnum);
			if (pObjIndex == NULL) {
				BUG("augment component [%d] does not exist.",
					aug->component_vnum);
				return;
			}
			char_printf(ch, "Without %s, that would be impossible.",
				mlstr_mval(pObjIndex->short_descr));
			break;
		}

		/* okay, go ahead a buy */
		if (!aug->augment_buy(ch, obj, component, argument))
			continue;

		if (component) {
			obj_from_char(component);
			extract_obj(component, 0);
		}
			
		/* gp cost */
		char_printf(ch, "Your toil cost {Y%d{x gold.\n",
			gp_cost);

		if (ch->gold >= gp_cost)
			ch->gold -= gp_cost;
		else {
			ch->pcdata->bank_g -= gp_cost - ch->gold;
			ch->gold = 0;
		}

		/* pts */
		obj->augment->pts -= cost;
		break;
	}
}

/*
 * show the augment menu for whatever type of item this is.
 */
void show_augment_menu (CHAR_DATA *ch, OBJ_DATA *obj)
{
	switch (get_augment_type(obj)) {
	case AUGMENT_TYPE_WEAPON:
		show_augment_weapon_menu(ch, obj);
		break;

	case AUGMENT_TYPE_FOCUS:
		show_augment_focus_menu(ch, obj);
		break;
	}
}

/*
 * menu of possible weapon augmentations for this weapon
 */
void show_augment_weapon_menu(CHAR_DATA *ch, OBJ_DATA *obj)
{
	BUFFER *output;
	AUGMENTATION_DATA *aug;
	output = buf_new(-1);

	buf_printf(output,
		"Research into the following weapon augmentations are possible:\n");

	for (aug = augmentation_table; aug->name; aug++) {
		if (aug->type == AUGMENT_TYPE_WEAPON
		&& aug->augment_cost(ch, obj) > 0) {
			buf_printf(output,
				"   {c%-20s{x {c%2d{xpts\t{Y%6d{xgp\n",
				aug->name,
				aug->augment_cost(ch, obj),
				((obj->augment->level >0) 
					? obj->augment->level 
					: 1) 
					* aug->gp_cost_per_level);
		}
	}

	send_to_char(buf_string(output), ch);
	buf_free(output);
}

/*
 * menu of possible focus augmentations for this focus item
 */
void show_augment_focus_menu(CHAR_DATA *ch, OBJ_DATA *obj)
{
	BUFFER *output;
	AUGMENTATION_DATA *aug;
	output = buf_new(-1);

	buf_printf(output,
		"Research into the following focus augmentations are possible:\n");

	for (aug = augmentation_table; aug->name; aug++) {
		if (aug->type == AUGMENT_TYPE_FOCUS
		&& aug->augment_cost(ch, obj) > 0) {
			buf_printf(output,
				"%s costs %d\n",
				aug->name,
				aug->augment_cost(ch, obj));
		}
	}

	send_to_char(buf_string(output), ch);
	buf_free(output);
}

/**
 * certain items can be augmented in certain ways
 */
int get_augment_type (OBJ_DATA *obj)
{
	int type = AUGMENT_TYPE_NONE;

	if (obj->pIndexData->item_type == ITEM_WEAPON
		&& IS_SET(obj->pIndexData->wear_flags, ITEM_WIELD)) {
		type = AUGMENT_TYPE_WEAPON;
	}
	else if (obj->pIndexData->item_type != ITEM_WEAPON
		&& IS_SET(obj->pIndexData->wear_flags, ITEM_HOLD)) {
		type = AUGMENT_TYPE_FOCUS;
	}

	return type;
}

/*
 * it the object augmented?
 */
bool is_augmented (OBJ_DATA *obj)
{
	if (!obj || !obj->augment)
		return FALSE;
	return TRUE;
}

/*
 * is this an augmented object which is mine?
 */
bool is_not_my_augment (CHAR_DATA *ch, OBJ_DATA *obj)
{
	if (obj && obj->augment
	&& !mlstr_null(obj->owner) 
	&& !IS_OWNER(ch, obj))
		return TRUE;

	return FALSE;
}

/*
 * when someone who doesn't own an augmented item uses it, it 
 * gets purged to keep people from passing objects.
 */
OBJ_DATA * augment_unauthorized_use (CHAR_DATA *ch, OBJ_DATA *wield)
{
	act("Dissonance between your soul and $p cause it to {Cvaporize{x!",
		ch, wield, NULL, TO_CHAR);
	act("$n's $p shakes violently and {Cvaporizes{x!",
		ch, wield, NULL, TO_ROOM);
	obj_from_char(wield);
	extract_obj(wield, 0);
	wield = NULL;
	return wield;
}

/*
 * can this item be augmented?
 */
bool can_augment (OBJ_DATA *obj)
{
	if (IS_OBJ_LIMITED(obj->pIndexData))
		return FALSE;

	if (IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST))
		return FALSE;

	if (IS_SET(obj->pIndexData->extra_flags, 
		ITEM_NOSAC | ITEM_NOPURGE | ITEM_HAD_TIMER 
		| ITEM_MELT_DROP | ITEM_INVENTORY)) 
		return FALSE;

	return TRUE;
}

/**
 * find out if the character can augment this type of item
 * and tell them why they can't.
 */
bool can_augment_obj (CHAR_DATA *ch, OBJ_DATA *obj)
{
	int type = get_augment_type(obj);
	pcskill_t *ps = NULL;
	int sn = 0;

	if (ch->level <= AUGMENT_MIN_LEVEL) {
		act("You must become more powerful to extend your will"
				" over items in this realm.",
				ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}

	if (ch->class == CLASS_CLERIC) {
		act("Seek greater power by performing the will of your"
			" god, instead of material possessions.",
			ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}

	/* not the owner */
	if (!mlstr_null(obj->owner) 
	&& !IS_OWNER(ch, obj)) {
		act("This item is already bound to another soul.",
			ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}

	if (obj->augment) {
		act("This item is already bound to your soul.",
			ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}


	/* no artifacts */
	if (IS_OBJ_LIMITED(obj->pIndexData)) {
		act("This item is too ancient to be bound to your young soul.",
			ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}

	/* no quest items */
	if (IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST)) {
		act("Quest items can not be consecrated.",
			ch, NULL, NULL, TO_CHAR);
		return FALSE;
	}

	/* no non-sac items */
	if (IS_SET(obj->pIndexData->extra_flags, 
		ITEM_NOSAC | ITEM_NOPURGE | ITEM_HAD_TIMER 
		| ITEM_MELT_DROP | ITEM_INVENTORY)) {
		act("Something strange about $p makes you think better of"
			" performing the ritual on it.",
			ch, obj, NULL, TO_CHAR);
		return FALSE;
	}

	switch (type) {
	case AUGMENT_TYPE_NONE:
		act("You can't consecrate $p.",
			ch, obj, NULL, TO_CHAR);
		return FALSE;
		break;

	case AUGMENT_TYPE_WEAPON:
		if (ch->class == CLASS_WARLOCK
		|| ch->class == CLASS_WITCH
		|| ch->class == CLASS_NECROMANCER) {
			act("You could never atune your existence to an item"
				" of such brutish intent.",
				ch, NULL, NULL, TO_CHAR);
			return FALSE;
		}

		sn = get_weapon_sn(obj);
		ps = pcskill_lookup(ch, sn);

		if (ps->percent < 100) {
			act("You must first master the techniques of $p"
				" to consecrate it.",
				ch, obj, NULL, TO_CHAR);
			return FALSE;
		}

		if (IS_SET(obj->pIndexData->extra_flags, ITEM_ENCHANTED)) {
			act("Strange magic already binds $p to another source.",
				ch, obj, NULL, TO_CHAR);
			return FALSE;
		}

		break;

	case AUGMENT_TYPE_FOCUS:
		if (ch->class != CLASS_WARLOCK
		&& ch->class != CLASS_WITCH
		&& ch->class != CLASS_NECROMANCER) {
			act("You do not possess the intellectual discipline"
				" to focus your energies in such an object.",
				ch, NULL, NULL, TO_CHAR);
			return FALSE;
		}

		switch (obj->pIndexData->item_type) {
		case ITEM_GEM:
		case ITEM_JEWELRY:
		case ITEM_TREASURE:
		case ITEM_WARP_STONE:
		case ITEM_STAFF:
		case ITEM_WAND:
			break;
		default:
			act("You are unable to focus your powers with this"
				" type of item.",
				ch, NULL, NULL, TO_CHAR);
			return FALSE;

		}
		break;
	}
	
	return TRUE;
}


OBJ_DATA * get_augment_obj (CHAR_DATA *ch)
{
	OBJ_DATA *obj = NULL;
	for (obj = ch->carrying; obj; obj = obj->next_content) {
		if (obj->augment)
			return obj;
	}
	return NULL;
}

/**
 * items with higher BP value are harder to augment
 * returns a percentage.
 */
int get_augment_exp_modifier(CHAR_DATA *ch, OBJ_DATA *obj)
{
	int mod = 100;
	int bp = 0;
	int bpmax = 0;


	build_points(obj->pIndexData, &bp, &bpmax);

	if (obj->augment->skill_sn > 0) {
		
		mod += bp / 2;
		mod += skill_level(ch, obj->augment->skill_sn) / (LEVEL_HERO / 5);
	}
	else {
		mod += bp;
	}

	switch (obj->pIndexData->item_type) {
	case ITEM_WAND: mod = mod * AUGMENT_WAND_EXP_MOD /100; break;
	case ITEM_STAFF: mod = mod * AUGMENT_STAFF_EXP_MOD /100; break;
	}
	
	mod = UMAX(50, mod);
	return mod;
}

int augment_exp_for_level (OBJ_DATA *obj, int target_level)
{
	int i = 0;
	int tnl = AUGMENT_BASE_EXP;

	if (!obj->augment) return -1;

	tnl = tnl * obj->augment->exp_modifier / 100;

	for (i = 2; i < target_level; i++) {
		tnl += (tnl / 2);
	}

	return tnl;
}

int augment_tnl (OBJ_DATA *obj)
{
	int next_level = 0;
	
	if (!obj->augment) return -1;
	
	next_level = augment_exp_for_level(obj, obj->augment->level+1);

	return next_level - obj->augment->exp;
}

bool gain_obj_exp (CHAR_DATA *ch, OBJ_DATA *obj, int gain)
{
	int tnl = 0;
	char mesg[MAX_STRING_LENGTH];
	if (!obj->augment) return FALSE;
	if (ch->level < AUGMENT_MIN_LEVEL) return FALSE;
	if (mlstr_null(obj->owner) 
	|| !IS_OWNER(ch, obj)) return FALSE;

	tnl = augment_tnl(obj);

	obj->augment->exp += gain;

	if (tnl <= gain) {
		obj->augment->level++;
		obj->augment->pts++;
		act("$p {Craises a level!!{x",
			ch, obj, NULL, TO_CHAR);
		snprintf(mesg, sizeof(mesg),
			"{W$N's %s {Bhas attained level {Y$j!{x",
				mlstr_mval(obj->short_descr));
		wiznet(mesg, ch, (const void *) obj->augment->level, 
			WIZ_LEVELS, 0, 0);
		save_char_obj(ch, FALSE);
	}
	return TRUE;
}

/* constructors/destructors -------------------------------------------*/

OBJ_AUGMENT_DATA *obj_augment_new(void)
{
	OBJ_AUGMENT_DATA *aug;

	aug = calloc(1, sizeof(OBJ_AUGMENT_DATA));
	aug->racial_hatred = RACE_NEUTRAL;
	return aug;

}               
        
void obj_augment_free(OBJ_AUGMENT_DATA *poa)
{       
	if (poa == NULL) return;
	free(poa);
}

/* writer/reader ------------------------------------------------------*/
void fwrite_augment (OBJ_DATA *obj, FILE *fp)
{
	fprintf(fp, "AugTime %ld\n", obj->augment->consecration_time);
	fprintf(fp, "AugLastLevel %ld\n", obj->augment->last_level);
	fprintf(fp, "AugExp %d %d %d %d\n",
		obj->augment->level,
		obj->augment->exp_modifier,
		obj->augment->exp,
		obj->augment->pts);

	switch(get_augment_type(obj)) {
	case AUGMENT_TYPE_WEAPON:
		if (obj->augment->dicesize)
			fprintf(fp, "AugDiceSize %d\n", 
				obj->augment->dicesize);
		if (obj->augment->dicenum)
			fprintf(fp, "AugDiceNumber %d\n", 
				obj->augment->dicenum);
		if (obj->augment->weapon_flags)
			fprintf(fp, "AugFlags %s\n", 
				format_flags(obj->augment->weapon_flags));
		if (obj->augment->racial_hatred != RACE_NEUTRAL)
			fprintf(fp, "AugHatred %s\n", 
				race_name(obj->augment->racial_hatred));
		break;
	case AUGMENT_TYPE_FOCUS:
		fprintf(fp, "AugSkill '%s'\n",
			skill_name(obj->augment->skill_sn));
		if (obj->augment->focus_level)
			fprintf(fp, "AugFocus %d\n", 
				obj->augment->focus_level);
		if (obj->augment->mastery_level)
			fprintf(fp, "AugMastery %d\n", 
				obj->augment->mastery_level);
		if (obj->augment->power_level)
			fprintf(fp, "AugPower %d\n", 
				obj->augment->power_level);
		if (obj->augment->mana_level)
			fprintf(fp, "AugMana %d\n", 
				obj->augment->mana_level);
		break;
	}
}

/*
 * read and fill the data
 */
bool fread_augment (OBJ_DATA *obj, FILE *fp, char *word)
{
	bool fMatch = FALSE;

	if (!obj->augment)
		obj->augment = obj_augment_new();

	switch(word[3]) {
	case 'D':
		KEY("AugDiceSize", obj->augment->dicesize, fread_number(fp));
		KEY("AugDiceNumber", obj->augment->dicesize, fread_number(fp));
		break;

	case 'E':
		obj->augment->level = fread_number(fp);
		obj->augment->exp_modifier = fread_number(fp);
		obj->augment->exp = fread_number(fp);
		obj->augment->pts = fread_number(fp);
		fMatch = TRUE;
		break;

	case 'F':
		KEY("AugFlags", obj->augment->weapon_flags, fread_flags(fp));
		KEY("AugFocus", obj->augment->focus_level, fread_number(fp));
		break;

	case 'H':
		if (!str_cmp(word, "AugHatred")) {
			const char *race = fread_string(fp);
			obj->augment->racial_hatred = rn_lookup(race);
			free_string(race);
			if (obj->augment->racial_hatred != RACE_NEUTRAL)
				fMatch = TRUE;
			break;
		}
		break;
		
	case 'L':
		KEY("AugLastLevel", obj->augment->last_level, fread_number(fp));
		break;

	case 'M':
		KEY("AugMana", obj->augment->mana_level, fread_number(fp));
		KEY("AugMastery", obj->augment->mastery_level, fread_number(fp));
		break;

	case 'P':
		KEY("AugPower", obj->augment->power_level, fread_number(fp));
		break;

	case 'S':
		KEY("AugSkill", obj->augment->skill_sn, sn_lookup(fread_word(fp)));
		break;

	case 'T':
		KEY("AugTime", obj->augment->consecration_time, fread_number(fp));
		break;
	}

	if (!fMatch) {
		obj_augment_free(obj->augment);
		obj->augment = NULL;
	}

	return fMatch;
}

/* -----------------------------------------------------------------
 * augments
 * -----------------------------------------------------------------*/

/* dicenumber ******************************************************/

static int augment_cost_dicenumber (CHAR_DATA *ch, OBJ_DATA *obj)
{
	return AUGMENT_COST_WEAPON_DICENUMBER;
}

static bool augment_buy_dicenumber (CHAR_DATA *ch, OBJ_DATA *obj,
	OBJ_DATA *component, const char *argument)
{
	act("You shave off a section of the $p drastically improving its deadly balance.",
		ch, obj, NULL, TO_CHAR);
	act("$n shaves off a section of the $p, and seems pleased with the result.",
		ch, obj, NULL, TO_ROOM);
	obj->value[ITEM_WEAPON_DICE_NUM]++;
	obj->augment->dicenum++;

	if (component)
		act("$p crumbles to dust.",
			ch, component, NULL, TO_ALL);

	return TRUE;
}

/* dicesize *******************************************************/
static int augment_cost_dicesize (CHAR_DATA *ch, OBJ_DATA *obj)
{
	return AUGMENT_COST_WEAPON_DICESIZE;
}

static bool augment_buy_dicesize (CHAR_DATA *ch, OBJ_DATA *obj,
	OBJ_DATA *component, const char *argument)
{
	act("You hone out some imperfections in $p.",
		ch, obj, NULL, TO_CHAR);
	act("$n hones out some imperfections in $p.",
		ch, obj, NULL, TO_ROOM);
	obj->value[ITEM_WEAPON_DICE_SIZE]++;
	obj->augment->dicesize++;

	if (component)
		act("$p crumbles to dust.",
			ch, component, NULL, TO_ALL);

	return TRUE;
}


/* holy ***********************************************************/
static int augment_cost_holy (CHAR_DATA *ch, OBJ_DATA *obj)
{
	if (IS_WEAPON_STAT(obj, WEAPON_HOLY))
		return 0;

	return AUGMENT_COST_WEAPON_HOLY;
}

static bool augment_buy_holy (CHAR_DATA *ch, OBJ_DATA *obj,
	OBJ_DATA *component, const char *argument)
{
	if (IS_WEAPON_STAT(obj, WEAPON_VAMPIRIC)
	|| IS_WEAPON_STAT(obj, ITEM_ANTI_GOOD)) {
		act("$p rejects the holy blessing.",
			ch, obj, NULL, TO_CHAR);
		return FALSE;
	}

	SET_BIT(obj->value[ITEM_WEAPON_FLAGS], WEAPON_HOLY);
	SET_BIT(obj->augment->weapon_flags, WEAPON_HOLY);

	act("$p bursts with holy light!",
		ch, obj, NULL, TO_ALL);

	if (component)
		act("$p turns to light and ascends to heaven.",
			ch, component, NULL, TO_ALL);

	return TRUE;
}

/* flaming ********************************************************/
static int augment_cost_flaming (CHAR_DATA *ch, OBJ_DATA *obj)
{
	if (IS_WEAPON_STAT(obj, WEAPON_FLAMING))
		return 0;

	return AUGMENT_COST_WEAPON_FLAMING;
}

static bool augment_buy_flaming (CHAR_DATA *ch, OBJ_DATA *obj, 
	OBJ_DATA *component, const char *argument)
{
	if (IS_WEAPON_STAT(obj, WEAPON_FROST)
	|| IS_WEAPON_STAT(obj, WEAPON_SHOCKING)) {
		act("$p seems bound to another elemental force already.",
			ch, obj, NULL, TO_CHAR);
		return FALSE;
	}

	SET_BIT(obj->value[ITEM_WEAPON_FLAGS], WEAPON_FLAMING);
	SET_BIT(obj->augment->weapon_flags, WEAPON_FLAMING);
	act("$p ignites in flames!",
		ch, obj, NULL, TO_ALL);

	if (component)
		act("$p vanishes in a puff of smoke.",
			ch, component, NULL, TO_ALL);

	return TRUE;
}

/* hatred *********************************************************/
static int augment_cost_hatred (CHAR_DATA *ch, OBJ_DATA *obj)
{
	if (obj->augment->racial_hatred == RACE_NEUTRAL)
		return 0;

	return AUGMENT_COST_WEAPON_HATRED;
}

static bool augment_buy_hatred (CHAR_DATA *ch, OBJ_DATA *obj, 
	OBJ_DATA *component, const char *argument)
{
	int race = RACE_NEUTRAL;

	if (obj->augment->racial_hatred != RACE_NEUTRAL) {
		act("$p is already filled with rage!",
			ch, obj, NULL, TO_CHAR);
		return FALSE;
	}

	race = rn_lookup(argument);

	if (race < 0) {
                char_puts("Available races are:", ch);

                for (race = 1; race < races.nused; race++) {
                        if ((race % 3) == 0)
                                char_puts("\n", ch);
                        char_printf(ch, " %-15s", RACE(race)->name);
                }

                char_puts("\n", ch);
		
		return FALSE;
	}

	obj->augment->racial_hatred = race;

	act("$p pulses with animosity!",
		ch, obj, NULL, TO_ALL);

	if (component)
		act("$p disolves into dust.",
			ch, component, NULL, TO_ALL);

	return TRUE;
}