--- deliantra/server/server/spell_attack.C 2006/09/14 23:13:49 1.14 +++ deliantra/server/server/spell_attack.C 2008/07/14 00:04:57 1.62 @@ -1,25 +1,25 @@ /* - 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 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. + * + * 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 . + * + * 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 @@ -29,9 +29,7 @@ #include #include #include -#ifndef __CEXTRACT__ -# include -#endif +#include #include #include @@ -40,11 +38,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 */ @@ -59,7 +55,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; @@ -76,7 +72,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 @@ -110,16 +106,15 @@ * ***************************************************************************/ -/* 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) { int new_dir = 1; /* direction or -1 for left, +1 for right 0 if no new bolt */ int t_dir; /* stores temporary dir calculation */ - mapstruct *m; + maptile *m; sint16 sx, sy; object *new_bolt; @@ -142,42 +137,37 @@ return; /* OK, we made a fork */ - new_bolt = get_object (); - copy_object (tmp, new_bolt); + new_bolt = tmp->clone (); /* reduce chances of subsequent forking */ 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; - mapstruct *m; + maptile *m; - if (--(op->duration) < 0) + if (--op->duration < 0) { - remove_ob (op); - free_object (op); + op->destroy (); return; } @@ -187,9 +177,7 @@ return; if (--op->range < 0) - { - op->range = 0; - } + op->range = 0; else { x = op->x + DIRX (op); @@ -207,7 +195,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; @@ -247,27 +234,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 = get_object (); - copy_object (op, tmp); - tmp->speed_left = -0.1; - tmp->x += DIRX (tmp), tmp->y += DIRY (tmp); - tmp = insert_ob_in_map (tmp, op->map, op, 0); + object *tmp = op->clone (); + + 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. */ @@ -284,7 +269,6 @@ * This function sets up the appropriate owner and skill * pointers. */ - int fire_bolt (object *op, object *caster, int dir, object *spob, object *skill) { @@ -301,8 +285,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; @@ -312,38 +298,43 @@ if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) SET_ANIMATION (tmp, dir); - set_owner (tmp, op); + tmp->set_owner (op); set_spell_skill (op, caster, spob, tmp); tmp->x = op->x + DIRX (tmp); tmp->y = op->y + DIRY (tmp); tmp->map = op->map; - mflags = get_map_flags (tmp->map, &tmp->map, tmp->x, tmp->y, &tmp->x, &tmp->y); + maptile *newmap; + mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y); if (mflags & P_OUT_OF_MAP) { - free_object (tmp); + tmp->destroy (); return 0; } + + tmp->map = newmap; + if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y))) { if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) { - free_object (tmp); + tmp->destroy (); return 0; } + tmp->x = op->x; tmp->y = op->y; tmp->direction = absdir (tmp->direction + 4); 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 @@ -351,22 +342,21 @@ ***************************************************************************/ /* 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; - mapstruct *m = op->map; + maptile *m = op->map; int i; - if (--(op->duration) < 0) + if (--op->duration < 0) { - remove_ob (op); - free_object (op); + op->destroy (); return; } + hit_map (op, 0, op->attacktype, 0); if (op->range > 0) @@ -377,26 +367,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 = get_object (); - copy_object (op, tmp); + 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. @@ -409,33 +398,27 @@ if (op->other_arch == NULL) { LOG (llevError, "BUG: explode_bullet(): op without other_arch\n"); - remove_ob (op); - free_object (op); + op->destroy (); return; } 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"); - remove_ob (op); - free_object (op); + op->destroy (); return; } - remove_ob (op); - 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)) { LOG (llevError, "BUG: explode_bullet(): op out of map\n"); - remove_ob (op); - free_object (op); + op->destroy (); return; } @@ -444,14 +427,14 @@ // bad at the moment that might happen from this. if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE) { - remove_ob (op); - free_object (op); + op->destroy (); return; } if (op->attacktype) { hit_map (op, 0, op->attacktype, 1); + if (op->destroyed ()) return; } @@ -459,21 +442,17 @@ /* other_arch contains what this explodes into */ tmp = arch_to_object (op->other_arch); - copy_owner (tmp, op); + tmp->set_owner (op); tmp->skill = op->skill; - owner = get_owner (op); + owner = op->owner; if ((tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER) && owner && !tailor_god_spell (tmp, owner)) { - remove_ob (op); - free_object (op); + 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) { @@ -486,6 +465,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; @@ -503,27 +483,22 @@ /* 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 ()) - { - remove_ob (op); - free_object (op); - } + 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) { object *tmp; int dam, mflags; - mapstruct *m; + maptile *m; sint16 sx, sy; mflags = get_map_flags (op->map, &m, op->x, op->y, &sx, &sy); @@ -542,17 +517,17 @@ 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); + if (op->destroyed () || !tmp->destroyed () || (op->stats.dam -= dam) < 0) { if (!QUERY_FLAG (op, FLAG_REMOVED)) { - remove_ob (op); - free_object (op); + op->destroy (); return; } } @@ -560,19 +535,17 @@ } } - /* Basically, we move 'op' one square, and if it hits something, * call check_bullet. * This function is only applicable to bullets, but not to all * fired arches (eg, bolts). */ - void move_bullet (object *op) { sint16 new_x, new_y; int mflags; - mapstruct *m; + maptile *m; #if 0 /* We need a better general purpose way to do this */ @@ -591,14 +564,10 @@ if (--op->range <= 0) { if (op->other_arch) - { - explode_bullet (op); - } + explode_bullet (op); else - { - remove_ob (op); - free_object (op); - } + op->destroy (); + return; } @@ -609,29 +578,21 @@ if (mflags & P_OUT_OF_MAP) { - remove_ob (op); - free_object (op); + op->destroy (); return; } if (!op->direction || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y))) { if (op->other_arch) - { - explode_bullet (op); - } + explode_bullet (op); else - { - remove_ob (op); - free_object (op); - } + op->destroy (); + return; } - remove_ob (op); - op->x = new_x; - op->y = new_y; - if ((op = insert_ob_in_map (op, m, op, 0)) == NULL) + if (!(op = m->insert (op, new_x, new_y, op))) return; if (reflwall (op->map, op->x, op->y, op)) @@ -640,14 +601,9 @@ update_turn_face (op); } else - { - check_bullet (op); - } + check_bullet (op); } - - - /* fire_bullet * object op (cast from caster) files a bolt in dir. * spob is the spell object for the bolt. @@ -656,7 +612,6 @@ * This function sets up the appropriate owner and skill * pointers. */ - int fire_bullet (object *op, object *caster, int dir, object *spob) { @@ -666,11 +621,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) @@ -687,40 +642,42 @@ if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) SET_ANIMATION (tmp, dir); - set_owner (tmp, op); + 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; - mflags = get_map_flags (tmp->map, &tmp->map, tmp->x, tmp->y, &tmp->x, &tmp->y); + maptile *newmap; + mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y); if (mflags & P_OUT_OF_MAP) { - free_object (tmp); + tmp->destroy (); return 0; } + + tmp->map = newmap; + if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y))) { if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) { - free_object (tmp); + tmp->destroy (); return 0; } + tmp->x = op->x; tmp->y = op->y; tmp->direction = absdir (tmp->direction + 4); tmp->map = op->map; } - if ((tmp = insert_ob_in_map (tmp, tmp->map, op, 0)) != NULL) - { - check_bullet (tmp); - } - return 1; -} - + if ((tmp = tmp->insert_at (tmp, op))) + check_bullet (tmp); + return 1; +} /***************************************************************************** * @@ -728,25 +685,20 @@ * *****************************************************************************/ - /* 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; - set_owner (new_ob, op->owner); + 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 */ @@ -754,14 +706,11 @@ void move_cone (object *op) { - int i; - /* if no map then hit_map will crash so just ignore object */ if (!op->map) { LOG (llevError, "Tried to move_cone object %s without a map.\n", op->name ? &op->name : "unknown"); - op->speed = 0; - update_ob_speed (op); + op->set_speed (0); return; } @@ -777,10 +726,9 @@ * when their cone dies when they die. */ /* If no owner left, the spell dies out. */ - if (get_owner (op) == NULL) + if (op->owner == NULL) { - remove_ob (op); - free_object (op); + op->destroy (); return; } #endif @@ -797,10 +745,9 @@ if (op->destroyed ()) return; - if ((op->duration--) < 0) + if (op->duration-- < 0) { - remove_ob (op); - free_object (op); + op->destroy (); return; } /* Object has hit maximum range, so don't have it move @@ -813,23 +760,21 @@ 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)]; if (ok_to_put_more (op->map, x, y, op, op->attacktype)) { - object *tmp = get_object (); - - copy_object (op, tmp); - tmp->x = x; - tmp->y = y; + object *tmp = op->clone (); 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); } @@ -849,7 +794,7 @@ { object *tmp; int i, success = 0, range_min = -1, range_max = 1; - mapstruct *m; + maptile *m; sint16 sx, sy; MoveType movetype; @@ -872,7 +817,7 @@ * 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++) { @@ -915,11 +860,9 @@ success = 1; tmp = arch_to_object (spell->other_arch); - set_owner (tmp, op); + tmp->set_owner (op); set_spell_skill (op, caster, spell, tmp); tmp->level = caster_level (caster, spell); - tmp->x = sx; - tmp->y = sy; tmp->attacktype = spell->attacktype; /* holy word stuff */ @@ -963,14 +906,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. @@ -990,34 +931,23 @@ * ****************************************************************************/ - /* 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) 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) + if (!(op = op->insert_at (env, op))) return; } @@ -1026,8 +956,7 @@ // as bombs can be carried. if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE) { - remove_ob (op); - free_object (op); + op->destroy (); return; } @@ -1035,29 +964,27 @@ * 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; tmp->duration = op->duration; tmp->attacktype = op->attacktype; - copy_owner (tmp, op); + 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); } } @@ -1068,11 +995,10 @@ 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]; - mapstruct *m; + 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)) @@ -1080,6 +1006,7 @@ 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 */ @@ -1088,11 +1015,10 @@ tmp->duration = spell->duration + SP_level_duration_adjust (caster, spell); tmp->attacktype = spell->attacktype; - set_owner (tmp, op); + 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; } @@ -1111,14 +1037,13 @@ * 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) { object *target; sint16 x, y; int dist, mflags; - mapstruct *mp; + maptile *mp; if (dir == 0) return NULL; @@ -1140,20 +1065,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 - @@ -1162,7 +1081,6 @@ * dir = direction being cast * spell = spell object */ - int cast_smite_spell (object *op, object *caster, int dir, object *spell) { @@ -1218,8 +1136,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 (QUERY_FLAG (target, FLAG_UNDEAD)) { if (random_roll (0, 2, op, PREFER_LOW)) { @@ -1231,7 +1148,7 @@ { new_draw_info_format (NDI_UNIQUE, 0, op, "The %s looks stronger!", query_name (target)); target->stats.hp = target->stats.maxhp * 2; - free_object (effect); + effect->destroy (); return 0; } } @@ -1242,18 +1159,15 @@ effect->stats.dam = spell->stats.dam + SP_level_dam_adjust (caster, spell); } - set_owner (effect, op); + effect->set_owner (op); 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. @@ -1265,67 +1179,47 @@ void move_missile (object *op) { - int i, mflags; - object *owner; - sint16 new_x, new_y; - mapstruct *m; - if (op->range-- <= 0) { - remove_ob (op); - free_object (op); + op->destroy (); return; } - owner = get_owner (op); -#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 ()) { - remove_ob (op); - free_object (op); + op->destroy (); return; } -#endif - - new_x = op->x + DIRX (op); - new_y = op->y + DIRY (op); - mflags = get_map_flags (op->map, &m, new_x, new_y, &new_x, &new_y); + mapspace &ms = pos.ms (); - 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 ()) - free_object (op); - + op->destroy (); return; } - remove_ob (op); - - if (!op->direction || (mflags & P_OUT_OF_MAP)) + if (!op->direction) { - free_object (op); + op->destroy (); return; } - op->x = new_x; - op->y = new_y; - op->map = m; - i = spell_find_dir (op->map, op->x, op->y, get_owner (op)); + 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); } /**************************************************************************** @@ -1338,17 +1232,14 @@ * 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); @@ -1356,31 +1247,20 @@ 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 = insert_ob_in_ob (tmp, op); + if (tmp->glow_radius > op->glow_radius) op->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; - mapstruct *m; + maptile *m; object *tmp; const char *skill; @@ -1404,25 +1284,26 @@ else op->skill = NULL; - 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++) + for (i = -range; i <= range; i++) { - for (j = -range; j < range; j++) + 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) { - for (tmp = get_map_ob (m, sx, sy); tmp; tmp = tmp->above) - { - if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER) - break; - } + for (tmp = GET_MAP_OB (m, sx, sy); tmp; tmp = tmp->above) + if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER) + break; + if (tmp) { if (tmp->head) @@ -1434,30 +1315,21 @@ 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); - } + 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) - { - object *effect = arch_to_object (spell_ob->other_arch); - - effect->x = sx; - effect->y = sy; - insert_ob_in_map (effect, m, op, 0); - } + m->insert (arch_to_object (spell_ob->other_arch), sx, sy, op); } } } } } } + op->skill = skill; return 1; } @@ -1481,8 +1353,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) { @@ -1498,10 +1372,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 @@ -1521,14 +1396,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) @@ -1551,12 +1426,11 @@ change_abil (tmp, force); /* Mostly to display any messages */ insert_ob_in_ob (force, tmp); - fix_player (tmp); + tmp->update_stats (); return 1; } - /********************************************************************** * mood change * Arguably, this may or may not be an attack spell. But since it @@ -1572,7 +1446,7 @@ object *tmp, *god, *head; int done_one, range, mflags, level, at, best_at; sint16 x, y, nx, ny; - mapstruct *m; + maptile *m; const char *race; /* We precompute some values here so that we don't have to keep @@ -1595,11 +1469,9 @@ 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; @@ -1612,7 +1484,12 @@ if (!(mflags & P_IS_ALIVE)) continue; - for (tmp = get_map_ob (m, nx, ny); tmp; tmp = tmp->above) + // 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; @@ -1629,6 +1506,7 @@ /* 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; @@ -1655,35 +1533,34 @@ 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 ) ) + { + /* + 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. + 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... + This is required to fix the 'charm monster' abuse, where a player level 1 can + charm a level 125 monster... - Ryo, august 14th - */ - { + 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 */ + /* Done with saving throw. Now start affecting the monster */ /* aggravation */ if (QUERY_FLAG (spell, FLAG_MONSTER)) { CLEAR_FLAG (head, FLAG_SLEEP); - if (QUERY_FLAG (head, FLAG_FRIENDLY)) - remove_friendly_object (head); - + remove_friendly_object (head); done_one = 1; head->enemy = op; } @@ -1702,15 +1579,17 @@ 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. + 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); - set_owner (head, op); + head->set_owner (op); set_spell_skill (op, caster, spell, head); add_friendly_object (head); head->attack_movement = PETMOVE; @@ -1721,12 +1600,7 @@ /* 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); - } + m->insert (arch_to_object (spell->other_arch), nx, ny, op); } /* for y */ return 1; @@ -1739,16 +1613,15 @@ * op is the spell effect. * note that duration is handled by process_object() in time.c */ - void move_ball_spell (object *op) { int i, j, dam_save, dir, mflags; sint16 nx, ny, hx, hy; object *owner; - mapstruct *m; + maptile *m; - owner = get_owner (op); + owner = op->owner; /* the following logic makes sure that the ball doesn't move into a wall, * and makes sure that it will move along a wall to try and get at it's @@ -1787,10 +1660,7 @@ m = op->map; } - remove_ob (op); - 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 */ @@ -1801,8 +1671,6 @@ */ for (j = 0; j < 9; j++) { - object *new_ob; - hx = nx + freearr_x[j]; hy = ny + freearr_y[j]; @@ -1820,46 +1688,38 @@ { 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 */ op->stats.dam = dam_save; - i = spell_find_dir (op->map, op->x, op->y, get_owner (op)); + i = spell_find_dir (op->map, op->x, op->y, op->owner); 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 || 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) { @@ -1868,25 +1728,31 @@ static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 }; sint16 target_x, target_y, origin_x, origin_y; int adjustdir; - mapstruct *m; + maptile *m; #endif - int basedir; - object *owner; + object *owner = op->env; - owner = get_owner (op); - if (op->duration == 0 || owner == NULL) + if (!owner) // MUST not happen, remove when true TODO { - remove_ob (op); - free_object (op); + LOG (llevError, "swarm spell found outside inventory: %s\n", op->debug_desc ()); + op->destroy (); return; } + + if (!op->duration || !owner->is_on_map ()) + { + op->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 @@ -1894,7 +1760,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 @@ -1957,13 +1823,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 @@ -1975,43 +1838,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; - set_owner (tmp, 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 = caster_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. */ @@ -2021,7 +1879,7 @@ object *target = NULL, *tmp = NULL; sint16 x, y; int dam, mflags; - mapstruct *m; + maptile *m; dam = spell->stats.dam + SP_level_dam_adjust (caster, spell); @@ -2045,13 +1903,14 @@ if (mflags & P_IS_ALIVE && spell->attacktype) { - for (target = get_map_ob (m, x, y); target; target = target->above) + 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); + + hit_player (target, dam, op, spell->attacktype, 1); return 1; /* one success only! */ } } @@ -2077,29 +1936,24 @@ 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); + + m->insert (tmp, x, y, op); return 1; } - - - /* cast_cause_disease: this spell looks along 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 * perhaps this should actually be in disease.c? */ - int cast_cause_disease (object *op, object *caster, object *spell, int dir) { sint16 x, y; int i, mflags, range, dam_mod, dur_mod; object *walk; - mapstruct *m; + maptile *m; x = op->x; y = op->y; @@ -2109,6 +1963,7 @@ */ if (!dir) dir = op->facing; + if (!dir) return 0; /* won't find anything if casting on ourself, so just return */ @@ -2137,12 +1992,12 @@ if (mflags & P_IS_ALIVE) { /* search this square for a victim */ - for (walk = get_map_ob (m, x, y); walk; walk = walk->above) + 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 (spell->other_arch); - set_owner (disease, op); + disease->set_owner (op); set_spell_skill (op, caster, spell, disease); disease->stats.exp = 0; disease->level = caster_level (caster, spell); @@ -2152,7 +2007,7 @@ 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; @@ -2197,22 +2052,18 @@ 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); - free_object (disease); /* 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); + disease->destroy (); /* don't need this one anymore */ + walk->map->insert (get_archetype ("detect_magic"), x, y, op); return 1; } - free_object (disease); + + disease->destroy (); } } /* if living creature */ } /* for range of spaces */ + new_draw_info (NDI_UNIQUE, 0, op, "No one caught anything!"); return 1; }