rasputin/bin/
rasputin/cnf/
rasputin/doc/cwg/
rasputin/lib/
rasputin/lib/etc/
rasputin/lib/etc/boards/
rasputin/lib/house/
rasputin/lib/misc/
rasputin/lib/plralias/A-E/
rasputin/lib/plralias/F-J/
rasputin/lib/plralias/K-O/
rasputin/lib/plralias/P-T/
rasputin/lib/plralias/U-Z/
rasputin/lib/plralias/ZZZ/
rasputin/lib/plrfiles/
rasputin/lib/plrfiles/A-E/
rasputin/lib/plrfiles/F-J/
rasputin/lib/plrfiles/K-O/
rasputin/lib/plrfiles/P-T/
rasputin/lib/plrfiles/U-Z/
rasputin/lib/plrfiles/ZZZ/
rasputin/lib/plrobjs/
rasputin/lib/plrobjs/A-E/
rasputin/lib/plrobjs/F-J/
rasputin/lib/plrobjs/K-O/
rasputin/lib/plrobjs/P-T/
rasputin/lib/plrobjs/U-Z/
rasputin/lib/plrobjs/ZZZ/
rasputin/lib/plrvars/A-E/
rasputin/lib/plrvars/F-J/
rasputin/lib/plrvars/K-O/
rasputin/lib/plrvars/P-T/
rasputin/lib/plrvars/U-Z/
rasputin/lib/plrvars/ZZZ/
rasputin/lib/world/gld/
rasputin/lib/world/trg/
rasputin/src/
rasputin/src/doc/
/* ************************************************************************
*   File: magic.c                                       Part of CircleMUD *
*  Usage: low-level functions for magic; spell template code              *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */


#include "conf.h"
#include "sysdep.h"

CVSHEADER("$CVSHeader: cwg/rasputin/src/magic.c,v 1.16 2005/01/05 16:27:27 fnord Exp $");

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "spells.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "constants.h"
#include "dg_scripts.h"
#include "feats.h"

/* external variables */
extern int mini_mud;
extern struct spell_info_type spell_info[];
byte object_saving_throws(int material_type, int type);

/* external functions */
void clearMemory(struct char_data *ch);
void weight_change_object(struct obj_data *obj, int weight);

/* local functions */
int mag_materials(struct char_data *ch, int item0, int item1, int item2, int extract, int verbose);
void perform_mag_groups(int level, struct char_data *ch, struct char_data *tch, int spellnum);
void affect_update(void);
void affect_update_violence(void);
int obj_savingthrow(int material, int type);


int obj_savingthrow(int material, int type)
{
  int save, rnum;

  save = object_saving_throws(material, type);

  rnum = rand_number(1,100);

  if (rnum < save) {
    return (TRUE);
  }

  return (FALSE);
}

/* affect_update: called from comm.c (causes spells to wear off) */
void affect_update(void)
{
  struct affected_type *af, *next;
  struct char_data *i;

  for (i = affect_list; i; i = i->next_affect) {
    for (af = i->affected; af; af = next) {
      next = af->next;
      if (af->duration >= 1)
	af->duration--;
      else if (af->duration == 0) {
	if (af->type > 0)
	  if (!af->next || (af->next->type != af->type) ||
	      (af->next->duration > 0))
	    if (spell_info[af->type].wear_off_msg)
	      send_to_char(i, "%s\r\n", spell_info[af->type].wear_off_msg);
	affect_remove(i, af);
      }
    }
  }
}


/*
 *  mag_materials:
 *  Checks for up to 3 vnums (spell reagents) in the player's inventory.
 *
 * No spells implemented in Circle use mag_materials, but you can use
 * it to implement your own spells which require ingredients (i.e., some
 * heal spell which requires a rare herb or some such.)
 */
int mag_materials(struct char_data *ch, int item0, int item1, int item2,
		      int extract, int verbose)
{
  struct obj_data *tobj;
  struct obj_data *obj0 = NULL, *obj1 = NULL, *obj2 = NULL;

  for (tobj = ch->carrying; tobj; tobj = tobj->next_content) {
    if ((item0 > 0) && (GET_OBJ_VNUM(tobj) == item0)) {
      obj0 = tobj;
      item0 = -1;
    } else if ((item1 > 0) && (GET_OBJ_VNUM(tobj) == item1)) {
      obj1 = tobj;
      item1 = -1;
    } else if ((item2 > 0) && (GET_OBJ_VNUM(tobj) == item2)) {
      obj2 = tobj;
      item2 = -1;
    }
  }
  if ((item0 > 0) || (item1 > 0) || (item2 > 0)) {
    if (verbose) {
      switch (rand_number(0, 2)) {
      case 0:
	send_to_char(ch, "A wart sprouts on your nose.\r\n");
	break;
      case 1:
	send_to_char(ch, "Your hair falls out in clumps.\r\n");
	break;
      case 2:
	send_to_char(ch, "A huge corn develops on your big toe.\r\n");
	break;
      }
    }
    return (FALSE);
  }
  if (extract) {
    if (item0 < 0)
      extract_obj(obj0);
    if (item1 < 0)
      extract_obj(obj1);
    if (item2 < 0)
      extract_obj(obj2);
  }
  if (verbose) {
    send_to_char(ch, "A puff of smoke rises from your pack.\r\n");
    act("A puff of smoke rises from $n's pack.", TRUE, ch, NULL, NULL, TO_ROOM);
  }
  return (TRUE);
}



int mag_newsaves(struct char_data *ch, struct char_data *victim, int spellnum, int level, int cast_stat)
{
  int stype;
  int dc;
  int total;
  if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_FORT))
    stype = SAVING_FORTITUDE;
  else if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_REFLEX))
    stype = SAVING_REFLEX;
  else if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_WILL))
    stype = SAVING_WILL;
  else
    return FALSE;
  total = GET_SAVE(victim, stype) + rand_number(1, 20);
  dc = spell_info[spellnum].spell_level + level + ability_mod_value(cast_stat);
  if (ch) {
    if (HAS_SCHOOL_FEAT(ch, CFEAT_SPELL_FOCUS, spell_info[spellnum].school))
      dc++;
    if (HAS_SCHOOL_FEAT(ch, CFEAT_GREATER_SPELL_FOCUS, spell_info[spellnum].school))
      dc++;
  }
  if (total >= dc)
    return TRUE;
  return FALSE;
}


/*
 * Every spell that does damage comes through here.  This calculates the
 * amount of damage, adds in any modifiers, determines what the saves are,
 * tests for save and calls damage().
 *
 * -1 = dead, otherwise the amount of damage done.
 */
int mag_damage(int level, struct char_data *ch, struct char_data *victim,
		     int spellnum)
{
  int dam = 0;

  if (victim == NULL || ch == NULL)
    return (0);

  switch (spellnum) {
    /* Mostly mages */
  case SPELL_MAGIC_MISSILE:
    dam = dice(MIN(level, 5), 4) + MIN(level, 5);
    break;

  case SPELL_CHILL_TOUCH:	/* chill touch also has an affect */
    dam = dice(1, 6);
    break;

  case SPELL_BURNING_HANDS:
    dam = dice(MIN(level, 5), 4);
    break;

  case SPELL_SHOCKING_GRASP:
    dam = dice(1, 8) + MIN(level, 20);
    break;

  case SPELL_LIGHTNING_BOLT:
  case SPELL_FIREBALL:
    dam = dice(MIN(level, 10), 6);
    break;

  case SPELL_COLOR_SPRAY:
    dam = dice(1, 1) + 1;
    break;

    /* Mostly clerics */
  case SPELL_DISPEL_EVIL:
    dam = dice(6, 8) + 6;
    if (IS_EVIL(ch)) {
      victim = ch;
      dam = GET_HIT(ch) - 1;
    } else if (IS_GOOD(victim)) {
      act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
      return (0);
    }
    break;

  case SPELL_DISPEL_GOOD:
    dam = dice(6, 8) + 6;
    if (IS_GOOD(ch)) {
      victim = ch;
      dam = GET_HIT(ch) - 1;
    } else if (IS_EVIL(victim)) {
      act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
      return (0);
    }
    break;

  case SPELL_CALL_LIGHTNING:
    dam = dice(MIN(level, 10), 10);
    break;

  case SPELL_HARM:
    dam = dice(8, 8) + 8;
    break;

  case SPELL_ENERGY_DRAIN:
    if (GET_LEVEL(victim) <= 2)
      dam = 100;
    else
      dam = dice(1, 10);
    break;

    /* Area spells */
  case SPELL_EARTHQUAKE:
    dam = dice(2, 8) + level;
    break;

  case SPELL_INFLICT_LIGHT:
    dam = dice(1, 8) + MIN(level, 5);
    break;
  case SPELL_INFLICT_CRITIC:
    dam = dice(4, 8) + MIN(level, 20);
    break;

  case SPELL_ACID_SPLASH:
  case SPELL_RAY_OF_FROST:
    dam = dice(1, 3);
    break;

  case SPELL_DISRUPT_UNDEAD:
    if (AFF_FLAGGED(victim, AFF_UNDEAD))
      dam = dice(1, 6);
    else {
      send_to_char(ch, "This magic only affects the undead!\r\n");
      dam = 0;
    }
    break;

  case SPELL_ICE_STORM:
  case SPELL_SHOUT:
    dam = dice(5, 6);
    break;

  case SPELL_CONE_OF_COLD:
    dam = dice(MIN(level, 15), 6);
    break;

  } /* switch(spellnum) */

  if (mag_newsaves(ch, victim, spellnum, level, GET_INT(ch))) {
    if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_NONE)) {
      send_to_char(victim, "@g*save*@y You avoid any injury.@n\r\n");
      dam = 0;
    } else if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_HALF)) {
      if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_REFLEX) &&
          HAS_FEAT(victim, FEAT_EVASION) && 
          (!GET_EQ(victim, WEAR_BODY) ||
          GET_OBJ_TYPE(GET_EQ(victim, WEAR_BODY)) != ITEM_ARMOR ||
          GET_OBJ_VAL(GET_EQ(victim, WEAR_BODY), VAL_ARMOR_SKILL) < ARMOR_TYPE_MEDIUM)) {
        send_to_char(victim, "@g*save*@y Your evasion ability allows you to avoid ANY injury.@n\r\n");
        dam = 0;
      } else {
        send_to_char(victim, "@g*save*@y You take half damage.@n\r\n");
        dam /= 2;
      }
    }
  } else if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_HALF) &&
             IS_SET(spell_info[spellnum].save_flags, MAGSAVE_REFLEX) &&
             HAS_FEAT(victim, FEAT_IMPROVED_EVASION) && 
             (!GET_EQ(victim, WEAR_BODY) ||
              GET_OBJ_TYPE(GET_EQ(victim, WEAR_BODY)) != ITEM_ARMOR ||
              GET_OBJ_VAL(GET_EQ(victim, WEAR_BODY), VAL_ARMOR_SKILL) < ARMOR_TYPE_MEDIUM)) {
    send_to_char(victim, "@r*save*@y Your improved evasion prevents full damage even on failure.@n\r\n");
    dam /= 2;
  }

  /* and finally, inflict the damage */
  return (damage(ch, victim, dam, spellnum, 0, -1, 0, spellnum, 1));
}


/*
 * Every spell that does an affect comes through here.  This determines
 * the effect, whether it is added or replacement, whether it is legal or
 * not, etc.
 *
 * affect_join(vict, aff, add_dur, avg_dur, add_mod, avg_mod)
 */

#define MAX_SPELL_AFFECTS 5	/* change if more needed */

void mag_affects(int level, struct char_data *ch, struct char_data *victim,
		      int spellnum)
{
  struct affected_type af[MAX_SPELL_AFFECTS];
  bool accum_affect = FALSE, accum_duration = FALSE;
  const char *to_vict = NULL, *to_room = NULL;
  int i;


  if (victim == NULL || ch == NULL)
    return;

  for (i = 0; i < MAX_SPELL_AFFECTS; i++) {
    af[i].type = spellnum;
    af[i].bitvector = 0;
    af[i].modifier = 0;
    af[i].location = APPLY_NONE;
  }

  if (mag_newsaves(ch, victim, spellnum, level, GET_INT(ch))) {
    if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_PARTIAL | MAGSAVE_NONE)) {
      send_to_char(victim, "@g*save*@y You avoid any lasting affects.@n\r\n");
      return;
    }
  }

  switch (spellnum) {

  case SPELL_CHILL_TOUCH:
    af[0].location = APPLY_STR;
    af[0].duration = 24;
    af[0].modifier = -1;
    accum_duration = TRUE;
    to_vict = "You feel your strength wither!";
    break;

  case SPELL_MAGE_ARMOR:
    af[0].location = APPLY_AC;
    af[0].modifier = 40;
    af[0].duration = 1 * GET_LEVEL(ch);
    accum_duration = FALSE;
    to_vict = "You feel someone protecting you.";
    break;

  case SPELL_BLESS:
    af[0].location = APPLY_ACCURACY;
    af[0].modifier = 2;
    af[0].duration = 6;

    af[1].location = APPLY_SAVING_SPELL;
    af[1].modifier = -1;
    af[1].duration = 6;

    accum_duration = TRUE;
    to_vict = "You feel righteous.";
    break;

  case SPELL_BLINDNESS:
    if (MOB_FLAGGED(victim,MOB_NOBLIND)) {
      send_to_char(ch, "You fail.\r\n");
      return;
    }

    af[0].location = APPLY_ACCURACY;
    af[0].modifier = -4;
    af[0].duration = 2;
    af[0].bitvector = AFF_BLIND;

    af[1].location = APPLY_AC;
    af[1].modifier = -4;
    af[1].duration = 2;
    af[1].bitvector = AFF_BLIND;

    to_room = "$n seems to be blinded!";
    to_vict = "You have been blinded!";
    break;

  case SPELL_BESTOW_CURSE:
    af[0].location = APPLY_ACCURACY;
    af[0].duration = 1 + (level / 2);
    af[0].modifier = -1;
    af[0].bitvector = AFF_CURSE;

    af[1].location = APPLY_DAMAGE;
    af[1].duration = 1 + (level / 2);
    af[1].modifier = -1;
    af[1].bitvector = AFF_CURSE;

    accum_duration = TRUE;
    accum_affect = TRUE;
    to_room = "$n briefly glows red!";
    to_vict = "You feel very uncomfortable.";
    break;

  case SPELL_DETECT_ALIGN:
    af[0].duration = 12 + level;
    af[0].bitvector = AFF_DETECT_ALIGN;
    accum_duration = TRUE;
    to_vict = "Your eyes tingle.";
    break;

  case SPELL_SEE_INVIS:
    af[0].duration = 12 + level;
    af[0].bitvector = AFF_DETECT_INVIS;
    accum_duration = TRUE;
    to_vict = "Your eyes tingle.";
    break;

  case SPELL_DETECT_MAGIC:
    af[0].duration = 12 + level;
    af[0].bitvector = AFF_DETECT_MAGIC;
    accum_duration = TRUE;
    to_vict = "Your eyes tingle.";
    break;

  case SPELL_FAERIE_FIRE:
    af[0].location = APPLY_AC;
    af[0].modifier = -1; /*should make target easier to hit */
    af[0].duration = 3;
    accum_duration = FALSE;
    to_vict = "Your body flickers with a purplish light.";
    to_room = "$n's body flickers with with a purplish light.";
    break;

  case SPELL_DARKVISION:
    af[0].duration = 12 + level;
    af[0].bitvector = AFF_INFRAVISION;
    accum_duration = TRUE;
    to_vict = "Your eyes glow red.";
    to_room = "$n's eyes glow red.";
    break;

  case SPELL_INVISIBLE:
    if (!victim)
      victim = ch;

    af[0].duration = 12 + (level / 4);
    af[0].modifier = 4;
    af[0].location = APPLY_AC;
    af[0].bitvector = AFF_INVISIBLE;
    accum_duration = TRUE;
    to_vict = "You vanish.";
    to_room = "$n slowly fades out of existence.";
    break;

  case SPELL_POISON:
    af[0].location = APPLY_STR;
    af[0].duration = level;
    af[0].modifier = -2;
    af[0].bitvector = AFF_POISON;
    to_vict = "You feel very sick.";
    to_room = "$n gets violently ill!";
    break;

  case SPELL_PROT_FROM_EVIL:
    af[0].duration = 24;
    af[0].bitvector = AFF_PROTECT_EVIL;
    accum_duration = TRUE;
    to_vict = "You feel invulnerable!";
    break;

  case SPELL_SANCTUARY:
    af[0].duration = 4;
    af[0].bitvector = AFF_SANCTUARY;

    accum_duration = TRUE;
    to_vict = "A white aura momentarily surrounds you.";
    to_room = "$n is surrounded by a white aura.";
    break;

  case SPELL_SLEEP:
    if (!CONFIG_PK_ALLOWED && !IS_NPC(ch) && !IS_NPC(victim))
      return;
    if (MOB_FLAGGED(victim, MOB_NOSLEEP))
      return;

    af[0].duration = 4 + (level / 4);
    af[0].bitvector = AFF_SLEEP;

    if (GET_POS(victim) > POS_SLEEPING) {
      send_to_char(victim, "You feel very sleepy...  Zzzz......\r\n");
      act("$n goes to sleep.", TRUE, victim, 0, 0, TO_ROOM);
      GET_POS(victim) = POS_SLEEPING;
    }
    break;

  case SPELL_BULL_STRENGTH:
    af[0].location = APPLY_STR;
    af[0].duration = level;
    af[0].modifier = 1 + (rand_number(1, 4));
    accum_duration = FALSE;
    accum_affect = FALSE;
    to_vict = "You feel stronger!";
    break;

  case SPELL_SENSE_LIFE:
    to_vict = "Your feel your awareness improve.";
    af[0].duration = level;
    af[0].bitvector = AFF_SENSE_LIFE;
    accum_duration = TRUE;
    break;

  case SPELL_WATERWALK:
    af[0].duration = 24;
    af[0].bitvector = AFF_WATERWALK;
    accum_duration = TRUE;
    to_vict = "You feel webbing between your toes.";
    break;

  case SPELL_STONESKIN:
    af[0].duration = 1 * GET_LEVEL(ch);
    accum_duration = FALSE;
    to_vict = "Your skin hardens into stone!";
    break;

  }

  /*
   * If this is a mob that has this affect set in its mob file, do not
   * perform the affect.  This prevents people from un-sancting mobs
   * by sancting them and waiting for it to fade, for example.
   */
  if (IS_NPC(victim) && !affected_by_spell(victim, spellnum))
    for (i = 0; i < MAX_SPELL_AFFECTS; i++)
      if (AFF_FLAGGED(victim, af[i].bitvector)) {
	send_to_char(ch, "%s", CONFIG_NOEFFECT);
	return;
      }

  /*
   * If the victim is already affected by this spell, and the spell does
   * not have an accumulative effect, then fail the spell.
   */
  if (affected_by_spell(victim,spellnum) && !(accum_duration||accum_affect)) {
    send_to_char(ch, "%s", CONFIG_NOEFFECT);
    return;
  }

  for (i = 0; i < MAX_SPELL_AFFECTS; i++)
    if (af[i].bitvector || (af[i].location != APPLY_NONE))
      affect_join(victim, af+i, accum_duration, FALSE, accum_affect, FALSE);

  if (to_vict != NULL)
    act(to_vict, FALSE, victim, 0, ch, TO_CHAR);
  if (to_room != NULL)
    act(to_room, TRUE, victim, 0, ch, TO_ROOM);
}


/*
 * This function is used to provide services to mag_groups.  This function
 * is the one you should change to add new group spells.
 */
void perform_mag_groups(int level, struct char_data *ch,
			struct char_data *tch, int spellnum)
{
  switch (spellnum) {
    case SPELL_MASS_HEAL:
    mag_points(level, ch, tch, SPELL_HEAL);
    break;
  case SPELL_GROUP_ARMOR:
    mag_affects(level, ch, tch, SPELL_MAGE_ARMOR);
    break;
  case SPELL_GROUP_RECALL:
    spell_recall(level, ch, tch, NULL, NULL);
    break;
  }
}


/*
 * Every spell that affects the group should run through here
 * perform_mag_groups contains the switch statement to send us to the right
 * magic.
 *
 * group spells affect everyone grouped with the caster who is in the room,
 * caster last.
 *
 * To add new group spells, you shouldn't have to change anything in
 * mag_groups -- just add a new case to perform_mag_groups.
 */
void mag_groups(int level, struct char_data *ch, int spellnum)
{
  struct char_data *tch, *k;
  struct follow_type *f, *f_next;

  if (ch == NULL)
    return;

  if (!AFF_FLAGGED(ch, AFF_GROUP))
    return;
  if (ch->master != NULL)
    k = ch->master;
  else
    k = ch;
  for (f = k->followers; f; f = f_next) {
    f_next = f->next;
    tch = f->follower;
    if (IN_ROOM(tch) != IN_ROOM(ch))
      continue;
    if (!AFF_FLAGGED(tch, AFF_GROUP))
      continue;
    if (ch == tch)
      continue;
    perform_mag_groups(level, ch, tch, spellnum);
  }

  if ((k != ch) && AFF_FLAGGED(k, AFF_GROUP))
    perform_mag_groups(level, ch, k, spellnum);
  perform_mag_groups(level, ch, ch, spellnum);
}


/*
 * mass spells affect every creature in the room except the caster.
 *
 * No spells of this class currently implemented.
 */
void mag_masses(int level, struct char_data *ch, int spellnum)
{
  struct char_data *tch, *tch_next;

  for (tch = world[IN_ROOM(ch)].people; tch; tch = tch_next) {
    tch_next = tch->next_in_room;
    if (tch == ch)
      continue;

    switch (spellnum) {
    }
  }
}


/*
 * Every spell that affects an area (room) runs through here.  These are
 * generally offensive spells.  This calls mag_damage to do the actual
 * damage -- all spells listed here must also have a case in mag_damage()
 * in order for them to work.
 *
 *  area spells have limited targets within the room.
 */
void mag_areas(int level, struct char_data *ch, int spellnum)
{
  struct char_data *tch, *next_tch;
  const char *to_char = NULL, *to_room = NULL;

  if (ch == NULL)
    return;

  /*
   * to add spells to this fn, just add the message here plus an entry
   * in mag_damage for the damaging part of the spell.
   */
  switch (spellnum) {
  case SPELL_EARTHQUAKE:
    to_char = "You gesture and the earth begins to shake all around you!";
    to_room ="$n gracefully gestures and the earth begins to shake violently!";
    break;
  }

  if (to_char != NULL)
    act(to_char, FALSE, ch, 0, 0, TO_CHAR);
  if (to_room != NULL)
    act(to_room, FALSE, ch, 0, 0, TO_ROOM);
  

  for (tch = world[IN_ROOM(ch)].people; tch; tch = next_tch) {
    next_tch = tch->next_in_room;

    /*
     * The skips: 1: the caster
     *            2: immortals
     *            3: if no pk on this mud, skips over all players
     *            4: pets (charmed NPCs)
     */

    if (tch == ch)
      continue;
    if (ADM_FLAGGED(tch, ADM_NODAMAGE))
      continue;
    if (!CONFIG_PK_ALLOWED && !IS_NPC(ch) && !IS_NPC(tch))
      continue;
    if (!IS_NPC(ch) && IS_NPC(tch) && AFF_FLAGGED(tch, AFF_CHARM))
      continue;

    /* Doesn't matter if they die here so we don't check. -gg 6/24/98 */
    mag_damage(level, ch, tch, spellnum);
  }
}


/*
 *  Every spell which summons/gates/conjours a mob comes through here.
 */

mob_vnum monsum_list_lg_1[] = { 300, 301, 302, NOBODY };
mob_vnum monsum_list_ng_1[] = { 300, 301, 302, 303, 304, NOBODY };
mob_vnum monsum_list_cg_1[] = { 302, 303, 304, NOBODY };
mob_vnum monsum_list_ln_1[] = { 300, 301, 305, 306, NOBODY };
mob_vnum monsum_list_nn_1[] = { 302, 307, 308, NOBODY };
mob_vnum monsum_list_cn_1[] = { 303, 304, 309, 310, 311, 312, NOBODY };
mob_vnum monsum_list_le_1[] = { 305, 306, 307, 308, NOBODY };
mob_vnum monsum_list_ne_1[] = { 305, 306, 307, 308, 309, 310, 311, 312, NOBODY };
mob_vnum monsum_list_ce_1[] = { 307, 308, 309, 310, 311, 312, NOBODY };

mob_vnum monsum_list_lg_2[] = { NOBODY };
mob_vnum monsum_list_ng_2[] = { NOBODY };
mob_vnum monsum_list_cg_2[] = { NOBODY };
mob_vnum monsum_list_ln_2[] = { NOBODY };
mob_vnum monsum_list_nn_2[] = { NOBODY };
mob_vnum monsum_list_cn_2[] = { NOBODY };
mob_vnum monsum_list_le_2[] = { NOBODY };
mob_vnum monsum_list_ne_2[] = { NOBODY };
mob_vnum monsum_list_ce_2[] = { NOBODY };

mob_vnum monsum_list_lg_3[] = { NOBODY };
mob_vnum monsum_list_ng_3[] = { NOBODY };
mob_vnum monsum_list_cg_3[] = { NOBODY };
mob_vnum monsum_list_ln_3[] = { NOBODY };
mob_vnum monsum_list_nn_3[] = { NOBODY };
mob_vnum monsum_list_cn_3[] = { NOBODY };
mob_vnum monsum_list_le_3[] = { NOBODY };
mob_vnum monsum_list_ne_3[] = { NOBODY };
mob_vnum monsum_list_ce_3[] = { NOBODY };

mob_vnum monsum_list_lg_4[] = { NOBODY };
mob_vnum monsum_list_ng_4[] = { NOBODY };
mob_vnum monsum_list_cg_4[] = { NOBODY };
mob_vnum monsum_list_ln_4[] = { NOBODY };
mob_vnum monsum_list_nn_4[] = { NOBODY };
mob_vnum monsum_list_cn_4[] = { NOBODY };
mob_vnum monsum_list_le_4[] = { NOBODY };
mob_vnum monsum_list_ne_4[] = { NOBODY };
mob_vnum monsum_list_ce_4[] = { NOBODY };

mob_vnum monsum_list_lg_5[] = { NOBODY };
mob_vnum monsum_list_ng_5[] = { NOBODY };
mob_vnum monsum_list_cg_5[] = { NOBODY };
mob_vnum monsum_list_ln_5[] = { NOBODY };
mob_vnum monsum_list_nn_5[] = { NOBODY };
mob_vnum monsum_list_cn_5[] = { NOBODY };
mob_vnum monsum_list_le_5[] = { NOBODY };
mob_vnum monsum_list_ne_5[] = { NOBODY };
mob_vnum monsum_list_ce_5[] = { NOBODY };

mob_vnum monsum_list_lg_6[] = { NOBODY };
mob_vnum monsum_list_ng_6[] = { NOBODY };
mob_vnum monsum_list_cg_6[] = { NOBODY };
mob_vnum monsum_list_ln_6[] = { NOBODY };
mob_vnum monsum_list_nn_6[] = { NOBODY };
mob_vnum monsum_list_cn_6[] = { NOBODY };
mob_vnum monsum_list_le_6[] = { NOBODY };
mob_vnum monsum_list_ne_6[] = { NOBODY };
mob_vnum monsum_list_ce_6[] = { NOBODY };

mob_vnum monsum_list_lg_7[] = { NOBODY };
mob_vnum monsum_list_ng_7[] = { NOBODY };
mob_vnum monsum_list_cg_7[] = { NOBODY };
mob_vnum monsum_list_ln_7[] = { NOBODY };
mob_vnum monsum_list_nn_7[] = { NOBODY };
mob_vnum monsum_list_cn_7[] = { NOBODY };
mob_vnum monsum_list_le_7[] = { NOBODY };
mob_vnum monsum_list_ne_7[] = { NOBODY };
mob_vnum monsum_list_ce_7[] = { NOBODY };

mob_vnum monsum_list_lg_8[] = { NOBODY };
mob_vnum monsum_list_ng_8[] = { NOBODY };
mob_vnum monsum_list_cg_8[] = { NOBODY };
mob_vnum monsum_list_ln_8[] = { NOBODY };
mob_vnum monsum_list_nn_8[] = { NOBODY };
mob_vnum monsum_list_cn_8[] = { NOBODY };
mob_vnum monsum_list_le_8[] = { NOBODY };
mob_vnum monsum_list_ne_8[] = { NOBODY };
mob_vnum monsum_list_ce_8[] = { NOBODY };

mob_vnum monsum_list_lg_9[] = { NOBODY };
mob_vnum monsum_list_ng_9[] = { NOBODY };
mob_vnum monsum_list_cg_9[] = { NOBODY };
mob_vnum monsum_list_ln_9[] = { NOBODY };
mob_vnum monsum_list_nn_9[] = { NOBODY };
mob_vnum monsum_list_cn_9[] = { NOBODY };
mob_vnum monsum_list_le_9[] = { NOBODY };
mob_vnum monsum_list_ne_9[] = { NOBODY };
mob_vnum monsum_list_ce_9[] = { NOBODY };

mob_vnum *monsum_list[9][9] = {
  { monsum_list_lg_1, monsum_list_ng_1, monsum_list_cg_1,
    monsum_list_ln_1, monsum_list_nn_1, monsum_list_cn_1,
    monsum_list_le_1, monsum_list_ne_1, monsum_list_ce_1 },
  { monsum_list_lg_2, monsum_list_ng_2, monsum_list_cg_2,
    monsum_list_ln_2, monsum_list_nn_2, monsum_list_cn_2,
    monsum_list_le_2, monsum_list_ne_2, monsum_list_ce_2 },
  { monsum_list_lg_3, monsum_list_ng_3, monsum_list_cg_3,
    monsum_list_ln_3, monsum_list_nn_3, monsum_list_cn_3,
    monsum_list_le_3, monsum_list_ne_3, monsum_list_ce_3 },
  { monsum_list_lg_4, monsum_list_ng_4, monsum_list_cg_4,
    monsum_list_ln_4, monsum_list_nn_4, monsum_list_cn_4,
    monsum_list_le_4, monsum_list_ne_4, monsum_list_ce_4 },
  { monsum_list_lg_5, monsum_list_ng_5, monsum_list_cg_5,
    monsum_list_ln_5, monsum_list_nn_5, monsum_list_cn_5,
    monsum_list_le_5, monsum_list_ne_5, monsum_list_ce_5 },
  { monsum_list_lg_6, monsum_list_ng_6, monsum_list_cg_6,
    monsum_list_ln_6, monsum_list_nn_6, monsum_list_cn_6,
    monsum_list_le_6, monsum_list_ne_6, monsum_list_ce_6 },
  { monsum_list_lg_7, monsum_list_ng_7, monsum_list_cg_7,
    monsum_list_ln_7, monsum_list_nn_7, monsum_list_cn_7,
    monsum_list_le_7, monsum_list_ne_7, monsum_list_ce_7 },
  { monsum_list_lg_8, monsum_list_ng_8, monsum_list_cg_8,
    monsum_list_ln_8, monsum_list_nn_8, monsum_list_cn_8,
    monsum_list_le_8, monsum_list_ne_8, monsum_list_ce_8 },
  { monsum_list_lg_9, monsum_list_ng_9, monsum_list_cg_9,
    monsum_list_ln_9, monsum_list_nn_9, monsum_list_cn_9,
    monsum_list_le_9, monsum_list_ne_9, monsum_list_ce_9 }
};

/*
 * These use act(), don't put the \r\n.
 */
const char *mag_summon_msgs[] = {
  "\r\n",
  "$n animates a corpse!",
  "$n summons extraplanar assistance!"
};

/* Defined mobiles. */
#define MOB_ELEMENTAL_BASE	20	/* Only one for now. */
#define MOB_ZOMBIE		11
#define MOB_AERIALSERVANT	19

void mag_summons(int level, struct char_data *ch, struct obj_data *obj,
		      int spellnum, const char *arg)
{
  struct char_data *mob = NULL;
  struct obj_data *tobj, *next_obj;
  int msg = 0, num = 1, handle_corpse = FALSE, affs = 0, affvs = 0, assist = 0, i, j, count;
  char *buf = NULL, buf2[MAX_INPUT_LENGTH];
  int lev;
  mob_vnum mob_num;

  if (ch == NULL)
    return;

  lev = spell_info[spellnum].spell_level;

  switch (spellnum) {
  case SPELL_ANIMATE_DEAD:
    if (obj == NULL) {
      send_to_char(ch, "With what corpse?\r\n");
      return;
    }
    if (!IS_CORPSE(obj)) {
      send_to_char(ch, "That's not a corpse!\r\n");
      return;
    }
    handle_corpse = TRUE;
    msg = 11;
    mob_num = MOB_ZOMBIE;
    break;

  case SPELL_SUMMON_MONSTER_I:
  case SPELL_SUMMON_MONSTER_II:
  case SPELL_SUMMON_MONSTER_III:
  case SPELL_SUMMON_MONSTER_IV:
  case SPELL_SUMMON_MONSTER_V:
  case SPELL_SUMMON_MONSTER_VI:
  case SPELL_SUMMON_MONSTER_VII:
  case SPELL_SUMMON_MONSTER_VIII:
  case SPELL_SUMMON_MONSTER_IX:
    mob_num = NOBODY;
    affvs = 1;
    assist = 1;
    if (arg) {
      buf = (char *)arg;
      skip_spaces(&buf);
      if (!*buf)
        buf = NULL;
    }
    j = ALIGN_TYPE(ch);
    if (buf) {
      buf = any_one_arg(buf, buf2);
      for (i = lev - 1; i >= 0; i--) {
        for (count = 0; monsum_list[i][j][count] != NOBODY; count++) {
          mob_num = monsum_list[i][j][count];
          if (real_mobile(mob_num) == NOBODY)
            mob_num = NOBODY;
          else if (!is_name(buf2, mob_proto[real_mobile(mob_num)].name))
            mob_num = NOBODY;
          else
            break;
        }
        if (mob_num != NOBODY)
          break;
      }
      if (mob_num == NOBODY) {
        send_to_char(ch, "That's not a name for a monster you can summon. Summoning something else.\r\n");
      } else {
        log("lev=%d, i=%d, ngen=%d", lev, i, lev - i);
        switch (lev - i) {
        case 1:
          num = 1;
          break;
        case 2:
          num = rand_number(1, 3);
          break;
        default:
          num = rand_number(1, 4) + 1;
          break;
        }
      }
    }
    if (mob_num == NOBODY) {
      num = 1;
      for (count = 0; monsum_list[lev - 1][j][count] != NOBODY; count++);
      if (!count) {
        log("No monsums for spell level %d align %s", lev, alignments[j]);
        return;
      }
      count--;
      mob_num = monsum_list[lev - 1][j][rand_number(0, count)];
    }
    break;

  default:
    return;
  }

  if (AFF_FLAGGED(ch, AFF_CHARM)) {
    send_to_char(ch, "You are too giddy to have any followers!\r\n");
    return;
  }
  for (i = 0; i < num; i++) {
    if (!(mob = read_mobile(mob_num, VIRTUAL))) {
      send_to_char(ch, "You don't quite remember how to summon that creature.\r\n");
      return;
    }
    char_to_room(mob, IN_ROOM(ch));
    if (affs)
      mag_affects(level, ch, mob, spellnum);
    if (affvs)
      mag_affectsv(level, ch, mob, spellnum);
    IS_CARRYING_W(mob) = 0;
    IS_CARRYING_N(mob) = 0;
    SET_BIT_AR(AFF_FLAGS(mob), AFF_CHARM);
    act(mag_summon_msgs[msg], FALSE, ch, 0, mob, TO_ROOM);
    load_mtrigger(mob);
    add_follower(mob, ch);
    if (assist && FIGHTING(ch)) {
      set_fighting(mob, FIGHTING(ch));
    }
    mob->master_id = GET_IDNUM(ch);
  }
  if (handle_corpse) {
    for (tobj = obj->contains; tobj; tobj = next_obj) {
      next_obj = tobj->next_content;
      obj_from_obj(tobj);
      obj_to_char(tobj, mob);
    }
    extract_obj(obj);
  }
}


void mag_points(int level, struct char_data *ch, struct char_data *victim,
		     int spellnum)
{
  int healing = 0, move = 0;
  int tmp;

  if (victim == NULL)
    return;

  switch (spellnum) {
  case SPELL_CURE_LIGHT:
    healing = dice(1, 8) + MIN(level, 5);
    send_to_char(victim, "You feel better.\r\n");
    break;
  case SPELL_CURE_CRITIC:
    healing = dice(4, 8) + MIN(level, 20);
    send_to_char(victim, "You feel a lot better!\r\n");
    break;
  case SPELL_HEAL:
    healing = 100 + dice(3, 8);
    send_to_char(victim, "A warm feeling floods your body.\r\n");
    if (AFF_FLAGGED(ch, AFF_CDEATH)) {
      affectv_from_char(ch, ART_QUIVERING_PALM);
      send_to_char(ch, "Your nerves settle slightly\r\n");
    }
    break;
  case ART_WHOLENESS_OF_BODY:
    healing = GET_MAX_HIT(victim) - GET_HIT(victim);
    healing = MAX(0, healing);
    tmp = GET_KI(ch) / 2;
    if (tmp > healing)
      tmp = healing;
    else {
      healing = tmp;
    }
    GET_KI(ch) -= tmp * 2;
    break;
  }
  GET_HIT(victim) = MIN(GET_MAX_HIT(victim), GET_HIT(victim) + healing);
  GET_MOVE(victim) = MIN(GET_MAX_MOVE(victim), GET_MOVE(victim) + move);
  update_pos(victim);
}


void mag_unaffects(int level, struct char_data *ch, struct char_data *victim,
		        int spellnum)
{
  int spell = 0, msg_not_affected = TRUE;
  const char *to_vict = NULL, *to_room = NULL;

  if (victim == NULL)
    return;

  switch (spellnum) {
  case SPELL_HEAL:
    /*
     * Heal also restores health, so don't give the "no effect" message
     * if the target isn't afflicted by the 'blindness' spell.
     */
    msg_not_affected = FALSE;
    /* fall-through */
  case SPELL_REMOVE_BLINDNESS:
    spell = SPELL_BLINDNESS;
    to_vict = "Your vision returns!";
    to_room = "There's a momentary gleam in $n's eyes.";
    break;
  case SPELL_NEUTRALIZE_POISON:
    spell = SPELL_POISON;
    to_vict = "A warm feeling runs through your body!";
    to_room = "$n looks better.";
    break;
  case SPELL_REMOVE_CURSE:
    spell = SPELL_BESTOW_CURSE;
    to_vict = "You don't feel so unlucky.";
    break;
  default:
    log("SYSERR: unknown spellnum %d passed to mag_unaffects.", spellnum);
    return;
  }

  if (!affected_by_spell(victim, spell)) {
    if (msg_not_affected)
      send_to_char(ch, "%s", CONFIG_NOEFFECT);
    return;
  }

  affect_from_char(victim, spell);
  if (to_vict != NULL)
    act(to_vict, FALSE, victim, 0, ch, TO_CHAR);
  if (to_room != NULL)
    act(to_room, TRUE, victim, 0, ch, TO_ROOM);

}


void mag_alter_objs(int level, struct char_data *ch, struct obj_data *obj,
		         int spellnum)
{
  const char *to_char = NULL, *to_room = NULL;

  if (obj == NULL)
    return;

  switch (spellnum) {
    case SPELL_BLESS:
      if (!OBJ_FLAGGED(obj, ITEM_BLESS) &&
	  (GET_OBJ_WEIGHT(obj) <= 5 * level)) {
	SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_BLESS);
	to_char = "$p glows briefly.";
      }
      break;
    case SPELL_BESTOW_CURSE:
      if (!OBJ_FLAGGED(obj, ITEM_NODROP)) {
	SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_NODROP);
	if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
	  GET_OBJ_VAL(obj, VAL_WEAPON_DAMSIZE)--;
	to_char = "$p briefly glows red.";
      }
      break;
    case SPELL_INVISIBLE:
      if (!OBJ_FLAGGED(obj, ITEM_NOINVIS | ITEM_INVISIBLE)) {
        SET_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_INVISIBLE);
        to_char = "$p vanishes.";
      }
      break;
    case SPELL_POISON:
      if (((GET_OBJ_TYPE(obj) == ITEM_DRINKCON) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOOD)) && !GET_OBJ_VAL(obj, VAL_FOOD_POISON)) {
      GET_OBJ_VAL(obj, VAL_FOOD_POISON) = 1;
      to_char = "$p steams briefly.";
      }
      break;
    case SPELL_REMOVE_CURSE:
      if (OBJ_FLAGGED(obj, ITEM_NODROP)) {
        REMOVE_BIT_AR(GET_OBJ_EXTRA(obj), ITEM_NODROP);
        if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
          GET_OBJ_VAL(obj, VAL_WEAPON_DAMSIZE)++;
        to_char = "$p briefly glows blue.";
      }
      break;
    case SPELL_NEUTRALIZE_POISON:
      if (((GET_OBJ_TYPE(obj) == ITEM_DRINKCON) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOOD)) && GET_OBJ_VAL(obj, VAL_FOOD_POISON)) {
        GET_OBJ_VAL(obj, VAL_FOOD_POISON) = 0;
        to_char = "$p steams briefly.";
      }
      break;
  }

  if (to_char == NULL)
    send_to_char(ch, "%s", CONFIG_NOEFFECT);
  else
    act(to_char, TRUE, ch, obj, 0, TO_CHAR);

  if (to_room != NULL)
    act(to_room, TRUE, ch, obj, 0, TO_ROOM);
  else if (to_char != NULL)
    act(to_char, TRUE, ch, obj, 0, TO_ROOM);

}



void mag_creations(int level, struct char_data *ch, int spellnum)
{
  struct obj_data *tobj;
  obj_vnum z;

  if (ch == NULL)
    return;
  /* level = MAX(MIN(level, LVL_IMPL), 1); - Hm, not used. */

  switch (spellnum) {
  case SPELL_CREATE_FOOD:
    z = 10;
    break;
  default:
    send_to_char(ch, "Spell unimplemented, it would seem.\r\n");
    return;
  }

  if (!(tobj = read_object(z, VIRTUAL))) {
    send_to_char(ch, "I seem to have goofed.\r\n");
    log("SYSERR: spell_creations, spell %d, obj %d: obj not found",
	    spellnum, z);
    return;
  }
  add_unique_id(tobj);
  obj_to_char(tobj, ch);
  act("$n creates $p.", FALSE, ch, tobj, 0, TO_ROOM);
  act("You create $p.", FALSE, ch, tobj, 0, TO_CHAR);
  load_otrigger(tobj);
}

/* affect_update_violence: called from fight.c (causes spells to wear off) */
void affect_update_violence(void)
{
  struct affected_type *af, *next;
  struct char_data *i;
  int dam;
  int maxdam;

  for (i = affectv_list; i; i = i->next_affectv) {
    for (af = i->affectedv; af; af = next) {
      next = af->next;
      if (af->duration >= 1) {
        af->duration--;
        switch (af->type) {
        case ART_EMPTY_BODY:
          if (GET_KI(i) >= 10) {
            GET_KI(i) -= 10;
          } else {
            af->duration = 0; /* Wear it off immediately! No more ki */
          }
        }
      } else if (af->duration == -1) {     /* No action */
        continue;
      }
      if (!af->duration) {
        if ((af->type > 0) && (af->type < SKILL_TABLE_SIZE))
          if (!af->next || (af->next->type != af->type) ||
              (af->next->duration > 0))
            if (spell_info[af->type].wear_off_msg)
              send_to_char(i, "%s\r\n", spell_info[af->type].wear_off_msg);
          if (af->bitvector == AFF_SUMMONED) {
            stop_follower(i);
            if (!DEAD(i))
              extract_char(i);
          }
          if (af->type == ART_QUIVERING_PALM) {
            maxdam = GET_HIT(i) + 8;
            dam = GET_MAX_HIT(i) * 3 / 4;
            dam = MIN(dam, maxdam);
            dam = MAX(0, dam);
            log("Creeping death strike doing %d dam", dam);
            damage(i, i, dam, af->type, 0, -1, 0, 0, 1);
          }
        affectv_remove(i, af);
      }
    }
  }
}

void mag_affectsv(int level, struct char_data *ch, struct char_data *victim,
                      int spellnum)
{
  struct affected_type af[MAX_SPELL_AFFECTS];
  bool accum_affect = FALSE, accum_duration = FALSE;
  const char *to_vict = NULL, *to_room = NULL;
  int i;


  if (victim == NULL || ch == NULL)
    return;

  for (i = 0; i < MAX_SPELL_AFFECTS; i++) {
    af[i].type = spellnum;
    af[i].bitvector = 0;
    af[i].modifier = 0;
    af[i].location = APPLY_NONE;
  }

  if (mag_newsaves(ch, victim, spellnum, level, GET_INT(ch))) {
    if (IS_SET(spell_info[spellnum].save_flags, MAGSAVE_PARTIAL | MAGSAVE_NONE)) {
      send_to_char(victim, "@g*save*@y You avoid any lasting affects.@n\r\n");
      return;
    } 
  }

  switch (spellnum) {
  case SPELL_PARALIZE:
    af[0].duration = level/2;
    af[0].bitvector = AFF_PARALYZE;
    accum_duration = FALSE;
    to_vict = "You feel your limbs freeze!";
    to_room = "$n suddenly freezes in place!";
    break;
  case ART_STUNNING_FIST:
    af[0].duration = 1;
    af[0].bitvector = AFF_STUNNED;
    accum_duration = FALSE;
    to_vict = "You are in a stunned daze!";
    to_room = "$n is stunned.";
    break;
  case ART_EMPTY_BODY:
    af[0].duration = GET_KI(ch) / 10;
    af[0].bitvector = AFF_ETHEREAL;
    accum_duration = FALSE;
    to_vict = "You switch to the ethereal plane.";
    to_room = "$n disappears.";
    break;
  case ART_QUIVERING_PALM:
    if (GET_LEVEL(ch) <= GET_LEVEL(victim)) {
      send_to_char(ch, "They are too high level for that.\r\n");
      return;
    }
    af[0].duration = MAX(6, 20 - level);
    af[0].bitvector = AFF_CDEATH;
    accum_duration = FALSE;
    to_vict = "You feel death closing in.";
    break;
  case SPELL_RESISTANCE:
    af[0].duration = 12;
    af[0].location = APPLY_ALLSAVES;
    af[0].modifier = 1;
    accum_duration = FALSE;
    to_vict = "You glow briefly with a silvery light.";
    to_room = "$n glows briefly with a silvery light.";
    break;
  case SPELL_DAZE:
    if (GET_HITDICE(victim) < 5)
      af[0].bitvector = AFF_NEXTNOACTION;
      accum_duration = FALSE;
      to_vict = "You are struck dumb by a flash of bright light!";
      to_room = "$n is struck dumb by a flash of bright light!";
    break;
  case SPELL_SUMMON_MONSTER_I:
  case SPELL_SUMMON_MONSTER_II:
  case SPELL_SUMMON_MONSTER_III:
  case SPELL_SUMMON_MONSTER_IV:
  case SPELL_SUMMON_MONSTER_V:
  case SPELL_SUMMON_MONSTER_VI:
  case SPELL_SUMMON_MONSTER_VII:
  case SPELL_SUMMON_MONSTER_VIII:
  case SPELL_SUMMON_MONSTER_IX:
    af[0].duration = level;
    af[0].bitvector = AFF_SUMMONED;
    accum_duration = FALSE;
    to_vict = "You are summoned to assist $N!";
    to_room = "$n appears, ready for action.";
    break;
  }

  /*
   * If this is a mob that has this affect set in its mob file, do not
   * perform the affect.  This prevents people from un-sancting mobs
   * by sancting them and waiting for it to fade, for example.
   */
  if (IS_NPC(victim) && !affected_by_spell(victim, spellnum))
    for (i = 0; i < MAX_SPELL_AFFECTS; i++)
      if (AFF_FLAGGED(victim, af[i].bitvector)) {
        send_to_char(ch, "%s", CONFIG_NOEFFECT);
        return;
      }
  /*
   * If the victim is already affected by this spell, and the spell does
   * not have an accumulative effect, then fail the spell.
   */
  if (affected_by_spell(victim,spellnum) && !(accum_duration||accum_affect)) {
    send_to_char(ch, "%s", CONFIG_NOEFFECT);
    return;
  }

  for (i = 0; i < MAX_SPELL_AFFECTS; i++)
    if (af[i].bitvector || (af[i].location != APPLY_NONE))
      affectv_join(victim, af+i, accum_duration, FALSE, accum_affect, FALSE);

  if (to_vict != NULL)
    act(to_vict, FALSE, victim, 0, ch, TO_CHAR);
  if (to_room != NULL)
    act(to_room, TRUE, victim, 0, ch, TO_ROOM);
}