server-1.12/server/gods.c

1474 lines
50 KiB
C

/*
* 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 <global.h>
#include <living.h>
#include <object.h>
#include <spells.h>
#include <sounds.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#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;
}