/**************************************************************************
* # # # ## # # ### ## ## ### http://www.lyonesse.it *
* # # # # # ## # # # # # *
* # # # # # ## ## # # ## ## ## # # ## *
* # # # # # ## # # # # # # # # # # # *
* ### # ## # # ### ## ## ### # # #### ## Ver. 1.0 *
* *
* -Based on CircleMud & Smaug- Copyright (c) 2001-2002 by Mithrandir *
* *
* ********************************************************************** *
* *
* File: magic2.c *
* *
* - Spellbook system *
* - New magic system based upon spell memorization *
* - Study/Copy skill (for acquiring spells from objects) *
* - Enchant skill *
* *
**************************************************************************/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "spells.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "constants.h"
/* external variables */
extern SPELL_INFO_DATA spell_info[];
extern int prac_params[4][NUM_CLASSES];
#define SINFO spell_info[spellnum]
#define LEARNED(ch) (prac_params[0][(int)GET_CLASS(ch)])
/* ************************************************************** */
/* Spellbooks Code */
/* ************************************************************** */
/* ================================================================ */
/* Low-level functions to create or destroy pages and books */
/* ================================================================ */
/* instert in a book a new page with a random spells */
void add_rand_book_page(SPELLBOOK *book)
{
BOOK_PAGE *page;
int spellnum = number(1, LAST_SPELL);
CREATE(page, BOOK_PAGE, 1);
page->spellnum = spellnum;
page->status = number(60, 90);
page->flags = PAGE_WRITTEN;
page->spellname = str_dup(SINFO.name);
LINK(page, book->first_page, book->last_page, next, prev);
book->num_of_pages++;
book->num_of_spells++;
}
/* initialize a new blank page for a book */
BOOK_PAGE *new_book_page(void)
{
BOOK_PAGE *page;
CREATE(page, BOOK_PAGE, 1);
page->spellnum = NOTHING;
page->status = number(80, 100);
page->flags = PAGE_BLANK;
page->spellname = NULL;
return (page);
}
/* remove a page from a book */
void remove_book_page(SPELLBOOK *book, BOOK_PAGE *page)
{
if ( page->spellname )
STRFREE(page->spellname);
UNLINK(page, book->first_page, book->last_page, next, prev);
DISPOSE(page);
book->num_of_pages--;
}
/* initialize a new spellbook */
SPELLBOOK *new_spellbook(OBJ_DATA *obj, int type, bool rand)
{
SPELLBOOK *book;
CREATE(book, SPELLBOOK, 1);
book->first_page = NULL;
book->last_page = NULL;
book->num_of_pages = 0;
book->num_of_spells = 0;
book->type = type;
if ( rand )
{
int p;
for ( p = 0; p < MIN_PAGES; p++ )
add_rand_book_page( book );
}
return (book);
}
/* ================================================================ */
/* Spellbook info */
/* ================================================================ */
int find_page_cond( int percent )
{
int num;
if (percent >= 100) num = 0;
else if (percent > 80) num = 1;
else if (percent > 65) num = 2;
else if (percent > 40) num = 3;
else if (percent > 25) num = 4;
else if (percent > 9) num = 5;
else if (percent > 0) num = 6;
else num = 7;
return (num);
}
/* lists all the pages in a spellbook */
void ListSpellsInBook(CHAR_DATA *ch, SPELLBOOK *book, bool dampage)
{
BOOK_PAGE *page, *next_page = NULL;
char bbuf[MAX_STRING_LENGTH];
int pn, cond;
*bbuf = '\0';
send_to_char("The spellbook contains the following spells:\r\n", ch);
for ( pn = 0, page = book->first_page; page; page = next_page, pn++ )
{
next_page = page->next;
/* ruined pages are unreadables */
if ( page->flags == PAGE_RUINED )
strcat(bbuf, " - an unreadable page");
else if ( page->flags == PAGE_BLANK )
{
cond = find_page_cond(percentage(page->status, 100));
sprintf(bbuf+strlen(bbuf), " - a blank page that looks %s",
obj_cond_table[cond]);
}
else
{
cond = find_page_cond(percentage(page->status, 100));
sprintf(bbuf+strlen(bbuf), " - %s %s page that contains: '&b&3%s&0'",
AN(obj_cond_table[cond]), obj_cond_table[cond], page->spellname);
}
strcat(bbuf, ".\r\n");
/* take a chance to ruin the page */
if ( dampage && !number(0, 5) )
{
page->status--;
if ( page->status <= 0 )
{
remove_book_page(book, page);
send_to_char("The page crumbles under your fingers.\r\n",ch);
}
else if ( page->status < 5 )
page->flags = PAGE_RUINED;
}
}
page_string(ch->desc, bbuf, 1);
}
/*
* called from look_read in act.informative.c
*
* read the spellbook
*/
void ReadSpellbook(CHAR_DATA *ch, OBJ_DATA *obj, char *argument)
{
SPELLBOOK *book;
if ( !obj->special || !OBJ_FLAGGED(obj, ITEM_IS_SPELLBOOK) )
return;
if ( percentage(GET_OBJ_COND(obj), GET_OBJ_MAXCOND(obj)) < 10 )
{
send_to_char("The book is too damaged to be readable.\r\n", ch);
return;
}
book = (SPELLBOOK *) obj->special;
ch_printf(ch, "It's a spellbook with %d pages over a maximum of %d.\r\n",
book->num_of_pages, book_types[book->type].max_num_pages);
ListSpellsInBook(ch, book, TRUE);
/* take a chance to damage the book */
check_damage_obj(ch, obj, 4);
}
/* ================================================================ */
/* Spellbook checks */
/* ================================================================ */
/* check to see if the spellbook contains the spell */
BOOK_PAGE *BookHasSpell(SPELLBOOK *book, int sn)
{
BOOK_PAGE *page = NULL;
for ( page = book->first_page; page; page = page->next )
{
if ( page->flags == PAGE_RUINED )
continue;
if ( page->spellnum == sn )
break;
}
return (page);
}
void MakePageBlank(BOOK_PAGE *page)
{
if ( !page )
return;
if (page->spellname)
STRFREE(page->spellname);
page->spellnum = NOTHING;
page->flags = PAGE_BLANK;
}
/* check for a blank page into the spellbook */
int BookHasBlankPage(SPELLBOOK *book)
{
BOOK_PAGE *page = NULL;
int count = 0;
for ( page = book->first_page; page; page = page->next )
{
if ( page->flags == PAGE_RUINED )
continue;
if ( page->flags == PAGE_BLANK )
count++;
}
return (count);
}
/*
* recursive search of a spellbook into inventory
* and carried containers
*
* returns SPELLBOOK data of object
*/
SPELLBOOK *FindSpellbook(OBJ_DATA *list, long owner_id)
{
SPELLBOOK *book = NULL;
OBJ_DATA *obj;
for ( obj = list; obj; obj = obj->next_content )
{
if ( obj->special && OBJ_FLAGGED(obj, ITEM_IS_SPELLBOOK) &&
GET_OBJ_OWNER(obj) == owner_id )
{
book = (SPELLBOOK *) obj->special;
break;
}
if ( GET_OBJ_TYPE(obj) == ITEM_CONTAINER )
{
if ( (book = FindSpellbook(obj->first_content, owner_id)) )
break;
}
}
return ( book );
}
/*
* recursive search of a spellbook into inventory
* and carried containers
*
* returns the object
*/
OBJ_DATA *FindObjSpellbook( OBJ_DATA *list, long owner_id )
{
OBJ_DATA *obj;
for ( obj = list; obj; obj = obj->next_content )
{
if ( obj->special && OBJ_FLAGGED(obj, ITEM_IS_SPELLBOOK) &&
GET_OBJ_OWNER(obj) == owner_id )
break;
if ( GET_OBJ_TYPE(obj) == ITEM_CONTAINER )
{
if ( (obj = FindObjSpellbook(obj->first_content, owner_id)) )
break;
}
}
return ( obj );
}
/* add a blank page to the spellbook */
bool AddBlankPage( SPELLBOOK *book )
{
BOOK_PAGE *page;
if ( book->num_of_pages >= book_types[book->type].max_num_pages )
return (FALSE);
page = new_book_page();
LINK(page, book->first_page, book->last_page, next, prev);
book->num_of_pages++;
return (TRUE);
}
/* add the spell to the spellbook */
bool AddSpellToBook(CHAR_DATA *ch, SPELLBOOK *book, int sn)
{
BOOK_PAGE *page;
for (page = book->first_page; page; page = page->next)
{
if (page->flags == PAGE_RUINED) continue;
if (page->flags == PAGE_BLANK) break;
}
if (!page)
{
send_to_char("There isn't a blank page to write on.\r\n", ch);
return (FALSE);
}
/* take a chance to ruin the page */
if ( !number(0, 5) )
{
page->status--;
if ( page->status <= 0 )
{
remove_book_page(book, page);
send_to_char("The page crumbles under your fingers.\r\n",ch);
return (FALSE);
}
else if ( page->status < 5 )
{
page->flags = PAGE_RUINED;
return (FALSE);
}
}
page->spellnum = sn;
if (page->spellname)
STRFREE(page->spellname);
page->spellname = str_dup(skill_name(sn));
page->flags = PAGE_WRITTEN;
book->num_of_spells++;
return (TRUE);
}
int AddNewSpell(CHAR_DATA *ch, int spellnum)
{
SPELLBOOK *sbook;
if ( !(sbook = FindSpellbook(ch->first_carrying, GET_IDNUM(ch))) )
{
send_to_char("You need one of yours spellbook.\r\n", ch);
return (1);
}
if ( !BookHasSpell(sbook, spellnum ) )
{
if ( !BookHasBlankPage(sbook) )
{
if ( !AddBlankPage(sbook) )
{
send_to_char("You can't add any more pages to this spellbook.\r\n", ch);
return (1);
}
}
AddSpellToBook(ch, sbook, spellnum);
}
return (0);
}
/*
* called from do_start() for new sorcerers
*/
void create_new_spellbook(CHAR_DATA *ch, int type)
{
OBJ_DATA *obj = create_obj();
GET_OBJ_TYPE(obj) = ITEM_SPELLBOOK;
GET_OBJ_COST(obj) = 1000 * book_types[type].mult;
GET_OBJ_LEVEL(obj) = GET_LEVEL(ch);
GET_OBJ_OWNER(obj) = GET_IDNUM(ch);
GET_OBJ_VAL(obj, 0) = type;
GET_OBJ_WEIGHT(obj) = 20 * book_types[type].mult;
obj->name = str_dup("book spellbook");
obj->short_description = str_dup("a white leather covered book");
obj->description = str_dup("A white leather covered book with runes on the front has been left here.");
SET_BIT(GET_OBJ_EXTRA(obj), ITEM_MAGIC | ITEM_UNIQUE | ITEM_IS_SPELLBOOK | ITEM_NODONATE);
SET_BIT(GET_OBJ_WEAR(obj), ITEM_WEAR_TAKE);
obj->special = new_spellbook(obj, type, FALSE);
obj_to_char(obj, ch);
}
/* immortal command to create a new spellbook */
ACMD(do_newbook)
{
OBJ_DATA *obj = create_obj();
GET_OBJ_TYPE(obj) = ITEM_SPELLBOOK;
GET_OBJ_COST(obj) = 1000 * book_types[BOOK_BOOK].mult;
GET_OBJ_LEVEL(obj) = GET_LEVEL(ch);
GET_OBJ_OWNER(obj) = GET_IDNUM(ch);
GET_OBJ_VAL(obj, 0) = BOOK_BOOK;
GET_OBJ_WEIGHT(obj) = 20 * book_types[BOOK_BOOK].mult;
if (obj->name)
free(obj->name);
obj->name = str_dup("book spellbook");
if (obj->short_description)
free(obj->short_description);
obj->short_description = str_dup("a red leather covered book");
if (obj->description)
free(obj->description);
obj->description = str_dup("A red leather covered book with runes on the front has been left here.");
SET_BIT(GET_OBJ_EXTRA(obj), ITEM_MAGIC | ITEM_UNIQUE | ITEM_IS_SPELLBOOK);
SET_BIT(GET_OBJ_WEAR(obj), ITEM_WEAR_TAKE);
obj->special = new_spellbook(obj, BOOK_BOOK, TRUE);
obj_to_char(obj, ch);
send_to_char("You create a new spellbook out of the blue.\r\n", ch);
}
/* *************************************************************** */
/* Magic System based upon memorized spells and spellbooks */
/* *************************************************************** */
/* list of memorized spells */
void ListMemSpells(CHAR_DATA *ch)
{
char buf[MAX_STRING_LENGTH];
int spellnum;
if ( IS_NPC(ch) )
return;
send_to_char("You have memorized the following spells:\r\n", ch);
*buf = '\0';
for (spellnum = 1; spellnum <= MAX_SPELLS; spellnum++)
{
if ( MEMORIZED(ch, spellnum) )
sprintf(buf+strlen(buf), " [%d] '&b&3%s&0'\r\n",
MEMORIZED(ch, spellnum), SINFO.name);
}
if ( !*buf )
strcat(buf, "None.\r\n");
page_string(ch->desc, buf, 1);
}
/* return the maximum number of spell memorizable by ch */
int MaxSpellsCanMem(CHAR_DATA *ch)
{
int num;
if ( !ch || IS_NPC(ch) )
return (0);
num = (int) GET_LEVEL(ch);
num += (int) int_app[GET_INT(ch)].memspl;
return (URANGE(1, num, MAX_MEM_SPELLS));
}
/* return the number of spells memorized by ch */
int NumSpellsMem(CHAR_DATA *ch)
{
register int sn;
int num;
if ( !ch || IS_NPC(ch) )
return (0);
for ( sn = 1; sn <= MAX_SPELLS; sn++ )
{
if ( MEMORIZED(ch, sn) )
num += MEMORIZED(ch, sn);
}
return (num);
}
/*
* return TRUE if ch can memorize 'num' 'sn' spell
* FALSE otherwise
*/
bool CanMemSpell(CHAR_DATA *ch, int sn, int num)
{
if ( !ch || IS_NPC(ch) )
return (FALSE);
if ( MEMORIZED(ch, sn) + num <= MAX_SAME_SPELL )
return (TRUE);
return (FALSE);
}
/* stop the memorization of a spell */
void stop_memorizing(CHAR_DATA *ch)
{
if ( !ch || !ch->action || !AFF_FLAGGED(ch, AFF_MEMORIZING) )
return;
event_cancel(ch->action);
REMOVE_BIT(AFF_FLAGS(ch), AFF_MEMORIZING);
send_to_char("You stop memorizing your spell.\r\n", ch);
}
/* the memorizing event */
EVENTFUNC(mem_event)
{
MEM_EVENT *me = (MEM_EVENT *) event_obj;
CHAR_DATA *ch = me->ch;
int spellnum = me->spell;
int tnum = me->num;
ch_printf(ch, "You have finished memorizing '&b&3%s&0'.\r\n", SINFO.name);
MEMORIZED(ch, spellnum) += tnum;
ch->action = NULL;
REMOVE_BIT(AFF_FLAGS(ch), AFF_MEMORIZING);
DISPOSE(event_obj);
return (0);
}
/* command to memorize a spell */
ACMD(do_memorize)
{
MEM_EVENT *me;
SPELLBOOK *book;
char *s, arg1[MAX_INPUT_LENGTH];
int spellnum, tnum = 1;
if ( IS_NPC(ch) )
return;
if ( !MEMMING_CLASS(ch) )
{
send_to_char("You cannot memorize spells.\r\n", ch);
return;
}
if ( ch->action )
{
if ( AFF_FLAGGED(ch, AFF_MEMORIZING) )
send_to_char("You can memorize only one spell at time.\r\n", ch);
else
send_to_char("You are too busy to memorize a spell right now.\r\n", ch);
return;
}
/* get: blank, spell name, target name */
s = get_spell_name(argument);
if (s == NULL)
{
send_to_char("Usage: memorize 'spellname' <num>\r\n", ch);
ListMemSpells(ch);
return;
}
argument = strtok(NULL, "\0");
one_argument(argument, arg1);
if ( GET_POS(ch) != POS_RESTING )
{
send_to_char("You must rest before start memorizing spells.\r\n", ch);
return;
}
spellnum = find_skill_num(s);
if ((spellnum < 1) || (spellnum >= MAX_SPELLS))
{
send_to_char("Memorize what?!?\r\n", ch);
return;
}
if ( !(book = FindSpellbook(ch->first_carrying, GET_IDNUM(ch)) ) )
{
send_to_char("You need a spellbook to memorize spells.\r\n", ch);
return;
}
if ( !BookHasSpell(book, spellnum) )
{
send_to_char("This spellbook doesn't contains that spell.\r\n", ch);
return;
}
if (GET_LEVEL(ch) < SINFO.min_level[(int) GET_CLASS(ch)])
{
send_to_char("That spell is beyond your actual power!\r\n", ch);
return;
}
if ( *arg1 && is_number(arg1) )
{
tnum = atoi(arg1);
if ( tnum < 1 || tnum > MAX_SAME_SPELL )
{
ch_printf(ch, "You can memorize the same spell only %d times.\r\n", MAX_SAME_SPELL);
return;
}
}
if ( NumSpellsMem(ch) + tnum >= MaxSpellsCanMem(ch) )
{
send_to_char("You've already memorized all the spells you can handle at the moment.\r\n", ch);
return;
}
if ( !CanMemSpell(ch, spellnum, tnum) )
{
ch_printf(ch, "You cannot memorize '%s' any further.\r\n", SINFO.name);
return;
}
if (!(GET_COND(ch, FULL)) || !(GET_COND(ch, THIRST)))
{
send_to_char("You can't concentrate with ", ch);
if (!GET_COND(ch, FULL))
{
if (!GET_COND(ch, THIRST))
send_to_char("your stomach aching and your throat dry.\r\n", ch);
else
send_to_char("your stomach rumbling.\r\n", ch);
}
else
send_to_char("your throat this parched.\r\n", ch);
return;
}
send_to_char("You open your spellbook and begin studying it intently.\r\n", ch);
act("$n opens $s spellbook and begins studying it intently.", TRUE, ch, NULL, NULL, TO_ROOM);
if ( ch->in_obj && OBJ_FLAGGED(ch->in_obj, ITEM_FAST_MAGIC) )
{
ch_printf(ch, "You immediately memorize '&b&3%s&0'.\r\n", SINFO.name);
MEMORIZED(ch, spellnum) += tnum;
return;
}
CREATE(me, MEM_EVENT, 1);
me->ch = ch;
me->spell = spellnum;
me->num = tnum;
ch->action = event_create(mem_event, me, MEM_DELAY * tnum);
SET_BIT(AFF_FLAGS(ch), AFF_MEMORIZING);
}
/*
* called from ACMD(do_forget)
*
* return TRUE if *argument is a spell name, FALSE otherwise
*/
bool forget_spell( CHAR_DATA *ch, char *argument )
{
char *spellname = get_spell_name(argument);
int sn;
if ( !spellname )
return (FALSE);
sn = find_skill_num(spellname);
if ( sn < 0 || sn >= MAX_SPELLS )
return (FALSE);
if ( !MEMORIZED(ch, sn) )
send_to_char("You don't have that spell memorized.\r\n", ch);
else
{
ch_printf(ch, "You forget the '&b&3%s&0' spell.\r\n", spellname);
MEMORIZED(ch, sn) = 0;
}
return (TRUE);
}
/* ****************************************************** */
/* Study / Copy functions */
/* ****************************************************** */
void LearnOrImprove(CHAR_DATA *ch, int spellnum)
{
if (spellnum < 0 || spellnum >= MAX_SPELLS)
return;
if (GET_SKILL(ch, spellnum) >= LEARNED(ch))
{
send_to_char("You are already learned in that area.\r\n", ch);
return;
}
if ( GET_SKILL(ch, spellnum) > 0 )
{
GET_SKILL(ch, spellnum) += number(1, 3);
GET_SKILL(ch, spellnum) = MIN(GET_SKILL(ch, spellnum), LEARNED(ch));
ch_printf(ch, "You improve your knowledge of the spell '&b&3%s&0'.\r\n", SINFO.name);
return;
}
if ( MEMMING_CLASS(ch) )
{
if (AddNewSpell(ch, spellnum))
return;
send_to_char("You carefully copy the spell on you spellbook.\r\n", ch);
act("$n carefully write something on $s spellbook.", TRUE, ch, NULL, NULL, TO_ROOM);
}
if (GET_LEVEL(ch) >= SINFO.min_level[(int) GET_CLASS(ch)])
{
SET_SKILL(ch, spellnum, (5 + number(1, (int_app[GET_INT(ch)].learn) / 2)));
ch_printf(ch, "You have learned the art of '&b&3%s&0'.\r\n", SINFO.name);
}
else
ch_printf(ch, "The '&b&3%s&0' spell is beyond your actual power.\r\n", SINFO.name);
}
/*
* *POWERFUL*
*
* allow to study wands, staves, scrolls, spellbooks
* to learn theirs spells.
*
* FB 03-06-2002 -- players now can study maps
*/
ACMD(do_study) /* original study code for Rom 2.4 is by Absalom */
{
OBJ_DATA *obj;
BOOK_PAGE *page;
char *burngone = "$p burns brightly and is gone.";
char *burnpage = "$p's page glows brightly and the writing disappear.";
char *s;
int spellnum;
argument = one_argument(argument, arg);
if (!*arg)
{
send_to_char("You can study magical or enchanted objects to acquire new knowledge.\r\n", ch);
send_to_char(
"Usage: study <object>\r\n"
" : study <spelbook> 'spellname'\r\n"
" : study <map obj>\r\n"
, ch);
return;
}
if ( !GET_SKILL(ch, SKILL_STUDY) )
{
send_to_char("You have no idea on how to study.\r\n", ch);
return;
}
/* study scrolls, wands and staves */
if (!(obj = get_obj_in_list_vis_rev(ch, arg, NULL, ch->last_carrying)))
{
sprintf(buf, "You don't seem to have %s %s.\r\n", AN(arg), arg);
send_to_char(buf, ch);
return;
}
if (GET_OBJ_TYPE(obj) == ITEM_MAP)
{
// sea course map
if ( GET_OBJ_VAL(obj, 0) == 0 )
{
int learn_course(CHAR_DATA *ch, int cnum);
int ret;
ret = learn_course(ch, GET_OBJ_VAL(obj, 1));
switch (ret)
{
case -1:
log("SYSERR: invalid course vnum in obj.");
break;
case 0:
send_to_char("You already know that course.\r\n", ch);
break;
default:
send_to_char("You learn a new sea course.\r\n", ch);
break;
}
}
else
{
log("SYSERR: unknown map type.");
send_to_char("This is an unknown map.\r\n", ch);
}
return;
}
/* list here item type allowed to be studied */
if (GET_OBJ_TYPE(obj) != ITEM_STAFF && GET_OBJ_TYPE(obj) != ITEM_WAND &&
GET_OBJ_TYPE(obj) != ITEM_SCROLL && GET_OBJ_TYPE(obj) != ITEM_SPELLBOOK)
{
send_to_char("You can only study scrolls, wands, staves and spellbooks.\r\n", ch);
return;
}
/* sorcerer check to prevent studying his spellbook */
if ( MEMMING_CLASS(ch) )
{
OBJ_DATA *sbook = FindObjSpellbook(ch->first_carrying, GET_IDNUM(ch));
if ( (sbook) && sbook == obj )
{
send_to_char("Are you stupid??\r\n", ch);
return;
}
}
separate_obj(obj);
act("You start studying $p.", TRUE, ch, obj, NULL, TO_CHAR);
act("$n start studying $p.", TRUE, ch, obj, NULL, TO_ROOM);
if (GET_LEVEL(ch) + 5 < GET_OBJ_LEVEL(obj))
{
send_to_char("You cannot glean any knowledge from it.\r\n", ch);
act(burngone, FALSE, NULL, obj, 0, TO_ROOM);
extract_obj(obj);
return;
}
if (!success(ch, NULL, SKILL_STUDY, 0))
{
send_to_char("You fail to extract any knowledge from it.\r\n", ch);
act(burngone, FALSE, NULL, obj, 0, TO_ROOM);
extract_obj(obj);
return;
}
switch (GET_OBJ_TYPE(obj))
{
case ITEM_SCROLL:
if ( success(ch, NULL, SKILL_READ_MAGIC, 0) )
{
int sn;
for (sn = 1; sn < 4; sn++)
LearnOrImprove(ch, GET_OBJ_VAL(obj, sn));
}
else
send_to_char("You fail to decifrate the magic writing.\r\n", ch);
act(burngone, FALSE, NULL, obj, 0, TO_ROOM);
extract_obj(obj);
break;
case ITEM_WAND:
case ITEM_STAFF:
LearnOrImprove(ch, GET_OBJ_VAL(obj, 3));
act(burngone, FALSE, NULL, obj, 0, TO_ROOM);
extract_obj(obj);
break;
case ITEM_SPELLBOOK:
if ( !obj->special || !OBJ_FLAGGED(obj, ITEM_IS_SPELLBOOK) )
{
send_to_char("There is nothing to study in that.\r\n", ch);
return;
}
if ( !*argument )
{
send_to_char("You must specify which spell of the spellbook you want to study.\r\n", ch);
ListSpellsInBook(ch, (SPELLBOOK *) obj->special, TRUE);
return;
}
s = get_spell_name(argument);
if ( s == NULL )
{
send_to_char("You must specify which spell of the spellbook you want to study.\r\n", ch);
ListSpellsInBook(ch, (SPELLBOOK *) obj->special, TRUE);
return;
}
spellnum = find_skill_num(s);
if ( !(page = BookHasSpell((SPELLBOOK *) obj->special, spellnum)) )
{
send_to_char("The spellbook doesn't contains that spell.\r\n", ch);
return;
}
LearnOrImprove(ch, spellnum);
act(burnpage, FALSE, ch, obj, NULL, TO_CHAR);
act(burnpage, FALSE, ch, obj, NULL, TO_ROOM);
MakePageBlank(page);
break;
}
}
/*
* copy a spell from a scroll to the char spellbook
*/
void CopyScroll(CHAR_DATA *ch, OBJ_DATA *scroll, SPELLBOOK *book)
{
char *burngone = "$p burns brightly and is gone.";
int sn, spellnum, written = 0;
if ( GET_OBJ_VAL(scroll, 1) == -1 )
{
send_to_char("The scroll doesn't contains any spell.\r\n", ch);
act(burngone, FALSE, NULL, scroll, 0, TO_ROOM);
extract_obj(scroll);
return;
}
for (written = 0, sn = 1; sn < 4; sn++)
{
if ((spellnum = GET_OBJ_VAL(scroll, sn)) == -1)
continue;
if ( BookHasSpell(book, spellnum) )
{
ch_printf(ch, "You skip '%s' because your spellbook already contains this spell.\r\n",
skill_name(spellnum));
continue;
}
/*
* the only reason this return false is there's no blank pages on
* the spellbook, so we exit here
*/
if ( !AddSpellToBook(ch, book, spellnum) )
break;
written++;
}
if ( written )
{
send_to_char("You successfully copy the spells in your spellbook.\r\n", ch);
act("$n carefully write something on $s spellbook.", TRUE, ch, NULL, NULL, TO_ROOM);
act(burngone, FALSE, NULL, scroll, 0, TO_ROOM);
extract_obj(scroll);
}
}
/*
* copy a spell from a spellbook to the char spellbook
*/
void CopySpellbook(CHAR_DATA *ch, OBJ_DATA *obj, SPELLBOOK *chbook, char *argument)
{
SPELLBOOK *book;
BOOK_PAGE *page;
char *s, *burngone = "$p's page glows brightly and the writing disappear.";
int spellnum, written = 0;
s = get_spell_name(argument);
if (s == NULL)
{
act("Which spell do you want to copy from $p?", TRUE, ch, obj, NULL, TO_CHAR);
return;
}
book = (SPELLBOOK *) obj->special;
if ( ( spellnum = find_skill_num(s) ) == NOTHING )
{
send_to_char("That spell does not exists.\r\n", ch);
return;
}
if ( BookHasSpell(chbook, spellnum) )
{
ch_printf(ch, "Your spellbook already contains the '%s' spell.\r\n",
skill_name(spellnum));
return;
}
if ( !( page = BookHasSpell(book, spellnum) ) )
{
act("$p does not contain that spell.", TRUE, ch, obj, NULL, TO_CHAR);
return;
}
if ( AddSpellToBook(ch, chbook, spellnum) )
{
send_to_char("You successfully copy the spells in your spellbook.\r\n", ch);
act("$n carefully write something on $s spellbook.", TRUE, ch, NULL, NULL, TO_ROOM);
act(burngone, FALSE, ch, obj, NULL, TO_CHAR);
MakePageBlank(page);
check_damage_obj(ch, obj, 8);
}
}
ACMD(do_copy)
{
SPELLBOOK *book;
OBJ_DATA *obj;
char arg1[MAX_INPUT_LENGTH];
if ( !MEMMING_CLASS(ch) )
{
send_to_char("You have no idea on how to copy spells from objects.", ch);
if ( CASTING_CLASS(ch) )
send_to_char(" Try studying it.\r\n", ch);
send_to_char("\r\n", ch);
return;
}
argument = one_argument(argument, arg1);
if ( !(obj = get_obj_in_list_vis_rev(ch, arg1, NULL, ch->last_carrying)) )
{
ch_printf(ch, "You don't have %s %s.\r\n", AN(arg1), arg1);
return;
}
if ( GET_OBJ_TYPE(obj) != ITEM_SCROLL && GET_OBJ_TYPE(obj) != ITEM_SPELLBOOK )
{
send_to_char("You can copy spells only from scrolls or spellbooks.\r\n", ch);
return;
}
if ( !(book = FindSpellbook(ch->first_carrying, GET_IDNUM(ch))) )
{
send_to_char("You don't have a spellbook to copy the spell to.\r\n", ch);
return;
}
if ( !BookHasBlankPage(book) )
{
send_to_char("There isn't a blank page on your spellbook to write on.\r\n", ch);
return;
}
if ( !success(ch, NULL, SKILL_READ_MAGIC, 0) )
{
send_to_char("You fail to decifrate the magic writing.\r\n", ch);
return;
}
if ( GET_OBJ_TYPE(obj) == ITEM_SCROLL )
CopyScroll(ch, obj, book);
else if ( obj->special && OBJ_FLAGGED(obj, ITEM_IS_SPELLBOOK) )
CopySpellbook(ch, obj, book, argument);
}
/* *************************************************************** */
/* Code for storing and using spells on magic items */
/* *************************************************************** */
/*
* if target object has the given spell stored, return
* a pointer to the obj spell data
*/
OBJ_SPELLS_DATA *ItemHasSpell(OBJ_DATA *obj, int sn)
{
OBJ_SPELLS_DATA *oSpell;
if (!obj || !obj->special || !OBJ_FLAGGED(obj, ITEM_HAS_SPELLS))
return (NULL);
for (oSpell = (OBJ_SPELLS_DATA *) obj->special; oSpell; oSpell = oSpell->next)
{
if ( oSpell->spellnum == sn )
break;
}
return (oSpell);
}
/*
* search all char equipped items of type ITEM_WORN
* that can contains the given spell
*/
OBJ_SPELLS_DATA *GetCharItemsSpell(CHAR_DATA *ch, int sn)
{
OBJ_SPELLS_DATA *oSpell;
if ( GET_EQ(ch, WEAR_FINGER_R) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_FINGER_R), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_FINGER_R), 2);
return (oSpell);
}
if ( GET_EQ(ch, WEAR_FINGER_L) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_FINGER_L), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_FINGER_L), 2);
return (oSpell);
}
if ( GET_EQ(ch, WEAR_NECK_1) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_NECK_1), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_NECK_1), 2);
return (oSpell);
}
if ( GET_EQ(ch, WEAR_NECK_2) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_NECK_2), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_NECK_2), 2);
return (oSpell);
}
if ( GET_EQ(ch, WEAR_WRIST_R) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_WRIST_R), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_WRIST_R), 2);
return (oSpell);
}
if ( GET_EQ(ch, WEAR_WRIST_L) && (oSpell = ItemHasSpell(GET_EQ(ch, WEAR_WRIST_L), sn)) )
{
check_damage_obj(ch, GET_EQ(ch, WEAR_WRIST_L), 2);
return (oSpell);
}
return (NULL);
}
/*
* *POWERFUL*
*
* store a spell into an object.
*
* that allow to cast that spell thru the object
*/
ACMD(do_enchant)
{
OBJ_DATA *obj, *pObj;
OBJ_SPELLS_DATA *oSpell, *spells_list = NULL;
char *s, arg1[MAX_INPUT_LENGTH];
int spellnum;
if ( !GET_SKILL(ch, SKILL_ENCHANT_ITEMS) )
{
send_to_char("You don't know how to enchant an item.\r\n", ch);
return;
}
/* get: blank, spell name, target name */
s = get_spell_name(argument);
if (s == NULL)
{
send_to_char("Usage: enchant 'spellname' <object>\r\n", ch);
return;
}
argument = strtok(NULL, "\0");
spellnum = find_skill_num(s);
if ( spellnum < 0 || spellnum >= MAX_SPELLS )
{
send_to_char("That spell doesn't exist.\r\n", ch);
return;
}
one_argument(argument, arg1);
if ( !*arg1 )
{
send_to_char("Usage: enchant 'spellname' <object>\r\n", ch);
return;
}
if ( !(obj = get_obj_in_list_vis_rev(ch, arg1, NULL, ch->last_carrying)) )
{
ch_printf(ch, "You don't have %s %s.\r\n", AN(arg1), arg1);
return;
}
if ( GET_OBJ_TYPE(obj) != ITEM_WORN && GET_OBJ_TYPE(obj) != ITEM_WEAPON )
{
send_to_char("You can enchant, in different ways, only weapons and jewelry.\r\n", ch);
return;
}
if ( GET_OBJ_TYPE(obj) == ITEM_WEAPON && !IS_SET(SINFO.routines, MAG_DAMAGE))
{
send_to_char("A weapon can be enchanted only with offensive spells.\r\n", ch);
return;
}
if ( obj->special )
{
if (!OBJ_FLAGGED(obj, ITEM_HAS_SPELLS))
{
act("$p cannot be enchanted.", TRUE, ch, obj, NULL, TO_CHAR);
return;
}
if ( ItemHasSpell(obj, spellnum) )
{
act("$p is already enchanted with that spell.", TRUE, ch, obj, NULL, TO_CHAR);
return;
}
}
separate_obj(obj);
if (IS_MORTAL(ch))
{
if ( !success(ch, NULL, SKILL_ENCHANT_ITEMS, 0) )
{
act("You fail in enchanting $p, and it explodes.", FALSE, ch, obj, NULL, TO_CHAR);
act("$n casts a spell upon $p, but something goes badly wrong.", FALSE, ch, obj, NULL, TO_ROOM);
damage(ch, ch, number(20, 50), TYPE_UNDEFINED);
extract_obj(obj);
return;
}
}
pObj = clone_object(obj);
/* make the object unique */
if ( !OBJ_FLAGGED(pObj, ITEM_UNIQUE) )
{
/* clone obj increase the copy number of the prototype */
if (pObj->item_number != NOTHING)
obj_index[pObj->item_number].number--;
pObj->item_number = NOTHING;
SET_BIT(GET_OBJ_EXTRA(pObj), ITEM_UNIQUE);
}
if ( pObj->special )
spells_list = (OBJ_SPELLS_DATA *) pObj->special;
CREATE(oSpell, OBJ_SPELLS_DATA, 1);
oSpell->spellnum = spellnum;
oSpell->percent = MIN(100, number(60, 80) + GET_LEVEL(ch));
oSpell->level = MAX(1, GET_LEVEL(ch) - number(1, 4));
oSpell->next = spells_list;
spells_list = oSpell;
pObj->special = spells_list;
SET_BIT(GET_OBJ_EXTRA(pObj), ITEM_MAGIC | ITEM_HAS_SPELLS);
act("You enchant $p, that glows white for a moment.", TRUE, ch, pObj, NULL, TO_CHAR);
act("$n enchants $p, that glows white for a moment.", TRUE, ch, pObj, NULL, TO_ROOM);
extract_obj(obj);
obj_to_char(pObj, ch);
/* TODO
* character must have drawbacks for enchanting items...
*/
}