crodo_mud/cnf/
crodo_mud/lib/
crodo_mud/lib/house/
crodo_mud/lib/misc/
crodo_mud/lib/plralias/F-J/
crodo_mud/lib/plralias/U-Z/
crodo_mud/lib/plrobjs/
crodo_mud/lib/plrvars/A-E/
crodo_mud/lib/plrvars/F-J/
crodo_mud/lib/plrvars/K-O/
crodo_mud/lib/plrvars/P-T/
crodo_mud/lib/plrvars/U-Z/
crodo_mud/lib/text/
crodo_mud/lib/text/help/
crodo_mud/lib/world/
crodo_mud/src/
/* ************************************************************************
*   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"

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

extern struct room_data *world;
extern struct obj_data *object_list;
extern struct char_data *character_list;
extern struct index_data *obj_index;
extern struct char_data *mob_proto;
extern struct descriptor_data *descriptor_list;
extern struct zone_data *zone_table;

extern int mini_mud;
extern int pk_allowed;
extern char *spell_wear_off_msg[];
extern char *spell_wearing_off_msg[];

byte saving_throws(int class_num, int type, int level); /* class.c */
void clearMemory(struct char_data * ch);
void weight_change_object(struct obj_data * obj, int weight);
void add_follower(struct char_data * ch, struct char_data * leader);
extern struct spell_info_type spell_info[];

/* 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, int savetype);
int mag_savingthrow(struct char_data * ch, int type, int modifier);
void affect_update(void);

/*
 * Saving throws are now in class.c as of bpl13.
 */


/*
 * Negative apply_saving_throw[] values make saving throws better!
 * Then, so do negative modifiers.  Though people may be used to
 * the reverse of that. It's due to the code modifying the target
 * saving throw instead of the random number of the character as
 * in some other systems.
 */
int mag_savingthrow(struct char_data * ch, int type, int modifier)
{
  /* NPCs use warrior tables according to some book */
  int class_sav = CLASS_WARRIOR;
  int save;

  if (!IS_NPC(ch))
    class_sav = GET_CLASS(ch);

  save = saving_throws(class_sav, type, GET_LEVEL(ch));
  save += GET_SAVE(ch, type);
  save += modifier;

  /* Throwing a 0 is always a failure. */
  if (MAX(1, save) < number(0, 99))
    return (TRUE);

  /* Oops, failed. Sorry. */
  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 = character_list; i; i = i->next)
    for (af = i->affected; af; af = next) {
      next = af->next;
      if (af->duration > 1)
	af->duration--;
      else if (af->duration == 1) { /* Spell about to wear off */
        af->duration--;
            if (spell_wearing_off_msg[af->type][0] != '!') {
              send_to_char(spell_wearing_off_msg[af->type], i);
              send_to_char("\r\n", i);
            }
    } else if (af->duration == -1)	/* No action */
	af->duration = -1;	/* GODs only! unlimited */
      else {
	if ((af->type > 0) && (af->type <= MAX_SPELLS))
	  if (!af->next || (af->next->type != af->type) ||
	      (af->next->duration > 0))
	    if (*spell_wear_off_msg[af->type]) {
	      send_to_char(spell_wear_off_msg[af->type], i);
	      send_to_char("\r\n", i);
	    }
	affect_remove(i, af);
      }
    }
}


/*
 *  mag_materials:
 *  Checks for up to 3 vnums (spell reagents) in the player's inventory.
 *
 * No spells implemented in Circle 3.0 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 (number(0, 2)) {
      case 0:
	send_to_char("A wart sprouts on your nose.\r\n", ch);
	break;
      case 1:
	send_to_char("Your hair falls out in clumps.\r\n", ch);
	break;
      case 2:
	send_to_char("A huge corn develops on your big toe.\r\n", ch);
	break;
      }
    }
    return (FALSE);
  }
  if (extract) {
    if (item0 < 0) {
      obj_from_char(obj0);
      extract_obj(obj0);
    }
    if (item1 < 0) {
      obj_from_char(obj1);
      extract_obj(obj1);
    }
    if (item2 < 0) {
      obj_from_char(obj2);
      extract_obj(obj2);
    }
  }
  if (verbose) {
    send_to_char("A puff of smoke rises from your pack.\r\n", ch);
    act("A puff of smoke rises from $n's pack.", TRUE, ch, NULL, NULL, TO_ROOM);
  }
  return (TRUE);
}




/*
 * 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 savetype)
{
  int dam = 0;

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

  switch (spellnum) {
    /* Mostly mages */
  case SPELL_MAGIC_MISSILE:
  case SPELL_CONE_OF_COLD:	/* chill touch also has an affect */
    if (IS_SORCERER(ch))
      dam = dice(1, 8) + level;
    else
      dam = dice(1, 6) + 1;
    break;
  case SPELL_FLAME_STRIKE:
    if (IS_SORCERER(ch))
      dam = dice(3, 8) + 3;
    else
      dam = dice(3, 6) + 3;
    break;
  case SPELL_ACID_ARROW:
    if (IS_SORCERER(ch))
      dam = dice(5, 8) + 5;
    else
      dam = dice(5, 6) + 5;
    break;
  case SPELL_LIGHTNING_BOLT:
    if (IS_SORCERER(ch))
      dam = dice(7, 8) + 7;
    else
      dam = dice(7, 6) + 7;
    break;
  case SPELL_CORRUPTION:
     dam = dice(level, 4);
    break;
  case SPELL_PRISMATIC_SPRAY:
    if (IS_SORCERER(ch))
      dam = dice(9, 8) + 9;
    else
      dam = dice(9, 6) + 9;
    break;
  case SPELL_FIREBALL:
    if (IS_SORCERER(ch))
      dam = dice(11, 8) + 11;
    else
      dam = dice(11, 6) + 11;
    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_LIFE_TAP:
    dam = dice(3, 8) + 3 + (level / 4);
  
    if (GET_MAX_HIT(ch) < GET_HIT(ch) + dam)
     GET_HIT(ch) = GET_MAX_HIT(ch);
    else
     GET_HIT(ch) += dam;

    break;

  case SPELL_MINOR_CORRUPTION:
      dam = dice(3, 8) + 3 + (level / 4);
    break;

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

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

  } /* switch(spellnum) */


  /* divide damage by two if victim makes his saving throw */
  if (mag_savingthrow(victim, savetype, 0))
    dam /= 2;

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


/*
 * 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, int savetype)
{
  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;
  }

  switch (spellnum) {

  case SPELL_CONE_OF_COLD:
    af[0].location = APPLY_STR;
    if (mag_savingthrow(victim, savetype, 0))
      af[0].duration = 1;
    else
      af[0].duration = 4;
    af[0].modifier = -1;
    accum_duration = TRUE;
    to_vict = "You feel your strength wither!";
    break;

  case SPELL_STONESKIN:
    af[0].location = APPLY_AC;
    af[0].modifier = -10;
    af[0].duration = level;
    accum_duration = FALSE;
    to_vict = "Your skin hardens like stone!";
    to_room = "$n's skin hardens like stone!";
    break;

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

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

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

  case SPELL_DUST_OF_DEAD:
    if (MOB_FLAGGED(victim, MOB_NOBLIND) || mag_savingthrow(victim, savetype, 0)) {
      send_to_char("You summon forth the dust of the dead, but it has no affect.\r\n", ch);
      return;
    }

    af[0].location = APPLY_HITROLL;
    af[0].modifier = -4;
    af[0].duration = level;
    af[0].bitvector = AFF_BLIND;

    af[1].location = APPLY_AC;
    af[1].modifier = 40;
    af[1].duration = level;
    af[1].bitvector = AFF_BLIND;

    to_room = "The dust of dead warriors is summoned forth blinding $n as it encrusts their eyes!";
    to_vict = "The dust of the dead is summoned forth and encrusts your eyes.  You have been blinded!";
    break;

  case SPELL_CURSE:
    if (mag_savingthrow(victim, savetype, 0)) {
      send_to_char(NOEFFECT, ch);
      return;
    }

    af[0].location = APPLY_HITROLL;
    af[0].duration = 1 + (GET_LEVEL(ch) / 2);
    af[0].modifier = -1;
    af[0].bitvector = AFF_CURSE;

    af[1].location = APPLY_DAMROLL;
    af[1].duration = 1 + (GET_LEVEL(ch) / 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_DETECT_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_HASTE:
    af[0].duration = 24;
    af[0].bitvector = AFF_HASTE;
    accum_duration = FALSE;
    to_vict = "Reality appears to slow down around you!";
    break;

  case SPELL_INFRAVISION:
    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 + (GET_LEVEL(ch) / 4);
    af[0].modifier = -40;
    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:
    if (mag_savingthrow(victim, savetype, 0)) {
      send_to_char(NOEFFECT, ch);
      return;
    }

    af[0].location = APPLY_STR;
    af[0].duration = GET_LEVEL(ch);
    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_BONE_SHIELD:
    af[0].duration = 2;
    af[0].bitvector = AFF_BONE_SHIELD;

    accum_duration = TRUE;
    to_vict = "The bones of the dead swirl about you like a storm!";
    to_room = "The bones of the dead fly about $n like a whirlwind!";
    break;

  case SPELL_SLEEP:
    if (MOB_FLAGGED(victim, MOB_NOSLEEP))
      return;
    if (mag_savingthrow(victim, savetype, 0))
      return;

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

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

  case SPELL_STOUT:
    if (GET_ADD(victim) == 100)
      return;

    af[0].location = APPLY_STR;
    af[0].duration = (GET_LEVEL(ch) / 2) + 4;
    af[0].modifier = 1 + (level > 18);
    af[1].location = APPLY_MOVE;
    af[0].duration = (GET_LEVEL(ch) / 2) + 4;
    af[1].modifier = 20;
    accum_duration = FALSE;
    accum_affect = FALSE;
    to_vict = "You feel the vitality of an ox surge through you!";
    to_room = "$n seems to grow muscles as you are watching!";   
    break;

  case SPELL_ENFEEBLEMENT:

    if (GET_STR(victim) < 4)
      return;

    af[0].location = APPLY_STR;
    af[0].duration = (GET_LEVEL(ch) / 2) + 4;
    af[0].modifier = -3;
    af[1].location = APPLY_MOVE;
    af[0].duration = (GET_LEVEL(ch) / 2) + 4;
    af[1].modifier = -25;
    accum_duration = FALSE;
    accum_affect = FALSE;
    to_vict = "You feel your vitality whither away!";
    to_room = "$n seems to whither away as you are watching!";
    break;

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

  case SPELL_ENRAGE:

   if (AFF_FLAGGED(victim, AFF_WARSONG)) {
    send_to_char("The song does not enrage you any further.\r\n",victim);
    return; }

    af[0].duration = 2 + (GET_LEVEL(ch) / 2);
    af[1].duration = 2 + (GET_LEVEL(ch) / 2);;
    af[0].bitvector = AFF_WARSONG;
    af[0].location = APPLY_HITROLL;
    af[1].location = APPLY_DAMROLL;
    af[0].modifier = 1;
    af[1].modifier = 1;
    accum_duration = FALSE;
    to_vict = "Your blood boils with &rrage!&n";
    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_FLY:
    af[0].duration = (GET_LEVEL(ch) / 2) + 12;
    af[0].bitvector = AFF_FLY;
    accum_duration = TRUE;
    to_vict = "You float up into the air!";
    to_room = "$n floats up into the air!";
    GET_POS(victim) = POS_FLYING;
    break;

  case SPELL_WATERLUNG:
    af[0].duration = 2;
    af[0].bitvector = AFF_WATERLUNG;
    accum_duration = TRUE;
    to_vict = "You feel your lungs change form.";
    break;

  case SPELL_AWARE:
   af[0].duration = 2;
   af[0].bitvector = AFF_AWARE;
   accum_duration = FALSE;
   break;

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

    af[0].duration = 8 + (GET_LEVEL(ch) / 4);
    af[0].bitvector = AFF_SHADOWS;
    accum_duration = TRUE;
    to_vict = "You summon a veil of shadows from the underworld.";
    to_room = "$n appears for an instant to be only a shadow.";
    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(NOEFFECT, ch);
	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(NOEFFECT, ch);
    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, int savetype)
{
  switch (spellnum) {
    case SPELL_GROUP_PURIFY:
    mag_points(level, ch, tch, SPELL_PURIFY_FLESH, savetype);
    break;
  case SPELL_GROUP_STONESKIN:
    mag_affects(level, ch, tch, SPELL_STONESKIN, savetype);
    break;
  case SPELL_GROUP_RECALL:
    spell_recall(level, ch, tch, 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, int savetype)
{
  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 (tch->in_room != ch->in_room)
      continue;
    if (!AFF_FLAGGED(tch, AFF_GROUP))
      continue;
    if (ch == tch)
      continue;
    perform_mag_groups(level, ch, tch, spellnum, savetype);
  }

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


/*
 * mass spells affect every creature in the room except the caster.
 *
 * No spells of this class currently implemented as of Circle 3.0.
 */

void mag_masses(int level, struct char_data * ch, int spellnum, int savetype)
{
  struct char_data *tch, *tch_next;

  for (tch = world[ch->in_room].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, int savetype)
{
  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;

  case SPELL_ENRAGE_STORM:
   /* must be outdoors */
    if (!OUTSIDE(ch)) {
     send_to_char("Sadly, it's not raining here indoors.\r\n", ch);
     return; }

   /* Must be a Storm */
    if (weather_info.sky < 2) {
      send_to_char("It must be at least raining in order to enrage a storm.\r\n", ch);
      return; }

    to_char = "You gesture and lightning rains down from the storm clouds!";
    to_room ="$n gracefully gestures and the storm above violently ejects lighting bolts!";
    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[ch->in_room].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 (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_IMMORT)
      continue;
    if (!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, 1);
  }
}


/*
 *  Every spell which summons/gates/conjours a mob comes through here.
 *
 *  None of these spells are currently implemented in Circle 3.0; these
 *  were taken as examples from the JediMUD code.  Summons can be used
 *  for spells like clone, ariel servant, etc.
 *
 * 10/15/97 (gg) - Implemented Animate Dead and Clone.
 */

/*
 * These use act(), don't put the \r\n.
 */
const char *mag_summon_msgs[] = {
  "\r\n",
  "$n makes a strange magical gesture; you feel a strong breeze!",
  "$n stares intently as you watch a corpse becomes animated!",
  "$N appears from a cloud of thick blue smoke!",
  "$N appears from a cloud of thick green smoke!",
  "$N appears from a cloud of thick red smoke!",
  "$N disappears in a thick black cloud!"
  "As $n makes a strange magical gesture, you feel a strong breeze.",
  "As $n makes a strange magical gesture, you feel a searing heat.",
  "As $n makes a strange magical gesture, you feel a sudden chill.",
  "As $n makes a strange magical gesture, you feel the dust swirl.",
  "$n appears to magically divide!",
  "$n summons a soul to inhabit the skeleton of a corpse!",
  "$n stares intently at a corpse and summons forth a skeletal mage!"
};

/*
 * Keep the \r\n because these use send_to_char.
 */
const char *mag_summon_fail_msgs[] = {
  "\r\n",
  "There are no such creatures.\r\n",
  "Uh oh...\r\n",
  "Oh dear.\r\n",
  "Oh shit!\r\n",
  "The elements resist!\r\n",
  "You failed.\r\n",
  "There is no corpse!\r\n",
  "You do not have the wisdom necessary for any more minions!\r\n"
};

/* These mobiles do not exist. */
#define MOB_MONSUM_I		130
#define MOB_MONSUM_II		140
#define MOB_MONSUM_III		150
#define MOB_GATE_I		160
#define MOB_GATE_II		170
#define MOB_GATE_III		180

/* Defined mobiles. */
#define MOB_CLONE		10
#define MOB_ZOMBIE              11
#define MOB_SKELETON            13
#define MOB_SKELETAL_MAGE       13
#define MOB_AIR_ELEMENTAL	19

void mag_summons(int level, struct char_data * ch, struct obj_data * obj,
		      int spellnum, int savetype)
{
  struct char_data *mob = NULL;
  struct obj_data *tobj, *next_obj;
  int pfail = 0, msg = 0, fmsg = 0, num = 1,  i;
  mob_vnum mob_num, corpse_vnum;
  mob_rnum corpse_rnum;
  int handle_corpse = FALSE;

  if (ch == NULL)
    return;

    /* Charmed Out Check */
    if (get_num_of_charms(ch) >= (GET_WIS(ch) * 18) / GET_CHA(ch) ) {
      act(mag_summon_fail_msgs[8], FALSE, ch, 0, 0, TO_CHAR);
      return;
    }

  switch (spellnum) {
  case SPELL_MIRROR_IMAGE:
    msg = 11;
    fmsg = number(2, 6);	/* Random fail message. */
    mob_num = MOB_CLONE;
    pfail = 10;	/* 10% failure, should be based on something later. */
    handle_corpse = FALSE;
    break;
  case SPELL_AERIAL_SERVANT:
    msg = 7;
    fmsg = number(2, 6);        /* Random fail message. */
    mob_num = MOB_AIR_ELEMENTAL;
    pfail = 10; /* 10% failure, should be based on something later. */
    handle_corpse = FALSE;
    break;

  case SPELL_RAISE_UNDEAD:
    if (obj == NULL || !IS_CORPSE(obj)) {
      act(mag_summon_fail_msgs[7], FALSE, ch, 0, 0, TO_CHAR);
      return;
    }

    msg = 2;
    fmsg = number(2, 6);	/* Random fail message. */
    mob_num = MOB_ZOMBIE;
    pfail = 10;	/* 10% failure, should vary in the future. */
    handle_corpse = TRUE;
    break;

  case SPELL_RAISE_SKELETON:

    if (obj == NULL || !IS_CORPSE(obj)) {
      act(mag_summon_fail_msgs[7], FALSE, ch, 0, 0, TO_CHAR);
      return;
    }

    msg = 12;
    fmsg = number(2, 6);        /* Random fail message. */
    mob_num = MOB_SKELETON; 
    pfail = 10; /* 10% failure, should vary in the future. */
    handle_corpse = TRUE;
    break;

  case SPELL_RAISE_SKELETAL_MAGE:
    if (obj == NULL || !IS_CORPSE(obj)) {
      act(mag_summon_fail_msgs[7], FALSE, ch, 0, 0, TO_CHAR);
      return;
    }
      
    msg = 12;
    fmsg = number(2, 6);        /* Random fail message. */
    mob_num = MOB_SKELETAL_MAGE;
    pfail = 10; /* 10% failure, should vary in the future. */
    handle_corpse = TRUE;
    break;

  default:
    return;
  }

  if (AFF_FLAGGED(ch, AFF_CHARM)) {
    send_to_char("You are too giddy to have any followers!\r\n", ch);
    return;
  }

  if (number(0, 101) < pfail) {
    send_to_char(mag_summon_fail_msgs[fmsg], ch);
    return;
  }

 /* Level check */
  if (handle_corpse)
    if (GET_LEVEL(ch) < mob_proto[real_mobile(GET_OBJ_VAL(obj, 4))].player.level) {
      act(mag_summon_fail_msgs[5], FALSE, ch, 0, 0, TO_CHAR);
      return;
    }

  for (i = 0; i < num; i++) {
    if (!(mob = read_mobile(mob_num, VIRTUAL))) {
      send_to_char("You don't quite remember how to make that creature.\r\n", ch);
      return;
    }

    char_to_room(mob, ch->in_room);
    IS_CARRYING_W(mob) = 0;
    IS_CARRYING_N(mob) = 0;
    SET_BIT(AFF_FLAGS(mob), AFF_CHARM);

   switch (spellnum) {

   case SPELL_MIRROR_IMAGE:
      sprintf(buf, "mirror image %s", GET_NAME(ch));
      mob->player.name = str_dup(buf);
      sprintf(buf, "the mirror image of %s", str_dup(GET_NAME(ch)));
      mob->player.short_descr = str_dup(buf);
      sprintf(buf, "A mirror image of %s is standing here.\r\n", GET_NAME(ch));
      mob->player.long_descr = str_dup(buf);
      SET_BIT(MOB_FLAGS(mob), MOB_NOCORPSE);
      SET_BIT(MOB_FLAGS(mob), MOB_SENTINEL);
   break;
   case SPELL_RAISE_UNDEAD:
      /* Find original monster/person.  Create accordingly */
      corpse_vnum = GET_OBJ_VAL(obj, 4);
      corpse_rnum = real_mobile(corpse_vnum);
      mob->player.name = str_dup("undead corpse");
      mob->points.armor = mob_proto[corpse_rnum].points.armor;
      mob->player.level = MAX(mob_proto[corpse_rnum].player.level - 3, 1);
      mob->points.max_hit = dice(mob->player.level, 4);
      mob->points.hit = mob->points.max_hit;
      mob->points.hitroll = MAX(mob_proto[corpse_rnum].points.hitroll / 4, 0);
      mob->points.damroll = MAX(mob_proto[corpse_rnum].points.damroll / 4, 0);
      MOB_FLAGS(mob) = mob_proto[corpse_rnum].char_specials.saved.act;
      SET_BIT(MOB_FLAGS(mob), MOB_NOCORPSE);
      SET_BIT(MOB_FLAGS(mob), MOB_SENTINEL);
      mob->mob_specials.attack_type = mob_proto[i].mob_specials.attack_type;
      sprintf(buf, "the undead corpse of %s", mob_proto[corpse_rnum].player.short_descr);
      mob->player.short_descr = str_dup(buf); 
      sprintf(buf, "The undead corpse of %s is standing here.\r\n", mob_proto[corpse_rnum].player.short_descr);
      mob->player.long_descr = str_dup(buf);
    break;
   case SPELL_RAISE_SKELETON:
      /* Find original monster/person.  Create accordingly */
      corpse_vnum = GET_OBJ_VAL(obj, 4);
      corpse_rnum = real_mobile(corpse_vnum);
      mob->player.name = str_dup("undead skeleton");
      mob->points.armor = mob_proto[corpse_rnum].points.armor;
      mob->player.level = MAX(mob_proto[corpse_rnum].player.level - 3, 1);
      mob->points.max_hit = dice(mob->player.level, 6);             
      mob->points.hit = mob->points.max_hit;
      mob->points.hitroll = MAX(mob_proto[corpse_rnum].points.hitroll / 2, 0);
      mob->points.damroll = MAX(mob_proto[corpse_rnum].points.damroll / 2, 0);
      MOB_FLAGS(mob) = mob_proto[corpse_rnum].char_specials.saved.act;
      SET_BIT(MOB_FLAGS(mob), MOB_NOCORPSE);
      REMOVE_BIT(MOB_FLAGS(mob), MOB_SENTINEL);
      sprintf(buf, "the undead skeleton of %s", mob_proto[corpse_rnum].player.short_descr);
      mob->player.short_descr = str_dup(buf);
      sprintf(buf, "The undead skeleton of %s is standing here.\r\n", mob_proto[corpse_rnum].player.short_descr);
      mob->player.long_descr = str_dup(buf);
    break;
   case SPELL_RAISE_SKELETAL_MAGE:
      corpse_vnum = GET_OBJ_VAL(obj, 4);
      corpse_rnum = real_mobile(corpse_vnum);
      mob->player.level = MIN(mob_proto[corpse_rnum].player.level - 1, 1);
      mob->points.max_hit = dice(mob->player.level, 8);
      REMOVE_BIT(MOB_FLAGS(mob), MOB_SENTINEL);
    break;
   default:
    break;
                     }

   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);
     }

  /* Send out success messages */
    act(mag_summon_msgs[msg], FALSE, ch, 0, mob, TO_ROOM);
 
switch (spellnum) {
case SPELL_RAISE_UNDEAD:
case SPELL_RAISE_SKELETON:
case SPELL_RAISE_SKELETAL_MAGE:
  send_to_char("You summon forth a soul to inhabit the corpse!\r\n", ch);
 break;
case SPELL_MIRROR_IMAGE:
  send_to_char("You magically divide yourself!\r\n", ch);
break;
case SPELL_AERIAL_SERVANT:
  send_to_char("You successfully gate an air elemntal and bind it to your will.\r\n", ch);
break;
default:
 break;
}

   load_mtrigger(mob);
    add_follower(mob, ch);
}

}


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

  if (victim == NULL)
    return;

  switch (spellnum) {
  case SPELL_CURE_LIGHT:
    hit = dice(1, 8) + 1 + (level / 4);
    send_to_char("You feel better.\r\n", victim);
    break;
  case SPELL_CURE_CRITIC:
    hit = dice(3, 8) + 3 + (level / 4);
    send_to_char("You feel a lot better!\r\n", victim);
    break;
  case SPELL_PURIFY_FLESH:
    hit = dice(level, 4);
    send_to_char("You feel life restored as the corruption of your body is purified!\r\n", victim);
    break;
  case SPELL_MINOR_PURIFY:
    hit = dice(3, 8) + 3 + (level / 4);
    send_to_char("You body is made less corrupted, some of your wounds heal!\r\n", victim);
    break;
  }
  GET_HIT(victim) = MIN(GET_MAX_HIT(victim), GET_HIT(victim) + hit);
  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 type)
{
  int spell = 0;
  const char *to_vict = NULL, *to_room = NULL;

  if (victim == NULL)
    return;

  switch (spellnum) {
  case SPELL_UNHOLY_LIGHT:
  case SPELL_PURIFY_FLESH:
    spell = SPELL_DUST_OF_DEAD;
    to_vict = "The dust of the dead is purged from your eyes.  Your vision returns!";
    to_room = "There's a momentary gleam in $n's eyes as dust falls from them.";
    break;
  case SPELL_REMOVE_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_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 (spellnum != SPELL_PURIFY_FLESH)		/* 'cure blindness' message. */
      send_to_char(NOEFFECT, ch);
    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, int savetype)
{
  const char *to_char = NULL, *to_room = NULL;

  if (obj == NULL)
    return;

  switch (spellnum) {
    case SPELL_BLESS:
      if (!IS_OBJ_STAT(obj, ITEM_BLESS) &&
	  (GET_OBJ_WEIGHT(obj) <= 5 * GET_LEVEL(ch))) {
	SET_BIT(GET_OBJ_EXTRA(obj), ITEM_BLESS);
	to_char = "$p glows briefly.";
      }
      break;
    case SPELL_CURSE:
      if (!IS_OBJ_STAT(obj, ITEM_NODROP)) {
	SET_BIT(GET_OBJ_EXTRA(obj), ITEM_NODROP);
	if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
	  GET_OBJ_VAL(obj, 2)--;
	to_char = "$p briefly glows red.";
      }
      break;
    case SPELL_INVISIBLE:
      if (!IS_OBJ_STAT(obj, ITEM_NOINVIS | ITEM_INVISIBLE)) {
        SET_BIT(obj->obj_flags.extra_flags, 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, 3)) {
      GET_OBJ_VAL(obj, 3) = 1;
      to_char = "$p steams briefly.";
      }
      break;
    case SPELL_REMOVE_CURSE:
      if (IS_OBJ_STAT(obj, ITEM_NODROP)) {
        REMOVE_BIT(obj->obj_flags.extra_flags, ITEM_NODROP);
        if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
          GET_OBJ_VAL(obj, 2)++;
        to_char = "$p briefly glows blue.";
      }
      break;
    case SPELL_REMOVE_POISON:
      if (((GET_OBJ_TYPE(obj) == ITEM_DRINKCON) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) ||
         (GET_OBJ_TYPE(obj) == ITEM_FOOD)) && GET_OBJ_VAL(obj, 3)) {
        GET_OBJ_VAL(obj, 3) = 0;
        to_char = "$p steams briefly.";
      }
      break;
  }

  if (to_char == NULL)
    send_to_char(NOEFFECT, ch);
  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("Spell unimplemented, it would seem.\r\n", ch);
    return;
  }

  if (!(tobj = read_object(z, VIRTUAL))) {
    send_to_char("I seem to have goofed.\r\n", ch);
    log("SYSERR: spell_creations, spell %d, obj %d: obj not found",
	    spellnum, z);
    return;
  }
  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);
}