--- deliantra/server/server/spell_attack.C 2006/12/20 09:14:22 1.21 +++ deliantra/server/server/spell_attack.C 2007/05/18 13:15:29 1.36 @@ -1,25 +1,26 @@ /* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2002-2003 Mark Wedel & Crossfire Development Team - Copyright (C) 1992 Frank Tore Johansen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - The authors can be reached via e-mail at -*/ + * CrossFire, A Multiplayer game + * + * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team + * 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 contains all the spell attack code. Grouping this code * together should hopefully make it easier to find the relevent bits @@ -29,9 +30,7 @@ #include #include #include -#ifndef __CEXTRACT__ -# include -#endif +#include #include #include @@ -148,17 +147,15 @@ 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, @@ -168,12 +165,11 @@ void move_bolt (object *op) { - object *tmp; int mflags; sint16 x, y; maptile *m; - if (--(op->duration) < 0) + if (--op->duration < 0) { op->destroy (); return; @@ -185,9 +181,7 @@ return; if (--op->range < 0) - { - op->range = 0; - } + op->range = 0; else { x = op->x + DIRX (op); @@ -205,7 +199,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; @@ -245,27 +238,27 @@ else if (right) op->direction = absdir (op->direction - 2); } + update_turn_face (op); /* A bolt *must* be IS_TURNABLE */ return; } else { /* Create a copy of this object and put it ahead */ - tmp = op->clone (); + object *tmp = op->clone (); - tmp->speed_left = -0.1; - tmp->x += DIRX (tmp), tmp->y += DIRY (tmp); - tmp = insert_ob_in_map (tmp, op->map, op, 0); + m->insert (tmp, x, y, op); + tmp->speed_left = -0.1f; /* To make up for the decrease at the top of the function */ tmp->duration++; /* New forking code. Possibly create forks of this object * going off in other directions. */ - if (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. */ @@ -282,7 +275,6 @@ * This function sets up the appropriate owner and skill * pointers. */ - int fire_bolt (object *op, object *caster, int dir, object *spob, object *skill) { @@ -317,13 +309,16 @@ 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) { 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)) @@ -338,7 +333,7 @@ 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; @@ -359,11 +354,10 @@ void explosion (object *op) { - object *tmp; maptile *m = op->map; int i; - if (--(op->duration) < 0) + if (--op->duration < 0) { op->destroy (); return; @@ -379,19 +373,20 @@ dx = op->x + freearr_x[i]; dy = op->y + freearr_y[i]; + /* ok_to_put_more already does things like checks for walls, * out of map, etc. */ if (ok_to_put_more (op->map, dx, dy, op, op->attacktype)) { - tmp = op->clone (); + object *tmp = op->clone (); + tmp->state = 0; - tmp->speed_left = -0.21; + tmp->speed_left = -0.21f; tmp->range--; tmp->value = 0; - tmp->x = dx; - tmp->y = dy; - insert_ob_in_map (tmp, m, op, 0); + + m->insert (tmp, dx, dy, op); } } } @@ -416,9 +411,7 @@ if (op->env) { - object *env; - - env = object_get_env_recursive (op); + object *env = object_get_env_recursive (op); if (env->map == NULL || out_of_map (env->map, env->x, env->y)) { LOG (llevError, "BUG: explode_bullet(): env out of map\n"); @@ -426,10 +419,7 @@ return; } - op->remove (); - op->x = env->x; - op->y = env->y; - insert_ob_in_map (op, env->map, op, INS_NO_MERGE | INS_NO_WALK_ON); + op->insert_at (env, op, INS_NO_MERGE | INS_NO_WALK_ON); } else if (out_of_map (op->map, op->x, op->y)) { @@ -468,9 +458,6 @@ return; } - tmp->x = op->x; - tmp->y = op->y; - /* special for bombs - it actually has sane values for these */ if (op->type == SPELL_EFFECT && op->subtype == SP_BOMB) { @@ -483,6 +470,7 @@ { if (op->attacktype & AT_MAGIC) tmp->attacktype |= AT_MAGIC; + /* Spell doc describes what is going on here */ tmp->stats.dam = op->dam_modifier; tmp->range = op->stats.maxhp; @@ -500,20 +488,14 @@ /* Prevent recursion */ op->move_on = 0; - insert_ob_in_map (tmp, op->map, op, 0); + tmp->insert_at (op, op); /* remove the firebullet */ - if (!op->destroyed ()) - { - op->destroy (); - } + op->destroy (); } - - /* checks to see what op should do, given the space it is on * (eg, explode, damage player, etc) */ - void check_bullet (object *op) { @@ -538,7 +520,7 @@ 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)) { @@ -555,13 +537,11 @@ } } - /* 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) { @@ -614,10 +594,7 @@ return; } - op->remove (); - op->x = new_x; - op->y = new_y; - if ((op = insert_ob_in_map (op, m, op, 0)) == NULL) + if (!(op = m->insert (op, new_x, new_y, op))) return; if (reflwall (op->map, op->x, op->y, op)) @@ -626,14 +603,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. @@ -680,13 +652,16 @@ 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) { 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)) @@ -701,40 +676,32 @@ tmp->map = op->map; } - if ((tmp = insert_ob_in_map (tmp, tmp->map, op, 0)) != NULL) + if ((tmp = tmp->insert_at (tmp, op))) check_bullet (tmp); return 1; } - - - /***************************************************************************** * * CONE RELATED FUNCTIONS * *****************************************************************************/ - /* drops an object based on what is in the cone's "other_arch" */ void cone_drop (object *op) { object *new_ob = arch_to_object (op->other_arch); - new_ob->x = op->x; - new_ob->y = op->y; new_ob->level = op->level; new_ob->set_owner (op->owner); /* preserve skill ownership */ if (op->skill && op->skill != new_ob->skill) - { - new_ob->skill = op->skill; - } - insert_ob_in_map (new_ob, op->map, op, 0); + new_ob->skill = op->skill; + new_ob->insert_at (op, op); } /* move_cone: causes cone object 'op' to move a space/hit creatures */ @@ -748,8 +715,7 @@ 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; } @@ -807,14 +773,13 @@ { object *tmp = op->clone (); - tmp->x = x; - tmp->y = y; - tmp->duration = op->duration + 1; /* Use for spell tracking - see ok_to_put_more() */ tmp->stats.maxhp = op->stats.maxhp; - insert_ob_in_map (tmp, op->map, op, 0); + + op->map->insert (tmp, x, y, op); + if (tmp->other_arch) cone_drop (tmp); } @@ -903,8 +868,6 @@ 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 */ @@ -951,11 +914,9 @@ LOG (llevDebug, "cast_cone(): arch %s doesn't have flying 1\n", &spell->other_arch->name); 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->name); - 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. @@ -984,7 +945,6 @@ { int i; object *env, *tmp; - archetype *at; if (op->state != NUM_ANIMATIONS (op) - 1) return; @@ -999,10 +959,7 @@ if (env->type == PLAYER) esrv_del_item (env->contr, op->count); - op->remove (); - op->x = env->x; - op->y = env->y; - if ((op = insert_ob_in_map (op, env->map, op, 0)) == NULL) + if (!(op = op->insert_at (env, op))) return; } @@ -1019,13 +976,13 @@ * 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++) { if (out_of_map (op->map, op->x + freearr_x[i], op->y + freearr_x[i])) continue; + tmp = arch_to_object (at); tmp->direction = i; tmp->range = op->range; @@ -1034,14 +991,12 @@ tmp->attacktype = op->attacktype; tmp->set_owner (op); if (op->skill && op->skill != tmp->skill) - { - tmp->skill = op->skill; - } + tmp->skill = op->skill; + if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE)) SET_ANIMATION (tmp, i); - tmp->x = op->x + freearr_x[i]; - tmp->y = op->y + freearr_x[i]; - insert_ob_in_map (tmp, op->map, op, 0); + + op->map->insert (tmp, op->x + freearr_x[i], op->y + freearr_x[i], op); move_bullet (tmp); } } @@ -1074,9 +1029,8 @@ tmp->set_owner (op); set_spell_skill (op, caster, spell, tmp); - tmp->x = dx; - tmp->y = dy; - insert_ob_in_map (tmp, m, op, 0); + + m->insert (tmp, dx, dy, op); return 1; } @@ -1229,9 +1183,7 @@ 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; } @@ -1296,17 +1248,14 @@ return; } - op->x = new_x; - op->y = new_y; - op->map = m; - i = spell_find_dir (op->map, op->x, op->y, op->owner); + i = spell_find_dir (m, new_x, new_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); + m->insert (op, new_x, new_y, op); } /**************************************************************************** @@ -1353,9 +1302,6 @@ return 1; } - - - int cast_destruction (object *op, object *caster, object *spell_ob) { @@ -1385,7 +1331,7 @@ 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++) { @@ -1394,16 +1340,17 @@ 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; - } + if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER) + break; + if (tmp) { if (tmp->head) @@ -1416,29 +1363,19 @@ { 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; } @@ -1508,8 +1445,8 @@ 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) @@ -1532,12 +1469,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 @@ -1576,11 +1512,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; @@ -1593,7 +1527,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; @@ -1610,6 +1549,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; @@ -1636,35 +1576,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; } @@ -1683,10 +1622,10 @@ SET_FLAG (head, FLAG_BERSERK); done_one = 1; } + /* charm */ if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY)) { - SET_FLAG (head, FLAG_FRIENDLY); /* Prevent uncontolled outbreaks of self replicating monsters. Typical use case is charm, go somwhere, use aggravation to make hostile. This could lead to fun stuff like mice outbreak in bigworld and server crawl. */ @@ -1702,12 +1641,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; @@ -1768,10 +1702,7 @@ m = op->map; } - op->remove (); - op->y = ny; - op->x = nx; - insert_ob_in_map (op, m, op, 0); + m->insert (op, nx, ny, op); dam_save = op->stats.dam; /* save the original dam: we do halfdam on surrounding squares */ @@ -1782,8 +1713,6 @@ */ for (j = 0; j < 9; j++) { - object *new_ob; - hx = nx + freearr_x[j]; hy = ny + freearr_y[j]; @@ -1807,12 +1736,7 @@ /* 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 */ @@ -1825,9 +1749,8 @@ /* 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; } } @@ -1875,7 +1798,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 @@ -1967,8 +1890,6 @@ return 0; tmp = get_archetype (SWARM_SPELL); - tmp->x = op->x; - tmp->y = op->y; tmp->set_owner (op); /* needed so that if swarm elements kill, caster gets xp. */ set_spell_skill (op, caster, spell, tmp); @@ -1978,17 +1899,17 @@ 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++) 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->insert_at (op, op); return 1; } @@ -2058,9 +1979,8 @@ 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; } @@ -2178,16 +2098,10 @@ if (infect_object (walk, disease, 1)) { - object *flash; /* visual effect for inflicting disease */ - new_draw_info_format (NDI_UNIQUE, 0, op, "You inflict %s on %s!", &disease->name, &walk->name); disease->destroy (); /* don't need this one anymore */ - flash = get_archetype (ARCH_DETECT_MAGIC); - flash->x = x; - flash->y = y; - flash->map = walk->map; - insert_ob_in_map (flash, walk->map, op, 0); + walk->map->insert (get_archetype (ARCH_DETECT_MAGIC), x, y, op); return 1; } @@ -2195,6 +2109,7 @@ } } /* if living creature */ } /* for range of spaces */ + new_draw_info (NDI_UNIQUE, 0, op, "No one caught anything!"); return 1; }