--- deliantra/server/server/spell_attack.C 2008/09/29 06:32:09 1.64 +++ deliantra/server/server/spell_attack.C 2018/11/17 23:40:04 1.117 @@ -1,23 +1,25 @@ /* * This file is part of Deliantra, the Roguelike Realtime MMORPG. - * - * Copyright (©) 2005,2006,2007,2008 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 GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * + * + * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team + * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * Copyright (©) 2002-2003 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992 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 GNU General Public License - * along with this program. If not, see . - * + * + * 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 */ @@ -38,7 +40,7 @@ * but moved here so it could be applied to bolts too * op is the spell object. */ -void +static void check_spell_knockback (object *op) { int weight_move; @@ -60,7 +62,7 @@ int num_sections = 1; /* don't move DM */ - if (QUERY_FLAG (tmp, FLAG_WIZ)) + if (tmp->flag [FLAG_WIZ]) return; /* don't move parts of objects */ @@ -68,7 +70,7 @@ continue; /* don't move floors or immobile objects */ - if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) || (!QUERY_FLAG (tmp, FLAG_ALIVE) && QUERY_FLAG (tmp, FLAG_NO_PICK))) + if (tmp->flag [FLAG_IS_FLOOR] || (!tmp->flag [FLAG_ALIVE] && tmp->flag [FLAG_NO_PICK])) continue; /* count the object's sections */ @@ -87,14 +89,14 @@ if (rndm (0, weight_move - 1) > ((tmp->weight / num_sections) * frictionmod)) { /* move it. */ - /* move_object is really for monsters, but looking at + /* move_object is really for monsters, but looking at * the move_object function, it appears that it should * also be safe for objects. * This does return if successful or not, but * I don't see us doing anything useful with that information * right now. */ - move_object (tmp, absdir (op->stats.sp)); + tmp->move (absdir (op->stats.sp)); } } @@ -109,7 +111,7 @@ /* Causes op to fork. op is the original bolt, tmp * is the first piece of the fork. */ -void +static void forklightning (object *op, object *tmp) { int new_dir = 1; /* direction or -1 for left, +1 for right 0 if no new bolt */ @@ -130,7 +132,7 @@ /* check the new dir for a wall and in the map */ t_dir = absdir (tmp->direction + new_dir); - if (get_map_flags (tmp->map, &m, tmp->x + freearr_x[t_dir], tmp->y + freearr_y[t_dir], &sx, &sy) & P_OUT_OF_MAP) + if (get_map_flags (tmp->map, &m, tmp->x + DIRX (t_dir), tmp->y + DIRY (t_dir), &sx, &sy) & P_OUT_OF_MAP) return; if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (m, sx, sy))) @@ -167,7 +169,7 @@ if (--op->duration < 0) { - op->destroy (); + op->drop_and_destroy (); return; } @@ -180,8 +182,8 @@ op->range = 0; else { - x = op->x + DIRX (op); - y = op->y + DIRY (op); + x = op->x + DIRX (op->direction); + y = op->y + DIRY (op->direction); m = op->map; mflags = get_map_flags (m, &m, x, y, &x, &y); @@ -195,7 +197,7 @@ */ 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)) + if (!op->flag [FLAG_REFLECTING]) return; /* Since walls don't run diagonal, if the bolt is in @@ -218,13 +220,13 @@ * op->direction is within an adjacent map but either * op->direction-1 or op->direction+1 does not exist. */ - mflags = get_map_flags (op->map, &m, op->x + freearr_x[absdir (op->direction - 1)], - op->y + freearr_y[absdir (op->direction - 1)], &x, &y); + mflags = get_map_flags (op->map, &m, op->x + DIRX (absdir (op->direction - 1)), + op->y + DIRY (absdir (op->direction - 1)), &x, &y); left = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)); - mflags = get_map_flags (op->map, &m, op->x + freearr_x[absdir (op->direction + 1)], - op->y + freearr_y[absdir (op->direction + 1)], &x, &y); + mflags = get_map_flags (op->map, &m, op->x + DIRX (absdir (op->direction + 1)), + op->y + DIRY (absdir (op->direction + 1)), &x, &y); right = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)); if (left == right) @@ -248,7 +250,7 @@ tmp->duration++; /* New forking code. Possibly create forks of this object - * going off in other directions. + * going off in other directions. */ if (tmp->stats.Dex && rndm (0, 99) < tmp->stats.Dex) forklightning (op, tmp); /* stats.Dex % of forking */ @@ -278,7 +280,7 @@ if (!spob->other_arch) return 0; - tmp = arch_to_object (spob->other_arch); + tmp = spob->other_arch->instance (); if (tmp == NULL) return 0; @@ -295,21 +297,21 @@ tmp->stats.Con = spob->stats.Con; tmp->direction = dir; - if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) + if (tmp->flag [FLAG_IS_TURNABLE]) SET_ANIMATION (tmp, dir); tmp->set_owner (op); set_spell_skill (op, caster, spob, tmp); - tmp->x = op->x + DIRX (tmp); - tmp->y = op->y + DIRY (tmp); + tmp->x = op->x + DIRX (tmp->direction); + tmp->y = op->y + DIRY (tmp->direction); tmp->map = op->map; maptile *newmap; 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; } @@ -317,9 +319,9 @@ if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y))) { - if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) + if (!tmp->flag [FLAG_REFLECTING]) { - tmp->destroy (); + tmp->drop_and_destroy (); return 0; } @@ -365,10 +367,10 @@ { sint16 dx, dy; - dx = op->x + freearr_x[i]; - dy = op->y + freearr_y[i]; + dx = op->x + DIRX (i); + dy = op->y + DIRY (i); - /* ok_to_put_more already does things like checks for walls, + /* 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)) @@ -390,7 +392,7 @@ * poison cloud ball, etc. op is the object to * explode. */ -void +static void explode_bullet (object *op) { object *tmp, *owner; @@ -440,14 +442,17 @@ } /* other_arch contains what this explodes into */ - tmp = arch_to_object (op->other_arch); + tmp = op->other_arch->instance (); tmp->set_owner (op); tmp->skill = op->skill; 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; @@ -519,13 +524,14 @@ for (tmp = op->ms ().bot; tmp; tmp = tmp->above) { - if (QUERY_FLAG (tmp, FLAG_ALIVE)) + if (tmp->flag [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)) + if (!op->flag [FLAG_REMOVED]) { op->destroy (); return; @@ -543,14 +549,10 @@ 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 */ - /* peterm: added to make comet leave a trail of burnouts + /* peterm: added to make comet leave a trail of burnouts it's an unadulterated hack, but the effect is cool. */ if (op->stats.sp == SP_METEOR) { @@ -571,18 +573,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); @@ -592,7 +596,7 @@ return; } - if (!(op = m->insert (op, new_x, new_y, op))) + if (!(op = pos.insert (op, op))) return; if (reflwall (op->map, op->x, op->y, op)) @@ -639,14 +643,14 @@ tmp->dam_modifier = spob->stats.food + SP_level_dam_adjust (caster, spob); tmp->direction = dir; - if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) + if (tmp->flag [FLAG_IS_TURNABLE]) SET_ANIMATION (tmp, dir); 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 + DIRX (dir); + tmp->y = op->y + DIRY (dir); tmp->map = op->map; maptile *newmap; @@ -659,9 +663,22 @@ tmp->map = newmap; + // in case the bullet has direction 0 we explode it in place. + // direction 0 is possible for instance when a poison cloud trap springs. + if (tmp->direction == 0) + { + if (tmp->other_arch + && (tmp = tmp->insert_at (tmp, op))) // insert before explode cleanly + explode_bullet (tmp); // explode object will/should remove tmp + else + tmp->destroy (); + + return 0; + } + if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y))) { - if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) + if (!tmp->flag [FLAG_REFLECTING]) { tmp->destroy (); return 0; @@ -686,10 +703,10 @@ *****************************************************************************/ /* drops an object based on what is in the cone's "other_arch" */ -void +static void cone_drop (object *op) { - object *new_ob = arch_to_object (op->other_arch); + object *new_ob = op->other_arch->instance (); new_ob->level = op->level; new_ob->set_owner (op->owner); @@ -715,7 +732,7 @@ } /* lava saves it's life, but not yours :) */ - if (QUERY_FLAG (op, FLAG_LIFESAVE)) + if (op->flag [FLAG_LIFESAVE]) { hit_map (op, 0, op->attacktype, 0); return; @@ -735,15 +752,20 @@ 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. + * 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) { @@ -762,7 +784,7 @@ 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)]; + sint16 x = op->x + DIRX (absdir (op->stats.sp + i)), y = op->y + DIRY (absdir (op->stats.sp + i)); if (ok_to_put_more (op->map, x, y, op, op->attacktype)) { @@ -801,9 +823,9 @@ if (!spell->other_arch) return 0; - if (op->type == PLAYER && QUERY_FLAG (op, FLAG_UNDEAD) && op->attacktype & AT_TURN_UNDEAD) + if (op->type == PLAYER && op->flag [FLAG_UNDEAD] && op->attacktype & AT_TURN_UNDEAD) { - new_draw_info (NDI_UNIQUE, 0, op, "Your undead nature prevents you from turning undead!"); + op->failmsg ("Your undead nature prevents you from turning undead!"); return 0; } @@ -821,18 +843,14 @@ 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, @@ -849,8 +867,8 @@ continue; } - x = op->x + freearr_x[d]; - y = op->y + freearr_y[d]; + x = op->x + DIRX (d); + y = op->y + DIRY (d); if (get_map_flags (op->map, &m, x, y, &sx, &sy) & P_OUT_OF_MAP) continue; @@ -859,7 +877,7 @@ continue; success = 1; - tmp = arch_to_object (spell->other_arch); + tmp = spell->other_arch->instance (); tmp->set_owner (op); set_spell_skill (op, caster, spell, tmp); tmp->level = casting_level (caster, spell); @@ -944,7 +962,7 @@ if (op->env) { - if (env->map == NULL) + if (!env->map) return; if (!(op = op->insert_at (env, op))) @@ -954,7 +972,7 @@ // 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; @@ -968,10 +986,10 @@ { for (int i = 1; i < 9; i++) { - if (out_of_map (op->map, op->x + freearr_x[i], op->y + freearr_x[i])) + if (out_of_map (op->map, op->x + DIRX (i), op->y + DIRX (i))) continue; - object *tmp = arch_to_object (at); + object *tmp = at->instance (); tmp->direction = i; tmp->range = op->range; tmp->stats.dam = op->stats.dam; @@ -981,10 +999,10 @@ if (op->skill && op->skill != tmp->skill) tmp->skill = op->skill; - if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) + if (tmp->flag [FLAG_IS_TURNABLE]) SET_ANIMATION (tmp, i); - op->map->insert (tmp, op->x + freearr_x[i], op->y + freearr_x[i], op); + op->map->insert (tmp, op->x + DIRX (i), op->y + DIRX (i), op); move_bullet (tmp); } } @@ -997,17 +1015,25 @@ { object *tmp; int mflags; - sint16 dx = op->x + freearr_x[dir], dy = op->y + freearr_y[dir]; + sint16 dx = op->x + DIRX (dir), dy = op->y + DIRY (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)) + { + op->failmsg ("There is something in the way."); + return 0; + } } - tmp = arch_to_object (spell->other_arch); + tmp = spell->other_arch->instance (); /* level dependencies for bomb */ tmp->range = spell->range + SP_level_range_adjust (caster, spell); @@ -1037,7 +1063,7 @@ * type is the type of spell - either SPELL_MANA or SPELL_GRACE. * this info is used for blocked magic/unholy spaces. */ -object * +static object * get_pointed_target (object *op, int dir, int range, int type) { object *target; @@ -1050,8 +1076,8 @@ for (dist = 1; dist < range; dist++) { - x = op->x + freearr_x[dir] * dist; - y = op->y + freearr_y[dir] * dist; + x = op->x + DIRX (dir) * dist; + y = op->y + DIRY (dir) * dist; mp = op->map; mflags = get_map_flags (op->map, &mp, x, y, &x, &y); @@ -1066,15 +1092,15 @@ if (mflags & P_IS_ALIVE) for (target = GET_MAP_OB (mp, x, y); target; target = target->above) - if (QUERY_FLAG (target, FLAG_MONSTER)) + if (target->flag [FLAG_MONSTER]) return target; } return NULL; } -/* cast_smite_arch() - the priest points to a creature and causes - * a 'godly curse' to decend. +/* cast_smite_spell() - the priest points to a creature and causes + * a 'godly curse' to decend. * usual params - * op = player * caster = object casting the spell. @@ -1086,12 +1112,10 @@ { object *effect, *target; object *god = find_god (determine_god (op)); - int range; - range = spell->range + SP_level_range_adjust (caster, spell); target = get_pointed_target (op, dir, 50, spell->stats.grace ? SPELL_GRACE : SPELL_MANA); - /* Bunch of conditions for casting this spell. Note that only + /* Bunch of conditions for casting this spell. Note that only * require a god if this is a cleric spell (requires grace). * This makes this spell much more general purpose - it can be used * by wizards also, which is good, because I think this is a very @@ -1100,16 +1124,18 @@ * 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."); + op->failmsg ("Your request is unheeded."); return 0; } if (spell->other_arch) - effect = arch_to_object (spell->other_arch); + effect = spell->other_arch->instance (); else return 0; @@ -1119,10 +1145,10 @@ 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."); + op->failmsg ("Your request is ignored."); return 0; } } @@ -1136,7 +1162,7 @@ effect->level = spell->stats.dam + SP_level_dam_adjust (caster, spell); /* casting death spells at undead isn't a good thing */ - if (QUERY_FLAG (target, FLAG_UNDEAD)) + if (target->flag [FLAG_UNDEAD]) { if (random_roll (0, 2, op, PREFER_LOW)) { @@ -1181,7 +1207,7 @@ { if (op->range-- <= 0) { - op->destroy (); + op->drop_and_destroy (); return; } @@ -1227,30 +1253,27 @@ ****************************************************************************/ /* make_object_glow() - currently only makes living objects glow. - * we do this by creating a force and inserting it in the + * 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 + * make this work for non-living objects, we would have to * give them the capability to have an inventory. b.t. */ -int +static int make_object_glow (object *op, int radius, int time) { /* some things are unaffected... */ if (op->path_denied & PATH_LIGHT) return 0; - object *tmp = get_archetype (FORCE_NAME); - tmp->speed = 0.01; + object *tmp = archetype::get (FORCE_NAME); + tmp->set_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->set_flag (FLAG_IS_USED_UP); + 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); return 1; } @@ -1258,79 +1281,46 @@ 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; - - /* 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; - if (caster == op) - op->skill = spell_ob->skill; - else if (caster->skill) - op->skill = caster->skill; - else - op->skill = NULL; + bool friendly = op->flag [FLAG_FRIENDLY] || op->is_player (); - op->change_skill (find_skill_by_name (op, op->skill)); - - for (i = -range; i <= range; i++) + dynbuf buf; + unordered_mapwalk (buf, op, -range, -range, range, range) { - for (j = -range; j <= range; j++) - { - m = op->map; - sx = op->x + i; - sy = op->y + j; + mapspace &ms = m->at (nx, ny); - mflags = get_map_flags (m, &m, sx, sy, &sx, &sy); - if (mflags & P_OUT_OF_MAP) - continue; - - if (mflags & P_IS_ALIVE) - { - for (tmp = GET_MAP_OB (m, sx, sy); tmp; tmp = tmp->above) - if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER) - break; + if (ms.flags () & P_IS_ALIVE) + for (object *next, *tmp = ms.bot; tmp; tmp = next) + { + next = tmp->above; - if (tmp) - { - if (tmp->head) - tmp = tmp->head; + if (tmp->flag [FLAG_ALIVE] || tmp->is_player ()) + { + tmp = tmp->head_ (); - if ((friendly && !QUERY_FLAG (tmp, FLAG_FRIENDLY) && tmp->type != PLAYER) || - (!friendly && (QUERY_FLAG (tmp, FLAG_FRIENDLY) || tmp->type == PLAYER))) - { - if (spell_ob->subtype == SP_DESTRUCTION) - { - hit_player (tmp, dam, op, spell_ob->attacktype, 0); - - if (spell_ob->other_arch) - m->insert (arch_to_object (spell_ob->other_arch), sx, sy, op); - } - else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist[ATNR_MAGIC] != 100) - { - if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch) - m->insert (arch_to_object (spell_ob->other_arch), sx, sy, op); - } - } - } - } - } + if ((friendly && !tmp->flag [FLAG_FRIENDLY] && !tmp->is_player ()) + || (!friendly && (tmp->flag [FLAG_FRIENDLY] || tmp->is_player ()))) + { + if (spell_ob->subtype == SP_DESTRUCTION) + { + hit_player (tmp, dam, op, spell_ob->attacktype, 0); + + if (spell_ob->other_arch) + m->insert (spell_ob->other_arch->instance (), nx, ny, op); + } + else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist [ATNR_MAGIC] != 100) + { + if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch) + m->insert (spell_ob->other_arch->instance (), nx, ny, op); + } + } + } + } } - op->skill = skill; return 1; } @@ -1339,7 +1329,6 @@ * CURSE * ***************************************************************************/ - int cast_curse (object *op, object *caster, object *spell_ob, int dir) { @@ -1349,7 +1338,7 @@ tmp = get_pointed_target (op, (dir == 0) ? op->direction : dir, spell_ob->range, SPELL_GRACE); if (!tmp) { - new_draw_info (NDI_UNIQUE, 0, op, "There is no one in that direction to curse."); + op->failmsg ("There is no one in that direction to curse."); return 0; } @@ -1374,7 +1363,7 @@ if (!force) { - force = get_archetype (FORCE_NAME); + force = archetype::get (FORCE_NAME); force->subtype = FORCE_CHANGE_ABILITY; if (spell_ob->race) @@ -1402,9 +1391,9 @@ } force->duration = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob) * 50; - force->speed = 1.f; force->speed_left = -1.f; - SET_FLAG (force, FLAG_APPLIED); + force->set_speed (1.f); + force->set_flag (FLAG_APPLIED); if (god) { @@ -1427,8 +1416,8 @@ change_abil (tmp, force); /* Mostly to display any messages */ insert_ob_in_ob (force, tmp); tmp->update_stats (); - return 1; + return 1; } /********************************************************************** @@ -1444,9 +1433,7 @@ 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 @@ -1462,151 +1449,144 @@ */ 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; - 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; - - // players can only affect spaces that they can actually see - if (caster && caster->contr - && caster->contr->visibility_at (m, nx, ny) < 70) - continue; - - for (tmp = GET_MAP_TOP (m, nx, ny); tmp; tmp = tmp->below) - 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 */ - { - /* - Spell has no attacktype (charm & such), so we'll have a specific saving: - * if spell level < monster level, no go - * else, chance of effect = 20 + min( 50, 2 * ( spell level - monster level ) ) - - The chance will then be in the range [20-70] percent, not too bad. - - This is required to fix the 'charm monster' abuse, where a player level 1 can - 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; - } + dynbuf buf; + unordered_mapwalk (buf, op, -range, -range, range, range) + { + mapspace &ms = m->at (nx, ny); - /* Done with saving throw. Now start affecting the monster */ + /* If there is nothing living on this space, no need to go further */ + if (!(ms.flags () & P_IS_ALIVE)) + continue; - /* aggravation */ - if (QUERY_FLAG (spell, FLAG_MONSTER)) - { - CLEAR_FLAG (head, FLAG_SLEEP); - remove_friendly_object (head); - done_one = 1; - head->enemy = op; - } + // players can only affect spaces that they can actually see + if (caster + && caster->contr + && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED) + continue; - /* calm monsters */ - if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE)) - { - SET_FLAG (head, FLAG_UNAGGRESSIVE); - head->enemy = NULL; - done_one = 1; - } + for (tmp = ms.top; tmp; tmp = tmp->below) + if (tmp->flag [FLAG_MONSTER]) + break; - /* berserk monsters */ - if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK)) - { - SET_FLAG (head, FLAG_BERSERK); - done_one = 1; - } + /* There can be living objects that are not monsters */ + if (!tmp) + continue; - /* charm */ - if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY)) - { - INVOKE_OBJECT (KILL, head, ARG_OBJECT (caster)); + /* Only the head has meaningful data, so resolve to that */ + head = tmp->head_ (); - /* 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; - } + /* 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 (head->flag [FLAG_UNDEAD] && !spell->flag [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 + * else, chance of effect = 20 + min( 50, 2 * ( spell level - monster level ) ) + + The chance will then be in the range [20-70] percent, not too bad. - /* 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); - } /* for y */ + This is required to fix the 'charm monster' abuse, where a player level 1 can + 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 affecting the monster */ + done_one = 0; + + /* aggravation */ + if (spell->flag [FLAG_MONSTER]) + { + head->clr_flag (FLAG_SLEEP); + remove_friendly_object (head); + done_one = 1; + head->enemy = op; + } + + /* calm monsters */ + if (spell->flag [FLAG_UNAGGRESSIVE] && !head->flag [FLAG_UNAGGRESSIVE]) + { + head->set_flag (FLAG_UNAGGRESSIVE); + head->enemy = NULL; + done_one = 1; + } + + /* berserk monsters */ + if (spell->flag [FLAG_BERSERK] && !head->flag [FLAG_BERSERK]) + { + head->set_flag (FLAG_BERSERK); + done_one = 1; + } + + /* charm */ + if (spell->flag [FLAG_NO_ATTACK] && !head->flag [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. */ + head->clr_flag (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 (spell->other_arch->instance (), 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. @@ -1641,18 +1621,18 @@ /* 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); - nx = op->x + freearr_x[tmpdir]; - ny = op->y + freearr_y[tmpdir]; + nx = op->x + DIRX (tmpdir); + ny = op->y + DIRY (tmpdir); if (!(get_map_flags (op->map, &m, nx, ny, &nx, &ny) & P_OUT_OF_MAP) && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, nx, ny)))) { dir = tmpdir; break; } } + if (dir == 0) { nx = op->x; @@ -1662,7 +1642,7 @@ m->insert (op, nx, ny, op); - dam_save = op->stats.dam; /* save the original dam: we do halfdam on + dam_save = op->stats.dam; /* save the original dam: we do halfdam on surrounding squares */ /* loop over current square and neighbors to hit. @@ -1671,8 +1651,8 @@ */ for (j = 0; j < 9; j++) { - hx = nx + freearr_x[j]; - hy = ny + freearr_y[j]; + hx = nx + DIRX (j); + hy = ny + DIRY (j); m = op->map; mflags = get_map_flags (m, &m, hx, hy, &hx, &hy); @@ -1694,7 +1674,7 @@ /* insert the other arch */ if (op->other_arch && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy)))) - m->insert (arch_to_object (op->other_arch), hx, hy, op); + m->insert (op->other_arch->instance (), hx, hy, op); } /* restore to the center location and damage */ @@ -1705,7 +1685,7 @@ if (i >= 0) { /* we have a preferred direction! */ /* 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 || + if (get_map_flags (op->map, &m, nx + DIRX (i), ny + DIRY (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 */ @@ -1713,12 +1693,12 @@ } } -/* 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 * is a special type of object that casts swarms of other types * of spells. Which spell it casts is flexible. It fires the spells - * from a set of squares surrounding the caster, in a given direction. + * from a set of squares surrounding the caster, in a given direction. */ void move_swarm_spell (object *op) @@ -1741,7 +1721,7 @@ if (!op->duration || !owner->is_on_map ()) { - op->destroy (); + op->drop_and_destroy (); return; } @@ -1764,7 +1744,7 @@ // (schmorp) /* new offset calculation to make swarm element distribution - * more uniform + * more uniform */ if (op->duration) { @@ -1782,14 +1762,14 @@ adjustdir = 0; /* fire the last one from forward. */ } - target_x = op->x + freearr_x[absdir (basedir + adjustdir)]; - target_y = op->y + freearr_y[absdir (basedir + adjustdir)]; + target_x = op->x + DIRX (absdir (basedir + adjustdir)); + target_y = op->y + DIRY (absdir (basedir + adjustdir)); /* back up one space so we can hit point-blank targets, but this - * necessitates extra out_of_map check below + * necessitates extra out_of_map check below */ - origin_x = target_x - freearr_x[basedir]; - origin_y = target_y - freearr_y[basedir]; + origin_x = target_x - DIRX (basedir); + origin_y = target_y - DIRY (basedir); /* spell pointer is set up for the spell this casts. Since this @@ -1830,7 +1810,7 @@ /* fire_swarm: * The following routine creates a swarm of objects. It actually * sets up a specific swarm object, which then fires off all - * the parts of the swarm. + * the parts of the swarm. * * op: the owner * caster: the caster (owner, wand, rod, scroll) @@ -1883,66 +1863,67 @@ 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 + DIRX (dir); + y = op->y + DIRY (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) + { + op->failmsg ("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 (target->flag [FLAG_MONSTER]) + { + /* oky doky. got a target monster. Lets make a blinding attack */ + if (target->head) + target = target->head; - 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))) + { + op->failmsg ("Something is in the way."); + return 0; + } } /* ok, looks groovy to just insert a new light on the map */ - tmp = arch_to_object (spell->other_arch); + tmp = spell->other_arch->instance (); if (!tmp) { 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->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); - m->insert (tmp, x, y, op); return 1; } /* cast_cause_disease: this spell looks along from the - * player and infects someone. + * 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 * perhaps this should actually be in disease.c? @@ -1958,7 +1939,7 @@ x = op->x; y = op->y; - /* If casting from a scroll, no direction will be available, so refer to the + /* If casting from a scroll, no direction will be available, so refer to the * direction the player is pointing. */ if (!dir) @@ -1975,8 +1956,8 @@ /* search in a line for a victim */ for (i = 1; i < range; i++) { - x = op->x + i * freearr_x[dir]; - y = op->y + i * freearr_y[dir]; + x = op->x + i * DIRX (dir); + y = op->y + i * DIRY (dir); m = op->map; mflags = get_map_flags (m, &m, x, y, &x, &y); @@ -1993,9 +1974,9 @@ { /* 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)) + if (walk->flag [FLAG_MONSTER] || (walk->type == PLAYER)) { /* found a victim */ - object *disease = arch_to_object (spell->other_arch); + object *disease = spell->other_arch->instance (); disease->set_owner (op); set_spell_skill (op, caster, spell, disease); @@ -2003,59 +1984,32 @@ 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 / 8; - - if (disease->stats.maxhp > 0) - disease->stats.maxhp += dur_mod; - - if (disease->stats.maxgrace > 0) - disease->stats.maxgrace += dur_mod; - - if (disease->stats.dam) - { - if (disease->stats.dam > 0) - disease->stats.dam += dam_mod; - else - disease->stats.dam -= dam_mod; - } + if (disease->stats.wc ) disease->stats.wc += dur_mod / 2; + if (disease->magic > 0) disease->magic += dur_mod / 8; + if (disease->stats.maxhp > 0) disease->stats.maxhp += dur_mod; + if (disease->stats.maxgrace > 0) disease->stats.maxgrace += dur_mod; if (disease->last_sp) { disease->last_sp -= 2 * dam_mod; + if (disease->last_sp < 1) disease->last_sp = 1; } - if (disease->stats.maxsp) - { - if (disease->stats.maxsp > 0) - disease->stats.maxsp += dam_mod; - else - disease->stats.maxsp -= dam_mod; - } - - if (disease->stats.ac) - disease->stats.ac += dam_mod; - - if (disease->last_eat) - disease->last_eat -= dam_mod; - - if (disease->stats.hp) - disease->stats.hp -= dam_mod; - - if (disease->stats.sp) - disease->stats.sp -= dam_mod; + if (disease->stats.dam ) disease->stats.dam += copysignl (disease->stats.dam , dam_mod); + if (disease->stats.maxsp) disease->stats.maxsp += copysignl (disease->stats.maxsp, dam_mod); + if (disease->stats.ac ) disease->stats.ac += dam_mod; + if (disease->last_eat ) disease->last_eat -= dam_mod; + if (disease->stats.hp ) disease->stats.hp -= dam_mod; + if (disease->stats.sp ) disease->stats.sp -= dam_mod; if (infect_object (walk, disease, 1)) { - new_draw_info_format (NDI_UNIQUE, 0, op, "You inflict %s on %s!", &disease->name, &walk->name); + op->statusmsg (format ("You inflict %s on %s!", &disease->name, &walk->name)); disease->destroy (); /* don't need this one anymore */ - walk->map->insert (get_archetype ("detect_magic"), x, y, op); + walk->map->insert (archetype::get (shstr_detect_magic), x, y, op); return 1; }