/* * static char *rcsid_gods_c = * "$Id: gods.c 14477 2011-05-21 17:59:50Z ryo_saeba $"; */ /* CrossFire, A Multiplayer game for X-windows Copyright (C) 2006 Mark Wedel & 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 * All this functions handle gods: give presents, punish, and so on. * * Oct 3, 1995 - Code laid down for initial gods, priest alignment, and * monster race initialization. b.t. * * Sept 1996 - moved code over to object -oriented gods -b.t. */ #include #include #include #include #include #ifndef __CEXTRACT__ #include #endif static int worship_forbids_use(object *op, object *exp_obj, uint32 flag, const char *string); static void stop_using_item(object *op, int type, int number); static void update_priest_flag(const object *god, object *exp_ob, uint32 flag); static void god_intervention(object *op, const object *god, object *skill); static int god_examines_priest(object *op, const object *god); static int god_examines_item(const object *god, object *item); static const char *get_god_for_race(const char *race); /** * Returns the id of specified god. * * @param name * god to search for. * @return * identifier of god, -1 if not found. * @todo * couldn't == be used for comparison, if name is a shared string? */ static int lookup_god_by_name(const char *name) { int godnr = -1; size_t nmlen = strlen(name); if (name && strcmp(name, "none")) { godlink *gl; for (gl = first_god; gl; gl = gl->next) if (!strncmp(name, gl->name, MIN(strlen(gl->name), nmlen))) break; if (gl) godnr = gl->id; } return godnr; } /** * Returns pointer to specified god's object through pntr_to_god_obj(). * * @note * returned object shouldn't be modified. * * @param name * god's name. * @return * pointer to god's object, NULL if doesn't match any god. */ const object *find_god(const char *name) { godlink *gl; if (!name) return NULL; for (gl = first_god; gl; gl = gl->next) { if (!strcmp(name, gl->name)) return pntr_to_god_obj(gl); } return NULL; } /** * Determines if op worships a god. * Returns the godname if they do or "none" if they have no god. * In the case of an NPC, if they have no god, we try and guess * who they should worship based on their race. If that fails we * give them a random one. * * @param op * object to get name of. * @return * god name, "none" if nothing suitable. */ const char *determine_god(object *op) { int godnr = -1; const char *godname; /* spells */ if ((op->type == SPELL || op->type == SPELL_EFFECT) && op->title) { if (lookup_god_by_name(op->title) >= 0) return op->title; } if (op->type != PLAYER && QUERY_FLAG(op, FLAG_ALIVE)) { /* find a god based on race */ if (!op->title) { if (op->race != NULL) { godname = get_god_for_race(op->race); if (godname != NULL) { op->title = add_string(godname); } } } /* find a random god */ if (!op->title) { godlink *gl = first_god; godnr = rndm(1, gl->id); while (gl) { if (gl->id == godnr) break; gl = gl->next; } op->title = add_string(gl->name); } return op->title; } /* The god the player worships is in the praying skill (native skill * not skill tool). Since a player can only have one instance of * that skill, once we find it, we can return, either with the * title or "none". */ if (op->type == PLAYER) { object *tmp; for (tmp = op->inv; tmp != NULL; tmp = tmp->below) if (tmp->type == SKILL && tmp->subtype == SK_PRAYING) { if (tmp->title) return (tmp->title); else return("none"); } } return ("none"); } /** * Compares 2 strings. * @param s1 * @param s2 * strings to compare. * @return * 1 if s1 and s2 are the same - either both NULL, or strcmp( ) == 0. */ static int same_string(const char *s1, const char *s2) { if (s1 == NULL) if (s2 == NULL) return 1; else return 0; else if (s2 == NULL) return 0; else return strcmp(s1, s2) == 0; } /** * Removes from a player's inventory all items bestowed by * a particular god. Intended mainly for use in punishing * characters for switching gods. * * @param pl * the player object * * @param op * the object to be searched for items * * @param god * the god whose objects to remove * */ static void follower_remove_given_items(object *pl, object *op, const object *god) { object *tmp, *next; const char *given_by; /* search the inventory */ for (tmp = op->inv; tmp != NULL; tmp = next) { next = tmp->below; /* backup in case we remove tmp */ given_by = get_ob_key_value(tmp, "divine_giver_name"); if (given_by == god->name) { char name[HUGE_BUF]; query_short_name(tmp, name, HUGE_BUF); /* Send the client a message. */ if (tmp->nrof > 1) draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "The %s crumble to dust!", "The %s crumble to dust!", name); else draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "The %s crumbles to dust!", "The %s crumbles to dust!", name); remove_ob(tmp); /* remove obj from players inv. */ free_object(tmp); } else if (tmp->inv) follower_remove_given_items(pl, tmp, god); } } /** * Checks for any occurrence of the given 'item' in the inventory of 'op' (recursively). * @param op * object to check. * @param item * object to check for. * @return * 1 if found, else 0. */ static int follower_has_similar_item(object *op, object *item) { object *tmp; for (tmp = op->inv; tmp != NULL; tmp = tmp->below) { if (tmp->type == item->type && same_string(tmp->name, item->name) && same_string(tmp->title, item->title) && same_string(tmp->msg, item->msg) && same_string(tmp->slaying, item->slaying)) return 1; if (tmp->inv && follower_has_similar_item(tmp, item)) return 1; } return 0; } /** * God gives an item to the player. Inform player of the present. * * @param op * who is getting the treasure. * @param god * god giving the present. * @param tr * object to give. Should be a single object on list. * @return * 0 if nothing was given, 1 else. */ static int god_gives_present(object *op, const object *god, treasure *tr) { object *tmp; char name[HUGE_BUF]; if (follower_has_similar_item(op, &tr->item->clone)) return 0; tmp = arch_to_object(tr->item); /* * Give inventory if needed, for instance Lythander's pipe. * Use high level and magic so something IS put in inventory. */ fix_generated_item(tmp, NULL, 120, 120, GT_ONLY_GOOD); /* And just in case nothing was put and something is needed, bail out. */ if ((tmp->type == ROD || tmp->type == WAND) && (tmp->inv == NULL)) { free_object2(tmp, 0); return 0; } query_short_name(tmp, name, HUGE_BUF); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_ADD, "%s lets %s appear in your hands.", NULL, god->name, name); /** * Mark what god gave it, so it can be taken vengefully later! */ set_ob_key_value(tmp, "divine_giver_name", god->name, TRUE); insert_ob_in_ob(tmp, op); return 1; } /** * Player prays at altar. * Checks for god changing, divine intervention, and so on. * * @param pl * player praying. * @param altar * altar player's praying on. Doesn't need to be consecrated. * @param skill * praying skill. */ void pray_at_altar(object *pl, object *altar, object *skill) { const object *pl_god = find_god(determine_god(pl)); /* Lauwenmark: Handle for plugin altar-praying (apply) event */ if (execute_event(altar, EVENT_APPLY, pl, NULL, NULL, SCRIPT_FIX_ALL) != 0) return; /* If non consecrate altar, don't do anything */ if (!altar->other_arch) return; /* hmm. what happend depends on pl's current god, level, etc */ if (!pl_god) { /*new convert */ become_follower(pl, &altar->other_arch->clone); return; } else if (!strcmp(pl_god->name, altar->other_arch->clone.name)) { /* pray at your gods altar */ int bonus = (pl->stats.Wis+skill->level)/10; /* we can get neg grace up faster */ if (pl->stats.grace < 0) pl->stats.grace += (bonus > -1*(pl->stats.grace/10) ? bonus : -1*(pl->stats.grace/10)); /* we can super-charge grace to 2x max */ if (pl->stats.grace < (2*pl->stats.maxgrace)) { pl->stats.grace += bonus/2; } if (pl->stats.grace > (2*pl->stats.maxgrace)) { pl->stats.grace = (2*pl->stats.maxgrace); } /* Every once in a while, the god decides to checkup on their * follower, and may intervene to help them out. */ bonus = MAX(1, bonus+MAX(pl->stats.luck, -3)); /* -- DAMN -- */ if (((random_roll(0, 399, pl, PREFER_LOW))-bonus) < 0) god_intervention(pl, pl_god, skill); } else { /* praying to another god! */ uint64 loss = 0; int angry = 1; /* I believe the logic for detecting opposing gods was completely * broken - I think it should work now. altar->other_arch * points to the god of this altar (which we have * already verified is non null). pl_god->other_arch * is the opposing god - we need to verify that exists before * using its values. */ if (pl_god->other_arch && (altar->other_arch->name == pl_god->other_arch->name)) { angry = 2; if (random_roll(0, skill->level+2, pl, PREFER_LOW)-5 > 0) { object *tmp; /* you really screwed up */ angry = 3; draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, pl, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "Foul Priest! %s punishes you!", "Foul Priest! %s punishes you!", pl_god->name); tmp = create_archetype(LOOSE_MANA); cast_magic_storm(pl, tmp, pl_god->level+20); } else draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, pl, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "Foolish heretic! %s is livid!", "Foolish heretic! %s is livid!", pl_god->name); } else draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, pl, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "Heretic! %s is angered!", "Heretic! %s is angered!", pl_god->name); /* whether we will be successfull in defecting or not - * we lose experience from the clerical experience obj */ loss = angry*(skill->stats.exp/10); if (loss) change_exp(pl, -random_roll64(0, loss, pl, PREFER_LOW), skill ? skill->skill : "none", SK_SUBTRACT_SKILL_EXP); /* May switch Gods, but its random chance based on our current level * note it gets harder to swap gods the higher we get */ if ((angry == 1) && !(random_roll(0, skill->level, pl, PREFER_LOW))) { become_follower(pl, &altar->other_arch->clone); } else { /* toss this player off the altar. He can try again. */ draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, pl, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "A divine force pushes you off the altar.", NULL); move_player(pl, absdir(pl->facing+4)); /* back him off the way he came. */ } } } /** * Removes special prayers given by a god. * * @param op * player to remove prayers from. * @param god * god we're removing the prayers. */ static void check_special_prayers(object *op, const object *god) { /* Ensure that 'op' doesn't know any special prayers that are not granted * by 'god'. */ treasure *tr; object *tmp, *next_tmp; int remove = 0; /* Outer loop iterates over all special prayer marks */ for (tmp = op->inv; tmp; tmp = next_tmp) { next_tmp = tmp->below; /* we mark special prayers with the STARTEQUIP flag, so if it isn't * in that category, not something we need to worry about. */ if (tmp->type != SPELL || !QUERY_FLAG(tmp, FLAG_STARTEQUIP)) continue; if (god->randomitems == NULL) { LOG(llevError, "BUG: check_special_prayers(): god %s without randomitems\n", god->name); do_forget_spell(op, tmp->name); continue; } /* Inner loop tries to find the special prayer in the god's treasure * list. We default that the spell should be removed. */ remove = 1; for (tr = god->randomitems->items; tr; tr = tr->next) { object *item; if (tr->item == NULL) continue; item = &tr->item->clone; /* Basically, see if the matching spell is granted by this god. */ if (tr->item->clone.type == SPELL && tr->item->clone.name == tmp->name) { remove = 0; break; } } if (remove) { /* just do the work of removing the spell ourselves - we already * know that the player knows the spell */ draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You lose knowledge of %s.", "You lose knowledge of %s.", tmp->name); player_unready_range_ob(op->contr, tmp); remove_ob(tmp); free_object(tmp); } } } /** * This function is called whenever a player has * switched to a new god. It handles basically all the stat changes * that happen to the player, including the removal of godgiven * items (from the former cult). * * @param op * player switching cults. * @param new_god * new god to worship. * @todo isn't there duplication with check_special_prayers() for spell removing? */ void become_follower(object *op, const object *new_god) { const object *old_god = NULL; /* old god */ treasure *tr; object *item, *skop, *next; int i, sk_applied; int undeadified = 0; /* Turns to true if changing god can changes the undead * status of the player.*/ old_god = find_god(determine_god(op)); /* take away any special god-characteristic items. */ for (item = op->inv; item != NULL; item = next) { next = item->below; /* remove all invisible startequip items which are * not skill, exp or force */ if (QUERY_FLAG(item, FLAG_STARTEQUIP) && item->invisible && (item->type != SKILL) && (item->type != EXPERIENCE) && (item->type != FORCE)) { if (item->type == SPELL) draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You lose knowledge of %s.", "You lose knowledge of %s.", item->name); player_unready_range_ob(op->contr, item); remove_ob(item); free_object(item); } } /* remove any items given by the old god */ if (old_god) { /* Changed to use the new "divine_giver_name" key_value * so it can reliably delete enchanted items. Now it loops * through the player's inventory, instead of the god's * treasure list. */ follower_remove_given_items(op, op, old_god); } if (!op || !new_god) return; if (op->race && new_god->slaying && strstr(op->race, new_god->slaying)) { draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "Fool! %s detests your kind!", "Fool! %s detests your kind!", new_god->name); if (random_roll(0, op->level-1, op, PREFER_LOW)-5 > 0) { object *tmp = create_archetype(LOOSE_MANA); cast_magic_storm(op, tmp, new_god->level+10); } return; } /* give the player any special god-characteristic-items. */ for (tr = new_god->randomitems->items; tr != NULL; tr = tr->next) { if (tr->item && tr->item->clone.invisible && tr->item->clone.type != SPELLBOOK && tr->item->clone.type != BOOK && tr->item->clone.type != SPELL) god_gives_present(op, new_god, tr); } draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You become a follower of %s!", "You become a follower of %s!", new_god->name); for (skop = op->inv; skop != NULL; skop = skop->below) if (skop->type == SKILL && skop->subtype == SK_PRAYING) break; /* Player has no skill - give them the skill */ if (!skop) { /* The archetype should always be defined - if we crash here because it doesn't, * things are really messed up anyways. */ skop = give_skill_by_name(op, get_archetype_by_type_subtype(SKILL, SK_PRAYING)->clone.skill); link_player_skills(op); } sk_applied = QUERY_FLAG(skop, FLAG_APPLIED); /* save skill status */ /* Clear the "undead" status. We also need to force a call to change_abil, * so I set undeadified for that. * - gros, 21th July 2006. */ if ((old_god) && (QUERY_FLAG(old_god, FLAG_UNDEAD))) { CLEAR_FLAG(skop, FLAG_UNDEAD); undeadified = 1; } if (skop->title) { /* get rid of old god */ draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "%s's blessing is withdrawn from you.", "%s's blessing is withdrawn from you.", skop->title); /* The point of this is to really show what abilities the player just lost */ if (sk_applied || undeadified) { CLEAR_FLAG(skop, FLAG_APPLIED); (void)change_abil(op, skop); } free_string(skop->title); } /* now change to the new gods attributes to exp_obj */ skop->title = add_string(new_god->name); skop->path_attuned = new_god->path_attuned; skop->path_repelled = new_god->path_repelled; skop->path_denied = new_god->path_denied; /* copy god's resistances */ memcpy(skop->resist, new_god->resist, sizeof(new_god->resist)); /* make sure that certain immunities do NOT get passed * to the follower! */ for (i = 0; i < NROFATTACKS; i++) if (skop->resist[i] > 30 && (i == ATNR_FIRE || i == ATNR_COLD || i == ATNR_ELECTRICITY || i == ATNR_POISON)) skop->resist[i] = 30; skop->stats.hp = (sint16)new_god->last_heal; skop->stats.sp = (sint16)new_god->last_sp; skop->stats.grace = (sint16)new_god->last_grace; skop->stats.food = (sint16)new_god->last_eat; skop->stats.luck = (sint8)new_god->stats.luck; /* gods may pass on certain flag properties */ update_priest_flag(new_god, skop, FLAG_SEE_IN_DARK); update_priest_flag(new_god, skop, FLAG_REFL_SPELL); update_priest_flag(new_god, skop, FLAG_REFL_MISSILE); update_priest_flag(new_god, skop, FLAG_STEALTH); update_priest_flag(new_god, skop, FLAG_MAKE_INVIS); update_priest_flag(new_god, skop, FLAG_UNDEAD); update_priest_flag(new_god, skop, FLAG_BLIND); update_priest_flag(new_god, skop, FLAG_XRAYS); /* better have this if blind! */ update_priest_flag(new_god, skop, FLAG_USE_WEAPON); update_priest_flag(new_god, skop, FLAG_USE_ARMOUR); update_priest_flag(new_god, skop, FLAG_USE_SHIELD); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You are bathed in %s's aura.", "You are bathed in %s's aura.", new_god->name); /* Weapon/armour use are special...handle flag toggles here as this can * only happen when gods are worshipped and if the new priest could * have used armour/weapons in the first place. * * This also can happen for monks which cannot use weapons. In this case * do not allow to use weapons even if the god otherwise would allow it. */ if (!present_in_ob_by_name(FORCE, "no weapon force", op)) { if (worship_forbids_use(op, skop, FLAG_USE_WEAPON, "weapons")) stop_using_item(op, WEAPON, 2); } update_priest_flag(new_god, skop, FLAG_USE_ARMOUR); if (worship_forbids_use(op, skop, FLAG_USE_ARMOUR, "armour")) { stop_using_item(op, ARMOUR, 1); stop_using_item(op, HELMET, 1); stop_using_item(op, BOOTS, 1); stop_using_item(op, GLOVES, 1); } if (worship_forbids_use(op, skop, FLAG_USE_SHIELD, "shield")) stop_using_item(op, SHIELD, 1); SET_FLAG(skop, FLAG_APPLIED); (void)change_abil(op, skop); /* return to previous skill status */ if (!sk_applied) CLEAR_FLAG(skop, FLAG_APPLIED); check_special_prayers(op, new_god); } /** * Forbids or let player use something item type. * @param op * player. * @param exp_obj * praying skill. * @param flag * FLAG_xxx to check against. * @param string * what flag corresponds to ("weapons", "shield", ...). * @return * 1 if player was changed, 0 if no change. */ static int worship_forbids_use(object *op, object *exp_obj, uint32 flag, const char *string) { if (QUERY_FLAG(&op->arch->clone, flag)) { if (QUERY_FLAG(op, flag) != QUERY_FLAG(exp_obj, flag)) { update_priest_flag(exp_obj, op, flag); if (QUERY_FLAG(op, flag)) draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You may use %s again.", "You may use %s again.", string); else { draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "You are forbidden to use %s.", "You are forbidden to use %s.", string); return 1; } } } return 0; } /** * Unapplies up to number worth of items of type type, ignoring curse status. * This is used when the player gets forbidden to use eg weapons. * * @param op * player we're considering. * @param type * item type to remove. * @param number * maximum number of items to unapply. */ static void stop_using_item(object *op, int type, int number) { object *tmp; for (tmp = op->inv; tmp && number; tmp = tmp->below) if (tmp->type == type && QUERY_FLAG(tmp, FLAG_APPLIED)) { apply_special(op, tmp, AP_UNAPPLY|AP_IGNORE_CURSE); number--; } } /** * If the god does/doesnt have this flag, we * give/remove it from the experience object if it doesnt/does * already exist. * * @param god * god object. * @param exp_ob * player's praying skill object. * @param flag * flag to consider. */ static void update_priest_flag(const object *god, object *exp_ob, uint32 flag) { if (QUERY_FLAG(god, flag) && !QUERY_FLAG(exp_ob, flag)) SET_FLAG(exp_ob, flag); else if (QUERY_FLAG(exp_ob, flag) && !QUERY_FLAG(god, flag)) { /* When this is called with the exp_ob set to the player, * this check is broken, because most all players arch * allow use of weapons. I'm not actually sure why this * check is here - I guess if you had a case where the * value in the archetype (wisdom) should over ride the restrictions * the god places on it, this may make sense. But I don't think * there is any case like that. */ /* if (!(QUERY_FLAG(&(exp_ob->arch->clone), flag)))*/ CLEAR_FLAG(exp_ob, flag); } } /** * Determines the archetype for holy servant and god avatar. * * Possible monsters are stored as invisible books in god's inventory, * one having the right name is selected randomly. * * @param god * god for which we want something. * @param type * what the summon type is. Must be a shared string. * @return * random archetype matching the type, NULL if none found. */ archetype *determine_holy_arch(const object *god, const char *type) { treasure *tr; int count; archetype *last; object *item; if (!god || !god->randomitems) { LOG(llevError, "BUG: determine_holy_arch(): no god or god without randomitems\n"); return NULL; } count = 0; last = NULL; for (tr = god->randomitems->items; tr != NULL; tr = tr->next) { if (!tr->item) continue; item = &tr->item->clone; if (item->type == BOOK && item->invisible && item->name == type) count++; } if (count == 0) { return NULL; } count = rndm(1, count); for (tr = god->randomitems->items; tr != NULL; tr = tr->next) { if (!tr->item) continue; item = &tr->item->clone; if (item->type == BOOK && item->invisible && item->name == type) { count--; if (count == 0) return item->other_arch; } } return NULL; } /** * God helps player by removing curse and/or damnation. * * @param op * player to help. * @param remove_damnation * if set, also removes damned items. * @return * 1 if at least one item was uncursed, 0 else. */ static int god_removes_curse(object *op, int remove_damnation) { object *tmp; int success = 0; for (tmp = op->inv; tmp; tmp = tmp->below) { if (tmp->invisible) continue; if (QUERY_FLAG(tmp, FLAG_DAMNED) && !remove_damnation) continue; if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) { success = 1; CLEAR_FLAG(tmp, FLAG_DAMNED); CLEAR_FLAG(tmp, FLAG_CURSED); CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED); if (op->type == PLAYER) esrv_update_item(UPD_FLAGS, op, tmp); } } if (success) draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "You feel like someone is helping you.", NULL); return success; } /** * Converts a level and difficulty to a magic/enchantment value for eg weapons. * @param level * level * @param difficulty * difficulty. Must be 1 or more. * @return * number of enchantments for level and difficulty. */ static int follower_level_to_enchantments(int level, int difficulty) { if (difficulty < 1) { LOG(llevError, "follower_level_to_enchantments(): difficulty %d is invalid\n", difficulty); return 0; } if (level <= 20) return level/difficulty; if (level <= 40) return (20+(level-20)/2)/difficulty; return (30+(level-40)/4)/difficulty; } /** * Utility function for improving the magic on a weapon. * Affected weapon is the applied one (weapon or bow). This utility function * improves the weapon magic on a weapon being enchanted by a god. This was * necessary because the same block of the code was being called from two * places in the god_enchants_weapon(...) function. * * @param op * player * @param tr * treasure list item for enchanting weapon, contains the enchantment level. * @param weapon * weapon being modified * @param skill * praying skill of op. * @return * 0 if weapon wasn't changed, 1 if changed. */ static int improve_weapon_magic(object *op, object *tr, object *weapon, object *skill) { int tmp = follower_level_to_enchantments(skill->level, tr->level); if (weapon->magic < tmp) { draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "A phosphorescent glow envelops your weapon!", NULL); weapon->magic++; if (op->type == PLAYER) esrv_update_item(UPD_NAME, op, weapon); weapon->item_power++; return 1; } return 0; } /** * God wants to enchant weapon. * Affected weapon is the applied one (weapon or bow). It's checked to make sure * it isn't a weapon for another god. If all is all right, update weapon with * attacktype, slaying and such. * * @param op * player * @param god * god enchanting weapon. * @param tr * treasure list item for enchanting weapon, contains the enchantment level. * @param skill * praying skill of op. * @return * 0 if weapon wasn't changed, 1 if changed. */ static int god_enchants_weapon(object *op, const object *god, object *tr, object *skill) { char buf[MAX_BUF]; object *weapon; uint32 attacktype; for (weapon = op->inv; weapon; weapon = weapon->below) if ((weapon->type == WEAPON || weapon->type == BOW) && QUERY_FLAG(weapon, FLAG_APPLIED)) break; if (weapon == NULL || god_examines_item(god, weapon) <= 0) return 0; if (weapon->item_power >= MAX_WEAPON_ITEM_POWER) { draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, "%s considers your %s is not worthy to be enchanted any more.", "%s considers your %s is not worthy to be enchanted any more.", god->name, weapon->name); return 0; } /* If personalized_blessings is activated, then the god's name is * associated with the weapon, as well as the owner (the one who blesses it), * and a "weapon willpower", which is equivalent to the owner's experience * in praying when the weapon is blessed. * Those values are used later, when another player attempts to wield the * weapon - nasty things may happen to those who do not deserve to use it ! :) */ if (settings.personalized_blessings) { const char *divine_owner = get_ob_key_value(weapon, "divine_blessing_name"); const char *owner = get_ob_key_value(weapon, "item_owner"); object *skillop = NULL; if (divine_owner != NULL) { if (!strcmp(divine_owner, god->name)) { /* It already belongs to this god - do not go further. */ /* [DT] (2009-06-10): It is ok if the weapon has already been enchanted one time, but in that particular case we only give out additional plusses. No new slays or new attacktypes */ /* @@ Quick hack to allow already enchanted items of this god to add slaying and attacktype. Presumably negating the above comment. */ /* Allow the weapon to slay enemies */ if (!weapon->slaying && god->slaying) { weapon->slaying = add_string(god->slaying); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "Your %s now hungers to slay enemies of your god!", "Your %s now hungers to slay enemies of your god!", weapon->name); weapon->item_power++; return 1; } /* Add the gods attacktype */ attacktype = (weapon->attacktype == 0) ? AT_PHYSICAL : weapon->attacktype; if ((attacktype&god->attacktype) != god->attacktype) { draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "Your weapon suddenly glows!", NULL); weapon->attacktype = attacktype|god->attacktype; weapon->item_power++; return 1; } return improve_weapon_magic(op, tr, weapon, skill); } /* Huho... Another god already blessed this one ! */ draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, "Your %s already belongs to %s !", "Your %s already belongs to %s !", weapon->name, divine_owner); return 0; } else if ((owner != NULL) && (!strcmp(owner, op->name))) { /* Maybe the weapon itself will not agree ? */ draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, "The %s is not yours, and is magically protected against such changes !", "The %s is not yours, and is magically protected against such changes !", weapon->name); return 0; } skillop = find_skill_by_number(op, SK_PRAYING); if (skillop == NULL) { LOG(llevError, "god_enchants_weapon: no praying skill object found ?!\n"); snprintf(buf, sizeof(buf), "%d", 1); } else snprintf(buf, sizeof(buf), "%"FMT64, skillop->stats.exp); set_ob_key_value(weapon, "divine_blessing_name", god->name, TRUE); set_ob_key_value(weapon, "item_owner", op->name, TRUE); set_ob_key_value(weapon, "item_willpower", buf, TRUE); } /* First give it a title, so other gods won't touch it */ if (!weapon->title) { snprintf(buf, sizeof(buf), "of %s", god->name); weapon->title = add_string(buf); if (op->type == PLAYER) esrv_update_item(UPD_NAME, op, weapon); draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "Your weapon quivers as if struck!", NULL); } /* Allow the weapon to slay enemies */ if (!weapon->slaying && god->slaying) { weapon->slaying = add_string(god->slaying); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "Your %s now hungers to slay enemies of your god!", "Your %s now hungers to slay enemies of your god!", weapon->name); weapon->item_power++; return 1; } /* Add the gods attacktype */ attacktype = (weapon->attacktype == 0) ? AT_PHYSICAL : weapon->attacktype; if ((attacktype&god->attacktype) != god->attacktype) { draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, "Your weapon suddenly glows!", NULL); weapon->attacktype = attacktype|god->attacktype; weapon->item_power++; return 1; } /* Higher magic value */ return improve_weapon_magic(op, tr, weapon, skill); } /** * Every once in a while the god will intervene to help the worshiper. * Later, this fctn can be used to supply quests, etc for the * priest. -b.t. * called from pray_at_altar() currently. * * @param op * player praying. * @param god * god player is praying to. * @param skill * player's praying skill. */ static void god_intervention(object *op, const object *god, object *skill) { treasure *tr; if (!god || !god->randomitems) { LOG(llevError, "BUG: god_intervention(): no god or god without randomitems\n"); return; } check_special_prayers(op, god); /* lets do some checks of whether we are kosher with our god */ if (god_examines_priest(op, god) < 0) return; draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "You feel a holy presence!", NULL); for (tr = god->randomitems->items; tr != NULL; tr = tr->next) { object *item; if (tr->chance <= random_roll(0, 99, op, PREFER_HIGH)) continue; /* Treasurelist - generate some treasure for the follower */ if (tr->name) { treasurelist *tl = find_treasurelist(tr->name); if (tl == NULL) continue; draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_ADD, "Something appears before your eyes. You catch it before it falls to the ground.", NULL); create_treasure(tl, op, GT_STARTEQUIP|GT_ONLY_GOOD|GT_UPDATE_INV, skill->level, 0); return; } if (!tr->item) { LOG(llevError, "BUG: empty entry in %s's treasure list\n", god->name); continue; } item = &tr->item->clone; /* Grace limit */ if (item->type == BOOK && item->invisible && strcmp(item->name, "grace limit") == 0) { if (op->stats.grace < item->stats.grace || op->stats.grace < op->stats.maxgrace) { object *tmp; /* Follower lacks the required grace for the following * treasure list items. */ tmp = create_archetype(HOLY_POSSESSION); cast_change_ability(op, op, tmp, 0, 1); free_object(tmp); return; } continue; } /* Restore grace */ if (item->type == BOOK && item->invisible && strcmp(item->name, "restore grace") == 0) { if (op->stats.grace >= 0) continue; op->stats.grace = random_roll(0, 9, op, PREFER_HIGH); draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "You are returned to a state of grace.", NULL); return; } /* Heal damage */ if (item->type == BOOK && item->invisible && strcmp(item->name, "restore hitpoints") == 0) { if (op->stats.hp >= op->stats.maxhp) continue; draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "A white light surrounds and heals you!", NULL); op->stats.hp = op->stats.maxhp; return; } /* Restore spellpoints */ if (item->type == BOOK && item->invisible && strcmp(item->name, "restore spellpoints") == 0) { int max = op->stats.maxsp*(item->stats.maxsp/100.0); /* Restore to 50 .. 100%, if sp < 50% */ int new_sp = random_roll(1000, 1999, op, PREFER_HIGH)/2000.0*max; if (op->stats.sp >= max/2) continue; draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "A blue lightning strikes your head but doesn't hurt you!", NULL); op->stats.sp = new_sp; } /* Various heal spells */ if (item->type == BOOK && item->invisible && strcmp(item->name, "heal spell") == 0) { object *tmp; int success; tmp = create_archetype_by_object_name(item->slaying); success = cast_heal(op, op, tmp, 0); free_object(tmp); if (success) return; else continue; } /* Remove curse */ if (item->type == BOOK && item->invisible && strcmp(item->name, "remove curse") == 0) { if (god_removes_curse(op, 0)) return; else continue; } /* Remove damnation */ if (item->type == BOOK && item->invisible && strcmp(item->name, "remove damnation") == 0) { if (god_removes_curse(op, 1)) return; else continue; } /* Heal depletion */ if (item->type == BOOK && item->invisible && strcmp(item->name, "heal depletion") == 0) { object *depl; archetype *at; int i; if ((at = find_archetype(ARCH_DEPLETION)) == NULL) { LOG(llevError, "Could not find archetype depletion.\n"); continue; } depl = present_arch_in_ob(at, op); if (depl == NULL) continue; draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "Shimmering light surrounds and restores you!", NULL); for (i = 0; i < NUM_STATS; i++) if (get_attr_value(&depl->stats, i)) draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END, restore_msg[i], restore_msg[i]); remove_ob(depl); free_object(depl); fix_object(op); return; } /* Voices */ if (item->type == BOOK && item->invisible && strcmp(item->name, "voice_behind") == 0) { draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "You hear a voice from behind you, but you don't dare to " "turn around:", NULL); draw_ext_info(NDI_WHITE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, item->msg, item->msg); return; } /* Messages */ if (item->type == BOOK && item->invisible && strcmp(item->name, "message") == 0) { draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, item->msg, item->msg); return; } /* Enchant weapon */ if (item->type == BOOK && item->invisible && strcmp(item->name, "enchant weapon") == 0) { if (god_enchants_weapon(op, god, item, skill)) return; else continue; } /* Spellbooks - works correctly only for prayers */ if (item->type == SPELL) { if (check_spell_known(op, item->name)) continue; if (item->level > skill->level) continue; draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "%s grants you use of a special prayer!", "%s grants you use of a special prayer!", god->name); do_learn_spell(op, item, 1); return; } /* Other gifts */ if (!item->invisible) { if (god_gives_present(op, god, tr)) return; else continue; } /* else ignore it */ } draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_PRAY, "You feel rapture.", NULL); } /** * Checks and maybe punishes someone praying. * All applied items are examined, if player is using more items of other gods, * s/he loses experience in praying or general experience if no praying. * * @param op * player the god examines. * @param god * god examining the player. * @return * negative value is god is not pleased, else positive value, the higher the better. */ static int god_examines_priest(object *op, const object *god) { int reaction = 1; object *item = NULL, *skop; for (item = op->inv; item; item = item->below) { if (QUERY_FLAG(item, FLAG_APPLIED)) { reaction += god_examines_item(god, item)*(item->magic ? abs(item->magic) : 1); } } /* well, well. Looks like we screwed up. Time for god's revenge */ if (reaction < 0) { int loss = 10000000; int angry = abs(reaction); for (skop = op->inv; skop != NULL; skop = skop->below) if (skop->type == SKILL && skop->subtype == SK_PRAYING) break; if (skop) loss = 0.05*(float)skop->stats.exp; change_exp(op, -random_roll(0, loss*angry-1, op, PREFER_LOW), skop ? skop->skill : "none", SK_SUBTRACT_SKILL_EXP); if (random_roll(0, angry, op, PREFER_LOW)) { object *tmp = create_archetype(LOOSE_MANA); cast_magic_storm(op, tmp, op->level+(angry*3)); } draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "%s becomes angry and punishes you!", "%s becomes angry and punishes you!", god->name); } return reaction; } /** * God checks item the player is using. * If you are using the item of an enemy * god, it can be bad...-b.t. * * @param god * god checking. * @param item * item to check. * @retval -1 * item is bad. * @retval 0 * item is neutral. * @retval 1 * item is good. */ static int god_examines_item(const object *god, object *item) { char buf[MAX_BUF]; if (!god || !item) return 0; if (!item->title) return 1; /* unclaimed item are ok */ snprintf(buf, sizeof(buf), "of %s", god->name); if (!strcmp(item->title, buf)) return 1; /* belongs to that God */ if (god->title) { /* check if we have any enemy blessed item*/ snprintf(buf, sizeof(buf), "of %s", god->title); if (!strcmp(item->title, buf)) { if (item->env) { char name[MAX_BUF]; query_name(item, name, MAX_BUF); draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, item->env, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "Heretic! You are using %s!", "Heretic! You are using %s!", name); } return -1; } } return 0; /* item is sacred to a non-enemy god/or is otherwise magical */ } /** * Returns a string that is the name of the god that should be natively worshipped by a * creature of who has race *race * if we can't find a god that is appropriate, we return NULL * * @param race * race we're getting the god of. * @return * NULL if no matching race, else god's name. */ static const char *get_god_for_race(const char *race) { godlink *gl = first_god; const char *godname = NULL; if (race == NULL) return NULL; while (gl) { if (gl->arch->clone.race && !strcasecmp(gl->arch->clone.race, race)) { godname = gl->name; break; } gl = gl->next; } return godname; } /** * Changes the attributes of cone, smite, and ball spells as needed by the code. * * @param spellop * spell object to change. * @param caster * what is casting spellop (player, spell, ...). * @return * 0 if there was no race to assign to the slaying field of the spell, but * the spell attacktype contains AT_HOLYWORD, 1 else. */ int tailor_god_spell(object *spellop, object *caster) { const object *god = find_god(determine_god(caster)); int caster_is_spell = 0; if (caster->type == SPELL_EFFECT || caster->type == SPELL) caster_is_spell = 1; /* if caster is a rune or the like, it doesn't worship anything. However, * if this object is owned by someone, then the god that they worship * is relevant, so use that. */ if (!god && get_owner(caster)) god = find_god(determine_god(get_owner(caster))); if (!god || (spellop->attacktype&AT_HOLYWORD && !god->race)) { if (!caster_is_spell) draw_ext_info(NDI_UNIQUE, 0, caster, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD, "This prayer is useless unless you worship an appropriate god", NULL); else LOG(llevError, "BUG: tailor_god_spell(): no god\n"); free_object(spellop); return 0; } /* either holy word or godpower attacks will set the slaying field */ if (spellop->attacktype&AT_HOLYWORD || spellop->attacktype&AT_GODPOWER) { if (spellop->slaying) { free_string(spellop->slaying); spellop->slaying = NULL; } if (!caster_is_spell) spellop->slaying = god->slaying ? add_string(god->slaying) : NULL; else if (caster->slaying) spellop->slaying = add_string(caster->slaying); } /* only the godpower attacktype adds the god's attack onto the spell */ if (spellop->attacktype&AT_GODPOWER) spellop->attacktype = spellop->attacktype|god->attacktype; /* tack on the god's name to the spell */ if (spellop->attacktype&AT_HOLYWORD || spellop->attacktype&AT_GODPOWER) { if (spellop->title) free_string(spellop->title); spellop->title = add_string(god->name); if (spellop->title) { char buf[MAX_BUF]; snprintf(buf, sizeof(buf), "%s of %s", spellop->name, spellop->title); FREE_AND_COPY(spellop->name, buf); FREE_AND_COPY(spellop->name_pl, buf); } } return 1; }