--- deliantra/server/server/spell_attack.C 2006/09/14 21:16:13 1.12
+++ deliantra/server/server/spell_attack.C 2009/01/01 16:05:13 1.79
@@ -1,33 +1,26 @@
-
/*
- * static char *rcsid_spell_attack_c =
- * "$Id: spell_attack.C,v 1.12 2006/09/14 21:16:13 root Exp $";
+ * 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
*/
-
-/*
- 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-devel@real-time.com
-*/
-
/* This file contains all the spell attack code. Grouping this code
* together should hopefully make it easier to find the relevent bits
* of code
@@ -36,9 +29,7 @@
#include
#include
#include
-#ifndef __CEXTRACT__
-# include
-#endif
+#include
#include
#include
@@ -47,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 */
@@ -66,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;
@@ -83,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
@@ -117,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;
@@ -149,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->drop_and_destroy ();
return;
}
@@ -194,9 +177,7 @@
return;
if (--op->range < 0)
- {
- op->range = 0;
- }
+ op->range = 0;
else
{
x = op->x + DIRX (op);
@@ -214,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;
@@ -254,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.
*/
@@ -291,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)
{
@@ -308,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;
@@ -319,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->drop_and_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->drop_and_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
@@ -358,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)
@@ -384,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.
@@ -411,39 +393,32 @@
void
explode_bullet (object *op)
{
- tag_t op_tag = op->count;
object *tmp, *owner;
- if (op->other_arch == NULL)
+ if (!op->other_arch)
{
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;
}
@@ -452,36 +427,35 @@
// 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 (was_destroyed (op, op_tag))
+
+ if (op->destroyed ())
return;
}
/* 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))
+ 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)
{
@@ -494,6 +468,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;
@@ -511,28 +486,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 (!was_destroyed (op, op_tag))
- {
- 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)
{
- tag_t op_tag = op->count, tmp_tag;
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);
@@ -551,18 +520,18 @@
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))
{
- tmp_tag = tmp->count;
dam = hit_player (tmp, op->stats.dam, op, op->attacktype, 1);
- if (was_destroyed (op, op_tag) || !was_destroyed (tmp, tmp_tag) || (op->stats.dam -= dam) < 0)
+
+ // TODO: can't understand the following if's
+ 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;
}
}
@@ -570,19 +539,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 */
@@ -592,7 +559,7 @@
if (op->stats.sp == SP_METEOR)
{
replace_insert_ob_in_map ("fire_trail", op);
- if (was_destroyed (op, op_tag))
+ if (op->destroyed ())
return;
} /* end addition. */
#endif
@@ -601,14 +568,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;
}
@@ -619,29 +582,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))
@@ -650,14 +605,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.
@@ -666,7 +616,6 @@
* This function sets up the appropriate owner and skill
* pointers.
*/
-
int
fire_bullet (object *op, object *caster, int dir, object *spob)
{
@@ -676,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)
@@ -697,40 +646,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;
+}
/*****************************************************************************
*
@@ -738,25 +689,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 */
@@ -764,15 +710,11 @@
void
move_cone (object *op)
{
- int i;
- tag_t tag;
-
/* 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;
}
@@ -788,31 +730,33 @@
* 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
- tag = op->count;
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 (was_destroyed (op, tag))
- return;
+ if (!op->is_on_map ())
+ 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
@@ -825,23 +769,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);
}
@@ -861,7 +803,7 @@
{
object *tmp;
int i, success = 0, range_min = -1, range_max = 1;
- mapstruct *m;
+ maptile *m;
sint16 sx, sy;
MoveType movetype;
@@ -884,22 +826,18 @@
* 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++)
{
sint16 x, y, d;
/* 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;
+ d = (dir + i) % 9;
/* 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,
@@ -927,11 +865,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->level = casting_level (caster, spell);
tmp->attacktype = spell->attacktype;
/* holy word stuff */
@@ -975,14 +911,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.
@@ -1002,34 +936,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;
}
@@ -1038,8 +961,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;
}
@@ -1047,29 +969,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);
}
}
@@ -1080,18 +1000,26 @@
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))
+
+ // 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);
/* level dependencies for bomb */
@@ -1100,11 +1028,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;
}
@@ -1123,14 +1050,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;
@@ -1152,20 +1078,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 -
@@ -1174,7 +1094,6 @@
* dir = direction being cast
* spell = spell object
*/
-
int
cast_smite_spell (object *op, object *caster, int dir, object *spell)
{
@@ -1194,9 +1113,11 @@
* 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)))
+ || (target->title && god && target->title == god->name)
+ || (target->race && god && target->race.contains (god->race)))
{
new_draw_info (NDI_UNIQUE, 0, op, "Your request is unheeded.");
return 0;
@@ -1208,12 +1129,12 @@
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))
{
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.");
@@ -1230,8 +1151,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))
{
@@ -1243,7 +1163,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;
}
}
@@ -1254,18 +1174,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.
@@ -1277,68 +1194,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->drop_and_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);
+ 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))
{
- tag_t tag = op->count;
-
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 (!was_destroyed (op, tag))
- {
- remove_ob (op);
- 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);
}
/****************************************************************************
@@ -1351,126 +1247,78 @@
* 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);
- tmp->glow_radius = radius;
- 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->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;
- 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;
- 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;
+ bool friendly = op->flag [FLAG_FRIENDLY] || op->is_player ();
/* 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;
+ const shstr skill = op->skill;
+
if (caster == op)
op->skill = spell_ob->skill;
else if (caster->skill)
op->skill = caster->skill;
else
- op->skill = NULL;
+ op->skill = 0;
- 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++)
+ unordered_mapwalk (op, -range, -range, range, range)
{
- 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)
+ mapspace &ms = m->at (nx, ny);
+
+ if (ms.flags () & P_IS_ALIVE)
+ for (object *tmp = ms.bot; tmp; tmp = tmp->above)
+ if (tmp->flag [FLAG_ALIVE] || tmp->is_player ())
{
- for (tmp = get_map_ob (m, sx, sy); tmp; tmp = tmp->above)
- {
- if (QUERY_FLAG (tmp, FLAG_ALIVE) || tmp->type == PLAYER)
- break;
- }
- if (tmp)
+ tmp = tmp->head_ ();
+
+ if ((friendly && !tmp->flag [FLAG_FRIENDLY] && !tmp->is_player ())
+ || (!friendly && (tmp->flag [FLAG_FRIENDLY] || tmp->is_player ())))
{
- if (tmp->head)
- tmp = tmp->head;
+ if (spell_ob->subtype == SP_DESTRUCTION)
+ {
+ hit_player (tmp, dam, op, spell_ob->attacktype, 0);
- if ((friendly && !QUERY_FLAG (tmp, FLAG_FRIENDLY) && tmp->type != PLAYER) ||
- (!friendly && (QUERY_FLAG (tmp, FLAG_FRIENDLY) || tmp->type == PLAYER)))
+ if (spell_ob->other_arch)
+ m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op);
+ }
+ else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist [ATNR_MAGIC] != 100)
{
- 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);
- }
- }
- 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);
- }
- }
+ if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch)
+ m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op);
}
}
}
- }
}
+
op->skill = skill;
return 1;
}
@@ -1494,8 +1342,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)
{
@@ -1511,10 +1361,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
@@ -1534,14 +1385,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)
@@ -1564,12 +1415,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
@@ -1583,16 +1433,14 @@
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;
- mapstruct *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
* 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_...
@@ -1601,73 +1449,70 @@
*/
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;
+ unordered_mapwalk (op, -range, -range, range, range)
+ {
+ mapspace &ms = m->at (nx, ny);
- 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;
-
- for (tmp = get_map_ob (m, nx, ny); tmp; tmp = tmp->above)
- 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 */
+ /* If there is nothing living on this space, no need to go further */
+ if (!ms.flags () & P_IS_ALIVE)
+ continue;
+
+ // players can only affect spaces that they can actually see
+ if (caster
+ && caster->contr
+ && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED)
+ continue;
+
+ for (tmp = ms.top; tmp; tmp = tmp->below)
+ if (tmp->flag [FLAG_MONSTER])
+ break;
+
+ /* There can be living objects that are not monsters */
+ if (!tmp)
+ continue;
+
+ /* Only the head has meaningful data, so resolve to that */
+ head = tmp->head_ ();
+
+ /* 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
@@ -1679,89 +1524,83 @@
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 effecting the monster */
-
- /* aggravation */
- if (QUERY_FLAG (spell, FLAG_MONSTER))
- {
- CLEAR_FLAG (head, FLAG_SLEEP);
- if (QUERY_FLAG (head, FLAG_FRIENDLY))
- remove_friendly_object (head);
-
- done_one = 1;
- head->enemy = op;
- }
-
- /* calm monsters */
- if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE))
- {
- SET_FLAG (head, FLAG_UNAGGRESSIVE);
- head->enemy = NULL;
- done_one = 1;
- }
-
- /* berserk monsters */
- if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK))
- {
- 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. */
- CLEAR_FLAG (head, FLAG_GENERATOR);
- set_owner (head, 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)
- {
- tmp = arch_to_object (spell->other_arch);
- tmp->x = nx;
- tmp->y = ny;
- insert_ob_in_map (tmp, m, op, 0);
- }
- } /* for y */
+ */
+ 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 (QUERY_FLAG (spell, FLAG_MONSTER))
+ {
+ CLEAR_FLAG (head, FLAG_SLEEP);
+ remove_friendly_object (head);
+ done_one = 1;
+ head->enemy = op;
+ }
+
+ /* calm monsters */
+ if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE))
+ {
+ SET_FLAG (head, FLAG_UNAGGRESSIVE);
+ head->enemy = NULL;
+ done_one = 1;
+ }
+
+ /* berserk monsters */
+ if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK))
+ {
+ SET_FLAG (head, FLAG_BERSERK);
+ done_one = 1;
+ }
+
+ /* charm */
+ if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, 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. */
+ 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;
+ }
+
+ /* 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);
+ }
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)
{
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
@@ -1781,7 +1620,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);
@@ -1793,6 +1631,7 @@
break;
}
}
+
if (dir == 0)
{
nx = op->x;
@@ -1800,10 +1639,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 */
@@ -1814,8 +1650,6 @@
*/
for (j = 0; j < 9; j++)
{
- object *new_ob;
-
hx = nx + freearr_x[j];
hy = ny + freearr_y[j];
@@ -1833,46 +1667,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)
{
@@ -1881,25 +1707,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->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
@@ -1907,7 +1739,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
@@ -1970,13 +1802,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
@@ -1988,43 +1817,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 = 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)
- {
- 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.
*/
@@ -2034,46 +1858,44 @@
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);
- 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 */
@@ -2083,21 +1905,19 @@
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->x = x;
- tmp->y = y;
- insert_ob_in_map (tmp, m, op, 0);
- return 1;
-}
+ if (tmp->glow_radius)
+ 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);
+ return 1;
+}
/* cast_cause_disease: this spell looks along from the
* player and infects someone.
@@ -2105,14 +1925,13 @@
* 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;
@@ -2122,6 +1941,7 @@
*/
if (!dir)
dir = op->facing;
+
if (!dir)
return 0; /* won't find anything if casting on ourself, so just return */
@@ -2150,22 +1970,22 @@
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);
+ 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 / 4;
+ disease->magic += dur_mod / 8;
if (disease->stats.maxhp > 0)
disease->stats.maxhp += dur_mod;
@@ -2210,22 +2030,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;
}