--- deliantra/server/server/spell_attack.C 2006/12/26 08:55:00 1.24 +++ deliantra/server/server/spell_attack.C 2009/10/12 14:00:59 1.88 @@ -1,25 +1,26 @@ /* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2002-2003 Mark Wedel & Crossfire Development Team - Copyright (C) 1992 Frank Tore Johansen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - The authors can be reached via e-mail at -*/ + * This file is part of Deliantra, the Roguelike Realtime MMORPG. + * + * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * Copyright (©) 2002-2003,2007 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992,2007 Frank Tore Johansen + * + * Deliantra is free software: you can redistribute it and/or modify it under + * the terms of the Affero GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Affero GNU General Public License + * and the GNU General Public License along with this program. If not, see + * . + * + * The authors can be reached via e-mail to + */ /* This file contains all the spell attack code. Grouping this code * together should hopefully make it easier to find the relevent bits @@ -38,11 +39,9 @@ * but moved here so it could be applied to bolts too * op is the spell object. */ - void check_spell_knockback (object *op) { - object *tmp, *tmp2; /* object on the map */ int weight_move; int frictionmod = 2; /*poor man's physics - multipy targets weight by this amount */ @@ -57,7 +56,7 @@ /*LOG (llevDebug, "DEBUG: arch weighs %d and masses %d (%s,level %d)\n", op->weight,weight_move,op->name,op->level); */ } - for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) + for (object *tmp = op->ms ().bot; tmp; tmp = tmp->above) { int num_sections = 1; @@ -74,7 +73,7 @@ continue; /* count the object's sections */ - for (tmp2 = tmp; tmp2 != NULL; tmp2 = tmp2->more) + for (object *tmp2 = tmp; tmp2; tmp2 = tmp2->more) num_sections++; /* I'm not sure if it makes sense to divide by num_sections - bigger @@ -108,10 +107,9 @@ * ***************************************************************************/ -/* Causes op to fork. op is the original bolt, tmp +/* Causes op to fork. op is the original bolt, tmp * is the first piece of the fork. */ - void forklightning (object *op, object *tmp) { @@ -146,34 +144,31 @@ new_bolt->stats.Dex -= 10; tmp->stats.Dex -= 10; /* less forks from main bolt too */ new_bolt->stats.Con += 25 * new_dir; /* adjust the left bias */ - new_bolt->speed_left = -0.1; + new_bolt->speed_left = -0.1f; new_bolt->direction = t_dir; new_bolt->duration++; - new_bolt->x = sx; - new_bolt->y = sy; new_bolt->stats.dam /= 2; /* reduce daughter bolt damage */ new_bolt->stats.dam++; tmp->stats.dam /= 2; /* reduce father bolt damage */ tmp->stats.dam++; - new_bolt = insert_ob_in_map (new_bolt, m, op, 0); - update_turn_face (new_bolt); + + if ((new_bolt = m->insert (new_bolt, sx, sy, op))) + update_turn_face (new_bolt); } /* move_bolt: moves bolt 'op'. Basically, it just advances a space, * and checks for various things that may stop it. */ - void move_bolt (object *op) { - object *tmp; int mflags; sint16 x, y; maptile *m; - if (--(op->duration) < 0) + if (--op->duration < 0) { - op->destroy (); + op->drop_and_destroy (); return; } @@ -183,9 +178,7 @@ return; if (--op->range < 0) - { - op->range = 0; - } + op->range = 0; else { x = op->x + DIRX (op); @@ -203,7 +196,6 @@ */ if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)) || ((mflags & P_IS_ALIVE) && reflwall (m, x, y, op))) { - if (!QUERY_FLAG (op, FLAG_REFLECTING)) return; @@ -243,27 +235,25 @@ else if (right) op->direction = absdir (op->direction - 2); } + update_turn_face (op); /* A bolt *must* be IS_TURNABLE */ return; } else { /* Create a copy of this object and put it ahead */ - tmp = op->clone (); + object *tmp = op->clone (); - tmp->speed_left = -0.1; - tmp->x += DIRX (tmp), tmp->y += DIRY (tmp); - tmp = insert_ob_in_map (tmp, op->map, op, 0); + m->insert (tmp, x, y, op); + tmp->speed_left = -0.1f; /* To make up for the decrease at the top of the function */ tmp->duration++; /* New forking code. Possibly create forks of this object * going off in other directions. */ + if (tmp->stats.Dex && rndm (0, 99) < tmp->stats.Dex) + forklightning (op, tmp); /* stats.Dex % of forking */ - if (rndm (0, 99) < tmp->stats.Dex) - { /* stats.Dex % of forking */ - forklightning (op, tmp); - } /* In this way, the object left behind sticks on the space, but * doesn't create any bolts that continue to move onward. */ @@ -296,8 +286,10 @@ /* peterm: level dependency for bolts */ tmp->stats.dam = spob->stats.dam + SP_level_dam_adjust (caster, spob); tmp->attacktype = spob->attacktype; + if (spob->slaying) tmp->slaying = spob->slaying; + tmp->range = spob->range + SP_level_range_adjust (caster, spob); tmp->duration = spob->duration + SP_level_duration_adjust (caster, spob); tmp->stats.Dex = spob->stats.Dex; @@ -318,7 +310,7 @@ mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y); if (mflags & P_OUT_OF_MAP) { - tmp->destroy (); + tmp->drop_and_destroy (); return 0; } @@ -328,7 +320,7 @@ { if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) { - tmp->destroy (); + tmp->drop_and_destroy (); return 0; } @@ -338,14 +330,12 @@ tmp->map = op->map; } - if ((tmp = insert_ob_in_map (tmp, tmp->map, op, 0)) != NULL) + if ((tmp = tmp->insert_at (tmp, op))) move_bolt (tmp); return 1; } - - /*************************************************************************** * * BULLET/BALL CODE @@ -353,17 +343,16 @@ ***************************************************************************/ /* expands an explosion. op is a piece of the - * explosion - this expans it in the different directions. + * explosion - this expands it in the different directions. * At least that is what I think this does. */ void explosion (object *op) { - object *tmp; maptile *m = op->map; int i; - if (--(op->duration) < 0) + if (--op->duration < 0) { op->destroy (); return; @@ -379,25 +368,25 @@ dx = op->x + freearr_x[i]; dy = op->y + freearr_y[i]; + /* ok_to_put_more already does things like checks for walls, * out of map, etc. */ if (ok_to_put_more (op->map, dx, dy, op, op->attacktype)) { - tmp = op->clone (); + object *tmp = op->clone (); + tmp->state = 0; - tmp->speed_left = -0.21; + tmp->speed_left = -0.21f; tmp->range--; tmp->value = 0; - tmp->x = dx; - tmp->y = dy; - insert_ob_in_map (tmp, m, op, 0); + + m->insert (tmp, dx, dy, op); } } } } - /* Causes an object to explode, eg, a firebullet, * poison cloud ball, etc. op is the object to * explode. @@ -407,7 +396,7 @@ { object *tmp, *owner; - if (op->other_arch == NULL) + if (!op->other_arch) { LOG (llevError, "BUG: explode_bullet(): op without other_arch\n"); op->destroy (); @@ -416,20 +405,16 @@ if (op->env) { - object *env; + object *env = op->outer_env (); - env = object_get_env_recursive (op); - if (env->map == NULL || out_of_map (env->map, env->x, env->y)) + if (!env->map || out_of_map (env->map, env->x, env->y)) { LOG (llevError, "BUG: explode_bullet(): env out of map\n"); op->destroy (); return; } - op->remove (); - op->x = env->x; - op->y = env->y; - insert_ob_in_map (op, env->map, op, INS_NO_MERGE | INS_NO_WALK_ON); + op->insert_at (env, op, INS_NO_MERGE | INS_NO_WALK_ON); } else if (out_of_map (op->map, op->x, op->y)) { @@ -450,6 +435,7 @@ if (op->attacktype) { hit_map (op, 0, op->attacktype, 1); + if (op->destroyed ()) return; } @@ -462,15 +448,15 @@ owner = op->owner; - if ((tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER) && owner && !tailor_god_spell (tmp, owner)) + if ((tmp->attacktype & AT_HOLYWORD + || tmp->attacktype & AT_GODPOWER) + && owner + && !tailor_god_spell (tmp, owner)) { op->destroy (); return; } - tmp->x = op->x; - tmp->y = op->y; - /* special for bombs - it actually has sane values for these */ if (op->type == SPELL_EFFECT && op->subtype == SP_BOMB) { @@ -483,6 +469,7 @@ { if (op->attacktype & AT_MAGIC) tmp->attacktype |= AT_MAGIC; + /* Spell doc describes what is going on here */ tmp->stats.dam = op->dam_modifier; tmp->range = op->stats.maxhp; @@ -500,20 +487,16 @@ /* Prevent recursion */ op->move_on = 0; - insert_ob_in_map (tmp, op->map, op, 0); + tmp->insert_at (op, op); + tmp->play_sound (tmp->sound); + /* remove the firebullet */ - if (!op->destroyed ()) - { - op->destroy (); - } + op->destroy (); } - - /* checks to see what op should do, given the space it is on * (eg, explode, damage player, etc) */ - void check_bullet (object *op) { @@ -538,11 +521,13 @@ if (!(mflags & P_IS_ALIVE)) return; - for (tmp = GET_MAP_OB (op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) + for (tmp = op->ms ().bot; tmp; tmp = tmp->above) { if (QUERY_FLAG (tmp, FLAG_ALIVE)) { dam = hit_player (tmp, op->stats.dam, op, op->attacktype, 1); + + // TODO: can't understand the following if's if (op->destroyed () || !tmp->destroyed () || (op->stats.dam -= dam) < 0) { if (!QUERY_FLAG (op, FLAG_REMOVED)) @@ -563,10 +548,6 @@ void move_bullet (object *op) { - sint16 new_x, new_y; - int mflags; - maptile *m; - #if 0 /* We need a better general purpose way to do this */ @@ -591,18 +572,20 @@ return; } - new_x = op->x + DIRX (op); - new_y = op->y + DIRY (op); - m = op->map; - mflags = get_map_flags (m, &m, new_x, new_y, &new_x, &new_y); + mapxy pos (op); + pos.move (op->direction); - if (mflags & P_OUT_OF_MAP) + if (!pos.normalise ()) { op->destroy (); return; } - if (!op->direction || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y))) + mapspace &ms = pos.ms (); + + ms.update (); + + if (!op->direction || OB_TYPE_MOVE_BLOCK (op, ms.move_block)) { if (op->other_arch) explode_bullet (op); @@ -612,10 +595,7 @@ return; } - op->remove (); - op->x = new_x; - op->y = new_y; - if ((op = insert_ob_in_map (op, m, op, 0)) == NULL) + if (!(op = pos.insert (op, op))) return; if (reflwall (op->map, op->x, op->y, op)) @@ -627,9 +607,6 @@ check_bullet (op); } - - - /* fire_bullet * object op (cast from caster) files a bolt in dir. * spob is the spell object for the bolt. @@ -638,7 +615,6 @@ * This function sets up the appropriate owner and skill * pointers. */ - int fire_bullet (object *op, object *caster, int dir, object *spob) { @@ -648,11 +624,11 @@ if (!spob->other_arch) return 0; - tmp = arch_to_object (spob->other_arch); - if (tmp == NULL) + tmp = spob->other_arch->instance (); + if (!tmp) return 0; - /* peterm: level dependency for bolts */ + /* peterm: level dependency for bolts */ tmp->stats.dam = spob->stats.dam + SP_level_dam_adjust (caster, spob); tmp->attacktype = spob->attacktype; if (spob->slaying) @@ -672,8 +648,8 @@ tmp->set_owner (op); set_spell_skill (op, caster, spob, tmp); - tmp->x = op->x + freearr_x[dir]; - tmp->y = op->y + freearr_y[dir]; + tmp->x = op->x + freearr_x[dir]; + tmp->y = op->y + freearr_y[dir]; tmp->map = op->map; maptile *newmap; @@ -700,40 +676,32 @@ tmp->map = op->map; } - if ((tmp = insert_ob_in_map (tmp, tmp->map, op, 0))) + if ((tmp = tmp->insert_at (tmp, op))) check_bullet (tmp); return 1; } - - - /***************************************************************************** * * CONE RELATED FUNCTIONS * *****************************************************************************/ - /* drops an object based on what is in the cone's "other_arch" */ void cone_drop (object *op) { object *new_ob = arch_to_object (op->other_arch); - new_ob->x = op->x; - new_ob->y = op->y; new_ob->level = op->level; new_ob->set_owner (op->owner); /* preserve skill ownership */ if (op->skill && op->skill != new_ob->skill) - { - new_ob->skill = op->skill; - } - insert_ob_in_map (new_ob, op->map, op, 0); + new_ob->skill = op->skill; + new_ob->insert_at (op, op); } /* move_cone: causes cone object 'op' to move a space/hit creatures */ @@ -741,8 +709,6 @@ void move_cone (object *op) { - int i; - /* if no map then hit_map will crash so just ignore object */ if (!op->map) { @@ -772,17 +738,22 @@ hit_map (op, 0, op->attacktype, 0); + if (!op->is_on_map ()) + return; + /* Check to see if we should push anything. * Spell objects with weight push whatever they encounter to some * degree. */ if (op->weight) - check_spell_knockback (op); + { + check_spell_knockback (op); - if (op->destroyed ()) - return; + if (!op->is_on_map ()) + return; + } - if ((op->duration--) < 0) + if (op->duration-- < 0) { op->destroy (); return; @@ -797,7 +768,7 @@ return; } - for (i = -1; i < 2; i++) + for (int i = -1; i <= 1; i++) { sint16 x = op->x + freearr_x[absdir (op->stats.sp + i)], y = op->y + freearr_y[absdir (op->stats.sp + i)]; @@ -805,14 +776,13 @@ { object *tmp = op->clone (); - tmp->x = x; - tmp->y = y; - tmp->duration = op->duration + 1; /* Use for spell tracking - see ok_to_put_more() */ tmp->stats.maxhp = op->stats.maxhp; - insert_ob_in_map (tmp, op->map, op, 0); + + op->map->insert (tmp, x, y, op); + if (tmp->other_arch) cone_drop (tmp); } @@ -855,22 +825,18 @@ * to create is, so we can know if the space we are about to * insert it into is blocked. */ - movetype = spell->other_arch->clone.move_type; + movetype = spell->other_arch->move_type; for (i = range_min; i <= range_max; i++) { - sint16 x, y, d; + sint16 x, y; /* We can't use absdir here, because it never returns - * 0. If this is a rune, we want to hit the person on top + * 0. If this is a rune, we want to hit the person on top * of the trap (d==0). If it is not a rune, then we don't want * to hit that person. */ - d = dir + i; - while (d < 0) - d += 8; - while (d > 8) - d -= 8; + int d = dir ? absdir (dir + i) : i; /* If it's not a rune, we don't want to blast the caster. * In that case, we have to see - if dir is specified, @@ -900,9 +866,7 @@ tmp = arch_to_object (spell->other_arch); tmp->set_owner (op); set_spell_skill (op, caster, spell, tmp); - tmp->level = caster_level (caster, spell); - tmp->x = sx; - tmp->y = sy; + tmp->level = casting_level (caster, spell); tmp->attacktype = spell->attacktype; /* holy word stuff */ @@ -946,14 +910,12 @@ } if (!(tmp->move_type & MOVE_FLY_LOW)) - LOG (llevDebug, "cast_cone(): arch %s doesn't have flying 1\n", &spell->other_arch->name); + LOG (llevDebug, "cast_cone(): arch %s doesn't have flying 1\n", &spell->other_arch->archname); if (!tmp->move_on && tmp->stats.dam) - { - LOG (llevDebug, "cast_cone(): arch %s doesn't have move_on set\n", &spell->other_arch->name); - } + LOG (llevDebug, "cast_cone(): arch %s doesn't have move_on set\n", &spell->other_arch->archname); - insert_ob_in_map (tmp, m, op, 0); + m->insert (tmp, sx, sy, op); /* This is used for tracking spells so that one effect doesn't hit * a single space too many times. @@ -973,41 +935,30 @@ * ****************************************************************************/ - /* This handles an exploding bomb. * op is the original bomb object. */ void animate_bomb (object *op) { - int i; - object *env, *tmp; - archetype *at; - if (op->state != NUM_ANIMATIONS (op) - 1) return; - env = object_get_env_recursive (op); + object *env = op->outer_env (); if (op->env) { - if (env->map == NULL) + if (!env->map) return; - if (env->type == PLAYER) - esrv_del_item (env->contr, op->count); - - op->remove (); - op->x = env->x; - op->y = env->y; - if ((op = insert_ob_in_map (op, env->map, op, 0)) == NULL) + if (!(op = op->insert_at (env, op))) return; } // elmex Tue Aug 15 17:46:51 CEST 2006: Prevent bomb from exploding // on a safe map. I don't like this special casing, but it seems to be neccessary // as bombs can be carried. - if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE) + if (op->ms ().flags () & P_SAFE) { op->destroy (); return; @@ -1017,14 +968,14 @@ * but using the cast_bullet isn't really feasible, * so just set up the appropriate values. */ - at = archetype::find (SPLINT); - if (at) + if (archetype *at = archetype::find (SPLINT)) { - for (i = 1; i < 9; i++) + for (int i = 1; i < 9; i++) { if (out_of_map (op->map, op->x + freearr_x[i], op->y + freearr_x[i])) continue; - tmp = arch_to_object (at); + + object *tmp = arch_to_object (at); tmp->direction = i; tmp->range = op->range; tmp->stats.dam = op->stats.dam; @@ -1032,14 +983,12 @@ tmp->attacktype = op->attacktype; tmp->set_owner (op); if (op->skill && op->skill != tmp->skill) - { - tmp->skill = op->skill; - } + tmp->skill = op->skill; + if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) SET_ANIMATION (tmp, i); - tmp->x = op->x + freearr_x[i]; - tmp->y = op->y + freearr_x[i]; - insert_ob_in_map (tmp, op->map, op, 0); + + op->map->insert (tmp, op->x + freearr_x[i], op->y + freearr_x[i], op); move_bullet (tmp); } } @@ -1050,18 +999,26 @@ int create_bomb (object *op, object *caster, int dir, object *spell) { - object *tmp; int mflags; sint16 dx = op->x + freearr_x[dir], dy = op->y + freearr_y[dir]; maptile *m; mflags = get_map_flags (op->map, &m, dx, dy, &dx, &dy); - if ((mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, dx, dy) & MOVE_WALK)) + + // when creating a bomb below ourself it should always work, even + // when movement is blocked (somehow we got here, somehow we are here, + // so we should also be able to make a bomb here). (originally added + // to fix create bomb traps in doors, which cast with dir=0). + if (dir) { - new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way."); - return 0; + if ((mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, dx, dy) & MOVE_WALK)) + { + new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way."); + return 0; + } } + tmp = arch_to_object (spell->other_arch); /* level dependencies for bomb */ @@ -1072,9 +1029,8 @@ tmp->set_owner (op); set_spell_skill (op, caster, spell, tmp); - tmp->x = dx; - tmp->y = dy; - insert_ob_in_map (tmp, m, op, 0); + + m->insert (tmp, dx, dy, op); return 1; } @@ -1093,7 +1049,6 @@ * type is the type of spell - either SPELL_MANA or SPELL_GRACE. * this info is used for blocked magic/unholy spaces. */ - object * get_pointed_target (object *op, int dir, int range, int type) { @@ -1122,20 +1077,14 @@ return NULL; if (mflags & P_IS_ALIVE) - { - for (target = GET_MAP_OB (mp, x, y); target; target = target->above) - { - if (QUERY_FLAG (target->head ? target->head : target, FLAG_MONSTER)) - { - return target; - } - } - } + for (target = GET_MAP_OB (mp, x, y); target; target = target->above) + if (QUERY_FLAG (target, FLAG_MONSTER)) + return target; } + return NULL; } - /* cast_smite_arch() - the priest points to a creature and causes * a 'godly curse' to decend. * usual params - @@ -1144,7 +1093,6 @@ * dir = direction being cast * spell = spell object */ - int cast_smite_spell (object *op, object *caster, int dir, object *spell) { @@ -1164,9 +1112,11 @@ * can't be friendly to your god. */ - if (!target || QUERY_FLAG (target, FLAG_REFL_SPELL) + if (!target + || target->flag [FLAG_REFL_SPELL] || (!god && spell->stats.grace) - || (target->title && god && !strcmp (target->title, god->name)) || (target->race && god && strstr (target->race, god->race))) + || (god && target->title == god->name) + || (god && target->race.contains (god->race))) { new_draw_info (NDI_UNIQUE, 0, op, "Your request is unheeded."); return 0; @@ -1178,12 +1128,12 @@ return 0; /* tailor the effect by priest level and worshipped God */ - effect->level = caster_level (caster, spell); + effect->level = casting_level (caster, spell); effect->attacktype = spell->attacktype; if (effect->attacktype & (AT_HOLYWORD | AT_GODPOWER)) { if (tailor_god_spell (effect, op)) - new_draw_info_format (NDI_UNIQUE, 0, op, "%s answers your call!", determine_god (op)); + new_draw_info_format (NDI_UNIQUE, 0, op, "%s answers your call!", (const char *)determine_god (op)); else { new_draw_info (NDI_UNIQUE, 0, op, "Your request is ignored."); @@ -1227,14 +1177,11 @@ set_spell_skill (op, caster, spell, effect); /* ok, tell it where to be, and insert! */ - effect->x = target->x; - effect->y = target->y; - insert_ob_in_map (effect, target->map, op, 0); + effect->insert_at (target, op); return 1; } - /**************************************************************************** * * MAGIC MISSILE code. @@ -1246,65 +1193,47 @@ void move_missile (object *op) { - int i, mflags; - object *owner; - sint16 new_x, new_y; - maptile *m; - if (op->range-- <= 0) { - op->destroy (); + op->drop_and_destroy (); return; } - owner = op->owner; -#if 0 - /* It'd make things nastier if this wasn't here - spells cast by - * monster that are then killed would continue to survive - */ - if (owner == NULL) + mapxy pos (op); + pos.move (op->direction); + + if (!pos.normalise ()) { op->destroy (); return; } -#endif - new_x = op->x + DIRX (op); - new_y = op->y + DIRY (op); + mapspace &ms = pos.ms (); - mflags = get_map_flags (op->map, &m, new_x, new_y, &new_x, &new_y); - - if (!(mflags & P_OUT_OF_MAP) && ((mflags & P_IS_ALIVE) || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y)))) + if (ms.flags () & P_IS_ALIVE || ms.blocks (op)) { hit_map (op, op->direction, AT_MAGIC, 1); /* Basically, missile only hits one thing then goes away. * we need to remove it if someone hasn't already done so. */ - if (!op->destroyed ()) - op->destroy (); - + op->destroy (); return; } - op->remove (); - - if (!op->direction || (mflags & P_OUT_OF_MAP)) + if (!op->direction) { op->destroy (); return; } - op->x = new_x; - op->y = new_y; - op->map = m; - i = spell_find_dir (op->map, op->x, op->y, op->owner); + int i = spell_find_dir (pos.m, pos.x, pos.y, op->owner); if (i > 0 && i != op->direction) { op->direction = i; SET_ANIMATION (op, op->direction); } - insert_ob_in_map (op, op->map, op, 0); + pos.insert (op, op); } /**************************************************************************** @@ -1317,126 +1246,78 @@ * 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_NAME); + object *tmp = get_archetype (FORCE_NAME); tmp->speed = 0.01; tmp->stats.food = time; SET_FLAG (tmp, FLAG_IS_USED_UP); - tmp->glow_radius = radius; - if (tmp->glow_radius > MAX_LIGHT_RADII) - tmp->glow_radius = MAX_LIGHT_RADII; - - tmp->x = op->x; - tmp->y = op->y; - if (tmp->speed < MIN_ACTIVE_SPEED) - tmp->speed = MIN_ACTIVE_SPEED; /* safety */ + tmp->set_glow_radius (min (MAX_LIGHT_RADIUS, radius)); tmp = insert_ob_in_ob (tmp, op); + if (tmp->glow_radius > op->glow_radius) - op->glow_radius = tmp->glow_radius; + op->set_glow_radius (tmp->glow_radius); - 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; } - - - int cast_destruction (object *op, object *caster, object *spell_ob) { - int i, j, range, mflags, friendly = 0, dam, dur; - sint16 sx, sy; - maptile *m; - object *tmp; - const char *skill; + int range = spell_ob->range + SP_level_range_adjust (caster, spell_ob); + int dam = spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob); + int dur = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob); - range = spell_ob->range + SP_level_range_adjust (caster, spell_ob); - dam = spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob); - dur = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob); - if (QUERY_FLAG (op, FLAG_FRIENDLY) || op->type == PLAYER) - friendly = 1; + bool friendly = op->flag [FLAG_FRIENDLY] || op->is_player (); /* destruction doesn't use another spell object, so we need * update op's skill pointer so that exp is properly awarded. - * We do some shortcuts here - since this is just temporary - * and we'll reset the values back, we don't need to go through - * the full share string/free_string route. */ - skill = op->skill; + const shstr skill = op->skill; + if (caster == op) op->skill = spell_ob->skill; else if (caster->skill) op->skill = caster->skill; else - op->skill = NULL; + op->skill = 0; - change_skill (op, find_skill_by_name (op, op->skill), 1); + op->change_skill (find_skill_by_name (op, op->skill)); - for (i = -range; i < range; i++) + unordered_mapwalk (op, -range, -range, range, range) { - for (j = -range; j < range; j++) - { - m = op->map; - sx = op->x + i; - sy = op->y + j; - mflags = get_map_flags (m, &m, sx, sy, &sx, &sy); - if (mflags & P_OUT_OF_MAP) - continue; - if (mflags & P_IS_ALIVE) + mapspace &ms = m->at (nx, ny); + + if (ms.flags () & P_IS_ALIVE) + for (object *tmp = ms.bot; tmp; tmp = tmp->above) + if (tmp->flag [FLAG_ALIVE] || tmp->is_player ()) { - for (tmp = GET_MAP_OB (m, sx, sy); tmp; tmp = tmp->above) - { - if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER) - break; - } - if (tmp) + tmp = tmp->head_ (); + + if ((friendly && !tmp->flag [FLAG_FRIENDLY] && !tmp->is_player ()) + || (!friendly && (tmp->flag [FLAG_FRIENDLY] || tmp->is_player ()))) { - if (tmp->head) - tmp = tmp->head; + if (spell_ob->subtype == SP_DESTRUCTION) + { + hit_player (tmp, dam, op, spell_ob->attacktype, 0); - if ((friendly && !QUERY_FLAG (tmp, FLAG_FRIENDLY) && tmp->type != PLAYER) || - (!friendly && (QUERY_FLAG (tmp, FLAG_FRIENDLY) || tmp->type == PLAYER))) + if (spell_ob->other_arch) + m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op); + } + else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist [ATNR_MAGIC] != 100) { - if (spell_ob->subtype == SP_DESTRUCTION) - { - hit_player (tmp, dam, op, spell_ob->attacktype, 0); - if (spell_ob->other_arch) - { - tmp = arch_to_object (spell_ob->other_arch); - tmp->x = sx; - tmp->y = sy; - insert_ob_in_map (tmp, m, op, 0); - } - } - else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist[ATNR_MAGIC] != 100) - { - if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch) - { - object *effect = arch_to_object (spell_ob->other_arch); - - effect->x = sx; - effect->y = sy; - insert_ob_in_map (effect, m, op, 0); - } - } + if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch) + m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op); } } } - } } + op->skill = skill; return 1; } @@ -1460,8 +1341,10 @@ return 0; } + tmp = tmp->head_ (); + /* If we've already got a force of this type, don't add a new one. */ - for (force = tmp->inv; force != NULL; force = force->below) + for (force = tmp->inv; force; force = force->below) { if (force->type == FORCE && force->subtype == FORCE_CHANGE_ABILITY) { @@ -1477,10 +1360,11 @@ } } - if (force == NULL) + if (!force) { force = get_archetype (FORCE_NAME); force->subtype = FORCE_CHANGE_ABILITY; + if (spell_ob->race) force->name = spell_ob->race; else @@ -1500,14 +1384,14 @@ 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."); - } + 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; + force->speed = 1.f; + force->speed_left = -1.f; SET_FLAG (force, FLAG_APPLIED); if (god) @@ -1535,7 +1419,6 @@ } - /********************************************************************** * mood change * Arguably, this may or may not be an attack spell. But since it @@ -1549,16 +1432,14 @@ mood_change (object *op, object *caster, object *spell) { object *tmp, *god, *head; - int done_one, range, mflags, level, at, best_at; - sint16 x, y, nx, ny; - maptile *m; + int done_one, range, level, at, best_at; const char *race; /* We precompute some values here so that we don't have to keep * doing it over and over again. */ god = find_god (determine_god (op)); - level = caster_level (caster, spell); + level = casting_level (caster, spell); range = spell->range + SP_level_range_adjust (caster, spell); /* On the bright side, no monster should ever have a race of GOD_... @@ -1567,73 +1448,70 @@ */ if (!spell->race) race = NULL; - else if (god && !strcmp (spell->race, "GOD_SLAYING")) + else if (god && spell->race == shstr_GOD_SLAYING) race = god->slaying; - else if (god && !strcmp (spell->race, "GOD_FRIEND")) + else if (god && spell->race == shstr_GOD_FRIEND) race = god->race; else race = spell->race; + unordered_mapwalk (op, -range, -range, range, range) + { + mapspace &ms = m->at (nx, ny); - for (x = op->x - range; x <= op->x + range; x++) - for (y = op->y - range; y <= op->y + range; y++) - { - - done_one = 0; - m = op->map; - nx = x; - ny = y; - mflags = get_map_flags (m, &m, x, y, &nx, &ny); - if (mflags & P_OUT_OF_MAP) - continue; - - /* If there is nothing living on this space, no need to go further */ - if (!(mflags & P_IS_ALIVE)) - continue; - - for (tmp = GET_MAP_OB (m, nx, ny); tmp; tmp = tmp->above) - if (QUERY_FLAG (tmp, FLAG_MONSTER)) - break; - - /* There can be living objects that are not monsters */ - if (!tmp || tmp->type == PLAYER) - continue; - - /* Only the head has meaningful data, so resolve to that */ - if (tmp->head) - head = tmp->head; - else - head = tmp; - - /* Make sure the race is OK. Likewise, only effect undead if spell specifically allows it */ - if (race && head->race && !strstr (race, head->race)) - continue; - if (QUERY_FLAG (head, FLAG_UNDEAD) && !QUERY_FLAG (spell, FLAG_UNDEAD)) - continue; - - /* Now do a bunch of stuff related to saving throws */ - best_at = -1; - if (spell->attacktype) - { - for (at = 0; at < NROFATTACKS; at++) - if (spell->attacktype & (1 << at)) - if (best_at == -1 || head->resist[at] > head->resist[best_at]) - best_at = at; - - if (best_at == -1) - at = 0; - else - { - if (head->resist[best_at] == 100) - continue; - else - at = head->resist[best_at] / 5; - } - at -= level / 5; - if (did_make_save (head, head->level, at)) - continue; - } - else /* spell->attacktype */ + /* If there is nothing living on this space, no need to go further */ + if (!ms.flags () & P_IS_ALIVE) + continue; + + // players can only affect spaces that they can actually see + if (caster + && caster->contr + && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED) + continue; + + for (tmp = ms.top; tmp; tmp = tmp->below) + if (tmp->flag [FLAG_MONSTER]) + break; + + /* There can be living objects that are not monsters */ + if (!tmp) + continue; + + /* Only the head has meaningful data, so resolve to that */ + head = tmp->head_ (); + + /* Make sure the race is OK. Likewise, only effect undead if spell specifically allows it */ + if (race && head->race && !strstr (race, head->race)) + continue; + + if (QUERY_FLAG (head, FLAG_UNDEAD) && !QUERY_FLAG (spell, FLAG_UNDEAD)) + continue; + + /* Now do a bunch of stuff related to saving throws */ + best_at = -1; + if (spell->attacktype) + { + for (at = 0; at < NROFATTACKS; at++) + if (spell->attacktype & (1 << at)) + if (best_at == -1 || head->resist[at] > head->resist[best_at]) + best_at = at; + + if (best_at == -1) + at = 0; + else + { + if (head->resist[best_at] == 100) + continue; + else + at = head->resist[best_at] / 5; + } + + at -= level / 5; + if (did_make_save (head, head->level, at)) + continue; + } + else /* spell->attacktype */ + { /* Spell has no attacktype (charm & such), so we'll have a specific saving: * if spell level < monster level, no go @@ -1645,80 +1523,74 @@ charm a level 125 monster... Ryo, august 14th - */ - { - if (head->level > level) - continue; - if (random_roll (0, 100, caster, PREFER_LOW) >= (20 + MIN (50, 2 * (level - head->level)))) - /* Failed, no effect */ - continue; - } - - /* Done with saving throw. Now start effecting the monster */ - - /* aggravation */ - if (QUERY_FLAG (spell, FLAG_MONSTER)) - { - CLEAR_FLAG (head, FLAG_SLEEP); - if (QUERY_FLAG (head, FLAG_FRIENDLY)) - remove_friendly_object (head); - - done_one = 1; - head->enemy = op; - } - - /* calm monsters */ - if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE)) - { - SET_FLAG (head, FLAG_UNAGGRESSIVE); - head->enemy = NULL; - done_one = 1; - } - - /* berserk monsters */ - if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK)) - { - SET_FLAG (head, FLAG_BERSERK); - done_one = 1; - } - /* charm */ - if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY)) - { - SET_FLAG (head, FLAG_FRIENDLY); - /* Prevent uncontolled outbreaks of self replicating monsters. - Typical use case is charm, go somwhere, use aggravation to make hostile. - This could lead to fun stuff like mice outbreak in bigworld and server crawl. */ - CLEAR_FLAG (head, FLAG_GENERATOR); - head->set_owner (op); - set_spell_skill (op, caster, spell, head); - add_friendly_object (head); - head->attack_movement = PETMOVE; - done_one = 1; - change_exp (op, head->stats.exp / 2, head->skill, SK_EXP_ADD_SKILL); - head->stats.exp = 0; - } - - /* If a monster was effected, put an effect in */ - if (done_one && spell->other_arch) - { - tmp = arch_to_object (spell->other_arch); - tmp->x = nx; - tmp->y = ny; - insert_ob_in_map (tmp, m, op, 0); - } - } /* for y */ + */ + if (head->level > level) + continue; + + if (random_roll (0, 100, caster, PREFER_LOW) >= (20 + MIN (50, 2 * (level - head->level)))) + /* Failed, no effect */ + continue; + } + + /* Done with saving throw. Now start affecting the monster */ + done_one = 0; + + /* aggravation */ + if (QUERY_FLAG (spell, FLAG_MONSTER)) + { + CLEAR_FLAG (head, FLAG_SLEEP); + remove_friendly_object (head); + done_one = 1; + head->enemy = op; + } + + /* calm monsters */ + if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE)) + { + SET_FLAG (head, FLAG_UNAGGRESSIVE); + head->enemy = NULL; + done_one = 1; + } + + /* berserk monsters */ + if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK)) + { + SET_FLAG (head, FLAG_BERSERK); + done_one = 1; + } + + /* charm */ + if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY)) + { + INVOKE_OBJECT (KILL, head, ARG_OBJECT (caster)); + + /* Prevent uncontrolled outbreaks of self replicating monsters. + Typical use case is charm, go somwhere, use aggravation to make hostile. + This could lead to fun stuff like mice outbreak in bigworld and server crawl. */ + CLEAR_FLAG (head, FLAG_GENERATOR); + head->set_owner (op); + set_spell_skill (op, caster, spell, head); + add_friendly_object (head); + head->attack_movement = PETMOVE; + done_one = 1; + change_exp (op, head->stats.exp / 2, head->skill, SK_EXP_ADD_SKILL); + head->stats.exp = 0; + } + + /* If a monster was effected, put an effect in */ + if (done_one && spell->other_arch) + m->insert (arch_to_object (spell->other_arch), nx, ny, op); + } return 1; } - /* Move_ball_spell: This handles ball type spells that just sort of wander * about. was called move_ball_lightning, but since more than the ball * lightning spell used it, that seemed misnamed. * op is the spell effect. * note that duration is handled by process_object() in time.c */ - void move_ball_spell (object *op) { @@ -1747,7 +1619,6 @@ /* i bit 0: alters sign of offset * other bits (i / 2): absolute value of offset */ - int offset = ((i ^ j) & 1) ? (i / 2) : -(i / 2); int tmpdir = absdir (op->direction + offset); @@ -1759,6 +1630,7 @@ break; } } + if (dir == 0) { nx = op->x; @@ -1766,10 +1638,7 @@ m = op->map; } - op->remove (); - op->y = ny; - op->x = nx; - insert_ob_in_map (op, m, op, 0); + m->insert (op, nx, ny, op); dam_save = op->stats.dam; /* save the original dam: we do halfdam on surrounding squares */ @@ -1780,8 +1649,6 @@ */ for (j = 0; j < 9; j++) { - object *new_ob; - hx = nx + freearr_x[j]; hy = ny + freearr_y[j]; @@ -1799,18 +1666,13 @@ { if (j) op->stats.dam = dam_save / 2; - hit_map (op, j, op->attacktype, 1); + hit_map (op, j, op->attacktype, 1); } /* insert the other arch */ if (op->other_arch && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy)))) - { - new_ob = arch_to_object (op->other_arch); - new_ob->x = hx; - new_ob->y = hy; - insert_ob_in_map (new_ob, m, op, 0); - } + m->insert (arch_to_object (op->other_arch), hx, hy, op); } /* restore to the center location and damage */ @@ -1823,22 +1685,19 @@ /* pick another direction if the preferred dir is blocked. */ if (get_map_flags (op->map, &m, nx + freearr_x[i], ny + freearr_y[i], &hx, &hy) & P_OUT_OF_MAP || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy))) - { - i = absdir (i + rndm (0, 2) - 1); /* -1, 0, +1 */ - } + i = absdir (i + rndm (0, 2) - 1); /* -1, 0, +1 */ + op->direction = i; } } - -/* move_swarm_spell: peterm +/* move_swarm_spell: peterm * This is an implementation of the swarm spell. It was written for - * meteor swarm, but it could be used for any swarm. A swarm spell + * meteor swarm, but it could be used for any swarm. A swarm spell * is a special type of object that casts swarms of other types - * of spells. Which spell it casts is flexible. It fires the spells + * of spells. Which spell it casts is flexible. It fires the spells * from a set of squares surrounding the caster, in a given direction. */ - void move_swarm_spell (object *op) { @@ -1849,23 +1708,29 @@ int adjustdir; maptile *m; #endif - int basedir; - object *owner; + object *owner = op->env; - owner = op->owner; - if (op->duration == 0 || owner == NULL) + if (!owner) // MUST not happen, remove when true TODO { + LOG (llevError, "swarm spell found outside inventory: %s\n", op->debug_desc ()); op->destroy (); return; } + if (!op->duration || !owner->is_on_map ()) + { + op->drop_and_destroy (); + return; + } + op->duration--; - basedir = op->direction; - if (basedir == 0) + int basedir = op->direction; + if (!basedir) { /* spray in all directions! 8) */ - basedir = rndm (1, 8); + op->facing = (op->facing + op->state) & 7; + basedir = op->facing + 1; } #if 0 @@ -1873,7 +1738,7 @@ // (a wall 2 cells away will block the effect...) and // doesn't work for SP_BULLET anyhow, so again tests the wrong // space. - // should be fixed later, but correctness before featurs... + // should be fixed later, but correctness before features... // (schmorp) /* new offset calculation to make swarm element distribution @@ -1936,13 +1801,10 @@ if (op->spell->subtype == SP_BULLET) fire_bullet (owner, op, basedir, op->spell); else if (op->spell->subtype == SP_MAGIC_MISSILE) - fire_arch_from_position (owner, op, op->x, op->y, basedir, op->spell); + fire_arch_from_position (owner, op, owner->x, owner->y, basedir, op->spell); } } - - - /* fire_swarm: * The following routine creates a swarm of objects. It actually * sets up a specific swarm object, which then fires off all @@ -1954,43 +1816,38 @@ * spell - the spell that is this spell. * n: the number to be fired. */ - int fire_swarm (object *op, object *caster, object *spell, int dir) { - object *tmp; - int i; - if (!spell->other_arch) return 0; - tmp = get_archetype (SWARM_SPELL); - tmp->x = op->x; - tmp->y = op->y; - tmp->set_owner (op); /* needed so that if swarm elements kill, caster gets xp. */ - set_spell_skill (op, caster, spell, tmp); - - tmp->level = caster_level (caster, spell); /*needed later, to get level dep. right. */ - tmp->spell = arch_to_object (spell->other_arch); + object *tmp = archetype::get (SWARM_SPELL); + set_spell_skill (op, caster, spell, tmp); + tmp->level = casting_level (caster, spell); /* needed later, to get level dep. right. */ + tmp->spell = spell->other_arch->instance (); tmp->attacktype = tmp->spell->attacktype; if (tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER) - { - if (!tailor_god_spell (tmp, op)) - return 1; - } + if (!tailor_god_spell (tmp, op)) + return 1; + tmp->duration = SP_level_duration_adjust (caster, spell); - for (i = 0; i < spell->duration; i++) + for (int i = 0; i < spell->duration; i++) tmp->duration += die_roll (1, 3, op, PREFER_HIGH); - tmp->direction = dir; tmp->invisible = 1; - insert_ob_in_map (tmp, op->map, op, 0); + tmp->flag [FLAG_NO_DROP] = 1; // make sure it stays in inv, or else + tmp->direction = dir; + tmp->facing = rndm (1, 8); // initial firing direction + tmp->state = rndm (4) * 2 + 1; // direction increment + + op->insert (tmp); + return 1; } - /* See the spells documentation file for why this is its own * function. */ @@ -2004,42 +1861,40 @@ dam = spell->stats.dam + SP_level_dam_adjust (caster, spell); - if (!dir) + 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; - 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); - 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_OUT_OF_MAP) - { - new_draw_info (NDI_UNIQUE, 0, op, "Nothing is there."); - return 0; - } + if (mflags & P_IS_ALIVE && spell->attacktype) + { + 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; - if (mflags & P_IS_ALIVE && spell->attacktype) - { - 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, spell->attacktype, 1); - return 1; /* one success only! */ - } - } + hit_player (target, dam, op, spell->attacktype, 1); + return 1; /* one success only! */ + } + } - /* no live target, perhaps a wall is in the way? */ - if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y))) - { - new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way."); - return 0; + /* no live target, perhaps a wall is in the way? */ + if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y))) + { + 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 */ @@ -2049,21 +1904,21 @@ LOG (llevError, "Error: spell arch for cast_light() missing.\n"); return 0; } + tmp->stats.food = spell->duration + SP_level_duration_adjust (caster, spell); - if (tmp->glow_radius) - { - tmp->glow_radius = spell->range + SP_level_range_adjust (caster, spell); - if (tmp->glow_radius > MAX_LIGHT_RADII) - tmp->glow_radius = MAX_LIGHT_RADII; - } - tmp->x = x; - tmp->y = y; - insert_ob_in_map (tmp, m, op, 0); - return 1; -} + if (tmp->glow_radius) + tmp->set_glow_radius ( + clamp (spell->range + SP_level_range_adjust (caster, spell), 1, MAX_LIGHT_RADIUS) + ); + if (dir) + m->insert (tmp, x, y, op); + else + caster->outer_env_or_self ()->insert (tmp); + return 1; +} /* cast_cause_disease: this spell looks along from the * player and infects someone. @@ -2071,7 +1926,6 @@ * to cast, disease_arch is the specific disease, and type is the spell number * perhaps this should actually be in disease.c? */ - int cast_cause_disease (object *op, object *caster, object *spell, int dir) { @@ -2088,6 +1942,7 @@ */ if (!dir) dir = op->facing; + if (!dir) return 0; /* won't find anything if casting on ourself, so just return */ @@ -2124,14 +1979,14 @@ disease->set_owner (op); set_spell_skill (op, caster, spell, disease); disease->stats.exp = 0; - disease->level = caster_level (caster, spell); + disease->level = casting_level (caster, spell); /* do level adjustments */ if (disease->stats.wc) disease->stats.wc += dur_mod / 2; if (disease->magic > 0) - disease->magic += dur_mod / 4; + disease->magic += dur_mod / 8; if (disease->stats.maxhp > 0) disease->stats.maxhp += dur_mod; @@ -2176,16 +2031,10 @@ if (infect_object (walk, disease, 1)) { - object *flash; /* visual effect for inflicting disease */ - new_draw_info_format (NDI_UNIQUE, 0, op, "You inflict %s on %s!", &disease->name, &walk->name); disease->destroy (); /* don't need this one anymore */ - flash = get_archetype (ARCH_DETECT_MAGIC); - flash->x = x; - flash->y = y; - flash->map = walk->map; - insert_ob_in_map (flash, walk->map, op, 0); + walk->map->insert (get_archetype ("detect_magic"), x, y, op); return 1; } @@ -2193,6 +2042,7 @@ } } /* if living creature */ } /* for range of spaces */ + new_draw_info (NDI_UNIQUE, 0, op, "No one caught anything!"); return 1; }