version 1.105 | | version 1.106 |
---|
| | |
/* | | /* |
* static char *rcsid_spell_effect_c = | | * static char *rcsid_spell_effect_c = |
* "$Id: spell_effect.c,v 1.105 2003/09/03 12:25:06 tchize Exp $"; | | * "$Id: spell_effect.c,v 1.106 2003/09/13 05:02:13 mwedel Exp $"; |
*/ | | */ |
| | |
| | |
| | |
#include <spells.h> | | #include <spells.h> |
#include <sounds.h> | | #include <sounds.h> |
| | |
extern object *objects; | | /* cast_magic_storm: This is really used mostly for spell |
| | * fumbles at the like. tmp is the object to propogate. |
/* | | * op is what is casting this. |
* spell_failure() handles the various effects for differing degrees | | |
* of failure badness. | | |
*/ | | */ |
| | void cast_magic_storm(object *op, object *tmp, int lvl) |
void spell_failure(object *op, int failure,int power) | | |
{ | | |
if (settings.spell_failure_effects == FALSE) | | |
return; | | |
| | |
if(failure<= -20&&failure > -40) /* wonder */ | | |
{ | | |
new_draw_info(NDI_UNIQUE, 0,op,"Your spell causes an unexpected effect."); | | |
cast_cone(op,op,0,10,SP_WOW,spellarch[SP_WOW],0); | | |
} | | |
| | |
else if (failure <= -40&&failure > -60) /* confusion */ | | |
{ | | |
new_draw_info(NDI_UNIQUE, 0,op,"Your magic recoils on you!"); | | |
confuse_player(op,op,99); | | |
} | | |
else if (failure <= -60&&failure> -80) /* paralysis */ | | |
{ | | |
new_draw_info(NDI_UNIQUE, 0,op,"Your magic recoils on you!"); | | |
paralyze_player(op,op,99); | | |
} | | |
else if (failure <= -80) /* blast the immediate area */ | | |
{ | | |
object *tmp; | | |
/* Safety check to make sure we don't get any mana storms in scorn */ | | |
if (get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL) & P_NO_MAGIC) { | | |
new_draw_info(NDI_UNIQUE, 0, op, "The magic warps and you are turned inside out!"); | | |
hit_player(tmp,9998,op,AT_INTERNAL); | | |
| | |
} else { | | |
new_draw_info(NDI_UNIQUE, 0,op,"You lose control of the mana! The uncontrolled magic blasts you!"); | | |
tmp=get_archetype("loose_magic"); | | |
tmp->level=SK_level(op); | | |
tmp->x=op->x;tmp->y=op->y; | | |
| | |
/* increase the area of destruction a little for more powerful spells */ | | |
tmp->stats.hp+=isqrt(power); | | |
| | |
if (power>25) tmp->stats.dam = 25 + isqrt(power); | | |
else tmp->stats.dam=power; /* nasty recoils! */ | | |
| | |
tmp->stats.maxhp=tmp->count; /*??*/ | | |
insert_ob_in_map(tmp,op->map,NULL,0); | | |
} | | |
} | | |
} | | |
| | |
/* Oct 95 - hacked on this to bring in cosmetic differences for MULTIPLE_GOD hack -b.t. */ | | |
| | |
void prayer_failure(object *op, int failure,int power) | | |
{ | | |
char *godname; | | |
| | |
if(!strcmp((godname=determine_god(op)),"none")) godname="Your spirit"; | | |
| | |
if(failure<= -20&&failure > -40) /* wonder */ | | |
{ | | |
new_draw_info_format(NDI_UNIQUE, 0,op,"%s gives a sign to renew your faith.",godname); | | |
cast_cone(op,op,0,10,SP_WOW,spellarch[SP_WOW],0); | | |
} | | |
| | |
else if (failure <= -40&&failure > -60) /* confusion */ | | |
{ | | |
new_draw_info(NDI_UNIQUE, 0,op,"Your diety touches your mind!"); | | |
confuse_player(op,op,99); | | |
} | | |
else if (failure <= -60&&failure> -150) /* paralysis */ | | |
{ | | { |
new_draw_info_format(NDI_UNIQUE, 0,op,"%s requires you to pray NOW.",godname); | | |
new_draw_info(NDI_UNIQUE, 0,op,"You comply, ignoring all else."); | | |
paralyze_player(op,op,99); | | |
} | | |
else if (failure <= -150) /* blast the immediate area */ | | |
{ | | |
new_draw_info_format(NDI_UNIQUE, 0,op,"%s smites you!",godname); | | |
cast_magic_storm(op,get_archetype("god_power"), power); | | |
} | | |
} | | |
| | |
/* Should really just replace all calls to cast_mana_storm to call | | |
* cast_magic_storm directly. | | |
*/ | | |
| | |
void cast_mana_storm(object *op, int lvl) { | | |
object *tmp=get_archetype("loose_magic"); | | |
| | |
cast_magic_storm(op, tmp, lvl); | | |
} | | |
| | |
| | |
void cast_magic_storm(object *op, object *tmp, int lvl) { | | |
| | |
if (!tmp) return; /* error */ | | if (!tmp) return; /* error */ |
tmp->level=SK_level(op); | | tmp->level=op->level; |
tmp->x=op->x;tmp->y=op->y; | | tmp->x=op->x; |
tmp->stats.hp+=lvl/5; /* increase the area of destruction */ | | tmp->y=op->y; |
| | tmp->range+=lvl/5; /* increase the area of destruction */ |
| | tmp->duration+=lvl/5; |
tmp->stats.dam=lvl; /* nasty recoils! */ | | tmp->stats.dam=lvl; /* nasty recoils! */ |
tmp->stats.maxhp=tmp->count; /*??*/ | | tmp->stats.maxhp=tmp->count; /* tract single parent */ |
insert_ob_in_map(tmp,op->map,op,0); | | insert_ob_in_map(tmp,op->map,op,0); |
| | |
} | | } |
| | |
/* TODO: we need a special flag here to aggravate monster. | | |
* Enemy will be deleted in check_enemy() if not friendly->non friendly or visa verse */ | | |
void aggravate_monsters(object *op) { | | |
int i,j; | | |
object *tmp; | | |
| | |
spell_effect(SP_AGGRAVATION, op->x, op->y, op->map, op); | | |
| | |
for (i = 0; i < MAP_WIDTH(op->map); i++) | | |
for (j = 0; j < MAP_HEIGHT(op->map); j++) { | | |
if (out_of_map(op->map, op->x + i , op->y + j)) | | |
continue; | | |
for (tmp = get_map_ob(op->map, op->x + i, op->y + j); | | |
tmp; tmp = tmp->above) | | |
if (QUERY_FLAG(tmp, FLAG_MONSTER)) { | | |
CLEAR_FLAG(tmp, FLAG_SLEEP); | | |
if (!QUERY_FLAG(tmp, FLAG_FRIENDLY)) | | |
tmp->enemy = op; | | |
} | | |
} | | |
} | | |
| | |
| | |
int recharge(object *op) { | | int recharge(object *op, object *caster, object *spell_ob) { |
object *wand; | | object *wand, *tmp; |
| | int ncharges; |
| | |
for(wand = op->inv; wand != NULL; wand = wand->below) | | wand = find_marked_object(op); |
if(wand->type == WAND && QUERY_FLAG(wand, FLAG_APPLIED)) | | if(wand == NULL) { |
break; | | new_draw_info(NDI_UNIQUE, 0, op, "You need to mark the wand you want to recharge."); |
if(wand == NULL) | | |
return 0; | | return 0; |
if(!(random_roll(0, 3, op, PREFER_LOW))) { | | } |
| | if(!(random_roll(0, 3, op, PREFER_HIGH))) { |
new_draw_info_format(NDI_UNIQUE, 0, op, | | new_draw_info_format(NDI_UNIQUE, 0, op, |
"The %s vibrates violently, then explodes!",query_name(wand)); | | "The %s vibrates violently, then explodes!",query_name(wand)); |
play_sound_map(op->map, op->x, op->y, SOUND_OB_EXPLODE); | | play_sound_map(op->map, op->x, op->y, SOUND_OB_EXPLODE); |
spell_effect(SP_DESTRUCTION, op->x, op->y, op->map, op); | | esrv_del_item(op->contr, wand->count); |
remove_ob(wand); | | remove_ob(wand); |
free_object(wand); | | free_object(wand); |
| | tmp = get_archetype("fireball"); |
| | tmp->stats.dam = (spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob)) / 10; |
| | if (!tmp->stats.dam) tmp->stats.dam = 1; |
| | tmp->stats.hp = tmp->stats.dam / 2; |
| | if (tmp->stats.hp < 2) tmp->stats.hp = 2; |
| | tmp->x = op->x; |
| | tmp->y = op->y; |
| | insert_ob_in_map(tmp, op->map, NULL, 0); |
return 1; | | return 1; |
} | | } |
| | |
| | ncharges = (spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob)); |
| | if (wand->inv && wand->inv->level) |
| | ncharges /= wand->inv->level; |
| | else { |
| | new_draw_info_format(NDI_UNIQUE, 0, op, "Your %s is broken.", |
| | query_name(wand)); |
| | return 0; |
| | } |
| | if (!ncharges) ncharges = 1; |
| | |
| | wand->stats.food += ncharges; |
new_draw_info_format(NDI_UNIQUE, 0, op, | | new_draw_info_format(NDI_UNIQUE, 0, op, |
"The %s glows with power.",query_name(wand)); | | "The %s glows with power.",query_name(wand)); |
| | if(wand->arch && QUERY_FLAG(&wand->arch->clone, FLAG_ANIMATE)) { |
wand->stats.food += rndm(1, spells[wand->stats.sp].charges); | | |
if(wand->arch&&QUERY_FLAG(&wand->arch->clone, FLAG_ANIMATE)) | | |
{ | | |
SET_FLAG(wand, FLAG_ANIMATE); | | SET_FLAG(wand, FLAG_ANIMATE); |
wand->speed = wand->arch->clone.speed; | | wand->speed = wand->arch->clone.speed; |
update_ob_speed(wand); | | update_ob_speed(wand); |
| | |
return 1; | | return 1; |
} | | } |
| | |
| | |
/****************************************************************************** | | /****************************************************************************** |
* Start of polymorph related functions. | | * Start of polymorph related functions. |
* | | * |
| | |
} | | } |
| | |
/* Look through the monster. Unapply anything they have applied, | | /* Look through the monster. Unapply anything they have applied, |
* and remove any abilities. | | * and remove any spells. Note that if this is extended |
| | * to players, that would need to get fixed somehow. |
*/ | | */ |
for(tmp = op->inv; tmp != NULL; tmp = next) { | | for(tmp = op->inv; tmp != NULL; tmp = next) { |
next = tmp->below; | | next = tmp->below; |
if(QUERY_FLAG(tmp, FLAG_APPLIED)) | | if(QUERY_FLAG(tmp, FLAG_APPLIED)) |
manual_apply(op,tmp,0); | | manual_apply(op,tmp,0); |
if(tmp->type == ABILITY) { | | if(tmp->type == SPELL) { |
remove_ob(tmp); | | remove_ob(tmp); |
free_object(tmp); | | free_object(tmp); |
} | | } |
| | |
/* polymorph_item - changes an item to another item of similar type. | | /* polymorph_item - changes an item to another item of similar type. |
* who is the caster of spell, op is the object to be changed. | | * who is the caster of spell, op is the object to be changed. |
*/ | | */ |
| | |
void polymorph_item(object *who, object *op) { | | void polymorph_item(object *who, object *op) { |
archetype *at; | | archetype *at; |
int max_value, difficulty, tries=0,choice, charges=op->stats.food,numat=0; | | int max_value, difficulty, tries=0,choice, charges=op->stats.food,numat=0; |
object *new_ob; | | object *new_ob; |
| | |
| | |
/* We try and limit the maximum value of the changd object. */ | | /* We try and limit the maximum value of the changd object. */ |
max_value = op->value * 2; | | max_value = op->value * 2; |
if(max_value > 20000) | | if(max_value > 20000) |
| | |
} | | } |
| | |
/* We don't want rings to keep sustenance/hungry status. There are propably | | /* We don't want rings to keep sustenance/hungry status. There are propably |
other cases too that should be checked. */ | | * other cases too that should be checked. |
| | */ |
if(charges && op->type != RING && op->type != FOOD) | | if(charges && op->type != RING && op->type != FOOD) |
op->stats.food = charges; | | op->stats.food = charges; |
| | |
| | |
if(FABS(op->speed) > 0.001 && !QUERY_FLAG(op, FLAG_ANIMATE)) | | if(FABS(op->speed) > 0.001 && !QUERY_FLAG(op, FLAG_ANIMATE)) |
return; | | return; |
| | |
/* Do some sanity checking here. type=0 is unkown, objects | | /* Do some sanity checking here. type=0 is unknown, objects |
* without archetypes are not good. As are a few other | | * without archetypes are not good. As are a few other |
* cases. | | * cases. |
*/ | | */ |
| | |
} | | } |
| | |
| | |
/* cast_polymorph - object *op has cast it | | /* cast_polymorph - |
| | * object *op is the player/monster |
| | * caster is object that cast it. |
| | * spell_ob is the actually spell object. |
*dir is the direction. | | *dir is the direction. |
* Returns 0 on illegal cast, otherwise 1. | | * Returns 0 on illegal cast, otherwise 1. |
*/ | | */ |
| | |
int cast_polymorph(object *op, int dir) { | | int cast_polymorph(object *op, object *caster, object *spell_ob, int dir) { |
object *tmp, *next; | | object *tmp, *next; |
int range, mflags; | | int range, mflags, maxrange; |
archetype *poly; | | |
mapstruct *m; | | mapstruct *m; |
| | |
if(dir == 0) | | if(dir == 0) |
return 0; | | return 0; |
| | |
poly = find_archetype("polymorph"); | | maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob); |
/* Range is until it hits a wall */ | | for(range = 1;range < maxrange; range++) { |
for(range = 1;;range++) { | | |
sint16 x=op->x+freearr_x[dir]*range,y=op->y+freearr_y[dir]*range; | | sint16 x=op->x+freearr_x[dir]*range,y=op->y+freearr_y[dir]*range; |
object *image; | | object *image; |
| | |
| | |
polymorph(tmp, op); | | polymorph(tmp, op); |
tmp = next; | | tmp = next; |
} | | } |
image = arch_to_object(poly); | | image = arch_to_object(spell_ob->other_arch); |
image->x = x; image->y = y; | | image->x = x; |
/* I am not sure what this below does with the | | image->y = y; |
* range - msw 7/4/1998 | | image->stats.food = 5; |
*/ | | |
image->stats.food += range; | | |
image->speed_left = 0.1; | | image->speed_left = 0.1; |
insert_ob_in_map(image,m,op,0); | | insert_ob_in_map(image,m,op,0); |
} | | } |
| | |
} | | } |
| | |
| | |
| | |
| | /* Create a missile (nonmagic - magic +4). Will either create bolts or arrows |
| | * based on whether a crossbow or bow is equiped. If neither, it defaults to |
| | * arrows. |
| | * Sets the plus based on the casters level. It is also settable with the |
| | * invoke command. If the caster attempts to create missiles with too |
| | * great a plus, the default is used. |
| | * The # of arrows created also goes up with level, so if a 30th level mage |
| | * wants LOTS of arrows, and doesn't care what the plus is he could |
| | * create nonnmagic arrows, or even -1, etc... |
| | */ |
| | |
| | int cast_create_missile(object *op, object *caster,object *spell, int dir, char *stringarg) |
| | { |
| | int missile_plus=0; |
| | char *missile_name; |
| | object *tmp, *missile; |
| | tag_t tag; |
| | |
| | missile_name = "arrow"; |
| | |
| | for (tmp=op->inv; tmp != NULL; tmp=tmp->below) |
| | if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED)) { |
| | missile_name=tmp->race; |
| | } |
| | |
| | missile_plus = spell->stats.dam + SP_level_dam_adjust(caster, spell); |
| | |
| | if (stringarg) { |
| | if (atoi(stringarg) < missile_plus) |
| | missile_plus = atoi(stringarg); |
| | } |
| | if (missile_plus > 4) |
| | missile_plus = 4; |
| | else if (missile_plus < -4) |
| | missile_plus = -4; |
| | |
| | if (find_archetype(missile_name)==NULL) { |
| | LOG(llevDebug, "Cast create_missile: could not find archtype %s\n", |
| | missile_name); |
| | return 0; |
| | } |
| | missile = get_archetype(missile_name); |
| | missile->nrof = spell->duration + SP_level_duration_adjust(caster, spell); |
| | missile->nrof -= 3 * missile_plus; |
| | if (missile->nrof < 1) |
| | missile->nrof=1; |
| | |
| | missile->magic = missile_plus; |
| | /* Can't get any money for these objects */ |
| | missile->value=0; |
| | |
| | SET_FLAG(missile, FLAG_IDENTIFIED); |
| | tag = missile->count; |
| | |
| | if ( ! cast_create_obj (op, caster, missile, dir) && op->type == PLAYER |
| | && ! was_destroyed (missile, tag)) |
| | { |
| | pick_up(op, missile); |
| | } |
| | return 1; |
| | } |
| | |
| | |
/* allows the choice of what sort of food object to make. | | /* allows the choice of what sort of food object to make. |
If stringarg is NULL, it will create food dependent on level --PeterM*/ | | * If stringarg is NULL, it will create food dependent on level --PeterM*/ |
int cast_create_food(object *op,object *caster, int dir, char *stringarg) | | int cast_create_food(object *op,object *caster, object *spell_ob, int dir, char *stringarg) |
{ | | { |
int food_value; | | int food_value; |
archetype *at=NULL; | | archetype *at=NULL; |
object *new_op; | | object *new_op; |
| | |
food_value=SP_PARAMETERS[SP_CREATE_FOOD].bdam | | food_value=spell_ob->stats.food + |
+ 50 * SP_level_dam_adjust(op,caster,SP_CREATE_FOOD); | | + 50 * SP_level_duration_adjust(caster,spell_ob); |
| | |
if(stringarg) { | | if(stringarg) { |
at=find_archetype(stringarg); | | at=find_archetype(stringarg); |
| | |
| | |
if(!stringarg) { | | if(!stringarg) { |
archetype *at_tmp; | | archetype *at_tmp; |
| | |
/* We try to find the archetype with the maximum food value. | | /* We try to find the archetype with the maximum food value. |
* This removes the dependancy of hard coded food values in this | | * This removes the dependancy of hard coded food values in this |
* function, and addition of new food types is automatically added. | | * function, and addition of new food types is automatically added. |
| | |
new_op = get_object(); | | new_op = get_object(); |
copy_object(&at->clone, new_op); | | copy_object(&at->clone, new_op); |
new_op->nrof = food_value; | | new_op->nrof = food_value; |
/* lighten the food a little with increasing level. */ | | |
if(food_value > 1) new_op->weight = (int) (new_op->weight* 2.0 / (2.0 + food_value)); | | |
new_op->value = 0; | | new_op->value = 0; |
SET_FLAG(new_op, FLAG_STARTEQUIP); | | |
if (new_op->nrof<1) new_op->nrof = 1; | | if (new_op->nrof<1) new_op->nrof = 1; |
| | |
cast_create_obj(op, caster,new_op, dir); | | cast_create_obj(op, caster,new_op, dir); |
return 1; | | return 1; |
} | | } |
| | |
| | int probe(object *op, object *caster, object *spell_ob, int dir) { |
int cast_speedball(object *op, int dir, int type) { | | int r, mflags, maxrange; |
object *spb; | | |
mapstruct *m; | | |
sint16 x, y; | | |
| | |
x = op->x+freearr_x[dir]; | | |
y = op->y+freearr_y[dir]; | | |
m = op->map; | | |
| | |
if (get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) | | |
return 0; | | |
| | |
spb = clone_arch(SPEEDBALL); | | |
spb->x = x; | | |
spb->y = y; | | |
| | |
spb->speed_left= -0.1; | | |
if(type==SP_LARGE_SPEEDBALL) | | |
spb->stats.dam=30; | | |
| | |
insert_ob_in_map(spb,m,op,0); | | |
return 1; | | |
} | | |
| | |
int probe(object *op, int dir) { | | |
int r, mflags; | | |
object *tmp; | | object *tmp; |
mapstruct *m; | | mapstruct *m; |
| | |
| | |
examine_monster(op,op); | | examine_monster(op,op); |
return 1; | | return 1; |
} | | } |
for(r=1;;r++) { | | maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob); |
| | for(r=1;r < maxrange; r++) { |
sint16 x=op->x+r*freearr_x[dir],y=op->y+r*freearr_y[dir]; | | sint16 x=op->x+r*freearr_x[dir],y=op->y+r*freearr_y[dir]; |
| | |
m = op->map; | | m = op->map; |
| | |
return 1; | | return 1; |
} | | } |
| | |
| | |
| | /* This checks to see if 'pl' is invisible to 'mon'. |
| | * does race check, undead check, etc |
| | * Returns TRUE if mon can't see pl, false |
| | * otherwise. This doesn't check range, walls, etc. It |
| | * only checks the racial adjustments, and in fact that |
| | * pl is invisible. |
| | */ |
| | int makes_invisible_to(object *pl, object *mon) |
| | { |
| | |
| | if (!pl->invisible) return 0; |
| | if (pl->type == PLAYER ) { |
| | /* If race isn't set, then invisible unless it is undead */ |
| | if (!pl->contr->invis_race) { |
| | if (QUERY_FLAG(mon, FLAG_UNDEAD)) return 0; |
| | return 1; |
| | } |
| | /* invis_race is set if we get here */ |
| | if (!strcmp(pl->contr->invis_race, "undead") && is_true_undead(mon)) |
| | return 1; |
| | /* No race, can't be invisible to it */ |
| | if (!mon->race) return 0; |
| | if (strstr(mon->race, pl->contr->invis_race)) return 1; |
| | /* Nothing matched above, return 0 */ |
| | return 0; |
| | } else { |
| | /* monsters are invisible to everything */ |
| | return 1; |
| | } |
| | } |
| | |
/* Makes the player or character invisible. | | /* Makes the player or character invisible. |
* Note the spells to 'stack', but perhaps in odd ways. | | * Note the spells to 'stack', but perhaps in odd ways. |
* the duration for all is cumulative. | | * the duration for all is cumulative. |
| | |
* lose the improved part of it, and the above statement about undead/ | | * lose the improved part of it, and the above statement about undead/ |
* normal applies. | | * normal applies. |
*/ | | */ |
int cast_invisible(object *op, object *caster, int spell_type) { | | int cast_invisible(object *op, object *caster, object *spell_ob) { |
object *tmp; | | object *tmp; |
| | |
if(op->invisible>1000) { | | if(op->invisible>1000) { |
| | |
/* Remove the switch with 90% duplicate code - just handle the differences with | | /* Remove the switch with 90% duplicate code - just handle the differences with |
* and if statement or two. | | * and if statement or two. |
*/ | | */ |
CLEAR_FLAG(op, FLAG_INVIS_UNDEAD); | | op->invisible += spell_ob->duration + SP_level_duration_adjust(caster, spell_ob); |
op->invisible+=SP_PARAMETERS[spell_type].bdur; /* set the base */ | | /* max duration */ |
op->invisible+=SP_PARAMETERS[spell_type].ldam * | | if(op->invisible>1000) op->invisible = 1000; |
SP_level_strength_adjust(op,caster,spell_type); /* set the level bonus */ | | |
| | |
if (spell_type == SP_INVIS_UNDEAD) { | | if (op->type == PLAYER) { |
SET_FLAG(op, FLAG_INVIS_UNDEAD); | | if (op->contr->invis_race) FREE_AND_CLEAR_STR(op->contr->invis_race); |
if(op->type==PLAYER) | | if (spell_ob->race) |
op->contr->tmp_invis=1; | | op->contr->invis_race = add_refcount(spell_ob->race); |
} else if (spell_type == SP_INVIS) { | | if (QUERY_FLAG(spell_ob, FLAG_MAKE_INVIS)) |
if(op->type==PLAYER) | | op->contr->tmp_invis=0; |
| | else |
op->contr->tmp_invis=1; | | op->contr->tmp_invis=1; |
} | | |
/* Nothing special to be done for improved invis */ | | |
| | |
/* If we're to limit extending the duration above, we should | | |
* make that a maximum for newly cast spells. | | |
*/ | | |
if(op->invisible>1000) op->invisible = 1000; | | |
| | |
| | op->contr->hidden = 0; |
| | } |
| | if (makes_invisible_to(op, op)) |
new_draw_info(NDI_UNIQUE, 0,op,"You can't see your hands!"); | | new_draw_info(NDI_UNIQUE, 0,op,"You can't see your hands!"); |
| | else |
| | new_draw_info(NDI_UNIQUE, 0,op,"You feel more transparent!"); |
| | |
update_object(op,UP_OBJ_FACE); | | update_object(op,UP_OBJ_FACE); |
| | |
/* Only search the active objects - only these should actually do | | /* Only search the active objects - only these should actually do |
| | |
return 1; | | return 1; |
} | | } |
| | |
| | /* earth to dust spell. Basically destroys earthwalsl in the area. |
int | | */ |
cast_earth2dust(object *op,object *caster) { | | int cast_earth_to_dust(object *op,object *caster, object *spell_ob) { |
object *tmp, *next; | | object *tmp, *next; |
int strength,i,j; | | int range,i,j, mflags; |
| | sint16 sx, sy; |
| | mapstruct *m; |
| | |
if(op->type!=PLAYER) | | if(op->type!=PLAYER) |
return 0; | | return 0; |
strength=SP_PARAMETERS[SP_EARTH_DUST].bdur + SP_level_strength_adjust(op,caster,SP_EARTH_DUST); | | |
strength=(strength>15)?15:strength; | | range=spell_ob->range + SP_level_range_adjust(caster, spell_ob); |
for(i= -strength;i<strength;i++) | | |
for(j= -strength;j<strength;j++) { | | for(i= -range;i<range;i++) |
if(out_of_map(op->map,op->x+i,op->y+j)) | | for(j= -range;j<range;j++) { |
continue; | | sx = op->x + i; |
for(tmp=get_map_ob(op->map,op->x+i,op->y+j);tmp!=NULL;tmp=next) { | | sy = op->y + j; |
| | m = op->map; |
| | mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); |
| | |
| | if (mflags & P_OUT_OF_MAP) continue; |
| | /* If the space doesn't block, no wall here to remove */ |
| | if (mflags & P_BLOCKED) { |
| | for(tmp=get_map_ob(m, sx, sy);tmp!=NULL;tmp=next) { |
next=tmp->above; | | next=tmp->above; |
if(tmp&&QUERY_FLAG(tmp, FLAG_TEAR_DOWN)) | | if(tmp&&QUERY_FLAG(tmp, FLAG_TEAR_DOWN)) |
hit_player(tmp,9998,op,AT_PHYSICAL); | | hit_player(tmp,9998,op,AT_PHYSICAL); |
} | | } |
} | | } |
| | } |
return 1; | | return 1; |
} | | } |
| | |
/* puts a 'WORD_OF_RECALL_' object in player | | |
* modified to work faster for higher level casters -- DAMN | | void execute_word_of_recall(object *op) { |
| | object *wor=op; |
| | while(op!=NULL && op->type!=PLAYER) |
| | op=op->env; |
| | |
| | if(op!=NULL) { |
| | if (get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL) & P_NO_CLERIC) |
| | new_draw_info(NDI_UNIQUE, 0,op,"You feel something fizzle inside you."); |
| | else |
| | enter_exit(op,wor); |
| | } |
| | remove_ob(wor); |
| | free_object(wor); |
| | } |
| | |
| | /* Word of recall causes the player to return 'home'. |
| | * we put a force into the player object, so that there is a |
| | * time delay effect. |
*/ | | */ |
int cast_wor(object *op, object *caster) { | | int cast_word_of_recall(object *op, object *caster, object *spell_ob) { |
object *dummy; | | object *dummy; |
| | int time; |
| | |
if(op->type!=PLAYER) | | if(op->type!=PLAYER) |
return 0; | | return 0; |
| | |
if(get_map_flags(op->map,NULL, op->x,op->y, NULL, NULL) & P_NO_CLERIC) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your spell."); | | |
return 0; | | |
} | | |
dummy=get_archetype("force"); | | dummy=get_archetype("force"); |
if(dummy == NULL){ | | if(dummy == NULL){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed!\n"); | | LOG(llevError,"cast_word_of_recall: get_archetype(force) failed!\n"); |
return 0; | | return 0; |
} | | } |
if(op->owner) op=op->owner; /* better insert the spell in the player */ | | time = spell_ob->duration - SP_level_duration_adjust(caster, spell_ob); |
dummy->speed = 0.002 * ((float)(SP_PARAMETERS[SP_WOR].bdur | | if (time <1 ) time=1; |
+ SP_level_strength_adjust(op,caster,SP_WOR))); | | |
| | /* value of speed really doesn't make much difference, as long as it is |
| | * positive. Lower value may be useful so that the problem doesn't |
| | * do anything really odd if it say a -1000 or something. |
| | */ |
| | dummy->speed = 0.002; |
update_ob_speed(dummy); | | update_ob_speed(dummy); |
dummy->speed_left= -1; | | dummy->speed_left = -dummy->speed * time; |
dummy->type=WORD_OF_RECALL; | | dummy->type=SPELL_EFFECT; |
| | dummy->subtype = SP_WORD_OF_RECALL; |
| | |
/* If we could take advantage of enter_player_savebed() here, it would be | | /* If we could take advantage of enter_player_savebed() here, it would be |
* nice, but until the map load fails, we can't. | | * nice, but until the map load fails, we can't. |
*/ | | */ |
| | |
| | |
(void) insert_ob_in_ob(dummy,op); | | (void) insert_ob_in_ob(dummy,op); |
new_draw_info(NDI_UNIQUE, 0,op,"You feel a force starting to build up inside you."); | | new_draw_info(NDI_UNIQUE, 0,op,"You feel a force starting to build up inside you."); |
#if 0 | | |
LOG(llevDebug,"Word of Recall for %s in %f ticks.\n", op->name, | | |
((-dummy->speed_left)/(dummy->speed==0?0.0001:dummy->speed))); | | |
LOG(llevDebug,"Word of Recall for player level %d, caster level %d: 0.002 * %d + %d\n", | | |
SK_level(op), SK_level(caster), SP_PARAMETERS[SP_WOR].bdur, SP_level_strength_adjust(op,caster,SP_WOR)); | | |
#endif | | |
return 1; | | return 1; |
} | | } |
| | |
int cast_wow(object *op, int dir, int ability, SpellTypeFrom item) { | | /* cast_wonder |
int sp; | | * wonder is really just a spell that will likely cast another |
| | * spell. |
| | */ |
| | int cast_wonder(object *op, object *caster, int dir, object *spell_ob) { |
| | object *newspell; |
| | |
if(!rndm(0, 3)) | | if(!rndm(0, 3)) |
return cast_cone(op,op,0,10,SP_WOW,spellarch[SP_WOW],0); | | return cast_cone(op,caster,dir, spell_ob); |
do | | |
sp=rndm(0, NROFREALSPELLS-1); | | if (spell_ob->randomitems) { |
while (!spells[sp].books); | | newspell = generate_treasure(spell_ob->randomitems, caster->level); |
return cast_spell(op,op,dir,sp,ability,item,NULL); | | if (!newspell) { |
| | LOG(llevError,"cast_wonder: Unable to get a spell!\n"); |
| | return 0; |
| | } |
| | if (newspell->type != SPELL) { |
| | LOG(llevError,"cast_wonder: spell returned is not a spell (%d, %s)!\n", |
| | newspell->type, newspell->name); |
| | return 0; |
| | } |
| | /* Prevent inifinit recursion */ |
| | if (newspell->subtype == SP_WONDER) { |
| | LOG(llevError,"cast_wonder: spell returned is another wonder spell!"); |
| | return 0; |
| | } |
| | return cast_spell(op,caster,dir,newspell, NULL); |
| | } |
| | return 1; |
} | | } |
| | |
| | |
int perceive_self(object *op) { | | int perceive_self(object *op) { |
char *cp=describe_item(op, op), buf[MAX_BUF]; | | char *cp=describe_item(op, op), buf[MAX_BUF]; |
archetype *at=find_archetype("depletion"); | | archetype *at=find_archetype("depletion"); |
| | |
if (tmp!=NULL) { | | if (tmp!=NULL) { |
for (i=0; i<7; i++) { | | for (i=0; i<7; i++) { |
if (get_attr_value(&tmp->stats, i)<0) { | | if (get_attr_value(&tmp->stats, i)<0) { |
sprintf(buf,"Your %s is depleted by %d", statname[i], | | new_draw_info_format(NDI_UNIQUE, 0,op, |
| | "Your %s is depleted by %d", statname[i], |
-(get_attr_value(&tmp->stats,i))); | | -(get_attr_value(&tmp->stats,i))); |
new_draw_info(NDI_UNIQUE, 0,op, buf); | | |
} | | } |
} | | } |
} | | } |
} | | } |
| | |
if (is_dragon_pl(op)) | | if (is_dragon_pl(op)) { |
{ | | |
/* now grab the 'dragon_ability'-force from the player's inventory */ | | /* now grab the 'dragon_ability'-force from the player's inventory */ |
for (tmp = op->inv; tmp != NULL; tmp = tmp->below) { | | for (tmp = op->inv; tmp != NULL; tmp = tmp->below) { |
if (tmp->type == FORCE) { | | if (tmp->type == FORCE && !strcmp(tmp->arch->name, "dragon_ability_force")) { |
if (strcmp(tmp->arch->name, "dragon_ability_force") == 0) { | | |
if(tmp->stats.exp == 0) { | | if(tmp->stats.exp == 0) { |
sprintf(buf, "Your metabolism isn't focused on anything."); | | sprintf(buf, "Your metabolism isn't focused on anything."); |
} else { | | } else { |
| | |
} | | } |
} | | } |
} | | } |
} | | |
| | |
return 1; | | return 1; |
} | | } |
| | |
/* int cast_create_town_portal (object *op, object *caster, int dir) | | /* int cast_create_town_portal (object *op, object *caster, int dir) |
* | | * |
* This function cast the spell of town portal for op | | * This function cast the spell of town portal for op |
| | * |
* The spell operates in two passes. During the first one a place | | * The spell operates in two passes. During the first one a place |
* is marked as a destination for the portal. During the second one, | | * is marked as a destination for the portal. During the second one, |
* 2 portals are created, one in the position the player cast it and | | * 2 portals are created, one in the position the player cast it and |
| | |
* | | * |
* Code by Tchize (david.delbecq@usa.net) | | * Code by Tchize (david.delbecq@usa.net) |
*/ | | */ |
int cast_create_town_portal (object *op, object *caster, int dir) | | int cast_create_town_portal (object *op, object *caster, object *spell, int dir) |
{ | | { |
object *dummy, *force, *old_force, *tmp; | | object *dummy, *force, *old_force, *tmp; |
archetype *perm_portal; | | archetype *perm_portal; |
| | |
mapstruct *exitmap; | | mapstruct *exitmap; |
int op_level; | | int op_level; |
| | |
/* The first thing to do is to check if we have a marked destination | | |
* dummy is used to make a check inventory for the force | | |
*/ | | |
| | |
/* Check to see if the map the player is currently on is a per player unique | | /* Check to see if the map the player is currently on is a per player unique |
* map. This can be determined in that per player unique maps have the | | * map. This can be determined in that per player unique maps have the |
| | |
return 0; | | return 0; |
} | | } |
| | |
dummy=get_archetype("town_portal"); | | /* The first thing to do is to check if we have a marked destination |
| | * dummy is used to make a check inventory for the force |
| | */ |
| | dummy=arch_to_object(spell->other_arch); |
if(dummy == NULL){ | | if(dummy == NULL){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (force in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (force in cast_create_town_portal for %s!\n",op->name); |
| | |
* - To create the portal with the name of the player+destination map | | * - To create the portal with the name of the player+destination map |
* - set the owner of the town portal | | * - set the owner of the town portal |
* - To mark the position of the portal in the player's inventory | | * - To mark the position of the portal in the player's inventory |
* for easier destruction. ² | | * for easier destruction. |
* | | * |
* The mark works has follow: | | * The mark works has follow: |
* slaying: Existing town portal | | * slaying: Existing town portal |
| | |
*/ | | */ |
| | |
/* First step: killing existing town portals */ | | /* First step: killing existing town portals */ |
dummy=get_archetype("town_portal_active"); | | dummy=get_archetype(spell->race); |
if(dummy == NULL){ | | if(dummy == NULL){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); |
return 0; | | return 0; |
} | | } |
perm_portal = find_archetype ("town_portal_magic"); | | perm_portal = find_archetype (spell->slaying); |
| | |
/* To kill a town portal, we go trough the player's inventory, | | /* To kill a town portal, we go trough the player's inventory, |
* for each marked portal in player's inventory, | | * for each marked portal in player's inventory, |
| | |
exitx=EXIT_X(old_force); | | exitx=EXIT_X(old_force); |
exity=EXIT_Y(old_force); | | exity=EXIT_Y(old_force); |
LOG (llevDebug,"Trying to kill a portal in %s (%d,%d)\n",old_force->race,exitx,exity); | | LOG (llevDebug,"Trying to kill a portal in %s (%d,%d)\n",old_force->race,exitx,exity); |
| | |
if (!strncmp(old_force->race, settings.localdir, strlen(settings.localdir))) | | if (!strncmp(old_force->race, settings.localdir, strlen(settings.localdir))) |
exitmap = ready_map_name(old_force->race, MAP_PLAYER_UNIQUE); | | exitmap = ready_map_name(old_force->race, MAP_PLAYER_UNIQUE); |
else exitmap = ready_map_name(old_force->race, 0); | | else exitmap = ready_map_name(old_force->race, 0); |
| | |
* 'force' is the destination of the town portal, which we got | | * 'force' is the destination of the town portal, which we got |
* from the players inventory above. | | * from the players inventory above. |
*/ | | */ |
| | |
/* Ensure exit map is loaded*/ | | /* Ensure exit map is loaded*/ |
if (!strncmp(force->name, settings.localdir, strlen(settings.localdir))) | | if (!strncmp(force->name, settings.localdir, strlen(settings.localdir))) |
exitmap = ready_map_name(force->name, MAP_PLAYER_UNIQUE); | | exitmap = ready_map_name(force->name, MAP_PLAYER_UNIQUE); |
| | |
return 1; | | return 1; |
} | | } |
| | |
op_level =SK_level (op); | | op_level = caster_level(caster, spell); |
if (op_level<15) | | if (op_level<15) |
snprintf (portal_message,1024,"\nAir moves around you and\na huge smell of ammoniac\nrounds you as you pass\nthrough %s's portal\nPouah!\n",op->name); | | snprintf (portal_message,1024,"\nAir moves around you and\na huge smell of ammoniac\nrounds you as you pass\nthrough %s's portal\nPouah!\n",op->name); |
else if (op_level<30) | | else if (op_level<30) |
| | |
*/ | | */ |
| | |
snprintf (portal_name,1024,"%s's portal to %s",op->name,force->name); | | snprintf (portal_name,1024,"%s's portal to %s",op->name,force->name); |
dummy=get_archetype("town_portal_magic"); /*The portal*/ | | dummy=get_archetype(spell->slaying); /*The portal*/ |
if(dummy == NULL) { | | if(dummy == NULL) { |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name); |
| | |
* object, so on future castings, we can know that he has an active | | * object, so on future castings, we can know that he has an active |
* town portal. | | * town portal. |
*/ | | */ |
tmp=get_archetype("town_portal_active"); | | tmp=get_archetype(spell->race); |
if(tmp == NULL){ | | if(tmp == NULL){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); |
| | |
* where this portal goes to. | | * where this portal goes to. |
*/ | | */ |
snprintf (portal_name,1024,"%s's portal to %s",op->name,op->map->path); | | snprintf (portal_name,1024,"%s's portal to %s",op->name,op->map->path); |
dummy=get_archetype ("town_portal_magic"); /*The portal*/ | | dummy=get_archetype (spell->slaying); /*The portal*/ |
if(dummy == NULL) { | | if(dummy == NULL) { |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n",op->name); |
| | |
/* Now we create another town portal marker that | | /* Now we create another town portal marker that |
* points back to the one we just made | | * points back to the one we just made |
*/ | | */ |
tmp=get_archetype("town_portal_active"); | | tmp=get_archetype(spell->race); |
if(tmp == NULL){ | | if(tmp == NULL){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); | | LOG(llevError,"get_object failed (force) in cast_create_town_portal for %s!\n",op->name); |
| | |
return 1; | | return 1; |
} | | } |
| | |
int cast_destruction(object *op, object *caster, int dam, int attacktype) { | | |
int i,j; | | |
int r; /* peterm: added to make area of effect level dep. */ | | |
object *tmp; | | |
if(op->type!=PLAYER) | | |
return 0; | | |
r=5 + SP_level_strength_adjust(op,caster,SP_DESTRUCTION); | | |
dam+=SP_level_dam_adjust(op,caster,SP_DESTRUCTION); | | |
for(i= -r;i<r;i++) | | |
for(j= -r;j<r;j++) { | | |
if(out_of_map(op->map,op->x+i,op->y+j)) | | |
continue; | | |
tmp=get_map_ob(op->map,op->x+i,op->y+j); | | |
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE)||tmp->type==PLAYER)) | | |
tmp=tmp->above; | | |
if(tmp==NULL) | | |
continue; | | |
hit_player(tmp,dam,op,attacktype); | | |
} | | |
return 1; | | |
} | | |
| | |
int magic_wall(object *op,object *caster,int dir,int spell_type) { | | /* This creates magic walls. Really, it can create most any object, |
| | * within some reason. |
| | */ |
| | |
| | int magic_wall(object *op,object *caster,int dir,object *spell_ob) { |
object *tmp, *tmp2; | | object *tmp, *tmp2; |
int i,posblocked=0,negblocked=0; | | int i,posblocked,negblocked, maxrange; |
sint16 x, y; | | sint16 x, y; |
mapstruct *m; | | mapstruct *m; |
| | char *name; |
| | archetype *at; |
| | |
if(!dir) { | | if(!dir) { |
new_draw_info(NDI_UNIQUE, 0,op,"In what direction?"); | | dir=op->facing; |
return 0; | | x = op->x; |
} | | y = op->y; |
m = op->map; | | } else { |
x = op->x+freearr_x[dir]; | | x = op->x+freearr_x[dir]; |
y = op->y+freearr_y[dir]; | | y = op->y+freearr_y[dir]; |
| | } |
| | m = op->map; |
| | |
if (get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) { | | if ((QUERY_FLAG(spell_ob, FLAG_NO_PASS) || x != op->x || y != op->y) && |
| | get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) { |
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way."); | | new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way."); |
return 0; | | return 0; |
} | | } |
switch(spell_type) { | | if (spell_ob->other_arch) { |
case SP_EARTH_WALL: | | tmp = arch_to_object(spell_ob->other_arch); |
tmp=get_archetype("earthwall"); | | } else if (spell_ob->race) { |
for (i=0; i<NROFATTACKS; i++) | | char buf1[MAX_BUF]; |
tmp->resist[i]=0; | | |
tmp->stats.hp = SP_PARAMETERS[spell_type].bdur + | | |
10* SP_level_strength_adjust(op,caster,spell_type); | | |
/* More solid, since they can be torn down */ | | |
tmp->stats.maxhp = tmp->stats.hp; | | |
break; | | |
| | |
case SP_FIRE_WALL: | | |
tmp=get_archetype("firebreath"); | | |
tmp->attacktype |= AT_MAGIC; | | |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur | | |
+ 5*SP_level_strength_adjust(op,caster,spell_type); | | |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam | | |
+SP_level_dam_adjust(op,caster,spell_type); | | |
tmp->stats.food=1; /* so it doesn't propagate */ | | |
SET_FLAG(tmp, FLAG_WALK_ON); | | |
SET_FLAG(tmp, FLAG_FLY_ON); | | |
set_owner(tmp,op); | | |
break; | | |
| | |
case SP_FROST_WALL: | | sprintf(buf1,spell_ob->race,dir); |
tmp=get_archetype("icestorm"); | | at = find_archetype(buf1); |
tmp->attacktype |= AT_MAGIC; | | if (!at) { |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur | | LOG(llevError, "summon_wall: Unable to find archetype %s\n", buf1); |
+ 5*SP_level_strength_adjust(op,caster,spell_type); | | new_draw_info(NDI_UNIQUE, 0,op,"This spell is broken."); |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam | | return 0; |
+SP_level_dam_adjust(op,caster,spell_type); | | } |
| | tmp = arch_to_object(at); |
tmp->stats.food=1; /* so it doesn't propagate */ | | } else { |
SET_FLAG(tmp, FLAG_WALK_ON); | | LOG(llevError,"magic_wall: spell %s lacks other_arch\n", |
SET_FLAG(tmp, FLAG_FLY_ON); | | spell_ob->name); |
set_owner(tmp,op); | | return 0; |
break; | | } |
| | |
case SP_WALL_OF_THORNS: | | if (tmp->type == SPELL_EFFECT) { |
tmp=get_archetype("thorns"); | | tmp->attacktype = spell_ob->attacktype; |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur | | tmp->duration = spell_ob->duration + |
+ 3*SP_level_strength_adjust(op,caster,spell_type); | | SP_level_duration_adjust(caster, spell_ob); |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam | | tmp->stats.dam = spell_ob->stats.dam + |
+SP_level_dam_adjust(op,caster,spell_type); | | SP_level_dam_adjust(caster, spell_ob); |
SET_FLAG(tmp, FLAG_WALK_ON); | | tmp->range = 0; |
| | } else if (QUERY_FLAG(tmp, FLAG_ALIVE)) { |
| | tmp->stats.hp = spell_ob->duration + |
| | SP_level_duration_adjust(caster, spell_ob); |
| | tmp->stats.maxhp = tmp->stats.hp; |
set_owner(tmp,op); | | set_owner(tmp,op); |
break; | | set_spell_skill(op, caster, spell_ob, tmp); |
| | } |
case SP_CHAOS_POOL: | | if (QUERY_FLAG(spell_ob, FLAG_IS_USED_UP) || QUERY_FLAG(tmp, FLAG_IS_USED_UP)) { |
tmp=get_archetype("color_spray"); | | tmp->stats.food = spell_ob->duration + |
tmp->attacktype|=AT_MAGIC; | | SP_level_duration_adjust(caster, spell_ob); |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur | | SET_FLAG(tmp, FLAG_IS_USED_UP); |
+ 5*SP_level_strength_adjust(op,caster,spell_type); | | } |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam | | if (QUERY_FLAG(spell_ob, FLAG_TEAR_DOWN)) { |
+SP_level_dam_adjust(op,caster,spell_type); | | tmp->stats.hp = spell_ob->stats.dam + SP_level_dam_adjust(caster, spell_ob); |
| | tmp->stats.maxhp = tmp->stats.hp; |
tmp->stats.food=1; /* so the color spray object won't propagate */ | | SET_FLAG(tmp, FLAG_TEAR_DOWN); |
SET_FLAG(tmp, FLAG_WALK_ON); | | SET_FLAG(tmp, FLAG_ALIVE); |
SET_FLAG(tmp, FLAG_FLY_ON); | | } |
set_owner(tmp,op); | | |
break; | | |
| | |
case SP_DARKNESS: | | |
tmp=get_archetype("darkness"); | | |
tmp->stats.food = SP_PARAMETERS[SP_DARKNESS].bdur | | |
+ SP_level_strength_adjust (op, caster, SP_DARKNESS); | | |
break; | | |
| | |
case SP_COUNTERWALL: | | /* This can't really hurt - if the object doesn't kill anything, |
tmp=get_archetype("counterspell"); | | * these fields just won't be used. |
tmp->attacktype|=AT_MAGIC; | | */ |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur | | |
+ 5*SP_level_strength_adjust(op,caster,spell_type); | | |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam | | |
+SP_level_dam_adjust(op,caster,spell_type); | | |
tmp->stats.food=1; | | |
tmp->level=SK_level(op); | | |
SET_FLAG(tmp, FLAG_WALK_ON); | | |
SET_FLAG(tmp, FLAG_FLY_ON); | | |
set_owner(tmp,op); | | set_owner(tmp,op); |
break; | | set_spell_skill(op, caster, spell_ob, tmp); |
| | |
default: | | |
LOG(llevError,"Unimplemented magic_wall spell: %d\n",spell_type); | | |
return 0; | | |
} | | |
tmp->x = x; | | tmp->x = x; |
tmp->y = y; | | tmp->y = y; |
| | tmp->level = caster_level(caster, spell_ob) / 2; |
| | |
| | name = tmp->name; |
if ((tmp = insert_ob_in_map (tmp, m, op,0)) == NULL) { | | if ((tmp = insert_ob_in_map (tmp, m, op,0)) == NULL) { |
new_draw_info(NDI_UNIQUE, 0,op,"Something destroys your wall"); | | new_draw_info_format(NDI_UNIQUE, 0,op,"Something destroys your %s", name); |
return 0; | | return 0; |
} | | } |
| | |
/* This code causes the wall to extend to a distance of 5 in | | /* This code causes the wall to extend some distance in |
* each direction, or until an obstruction is encountered. | | * each direction, or until an obstruction is encountered. |
* posblocked and negblocked help determine how far the | | * posblocked and negblocked help determine how far the |
* created wall can extend, it won't go extend through | | * created wall can extend, it won't go extend through |
* blocked spaces. | | * blocked spaces. |
*/ | | */ |
| | maxrange = spell_ob->range + SP_level_range_adjust(caster, spell_ob); |
| | posblocked=0; |
| | negblocked=0; |
| | |
for(i=1;i<5;i++) { | | for(i=1; i<=maxrange; i++) { |
int dir2; | | int dir2; |
| | |
dir2 = (dir<4)?(dir+2):dir-2; | | dir2 = (dir<4)?(dir+2):dir-2; |
| | |
if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) && !posblocked) { | | if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) && !posblocked) { |
tmp2 = get_object(); | | tmp2 = get_object(); |
copy_object(tmp,tmp2); | | copy_object(tmp,tmp2); |
tmp2->x = x; tmp2->y = y; | | tmp2->x = x; |
| | tmp2->y = y; |
insert_ob_in_map(tmp2,m,op,0); | | insert_ob_in_map(tmp2,m,op,0); |
} else posblocked=1; | | } else posblocked=1; |
| | |
| | |
if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) && !negblocked) { | | if(!(get_map_flags(m, &m, x, y, &x, &y) & (P_OUT_OF_MAP | P_BLOCKED)) && !negblocked) { |
tmp2 = get_object(); | | tmp2 = get_object(); |
copy_object(tmp,tmp2); | | copy_object(tmp,tmp2); |
tmp2->x = x; tmp2->y = y; | | tmp2->x = x; |
| | tmp2->y = y; |
insert_ob_in_map(tmp2,m,op,0); | | insert_ob_in_map(tmp2,m,op,0); |
} else negblocked=1; | | } else negblocked=1; |
| | |
} | | } |
| | |
if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW)) | | if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW)) |
update_all_los(op->map, op->x, op->y); | | update_all_los(op->map, op->x, op->y); |
| | |
if(op->type!=PLAYER) | | |
SET_FLAG(op, FLAG_SCARED); /* We don't want them to walk through the wall! */ | | |
return 1; | | |
} | | |
| | |
/* cast_light() - I wanted this to be able to blind opponents who stand | | |
* adjacent to the caster, so I couldnt use magic_wall(). -b.t. | | |
*/ | | |
| | |
int cast_light(object *op,object *caster,int dir) { | | |
object *target=NULL,*tmp=NULL; | | |
sint16 x,y; | | |
int dam, mflags; | | |
mapstruct *m; | | |
| | |
dam = SP_PARAMETERS[SP_LIGHT].bdam + SP_level_dam_adjust(op,caster,SP_LIGHT); | | |
| | |
if(!dir) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"In what direction?"); | | |
return 0; | | |
} | | |
| | |
x=op->x+freearr_x[dir]; | | |
y=op->y+freearr_y[dir]; | | |
m = op->map; | | |
| | |
mflags = get_map_flags(m, &m, x, y, &x, &y); | | |
| | |
if (mflags & P_OUT_OF_MAP) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"Nothing is there."); | | |
return 0; | | |
} | | |
| | |
if (mflags & P_IS_ALIVE) { | | |
for(target=get_map_ob(m,x,y);target;target=target->above) | | |
if(QUERY_FLAG(target,FLAG_MONSTER)) { | | |
/* oky doky. got a target monster. Lets make a blinding attack */ | | |
if(target->head) target = target->head; | | |
(void) hit_player(target,dam,op,(AT_BLIND|AT_MAGIC)); | | |
return 1; /* one success only! */ | | |
} | | |
} | | |
| | |
/* no live target, perhaps a wall is in the way? */ | | |
if(mflags & P_BLOCKED) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way."); | | |
return 0; | | |
} | | |
| | |
/* ok, looks groovy to just insert a new light on the map */ | | |
tmp=get_archetype("light"); | | |
if(!tmp) { | | |
LOG(llevError,"Error: spell arch for cast_light() missing.\n"); | | |
return 0; | | |
} | | |
tmp->speed = 0.000001 * (SP_PARAMETERS[SP_LIGHT].bdur | | |
- (10*SP_level_strength_adjust(op,caster,SP_LIGHT))); | | |
if (tmp->speed < MIN_ACTIVE_SPEED) tmp->speed = MIN_ACTIVE_SPEED; | | |
tmp->glow_radius=dam; | | |
tmp->x=x; | | |
tmp->y=y; | | |
insert_ob_in_map(tmp,m,op,0); | | |
return 1; | | return 1; |
} | | } |
| | |
int dimension_door(object *op,int dir) { | | int dimension_door(object *op,object *caster, object *spob, int dir) { |
int dist, maxdist, mflags; | | int dist, maxdist, mflags; |
| | |
if(op->type!=PLAYER) | | if(op->type!=PLAYER) |
| | |
/* Given the new outdoor maps, can't let players dimension door for | | /* Given the new outdoor maps, can't let players dimension door for |
* ever, so put limits in. | | * ever, so put limits in. |
*/ | | */ |
maxdist = SP_PARAMETERS[SP_D_DOOR].bdur + | | maxdist = spob->range + |
SP_level_strength_adjust(op, op, SP_D_DOOR); | | SP_level_range_adjust(caster, spob); |
| | |
if(op->contr->count) { | | if(op->contr->count) { |
if (op->contr->count > maxdist) { | | if (op->contr->count > maxdist) { |
| | |
} | | } |
| | |
| | |
| | /* cast_heal: Heals something. |
int | | * op is the caster. |
cast_heal(object *op,int dir,int spell_type) { | | * dir is the direction he is casting it in. |
| | * spell is the spell object. |
| | */ |
| | int cast_heal(object *op,object *caster, object *spell, int dir) { |
object *tmp; | | object *tmp; |
archetype *at; | | archetype *at; |
object *poison; | | object *poison; |
| | |
tmp = find_target_for_friendly_spell(op,dir); | | tmp = find_target_for_friendly_spell(op,dir); |
| | |
if(tmp==NULL) return 0; | | if(tmp==NULL) return 0; |
switch(spell_type) { | | |
case SP_CURE_DISEASE: | | /* Figure out how many hp this spell might cure. |
| | * could be zero if this spell heals effects, not damage. |
| | */ |
| | heal = spell->stats.dam; |
| | if (spell->stats.hp) |
| | heal += random_roll(spell->stats.hp, 6, op, PREFER_HIGH) + |
| | spell->stats.hp; |
| | |
| | if (tmp->stats.hp >= tmp->stats.maxhp && heal) { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "Your are already fully healed."); |
| | } |
| | else { |
| | /* See how many points we actually heal. Instead of messages |
| | * based on type of spell, we instead do messages based |
| | * on amount of damage healed. |
| | */ |
| | if (heal > (tmp->stats.maxhp - tmp->stats.hp)) |
| | heal = tmp->stats.maxhp - tmp->stats.hp; |
| | tmp->stats.hp += heal; |
| | |
| | if (tmp->stats.hp >= tmp->stats.maxhp) { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "You feel just fine!"); |
| | } else if (heal > 50) { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds close!"); |
| | } else if (heal > 25) { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds mostly close."); |
| | } else if (heal > 10) { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to fade."); |
| | } else { |
| | new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to close."); |
| | } |
| | success=1; |
| | } |
| | if (spell->attacktype & AT_DISEASE) |
if (cure_disease (tmp, op)) | | if (cure_disease (tmp, op)) |
success = 1; | | success = 1; |
break; | | |
case SP_MINOR_HEAL: | | if (spell->attacktype & AT_POISON) { |
heal=random_roll(1, 7, op, PREFER_HIGH); | | |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to close."); | | |
break; | | |
case SP_MED_HEAL: | | |
heal=die_roll(3, 6, op, PREFER_HIGH)+4; | | |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to fade."); | | |
break; | | |
case SP_MAJOR_HEAL: | | |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your skin looks as good as new!"); | | |
heal=die_roll(4, 8, op, PREFER_HIGH)+8; | | |
break; | | |
case SP_HEAL: | | |
heal=tmp->stats.maxhp; /* or should be this tmp->stats.maxhp? */ | | |
new_draw_info(NDI_UNIQUE, 0,tmp, "You feel just fine!"); | | |
break; | | |
case SP_CURE_POISON: | | |
at = find_archetype("poisoning"); | | at = find_archetype("poisoning"); |
poison=present_arch_in_ob(at,tmp); | | poison=present_arch_in_ob(at,tmp); |
| | |
if (poison) { | | if (poison) { |
success = 1; | | success = 1; |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your body feels cleansed"); | | new_draw_info(NDI_UNIQUE, 0,tmp, "Your body feels cleansed"); |
poison->stats.food = 1; | | poison->stats.food = 1; |
} | | } |
break; | | } |
case SP_CURE_CONFUSION: | | if (spell->attacktype & AT_CONFUSION) { |
at=find_archetype("confusion"); | | at=find_archetype("confusion"); |
poison=present_arch_in_ob(at,tmp); | | poison=present_arch_in_ob(at,tmp); |
if (poison) { | | if (poison) { |
| | |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your mind feels clearer"); | | new_draw_info(NDI_UNIQUE, 0,tmp, "Your mind feels clearer"); |
poison->stats.food = 1; | | poison->stats.food = 1; |
} | | } |
break; | | } |
case SP_CURE_BLINDNESS: | | if (spell->attacktype & AT_BLIND) { |
at=find_archetype("blindness"); | | at=find_archetype("blindness"); |
poison=present_arch_in_ob(at,tmp); | | poison=present_arch_in_ob(at,tmp); |
if (poison) { | | if (poison) { |
| | |
new_draw_info(NDI_UNIQUE, 0,tmp,"Your vision begins to return."); | | new_draw_info(NDI_UNIQUE, 0,tmp,"Your vision begins to return."); |
poison->stats.food = 1; | | poison->stats.food = 1; |
} | | } |
break; | | |
case SP_RESTORATION: /* does cure poison, cure madness, heal,and removes depletion, food=999. */ | | |
if (cast_heal (op, dir, SP_CURE_POISON)) | | |
success = 1; | | |
if (cast_heal (op, dir, SP_CURE_CONFUSION)) | | |
success = 1; | | |
if (cast_heal (op, dir, SP_CURE_DISEASE)) | | |
success = 1; | | |
if (tmp->stats.food < 999) { | | |
success = 1; | | |
tmp->stats.food=999; | | |
} | | } |
#if 0 | | if (spell->last_sp && tmp->stats.sp < tmp->stats.maxsp) { |
/* Leave removing depletion to the restore potion. */ | | tmp->stats.sp += spell->last_sp; |
at=find_archetype("depletion"); | | if (tmp->stats.sp > tmp->stats.maxsp) tmp->stats.sp = tmp->stats.maxsp; |
poison=present_arch_in_ob(at,tmp); | | |
if (poison) { | | |
success = 1; | | success = 1; |
new_draw_info(NDI_UNIQUE, 0,tmp, "Your abilities seem to have recovered."); | | new_draw_info(NDI_UNIQUE, 0,tmp,"Magical energies surge through your body!"); |
remove_ob(poison); | | |
free_object(poison); | | |
poison = present_arch_in_ob(at, tmp); | | |
} | | } |
#endif | | if (spell->last_grace && tmp->stats.grace < tmp->stats.maxgrace) { |
/* Put this one last because it'll redraw the stats as a side effect. */ | | tmp->stats.grace += spell->last_grace; |
if (cast_heal (op, dir, SP_HEAL)) | | if (tmp->stats.grace > tmp->stats.maxgrace) tmp->stats.grace = tmp->stats.maxgrace; |
success = 1; | | success = 1; |
return success; | | new_draw_info(NDI_UNIQUE, 0,tmp,"You feel redeemed with you god!"); |
} | | } |
if (heal > 0) { | | if (spell->stats.food && tmp->stats.food < 999) { |
if (reduce_symptoms (tmp, heal)) | | tmp->stats.food += spell->stats.food; |
| | if (tmp->stats.food > 999) tmp->stats.food=999; |
success = 1; | | success = 1; |
if (tmp->stats.hp < tmp->stats.maxhp) { | | /* We could do something a bit better like the messages for healing above */ |
success = 1; | | new_draw_info(NDI_UNIQUE, 0,tmp,"You feel your belly fill with food"); |
tmp->stats.hp += heal; | | |
if (tmp->stats.hp > tmp->stats.maxhp) | | |
tmp->stats.hp = tmp->stats.maxhp; | | |
} | | |
} | | } |
| | |
if (success) | | |
op->speed_left = -FABS(op->speed)*3; /* Freeze them for a short while */ | | |
return success; | | return success; |
} | | } |
| | |
int cast_regenerate_spellpoints(object *op) { | | |
object *tmp; | | |
| | |
tmp = find_target_for_friendly_spell(op,0); | | |
| | |
if(tmp==NULL) return 0; | | |
| | |
tmp->stats.sp = tmp->stats.maxsp; | | |
new_draw_info(NDI_UNIQUE, 0,tmp, "Magical energies surge through your body!"); | | |
return 1; | | |
} | | |
| | |
/* This is used for the spells that gain stats. There are no spells | | /* This is used for the spells that gain stats. There are no spells |
* right now that icnrease wis/int/pow on a temp basis, so no | | * right now that icnrease wis/int/pow on a temp basis, so no |
| | |
"no pow" | | "no pow" |
}; | | }; |
| | |
int cast_change_attr(object *op,object *caster,int dir,int spell_type) { | | int cast_change_ability(object *op,object *caster,object *spell_ob, int dir) { |
object *tmp = op; | | object *tmp, *tmp2=NULL; |
object *tmp2=NULL; | | |
object *force=NULL; | | object *force=NULL; |
int is_refresh=0; | | int i; |
int atnr=0, path=0; /* see protection spells */ | | |
int i,stat=-1; | | |
| | |
/* if dir = 99 op defaults to tmp, eat_special_food() requires this. */ | | /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */ |
if(dir!=99) { | | if(dir!=0) { |
if (spell_type == SP_CURSE) { | | |
tmp=get_pointed_target(op, (dir==0)?op->direction:dir); | | |
if (!tmp) { | | |
new_draw_info(NDI_UNIQUE, 0, op, "There is no one in that direction to curse."); | | |
} | | |
} else { | | |
tmp=find_target_for_friendly_spell(op,dir); | | tmp=find_target_for_friendly_spell(op,dir); |
} | | } else { |
| | tmp = op; |
} | | } |
| | |
if(tmp==NULL) return 0; | | if(tmp==NULL) return 0; |
| | |
/* If we've already got a force of this type, don't add a new one. */ | | /* If we've already got a force of this type, don't add a new one. */ |
for(tmp2=tmp->inv; tmp2!=NULL; tmp2=tmp2->below) { | | for(tmp2=tmp->inv; tmp2!=NULL; tmp2=tmp2->below) { |
if (tmp2->type==FORCE) { | | if (tmp2->type==FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) { |
if(tmp2->value == spell_type) { | | if (tmp2->name == spell_ob->name) { |
force=tmp2; /* the old effect will be "refreshed" */ | | force=tmp2; /* the old effect will be "refreshed" */ |
is_refresh=1; | | break; |
new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect."); | | |
} | | } |
else if ((spell_type==SP_BLESS && tmp2->value==SP_HOLY_POSSESSION) || | | else if (spell_ob->race && spell_ob->race == tmp2->name) { |
(spell_type==SP_HOLY_POSSESSION && tmp2->value==SP_BLESS)) { | | new_draw_info_format(NDI_UNIQUE, 0, op, |
/* both bless AND holy posession are not allowed! */ | | "You can not cast %s while %s is in effect", |
new_draw_info(NDI_UNIQUE, 0, op,"No more blessings for you."); | | spell_ob->name, tmp2->name_pl); |
return 0; | | return 0; |
} | | } |
} | | } |
} | | } |
if(force==NULL) | | if(force==NULL) { |
force=get_archetype("force"); | | force=get_archetype("force"); |
force->value = spell_type; /* mark this force with the originating spell */ | | force->subtype = FORCE_CHANGE_ABILITY; |
| | free_string(force->name); |
i=0; /* (-> protection spells) */ | | if (spell_ob->race) |
switch(spell_type) { | | force->name = add_refcount(spell_ob->race); |
case SP_RAGE: | | else |
if(tmp->type!=PLAYER) | | force->name = add_refcount(spell_ob->name); |
break; | | free_string(force->name_pl); |
/* Str, Dex, Con */ | | force->name_pl = add_refcount(spell_ob->name); |
cast_change_attr(op,caster,dir,SP_HEROISM); | | |
/* haste */ | | |
cast_change_attr(op,caster,dir,SP_HASTE); | | |
/* armour */ | | |
cast_change_attr(op,caster,dir,SP_ARMOUR); | | |
/* regeneration */ | | |
cast_change_attr(op,caster,dir,SP_REGENERATION); | | |
/* weapon class */ | | |
force->stats.wc += SP_level_dam_adjust(op,caster,SP_BLESS); | | |
break; | | |
| | |
| | |
case SP_STRENGTH: if (stat==-1) stat = STR; | | } else { |
case SP_DEXTERITY: if (stat==-1) stat = DEX; | | int duration; |
case SP_CONSTITUTION: if (stat==-1) stat = CON; | | |
case SP_CHARISMA: if (stat==-1) stat = CHA; | | |
/* This area is common to all the stat change stuff above */ | | |
| | |
/* If we are refreshing this stat, don't do any of the below - if we do, we get odd | | |
* results - the first one gets maximum benefit, but then the next one isn't as | | |
* good because the player stat is now higher, so give less a bonus to the force, | | |
* then the third one may be more effective again. | | |
*/ | | |
if(tmp->type!=PLAYER || is_refresh) | | |
break; | | |
if(!(random_roll(0, (MAX(1,(10 - MAX_STAT + get_attr_value(&tmp->stats,stat))))-1, op, PREFER_LOW))) { | | |
set_attr_value(&force->stats, stat, 1); | | |
| | |
for(i=20;i>get_attr_value(&tmp->stats, stat);i-=2) | | duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50; |
change_attr_value(&force->stats, stat, 1); | | if (duration > force->duration) { |
| | force->duration = duration; |
| | new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect."); |
} else { | | } else { |
| | new_draw_info(NDI_UNIQUE, 0, op, "Recasting the spell had no effect."); |
| | } |
| | return 1; |
| | } |
| | force->duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50; |
| | force->speed = 1.0; |
| | force->speed_left = -1.0; |
| | SET_FLAG(force, FLAG_APPLIED); |
| | |
| | /* Now start processing the effects. First, protections */ |
| | for (i=0; i < NROFATTACKS; i++) { |
| | if (spell_ob->resist[i]) { |
| | force->resist[i] = spell_ob->resist[i] + SP_level_dam_adjust(caster, spell_ob); |
| | if (force->resist[i] > 100) force->resist[i] = 100; |
| | } |
| | } |
| | if (spell_ob->stats.hp) |
| | force->stats.hp = spell_ob->stats.hp + SP_level_dam_adjust(caster,spell_ob); |
| | |
| | if (tmp->type == PLAYER) { |
| | /* Stat adjustment spells */ |
| | for (i=0; i < NUM_STATS; i++) { |
| | int stat = get_attr_value(&spell_ob->stats, i), k, sm; |
| | if (stat) { |
| | sm=0; |
| | for (k=0; k<stat; k++) |
| | sm += rndm(1, 3); |
| | |
| | if ((get_attr_value(&tmp->stats, i) + sm) > (15 + 5 * stat)) { |
| | sm = (15 + 5 * stat) - get_attr_value(&tmp->stats, i); |
| | if (sm<0) sm = 0; |
| | } |
| | set_attr_value(&force->stats, i, sm); |
| | if (!sm) |
new_draw_info(NDI_UNIQUE, 0,op,no_gain_msgs[stat]); | | new_draw_info(NDI_UNIQUE, 0,op,no_gain_msgs[stat]); |
set_attr_value(&force->stats, stat, 0); | | |
} | | } |
break; | | } |
| | } |
| | |
case SP_IRONWOOD_SKIN: | | if (QUERY_FLAG(spell_ob, FLAG_FLYING)) |
case SP_ARMOUR: { | | SET_FLAG(force, FLAG_FLYING); |
/* armour MAY be used multiple times. */ | | |
force->value = 0; | | if (QUERY_FLAG(spell_ob, FLAG_SEE_IN_DARK)) |
/* With PR code, I wonder if this could get merged in with the other protection spells */ | | SET_FLAG(force, FLAG_SEE_IN_DARK); |
/* peterm, modified so that it uses level-depend functions */ | | |
force->stats.ac=2+SP_level_dam_adjust(op,caster,spell_type); | | if (QUERY_FLAG(spell_ob, FLAG_XRAYS)) |
if((tmp->stats.ac-force->stats.ac)<-20) | | SET_FLAG(force, FLAG_XRAYS); |
force->stats.ac=tmp->stats.ac+20; | | |
| | /* Haste/bonus speed */ |
force->resist[ATNR_PHYSICAL] = 5+4*SP_level_dam_adjust(op,caster,spell_type); | | if (spell_ob->stats.exp) { |
if (force->resist[ATNR_PHYSICAL] > 25) | | if (op->speed > 0.5) force->stats.exp = (float) spell_ob->stats.exp / (op->speed + 0.5); |
force->resist[ATNR_PHYSICAL]=25; | | else |
if(tmp->resist[ATNR_PHYSICAL]>70&& force->resist[ATNR_PHYSICAL]>(100-tmp->resist[ATNR_PHYSICAL])/3) | | force->stats.exp = spell_ob->stats.exp; |
force->resist[ATNR_PHYSICAL]=3; /* diminishing returns at high armor. */ | | |
new_draw_info(NDI_UNIQUE, 0,tmp,"A force shimmers around you."); | | |
break; | | |
} | | } |
| | |
case SP_CONFUSION: | | force->stats.wc = spell_ob->stats.wc; |
force->attacktype |= (AT_CONFUSION|AT_PHYSICAL); | | force->stats.ac = spell_ob->stats.ac; |
force->resist[ATNR_CONFUSION] = 50; /*??? It was here before PR */ | | force->attacktype = spell_ob->attacktype; |
break; | | |
| | |
case SP_HEROISM: | | change_abil(tmp,force); /* Mostly to display any messages */ |
if (tmp->type != PLAYER) | | insert_ob_in_ob(force,tmp); |
break; | | fix_player(tmp); |
cast_change_attr(op,caster,dir,SP_STRENGTH); | | return 1; |
cast_change_attr(op,caster,dir,SP_DEXTERITY); | | } |
cast_change_attr(op,caster,dir,SP_CONSTITUTION); | | |
break; | | /* This used to be part of cast_change_ability, but it really didn't make |
| | * a lot of sense, since most of the values it derives are from the god |
| | * of the caster. |
| | */ |
| | |
case SP_HOLY_POSSESSION: { | | int cast_bless(object *op,object *caster,object *spell_ob, int dir) { |
object *god = find_god(determine_god(op)); | | |
int i; | | int i; |
| | object *god = find_god(determine_god(op)), *tmp2, *force, *tmp; |
| | |
if(!god) { | | /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */ |
new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty."); | | if(dir!=0) { |
| | tmp=find_target_for_friendly_spell(op,dir); |
} else { | | } else { |
force->attacktype|=god->attacktype | AT_PHYSICAL; | | tmp = op; |
if(god->slaying) force->slaying = add_string(god->slaying); | | |
| | |
/* Only give out good benefits, not bad */ | | |
for (i=0; i<NROFATTACKS; i++) { | | |
if (god->resist[i]>0) { | | |
force->resist[i] = god->resist[i]; | | |
if (force->resist[i]>95) force->resist[i]=95; | | |
} | | |
else force->resist[i]=0; /* adding of diff. types not allowed */ | | |
} | | } |
| | |
force->path_attuned|=god->path_attuned; | | /* If we've already got a force of this type, don't add a new one. */ |
new_draw_info_format(NDI_UNIQUE, 0,tmp, | | for(tmp2=tmp->inv; tmp2!=NULL; tmp2=tmp2->below) { |
"You are possessed by the essence of %s!",god->name); | | if (tmp2->type==FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) { |
| | if (tmp2->name == spell_ob->name) { |
| | force=tmp2; /* the old effect will be "refreshed" */ |
| | break; |
} | | } |
| | else if (spell_ob->race && spell_ob->race == tmp2->name) { |
if(tmp!=op && op->type==PLAYER && tmp->type==PLAYER) { | | |
new_draw_info_format(NDI_UNIQUE, 0, op, | | new_draw_info_format(NDI_UNIQUE, 0, op, |
"You bless %s mightily!", tmp->name); | | "You can not cast %s while %s is in effect", |
new_draw_info_format(NDI_UNIQUE, 0, tmp, | | spell_ob->name, tmp2->name_pl); |
"%s blessed you mightily!", op->name); | | return 0; |
| | } |
} | | } |
force->stats.wc = SP_level_dam_adjust(op, caster,SP_HOLY_POSSESSION); | | |
force->stats.ac = SP_level_dam_adjust(op, caster,SP_HOLY_POSSESSION); | | |
break; | | |
} | | } |
| | if(force==NULL) { |
| | force=get_archetype("force"); |
| | force->subtype = FORCE_CHANGE_ABILITY; |
| | free_string(force->name); |
| | if (spell_ob->race) |
| | force->name = add_refcount(spell_ob->race); |
| | else |
| | force->name = add_refcount(spell_ob->name); |
| | free_string(force->name_pl); |
| | force->name_pl = add_refcount(spell_ob->name); |
| | } else { |
| | int duration; |
| | |
case SP_BLESS: { | | duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50; |
object *god = find_god(determine_god(op)); | | if (duration > force->duration) { |
if(god) { | | force->duration = duration; |
int i; | | new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect."); |
| | } else { |
| | new_draw_info(NDI_UNIQUE, 0, op, "Recasting the spell had no effect."); |
| | } |
| | return 0; |
| | } |
| | force->duration = spell_ob->duration + SP_level_duration_adjust(caster, spell_ob) * 50; |
| | force->speed = 1.0; |
| | force->speed_left = -1.0; |
| | SET_FLAG(force, FLAG_APPLIED); |
| | |
| | if(!god) { |
| | new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty."); |
| | } else { |
/* Only give out good benefits, and put a max on it */ | | /* Only give out good benefits, and put a max on it */ |
for (i=0; i<NROFATTACKS; i++) { | | for (i=0; i<NROFATTACKS; i++) { |
if (god->resist[i]>0) { | | if (god->resist[i]>0) { |
force->resist[i] = god->resist[i]; | | force->resist[i] = MIN(god->resist[i], spell_ob->resist[ATNR_GODPOWER]); |
if (force->resist[i]>30) force->resist[i]=30; | | |
} | | } |
else force->resist[i]=0; /* adding of diff. types not allowed */ | | |
} | | } |
force->path_attuned|=god->path_attuned; | | force->path_attuned|=god->path_attuned; |
new_draw_info_format(NDI_UNIQUE, 0,tmp, | | if (spell_ob->attacktype) { |
"You receive the blessing of %s.",god->name); | | force->attacktype|=god->attacktype | AT_PHYSICAL; |
} else | | if(god->slaying) force->slaying = add_string(god->slaying); |
new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty."); | | } |
| | if (tmp != op) { |
if(tmp!=op && op->type==PLAYER && tmp->type==PLAYER) { | | |
new_draw_info_format(NDI_UNIQUE, 0, op, "You bless %s.", tmp->name); | | new_draw_info_format(NDI_UNIQUE, 0, op, "You bless %s.", tmp->name); |
new_draw_info_format(NDI_UNIQUE, 0, tmp, "%s blessed you.", op->name); | | new_draw_info_format(NDI_UNIQUE, 0, tmp, "%s blessed you.", op->name); |
} | | } else { |
force->stats.wc = SP_level_dam_adjust(op, caster,SP_BLESS); | | |
force->stats.ac = SP_level_dam_adjust(op, caster,SP_BLESS); | | |
break; | | |
} | | |
| | |
case SP_REGENERATION: | | |
force->stats.hp = 1 + SP_level_dam_adjust(op, caster,SP_REGENERATION); | | |
break; | | |
| | |
case SP_CURSE: { | | |
object *god = find_god(determine_god(op)); | | |
if(god) { | | |
force->path_repelled|=god->path_repelled; | | |
force->path_denied|=god->path_denied; | | |
new_draw_info_format(NDI_UNIQUE, 0,tmp, | | new_draw_info_format(NDI_UNIQUE, 0,tmp, |
"You are a victim of %s's curse!",god->name); | | "You are blessed by %s!",god->name); |
} else | | |
new_draw_info(NDI_UNIQUE, 0,op,"Your curse seems empty."); | | |
| | |
if(tmp!=op && caster->type==PLAYER) | | |
new_draw_info_format(NDI_UNIQUE, 0, caster, "You curse %s!",tmp->name); | | |
force->stats.ac = -SP_level_dam_adjust(op, caster,SP_CURSE); | | |
force->stats.wc = -SP_level_dam_adjust(op, caster,SP_CURSE); | | |
break; | | |
} | | } |
| | |
case SP_DARK_VISION: | | } |
SET_FLAG(force,FLAG_SEE_IN_DARK); | | force->stats.wc = spell_ob->stats.wc; |
break; | | force->stats.ac = spell_ob->stats.ac; |
| | |
/* attacktype-protection spells: */ | | change_abil(tmp,force); /* Mostly to display any messages */ |
case SP_PROT_COLD: | | insert_ob_in_ob(force,tmp); |
if (!i) atnr=ATNR_COLD, path=PATH_FROST, i=1; | | fix_player(tmp); |
| | return 1; |
| | } |
| | |
case SP_PROT_FIRE: | | |
if (!i) atnr=ATNR_FIRE, path=PATH_FIRE, i=1; | | |
| | |
case SP_PROT_ELEC: | | |
if (!i) atnr=ATNR_ELECTRICITY, path=PATH_ELEC, i=1; | | |
| | |
case SP_PROT_POISON: | | /* Alchemy code by Mark Wedel |
if (!i) atnr=ATNR_POISON, i=1; | | * |
| | * This code adds a new spell, called alchemy. Alchemy will turn |
| | * objects to gold nuggets, the value of the gold nuggets being |
| | * about 90% of that of the item itself. It uses the value of the |
| | * object before charisma adjustments, because the nuggets themselves |
| | * will be will be adjusted by charisma when sold. |
| | * |
| | * Large nuggets are worth 25 gp each (base). You will always get |
| | * the maximum number of large nuggets you could get. |
| | * Small nuggets are worth 1 gp each (base). You will get from 0 |
| | * to the max amount of small nuggets as you could get. |
| | * |
| | * For example, if an item is worth 110 gold, you will get |
| | * 4 large nuggets, and from 0-10 small nuggets. |
| | * |
| | * There is also a chance (1:30) that you will get nothing at all |
| | * for the object. There is also a maximum weight that will be |
| | * alchemied. |
| | */ |
| | |
case SP_PROT_SLOW: | | /* I didn't feel like passing these as arguements to the |
if (!i) atnr=ATNR_SLOW, i=1; | | * two functions that need them. Real values are put in them |
| | * when the spell is cast, and these are freed when the spell |
| | * is finished. |
| | */ |
| | static object *small, *large; |
| | |
case SP_PROT_PARALYZE: | | static void alchemy_object(object *obj, int *small_nuggets, |
if (!i) atnr=ATNR_PARALYZE, path=PATH_MIND, i=1; | | int *large_nuggets, int *weight) |
| | { |
| | int value=query_cost(obj, NULL, F_TRUE); |
| | |
case SP_PROT_DRAIN: | | /* Give third price when we alchemy money (This should hopefully |
if (!i) atnr=ATNR_DRAIN, path=PATH_DEATH, i=1; | | * make it so that it isn't worth it to alchemy money, sell |
| | * the nuggets, alchemy the gold from that, etc. |
| | * Otherwise, give 9 silver on the gold for other objects, |
| | * so that it would still be more affordable to haul |
| | * the stuff back to town. |
| | */ |
| | |
case SP_PROT_ATTACK: | | if (QUERY_FLAG(obj, FLAG_UNPAID)) |
if (!i) atnr=ATNR_PHYSICAL, path=PATH_PROT, i=1; | | value=0; |
| | else if (obj->type==MONEY || obj->type==GEM) |
| | value /=3; |
| | else |
| | value *= 0.9; |
| | |
case SP_PROT_MAGIC: | | if ((obj->value>0) && rndm(0, 29)) { |
if (!i) atnr=ATNR_MAGIC, i=1; | | static int value_store; |
| | int count; |
| | |
case SP_PROT_CONFUSE: | | value_store += value; |
if (!i) atnr=ATNR_CONFUSION, path=PATH_MIND, i=1; | | count = value_store / large->value; |
| | *large_nuggets += count; |
case SP_PROT_CANCEL: | | value_store -= count * large->value; |
if (!i) atnr=ATNR_CANCELLATION, i=1; | | count = value_store / small->value; |
| | *small_nuggets += count; |
case SP_PROT_DEPLETE: | | value_store -= count * small->value; |
if (!i) atnr=ATNR_DEPLETE, path=PATH_DEATH, i=1; | | /* LOG(llevDebug, "alchemize value %d, remainder %d\n", value, value_store); */ |
| | |
/* The amount of prot. granted depends on caster's skill-level and | | |
* on attunement to spellpath, if there is a related one: | | |
*/ | | |
force->resist[atnr] = (20 + 30*SK_level(caster)/100 | | |
+ ((caster->path_attuned & path) ? 10 : 0) | | |
- ((caster->path_repelled & path) ? 10 : 0)) | | |
/ ((caster->path_denied & path) ? 2 : 1); | | |
break; | | |
| | |
case SP_LEVITATE: | | |
SET_FLAG(force, FLAG_FLYING); | | |
break; | | |
| | |
/* The following immunity spells are obsolete... -AV */ | | |
case SP_IMMUNE_COLD: | | |
force->resist[ATNR_COLD]=100; | | |
break; | | |
| | |
case SP_IMMUNE_FIRE: | | |
force->resist[ATNR_FIRE]=100; | | |
break; | | |
| | |
case SP_IMMUNE_ELEC: | | |
force->resist[ATNR_ELECTRICITY]=100; | | |
break; | | |
| | |
case SP_IMMUNE_POISON: | | |
force->resist[ATNR_POISON]=100; | | |
break; | | |
| | |
case SP_IMMUNE_SLOW: | | |
force->resist[ATNR_SLOW]=100; | | |
break; | | |
| | |
case SP_IMMUNE_PARALYZE: | | |
force->resist[ATNR_PARALYZE]=100; | | |
break; | | |
| | |
case SP_IMMUNE_DRAIN: | | |
force->resist[ATNR_DRAIN]=100; | | |
break; | | |
| | |
case SP_IMMUNE_ATTACK: | | |
force->resist[ATNR_PHYSICAL]=100; | | |
break; | | |
| | |
case SP_IMMUNE_MAGIC: | | |
force->resist[ATNR_MAGIC]=100; | | |
break; | | |
| | |
case SP_INVULNERABILITY: | | |
case SP_PROTECTION: | | |
/* Don't give them everything, so can't do a simple loop. | | |
* Added holyword & blind with PR's - they seemed to be | | |
* missing before. | | |
* Note: These Spells shouldn't be used. Especially not on players! -AV | | |
*/ | | |
if (spell_type == SP_INVULNERABILITY) i=100; | | |
else i=30; | | |
| | |
force->resist[ATNR_PHYSICAL]=i; | | |
force->resist[ATNR_MAGIC]=i; | | |
force->resist[ATNR_FIRE]=i; | | |
force->resist[ATNR_ELECTRICITY]=i; | | |
force->resist[ATNR_COLD]=i; | | |
force->resist[ATNR_CONFUSION]=i; | | |
force->resist[ATNR_ACID]=i; | | |
force->resist[ATNR_DRAIN]=i; | | |
force->resist[ATNR_GHOSTHIT]=i; | | |
force->resist[ATNR_POISON]=i; | | |
force->resist[ATNR_SLOW]=i; | | |
force->resist[ATNR_PARALYZE]=i; | | |
force->resist[ATNR_TURN_UNDEAD]=i; | | |
force->resist[ATNR_FEAR]=i; | | |
force->resist[ATNR_DEPLETE]=i; | | |
force->resist[ATNR_DEATH]=i; | | |
force->resist[ATNR_HOLYWORD]=i; | | |
force->resist[ATNR_BLIND]=i; | | |
| | |
| | |
case SP_HASTE: | | |
force->stats.exp=(3+SP_level_dam_adjust(op, caster,SP_HASTE)); | | |
if(op->speed > 0.2 * SP_level_strength_adjust(op,caster,SP_HASTE)) | | |
force->stats.exp=0; | | |
break; | | |
| | |
case SP_XRAY: | | |
SET_FLAG(force,FLAG_XRAYS); | | |
break; | | |
} | | |
force->speed_left= -1-SP_level_strength_adjust(op, caster,spell_type)*0.1; | | |
| | |
if (!is_refresh) { | | |
SET_FLAG(force, FLAG_APPLIED); | | |
force = insert_ob_in_ob(force,tmp); | | |
} | | |
/* Perhaps we should only do this is !is_refresh above? */ | | |
change_abil(tmp,force); /* Mostly to display any messages */ | | |
fix_player(tmp); /* This takes care of some stuff that change_abil() */ | | |
/* unfortunately is incapable off. */ | | |
| | |
return 1; | | |
} | | |
| | |
| | |
/* | | |
* Or even better, use treasure lists for this, | | |
* eg, mage_pet_monster treasure list, etc. Means | | |
* that these could be changed without recompiling the server. | | |
* | | |
* Garbled 2002-12-14 | | |
* Made summon_pet_monster use the treasurelist, as suggested above. Adding | | |
* new lines to the treasures for these categories will automatically work. | | |
*/ | | |
| | |
int summon_pet(object *op, int dir, SpellTypeFrom item) { | | |
int level, number, i; | | |
treasurelist *trlist = NULL; | | |
treasure *tr = NULL; | | |
| | |
level = ((op->head?op->head->level:SK_level(op)) / 4); | | |
| | |
switch(rndm(0, 2)) { | | |
case 0: | | |
trlist = find_treasurelist("mage_pet_monster"); | | |
break; | | |
case 1: | | |
trlist = find_treasurelist("priest_pet_monster"); | | |
break; | | |
default: | | |
trlist = find_treasurelist("altern_pet_monster"); | | |
break; | | |
} | | |
| | |
if (trlist == NULL) | | |
return 0; | | |
| | |
for (i=0, tr=trlist->items; tr != NULL && i < level && | | |
tr->next != NULL && tr->next->item != NULL; tr = tr->next, i++); | | |
| | |
if(tr == NULL || tr->item == NULL) { | | |
LOG(llevError,"Treasurelist Found NULL in summon_pet_monster()\n"); | | |
return 0; | | |
} | | |
| | |
number = tr->nrof; | | |
| | |
if (!dir) | | |
dir = find_free_spot(tr->item, op->map, op->x, op->y, 1, SIZEOFFREE); | | |
| | |
if ((dir==-1) || arch_blocked(tr->item, op->map, | | |
op->x + freearr_x[dir], op->y+freearr_y[dir])) { | | |
new_draw_info(NDI_UNIQUE, 0, op, "There is something in the way."); | | |
return 0; | | |
} | | |
for (i = 1; i < number + 1; i++) { | | |
archetype *atmp; | | |
object *prev = NULL, *head = NULL; /* We want to summon dragons *grin* */ | | |
| | |
for(atmp = tr->item; atmp!=NULL; atmp = atmp->more) { | | |
object *tmp; | | |
tmp = arch_to_object(atmp); | | |
| | |
/* if this is the head, set owner/friendly as needed */ | | |
if (atmp == tr->item) { | | |
set_owner(tmp, op); | | |
SET_FLAG(tmp, FLAG_MONSTER); | | |
if (op->type == PLAYER) { | | |
tmp->stats.exp = 0; | | |
add_friendly_object(tmp); | | |
SET_FLAG(tmp, FLAG_FRIENDLY); | | |
tmp->move_type = PETMOVE; | | |
} else if (QUERY_FLAG (op, FLAG_FRIENDLY)) { | | |
object *owner = get_owner(op); | | |
if (owner != NULL) { | | |
set_owner (tmp, owner); | | |
tmp->move_type = PETMOVE; | | |
add_friendly_object (tmp); | | |
SET_FLAG (tmp, FLAG_FRIENDLY); | | |
} | | |
} | | |
tmp->speed_left = -1; | | |
tmp->enemy = op->enemy; | | |
tmp->type = 0; | | |
} | | |
if(head == NULL) | | |
head = tmp; | | |
tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x; | | |
tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y; | | |
tmp->map = op->map; | | |
if(head != tmp) | | |
tmp->head = head, prev->more = tmp; | | |
prev = tmp; | | |
} | | |
head->direction = dir; | | |
| | |
/* Some monsters are sleeping by default - clear that */ | | |
CLEAR_FLAG(head, FLAG_SLEEP); | | |
| | |
head = insert_ob_in_map (head, op->map, op,0); | | |
if (head != NULL && head->randomitems != NULL) { | | |
object *tmp; | | |
create_treasure(head->randomitems,head,GT_APPLY,6,0); | | |
for(tmp = head->inv; tmp != NULL; tmp = tmp->below) | | |
if(!tmp->nrof) | | |
SET_FLAG(tmp, FLAG_NO_DROP); | | |
} | | |
dir = absdir(dir + 1); | | |
if (arch_blocked(tr->item, op->map, op->x + freearr_x[dir], op->y + freearr_y[dir])) { | | |
if (i < number) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way,"); | | |
new_draw_info(NDI_UNIQUE, 0,op, "no more pets for this casting."); | | |
return 1; | | |
} | | |
} | | |
} /* for i < number */ | | |
return 1; | | |
} | | |
| | |
int create_bomb(object *op,object *caster,int dir,int spell_type,char *name) { | | |
object *tmp; | | |
int dx=op->x+freearr_x[dir],dy=op->y+freearr_y[dir]; | | |
| | |
if(get_map_flags(op->map,NULL, dx,dy, NULL,NULL) & (P_OUT_OF_MAP | P_WALL)) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way."); | | |
return 0; | | |
} | | |
tmp=get_archetype(name); | | |
| | |
/* level dependencies for bomb */ | | |
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam + SP_level_dam_adjust(op,caster,spell_type); | | |
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur + SP_level_strength_adjust(op,caster,spell_type); | | |
tmp->level = casting_level (caster, spell_type); | | |
| | |
set_owner(tmp,op); | | |
tmp->x=dx,tmp->y=dy; | | |
insert_ob_in_map(tmp,op->map,op,0); | | |
return 1; | | |
} | | |
| | |
void animate_bomb(object *op) { | | |
int i; | | |
object *env; | | |
archetype *at; | | |
| | |
if(op->state!=NUM_ANIMATIONS(op)-1) | | |
return; | | |
at = find_archetype("splint"); | | |
for(env=op;env->env!=NULL;env=env->env); | | |
| | |
if (op->env) { | | |
if (env->map == NULL) | | |
return; | | |
| | |
if (env->type == PLAYER) | | |
esrv_del_item(env->contr, op->count); | | |
| | |
remove_ob(op); | | |
op->x = env->x; | | |
op->y = env->y; | | |
if ((op = insert_ob_in_map (op, env->map, op,0)) == NULL) | | |
return; | | |
} | | |
if (at) | | |
for(i=1;i<9;i++) | | |
fire_arch(op,op,i,at,0,0); | | |
| | |
explode_object(op); | | |
} | | |
| | |
| | |
int fire_cancellation(object *op,int dir,archetype *at, int magic) { | | |
object *tmp; | | |
| | |
if(at==NULL) | | |
return 0; | | |
| | |
tmp=arch_to_object(at); | | |
| | |
if(tmp==NULL) | | |
return 0; | | |
| | |
tmp->x=op->x,tmp->y=op->y; | | |
tmp->direction=dir; | | |
if(magic) | | |
tmp->attacktype|=AT_MAGIC; | | |
| | |
if(op->type==PLAYER) | | |
set_owner(tmp,op); | | |
| | |
if ((tmp = insert_ob_in_map(tmp,op->map,op,0)) != NULL) | | |
move_cancellation(tmp); | | |
return 1; | | |
} | | |
| | |
void move_cancellation(object *op) { | | |
remove_ob(op); | | |
| | |
op->x+=DIRX(op),op->y+=DIRY(op); | | |
if (!op->direction|| | | |
get_map_flags(op->map,NULL,op->x,op->y,NULL,NULL) & (P_OUT_OF_MAP | P_WALL)) { | | |
free_object(op); | | |
return; | | |
} | | |
if(reflwall(op->map,op->x,op->y, op)) { | | |
op->direction=absdir(op->direction+4); | | |
insert_ob_in_map(op,op->map,op,0); | | |
return; | | |
} | | |
if ((op = insert_ob_in_map (op, op->map, op,0)) != NULL) | | |
hit_map (op, 0, op->attacktype); | | |
} | | |
| | |
void cancellation(object *op) | | |
{ | | |
object *tmp; | | |
| | |
if (QUERY_FLAG (op, FLAG_ALIVE) || op->type == CONTAINER | | |
|| op->type == THROWN_OBJ) | | |
{ | | |
/* Recur through the inventory */ | | |
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) | | |
if (!did_make_save_item(tmp, AT_CANCELLATION,op)) | | |
cancellation(tmp); | | |
} | | |
else /* Nullify this object. */ | | |
if(FABS(op->magic)<=(rndm(0, 5))) { | | |
op->magic=0; | | |
CLEAR_FLAG(op, FLAG_DAMNED); | | |
CLEAR_FLAG(op, FLAG_CURSED); | | |
CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL); | | |
CLEAR_FLAG(op, FLAG_KNOWN_CURSED); | | |
if (op->env && op->env->type == PLAYER) { | | |
esrv_send_item (op->env, op); | | |
} | | |
} | | |
} | | |
| | |
| | |
/* Create a missile (nonmagic - magic +4). Will either create bolts or arrows | | |
* based on whether a crossbow or bow is equiped. If neither, it defaults to | | |
* arrows. | | |
* Sets the plus based on the casters level. It is also settable with the | | |
* invoke command. If the caster attempts to create missiles with too | | |
* great a plus, the default is used. | | |
* The # of arrows created also goes up with level, so if a 30th level mage | | |
* wants LOTS of arrows, and doesn't care what the plus is he could | | |
* create nonnmagic arrows, or even -1, etc... | | |
* | | |
* Written by Ben Fennema (huma@netcom.com) - bugs fixed by Raphael Quinet | | |
*/ | | |
| | |
int cast_create_missile(object *op, object *caster,int dir, char *stringarg) | | |
{ | | |
int missile_plus=0; | | |
char *missile_name; | | |
object *tmp, *missile; | | |
tag_t tag; | | |
| | |
missile_name = "arrow"; | | |
| | |
for (tmp=op->inv; tmp != NULL; tmp=tmp->below) | | |
if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED)) { | | |
if (strstr(tmp->race, "bolt")) /* crossbow bolts */ | | |
missile_name="bolt"; | | |
break; | | |
} | | |
| | |
if (stringarg) | | |
missile_plus = atoi(stringarg); | | |
if (!stringarg || ((1 + SP_level_strength_adjust(op, caster,SP_CREATE_MISSILE)) - | | |
(3 * missile_plus)) < 0) | | |
missile_plus = SP_PARAMETERS[SP_CREATE_MISSILE].bdam + | | |
SP_level_dam_adjust(op, caster,SP_CREATE_MISSILE); | | |
if (missile_plus > 4) | | |
missile_plus = 4; | | |
else if (missile_plus < -4) | | |
missile_plus = -4; | | |
| | |
if (find_archetype(missile_name)==NULL) { | | |
LOG(llevDebug, "Cast create_missile: could not find archtype %s\n", | | |
missile_name); | | |
return 0; | | |
} | | |
missile = get_archetype(missile_name); | | |
missile->nrof = SP_PARAMETERS[SP_CREATE_MISSILE].bdur | | |
* ((1 + SP_level_strength_adjust(op, caster,SP_CREATE_MISSILE)) | | |
- (3 * missile_plus)); | | |
if (missile->nrof < 1) | | |
missile->nrof=1; | | |
| | |
missile->magic = missile_plus; | | |
missile->value=0; /* it would be too easy to get money by creating | | |
arrows +4 and selling them, even with value = 1 */ | | |
| | |
SET_FLAG(missile, FLAG_IDENTIFIED); | | |
tag = missile->count; | | |
if ( ! cast_create_obj (op, caster, missile, dir) && op->type == PLAYER | | |
&& ! was_destroyed (missile, tag)) | | |
{ | | |
tmp= get_owner(op); | | |
if (!tmp) | | |
pick_up(op, missile); | | |
else | | |
pick_up(tmp, missile); | | |
} | | |
return 1; | | |
} | | |
| | |
| | |
/* Alchemy code by Mark Wedel | | |
* | | |
* This code adds a new spell, called alchemy. Alchemy will turn | | |
* objects to gold nuggets, the value of the gold nuggets being | | |
* about 90% of that of the item itself. It uses the value of the | | |
* object before charisma adjustments, because the nuggets themselves | | |
* will be will be adjusted by charisma when sold. | | |
* | | |
* Large nuggets are worth 25 gp each (base). You will always get | | |
* the maximum number of large nuggets you could get. | | |
* Small nuggets are worth 1 gp each (base). You will get from 0 | | |
* to the max amount of small nuggets as you could get. | | |
* | | |
* For example, if an item is worth 110 gold, you will get | | |
* 4 large nuggets, and from 0-10 small nuggets. | | |
* | | |
* There is also a chance (1:30) that you will get nothing at all | | |
* for the object. There is also a maximum weight that will be | | |
* alchemied. | | |
*/ | | |
| | |
/* I didn't feel like passing these as arguements to the | | |
* two functions that need them. Real values are put in them | | |
* when the spell is cast, and these are freed when the spell | | |
* is finished. | | |
*/ | | |
static object *small, *large; | | |
| | |
static void alchemy_object(object *obj, int *small_nuggets, | | |
int *large_nuggets, int *weight) | | |
{ | | |
int value=query_cost(obj, NULL, F_TRUE); | | |
| | |
/* Give third price when we alchemy money (This should hopefully | | |
* make it so that it isn't worth it to alchemy money, sell | | |
* the nuggets, alchemy the gold from that, etc. | | |
* Otherwise, give 9 silver on the gold for other objects, | | |
* so that it would still be more affordable to haul | | |
* the stuff back to town. | | |
*/ | | |
| | |
if (QUERY_FLAG(obj, FLAG_UNPAID)) | | |
value=0; | | |
else if (obj->type==MONEY || obj->type==GEM) | | |
value /=3; | | |
else | | |
value *= 0.9; | | |
| | |
if ((obj->value>0) && rndm(0, 29)) { | | |
static int value_store; | | |
int count; | | |
| | |
value_store += value; | | |
count = value_store / large->value; | | |
*large_nuggets += count; | | |
value_store -= count * large->value; | | |
count = value_store / small->value; | | |
*small_nuggets += count; | | |
value_store -= count * small->value; | | |
/* LOG(llevDebug, "alchemize value %d, remainder %d\n", value, value_store); */ | | |
} | | |
| | |
/* Turn 25 small nuggets into 1 large nugget. If the value | | |
* of large nuggets is not evenly divisable my the small nugget | | |
* value, take off an extra small_nugget (Assuming small_nuggets!=0) | | |
*/ | | |
if (*small_nuggets * small->value >= large->value) { | | |
(*large_nuggets)++; | | |
*small_nuggets -= large->value / small->value; | | |
if (*small_nuggets && large->value % small->value) | | |
(*small_nuggets)--; | | |
} | | |
weight += obj->weight; | | |
remove_ob(obj); | | |
free_object(obj); | | |
} | | |
| | |
static void update_map(object *op, mapstruct *m, int small_nuggets, int large_nuggets, | | |
int x, int y) | | |
{ | | |
object *tmp; | | |
int flag=0; | | |
| | |
/* Put any nuggets below the player, but we can only pass this | | |
* flag if we are on the same space as the player | | |
*/ | | |
if (x == op->x && y == op->y && op->map == m) flag = INS_BELOW_ORIGINATOR; | | |
| | |
if (small_nuggets) { | | |
tmp = get_object(); | | |
copy_object(small, tmp); | | |
tmp-> nrof = small_nuggets; | | |
tmp->x = x; | | |
tmp->y = y; | | |
insert_ob_in_map(tmp, m, op, flag); | | |
} | | |
if (large_nuggets) { | | |
tmp = get_object(); | | |
copy_object(large, tmp); | | |
tmp-> nrof = large_nuggets; | | |
tmp->x = x; | | |
tmp->y = y; | | |
insert_ob_in_map(tmp, m, op, flag); | | |
} | | |
} | | |
| | |
int alchemy(object *op) | | |
{ | | |
int x,y,weight=0,weight_max,large_nuggets,small_nuggets, mflags; | | |
sint16 nx, ny; | | |
object *next,*tmp; | | |
mapstruct *mp; | | |
| | |
if(op->type!=PLAYER) | | |
return 0; | | |
| | |
/* Put a maximum weight of items that can be alchemied. Limits the power | | |
* some, and also prevents people from alcheming every table/chair/clock | | |
* in sight | | |
*/ | | |
weight_max = 100000 + 50000*SK_level(op); | | |
small=get_archetype("smallnugget"), | | |
large=get_archetype("largenugget"); | | |
| | |
for(y= op->y-1;y<=op->y+1;y++) { | | |
for(x= op->x-1;x<=op->x+1;x++) { | | |
nx = x; | | |
ny = y; | | |
| | |
mp = op->map; | | |
| | |
mflags = get_map_flags(mp, &mp, nx, ny, &nx, &ny); | | |
| | |
if(mflags & (P_OUT_OF_MAP | P_WALL | P_NO_MAGIC)) | | |
continue; | | |
| | |
small_nuggets=0; | | |
large_nuggets=0; | | |
| | |
for(tmp=get_map_ob(mp,nx,ny);tmp!=NULL;tmp=next) { | | |
next=tmp->above; | | |
if (tmp->weight>0 && !QUERY_FLAG(tmp, FLAG_NO_PICK) && | | |
!QUERY_FLAG(tmp, FLAG_ALIVE) && | | |
!QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) { | | |
| | |
if (tmp->inv) { | | |
object *next1, *tmp1; | | |
for (tmp1 = tmp->inv; tmp1!=NULL; tmp1=next1) { | | |
next1 = tmp1->below; | | |
if (tmp1->weight>0 && !QUERY_FLAG(tmp1, FLAG_NO_PICK) && | | |
!QUERY_FLAG(tmp1, FLAG_ALIVE) && | | |
!QUERY_FLAG(tmp1, FLAG_IS_CAULDRON)) | | |
alchemy_object(tmp1, &small_nuggets, &large_nuggets, | | |
&weight); | | |
} | | |
} | | |
alchemy_object(tmp, &small_nuggets, &large_nuggets, &weight); | | |
| | |
if (weight>weight_max) { | | |
update_map(op, mp, small_nuggets, large_nuggets, nx, ny); | | |
free_object(large); | | |
free_object(small); | | |
return 1; | | |
} | | |
} /* is alchemable object */ | | |
} /* process all objects on this space */ | | |
| | |
/* Insert all the nuggets at one time. This probably saves time, but | | |
* it also prevents us from alcheming nuggets that were just created | | |
* with this spell. | | |
*/ | | |
update_map(op, mp, small_nuggets, large_nuggets, nx, ny); | | |
} | | |
} | | |
free_object(large); | | |
free_object(small); | | |
/* reset this so that if player standing on a big pile of stuff, | | |
* it is redrawn properly. | | |
*/ | | |
op->contr->socket.look_position = 0; | | |
return 1; | | |
} | | |
| | |
int remove_curse(object *op, int type, SpellTypeFrom src) { | | |
object *tmp; | | |
int success = 0, was_one = 0; | | |
| | |
for (tmp = op->inv; tmp; tmp = tmp->below) | | |
if (QUERY_FLAG(tmp, FLAG_APPLIED) && (QUERY_FLAG(tmp, FLAG_CURSED) || | | |
(type == SP_REMOVE_DAMNATION && QUERY_FLAG(tmp, FLAG_DAMNED)))) | | |
{ | | |
was_one++; | | |
/* if (tmp->level <= op->level) */ | | |
if (tmp->level <= SK_level(op)) | | |
{ | | |
success++; | | |
if (type == SP_REMOVE_DAMNATION) | | |
CLEAR_FLAG(tmp, FLAG_DAMNED); | | |
| | |
CLEAR_FLAG(tmp, FLAG_CURSED); | | |
CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED); | | |
tmp->value = 0; /* Still can't sell it */ | | |
if (op->type == PLAYER) | | |
esrv_send_item(op, tmp); | | |
} | | |
} | | |
if (op->type==PLAYER) { | | |
if (success) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "You feel like someone is helping you."); | | |
} else | | |
if (was_one) | | |
new_draw_info(NDI_UNIQUE, 0,op, "You failed to remove the curse."); | | |
else if (src == spellNormal) | | |
new_draw_info(NDI_UNIQUE, 0,op, "You are not using any cursed items."); | | |
else | | |
new_draw_info(NDI_UNIQUE, 0,op, "You hear manical laughter in the distance."); | | |
} | | |
return success; | | |
} | | |
| | |
| | |
int cast_identify(object *op) { | | |
object *tmp; | | |
int success = 0, random_val=0; | | |
int chance = 8 + op->stats.luck + op->stats.Wis; | | |
| | |
if (chance < 1) | | |
chance = 1; | | |
| | |
for (tmp = op->inv; tmp ; tmp = tmp->below) | | |
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && | | |
need_identify(tmp)) | | |
{ | | |
identify(tmp); | | |
if (op->type==PLAYER) { | | |
| | |
new_draw_info_format(NDI_UNIQUE, 0, op, | | |
"You have %s.", long_desc(tmp, op)); | | |
if (tmp->msg) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:"); | | |
new_draw_info(NDI_UNIQUE, 0,op, tmp->msg); | | |
} | | |
} | | |
| | |
if ((random_val=random_roll(0, chance-1, op, PREFER_LOW)) > | | |
(chance - ++success - 2)) | | |
break; | | |
} | | |
/* If all the power of the spell has been used up, don't go and identify | | |
* stuff on the floor. Only identify stuff on the floor if the spell | | |
* was not fully used. | | |
*/ | | |
if (random_val<=chance - success -2) { | | |
for(tmp = get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) | | |
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && | | |
need_identify(tmp)) | | |
{ | | |
identify(tmp); | | |
if (op->type==PLAYER) { | | |
new_draw_info_format(NDI_UNIQUE, 0,op, | | |
"On the ground is %s.", long_desc(tmp, op)); | | |
if (tmp->msg) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:"); | | |
new_draw_info(NDI_UNIQUE, 0,op, tmp->msg); | | |
} | | |
esrv_send_item(op, tmp); | | |
} | | |
if (random_roll(0, chance-1, op, PREFER_LOW) > (chance - ++success - 2)) | | |
break; | | |
} | | |
} | | |
if (!success) | | |
new_draw_info(NDI_UNIQUE, 0,op, "You can't reach anything unidentified."); | | |
else { | | |
spell_effect(SP_IDENTIFY, op->x, op->y, op->map, op); | | |
} | | |
return success; | | |
} | | |
| | |
int cast_detection(object *op, int type) { | | |
object *tmp, *last=NULL; | | |
int x,y,done_one,nx,ny; | | |
archetype *detect_arch; | | |
mapstruct *m; | | |
| | |
detect_arch = find_archetype("detect_magic"); | | |
if (detect_arch == (archetype *) NULL) { | | |
LOG(llevError, "Couldn't find archetype detect_magic.\n"); | | |
return 0; | | |
} | | |
| | |
/* this size should really be based on level or spell parameter - | | |
* even if out of the view, the setting of these values can be useful | | |
* simply so that when you come across them they have already been | | |
* set for you. | | |
*/ | | |
for (x = op->x - MAP_CLIENT_X/2; x <= op->x + MAP_CLIENT_X/2; x++) | | |
for (y = op->y - MAP_CLIENT_Y/2; y <= op->y + MAP_CLIENT_Y/2; y++) { | | |
| | |
if (out_of_map(op->map, x, y)) | | |
continue; | | |
| | |
done_one = 0; | | |
nx=x; | | |
ny=y; | | |
m = get_map_from_coord(op->map, &nx, &ny); | | |
| | |
/* Add some logic here to only examine objects above the floor. | | |
* This should not be done for show invis (may want to show | | |
* invisible floor objects) - may be others also. | | |
*/ | | |
| | |
if (type==SP_SHOW_INVIS) last = get_map_ob(m, nx, ny); | | |
else { | | |
for (tmp=get_map_ob(m, nx, ny); tmp; tmp=tmp->above) last=tmp; | | |
| | |
for (tmp=last; tmp; tmp=tmp->below) { | | |
if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) break; | | |
else last=tmp; | | |
} | | |
| | |
if (tmp) last=tmp->above; | | |
} | | |
| | |
/* detect magic and detect curse need to set flags on all the items. for | | |
* the other detect spells, we only care if there is one of the matching object | | |
* on that face. | | |
*/ | | |
for (tmp = last; | | |
tmp && (!done_one || type==SP_DETECT_MAGIC || type==SP_DETECT_CURSE); | | |
tmp=tmp->above) { | | |
| | |
switch(type) { | | |
| | |
case SP_DETECT_MAGIC: | | |
if (!QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(tmp, FLAG_IDENTIFIED) && | | |
is_magical(tmp)) { | | |
| | |
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); | | |
/* peterm: make magical runes more visible*/ | | |
if(tmp->type==RUNE && tmp->attacktype&AT_MAGIC) | | |
tmp->stats.Cha/=4; | | |
done_one = 1; | | |
} | | |
break; | | |
| | |
case SP_DETECT_MONSTER: | | |
/* lets not care about who is casting the spell - everyone on the | | |
* map sees the results anyways, | | |
*/ | | |
if ((QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type==PLAYER)) { | | |
done_one = 2; | | |
last=tmp; | | |
} | | |
break; | | |
| | |
| | |
case SP_DETECT_EVIL: | | |
done_one = 0; | | |
if(QUERY_FLAG(tmp,FLAG_MONSTER) && tmp->race) { | | |
object *god=find_god(determine_god(op)); | | |
if(god && god->slaying && strstr(god->slaying,tmp->race)) { | | |
last=tmp; | | |
done_one = 2; | | |
} | | |
} | | |
break; | | |
| | |
| | |
case SP_SHOW_INVIS: | | |
/* Might there be other objects that we can make visibile? */ | | |
if (tmp->invisible && (QUERY_FLAG(tmp, FLAG_MONSTER) || | | |
(tmp->type==PLAYER && !QUERY_FLAG(tmp, FLAG_WIZ)) || | | |
tmp->type==CF_HANDLE || | | |
tmp->type==TRAPDOOR || tmp->type==EXIT || tmp->type==HOLE || | | |
tmp->type==BUTTON || tmp->type==TELEPORTER || | | |
tmp->type==GATE || tmp->type==LOCKED_DOOR || | | |
tmp->type==WEAPON || tmp->type==ALTAR || tmp->type==SIGN || | | |
tmp->type==TRIGGER_PEDESTAL || tmp->type==SPECIAL_KEY || | | |
tmp->type==TREASURE || tmp->type==BOOK || | | |
tmp->type==HOLY_ALTAR)) { | | |
if(random_roll(0, SK_level(op)-1, op, PREFER_HIGH) > | | |
tmp->level/4) { | | |
tmp->invisible=0; | | |
done_one = 1; | | |
} | | |
} | | |
break; | | |
| | |
case SP_DETECT_CURSE: | | |
if (!QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && | | |
(QUERY_FLAG(tmp, FLAG_CURSED) || | | |
QUERY_FLAG(tmp, FLAG_DAMNED))) { | | |
SET_FLAG(tmp, FLAG_KNOWN_CURSED); | | |
done_one = 1; | | |
} | | |
break; | | |
| | |
} /* end of switch statement */ | | |
} /* Done all the object on this square */ | | |
| | |
if (done_one) { | | |
object *detect_ob = arch_to_object(detect_arch); | | |
detect_ob->x = x; | | |
detect_ob->y = y; | | |
/* if this is set, we want to copy the face */ | | |
if (done_one == 2 && last) { | | |
detect_ob->face = last->face; | | |
detect_ob->animation_id = last->animation_id; | | |
detect_ob->anim_speed = last->anim_speed; | | |
detect_ob->last_anim=0; | | |
/* by default, the detect_ob is already animated */ | | |
if (!QUERY_FLAG(last, FLAG_ANIMATE)) CLEAR_FLAG(detect_ob, FLAG_ANIMATE); | | |
} | | |
insert_ob_in_map(detect_ob, op->map, op,0); | | |
} | | |
} /* for processing the surrounding spaces */ | | |
| | |
/* Now process objects in the players inventory if detect curse or magic */ | | |
if ((type == SP_DETECT_MAGIC || type == SP_DETECT_CURSE) && op->type == PLAYER) { | | |
done_one = 0; | | |
for (tmp = op->inv; tmp; tmp = tmp->below) { | | |
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)) | | |
| | |
switch(type) { | | |
case SP_DETECT_MAGIC: | | |
if (is_magical(tmp) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)) { | | |
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); | | |
esrv_send_item (op, tmp); | | |
done_one = 1; | | |
} | | |
break; | | |
| | |
case SP_DETECT_CURSE: | | |
if (!QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && | | |
(QUERY_FLAG(tmp, FLAG_CURSED) || | | |
QUERY_FLAG(tmp, FLAG_DAMNED))) { | | |
SET_FLAG(tmp, FLAG_KNOWN_CURSED); | | |
esrv_send_item (op, tmp); | | |
done_one = 1; | | |
} | | |
break; | | |
} /* end of switch */ | | |
} /* for the players inventory */ | | |
} /* if detect magic/curse and object is a player */ | | |
return 1; | | |
} | | } |
| | |
/* Shamelessly hacked from PeterM's cast_charm and destruction code | | /* Turn 25 small nuggets into 1 large nugget. If the value |
* - b.t. thomas@nomad.astro.psu.edu | | * of large nuggets is not evenly divisable by the small nugget |
*/ | | * value, take off an extra small_nugget (Assuming small_nuggets!=0) |
| | |
/* Changes in the spell code to make it more powerfull - now it can | | |
* pacify multi-square creatures, and has greater range - Aug 95 b.t. | | |
*/ | | |
/* New modification -- w/ Multigod hack, now if its a member of an aligned | | |
* race, we automatically pacify it. b.t. | | |
*/ | | */ |
| | if (*small_nuggets * small->value >= large->value) { |
int cast_pacify(object *op, object *weap, archetype *arch,int spellnum ) { | | (*large_nuggets)++; |
int i,r,j; | | *small_nuggets -= large->value / small->value; |
object *tmp,*effect; | | if (*small_nuggets && large->value % small->value) |
object *god = find_god(determine_god(op)); | | (*small_nuggets)--; |
| | |
r= 1 + SP_level_strength_adjust(op,weap,SP_PACIFY); | | |
| | |
for(i= -r;i<r;i++) | | |
for(j= -r;j<r;j++) { | | |
if(out_of_map(op->map,op->x+i,op->y+j)) | | |
continue; | | |
for(tmp=get_map_ob(op->map,op->x+i,op->y+j); | | |
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above); | | |
if(!tmp) continue; | | |
if(tmp->type==PLAYER) continue; | | |
| | |
/* we only go through checking if the monster is not aligned | | |
member, we dont worship a god, or monster has no race */ | | |
if(!tmp->race||!god||!god->race | | |
||!strstr(god->race,tmp->race)) { | | |
| | |
if(tmp->resist[ATNR_MAGIC]==100||tmp->resist[ATNR_GODPOWER]==100) continue; | | |
/* multiple square monsters only when caster is => level of creature */ | | |
if((tmp->more || tmp->head) && (SK_level(op) < tmp->level)) continue; | | |
if(weap->slaying) /* selective pacify */ | | |
if(tmp->race != weap->slaying && tmp->name != weap->slaying) continue; | | |
/* if(op->level <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue; */ | | |
if(SK_level(op) < random_roll(0, 2*tmp->level, op, PREFER_LOW)-(op->stats.Cha-10)/2) continue; | | |
} | | |
| | |
if((effect=get_archetype("detect_magic"))){ | | |
effect->x = tmp->x; | | |
effect->y = tmp->y; | | |
insert_ob_in_map(effect,tmp->map,op,0); | | |
} | | |
SET_FLAG(tmp,FLAG_UNAGGRESSIVE); | | |
} | | } |
| | weight += obj->weight; |
return 1; | | remove_ob(obj); |
| | free_object(obj); |
} | | } |
| | |
/* summon fog code. I could'nt decide whether this | | static void update_map(object *op, mapstruct *m, int small_nuggets, int large_nuggets, |
* could just go into another routine (like create_ | | int x, int y) |
* the_feature) or have it alone. For now, its separate | | { |
* function. This code just creates a variable amount of | | |
* fog archetypes around the character. | | |
* Implementation by b.t. (thomas@nomad.astro.psu.edu) | | |
* (based on create bomb code) | | |
*/ | | |
| | |
int summon_fog(object *op, object *caster,int dir,int spellnum) { | | |
object *tmp; | | object *tmp; |
int i,dx=op->x+freearr_x[dir],dy=op->y+freearr_y[dir]; | | int flag=0; |
| | |
if (!spellarch[spellnum]) | | /* Put any nuggets below the player, but we can only pass this |
return 0; | | * flag if we are on the same space as the player |
| | */ |
| | if (x == op->x && y == op->y && op->map == m) flag = INS_BELOW_ORIGINATOR; |
| | |
for(i=1;i<MIN(2+SP_level_strength_adjust(op,caster,spellnum),SIZEOFFREE);i++) { | | if (small_nuggets) { |
if(get_map_flags(op->map,NULL, dx,dy,NULL,NULL) & (P_OUT_OF_MAP | P_WALL)) { | | tmp = get_object(); |
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way."); | | copy_object(small, tmp); |
return 0; | | tmp-> nrof = small_nuggets; |
| | tmp->x = x; |
| | tmp->y = y; |
| | insert_ob_in_map(tmp, m, op, flag); |
} | | } |
tmp=get_archetype(spellarch[spellnum]->name); | | if (large_nuggets) { |
tmp->x=dx,tmp->y=dy; /* all fog starts in 1 place */ | | tmp = get_object(); |
#ifdef WALL_CREDIT /* does someone get exp for the kills? */ | | copy_object(large, tmp); |
set_owner(tmp,op); /* note however, that after 'fog' moves */ | | tmp-> nrof = large_nuggets; |
/* it is no longer owned. It is unlikely */ | | tmp->x = x; |
/* that players will garner much exp with */ | | tmp->y = y; |
/* this spell */ | | insert_ob_in_map(tmp, m, op, flag); |
#endif | | |
insert_ob_in_map(tmp,op->map,op,0); | | |
} | | } |
return 1; | | |
} | | } |
| | |
/* create_the_feature: peterm */ | | int alchemy(object *op, object *caster, object *spell_ob) |
/* implementation of the spells which build directors, lightning | | |
walls, bullet walls, and fireballwalls. */ | | |
| | |
int create_the_feature(object *op, object *caster,int dir, int spell_effect) | | |
{ | | { |
object *tmp=NULL; | | int x,y,weight=0,weight_max,large_nuggets,small_nuggets, mflags; |
char buf1[20]; | | sint16 nx, ny; |
sint16 x, y; | | object *next,*tmp; |
mapstruct *m; | | mapstruct *mp; |
| | |
| | |
if(!dir) { | | |
dir=op->facing; | | |
x = op->x; | | |
y = op->y; | | |
} | | |
else { | | |
x = op->x+freearr_x[dir]; | | |
y = op->y+freearr_y[dir]; | | |
} | | |
| | |
m = op->map; | | if(op->type!=PLAYER) |
| | return 0; |
| | |
/* Basically, if player is casting build directory, they can cast it on | | /* Put a maximum weight of items that can be alchemied. Limits the power |
* the space they are standing, so don't check for blocked in that | | * some, and also prevents people from alcheming every table/chair/clock |
* case (we'll also presume that the fact that the player is on a space | | * in sight |
* means he's withing the map). Always check the map if we're not | | |
* doing a directory, or if the target space for the feature does not | | |
* match the space the player (op) is on. | | |
*/ | | */ |
if ((spell_effect != SP_BUILD_DIRECTOR || x != op->x || y != op->y) && | | weight_max = spell_ob->duration + +SP_level_duration_adjust(caster,spell_ob); |
(get_map_flags(m, &m, x, y, &x, &y) & (P_BLOCKED | P_OUT_OF_MAP))) { | | weight_max *= 1000; |
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way."); | | small=get_archetype("smallnugget"), |
return 0; | | large=get_archetype("largenugget"); |
} | | |
| | |
switch(spell_effect) { | | for(y= op->y-1;y<=op->y+1;y++) { |
case SP_BUILD_DIRECTOR: | | for(x= op->x-1;x<=op->x+1;x++) { |
sprintf(buf1,"director_%d",dir); | | nx = x; |
tmp=get_archetype(buf1); | | ny = y; |
SET_FLAG(tmp, FLAG_IS_USED_UP); | | |
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,caster,spell_effect); | | |
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,caster,spell_effect); | | |
tmp->stats.maxhp=tmp->stats.hp; | | |
break; | | |
| | |
case SP_BUILD_LWALL: | | mp = op->map; |
sprintf(buf1,"lightningwall_%d",dir); | | |
tmp=get_archetype(buf1); | | |
SET_FLAG(tmp, FLAG_IS_USED_UP); | | |
SET_FLAG(tmp, FLAG_TEAR_DOWN); | | |
SET_FLAG(tmp, FLAG_ALIVE); | | |
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,caster,spell_effect); | | |
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,caster,spell_effect); | | |
tmp->stats.maxhp=tmp->stats.hp; | | |
break; | | |
| | |
case SP_BUILD_BWALL: | | mflags = get_map_flags(mp, &mp, nx, ny, &nx, &ny); |
sprintf(buf1,"lbulletwall_%d",dir); | | |
tmp=get_archetype(buf1); | | |
SET_FLAG(tmp, FLAG_IS_USED_UP); | | |
SET_FLAG(tmp, FLAG_TEAR_DOWN); | | |
SET_FLAG(tmp, FLAG_ALIVE); | | |
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,caster,spell_effect); | | |
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,caster,spell_effect); | | |
tmp->stats.maxhp=tmp->stats.hp; | | |
break; | | |
| | |
case SP_BUILD_FWALL: | | if(mflags & (P_OUT_OF_MAP | P_WALL | P_NO_MAGIC)) |
sprintf(buf1,"firewall_%d",dir); | | continue; |
tmp=get_archetype(buf1); | | |
SET_FLAG(tmp, FLAG_IS_USED_UP); | | |
SET_FLAG(tmp, FLAG_TEAR_DOWN); | | |
SET_FLAG(tmp, FLAG_ALIVE); | | |
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,caster,spell_effect); | | |
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,caster,spell_effect); | | |
tmp->stats.maxhp=tmp->stats.hp; | | |
break; | | |
} | | |
| | |
#ifdef WALL_CREDIT | | small_nuggets=0; |
set_owner(tmp,op); | | large_nuggets=0; |
#endif /* determines whether someone gets credit for kills. */ | | |
| | |
tmp->level=SK_level(op)/2; /* so that the spell that the wall casts | | for(tmp=get_map_ob(mp,nx,ny);tmp!=NULL;tmp=next) { |
inherit part of the effectiveness of | | next=tmp->above; |
of the wall builder */ | | if (tmp->weight>0 && !QUERY_FLAG(tmp, FLAG_NO_PICK) && |
tmp->x=x; | | !QUERY_FLAG(tmp, FLAG_ALIVE) && |
tmp->y=y; | | !QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) { |
| | |
if ((tmp = insert_ob_in_map (tmp, m, op,0)) == NULL) | | if (tmp->inv) { |
return 1; | | object *next1, *tmp1; |
if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW)) | | for (tmp1 = tmp->inv; tmp1!=NULL; tmp1=next1) { |
update_all_los(op->map, op->x, op->y); | | next1 = tmp1->below; |
if(op->type!=PLAYER) | | if (tmp1->weight>0 && !QUERY_FLAG(tmp1, FLAG_NO_PICK) && |
SET_FLAG(op, FLAG_SCARED); /* We don't want them to walk through the wall! */ | | !QUERY_FLAG(tmp1, FLAG_ALIVE) && |
return 1; | | !QUERY_FLAG(tmp1, FLAG_IS_CAULDRON)) |
| | alchemy_object(tmp1, &small_nuggets, &large_nuggets, |
| | &weight); |
} | | } |
| | |
| | |
/* cast_transfer: peterm */ | | |
/* following spell transfers mana from one person to another. | | |
right now, it's no respecter of maximum sp limits. WOn't | | |
fix that, regard it as a feature. be nice to make someone's | | |
head explode if they supercharge too much, though. */ | | |
| | |
int cast_transfer(object *op,int dir) { | | |
object *plyr=NULL; | | |
/* see if we can find someone to give sp to. */ | | |
if (!out_of_map(op->map, op->x+freearr_x[dir], op->y+freearr_y[dir])) { | | |
for(plyr=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]); | | |
plyr!=NULL; plyr=plyr->above) | | |
if(QUERY_FLAG(plyr, FLAG_ALIVE)) | | |
break; | | |
} | | } |
/* If we did not find a player in the specified direction, transfer | | alchemy_object(tmp, &small_nuggets, &large_nuggets, &weight); |
to anyone on top of us. */ | | |
if(plyr==NULL) | | |
for(plyr=get_map_ob(op->map,op->x,op->y); plyr!=NULL; plyr=plyr->above) | | |
if(QUERY_FLAG(plyr,FLAG_ALIVE)) | | |
break; | | |
| | |
if(plyr) { | | |
/* DAMN: added spell strength adjust; higher level casters transfer mana faster */ | | |
int maxsp=plyr->stats.maxsp; | | |
int sp=(plyr->stats.sp + SP_PARAMETERS[SP_TRANSFER].bdam | | |
+ SP_level_dam_adjust(op,op,SP_TRANSFER)); | | |
| | |
plyr->stats.sp = sp; | | |
| | |
new_draw_info(NDI_UNIQUE, 0,plyr,"You feel energy course through you."); | | if (weight>weight_max) { |
if(sp>=maxsp*2) { | | update_map(op, mp, small_nuggets, large_nuggets, nx, ny); |
new_draw_info(NDI_UNIQUE, 0,plyr,"Your head explodes!"); | | free_object(large); |
fire_arch (op, plyr,0, spellarch[SP_L_FIREBALL], SP_L_FIREBALL, 0); | | free_object(small); |
/* Explodes a large fireball centered at player */ | | return 1; |
/* hit_player(plyr, 9998, op, AT_PHYSICAL);*/ | | } |
plyr->stats.sp = 2*maxsp; | | } /* is alchemable object */ |
| | } /* process all objects on this space */ |
| | |
| | /* Insert all the nuggets at one time. This probably saves time, but |
| | * it also prevents us from alcheming nuggets that were just created |
| | * with this spell. |
| | */ |
| | update_map(op, mp, small_nuggets, large_nuggets, nx, ny); |
} | | } |
else if(sp>=maxsp*1.88) | | |
new_draw_info(NDI_UNIQUE, NDI_ORANGE,plyr,"You feel like your head is going to explode."); | | |
else if(sp>=maxsp*1.66) | | |
new_draw_info(NDI_UNIQUE, 0,plyr, "You get a splitting headache!"); | | |
else if(sp>=maxsp*1.5) { | | |
new_draw_info(NDI_UNIQUE, 0,plyr,"CHaOs fills your world."); | | |
confuse_player(op,op,99); | | |
} | | } |
else if(sp>=maxsp*1.25) | | free_object(large); |
new_draw_info(NDI_UNIQUE, 0,plyr,"You start hearing voices."); | | free_object(small); |
| | /* reset this so that if player standing on a big pile of stuff, |
| | * it is redrawn properly. |
| | */ |
| | op->contr->socket.look_position = 0; |
return 1; | | return 1; |
} | | } |
else return 0; | | |
} | | |
| | |
/* drain_magic: peterm */ | | |
/* drains all the magic out of the victim. */ | | |
int drain_magic(object *op,int dir) { | | |
object *tmp=NULL; | | |
double mana, rate; | | |
| | |
if (!out_of_map(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) { | | |
for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]); | | |
tmp!=NULL; | | |
tmp=tmp->above) | | |
if(QUERY_FLAG(tmp, FLAG_ALIVE)) | | |
break; | | |
/* If we did not find a player in the specified direction, transfer | | |
to anyone on top of us. */ | | |
} | | |
| | |
if(tmp==NULL) | | /* This function removes the cursed/damned status on equipped |
for(tmp=get_map_ob(op->map,op->x,op->y); tmp!=NULL; tmp=tmp->above) | | * items. |
if(QUERY_FLAG(tmp, FLAG_ALIVE)) | | */ |
break; | | int remove_curse(object *op, object *caster, object *spell) { |
| | object *tmp; |
| | int success = 0, was_one = 0; |
| | |
/* DAMN: Percent spell point loss determined by caster level | | for (tmp = op->inv; tmp; tmp = tmp->below) |
Caster gains percent of drained mana, also determined by caster level */ | | if (QUERY_FLAG(tmp, FLAG_APPLIED) && |
if(tmp&&op!=tmp) { | | ((QUERY_FLAG(tmp, FLAG_CURSED) && QUERY_FLAG(spell, FLAG_CURSED)) || |
rate = (double)(SP_PARAMETERS[SP_MAGIC_DRAIN].bdam | | (QUERY_FLAG(tmp, FLAG_DAMNED) && QUERY_FLAG(spell, FLAG_DAMNED)))) { |
+ 5* SP_level_dam_adjust(op,op,SP_MAGIC_DRAIN)) / 100.0; | | |
if(rate > 0.95) rate = 0.95; | | was_one++; |
mana = tmp->stats.sp * rate; | | if (tmp->level <= caster_level(caster, spell)) { |
tmp->stats.sp -= mana; | | success++; |
if(QUERY_FLAG(op, FLAG_ALIVE)) { | | if (QUERY_FLAG(spell, FLAG_DAMNED)) |
rate = (double)(SP_PARAMETERS[SP_MAGIC_DRAIN].bdam | | CLEAR_FLAG(tmp, FLAG_DAMNED); |
+ 5* SP_level_strength_adjust(op,op,SP_MAGIC_DRAIN)) / 100.0; | | |
if(rate > 0.95) rate = 0.95; | | CLEAR_FLAG(tmp, FLAG_CURSED); |
mana = mana * rate; | | CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED); |
op->stats.sp += mana; | | tmp->value = 0; /* Still can't sell it */ |
| | if (op->type == PLAYER) |
| | esrv_send_item(op, tmp); |
} | | } |
return 1; | | } |
| | |
| | if (op->type==PLAYER) { |
| | if (success) { |
| | new_draw_info(NDI_UNIQUE, 0,op, "You feel like some of your items are looser now."); |
} else { | | } else { |
return 0; | | if (was_one) |
| | new_draw_info(NDI_UNIQUE, 0,op, "You failed to remove the curse."); |
| | else |
| | new_draw_info(NDI_UNIQUE, 0,op, "You are not using any cursed items."); |
} | | } |
} | | } |
| | return success; |
| | } |
| | |
/* counterspell: peterm */ | | /* Identifies objects in the players inventory/on the ground */ |
/* an object of type counterspell will nullify cone objects, | | |
explosion objects, and anything else that |=magic. */ | | |
| | |
void counterspell(object *op,int dir) | | |
{ | | |
object *tmp, *head, *next; | | |
int mflags; | | |
mapstruct *m; | | |
sint16 sx,sy; | | |
| | |
sx = op->x + freearr_x[dir]; | | |
sy = op->y+freearr_y[dir]; | | |
m = op->map; | | |
mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); | | |
if (mflags & P_OUT_OF_MAP) return; | | |
| | |
for(tmp=get_map_ob(m,sx,sy); tmp!=NULL; tmp=next) { | | int cast_identify(object *op, object *caster, object *spell) { |
next = tmp->above; | | object *tmp; |
| | int success = 0, num_ident; |
| | |
/* Need to look at the head object - otherwise, if tmp | | num_ident = spell->stats.dam + SP_level_dam_adjust(caster, spell); |
* points to a monster, we don't have all the necessary | | |
* info for it. | | |
*/ | | |
if (tmp->head) head = tmp->head; | | |
else head = tmp; | | |
| | |
/* don't attack our own spells */ | | if (num_ident < 1) num_ident=1; |
if(tmp->owner && tmp->owner == op->owner) continue; | | |
| | |
/* Basically, if the object is magical and not counterspell, | | |
* we will more or less remove the object. Don't counterspell | | |
* monsters either. Not sure what the material==0 is for, | | |
* since some pickable objects don't have a material. | | |
* I really wonder if it would just be better to let | | |
* that head->type catch everything. | | |
*/ | | |
| | |
if(head->material==0 && head->attacktype&AT_MAGIC && | | for (tmp = op->inv; tmp ; tmp = tmp->below) { |
!(head->attacktype&AT_COUNTERSPELL) && | | if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && need_identify(tmp)) { |
!QUERY_FLAG(head,FLAG_MONSTER)) { | | identify(tmp); |
if(SK_level(op) > head->level) { | | if (op->type==PLAYER) { |
remove_ob(head); | | new_draw_info_format(NDI_UNIQUE, 0, op, |
free_object(head); | | "You have %s.", long_desc(tmp, op)); |
| | if (tmp->msg) { |
| | new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:"); |
| | new_draw_info(NDI_UNIQUE, 0,op, tmp->msg); |
} | | } |
} else switch(head->type) { | | |
case CONE: | | |
case FBALL: | | |
case LIGHTNING: | | |
case FBULLET: | | |
case MMISSILE: | | |
case SPEEDBALL: | | |
case BOMB: | | |
case POISONCLOUD: | | |
case CANCELLATION: | | |
case SWARM_SPELL: | | |
case BALL_LIGHTNING: | | |
if(SK_level(op) > head->level) { | | |
remove_ob(head); | | |
free_object(head); | | |
} | | } |
break; | | num_ident--; |
| | success=1; |
| | if (!num_ident) break; |
| | } |
| | } |
| | /* If all the power of the spell has been used up, don't go and identify |
| | * stuff on the floor. Only identify stuff on the floor if the spell |
| | * was not fully used. |
| | */ |
| | if (num_ident) { |
| | for(tmp = get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) |
| | if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && |
| | need_identify(tmp)) { |
| | |
case RUNE: | | identify(tmp); |
if(rndm(0, 149) == 0) { | | if (op->type==PLAYER) { |
head->stats.hp--; /* weaken the rune */ | | new_draw_info_format(NDI_UNIQUE, 0,op, |
if(!head->stats.hp) { | | "On the ground is %s.", long_desc(tmp, op)); |
remove_ob(head); | | if (tmp->msg) { |
free_object(head); | | new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:"); |
| | new_draw_info(NDI_UNIQUE, 0,op, tmp->msg); |
| | } |
| | esrv_send_item(op, tmp); |
} | | } |
| | num_ident--; |
| | success=1; |
| | if (!num_ident) break; |
} | | } |
break; | | |
} | | } |
| | if (!success) |
| | new_draw_info(NDI_UNIQUE, 0,op, "You can't reach anything unidentified."); |
| | else { |
| | spell_effect(spell, op->x, op->y, op->map, op); |
} | | } |
| | return success; |
} | | } |
| | |
| | |
| | int cast_detection(object *op, object *caster, object *spell) { |
| | object *tmp, *last, *god, *detect; |
| | int done_one, range, mflags, floor, level; |
| | sint16 x, y; |
| | mapstruct *m; |
| | |
/* peterm: function which summons hostile monsters and | | /* We precompute some values here so that we don't have to keep |
places them in nearby squares. */ | | * doing it over and over again. |
| | */ |
| | god=find_god(determine_god(op)); |
| | level=caster_level(caster, spell); |
| | range = spell->range + SP_level_range_adjust(caster, spell); |
| | |
int summon_hostile_monsters(object *op,int n,char *monstername){ | | for (x = op->x - range; x <= op->x + range; x++) |
int i; | | for (y = op->y - range; y <= op->y + range; y++) { |
for(i=0;i<n;i++) | | |
put_a_monster(op,monstername); | | |
return n; | | |
} | | |
| | |
| | m = op->map; |
| | mflags = get_map_flags(m, &m, x, y, &x, &y); |
| | if (mflags & P_OUT_OF_MAP) continue; |
| | |
/* charm spell by peterm@soda.berkeley.edu | | /* For most of the detections, we only detect objects above the |
searches nearby squares for monsters to charm. Each of them | | * floor. But this is not true for show invisible. |
is subject to being charmed, that is, to becoming the pet | | * Basically, we just go and find the top object and work |
monster of the caster. Monsters larger than 1 square | | * down - that is easier than working up. |
are uncharmeable right now. */ | | */ |
| | |
/* Aug 95 - hack on the code to make it charm only undead for | | for (last=NULL, tmp=get_map_ob(m, x, y); tmp; tmp=tmp->above) last=tmp; |
* priests, and not charm undead for magicians -b.t | | /* Shouldn't happen, but if there are no objects on a space, this |
| | * would happen. |
*/ | | */ |
| | if (!last) continue; |
| | |
int cast_charm(object *op, object *caster,archetype *arch,int spellnum) { | | done_one=0; |
int i; | | floor=0; |
object *tmp,*effect; | | detect = NULL; |
| | for (tmp=last; tmp; tmp=tmp->below) { |
| | |
if (op->type != PLAYER) | | /* show invisible */ |
return 0; | | if (QUERY_FLAG(spell, FLAG_MAKE_INVIS) && |
for(i=1;i<MIN(9+SP_level_strength_adjust(op,caster,spellnum),SIZEOFFREE);i++) { | | /* Might there be other objects that we can make visibile? */ |
if (out_of_map(op->map,op->x+freearr_x[i],op->y+freearr_y[i])) | | (tmp->invisible && (QUERY_FLAG(tmp, FLAG_MONSTER) || |
continue; | | (tmp->type==PLAYER && !QUERY_FLAG(tmp, FLAG_WIZ)) || |
for(tmp=get_map_ob(op->map,op->x+freearr_x[i],op->y+freearr_y[i]); | | tmp->type==CF_HANDLE || |
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above); | | tmp->type==TRAPDOOR || tmp->type==EXIT || tmp->type==HOLE || |
if(!tmp) continue; | | tmp->type==BUTTON || tmp->type==TELEPORTER || |
if(tmp->type==PLAYER) continue; | | tmp->type==GATE || tmp->type==LOCKED_DOOR || |
if(tmp->resist[ATNR_MAGIC]==100) continue; | | tmp->type==WEAPON || tmp->type==ALTAR || tmp->type==SIGN || |
if(QUERY_FLAG(tmp,FLAG_UNDEAD)) continue; | | tmp->type==TRIGGER_PEDESTAL || tmp->type==SPECIAL_KEY || |
if(tmp->more || tmp->head) continue; /* multiple square monsters NOT */ | | tmp->type==TREASURE || tmp->type==BOOK || |
/* if(op->level <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue; */ | | tmp->type==HOLY_ALTAR))) { |
if(SK_level(op) <random_roll(0, 2*tmp->level, op, PREFER_LOW)-(op->stats.Cha-10)/2) continue; | | if(random_roll(0, op->chosen_skill->level-1, op, PREFER_HIGH) > level/4) { |
| | tmp->invisible=0; |
if((effect=get_archetype("detect_magic"))){ | | done_one = 1; |
effect->x = tmp->x; | | |
effect->y = tmp->y; | | |
insert_ob_in_map(effect,tmp->map,op,0); | | |
} | | |
set_owner(tmp,op); | | |
SET_FLAG(tmp,FLAG_MONSTER); | | |
SET_FLAG(tmp,FLAG_FRIENDLY); | | |
add_friendly_object (tmp); | | |
tmp->stats.exp = 0; | | |
tmp->move_type = PETMOVE; | | |
} | | } |
return 1; | | |
} | | } |
| | if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) floor=1; |
| | |
int cast_charm_undead(object *op, object *caster,archetype *arch,int spellnum) { | | /* All detections below this point don't descend beneath the floor, |
int i,bonus; | | * so just continue on. We could be clever and look at the type of |
object *tmp,*effect, *god=find_god(determine_god(op)); | | * detection to completely break out if we don't care about objects beneath |
| | * the floor, but once we get to the floor, not likely a very big issue anyways. |
if (op->type != PLAYER) | | */ |
return 0; | | if (floor) continue; |
if (QUERY_FLAG(caster,FLAG_UNDEAD) || (god->race && strstr(god->race,undead_name)!=NULL)) { | | |
bonus = 5; | | /* I had thought about making detect magic and detect curse |
} else if (god->slaying && strstr(god->slaying,undead_name)!=NULL) { | | * show the flash the magic item like it does for detect monster. |
bonus = -5; | | * however, if the object is within sight, this would then make it |
} else { | | * difficult to see what object is magical/cursed, so the |
bonus = -1; | | * effect wouldn't be as apparant. |
| | */ |
| | |
| | /* detect magic */ |
| | if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) && |
| | !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) && |
| | !QUERY_FLAG(tmp, FLAG_IDENTIFIED) && |
| | is_magical(tmp)) { |
| | SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); |
| | /* make runes more visibile */ |
| | if(tmp->type==RUNE && tmp->attacktype&AT_MAGIC) |
| | tmp->stats.Cha/=4; |
| | done_one = 1; |
} | | } |
for(i=1;i<MIN(9+SP_level_strength_adjust(op,caster,spellnum),SIZEOFFREE);i++) { | | /* detect monster */ |
if (out_of_map(op->map,op->x+freearr_x[i],op->y+freearr_y[i])) | | if (QUERY_FLAG(spell, FLAG_MONSTER) && |
continue; | | (QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type==PLAYER)) { |
for(tmp=get_map_ob(op->map,op->x+freearr_x[i],op->y+freearr_y[i]); | | done_one = 2; |
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above); | | if (!detect) detect=tmp; |
if(!tmp) continue; | | |
if(tmp->type==PLAYER) continue; | | |
if(tmp->resist[ATNR_MAGIC]==100) continue; | | |
if(!QUERY_FLAG(tmp,FLAG_UNDEAD)) continue; | | |
if(tmp->more || tmp->head) continue; /* multiple square monsters NOT */ | | |
if(SK_level(op)+bonus < random_roll(0, 2*tmp->level, op, PREFER_LOW)-(op->stats.Wis-10)/2) continue; | | |
| | |
if((effect=get_archetype("detect_magic"))){ | | |
effect->x = tmp->x; | | |
effect->y = tmp->y; | | |
insert_ob_in_map(effect,tmp->map,op,0); | | |
} | | } |
set_owner(tmp,op); | | /* Basically, if race is set in the spell, then the creatures race must |
SET_FLAG(tmp,FLAG_MONSTER); | | * match that. if the spell race is set to GOD, then the gods opposing |
SET_FLAG(tmp,FLAG_FRIENDLY); | | * race must match. |
add_friendly_object (tmp); | | */ |
tmp->stats.exp = 0; | | if (spell->race && QUERY_FLAG(tmp,FLAG_MONSTER) && tmp->race && |
tmp->move_type = PETMOVE; | | ((!strcmp(spell->race, "GOD") && god && god->slaying && strstr(god->slaying,tmp->race)) || |
| | (strstr(spell->race, tmp->race)))) { |
| | done_one = 2; |
| | if (!detect) detect=tmp; |
} | | } |
return 1; | | if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && |
| | (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) { |
| | SET_FLAG(tmp, FLAG_KNOWN_CURSED); |
| | done_one = 1; |
} | | } |
| | } /* for stack of objects on this space */ |
| | |
/* Returns a monster (chosen at random) that this particular player (and his | | /* Code here puts an effect of the spell on the space, so you can see |
* god) find acceptable. This checks level, races allowed by god, etc | | * where the magic is. |
* to determine what is acceptable. | | */ |
* This returns NULL if no match was found. | | if (done_one) { |
*/ | | object *detect_ob = arch_to_object(spell->other_arch); |
| | detect_ob->x = x; |
object *choose_cult_monster(object *pl, object *god, int summon_level) { | | detect_ob->y = y; |
char buf[MAX_BUF],*race; | | /* if this is set, we want to copy the face */ |
int racenr, mon_nr,i; | | if (done_one == 2 && detect) { |
racelink *list; | | detect_ob->face = detect->face; |
objectlink *tobl; | | detect_ob->animation_id = detect->animation_id; |
object *otmp; | | detect_ob->anim_speed = detect->anim_speed; |
| | detect_ob->last_anim=0; |
/* Determine the number of races available */ | | /* by default, the detect_ob is already animated */ |
racenr=0; | | if (!QUERY_FLAG(detect, FLAG_ANIMATE)) CLEAR_FLAG(detect_ob, FLAG_ANIMATE); |
strcpy(buf,god->race); | | } |
race = strtok(buf,","); | | insert_ob_in_map(detect_ob, op->map, op,0); |
while(race) { | | |
racenr++; | | |
race = strtok(NULL,","); | | |
} | | |
| | |
/* next, randomly select a race from the aligned_races string */ | | |
if(racenr>1) { | | |
racenr = rndm(0, racenr-1); | | |
strcpy(buf,god->race); | | |
race = strtok(buf,","); | | |
for(i=0;i<racenr;i++) | | |
race = strtok(NULL,","); | | |
} else | | |
race = god->race; | | |
| | |
| | |
/* see if a we can match a race list of monsters. This should not | | |
* happen, so graceful recovery isn't really needed, but this sanity | | |
* checking is good for cases where the god archetypes mismatch the | | |
* race file | | |
*/ | | |
if((list=find_racelink(race))==NULL) { | | |
new_draw_info_format(NDI_UNIQUE, 0,pl, | | |
"The spell fails! %s's creatures are beyond",god->name); | | |
new_draw_info(NDI_UNIQUE, 0,pl, | | |
"the range of your summons."); | | |
LOG(llevDebug,"choose_cult_monster() requested non-existant aligned race!\n"); | | |
return 0; | | |
} | | |
| | |
/* search for an apprplritate monster on this race list */ | | |
mon_nr=0; | | |
for(tobl=list->member;tobl;tobl=tobl->next) { | | |
otmp=tobl->ob; | | |
if(!otmp||!QUERY_FLAG(otmp,FLAG_MONSTER)) continue; | | |
if(otmp->level<=summon_level) mon_nr++; | | |
} | | |
/* If this god has multiple race entries, we should really choose another. | | |
* But then we either need to track which ones we have tried, or just | | |
* make so many calls to this function, and if we get so many without | | |
* a valid entry, assuming nothing is available and quit. | | |
*/ | | |
if (!mon_nr) return NULL; | | |
mon_nr = rndm(0, mon_nr-1); | | |
for(tobl=list->member;tobl;tobl=tobl->next) { | | |
otmp=tobl->ob; | | |
if(!otmp||!QUERY_FLAG(otmp,FLAG_MONSTER)) continue; | | |
if(otmp->level<=summon_level && !mon_nr--) return otmp; | | |
} | | |
/* This should not happen */ | | |
LOG(llevDebug,"choose_cult_monster() mon_nr was set, but did not find a monster\n"); | | |
return NULL; | | |
} | | |
| | |
| | |
| | |
/* summons a monster - the monster chosen is determined by the god | | |
* that is worshiiped. return 0 on failure, 1 on success | | |
*/ | | |
| | |
int summon_cult_monsters(object *op, int old_dir) { | | |
object *mon,*otmp,*god = find_god(determine_god(op)); | | |
int tries=0,i,summon_level,number,dir; | | |
char buf[MAX_BUF]; | | |
| | |
/* find deity */ | | |
if (!god) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "You worship no living deity!"); | | |
return 0; | | |
} else if(!god->race) { | | |
new_draw_info_format(NDI_UNIQUE, 0,op, | | |
"%s has no creatures that you may summon!",god->name); | | |
return 0; | | |
} | | } |
/* the summon level */ | | } /* for processing the surrounding spaces */ |
i=SK_level(op)+op->stats.Wis/10; | | |
if (i==0) i=1; | | |
summon_level = random_roll(0, i-1, op, PREFER_HIGH); | | |
if(op->path_attuned&PATH_SUMMON) summon_level += 5; | | |
if(op->path_repelled&PATH_SUMMON) summon_level -= 5; | | |
| | |
do { | | |
/* Need to set dir each time, as it may get clobbered */ | | |
dir=old_dir; | | |
mon=choose_cult_monster(op, god,summon_level); | | |
tries++; | | |
| | |
/* As per note in choose_cult_monster, if we have multiple race | | /* Now process objects in the players inventory if detect curse or magic */ |
* entries, we should really try again. | | if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) || QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL)) { |
*/ | | done_one = 0; |
if(!mon) { | | for (tmp = op->inv; tmp; tmp = tmp->below) { |
new_draw_info_format(NDI_UNIQUE, 0,op, | | if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { |
"%s fails to send anything.",god->name); | | if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) && |
return 0; | | is_magical(tmp) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)) { |
| | SET_FLAG(tmp,FLAG_KNOWN_MAGICAL); |
| | esrv_send_item (op, tmp); |
} | | } |
/* Now lets see if we can find a place for this monster. */ | | if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && |
if (!dir) | | (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) { |
dir = find_free_spot(mon->arch, op->map, op->x, op->y, 1, SIZEOFFREE); | | SET_FLAG(tmp, FLAG_KNOWN_CURSED); |
| | esrv_send_item (op, tmp); |
/* This only checks for the head of the monster. We still need | | |
* to check for the body. But if there is no space for the | | |
* head, trying a different monster won't help, so might as well | | |
* return now. | | |
*/ | | |
if((dir==-1) || arch_blocked(mon->arch,op->map, op->x + freearr_x[dir], op->y+freearr_y[dir])) { | | |
dir= -1; | | |
if (tries == 5) { | | |
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way."); | | |
return 0; | | |
} | | } |
else continue; /* Try a different monster */ | | } /* if item is not identified */ |
| | } /* for the players inventory */ |
| | } /* if detect magic/curse and object is a player */ |
| | return 1; |
} | | } |
} while (dir==-1); | | |
| | |
/* Aha - we have found a monster - lets customize it an put it on the | | |
* map. | | |
*/ | | |
| | |
if(mon->level>(summon_level/2)) | | |
number = random_roll(1, 2, op, PREFER_HIGH); | | |
else | | |
number = die_roll(2, 2, op, PREFER_HIGH); | | |
| | |
for (i = 1; i < number + 1; i++) { | | /* cast_transfer |
object *head; | | * This spell transfers sp from the player to another person. |
| | * We let the target go above their normal maximum SP. |
| | */ |
| | |
/* this allows multisq monsters to be done right */ | | int cast_transfer(object *op,object *caster, object *spell, int dir) { |
if(!(head = fix_summon_pet( mon->arch, op, dir, 0))) continue; | | object *plyr=NULL; |
| | sint16 x, y; |
| | mapstruct *m; |
| | int mflags; |
| | |
/* now, a little bit of tailoring. If the monster is much lower in | | m = op->map; |
* level than the summon_level, we get a better monster */ | | x = op->x+freearr_x[dir]; |
| | y = op->y+freearr_y[dir]; |
| | |
if((head->level+5)<summon_level) { | | mflags = get_map_flags(m, &m, x, y, &x, &y); |
int ii; | | |
| | |
for(ii=summon_level-(head->level)-5;ii>0;ii--) { | | if (!(mflags & P_OUT_OF_MAP) && mflags & P_IS_ALIVE) { |
switch(rndm(1, 3)) { | | for(plyr=get_map_ob(m, x, y); plyr!=NULL; plyr=plyr->above) |
case 1: | | if(QUERY_FLAG(plyr, FLAG_ALIVE)) |
head->stats.wc--; | | |
break; | | |
case 2: | | |
head->stats.ac--; | | |
break; | | |
case 3: | | |
head->stats.dam+=3; | | |
break; | | |
default: | | |
break; | | break; |
} | | } |
head->stats.hp+=3; | | |
} | | |
| | |
head->stats.maxhp=head->stats.hp; | | |
for(otmp=head;otmp;otmp=otmp->more) { | | /* If we did not find a player in the specified direction, transfer |
if(otmp->name) { | | * to anyone on top of us. This is used for the rune of transference mostly. |
if(summon_level>30+head->level) | | */ |
sprintf(buf,"Arch %s of %s",head->name,god->name); | | if(plyr==NULL) |
else | | for(plyr=get_map_ob(op->map,op->x,op->y); plyr!=NULL; plyr=plyr->above) |
sprintf(buf,"%s of %s",head->name,god->name); | | if(QUERY_FLAG(plyr,FLAG_ALIVE)) |
FREE_AND_COPY(otmp->name, buf); | | break; |
} | | |
| | if (!plyr) { |
| | new_draw_info(NDI_BLACK, 0, op, "There is no one there."); |
| | return 0; |
} | | } |
} /* if monster level is much less than character level */ | | /* give sp */ |
| | if(spell->stats.dam > 0) { |
| | plyr->stats.sp += spell->stats.dam + SP_level_dam_adjust(caster, spell); |
| | |
| | new_draw_info(NDI_UNIQUE, 0,plyr,"You feel energy course through you."); |
| | |
head = insert_ob_in_map (head, op->map, op,0); | | if(plyr->stats.sp>=plyr->stats.maxsp*2) { |
if (head != NULL && head->randomitems != NULL) { | | |
object *tmp; | | object *tmp; |
create_treasure(head->randomitems,head,GT_APPLY,6,0); | | |
for(tmp = head->inv; tmp != NULL; tmp = tmp->below) | | new_draw_info(NDI_UNIQUE, 0,plyr,"Your head explodes!"); |
if(!tmp->nrof) | | |
SET_FLAG(tmp, FLAG_NO_DROP); | | /* Explodes a fireball centered at player */ |
} | | tmp = get_archetype(EXPLODING_FIREBALL); |
dir = absdir(dir + 1); | | tmp->dam_modifier=random_roll(1, op->level, op, PREFER_LOW)/5+1; |
if (arch_blocked(mon->arch,op->map, op->x + freearr_x[dir], | | tmp->stats.maxhp=random_roll(1, op->level, op, PREFER_LOW)/10+2; |
op->y + freearr_y[dir])) { | | tmp->x = plyr->x; |
| | tmp->y = plyr->y; |
if (i < number) { | | insert_ob_in_map(tmp, plyr->map, NULL, 0); |
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way,"); | | plyr->stats.sp = 2*plyr->stats.maxsp; |
new_draw_info(NDI_UNIQUE, 0,op, "no more monsters for this casting."); | | |
return 1; | | |
} | | } |
| | else if(plyr->stats.sp>=plyr->stats.maxsp*1.88) |
| | new_draw_info(NDI_UNIQUE, NDI_ORANGE,plyr,"You feel like your head is going to explode."); |
| | else if(plyr->stats.sp>=plyr->stats.maxsp*1.66) |
| | new_draw_info(NDI_UNIQUE, 0,plyr, "You get a splitting headache!"); |
| | else if(plyr->stats.sp>=plyr->stats.maxsp*1.5) { |
| | new_draw_info(NDI_UNIQUE, 0,plyr,"Chaos fills your world."); |
| | confuse_player(op,op,99); |
} | | } |
} /* Loop to insert all the monster */ | | else if(plyr->stats.sp>=plyr->stats.maxsp*1.25) |
| | new_draw_info(NDI_UNIQUE, 0,plyr,"You start hearing voices."); |
return 1; | | return 1; |
} | | } |
| | /* suck sp away. Can't suck sp from yourself */ |
| | else if (op != plyr) { |
| | /* old dragin magic used floats. easier to just use ints and divide by 100 */ |
| | |
/* summon_avatar() - taken from the code which summons golems. We | | int rate = -spell->stats.dam + SP_level_dam_adjust(caster, spell), sucked=0; |
* cant use because we need to throw in a few extra's here. b.t. | | |
*/ | | |
| | |
int summon_avatar(object *op,object *caster,int dir, archetype *at, int spellnum) { | | |
object *tmp; | | |
char buf[MAX_BUF]; | | |
object *god = find_god(determine_god(caster)); | | |
| | |
if(god) | | if (rate > 95) rate=95; |
at = determine_holy_arch (god, (spellnum == SP_HOLY_SERVANT) | | |
? "holy servant" : "avatar"); | | sucked = (plyr->stats.sp * rate) / 100; |
else { | | plyr->stats.sp -= sucked; |
new_draw_info(NDI_UNIQUE, 0,op,"You must worship a god first."); | | /* Player doesn't get full credit */ |
return 0; | | if (QUERY_FLAG(op, FLAG_ALIVE)) { |
| | sucked = (sucked * rate) / 100; |
| | op->stats.sp += sucked; |
} | | } |
if ( ! at) { | | return 1; |
new_draw_info_format(NDI_UNIQUE, 0,op,"%s has no %s for you to call." | | |
,god->name,spellnum==SP_SUMMON_AVATAR?"avatar":"servant"); | | |
return 0; | | |
} | | } |
| | |
/* safety checks... */ | | |
if(op->type==PLAYER) | | |
if(op->contr->golem!=NULL&&!QUERY_FLAG(op->contr->golem,FLAG_FREED)) { | | |
control_golem(op->contr->golem,dir); | | |
return 0; | | return 0; |
} | | } |
if(!dir) | | |
dir = find_free_spot(at, op->map, op->x, op->y, 1, SIZEOFFREE); | | |
| | |
if((dir==-1) || arch_blocked(at,op->map, op->x + freearr_x[dir], op->y+freearr_y[dir])) { | | |
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way."); | | |
return 0; | | |
} | | |
| | |
if(!(tmp=fix_summon_pet(at,op,dir,GOLEM))) { | | /* counterspell: nullifies spell effects. |
new_draw_info(NDI_UNIQUE, 0,op,"Your spell fails."); | | * op is the counterspell object, dir is the direction |
return 0; | | * it was cast in. |
} | | * Basically, if the object has a magic attacktype, |
| | * this may nullify it. |
| | */ |
| | void counterspell(object *op,int dir) |
| | { |
| | object *tmp, *head, *next; |
| | int mflags; |
| | mapstruct *m; |
| | sint16 sx,sy; |
| | |
if(op->type==PLAYER) { | | sx = op->x + freearr_x[dir]; |
op->contr->golem=tmp; | | sy = op->y + freearr_y[dir]; |
op->contr->golem_count = tmp->count; | | m = op->map; |
/* give the player control of the golem */ | | mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); |
op->contr->shoottype=range_golem; | | if (mflags & P_OUT_OF_MAP) return; |
} | | |
| | |
/* This sets the level dependencies on dam, wc and hp */ | | for(tmp=get_map_ob(m,sx,sy); tmp!=NULL; tmp=next) { |
tmp->stats.wc -= SP_level_strength_adjust(op,caster,spellnum); | | next = tmp->above; |
if(tmp->stats.wc<-127) tmp->stats.wc = -127; | | |
tmp->stats.hp += SP_PARAMETERS[spellnum].bdur + | | /* Need to look at the head object - otherwise, if tmp |
12 * SP_level_strength_adjust(op,caster,spellnum); | | * points to a monster, we don't have all the necessary |
tmp->stats.dam += SP_PARAMETERS[spellnum].bdam +(2*SP_level_dam_adjust(op,caster,spellnum)); | | * info for it. |
if(tmp->stats.dam<0) tmp->stats.dam=127; /*seen this go negative!*/ | | */ |
if(tmp->other_arch) tmp->other_arch=NULL; | | if (tmp->head) head = tmp->head; |
| | else head = tmp; |
/* tailor it to the gods nature */ | | |
if(tmp) { | | /* don't attack our own spells */ |
object *tmp2; | | if(tmp->owner && tmp->owner == op->owner) continue; |
for(tmp2=tmp;tmp2;tmp2=tmp2->more) { | | |
sprintf(buf,"%s of %s",spellnum==SP_SUMMON_AVATAR?"Avatar":"Servant",god->name); | | /* Basically, if the object is magical and not counterspell, |
if(tmp2->name) free_string(tmp2->name); | | * we will more or less remove the object. Don't counterspell |
tmp2->name = add_string(buf); | | * monsters either. |
} | | */ |
} | | |
tmp->attacktype|=god->attacktype; | | if (head->attacktype & AT_MAGIC && |
memcpy(tmp->resist, god->resist, sizeof(tmp->resist)); | | !(head->attacktype & AT_COUNTERSPELL) && |
if (tmp->race) { | | !QUERY_FLAG(head,FLAG_MONSTER) && |
free_string (tmp->race); | | (op->level > head->level)) { |
tmp->race = NULL; | | remove_ob(head); |
} | | free_object(head); |
if (tmp->slaying) { | | } else switch(head->type) { |
free_string (tmp->slaying); | | case SPELL_EFFECT: |
tmp->slaying = NULL; | | if(op->level > head->level) { |
} | | remove_ob(head); |
if(god->race) tmp->race = add_string(god->race); | | free_object(head); |
if(god->slaying) tmp->slaying = add_string(god->slaying); | | |
/* safety, we must allow a god's servants some reasonable attack */ | | |
if(!(tmp->attacktype&AT_PHYSICAL)) tmp->attacktype|=AT_PHYSICAL; | | |
| | |
/* make experience increase in proportion to the strength of | | |
* the summoned creature. */ | | |
tmp->stats.exp *= SP_level_spellpoint_cost(op,caster,spellnum)/spells[spellnum].sp; | | |
tmp->speed_left= -1; | | |
tmp->x=op->x+freearr_x[dir],tmp->y=op->y+freearr_y[dir]; | | |
tmp->direction=dir; | | |
insert_ob_in_map(tmp,op->map,op,0); | | |
return 1; | | |
} | | } |
| | break; |
| | |
/* fix_summon_pet() - this makes multisquare/single square monsters | | /* I really don't get this rune code that much - that |
* properly for map insertion. | | * random chance seems really low. |
*/ | | */ |
object *fix_summon_pet(archetype *at, object *op, int dir, int type ) { | | case RUNE: |
archetype *atmp; | | if(rndm(0, 149) == 0) { |
object *tmp=NULL, *prev=NULL, *head=NULL; | | head->stats.hp--; /* weaken the rune */ |
| | if(!head->stats.hp) { |
for(atmp = at; atmp!=NULL; atmp = atmp->more) { | | remove_ob(head); |
tmp = arch_to_object(atmp); | | free_object(head); |
if (atmp == at) { | | |
if(type!=GOLEM) | | |
SET_FLAG(tmp, FLAG_MONSTER); | | |
set_owner(tmp, op); | | |
if (op->type == PLAYER) { | | |
tmp->stats.exp = 0; | | |
add_friendly_object(tmp); | | |
SET_FLAG(tmp, FLAG_FRIENDLY); | | |
if(type==GOLEM) CLEAR_FLAG(tmp, FLAG_MONSTER); | | |
} else { | | |
if(QUERY_FLAG(op, FLAG_FRIENDLY)) { | | |
object *owner = get_owner(op); | | |
if(owner != NULL) {/* For now, we transfer ownership */ | | |
set_owner(tmp,owner); | | |
tmp->move_type = PETMOVE; | | |
add_friendly_object(tmp); | | |
SET_FLAG(tmp, FLAG_FRIENDLY); | | |
} | | } |
} | | } |
| | break; |
} | | } |
if(op->type!=PLAYER||type!=GOLEM) { | | |
tmp->move_type = PETMOVE; | | |
tmp->speed_left = -1; | | |
tmp->type = 0; | | |
tmp->enemy = op->enemy; | | |
} else | | |
tmp->type = GOLEM; | | |
| | |
} | | } |
if(head == NULL) | | |
head = tmp; | | |
tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x; | | |
tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y; | | |
tmp->map = op->map; | | |
if(tmp->invisible) tmp->invisible=0; | | |
if(head != tmp) | | |
tmp->head = head, prev->more = tmp; | | |
prev = tmp; | | |
} | | |
head->direction = dir; | | |
| | |
/* need to change some monster attr to prevent problems/crashing */ | | |
if(head->last_heal) head->last_heal=0; | | |
if(head->last_eat) head->last_eat=0; | | |
if(head->last_grace) head->last_grace=0; | | |
if(head->last_sp) head->last_sp=0; | | |
if(head->attacktype&AT_GHOSTHIT) head->attacktype=(AT_PHYSICAL|AT_DRAIN); | | |
if(head->other_arch) head->other_arch=NULL; | | |
if(QUERY_FLAG(head,FLAG_CHANGING)) CLEAR_FLAG(head,FLAG_CHANGING); | | |
if(QUERY_FLAG(head,FLAG_STAND_STILL)) CLEAR_FLAG(head,FLAG_STAND_STILL); | | |
if(QUERY_FLAG(head,FLAG_GENERATOR)) CLEAR_FLAG(head,FLAG_GENERATOR); | | |
if(QUERY_FLAG(head,FLAG_SPLITTING)) CLEAR_FLAG(head,FLAG_SPLITTING); | | |
| | |
return head; | | |
} | | } |
| | |
/* cast_consecrate() - a spell to make an altar your god's */ | | |
| | |
int cast_consecrate(object *op) { | | |
| | /* cast_consecrate() - a spell to make an altar your god's */ |
| | int cast_consecrate(object *op, object *caster, object *spell) { |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
| | |
object *tmp, *god=find_god(determine_god(op)); | | object *tmp, *god=find_god(determine_god(op)); |
| | |
if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break; | | if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break; |
if(tmp->type==HOLY_ALTAR) { | | if(tmp->type==HOLY_ALTAR) { |
| | |
/* We use SK_level here instead of path_level mod because I think | | if(tmp->level > caster_level(caster, spell)) { |
* all the gods should give equal chance of re-consecrating altars | | |
*/ | | |
if(tmp->level > SK_level(op)) { | | |
new_draw_info_format(NDI_UNIQUE, 0,op, | | new_draw_info_format(NDI_UNIQUE, 0,op, |
"You are not poweful enough to reconsecrate the %s", tmp->name); | | "You are not poweful enough to reconsecrate the %s", tmp->name); |
return 0; | | return 0; |
| | |
if(tmp->name) free_string(tmp->name); | | if(tmp->name) free_string(tmp->name); |
sprintf(buf,"Altar of %s",god->name); | | sprintf(buf,"Altar of %s",god->name); |
tmp->name = add_string(buf); | | tmp->name = add_string(buf); |
tmp->level = SK_level(op); | | tmp->level = caster_level(caster, spell); |
tmp->other_arch = god->arch; | | tmp->other_arch = god->arch; |
if(op->type==PLAYER) esrv_update_item(UPD_NAME, op, tmp); | | if(op->type==PLAYER) esrv_update_item(UPD_NAME, op, tmp); |
new_draw_info_format(NDI_UNIQUE,0, op, | | new_draw_info_format(NDI_UNIQUE,0, op, |
| | |
return 0; | | return 0; |
} | | } |
| | |
| | |
/* finger_of_death() - boss high-level cleric spell. */ | | |
| | |
int finger_of_death(object *op, object *caster, int dir) { | | |
object *hitter,*target=get_pointed_target(op,dir); | | |
int success = 1; | | |
| | |
if(!target || QUERY_FLAG(target,FLAG_REFL_SPELL)) { | | |
new_draw_info(NDI_UNIQUE,0,op,"Nothing happens."); | | |
return 0; | | |
} | | |
| | |
/* we create a hitter object -- the spell */ | | |
hitter=get_archetype("face_of_death"); | | |
hitter->level = path_level_mod (caster, SP_PARAMETERS[SP_FINGER_DEATH].bdam + | | |
3*SP_level_dam_adjust(op,caster,SP_FINGER_DEATH), SP_FINGER_DEATH); | | |
set_owner(hitter,op); | | |
hitter->x=target->x; | | |
hitter->y=target->y; | | |
hitter->stats.maxhp=hitter->count; /*??*/ | | |
| | |
/* there are 'grave' consequences for using this spell on the unliving! */ | | |
if(QUERY_FLAG(target,FLAG_UNDEAD)) { | | |
success = 0; | | |
if(random_roll(0, 2, op, PREFER_LOW)) { | | |
new_draw_info(NDI_UNIQUE,0,op,"Idiot! Your spell boomerangs!"); | | |
hitter->x=op->x; | | |
hitter->y=op->y; | | |
} else { | | |
new_draw_info_format(NDI_UNIQUE,0,op,"The %s looks stronger!", | | |
query_name(target)); | | |
target->stats.hp = target->stats.maxhp*2; | | |
free_object(hitter); | | |
return 0; | | |
} | | |
} | | |
| | |
insert_ob_in_map(hitter,op->map,op,0); | | |
| | |
return success; | | |
} | | |
| | |
/* animate_weapon - | | /* animate_weapon - |
* Generalization of staff_to_snake. Makes a golem out of the caster's weapon. | | * Generalization of staff_to_snake. Makes a golem out of the caster's weapon. |
* The golem is based on the archetype specified, modified by the caster's level | | * The golem is based on the archetype specified, modified by the caster's level |
| | |
* player checks. MSW 2003-01-06 | | * player checks. MSW 2003-01-06 |
*/ | | */ |
| | |
int animate_weapon(object *op,object *caster,int dir, archetype *at, int spellnum) { | | int animate_weapon(object *op,object *caster,object *spell, int dir) { |
object *weapon, *tmp; | | object *weapon, *tmp; |
char buf[MAX_BUF]; | | char buf[MAX_BUF]; |
int a, i; | | int a, i; |
sint16 x, y; | | sint16 x, y; |
int magic; | | |
mapstruct *m; | | mapstruct *m; |
materialtype_t *mt; | | materialtype_t *mt; |
| | |
if(!at){ | | if(!spell->other_arch){ |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); |
LOG(llevError,"animate_weapon failed: missing archetype!\n"); | | LOG(llevError,"animate_weapon failed: spell %s missing other_arch!\n", spell->name); |
return 0; | | return 0; |
} | | } |
/* exit if it's not a player using this spell. */ | | /* exit if it's not a player using this spell. */ |
if(op->type!=PLAYER) return 0; | | if(op->type!=PLAYER) return 0; |
| | |
/* if player already has a golem, abort */ | | /* if player already has a golem, abort */ |
if(op->contr->golem!=NULL && op->contr->golem_count == op->contr->golem->count) { | | if(op->contr->ranges[range_golem]!=NULL && op->contr->golem_count == op->contr->ranges[range_golem]->count) { |
control_golem(op->contr->golem,dir); | | control_golem(op->contr->ranges[range_golem],dir); |
return 0; | | return 0; |
} | | } |
| | |
| | |
return 0; | | return 0; |
} | | } |
| | |
if(spellnum == SP_DANCING_SWORD) { | | /* Use the weapon marked by the player. */ |
archetype *weapon_at = find_archetype("sword"); | | weapon = find_marked_object(op); |
if(weapon_at) { | | |
weapon = &(weapon_at->clone); | | |
} else { | | |
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!"); | | |
LOG(llevError,"animate_weapon failed: missing archetype!\n"); | | |
return 0; | | |
} | | |
} else { | | |
/* get the weapon to transform */ | | |
for(weapon = op->inv; weapon; weapon = weapon->below) | | |
if(weapon->type==WEAPON&&QUERY_FLAG(weapon,FLAG_APPLIED)) break; | | |
| | |
if(!weapon) { | | if(!weapon) { |
new_draw_info(NDI_UNIQUE, 0,op,"You need to wield a weapon to animate it."); | | new_draw_info(NDI_BLACK, 0, op, "You must mark a weapon to use with this spell!"); |
return 0; | | return 0; |
} else if (spellnum == SP_STAFF_TO_SNAKE && strcmp(weapon->name,"quarterstaff")) { | | } |
| | if (spell->race && strcmp(weapon->arch->name, spell->race)) { |
new_draw_info(NDI_UNIQUE, 0,op,"The spell fails to transform your weapon."); | | new_draw_info(NDI_UNIQUE, 0,op,"The spell fails to transform your weapon."); |
return 0; | | return 0; |
} else if (QUERY_FLAG(weapon,FLAG_CURSED) || QUERY_FLAG(weapon,FLAG_DAMNED)) { | | } |
new_draw_info(NDI_UNIQUE, 0,op,"You can't animate it. It won't let go of your hand."); | | if (weapon->type != WEAPON) { |
| | new_draw_info(NDI_UNIQUE, 0,op,"You need to wield a weapon to animate it."); |
return 0; | | return 0; |
} | | } |
| | if (QUERY_FLAG(weapon, FLAG_APPLIED)) { |
| | new_draw_info_format(NDI_BLACK, 0, op, "You need to unequip %s before using it in this spell", |
| | query_name(weapon)); |
| | return 0; |
} | | } |
| | |
magic = weapon->magic>0 ? weapon->magic : -1*weapon->magic; | | if (weapon->nrof > 1) { |
| | tmp = get_split_ob(weapon, 1); |
| | esrv_send_item(op, weapon); |
| | weapon = tmp; |
| | } |
| | |
/* create the golem object */ | | /* create the golem object */ |
tmp=arch_to_object(at); | | tmp=arch_to_object(spell->other_arch); |
| | |
/* if animated by a player, give the player control of the golem */ | | /* if animated by a player, give the player control of the golem */ |
CLEAR_FLAG(tmp, FLAG_MONSTER); | | CLEAR_FLAG(tmp, FLAG_MONSTER); |
| | |
add_friendly_object(tmp); | | add_friendly_object(tmp); |
tmp->type=GOLEM; | | tmp->type=GOLEM; |
set_owner(tmp,op); | | set_owner(tmp,op); |
op->contr->golem=tmp; | | set_spell_skill(op, caster, spell, tmp); |
| | op->contr->ranges[range_golem]=tmp; |
op->contr->shoottype=range_golem; | | op->contr->shoottype=range_golem; |
op->contr->golem_count = tmp->count; | | op->contr->golem_count = tmp->count; |
| | |
/* ok, tailor the golem's characteristics based on the weapon */ | | /* Give the weapon to the golem now. A bit of a hack to check the |
if (spellnum == SP_STAFF_TO_SNAKE || spellnum == SP_ANIMATE_WEAPON) { | | * removed flag - it should only be set if get_split_object was |
if (apply_special (op, weapon, | | * used above. |
AP_UNAPPLY | AP_IGNORE_CURSE | AP_NO_MERGE)) { | | */ |
LOG (llevError, "BUG: animate_weapon(): can't unapply weapon\n"); | | if (!QUERY_FLAG(weapon, FLAG_REMOVED)) |
return 0; | | |
} | | |
remove_ob (weapon); | | remove_ob (weapon); |
insert_ob_in_ob (weapon, tmp); | | insert_ob_in_ob (weapon, tmp); |
esrv_send_item(op, weapon); | | esrv_send_item(op, weapon); |
| | /* To do everything necessary to let a golem use the weapon is a pain, |
| | * so instead, just set it as equipped (otherwise, we need to update |
| | * body_info, skills, etc) |
| | */ |
SET_FLAG (tmp, FLAG_USE_WEAPON); | | SET_FLAG (tmp, FLAG_USE_WEAPON); |
if (apply_special (tmp, weapon, AP_APPLY)) | | SET_FLAG(weapon, FLAG_APPLIED); |
LOG (llevError, "BUG: animate_weapon(): golem can't apply weapon\n"); | | fix_player(tmp); |
} | | |
| | /* There used to be 'odd' code that basically seemed to take the absolute |
| | * value of the weapon->magic an use that. IMO, that doesn't make sense - |
| | * if you're using a crappy weapon, it shouldn't be as good. |
| | */ |
| | |
/* modify weapon's animated wc */ | | /* modify weapon's animated wc */ |
tmp->stats.wc = tmp->stats.wc | | tmp->stats.wc = tmp->stats.wc - SP_level_range_adjust(caster,spell) |
- SP_level_dam_adjust(op,caster,spellnum) | | - 5 * weapon->stats.Dex - 2 * weapon->stats.Str - weapon->magic; |
- 5 * weapon->stats.Dex | | if(tmp->stats.wc<-127) tmp->stats.wc = -127; |
- 2 * weapon->stats.Str | | |
- magic; | | |
| | |
/* Modify hit points for weapon */ | | /* Modify hit points for weapon */ |
tmp->stats.maxhp = tmp->stats.maxhp | | tmp->stats.maxhp = tmp->stats.maxhp + spell->duration + |
+ SP_PARAMETERS[spellnum].bdur | | SP_level_duration_adjust(caster, spell) + |
+ 4 * SP_level_strength_adjust(op,caster,spellnum) | | + 8 * weapon->magic + 12 * weapon->stats.Con; |
+ 8 * magic | | if(tmp->stats.maxhp<0) tmp->stats.maxhp=10; |
+ 12 * weapon->stats.Con; | | tmp->stats.hp = tmp->stats.maxhp; |
| | |
/* Modify weapon's damage */ | | /* Modify weapon's damage */ |
tmp->stats.dam = SP_PARAMETERS[spellnum].bdam | | tmp->stats.dam = spell->stats.dam + SP_level_dam_adjust(caster, spell) |
+ weapon->stats.dam | | + weapon->stats.dam |
+ magic | | + weapon->magic |
+ 2 * SP_level_dam_adjust(op,caster,spellnum) | | |
+ 5 * weapon->stats.Str; | | + 5 * weapon->stats.Str; |
| | |
/* sanity checks */ | | |
if(tmp->stats.wc<-127) tmp->stats.wc = -127; | | |
if(tmp->stats.maxhp<0) tmp->stats.maxhp=10; | | |
tmp->stats.hp = tmp->stats.maxhp; | | |
| | |
if(tmp->stats.dam<0) tmp->stats.dam=127; | | if(tmp->stats.dam<0) tmp->stats.dam=127; |
| | |
/* LOG(llevDebug,"animate_weapon: wc:%d hp:%d dam:%d.\n", tmp->stats.wc, tmp->stats.hp, tmp->stats.dam);*/ | | |
| | |
/* attacktype */ | | /* attacktype */ |
if ( ! tmp->attacktype) | | if ( ! tmp->attacktype) |
| | |
| | |
/* Improve weapon's armour value according to best save vs. physical of its material */ | | /* Improve weapon's armour value according to best save vs. physical of its material */ |
| | |
tmp->resist[ATNR_PHYSICAL] = 100 - (int)((100.0-(float)tmp->resist[ATNR_PHYSICAL])/(30.0-2.0*(a>14?14.0:(float)a))); | | if (a > 14) a = 14; |
/* LOG (llevDebug, "animate_weapon: slaying %s\n", tmp->slaying ? tmp->slaying : "nothing"); */ | | tmp->resist[ATNR_PHYSICAL] = 100 - (int)((100.0-(float)tmp->resist[ATNR_PHYSICAL])/(30.0-2.0*a)); |
| | |
/* Determine golem's speed */ | | /* Determine golem's speed */ |
tmp->speed = 0.4 + 0.1 * SP_level_dam_adjust(op,caster,spellnum); | | tmp->speed = 0.4 + 0.1 * SP_level_range_adjust(caster,spell); |
| | |
if(tmp->speed > 3.33) tmp->speed = 3.33; | | if(tmp->speed > 3.33) tmp->speed = 3.33; |
/* LOG(llevDebug,"animate_weapon: armour:%d speed:%f exp:%d.\n", | | |
tmp->resist[ATNR_PHYSICAL], tmp->speed, tmp->stats.exp); | | |
*/ | | |
| | |
/* spell-dependent finishing touches and descriptive text */ | | |
switch(spellnum) { | | |
| | |
case SP_STAFF_TO_SNAKE: | | if (!spell->race) { |
new_draw_info(NDI_UNIQUE, 0,op,"Your staff becomes a serpent and leaps to the ground!"); | | |
break; | | |
| | |
case SP_ANIMATE_WEAPON: | | |
new_draw_info_format(NDI_UNIQUE, 0,op,"Your %s flies from your hand and hovers in mid-air!", weapon->name); | | |
sprintf(buf, "animated %s", weapon->name); | | sprintf(buf, "animated %s", weapon->name); |
if(tmp->name) free_string(tmp->name); | | if(tmp->name) free_string(tmp->name); |
tmp->name = add_string(buf); | | tmp->name = add_string(buf); |
| | |
CLEAR_FLAG(tmp,FLAG_ANIMATE); | | CLEAR_FLAG(tmp,FLAG_ANIMATE); |
} | | } |
update_ob_speed(tmp); | | update_ob_speed(tmp); |
break; | | |
| | |
case SP_DANCING_SWORD: | | |
new_draw_info(NDI_UNIQUE, 0,op,"A magical sword appears in mid air, eager to slay your foes for you!"); | | |
break; | | |
| | |
default: | | |
break; | | |
} | | } |
| | |
/* make experience increase in proportion to the strength of the summoned creature. */ | | /* make experience increase in proportion to the strength of the summoned creature. */ |
tmp->stats.exp *= SP_level_spellpoint_cost(op,caster,spellnum)/spells[spellnum].sp; | | tmp->stats.exp *= 1 + (MAX(spell->stats.maxgrace, spell->stats.sp) / caster_level(caster, spell)); |
| | |
tmp->speed_left= -1; | | tmp->speed_left= -1; |
tmp->x=x; | | tmp->x=x; |
tmp->y=y; | | tmp->y=y; |
| | |
| | |
/* cast_daylight() - changes the map darkness level *lower* */ | | /* cast_daylight() - changes the map darkness level *lower* */ |
| | |
int cast_daylight ( object *op ) { | | /* cast_change_map_lightlevel: Was cast_daylight/nightfall. |
int success = 0; | | * This changes the light level for the entire map. |
mapstruct *m=op->map; | | */ |
| | |
if(!m) return 0; /* shouldnt happen */ | | |
| | |
if(!(success=change_map_light(m,-1))) | | |
new_draw_info(NDI_UNIQUE,0,op,"It can be no brighter here."); | | |
| | |
return success; | | |
} | | |
| | |
/* cast_nightfall() - changes the map darkness level *higher* */ | | |
| | |
int cast_nightfall ( object *op ) { | | int cast_change_map_lightlevel( object *op, object *caster, object *spell ) { |
int success=0; | | int success; |
mapstruct *m=op->map; | | |
| | |
if(!m) return 0; /* shouldnt happen */ | | if(!op->map) return 0; /* shouldnt happen */ |
| | |
if(!(success=change_map_light(m,1))) | | success=change_map_light(op->map,spell->stats.dam); |
| | if(!success) { |
| | if (spell->stats.dam < 0) |
| | new_draw_info(NDI_UNIQUE,0,op,"It can be no brighter here."); |
| | else |
new_draw_info(NDI_UNIQUE,0,op,"It can be no darker here."); | | new_draw_info(NDI_UNIQUE,0,op,"It can be no darker here."); |
| | |
return success; | | |
} | | |
| | |
/* cast_faery_fire() - this spell primary purpose is to light | | |
* up all single-space monsters on a map. Magic immune and | | |
* multi-space monsters are currently not supposed to light | | |
* up. I hacked this out of the destruction code. -b.t. | | |
*/ | | |
| | |
int cast_faery_fire(object *op,object *caster) { | | |
int r,dam,i,j,success=0,factor; | | |
object *tmp; | | |
if(op->type!=PLAYER) | | |
return 0; | | |
/* the smaller this is, the longer it glows */ | | |
factor=SP_PARAMETERS[SP_FAERY_FIRE].bdur | | |
+ SP_level_strength_adjust(op,caster,SP_FAERY_FIRE); | | |
r=SP_PARAMETERS[SP_FAERY_FIRE].bdam | | |
+SP_level_dam_adjust(op,caster,SP_FAERY_FIRE); | | |
r = 5; | | |
factor = 10; | | |
dam=(SK_level(op)/10)+1; | | |
for(i= -r;i<r;i++) | | |
for(j= -r;j<r;j++) { | | |
if(out_of_map(op->map,op->x+i,op->y+j)) | | |
continue; | | |
tmp=get_map_ob(op->map,op->x+i,op->y+j); | | |
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE) | | |
||tmp->type==PLAYER||tmp->more||tmp->head | | |
||tmp->resist[ATNR_MAGIC]==100)) | | |
tmp=tmp->above; | | |
if(tmp==NULL) | | |
continue; | | |
if(make_object_glow(tmp,1,factor)) { | | |
object *effect=get_archetype("detect_magic"); | | |
success++; | | |
if(effect){ | | |
effect->x = tmp->x; | | |
effect->y = tmp->y; | | |
insert_ob_in_map(effect,op->map,op,0); | | |
} | | |
} | | |
} | | } |
return success; | | return success; |
} | | } |
| | |
#if 0 | | |
I set this out for now because the object code wont allow | | |
non-living objects to glow viz force in their inventory. This | | |
is because (Ibelive) the code doesnt currently allow non-living | | |
objects (exception: containers) to have an inventory. I may be | | |
wrong, but have not time to check it out now.. | | |
| | |
/* cast_glow() */ | | |
| | |
int cast_glow( object *op, object *caster,int dir) { | | |
int x,y,time,radius; | | |
object *tmp; | | |
| | |
if(!dir) return 0; | | |
x=op->x+freearr_x[dir]; | | |
y=op->y+freearr_y[dir]; | | |
| | |
if(out_of_map(op->map,x,y)) return 0; | | |
| | |
for(tmp=get_map_ob(op->map,x,y);tmp;tmp=tmp->above) { | | |
if(QUERY_FLAG(tmp, FLAG_ALIVE)||tmp->resist[ATNR_MAGIC]==100) | | |
continue; | | |
if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break; | | |
} | | |
| | |
if(!tmp||QUERY_FLAG(tmp,FLAG_IS_FLOOR)) { /* argh. nothing on the floor, | | |
* lets look at the inventory */ | | |
if(op->inv) tmp = op->inv; /* take first item */ | | |
else return 0; | | |
} | | |
| | |
time = (SP_PARAMETERS[SP_GLOW].bdur | | |
- (10*SP_level_strength_adjust(op,caster,SP_GLOW))); | | |
radius=SP_PARAMETERS[SP_GLOW].bdam | | |
+ SP_level_dam_adjust(op,caster,SP_GLOW); | | |
| | |
LOG(llevDebug,"cast_glow got item: %s to attempt glow\n"); | | |
/* ok, lets try to make tmp glow */ | | |
return make_object_glow(tmp,radius,time); | | |
} | | |
| | |
#endif | | |
| | |
/* make_object_glow() - currently only makes living objects glow. | | |
* we do this by creating a "force" and inserting it in the | | |
* object. if time is 0, the object glows permanently. To truely | | |
* make this work for non-living objects, we would have to | | |
* give them the capability to have an inventory. b.t. | | |
*/ | | |
| | |
int make_object_glow(object *op, int radius, int time) { | | |
object *tmp; | | |
| | |
/* some things are unaffected... */ | | |
if(op->path_denied&PATH_LIGHT) | | |
return 0; | | |
| | |
tmp=get_archetype("force"); | | |
tmp->speed = 0.000001 * time; | | |
tmp->glow_radius=radius; | | |
tmp->x=op->x,tmp->y=op->y; | | |
if(tmp->speed<MIN_ACTIVE_SPEED) tmp->speed = MIN_ACTIVE_SPEED; /* safety */ | | |
tmp=insert_ob_in_ob(tmp,op); | | |
| | |
if(!tmp->env||op!=tmp->env) { | | |
LOG(llevError,"make_object_glow() failed to insert glowing force in %s\n", | | |
op->name); | | |
return 0; | | |
} | | |
return 1; | | |
} | | |
| | |
| | |
/* cast_cause_disease: this spell looks along <dir> from the | | |
* player and infects someone. | | |
* op is the player/monster, caster is the object, dir is the direction | | |
* to cast, disease_arch is the specific disease, and type is the spell number | | |
*/ | | |
| | |
int cast_cause_disease(object *op, object *caster, int dir, archetype *disease_arch, int type) { | | |
sint16 x,y; | | |
int i, mflags; | | |
object *walk; | | |
mapstruct *m; | | |
| | |
x = op->x; | | |
y = op->y; | | |
| | |
/* If casting from a scroll, no direction will be available, so refer to the | | |
* direction the player is pointing. | | |
*/ | | |
if (!dir) dir=op->facing; | | |
if (!dir) return 0; /* won't find anything if casting on ourself, so just return */ | | |
| | |
/* search in a line for a victim */ | | |
for(i=1;i<6;i++) { | | |
x = op->x + i * freearr_x[dir]; | | |
y = op->y + i * freearr_y[dir]; | | |
m = op->map; | | |
| | |
mflags = get_map_flags(m, &m, x, y, &x, &y); | | |
| | |
if (mflags & P_OUT_OF_MAP) return 0; | | |
| | |
/* don't go through walls */ | | |
if (mflags & P_NO_PASS) return 0; | | |
| | |
/* Only bother looking on this space if there is something living here */ | | |
if (mflags & P_IS_ALIVE) { | | |
/* search this square for a victim */ | | |
for(walk=get_map_ob(m,x,y);walk;walk=walk->above) | | |
if (QUERY_FLAG(walk,FLAG_MONSTER) || (walk->type==PLAYER)) { /* found a victim */ | | |
object *disease = arch_to_object(disease_arch); | | |
| | |
set_owner(disease,op); | | |
disease->stats.exp = 0; | | |
disease->level = op->level; | | |
| | |
/* Try to get the experience into the correct category. | | /* create an aura spell object and put it in the player's inventory. |
* Need to set chosen_skill for it to work when cast from a | | * as usual, op is player, caster is the object casting the spell, |
* glyph - I'm not sure why this works when not cast as | | * spell is the spell object itself. |
* glyphs - the same code in attack.c that uses the chosen_skill | | |
* would seem to get used. | | |
*/ | | */ |
if(op->chosen_skill && op->chosen_skill->exp_obj) { | | int create_aura(object *op, object *caster, object *spell) |
disease->exp_obj = op->chosen_skill->exp_obj; | | { |
disease->chosen_skill = op->chosen_skill; | | int refresh=0; |
} | | object *new_aura; |
| | |
/* do level adjustments */ | | |
if(disease->stats.wc) | | |
disease->stats.wc += SP_level_strength_adjust(op,caster,type)/2; | | |
| | |
if(disease->magic> 0) | | |
disease->magic += SP_level_strength_adjust(op,caster,type)/4; | | |
| | |
if(disease->stats.maxhp>0) | | |
disease->stats.maxhp += SP_level_strength_adjust(op,caster,type); | | |
| | |
if(disease->stats.maxgrace>0) | | |
disease->stats.maxgrace += SP_level_strength_adjust(op,caster,type); | | |
| | |
if(disease->stats.dam) { | | |
if(disease->stats.dam > 0) | | |
disease->stats.dam += SP_level_dam_adjust(op,caster,type); | | |
else disease->stats.dam -= SP_level_dam_adjust(op,caster,type); | | |
} | | |
| | |
if(disease->last_sp) { | | |
disease->last_sp -= 2*SP_level_dam_adjust(op,caster,type); | | |
if(disease->last_sp <1) disease->last_sp = 1; | | |
} | | |
| | |
if(disease->stats.maxsp) { | | |
if(disease->stats.maxsp > 0) | | |
disease->stats.maxsp += SP_level_dam_adjust(op,caster,type); | | |
else disease->stats.maxsp -= SP_level_dam_adjust(op,caster,type); | | |
} | | |
| | |
if(disease->stats.ac) | | |
disease->stats.ac += SP_level_dam_adjust(op,caster,type); | | |
| | |
if(disease->last_eat) | | |
disease->last_eat -= SP_level_dam_adjust(op,caster,type); | | |
| | |
if(disease->stats.hp) | | new_aura = present_arch_in_ob(spell->other_arch, op); |
disease->stats.hp -= SP_level_dam_adjust(op,caster,type); | | if (new_aura) refresh=1; |
| | else new_aura = arch_to_object(spell->other_arch); |
| | |
if(disease->stats.sp) | | new_aura->duration = spell->duration + |
disease->stats.sp -= SP_level_dam_adjust(op,caster,type); | | 10* SP_level_duration_adjust(caster,spell); |
| | |
if(infect_object(walk,disease,1)) { | | new_aura->stats.dam = spell->stats.dam |
object *flash; /* visual effect for inflicting disease */ | | +SP_level_dam_adjust(caster,spell); |
| | |
new_draw_info_format(NDI_UNIQUE, 0, op, "You inflict %s on %s!",disease->name,walk->name); | | set_owner(new_aura,op); |
| | set_spell_skill(op, caster, spell, new_aura); |
| | new_aura->attacktype= spell->attacktype; |
| | |
free_object(disease); /* don't need this one anymore */ | | new_aura->level = caster_level(caster, spell); |
flash=get_archetype("detect_magic"); | | if (refresh) |
flash->x = x; | | new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect."); |
flash->y = y; | | else |
flash->map = walk->map; | | insert_ob_in_ob(new_aura, op); |
insert_ob_in_map(flash,walk->map,op,0); | | |
return 1; | | |
} | | |
free_object(disease); | | |
} | | |
} /* if living creature */ | | |
} /* for range of spaces */ | | |
new_draw_info(NDI_UNIQUE,0,op,"No one caught anything!"); | | |
return 1; | | return 1; |
} | | } |
| | |
| | |
| | |
/* move aura function. An aura is a part of someone's inventory, | | /* move aura function. An aura is a part of someone's inventory, |
* which he carries with him, but which acts on the map immediately | | * which he carries with him, but which acts on the map immediately |
* around him. | | * around him. |
* Aura parameters: | | * Aura parameters: |
* food: duration counter. | | * duration: duration counter. |
* attacktype: aura's attacktype | | * attacktype: aura's attacktype |
* other_arch: archetype to drop where we attack | | * other_arch: archetype to drop where we attack |
*/ | | */ |
| | |
remove_ob(aura); | | remove_ob(aura); |
| | |
/* exit if we're out of gas */ | | /* exit if we're out of gas */ |
if(aura->stats.food--< 0) { | | if(aura->duration--< 0) { |
free_object(aura); | | free_object(aura); |
return; | | return; |
} | | } |
| | |
insert_ob_in_ob(aura, env); | | insert_ob_in_ob(aura, env); |
} | | } |
| | |
| | /* moves the peacemaker spell. |
| | * op is the piece object. |
| | */ |
| | |
void move_peacemaker(object *op) { | | void move_peacemaker(object *op) { |
object *tmp; | | object *tmp; |
char buf[MAX_BUF]; | | |
for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) { | | for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) { |
int atk_lev, def_lev; | | int atk_lev, def_lev; |
object *victim=tmp; | | object *victim=tmp; |
| | |
if(tmp->head) victim=tmp->head; | | if(tmp->head) victim=tmp->head; |
if(!QUERY_FLAG(victim,FLAG_MONSTER)) continue; | | if(!QUERY_FLAG(victim,FLAG_MONSTER)) continue; |
if(QUERY_FLAG(victim,FLAG_UNAGGRESSIVE)) continue; | | if(QUERY_FLAG(victim,FLAG_UNAGGRESSIVE)) continue; |
if(victim->stats.exp == 0) continue; | | if(victim->stats.exp == 0) continue; |
| | |
def_lev = MAX(1,victim->level); | | def_lev = MAX(1,victim->level); |
atk_lev = MAX(1,op->level); | | atk_lev = MAX(1,op->level); |
if(rndm(0, atk_lev-1) > def_lev) { | | |
| | |
| | if (rndm(0, atk_lev-1) > def_lev) { |
/* make this sucker peaceful. */ | | /* make this sucker peaceful. */ |
victim->stats.dam = 0; | | |
add_exp(op->owner,victim->stats.exp); | | change_exp(get_owner(op),victim->stats.exp, op->skill, 0); |
victim->stats.exp=0; | | victim->stats.exp=0; |
| | #if 0 |
| | /* No idea why these were all set to zero - if something |
| | * makes this creature agressive, he should still do damage. |
| | */ |
| | victim->stats.dam = 0; |
victim->stats.sp = 0; | | victim->stats.sp = 0; |
victim->stats.grace = 0; | | victim->stats.grace = 0; |
victim->stats.Pow = 0; | | victim->stats.Pow = 0; |
| | #endif |
victim->move_type = RANDO2; | | victim->move_type = RANDO2; |
SET_FLAG(victim,FLAG_UNAGGRESSIVE); | | SET_FLAG(victim,FLAG_UNAGGRESSIVE); |
SET_FLAG(victim,FLAG_RUN_AWAY); | | SET_FLAG(victim,FLAG_RUN_AWAY); |
SET_FLAG(victim,FLAG_RANDOM_MOVE); | | SET_FLAG(victim,FLAG_RANDOM_MOVE); |
CLEAR_FLAG(victim,FLAG_MONSTER); | | CLEAR_FLAG(victim,FLAG_MONSTER); |
if(victim->name) { | | if(victim->name) { |
sprintf(buf,"%s no longer feels like fighting.",victim->name); | | new_draw_info_format(NDI_UNIQUE,0,op->owner,"%s no longer feels like fighting.",victim->name); |
new_draw_info(NDI_UNIQUE,0,op->owner,buf); | | |
} | | } |
} | | } |
} | | } |
} | | } |
| | |
| | |
int cast_cause_conflict(object *op, object *caster, archetype *spellarch,int type) | | /* This writes a rune that contains the appropriate message. |
{ | | * There really isn't any adjustments we make. |
int i,j; | | */ |
int r; /* peterm: added to make area of effect level dep. */ | | |
int level; | | int write_mark(object *op, object *spell, char *msg) { |
| | char rune[HUGE_BUF]; |
object *tmp; | | object *tmp; |
if(op->type!=PLAYER) | | |
return 0; | | |
r=5 + SP_level_strength_adjust(op,caster,type); | | |
for(i= -r;i<r;i++) | | |
for(j= -r;j<r;j++) { | | |
if(out_of_map(op->map,op->x+i,op->y+j)) | | |
continue; | | |
tmp=get_map_ob(op->map,op->x+i,op->y+j); | | |
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE)||tmp->type==PLAYER)) | | |
tmp=tmp->above; | | |
if(tmp==NULL) | | |
continue; | | |
if(tmp->head) continue; /* only hit the head with this one */ | | |
| | |
/* OK, now set the monster on other monsters */ | | if (!msg || msg[0] == 0) { |
level = MAX(1,SK_level(caster)/2); | | new_draw_info(NDI_UNIQUE, 0, op, "Write what?"); |
if(random_roll(0, level-1, op, PREFER_HIGH) > tmp->level) { | | return 0; |
/* successfully induced conflict */ | | |
char buf[MAX_BUF]; | | |
SET_FLAG(tmp,FLAG_BERSERK); | | |
if(tmp->name) { | | |
sprintf(buf,"You've clouded %s's mind. He turns on his friends!",tmp->name); | | |
new_draw_info(NDI_RED,0,op,buf); | | |
} | | |
} | | } |
| | |
| | if (strcasestr_local(msg, "endmsg")) { |
| | new_draw_info(NDI_UNIQUE, 0, op, "Trying to cheat are we?"); |
| | LOG(llevInfo,"write_rune: player %s tried to write bogus rune %s\n", op->name, msg); |
| | return 0; |
} | | } |
| | if (!spell->other_arch) return 0; |
| | tmp = arch_to_object(spell->other_arch); |
| | strncpy(rune, msg, HUGE_BUF-2); |
| | rune[HUGE_BUF-2] = 0; |
| | strcat(rune, "\n"); |
| | tmp->msg = add_string(rune); |
| | tmp->x = op->x; |
| | tmp->y = op->y; |
| | insert_ob_in_map(tmp, op->map, op, INS_BELOW_ORIGINATOR); |
return 1; | | return 1; |
} | | } |
| | |