/* CrossFire, A Multiplayer game for X-windows Copyright (C) 2007 Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at crossfire-devel@real-time.com */ /** * @file spellbook.c * Implimentation of spellbooks. */ #include #include #include #include #include static method_ret spellbook_type_apply(ob_methods *context, object *lighter, object *applier, int aflags); /** * Initializer for the SPELLBOOK object type. */ void init_type_spellbook(void) { register_apply(SPELLBOOK, spellbook_type_apply); } /** * Applies a spellbook. * Checks whether player has knowledge of required skill, doesn't * already know the spell, stuff like that. Random learning failure too. * @param context * method context. * @param book * Spellbook to apply. * @param applier * object attempting to apply the spellbook. Should be a player. * @param aflags * special flags (always apply/unapply). * @return * METHOD_OK always. * * @todo * handle failure differently for praying/magic. * @todo * split into multiple functions */ static method_ret spellbook_type_apply(ob_methods *context, object *book, object *applier, int aflags) { object *skapplier, *spell, *spell_skill; int read_level; char level[100]; /* Must be applied by a player. */ if (applier->type == PLAYER) { if (QUERY_FLAG(applier, FLAG_BLIND)&&!QUERY_FLAG(applier, FLAG_WIZ)) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, "You are unable to read while blind.", NULL); return METHOD_OK; } skapplier = find_skill_by_name(applier, book->skill); /* need a literacy skill to learn spells. Also, having a literacy level * lower than the spell will make learning the spell more difficult */ if (!skapplier) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, "You can't read! Your attempt fails.", NULL); return METHOD_OK; } read_level = skapplier->level; spell = book->inv; if (!spell) { LOG(llevError, "apply_spellbook: Book %s has no spell in it!\n", book->name); draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "The spellbook symbols make no sense.", NULL); return METHOD_OK; } if (QUERY_FLAG(book, FLAG_CURSED) || QUERY_FLAG(book, FLAG_DAMNED)) { char name[MAX_BUF]; /* Player made a mistake, let's shake her/him :) */ int failure = -35; if (settings.spell_failure_effects == TRUE) failure = -rndm(35, 100); query_name(book, name, MAX_BUF); draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, "The %s was %s!", "The %s was %s!", name, QUERY_FLAG(book, FLAG_DAMNED) ? "damned" : "cursed"); scroll_failure(applier, failure, (spell->level+4)*7); if (QUERY_FLAG(book, FLAG_DAMNED) && check_spell_known(applier, spell->name) && die_roll(1, 10, applier, 1) < 2) /* Really unlucky player, better luck next time */ do_forget_spell(applier, spell->name); book = decrease_ob(book); if (book && (!QUERY_FLAG(book, FLAG_IDENTIFIED))) { /* Well, not everything is lost, player now knows the * book is cursed/damned. */ identify(book); if (book->env) esrv_update_item(UPD_FLAGS|UPD_NAME, applier, book); else applier->contr->socket.update_look = 1; } return METHOD_OK; } if (QUERY_FLAG(book, FLAG_BLESSED)) read_level += 5; if (spell->level > (read_level+10)) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You are unable to decipher the strange symbols.", NULL); return METHOD_OK; } get_levelnumber(spell->level, level, sizeof(level)); draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, "The spellbook contains the %s level spell %s.", "The spellbook contains the %s level spell %s.", level, spell->name); if (!QUERY_FLAG(book, FLAG_IDENTIFIED)) { identify(book); if (book->env) esrv_update_item(UPD_FLAGS|UPD_NAME, applier, book); else applier->contr->socket.update_look = 1; } /* I removed the check for special_prayer_mark here - it didn't make * a lot of sense - special prayers are not found in spellbooks, and * if the player doesn't know the spell, doesn't make a lot of sense * that they would have a special prayer mark. */ if (check_spell_known(applier, spell->name)) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You already know that spell.\n", NULL); return METHOD_OK; } if (spell->skill) { spell_skill = find_skill_by_name(applier, spell->skill); if (!spell_skill) { draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, "You lack the skill %s to use this spell", "You lack the skill %s to use this spell", spell->skill); return METHOD_OK; } if (spell_skill->level < spell->level) { draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, "You need to be level %d in %s to learn this spell.", "You need to be level %d in %s to learn this spell.", spell->level, spell->skill); return METHOD_OK; } } /* Logic as follows * * 1- MU spells use Int to learn, Cleric spells use Wisdom * * 2- The learner's skill level in literacy adjusts the chance * to learn a spell. * * 3 -Automatically fail to learn if you read while confused * * Overall, chances are the same but a player will find having a high * literacy rate very useful! -b.t. */ if (QUERY_FLAG(applier, FLAG_CONFUSED)) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "In your confused state you flub the wording of the text!", NULL); scroll_failure(applier, 0-random_roll(0, spell->level, applier, PREFER_LOW), MAX(spell->stats.sp, spell->stats.grace)); } else if (QUERY_FLAG(book, FLAG_STARTEQUIP) || (random_roll(0, 100, applier, PREFER_LOW)-(5*read_level)) < learn_spell[spell->stats.grace ? applier->stats.Wis : applier->stats.Int]) { draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, "You succeed in learning the spell!", NULL); do_learn_spell(applier, spell, 0); /* xp gain to literacy for spell learning */ if (!QUERY_FLAG(book, FLAG_STARTEQUIP)) change_exp(applier, calc_skill_exp(applier, book, skapplier), skapplier->skill, 0); } else { play_sound_player_only(applier->contr, SOUND_TYPE_SPELL, book, 0, "fumble"); draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You fail to learn the spell.\n", NULL); } decrease_ob(book); } return METHOD_OK; }