--- deliantra/server/server/spell_attack.C 2007/11/08 19:43:28 1.49 +++ deliantra/server/server/spell_attack.C 2008/09/29 06:32:09 1.64 @@ -1,7 +1,7 @@ /* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * - * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * 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 * @@ -38,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 */ @@ -57,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; @@ -74,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 @@ -108,10 +106,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) { @@ -153,6 +150,7 @@ new_bolt->stats.dam++; tmp->stats.dam /= 2; /* reduce father bolt damage */ tmp->stats.dam++; + if ((new_bolt = m->insert (new_bolt, sx, sy, op))) update_turn_face (new_bolt); } @@ -160,7 +158,6 @@ /* 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) { @@ -253,10 +250,8 @@ /* New forking code. Possibly create forks of this object * going off in other directions. */ - if (rndm (0, 99) < tmp->stats.Dex) - { /* stats.Dex % of forking */ - forklightning (op, tmp); - } + if (tmp->stats.Dex && rndm (0, 99) < tmp->stats.Dex) + forklightning (op, tmp); /* stats.Dex % of forking */ /* In this way, the object left behind sticks on the space, but * doesn't create any bolts that continue to move onward. @@ -290,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; @@ -398,7 +395,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 (); @@ -407,8 +404,9 @@ if (op->env) { - object *env = object_get_env_recursive (op); - if (env->map == NULL || out_of_map (env->map, env->x, env->y)) + object *env = op->outer_env (); + + if (!env->map || out_of_map (env->map, env->x, env->y)) { LOG (llevError, "BUG: explode_bullet(): env out of map\n"); op->destroy (); @@ -436,6 +434,7 @@ if (op->attacktype) { hit_map (op, 0, op->attacktype, 1); + if (op->destroyed ()) return; } @@ -523,6 +522,7 @@ 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)) @@ -621,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) @@ -645,8 +645,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; @@ -706,8 +706,6 @@ void move_cone (object *op) { - int i; - /* if no map then hit_map will crash so just ignore object */ if (!op->map) { @@ -747,7 +745,7 @@ if (op->destroyed ()) return; - if ((op->duration--) < 0) + if (op->duration-- < 0) { op->destroy (); return; @@ -762,7 +760,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)]; @@ -864,7 +862,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->level = casting_level (caster, spell); tmp->attacktype = spell->attacktype; /* holy word stuff */ @@ -939,22 +937,16 @@ void animate_bomb (object *op) { - int i; - object *env, *tmp; - 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); - if (!(op = op->insert_at (env, op))) return; } @@ -974,12 +966,12 @@ */ 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; @@ -1003,7 +995,6 @@ 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]; @@ -1015,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 */ @@ -1122,7 +1114,7 @@ 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)) { @@ -1187,62 +1179,47 @@ void move_missile (object *op) { - int i, mflags; - object *owner; - sint16 new_x, new_y; - maptile *m; - if (op->range-- <= 0) { op->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; } - i = spell_find_dir (m, new_x, new_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); } - m->insert (op, new_x, new_y, op); + pos.insert (op, op); } /**************************************************************************** @@ -1309,9 +1286,9 @@ 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; @@ -1338,6 +1315,7 @@ 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); } @@ -1475,7 +1453,7 @@ * 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_... @@ -1605,7 +1583,9 @@ /* charm */ if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_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); @@ -1633,7 +1613,6 @@ * op is the spell effect. * note that duration is handled by process_object() in time.c */ - void move_ball_spell (object *op) { @@ -1709,8 +1688,8 @@ { 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 */ @@ -1734,15 +1713,13 @@ } } - -/* 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) { @@ -1753,11 +1730,16 @@ 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->destroy (); return; @@ -1765,11 +1747,12 @@ 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 @@ -1840,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 @@ -1858,23 +1838,17 @@ * 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->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) @@ -1882,17 +1856,20 @@ 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; + 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); - tmp->insert_at (op, op); return 1; } - /* See the spells documentation file for why this is its own * function. */ @@ -1932,7 +1909,8 @@ /* 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! */ } } @@ -2022,7 +2000,7 @@ 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)