--- deliantra/server/server/spell_attack.C 2008/12/28 07:48:44 1.74
+++ deliantra/server/server/spell_attack.C 2009/11/06 12:49:19 1.89
@@ -1,22 +1,23 @@
/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
- * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
+ * Copyright (©) 2005,2006,2007,2008,2009 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.
+ * Deliantra is free software: you can redistribute it and/or modify it under
+ * the terms of the Affero 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 .
+ * You should have received a copy of the Affero GNU General Public License
+ * and the GNU General Public License along with this program. If not, see
+ * .
*
* The authors can be reached via e-mail to
*/
@@ -38,7 +39,7 @@
* but moved here so it could be applied to bolts too
* op is the spell object.
*/
-void
+static void
check_spell_knockback (object *op)
{
int weight_move;
@@ -390,7 +391,7 @@
* poison cloud ball, etc. op is the object to
* explode.
*/
-void
+static void
explode_bullet (object *op)
{
object *tmp, *owner;
@@ -547,10 +548,6 @@
void
move_bullet (object *op)
{
- sint16 new_x, new_y;
- int mflags;
- maptile *m;
-
#if 0
/* We need a better general purpose way to do this */
@@ -575,18 +572,20 @@
return;
}
- new_x = op->x + DIRX (op);
- new_y = op->y + DIRY (op);
- m = op->map;
- mflags = get_map_flags (m, &m, new_x, new_y, &new_x, &new_y);
+ mapxy pos (op);
+ pos.move (op->direction);
- if (mflags & P_OUT_OF_MAP)
+ if (!pos.normalise ())
{
op->destroy ();
return;
}
- if (!op->direction || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y)))
+ mapspace &ms = pos.ms ();
+
+ ms.update ();
+
+ if (!op->direction || OB_TYPE_MOVE_BLOCK (op, ms.move_block))
{
if (op->other_arch)
explode_bullet (op);
@@ -596,7 +595,7 @@
return;
}
- if (!(op = m->insert (op, new_x, new_y, op)))
+ if (!(op = pos.insert (op, op)))
return;
if (reflwall (op->map, op->x, op->y, op))
@@ -690,7 +689,7 @@
*****************************************************************************/
/* drops an object based on what is in the cone's "other_arch" */
-void
+static void
cone_drop (object *op)
{
object *new_ob = arch_to_object (op->other_arch);
@@ -830,18 +829,14 @@
for (i = range_min; i <= range_max; i++)
{
- sint16 x, y, d;
+ sint16 x, y;
/* 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;
+ int d = dir ? absdir (dir + i) : i;
/* 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,
@@ -953,7 +948,7 @@
if (op->env)
{
- if (env->map == NULL)
+ if (!env->map)
return;
if (!(op = op->insert_at (env, op)))
@@ -963,7 +958,7 @@
// elmex Tue Aug 15 17:46:51 CEST 2006: Prevent bomb from exploding
// on a safe map. I don't like this special casing, but it seems to be neccessary
// as bombs can be carried.
- if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE)
+ if (op->ms ().flags () & P_SAFE)
{
op->destroy ();
return;
@@ -1117,9 +1112,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)))
+ || (god && target->title == god->name)
+ || (god && target->race.contains (god->race)))
{
new_draw_info (NDI_UNIQUE, 0, op, "Your request is unheeded.");
return 0;
@@ -1136,7 +1133,7 @@
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.");
@@ -1260,11 +1257,11 @@
tmp->speed = 0.01;
tmp->stats.food = time;
SET_FLAG (tmp, FLAG_IS_USED_UP);
- tmp->glow_radius = min (MAX_LIGHT_RADIUS, radius);
+ tmp->set_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;
+ op->set_glow_radius (tmp->glow_radius);
return 1;
}
@@ -1435,9 +1432,7 @@
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;
- maptile *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
@@ -1453,147 +1448,139 @@
*/
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;
- 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;
-
- // 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 = GET_MAP_TOP (m, nx, ny); tmp; tmp = tmp->below)
- 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 */
- {
- /*
- 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.
-
- This is required to fix the 'charm monster' abuse, where a player level 1 can
- 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 affecting the monster */
-
- /* 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);
- } /* for y */
+ unordered_mapwalk (op, -range, -range, range, range)
+ {
+ mapspace &ms = m->at (nx, ny);
+
+ /* 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
+ * 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.
+
+ This is required to fix the 'charm monster' abuse, where a player level 1 can
+ 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 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;
}
@@ -1921,12 +1908,14 @@
tmp->stats.food = spell->duration + SP_level_duration_adjust (caster, spell);
if (tmp->glow_radius)
- tmp->glow_radius = min (MAX_LIGHT_RADIUS, spell->range + SP_level_range_adjust (caster, spell));
+ tmp->set_glow_radius (
+ clamp (spell->range + SP_level_range_adjust (caster, spell), 1, MAX_LIGHT_RADIUS)
+ );
if (dir)
m->insert (tmp, x, y, op);
else
- caster->outer_env ()->insert (tmp);
+ caster->outer_env_or_self ()->insert (tmp);
return 1;
}