--- deliantra/server/server/spell_attack.C 2008/04/30 10:31:04 1.54 +++ deliantra/server/server/spell_attack.C 2008/12/28 06:59:27 1.73 @@ -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) { @@ -170,7 +167,7 @@ if (--op->duration < 0) { - op->destroy (); + op->drop_and_destroy (); return; } @@ -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; @@ -312,7 +309,7 @@ mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y); if (mflags & P_OUT_OF_MAP) { - tmp->destroy (); + tmp->drop_and_destroy (); return 0; } @@ -322,7 +319,7 @@ { if (!QUERY_FLAG (tmp, FLAG_REFLECTING)) { - tmp->destroy (); + tmp->drop_and_destroy (); return 0; } @@ -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 (); @@ -437,6 +434,7 @@ if (op->attacktype) { hit_map (op, 0, op->attacktype, 1); + if (op->destroyed ()) return; } @@ -449,7 +447,10 @@ 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; @@ -524,6 +525,8 @@ if (QUERY_FLAG (tmp, FLAG_ALIVE)) { dam = hit_player (tmp, op->stats.dam, op, op->attacktype, 1); + + // TODO: can't understand the following if's if (op->destroyed () || !tmp->destroyed () || (op->stats.dam -= dam) < 0) { if (!QUERY_FLAG (op, FLAG_REMOVED)) @@ -622,11 +625,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) @@ -646,8 +649,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; @@ -707,8 +710,6 @@ void move_cone (object *op) { - int i; - /* if no map then hit_map will crash so just ignore object */ if (!op->map) { @@ -738,17 +739,22 @@ hit_map (op, 0, op->attacktype, 0); + if (!op->is_on_map ()) + return; + /* Check to see if we should push anything. * Spell objects with weight push whatever they encounter to some * degree. */ if (op->weight) - check_spell_knockback (op); + { + check_spell_knockback (op); - if (op->destroyed ()) - return; + if (!op->is_on_map ()) + return; + } - if ((op->duration--) < 0) + if (op->duration-- < 0) { op->destroy (); return; @@ -763,7 +769,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)]; @@ -865,7 +871,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 */ @@ -1004,10 +1010,18 @@ maptile *m; mflags = get_map_flags (op->map, &m, dx, dy, &dx, &dy); - if ((mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, dx, dy) & MOVE_WALK)) + + // when creating a bomb below ourself it should always work, even + // when movement is blocked (somehow we got here, somehow we are here, + // so we should also be able to make a bomb here). (originally added + // to fix create bomb traps in doors, which cast with dir=0). + if (dir) { - new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way."); - return 0; + if ((mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, dx, dy) & MOVE_WALK)) + { + new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way."); + return 0; + } } tmp = arch_to_object (spell->other_arch); @@ -1117,7 +1131,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)) { @@ -1182,62 +1196,47 @@ void move_missile (object *op) { - int i, mflags; - object *owner; - sint16 new_x, new_y; - maptile *m; - if (op->range-- <= 0) { - op->destroy (); + op->drop_and_destroy (); return; } - owner = op->owner; -#if 0 - /* It'd make things nastier if this wasn't here - spells cast by - * monster that are then killed would continue to survive - */ - if (owner == NULL) + mapxy pos (op); + pos.move (op->direction); + + if (!pos.normalise ()) { op->destroy (); return; } -#endif - new_x = op->x + DIRX (op); - new_y = op->y + DIRY (op); - - 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 ()) - 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); } /**************************************************************************** @@ -1261,10 +1260,7 @@ tmp->speed = 0.01; tmp->stats.food = time; SET_FLAG (tmp, FLAG_IS_USED_UP); - tmp->glow_radius = radius; - if (tmp->glow_radius > MAX_LIGHT_RADII) - tmp->glow_radius = MAX_LIGHT_RADII; - + tmp->glow_radius = min (MAX_LIGHT_RADIUS, radius); tmp = insert_ob_in_ob (tmp, op); if (tmp->glow_radius > op->glow_radius) @@ -1304,9 +1300,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; @@ -1333,6 +1329,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); } @@ -1470,7 +1467,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_... @@ -1502,8 +1499,9 @@ continue; // players can only affect spaces that they can actually see - if (caster && caster->contr - && caster->contr->visibility_at (m, nx, ny) < 70) + if (caster + && caster->contr + && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED) continue; for (tmp = GET_MAP_TOP (m, nx, ny); tmp; tmp = tmp->below) @@ -1623,14 +1621,12 @@ return 1; } - /* Move_ball_spell: This handles ball type spells that just sort of wander * about. was called move_ball_lightning, but since more than the ball * lightning spell used it, that seemed misnamed. * op is the spell effect. * note that duration is handled by process_object() in time.c */ - void move_ball_spell (object *op) { @@ -1659,7 +1655,6 @@ /* i bit 0: alters sign of offset * other bits (i / 2): absolute value of offset */ - int offset = ((i ^ j) & 1) ? (i / 2) : -(i / 2); int tmpdir = absdir (op->direction + offset); @@ -1671,6 +1666,7 @@ break; } } + if (dir == 0) { nx = op->x; @@ -1706,8 +1702,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 */ @@ -1731,15 +1727,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) { @@ -1750,23 +1744,29 @@ int adjustdir; maptile *m; #endif - int basedir; - object *owner; + object *owner = op->env; - owner = op->owner; - if (op->duration == 0 || owner == NULL) + if (!owner) // MUST not happen, remove when true TODO { + LOG (llevError, "swarm spell found outside inventory: %s\n", op->debug_desc ()); op->destroy (); return; } + if (!op->duration || !owner->is_on_map ()) + { + op->drop_and_destroy (); + return; + } + op->duration--; - basedir = op->direction; - if (basedir == 0) + int basedir = op->direction; + if (!basedir) { /* spray in all directions! 8) */ - basedir = rndm (1, 8); + op->facing = (op->facing + op->state) & 7; + basedir = op->facing + 1; } #if 0 @@ -1837,13 +1837,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 @@ -1855,23 +1852,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) @@ -1879,17 +1870,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. */ @@ -1903,42 +1897,40 @@ dam = spell->stats.dam + SP_level_dam_adjust (caster, spell); - if (!dir) + if (dir) { - new_draw_info (NDI_UNIQUE, 0, op, "In what direction?"); - return 0; - } + x = op->x + freearr_x[dir]; + y = op->y + freearr_y[dir]; + m = op->map; - x = op->x + freearr_x[dir]; - y = op->y + freearr_y[dir]; - m = op->map; + mflags = get_map_flags (m, &m, x, y, &x, &y); - mflags = get_map_flags (m, &m, x, y, &x, &y); + if (mflags & P_OUT_OF_MAP) + { + new_draw_info (NDI_UNIQUE, 0, op, "Nothing is there."); + return 0; + } - if (mflags & P_OUT_OF_MAP) - { - new_draw_info (NDI_UNIQUE, 0, op, "Nothing is there."); - return 0; - } + if (mflags & P_IS_ALIVE && spell->attacktype) + { + for (target = GET_MAP_OB (m, x, y); target; target = target->above) + if (QUERY_FLAG (target, FLAG_MONSTER)) + { + /* oky doky. got a target monster. Lets make a blinding attack */ + if (target->head) + target = target->head; - if (mflags & P_IS_ALIVE && spell->attacktype) - { - for (target = GET_MAP_OB (m, x, y); target; target = target->above) - if (QUERY_FLAG (target, FLAG_MONSTER)) - { - /* oky doky. got a target monster. Lets make a blinding attack */ - if (target->head) - target = target->head; - (void) hit_player (target, dam, op, spell->attacktype, 1); - return 1; /* one success only! */ - } - } + hit_player (target, dam, op, spell->attacktype, 1); + return 1; /* one success only! */ + } + } - /* no live target, perhaps a wall is in the way? */ - if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y))) - { - new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way."); - return 0; + /* no live target, perhaps a wall is in the way? */ + if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y))) + { + new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way."); + return 0; + } } /* ok, looks groovy to just insert a new light on the map */ @@ -1948,15 +1940,17 @@ 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->glow_radius = min (MAX_LIGHT_RADIUS, spell->range + SP_level_range_adjust (caster, spell)); + + if (dir) + m->insert (tmp, x, y, op); + else + caster->outer_env ()->insert (tmp); - m->insert (tmp, x, y, op); return 1; } @@ -2019,7 +2013,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)