AwakeMUD-0.51Beta/area/
AwakeMUD-0.51Beta/doc/
AwakeMUD-0.51Beta/lib/
AwakeMUD-0.51Beta/lib/etc/
AwakeMUD-0.51Beta/lib/fixer_data/
AwakeMUD-0.51Beta/lib/misc/
AwakeMUD-0.51Beta/lib/plrobjs/
AwakeMUD-0.51Beta/lib/plrobjs/A-E/
AwakeMUD-0.51Beta/lib/plrobjs/K-O/
AwakeMUD-0.51Beta/lib/plrobjs/U-Z/
AwakeMUD-0.51Beta/lib/plrspells/A-E/
AwakeMUD-0.51Beta/lib/plrtext/A-E/
AwakeMUD-0.51Beta/lib/world/
AwakeMUD-0.51Beta/lib/world/mob/
AwakeMUD-0.51Beta/lib/world/obj/
AwakeMUD-0.51Beta/lib/world/qst/
AwakeMUD-0.51Beta/lib/world/shp/
AwakeMUD-0.51Beta/lib/world/wld/
AwakeMUD-0.51Beta/lib/world/zon/
/* *******************************************************************
* file: newmagic.cc                                                  *
* authors: Christopher J. Dickey and Andrew Hynek                    *
* purpose: this defines all the new magic routines for the mud       *
* (c)1997-2000 Christopher J. Dickey, Andrew Hynek, and Nick         *
* Robertson, (c)2001 The AwakeMUD Consortium                         *
******************************************************************* */


#define _newmagic_cc_

#include <fstream.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include "structs.h"
#include "awake.h"
#include "db.h"
#include "comm.h"
#include "interpreter.h"
#include "utils.h"
#include "handler.h"
#include "newmagic.h"
#include "spells.h"
#include "mag_create.h"
#include "shop.h"

// extern vars
extern struct time_info_data time_info;
extern struct room_data *world;
extern struct char_data *character_list;
extern struct index_data *obj_index;
extern struct zone_data *zone_table;

extern int material_ratings[];
extern int pk_allowed;
extern int top_of_world;
extern const char *material_names[];
extern const char *item_types[];
extern const char *wear_bits[];
extern const char *affected_bits[];
extern const char *apply_types[];
extern const char *wound_name[];
extern const char *spells[];
extern const char *drinks[];
extern const char *gear_name[];
extern const char *patch_names[];
extern const char *attack_types[];
extern const char *program_types[];
extern const char *spell_target[];
extern const char *wound_arr[];
extern const char *elements[];
extern const char *foci_types[];
extern const char *shape_forms[];
extern const char *spell_wear_off_msg[];
extern const char *spirits[];
extern const char *spell_categories[];
extern const char *lookdirs[];
extern int convert_look[];

// extern funcs
extern void resist_drain(struct char_data *ch, int power, spell_t *spell, int wound);
extern void damage_obj(struct char_data *, struct obj_data *, int, int);
extern void look_at_room(struct char_data * ch, int ignore_brief);
extern void add_follower(struct char_data * ch, struct char_data * leader);
extern void ranged_response(struct char_data *ch, struct char_data *vict);
extern void die(struct char_data *);
extern bool in_group(char_t *, char_t *);
extern int modify_target(char_t *);
extern int ok_damage_shopkeeper(struct char_data * ch, struct char_data * victim);
extern int find_sight(struct char_data * ch);
extern char *get_power(int);
extern char *get_attrib(int);
extern void damage_door(struct char_data *ch, int room, int dir, int power, int type);

#define FORCE_PENALTY(spell) (0-((spell)->damage <= LIGHT ? 100 : 8 - 2*((spell)->damage)))

// drain is compacted into 1 int...i % 10 = drain_level, i - (i % 10) = drain_add
spell_a grimoire[] =
{  // physical, category, target, drain, damage_level
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0},
   { TRUE, DETECT, TOUCH,  12, 0 },         // analyze device
   { TRUE, MANIPU, RANGED, 12, 0 },         // anti_ballistic barrier
   {FALSE, MANIPU, RANGED, 11, 0 },         // 5  - anti_mana barrier
   { TRUE, HEALTH, TOUCH,   0, 0 },         // antidote
   { TRUE, MANIPU, RANGED, 22, 0 },         // armor
   { TRUE, ILLUSI, RANGED, 22, 0 },         // chaos
   { TRUE, ILLUSI, AREA,   23, 0 },         // chaotic_world
   {FALSE, DETECT, RANGED,  2, 0 },         // 10 - clairvoyance
   { TRUE, MANIPU, RANGED, 12, MODERATE},   // clout
   { TRUE, DETECT, RANGED, 13, 0 },         // combat_sense
   {FALSE, ILLUSI, RANGED,  3, 0 },         // confusion
   { TRUE, HEALTH, TOUCH,   0, 0 },         // cure disease
   { TRUE, COMBAT, TOUCH,  13, DEADLY},     // 15 - death_touch
   {FALSE, DETECT, RANGED,  1, 0 },         // detect_align
   {FALSE, DETECT, RANGED,  1, 0 },         // detect_invis
   {FALSE, DETECT, RANGED,  1, 0 },         // detect_magic
   {FALSE, HEALTH, TOUCH,   0, 0 },         // heal
   { TRUE, COMBAT, AREA,   64, DEADLY},     // hellblast
   { TRUE, ILLUSI, TOUCH,  12, 0 },         // 20 - improved_invisibility
   {FALSE, MANIPU, RANGED, 23, 0 },         // influence
   {FALSE, ILLUSI, TOUCH,   2, 0 },         // invisibility
   {FALSE, COMBAT, AREA,    3, MODERATE},   // mana_ball
   {FALSE, COMBAT, AREA,    4, SERIOUS},    // mana_blast
   {FALSE, COMBAT, RANGED,  3, SERIOUS},    // 25 - mana_bolt
   {FALSE, COMBAT, AREA,    2, LIGHT},      // mana_cloud
   {FALSE, COMBAT, RANGED,  1, LIGHT},      // mana_dart
   {FALSE, COMBAT, RANGED,  2, MODERATE},   // mana_missile
   {FALSE, ILLUSI, RANGED, 12, 0 },         // overstimulation
   { TRUE, MANIPU, RANGED, 64, 0 },         // 30 - petrify
   { TRUE, COMBAT, AREA,   13, MODERATE},   // power_ball
   { TRUE, COMBAT, AREA,   14, SERIOUS},    // power_blast
   { TRUE, COMBAT, RANGED, 13, SERIOUS},    // power_bolt
   { TRUE, COMBAT, AREA,   12, LIGHT},      // power_cloud
   { TRUE, COMBAT, RANGED, 11, LIGHT},      // 35 - power_dart
   { TRUE, COMBAT, RANGED, 12, MODERATE},   // power_missile
   { TRUE, COMBAT, RANGED, 13, SERIOUS},    // ram
   { TRUE, COMBAT, TOUCH,  12, SERIOUS},    // ram_touch
   {FALSE, HEALTH, TOUCH,   0, 0 },         // resist_pain
   { TRUE, MANIPU, CASTER, 34, 0 },         // 40 - shape_change
   {FALSE, HEALTH, RANGED,  1, 0 },         // stabilize
   {FALSE, COMBAT, AREA,   -7, MODERATE},   // stun_ball
   {FALSE, COMBAT, AREA,   -6, SERIOUS},    // stun_blast
   {FALSE, COMBAT, RANGED, -7, SERIOUS},    // stun_bolt
   {FALSE, COMBAT, AREA,   -8, LIGHT},      // 45 - stun_cloud
   {FALSE, COMBAT, RANGED, -8, MODERATE},   // stun_missile
   {FALSE, COMBAT, RANGED, -7, DEADLY},     // stun_touch
   { TRUE, MANIPU, AREA,   84, DEADLY},     // toxic_wave
   { TRUE, COMBAT, RANGED, 14, DEADLY},     // power_shaft
   { TRUE, COMBAT, AREA,   34, DEADLY},     // 50 - power_burst
   {FALSE, COMBAT, RANGED,  4, DEADLY},     // mana_shaft
   {FALSE, COMBAT, AREA,   24, DEADLY},     // mana_burst
   {FALSE, COMBAT, RANGED, -6, DEADLY},     // stun_shaft
   {FALSE, COMBAT, AREA,   14, DEADLY},     // stun_burst
   {FALSE, COMBAT, AREA,   -9, LIGHT},      // 55 - stun_dart
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 60
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 65
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 70
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 75
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 80
   {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, {0,0,0,0,0}, // 85
   {0,0,0,0,0},
   { TRUE, DETECT, TOUCH,   2, 0 },         // analyze magic
   { TRUE, DETECT, TOUCH,   2, 0 },         // analyze person
   { TRUE, HEALTH, TOUCH,  10, 0 },         // decrease attribute
   { TRUE, COMBAT, AREA,   34, SERIOUS},    // 90 - <element>ball
   { TRUE, COMBAT, RANGED, 14, SERIOUS},    // <element>bolt
   { TRUE, COMBAT, AREA,   13, LIGHT},      // <element>_cloud
   { TRUE, COMBAT, RANGED, 12, LIGHT},      // <element>_dart
   { TRUE, COMBAT, RANGED, 13, MODERATE},   // <element>_missile
   { TRUE, HEALTH, TOUCH,  10, 0 },         // 95 - increase attribute
   { TRUE, HEALTH, TOUCH,   0, 0 },         // increase reflexes
   { TRUE, MANIPU, CASTER, 22, 0 },         // light
   { TRUE, HEALTH, TOUCH,   2, 0 },         // poison
   {FALSE, MANIPU, CASTER, 33, 0 },         // teleport
};

/* affect_update: called from comm.c (causes spells to wear off) */
void affect_update(void)
{
  static struct affected_type *af;
  static struct char_data *i, *j;
  extern const char *spell_wear_off_msg[];

  // loop through all the characters in the mud--eek
  for (i = character_list; i; i = i->next) {
    // reset their # of sustained spells
    GET_SUSTAINED(i) = 0;
    // then loop through the affected structs
    for (af = i->affected; af; af = af->next) {
      // and here we add up the # of sustained spells
      if (af->caster)
        GET_SUSTAINED(i)++;
      else if (!mag_can_see(i, af->sustained_by) && af->type != SPELL_HEAL && af->type != SPELL_ANTIDOTE &&
               af->type != SPELL_STABILIZE && af->type != SPELL_CURE_DISEASE) {
        send_to_char(i, "%s\r\n", spell_wear_off_msg[af->type]);
        affect_remove(i, af, 1);
      }
    }
  }
}

void check_spell_drain( struct char_data *ch, spell_t *spell )
{
  if (DRAIN_LEVEL(spell->drain) <= 4)
    return;

  sprintf(buf2, "Correcting drain code for %s from %d to %d",
	  spell->name, spell->drain, grimoire[spell->type].drain);
  mudlog(buf2, ch, LOG_SYSLOG, TRUE);
  spell->drain = grimoire[spell->type].drain;
}

int mag_can_see(struct char_data *ch, int id)
{
  int sight, room, nextroom, dir, distance;
  struct char_data *i, *vict, *tch = NULL;
  bool found = FALSE;

  for (i = character_list; i && !tch; i = i->next)
    if ((IS_NPC(i) ? -i->nr : GET_IDNUM(i)) == id)
      tch = i;

  if (!tch)
    return(FALSE);

  sight = find_sight(tch);

  for (vict = world[tch->in_room].people; vict && !found; vict = vict->next_in_room)
    if (vict == ch)
      found = TRUE;

  for (dir = 0; dir < (NUM_OF_DIRS - 1) && !found; dir++) {
    room = tch->in_room;
    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else nextroom = NOWHERE;
    for (distance = 1; ((nextroom != NOWHERE) && !found && (distance <= sight)); distance++) {
      for (vict = world[nextroom].people; vict; vict = vict->next_in_room)
        if (vict == ch)
          found = TRUE;
      room = nextroom;
      if (CAN_GO2(room, dir))
        nextroom = EXIT2(room, dir)->to_room;
      else nextroom = NOWHERE;
    }
  }
  return found;
}

int spell_resist(struct char_data *ch)
{
  struct affected_type *af;
  int resist = (int)(GET_MAGIC(ch) / 4);

  for (af = ch->affected; af; af = af->next)
    if (af->type == SPELL_ANTI_SPELL && af->modifier > 0)
      resist += af->modifier;

  return resist;
}

int foci_bonus(struct char_data *ch, struct spell_data *spell,
	       int force, bool fCast)
{
  int i;
  int bonus=0;
  struct obj_data *obj;

  for (i = 0; i < (NUM_WEARS - 1); i++)
    {
      if ((obj = GET_EQ(ch, i))
	  && GET_OBJ_TYPE(obj) == ITEM_FOCUS
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) == 1
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch))
	{
	  switch (GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE))
	    {
	    case FOCI_SPELL:
	      if (spell->type == GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY))
		bonus += GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
	      break;
	    case FOCI_SPELL_CAT:
	      if (spell->category == GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY))
		bonus += GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
	      break;
	    }
	}
    }
/*  for (obj = ch->carrying; obj; obj = obj->next_content)
    {
      if (GET_OBJ_TYPE(obj) == ITEM_FOCUS
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) == 1
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch))
	{
	  switch (GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE))
	    {
	    case FOCI_SPELL:
	      if (spell->type == GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY))
		bonus += GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
	      break;
	    case FOCI_SPELL_CAT:
	      if (spell->category == GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY))
		bonus += GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
	      break;
	    }
	}
    }
*/
  if ( !fCast )
    bonus += 1;
  return bonus/2;
}

int magic_pool_bonus(struct char_data *ch, struct spell_data *spell,
		     int force, bool fCast)
{
  int bonus;
  bonus = GET_MAGIC(ch);
#if 0
  if ( fCast )
    {
      if ( bonus > GET_MAG(ch) )
	bonus = GET_MAG(ch);
    }
  else
    {
      bonus -= GET_MAG(ch);
      if (bonus < 0)
	bonus = 0;
    }
#else
  bonus = (bonus + (fCast ? 1 : 0)) / 2;
#endif
  return bonus;
}

int totem_bonus(struct char_data *ch, struct spell_data *spell)
  {
  int bonus = 0;
  if (GET_TRADITION(ch) == TRAD_SHAMANIC) {
    switch (spell->category) {
      case SPELL_CATEGORY_COMBAT:
        switch (GET_TOTEM(ch)) {
          case TOTEM_OWL:
            if (time_info.hours > 6 && time_info.hours < 19)
              break;
          case TOTEM_GATOR: case TOTEM_LION: case TOTEM_SHARK: case TOTEM_WOLF:
            bonus += 2;
            break;
          case TOTEM_SNAKE:
            if (!FIGHTING(ch))
              break;
          case TOTEM_RAVEN: case TOTEM_RAT: case TOTEM_RACCOON:
            bonus -= 1;
            break;
          default:
            break;
        }
        break;
      case SPELL_CATEGORY_DETECTION:
        switch (GET_TOTEM(ch)) {
          case TOTEM_SNAKE:
            if (FIGHTING(ch))
              bonus -= 1;
            else bonus += 2;
            break;
          case TOTEM_OWL:
            if (time_info.hours > 6 && time_info.hours < 19)
              break;
          case TOTEM_DOG:   case TOTEM_EAGLE: case TOTEM_GATOR: case TOTEM_RAT:
          case TOTEM_SHARK: case TOTEM_WOLF:
            bonus += 2;
            break;
          default:
            break;
        }
        break;
      case SPELL_CATEGORY_HEALTH:
        switch (GET_TOTEM(ch)) {
          case TOTEM_SNAKE:
            if (FIGHTING(ch))
              bonus -= 1;
            else bonus += 2;
            break;
          case TOTEM_OWL:
            if (time_info.hours > 6 && time_info.hours < 19)
              break;
          case TOTEM_BEAR:
            bonus += 2;
            break;
          case TOTEM_LION:
            bonus -= 1;
            break;
          default:
            break;
        }
        break;
      case SPELL_CATEGORY_ILLUSION:
        switch (GET_TOTEM(ch)) {
          case TOTEM_SNAKE:
            if (FIGHTING(ch))
              bonus -= 1;
            else bonus += 2;
            break;
          case TOTEM_OWL:
            if (time_info.hours > 6 && time_info.hours < 19)
              break;
          case TOTEM_CAT: case TOTEM_RAT:
            bonus += 2;
            break;
          case TOTEM_GATOR:
            bonus -= 1;
            break;
          default:
            break;
        }
        break;
      case SPELL_CATEGORY_MANIPULATION:
        switch (GET_TOTEM(ch)) {
          case TOTEM_OWL:
            if (time_info.hours > 6 && time_info.hours < 19)
              break;
          case TOTEM_RACCOON: case TOTEM_RAVEN:
            bonus += 2;
            break;
          case TOTEM_SNAKE:
            if (!FIGHTING(ch))
              break;
            bonus -= 1;
            break;
          default:
            break;
        }
        break;
      default:
	break;
    }
  }

  return bonus;
}



void resist_drain(struct char_data *ch, int force, spell_t *spell, int wound)
{
  int successes, drain;
  int old_physical, old_mental;
  int staged_wound;
  int wil_dice, foci_dice, pool_dice, tnum;
  bool is_physical = FALSE;

  if (!IS_NPC(ch) && access_level(ch, LVL_PRESIDENT))
    return;

  if (force > (GET_MAG(ch) / 100))
    is_physical = TRUE;

  wil_dice = GET_WIL(ch);
  foci_dice = foci_bonus(ch, spell, force, FALSE);
  pool_dice = magic_pool_bonus(ch, spell, force, FALSE);
  tnum = MAX(2, (force >> 1) + DRAIN_POWER(spell->drain));
 
  successes = success_test(wil_dice + foci_dice + pool_dice, tnum);

  staged_wound = dec_staging(successes, wound);
  drain = convert_damage(staged_wound);

  old_physical = GET_PHYSICAL(ch);
  old_mental = GET_MENTAL(ch);

  if (is_physical)
    {
      GET_PHYSICAL(ch) -= drain * 100;
    }
  else
    {
      GET_MENTAL(ch) -= drain * 100;
      if (GET_MENTAL(ch) < 0)
	{
	  GET_PHYSICAL(ch) += GET_MENTAL(ch);
	  GET_MENTAL(ch) = 0;
	}
    }

  if (1)
    {
      char rbuf[MAX_STRING_LENGTH];
      sprintf( rbuf,
	       "Drain: F:%d/%d, Mag:%d, WFP/T %d+%d+%d/%d. %d(%d)->%d, D: %d%c. %d->%d/%d->%d",
	       force, spell->force,
	       GET_MAG(ch), wil_dice, foci_dice, pool_dice, tnum, wound,
	       successes, staged_wound, drain,
	       is_physical ? 'P' : 'M',
	       old_physical, GET_PHYSICAL(ch), old_mental, GET_MENTAL(ch));
      act( rbuf, 1, ch, NULL, NULL, TO_ROLLS );
    }

  update_pos(ch);
  if ((GET_POS(ch) <= POS_STUNNED) && (GET_POS(ch) > POS_DEAD)) {
    if (FIGHTING(ch))
       stop_fighting(ch);
    send_to_char("You are unable to resist the drain from using magic and fall unconscious!\r\n", ch);
    act("$n collapses unconscious from the use of magic!", FALSE, ch, 0, 0, TO_ROOM);
  } else {
    if (GET_POS(ch) == POS_DEAD) {
      if (FIGHTING(ch))
        stop_fighting(ch);
      send_to_char("The energy from the spell overloads your systems, killing you...\r\n", ch);
      act("$n collapses DEAD from an overload of magic!", FALSE, ch, 0, 0, TO_ROOM);
      die(ch);
    }
  }
}

void sustain_spell(int force, struct char_data *ch, struct char_data *victim, spell_t *spell, int level)
{
  char xbuf[MAX_STRING_LENGTH];
  struct affected_type af, af2;
  int success, stat1 = 0, stat2 = 0;
  int dice, tnum;
  int fbonus, tbonus, pbonus;
  int mod;
  int shop;
  extern int top_of_shopt;
  extern struct shop_data *shop_table;
  sh_int health = 0, attrib = 0;
  char *to_vict = NULL;
  char *to_room = NULL;

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

  if (spell->type == SPELL_HEAL || spell->type == SPELL_ANTIDOTE ||
      spell->type == SPELL_STABILIZE || spell->type == SPELL_CURE_DISEASE)
    health = 1;
  else if (spell->type == SPELL_DECREASE_ATTRIB || spell->type == SPELL_INCREASE_ATTRIB ||
           spell->type == SPELL_INCREASE_REFLEXES)
    attrib = 1;

  if (affected_by_spell(victim, spell->type)) {
    if (!health)
      sprintf(buf, "%s already affected by that spell.", (ch == victim ?
              "You are" : "$N is"));
    else strcpy(buf, "The spell has no effect.");
    act(buf, FALSE, ch, 0, victim, TO_CHAR);
    return;
  }

  if (GET_SUSTAINED(ch) >= (ubyte)(GET_SKILL(ch, SKILL_SORCERY)) && !health) {
    send_to_char("You can't sustain any more spells.  Release one first.\r\n", ch);
    return;
  }

  af.type = spell->type;
  af.bitvector = 0;
  af.modifier = 0;
  af.location = APPLY_NONE;
  af.duration = -1;
  af.sustained_by = (IS_NPC(ch) ? -ch->nr : GET_IDNUM(ch));
  af.caster = FALSE;

  af2.type = spell->type;
  af2.bitvector = 0;
  af2.modifier = 0;
  af2.location = APPLY_NONE;
  af2.duration = -1;
  af2.sustained_by = (IS_NPC(victim) ? -victim->nr : GET_IDNUM(victim));
  af2.caster = TRUE;

  tbonus = totem_bonus(ch, spell);
  fbonus = foci_bonus(ch, spell, force, TRUE);
  pbonus = magic_pool_bonus(ch, spell, force, TRUE);

  dice = force + tbonus + pbonus + fbonus;

  tnum = (spell->type == SPELL_ANTI_BULLET ||
	  spell->type == SPELL_ANTI_SPELL) ? 6 : 4;

  xbuf[0] = ' ';
  xbuf[1] = 0;
  mod = modify_target_rbuf(ch, xbuf);

  success = success_test(dice, tnum + mod);

  if ( 1 )
    {
      char rbuf[MAX_STRING_LENGTH];
      sprintf(rbuf,
	      "SusSpell: F:%d/%d, Mag:%d, FFPT/TM %d+%d+%d+%d/%d+%d.  %d.%s",
	      force, spell->force,
	      GET_MAG(ch),
	      force, fbonus, pbonus, tbonus,
	      tnum, mod, success,
	      xbuf);
      act(rbuf, 1, ch, NULL, NULL, TO_ROLLS);
    }


  if (success < 1 && !health && !attrib) {
    send_to_char("Your spell force doesn't seem powerful enough.\r\n", ch);
    return;
  }

  switch (spell->type) {
    case SPELL_ANTIDOTE: case SPELL_CURE_DISEASE:
    case SPELL_HEAL:     case SPELL_STABILIZE:
      af.duration = level;
      af2.duration = level;
      break;
    case SPELL_ANTI_BULLET:
      to_vict = "A shimmering sphere surrounds you.";
      to_room = "A shimmering sphere surrounds $n.";
      af.modifier = force;
      af.location = APPLY_BALLISTIC;
      break;
    case SPELL_ANTI_SPELL:
      to_vict = "A glowing sphere surrounds you briefly, then disappears.";
      to_room = "A glowing sphere surrounds $n briefly.";
      af.modifier = (int)(force / 2);
      break;
    case SPELL_ARMOR:
      af.modifier = (int)(success / 2);
      af.location = APPLY_BOD;
      to_vict = "You feel your skin toughen!";
      break;
    case SPELL_CHAOTIC_WORLD:
      af.type = SPELL_CHAOS;
      af2.type = SPELL_CHAOS;
    case SPELL_CHAOS:
      success -= success_test(GET_WIL(victim) + spell_resist(victim),
			      force + modify_target(victim));

      if (success < 1) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      af.modifier = success;
      af.location = APPLY_TARGET;
      to_vict = "You feel disorientated!";
      break;
    case SPELL_COMBAT_SENSE:
      af.modifier = (int)(success / 2);
      af.location = APPLY_COMBAT_POOL;
      to_vict = "You feel your combat sense improve!";
      if ( af.modifier == 0 )
	{
	  send_to_char("Your casting wasn't powerful enough.\r\n", ch);
	  return;
	}
      break;
    case SPELL_CONFUSION:
      success -= success_test(GET_WIL(victim) + spell_resist(victim),
			      force + modify_target(victim));
      if (success < 1) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      success = stage(success, 1);
      af.modifier = success;
      af.location = APPLY_TARGET;
      to_vict = "You feel confused!";
      break;
    case SPELL_DECREASE_ATTRIB:
    case SPELL_INCREASE_ATTRIB:
      switch (spell->effect) {
        case SPELL_EFFECT_DECREASE_BOD:
        case SPELL_EFFECT_INCREASE_BOD:
          stat1 = GET_BOD(victim);
          stat2 = victim->real_abils.bod;
          af.location = APPLY_BOD;
          break;
        case SPELL_EFFECT_DECREASE_CHA:
        case SPELL_EFFECT_INCREASE_CHA:
          stat1 = GET_CHA(victim);
          stat2 = victim->real_abils.cha;
          af.location = APPLY_CHA;
          break;
        case SPELL_EFFECT_DECREASE_INT:
        case SPELL_EFFECT_INCREASE_INT:
          stat1 = GET_INT(victim);
          stat2 = victim->real_abils.intel;
          af.location = APPLY_INT;
          break;
        case SPELL_EFFECT_DECREASE_QUI:
        case SPELL_EFFECT_INCREASE_QUI:
          stat1 = GET_QUI(victim);
          stat2 = victim->real_abils.qui;
          af.location = APPLY_QUI;
          break;
        case SPELL_EFFECT_DECREASE_REA:
        case SPELL_EFFECT_INCREASE_REA:
          stat1 = GET_REA(victim);
          stat2 = victim->real_abils.rea;
          af.location = APPLY_REA;
          break;
        case SPELL_EFFECT_DECREASE_STR:
        case SPELL_EFFECT_INCREASE_STR:
          stat1 = GET_STR(victim);
          stat2 = victim->real_abils.str;
          af.location = APPLY_STR;
          break;
        case SPELL_EFFECT_DECREASE_WIL:
        case SPELL_EFFECT_INCREASE_WIL:
          stat1 = GET_WIL(victim);
          stat2 = victim->real_abils.wil;
          af.location = APPLY_WIL;
          break;
        default:
          send_to_char("Undefined attribute.\r\n", ch);
          return;
      }
      if (stat1 != stat2) {
        send_to_char(ch, "You can't %s an already modified attribute.\r\n",
                     (spell->type == SPELL_INCREASE_ATTRIB ? "increase" : "decrease"));
        return;
      }
      if (spell->type == SPELL_DECREASE_ATTRIB)
	{
        success = resisted_test(force + spell_bonus(ch, spell),
				10 - (int)(GET_ESS(victim) / 100)
				  + modify_target(ch),
				stat1 + spell_resist(victim),
				10 - (int)(GET_ESS(victim) / 100)
				  + modify_target(victim));
	}
      else
	{
	  success = success_test(force + spell_bonus(ch, spell),
				 2 * stat1 + modify_target(ch));
	  
	  if ( 1 )
	    {
	      char rbuf[MAX_STRING_LENGTH];
	      sprintf(rbuf,
		      "IncrAttrib: F:%d/%d, Mag:%d, FB/TM %d+%d/%d+%d.  %d.",
		      force, spell->force,
		      GET_MAG(ch),
		      force, spell_bonus(ch, spell),
		      2*stat1, modify_target(ch),
		      success);
	      act(rbuf, 1, ch, NULL, NULL, TO_ROLLS);
	    }
	}

      if (success < 1) {
        if (ch == victim)
          send_to_char(NOEFFECT, ch);
        else
	  act("Your spell has no effect on $M.", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      af.modifier = spell->damage;
      if (spell->type == SPELL_DECREASE_ATTRIB)
        af.modifier = - af.modifier;
      break;
    case SPELL_DETECT_ALIGNMENT:
      af.bitvector = AFF_DETECT_ALIGN;
      to_vict = "Your eyes tingle.";
      break;
    case SPELL_DETECT_INVIS:
      to_vict = "Your eyes tingle.";
      af.bitvector = AFF_DETECT_INVIS;
      break;
    case SPELL_DETECT_MAGIC:
      to_vict = "Your eyes tingle.";
      af.bitvector = AFF_DETECT_MAGIC;
      break;
    case SPELL_INCREASE_REFLEXES:
      af.modifier = spell->damage;
      af.location = APPLY_INITIATIVE_DICE;
      if (GET_INIT_DICE(ch) > 0) {
        if (ch == victim)
          send_to_char("Your reflexes are already modified.", ch);
        else act("$S reflexes are already modified.", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      success = success_test(force + spell_bonus(ch, spell),
                             2 * GET_REA(victim) + modify_target(ch));
      if (success < 1) {
        if (ch == victim)
          send_to_char(NOEFFECT, ch);
        else act("Your spell has no effect on $M.", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      break;
    case SPELL_INFLUENCE:
      success -= success_test(GET_WIL(victim) + spell_resist(victim), force + modify_target(victim));
      if (success < 1 || MOB_FLAGGED(victim, MOB_NOCHARM)) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }

      for (shop = 0; shop < top_of_shopt; shop++)
        if (SHOP_KEEPER(shop) == GET_MOB_VNUM(victim))
        {
          send_to_char("They see right through your game.\n\r",ch);
          return;
        }

      if (circle_follow(victim, ch)) {
        act("You cannot charm someone you are following!", FALSE, ch, 0, 0, TO_CHAR);
        return;
      }
      if (victim->master)
        stop_follower(victim);
      REMOVE_BIT(AFF_FLAGS(victim), AFF_GROUP);
      add_follower(victim, ch);
      af.bitvector = AFF_CHARM;
      to_vict = "You feel an irrestible compulsion to follow $N.";
      act("$N promises to obey your every command.", FALSE, ch, 0, victim, TO_CHAR);
      break;
    case SPELL_IMPROVED_INVIS:
      to_room = "$n slowly fades out of existence.";
      to_vict = "You vanish.";
      af.bitvector = AFF_IMP_INVIS;
      break;
    case SPELL_INVISIBILITY:
      to_room = "$n slowly fades out of existence.";
      to_vict = "You vanish.";
      af.bitvector = AFF_INVISIBLE;
      break;
    case SPELL_LIGHT:
      to_room = "A softly glowing sphere appears from $n's hands.";
      to_vict = "A small sphere of light appears and begins to hover about your head.";
      world[victim->in_room].light++;
      break;
    case SPELL_OVERSTIMULATION:
      success -= success_test(GET_WIL(victim) + spell_resist(victim), force + modify_target(victim));
      if (success < 1) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      if (success >= 10) {
        af.bitvector = AFF_PETRIFY;
        to_vict = "Your brain screams in pain, and your muscles become distant!";
      }
      if (success >= 6)
        af.modifier = 3;
      else if (success >= 3)
        af.modifier = 2;
      else if (success >= 1)
        af.modifier = 1;
      else af.modifier = 0;
      af.location = APPLY_TARGET;
      break;
    case SPELL_PETRIFY:
      success -= success_test(GET_BOD(victim) + spell_resist(victim), force + modify_target(victim));
      if (success < 1) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      af.bitvector = AFF_PETRIFY;
      af.modifier = success;
      to_vict = "Your muscles seem to become solid stone.";
      to_room = "$n freezes in place, $s skin turning a textured gray.";
      break;
    case SPELL_POISON:
      success -= resisted_test(GET_BOD(victim) + spell_resist(victim), force + modify_target(victim),
                 force, GET_BOD(victim) + modify_target(ch));
      if (success < 1) {
        act("$N seems to resist the effects!", FALSE, ch, 0, victim, TO_CHAR);
        return;
      }
      af.modifier = level;
      af.duration = success;
      af.bitvector = AFF_POISON;
      to_vict = "You feel very sick.";
      to_room = "$n gets violently ill!";
      break;
    case SPELL_RESIST_PAIN:
      to_vict = "You lose feeling of your physical wounds.";
      af.modifier = level;
      break;
  }

  affect_to_char(victim, &af);
  affect_to_char(ch, &af2);
  GET_SUSTAINED(ch) += 1;
  if (!health)
    send_to_char("The spell is sustained.\r\n", ch);

  /* send messages to room and victim if necessary */
  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);
}

/* the way a mage/shaman stops sustaining a spell */
ACMD(do_release)
{
   char who[255], *spell;
   struct char_data *vict;
   int spellnum;
   static struct affected_type *af;

   if (!*argument) {
      send_to_char("What spell and who do you wish to release it from?\r\n", ch);
      return;
   }
   spell = any_one_arg(argument, who);

   if (!(vict = get_char_room_vis(ch, who))) {
      send_to_char("You can't seem to find that person here.\r\n", ch);
      return;
   }

   if (!spell) {
      send_to_char("What spell do you wish to release?\r\n", ch);
      return;
   }
   spellnum = find_skill_num(spell);
   if (spellnum == -1) {
     send_to_char("What spell is that?\r\n", ch);
     return;
   } else if (spellnum == SPELL_HEAL || spellnum == SPELL_CURE_DISEASE || spellnum == SPELL_STABILIZE ||
              spellnum == SPELL_ANTIDOTE) {
     send_to_char("How do you expect to release that?!\r\n", ch);
     return;
  }

  if (affected_by_spell(vict, spellnum) != 1) {
    act("$N doesn't have that spell sustained on $M.", FALSE, ch, 0, vict, TO_CHAR);
    return;
  }

   for (af = vict->affected; af; af = af->next)
     if ((af->type == spellnum) && (af->caster == FALSE)) {
       if (af->sustained_by != (IS_NPC(ch) ? -ch->nr : GET_IDNUM(ch))) {
         send_to_char("But you aren't sustaining that spell!\r\n", ch);
         return;
       } else {
         sprintf(buf, "%s\r\n", spell_wear_off_msg[af->type]);
         send_to_char(buf, vict);
         affect_remove(vict, af, 1);
         return;
       }
     }
}

#define NUM_SPIRITS       17

ACMD(do_bond)
{
  struct affected_type *af;
  int i, spellnum;
  struct obj_data *obj = NULL, *weapon = NULL, *temp;
  spell_t *spell;
  char *pbuf;

  pbuf = any_one_arg(argument, arg);
  while(*pbuf == ' ')
    pbuf++;

  if (!*arg) {
    send_to_char("Usage: bond <focus> <spell/category/weapon>\r\n"
                 "       bond <docwagon>\r\n", ch);
    return;
  }

  if (IS_NPC(ch)) {
    send_to_char("Mobs can't bond; go away.\r\n", ch);
    return;
  }

  for (i = 0; !obj && i < NUM_WEARS; i++)
    if (GET_EQ(ch, i) && isname(arg, GET_EQ(ch, i)->name))
      obj = GET_EQ(ch, i);

  if (!obj)
    for (temp = ch->carrying; !obj && temp; temp = temp->next_content)
      if (isname(arg, temp->name))
        obj = temp;

  if (!obj) {
    send_to_char(ch, "You do not seem to have a '%s'.\r\n", arg);
    return;
  }

  if (GET_OBJ_TYPE(obj) == ITEM_DOCWAGON) {
    if (GET_OBJ_VAL(obj, 1)) {
      act("$p has already been activated.", FALSE, ch, obj, 0, TO_CHAR);
      return;
    }
    GET_OBJ_VAL(obj, 1) = GET_IDNUM(ch);
    act("$p's lights begin to subtly flash in a rhythmic sequence.", FALSE,
        ch, obj, 0, TO_CHAR);
    return;
  }

  if (GET_OBJ_TYPE(obj) != ITEM_FOCUS) {
    send_to_char("You can only bond foci and DocWagon contracts.\r\n", ch);
    return;
  }
  if (GET_TRADITION(ch) == TRAD_MUNDANE) {
    send_to_char("Mundanes can't bond foci.\r\n", ch);
    return;
  } else if (GET_FOCI(ch) >= GET_INT(ch)) {
    send_to_char("You cannot bond any more foci.\r\n", ch);
    return;
  } else if (GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) > 0
	     || GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM)) {
    send_to_char("That focus has already been bonded.\r\n", ch);
    return;
  } else if (GET_TRADITION(ch) == TRAD_ADEPT
	     && GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE) != FOCI_WEAPON) {
    send_to_char("Adepts can only bond weapon foci.\r\n", ch);
    return;
  }

  if (obj->worn_by == ch)
    for (i = 0; i < MAX_OBJ_AFFECT; i++)
      affect_modify(ch, obj->affected[i].location, obj->affected[i].modifier,
                    obj->obj_flags.bitvector, FALSE);

  switch (GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE)) {
    case FOCI_SPELL:
      if (GET_KARMA(ch) < (GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 100)) {
        send_to_char(ch, "You do not have %d karma to bond this focus.\r\n",
		     GET_OBJ_VAL(obj, VALUE_FOCUS_RATING));
        return;
      }
      spell = find_spell( ch, pbuf );
      if ( !spell )
	{
	  send_to_char(ch, "You do not know spell '%s'.\r\n", pbuf);
	  return;
	}
      GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) = GET_IDNUM(ch);
      GET_OBJ_VAL(obj, VALUE_FOCUS_CAT) = spell->type;
      GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) = spell->type;
      GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
      sprintf(buf, "You have successfully bonded $p (%s).",
	      spells[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
      act(buf, FALSE, ch, obj, 0, TO_CHAR);
      GET_KARMA(ch) -= GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 100;
      GET_FOCI(ch)++;
      break;
    case FOCI_SPELL_CAT:
      if (GET_KARMA(ch) < (GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 300)) {
        send_to_char(ch, "You do not have %d karma to bond this focus.\r\n",
		     GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 3);
        return;
      }
      for( i = 0; *spell_categories[i] != '\n'; i++ )
	if (is_abbrev(pbuf, spell_categories[i]))
	  break;

      if ( *spell_categories[i] == '\n' )
	{
	  send_to_char("You must specify a category for this focus to assist.\r\n", ch);
	  return;
	}
      GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) = GET_IDNUM(ch);
      GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) = i;
      GET_OBJ_VAL(obj, VALUE_FOCUS_CAT) = i;
      GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
      GET_KARMA(ch) -= GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 300;
      GET_FOCI(ch)++;
      sprintf(buf, "You have successfully bonded $p (%s).",
	      spell_categories[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
      act(buf, FALSE, ch, obj, 0, TO_CHAR);
      break;
    case FOCI_SPIRIT:
      if (GET_KARMA(ch) < (GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 200)) {
        send_to_char(ch, "You do not have %d karma to bond this focus.\r\n",
		     GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 2);
        return;
      }
      for( i = 0; *spirits[i] != '\n'; i++ )
	if (is_abbrev(pbuf, spirits[i]))
	  break;
      if ( *spell_categories[i] == '\n' )
	{
	  send_to_char("You must specify a spirit type for this focus to assist.\r\n", ch);
	  return;
	}
      GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) = GET_IDNUM(ch);
      GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) = i;
      GET_OBJ_VAL(obj, VALUE_FOCUS_CAT) = i;
      GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
      GET_KARMA(ch) -= GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 200;
      GET_FOCI(ch)++;
      sprintf(buf, "You bond $p (%s).",
	      spirits[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
      act(buf, FALSE, ch, obj, 0, TO_CHAR);
      break;
    case FOCI_POWER:
      if (GET_KARMA(ch) < (GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 500)) {
        send_to_char(ch, "You do not have %d karma to bond this focus.\r\n",
		     GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 5);
        return;
      }
      GET_KARMA(ch) -= GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 500;
      GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) = GET_IDNUM(ch);
      GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
      for (i = 0; i < MAX_OBJ_AFFECT; i++)
        if (!obj->affected[i].modifier) {
          obj->affected[i].modifier = GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
          obj->affected[i].location = APPLY_MAG;
          affect_modify(ch, obj->affected[i].location, obj->affected[i].modifier,
                            obj->obj_flags.bitvector, TRUE);
          break;
        }
      for (i = 0; i < MAX_OBJ_AFFECT; i++)
        if (!obj->affected[i].modifier) {
          obj->affected[i].modifier = GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
          obj->affected[i].location = APPLY_MAGIC_POOL;
          affect_modify(ch, obj->affected[i].location, obj->affected[i].modifier,
                            obj->obj_flags.bitvector, TRUE);
          break;
        }
      act("You have successfully bonded $p.", FALSE, ch, obj, 0, TO_CHAR);
      GET_FOCI(ch)++;
      break;
    case FOCI_LOCK:
      if (!*pbuf) {
        send_to_char("You need to select a spell to lock.\r\n", ch);
        return;
      }
      if (GET_KARMA(ch) < 100) {
        send_to_char("You do not have the karma point to lock any spell.\r\n", ch);
        return;
      }
      spellnum = find_skill_num(pbuf);
      if (spellnum <= 0) {
        act("That spell doesn't exist.", FALSE, ch, 0, 0, TO_CHAR);
        return;
      }
      if (affected_by_spell(ch, spellnum) != 1) {
        act("You are not affected by that spell.", FALSE, ch, 0, 0, TO_CHAR);
        return;
      }
      for (af = ch->affected; af; af = af->next)
        if (af->type == spellnum && af->caster == FALSE) {
          if (af->sustained_by != GET_IDNUM(ch))
            send_to_char("You can only lock spells that you are sustaining.\r\n", ch);
          else {
            GET_KARMA(ch) -= 100;
            GET_FOCI(ch)++;
            GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) = GET_IDNUM(ch);
            GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
            GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) = af->type;
            if (af->bitvector)
              SET_BIT(obj->obj_flags.bitvector, af->bitvector);
            for (i = 0; i < MAX_OBJ_AFFECT; i++)
              if (!obj->affected[i].modifier) {
                obj->affected[i].location = af->location;
                obj->affected[i].modifier = af->modifier;
                affect_modify(ch, obj->affected[i].location, obj->affected[i].modifier,
                                  obj->obj_flags.bitvector, TRUE);
                break;
              }
            sprintf(buf, "You lock %s to $p.",
		    spells[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
            act(buf, FALSE, ch, obj, 0, TO_CHAR);
            affect_remove(ch, af, 1);
          }
          return;
        }
      break;
    case FOCI_WEAPON:
      if (GET_KARMA(ch) < (GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 500)) {
        send_to_char(ch, "You do not have %d karma to bond this focus.\r\n",
		     GET_OBJ_VAL(obj, VALUE_FOCUS_RATING) * 5);
        return;
      }
      for (i = 0; !weapon && i < NUM_WEARS; i++)
        if (GET_EQ(ch, i) && isname(pbuf, GET_EQ(ch, i)->name))
          weapon = GET_EQ(ch, i);
      if (!weapon)
        for (temp = ch->carrying; !weapon && temp; temp = temp->next_content)
          if (isname(pbuf, temp->name))
            weapon = temp;
      if (!weapon) {
        send_to_char(ch, "You do not seem to have a '%s'.\r\n", pbuf);
        break;
      }
      if (GET_OBJ_TYPE(weapon) != ITEM_WEAPON) {
        send_to_char("You can only bond weapon foci to weapons.\r\n", ch);
        break;
      }
      if (GET_OBJ_VAL(weapon, 3) == TYPE_HIT || GET_OBJ_VAL(weapon, 3) >= TYPE_TASER) {
        send_to_char("That is not a suitable weapon to bond a foci to.\r\n", ch);
        return;
      }
      GET_KARMA(ch) -= GET_OBJ_VAL(obj, 1) * 500;
      GET_FOCI(ch)++;
      GET_OBJ_VAL(weapon, 9) = GET_IDNUM(ch);
      GET_OBJ_VAL(weapon, 8) = GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);
      GET_OBJ_VAL(weapon, 7) = GET_OBJ_VNUM(obj);
      GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) = 1;
      sprintf(buf, "%s melts as its power flows into %s.\r\n",
	      CAP(obj->short_description),
	      weapon->short_description);
      send_to_char(buf, ch);
      extract_obj(obj);
      break;
  }
}

ACMD(do_status)
{
  struct affected_type *af;
  bool last_cast = FALSE, found = FALSE;
  struct char_data *tch, *target;
  struct obj_data *obj;
  int i;

  if (IS_AFFECTED(ch, AFF_DETECT_MAGIC) || IS_ASTRAL(ch) || IS_DUAL(ch)) {
    skip_spaces(&argument);
    if (!*argument)
      target = ch;
    else target = get_char_room_vis(ch, argument);
  } else target = ch;

  if (!target) {
    send_to_char("You can't seem to find that person.\r\n", ch);
    return;
  }

  sprintf(buf, "%s affected by:", (ch == target ? "You are" : "$N is"));
  act(buf, FALSE, ch, 0, target, TO_CHAR);

  if (ch == target) {
    for (i = 0; i < NUM_WEARS; i++)
      if ((obj = GET_EQ(ch, i))
	  && GET_OBJ_TYPE(obj) == ITEM_FOCUS
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE) == FOCI_LOCK
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch)) {
        sprintf(buf, "  %-20s          Locked on: $p",
		spells[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
        act(buf, FALSE, ch, obj, 0, TO_CHAR);
        found = TRUE;
      }
/*
    for (obj = ch->carrying; obj; obj = obj->next_content)
      if (GET_OBJ_TYPE(obj) == ITEM_FOCUS
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE) == FOCI_LOCK
	  && GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch)) {
        sprintf(buf, "  %-20s          Locked on: $p",
		spells[GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY)]);
        act(buf, FALSE, ch, obj, 0, TO_CHAR);
        found = TRUE;
      }*/
  }

  for (af = target->affected; af; af = af->next) {
    if (af->caster == TRUE && af->sustained_by ==
        (IS_NPC(target) ? -target->nr : GET_IDNUM(target)))
      last_cast = TRUE;
    else {
      if (af->caster == FALSE && af->type != SPELL_HEAL && af->type != SPELL_CURE_DISEASE &&
          af->type != SPELL_STABILIZE && af->type != SPELL_ANTIDOTE) {
        if (last_cast == TRUE) {
          last_cast = FALSE;
          if (ch == target)
            sprintf(buf, "  %-20s       Sustained by: yourself", spells[af->type]);
          else sprintf(buf, "  %-20s", spells[af->type]);
          act(buf, FALSE, ch, 0, target, TO_CHAR);
          found = TRUE;
        } else {
          tch = character_list;
          if (ch == target)
            sprintf(buf, "  %-20s       Sustained by: $N", spells[af->type]);
          else sprintf(buf, "  %-20s", spells[af->type]);
          found = TRUE;
          if (af->sustained_by < 0)
            while (tch != NULL && (IS_NPC(tch) ? tch->nr : 0) != -af->sustained_by)
              tch = tch->next;
          else while (tch != NULL && (!IS_NPC(tch) ? GET_IDNUM(tch) : 0) != af->sustained_by)
            tch = tch->next;
          act(buf, FALSE, ch, 0, tch, TO_CHAR);
        }
      }
    }
  }
  if (!found)
    send_to_char("  Nothing.\r\n", ch);

  found = FALSE;
  last_cast = FALSE;

  if (ch != target)
    return;

  if (GET_TRADITION(ch) != TRAD_SHAMANIC && GET_TRADITION(ch) != TRAD_HERMETIC &&
      !(!IS_NPC(ch) && access_level(ch, LVL_PRESIDENT)))
    return;

  send_to_char("\r\nYou are sustaining:\r\n", ch);

  if (!ch->affected)
    send_to_char("  Nothing.\r\n", ch);
  else {
    for (af = ch->affected; af; af = af->next) {
      if (af->caster == TRUE
	  && af->type != SPELL_HEAL
	  && af->type != SPELL_CURE_DISEASE
	  && af->type != SPELL_STABILIZE 
	  && af->type != SPELL_ANTIDOTE) {
        if (af->sustained_by == (IS_NPC(ch) ? -ch->nr : GET_IDNUM(ch)))
          sprintf(buf, "  %-20s       Sustained on: yourself", spells[af->type]);
        else
	  sprintf(buf, "  %-20s       Sustained on: $N", spells[af->type]);
        found = TRUE;
        tch = character_list;
        if (af->sustained_by < 0)
          while (tch != NULL && (IS_NPC(tch) ? tch->nr : 0) != -af->sustained_by)
            tch = tch->next;
        else while (tch != NULL && (!IS_NPC(tch) ? GET_IDNUM(tch) : 0) != af->sustained_by)
            tch = tch->next;
        act(buf, FALSE, ch, 0, tch, TO_CHAR);
      }
    }
    if (!found)
      send_to_char("  Nothing.\r\n", ch);
  }
}

int check_spirit_sector(int room, int spirit)
{
  if (spirit < 5)
    return 1;

  switch (spirit) {
    case 5:
      if (world[room].sector_type != SECT_CITY)
        return 0;
      break;
    case 6:
      if (world[room].sector_type != SECT_INSIDE)
        return 0;
      break;
    case 7: case 8: case 11:
      if (world[room].sector_type != SECT_FIELD)
        return 0;
      break;
    case 9:
      if (world[room].sector_type != SECT_FOREST)
        return 0;
      break;
    case 10:
      if (world[room].sector_type != SECT_MOUNTAIN && world[room].sector_type != SECT_HILLS)
        return 0;
      break;
    case 12: case 13:
      if (world[room].sector_type == SECT_INSIDE || world[room].sector_type > SECT_FLYING)
        return 0;
      break;
    case 14: case 15: case 17:
      if (world[room].sector_type != SECT_WATER_SWIM && world[room].sector_type != SECT_WATER_NOSWIM)
        return 0;
      break;
    case 16:
      if (world[room].sector_type != SECT_WATER_NOSWIM && world[room].sector_type != SECT_UNDERWATER)
        return 0;
      break;
  }

  return 1;
}

int spirit_bonus(struct char_data *ch, int spirit)
{
  int i, bonus = 0;
  struct obj_data *obj;

  if (IS_NPC(ch))
    return 0;

  for (i = 0; !bonus && i < (NUM_WEARS - 1); i++)
    if ((obj = GET_EQ(ch, i))
	&& GET_OBJ_TYPE(obj) == ITEM_FOCUS
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch)
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) == spirit
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) == 1)
      bonus = GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);

  for (obj = ch->carrying; !bonus && obj; obj = obj->next_content)
    if (GET_OBJ_TYPE(obj) == ITEM_FOCUS
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_CHAR_IDNUM) == GET_IDNUM(ch)
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_SPECIFY) == spirit
	&& GET_OBJ_VAL(obj, VALUE_FOCUS_BONDED) == 1)
      bonus = GET_OBJ_VAL(obj, VALUE_FOCUS_RATING);

  if (GET_TRADITION(ch) == TRAD_SHAMANIC)
    switch (GET_TOTEM(ch)) {
      case TOTEM_BEAR:
        if (spirit == 9)
          bonus += 2;
        break;
      case TOTEM_RACCOON: case TOTEM_CAT:
        if (spirit == 5)
          bonus += 2;
        break;
      case TOTEM_DOG:
        if (spirit == 6 || spirit == 7)
          bonus += 2;
        break;
      case TOTEM_EAGLE: case TOTEM_RAVEN:
        if (spirit == 13)
          bonus += 2;
        break;
      case TOTEM_GATOR:
        if ((!number(0, 1) ? spirit == 5 : spirit == 14 || spirit == 15 || spirit == 17))
          bonus += 2;
      case TOTEM_LION:
        if (spirit == 11)
          bonus += 2;
        break;
      case TOTEM_OWL:
        if (time_info.hours <= 6 || time_info.hours >= 19)
          bonus += 2;
        break;
      case TOTEM_RAT:
        if (spirit >= 5 && spirit <= 7)
          bonus += 2;
        break;
      case TOTEM_SHARK:
        if (spirit == 16)
          bonus += 2;
        break;
      case TOTEM_SNAKE:
        if (!number(0, 1)) {
          if (spirit == (5 + number(0, 2)))
            bonus += 2;
        } else if (spirit == (7 + number(0, 2)))
          bonus +=  2;
        break;
      case TOTEM_WOLF:
        if ((!number(0, 1) ? spirit == 9 : spirit == 11))
          bonus += 2;
        break;
  }
  return bonus;
}

ACMD(do_conjure)
{
  int num, i, force, successes, drain, wound, is_physical;
  struct char_data *spirit;
  char *s;

  if (IS_NPC(ch) || (GET_TRADITION(ch) != TRAD_HERMETIC &&
      GET_TRADITION(ch) != TRAD_SHAMANIC && !access_level(ch, LVL_OWNER))) {
    send_to_char("You aren't able to conjure beings!\r\n", ch);
    return;
  }

  if (GET_MAG(ch) < 100) {
    send_to_char("You have to have a magic rating of at least one to conjure.\r\n", ch);
    return;
  } else if (GET_SKILL(ch, SKILL_CONJURING) < 1) {
    send_to_char("You have to have some conjuring skill to attempt that!\r\n", ch);
    return;
  }
  any_one_arg(argument, arg);

  if (!*arg || !*argument) {
    send_to_char("Usage: conjure <force> <spirit/elemental>\r\n", ch);
    return;
  }

  if ((force = atoi(arg)) < 1) {
    send_to_char("You must supply a valid force for that to work.\r\n", ch);
    return;
  }

  s = strtok(argument, "'");

  if (s == NULL) {
    send_to_char("You must specify the name of the spirit you wish to conjure.\r\n", ch);
    return;
  }

  s = strtok(NULL, "'");

  if (s == NULL)  {
    send_to_char("Spirit names must be enclosed in single quotes (').\r\n", ch);
    return;
  }

  for (i = 1; *spirits[i] != '\n'; i++)
    if (is_abbrev(s, spirits[i]))
      break;

  if (*spirits[i] == '\n') {
    send_to_char(ch, "There is no '%s' spirit.\r\n", s);
    return;
  }

  if (GET_TRADITION(ch) == TRAD_HERMETIC && i > 4) {
    send_to_char("Hermetic mages can only conjure elementals.\r\n", ch);
    return;
  } else if (GET_TRADITION(ch) == TRAD_HERMETIC &&
             !ROOM_FLAGGED(ch->in_room, ROOM_HERMETIC_LIBRARY)) {
    send_to_char("Conjuring elementals requires a hermetic library.\r\n", ch);
    return;
  } else if (GET_TRADITION(ch) == TRAD_SHAMANIC) {
    if (i < 5) {
      send_to_char("Shamans can only conjure nature spirits.\r\n", ch);
      return;
    }
    if (!check_spirit_sector(ch->in_room, i)) {
      send_to_char("You can't conjure that spirit here.\r\n", ch);
      return;
    }
  }

  if ((num = real_mobile(24 + i)) < 0) {
    sprintf(buf, "No %s mob", spirits[i]);
    log(buf);
    send_to_char("The conjuring system seems to be screwed up right now...\r\n", ch);
    return;
  }

  if (ROOM_FLAGGED(ch->in_room, ROOM_NOMAGIC)) {
    send_to_char(ch, "Your attempt to conjure %s fizzles into oblivion.\r\n", spirits[i]);
    return;
  }

  successes = success_test(GET_CHA(ch) + (spirit_bonus(ch,i)/2),
			   force + modify_target(ch));
  if (force < (GET_CHA(ch) / 2)) {
    wound = LIGHT;
    is_physical = FALSE;
  } else if (force <= GET_CHA(ch)) {
    wound = MODERATE;
    is_physical = FALSE;
  } else if (force > (GET_CHA(ch) * 2)) {
    wound = DEADLY;
    is_physical = TRUE;
  } else {
    wound = SERIOUS;
    is_physical = TRUE;
  }

  drain = convert_damage(dec_staging(successes, wound));

  if (is_physical)
    GET_PHYSICAL(ch) -= drain * 100;
  else if ((int)(GET_MENTAL(ch) / 100) < drain) {
    GET_PHYSICAL(ch) -= (drain - (int)(GET_MENTAL(ch) / 100)) * 100;
    GET_MENTAL(ch) = 0;
  }
  else GET_MENTAL(ch) -= drain * 100;
  update_pos(ch);

  if (GET_POS(ch) <= POS_STUNNED && GET_POS(ch) > POS_DEAD) {
    if (FIGHTING(ch))
       stop_fighting(ch);
    send_to_char("You are unable to resist the drain from the evocation and fall unconscious!\r\n", ch);
    act("$n collapses unconscious from the use of magic!", FALSE, ch, 0, 0, TO_ROOM);
    return;
  } else if (GET_POS(ch) == POS_DEAD) {
    if (FIGHTING(ch))
      stop_fighting(ch);
    send_to_char("The energy from the evocation overloads your systems, killing you...\r\n", ch);
    act("$n collapses DEAD from an overload of magic!", FALSE, ch, 0, 0, TO_ROOM);
    die(ch);
    return;
  }

  if ((successes = success_test(GET_SKILL(ch, SKILL_CONJURING)
				+ (spirit_bonus(ch, i)+1)/2,
				force + modify_target(ch))) < 1) {
    send_to_char(ch, "You fail to conjure %s!\r\n", spirits[i]);
    return;
  }

  spirit = read_mobile(num, REAL);
  char_to_room(spirit, ch->in_room);
  GET_ACTIVE(spirit) = successes;
  spirit->real_abils.bod = force;
  spirit->real_abils.qui = force;
  spirit->real_abils.str = force;
  spirit->real_abils.cha = force;
  spirit->real_abils.intel = force;
  spirit->real_abils.wil = force;
  spirit->real_abils.ess = force * 100;
  GET_SKILL(spirit, SKILL_UNARMED_COMBAT) = force;
  GET_LEVEL(spirit) = force;
  switch (i) {
    case 1:
      spirit->real_abils.bod -= 2; spirit->real_abils.qui += 3; spirit->real_abils.str -= 3;
      break;
    case 2:
      spirit->real_abils.bod += 4; spirit->real_abils.qui -= 2; spirit->real_abils.str += 4;
      break;
    case 3:
      spirit->real_abils.bod += 1; spirit->real_abils.qui += 2; spirit->real_abils.str -= 2;
      break;
    case 4:
      spirit->real_abils.bod += 2;
      break;
    case 5: case 6: case 7:
      spirit->real_abils.bod += 1; spirit->real_abils.qui += 2; spirit->real_abils.str -= 2;
      break;
    case 8: case 9: case 10: case 11:
      spirit->real_abils.bod += 4; spirit->real_abils.qui -= 2; spirit->real_abils.str += 4;
      break;
    case 12: case 13:
      spirit->real_abils.bod -= 2; spirit->real_abils.qui += 3; spirit->real_abils.str -= 3;
      break;
    case 14: case 15: case 16: case 17:
      spirit->real_abils.bod += 2;
      break;
  }
  affect_total(spirit);
  act("$n appears from thin air.", TRUE, spirit, 0, 0, TO_ROOM);
  act("$n fades in from the air.", FALSE, spirit, 0, 0, TO_CHAR);
  if (!AFF_FLAGGED(spirit, AFF_CHARM))
    SET_BIT(AFF_FLAGS(spirit), AFF_CHARM);
  add_follower(spirit, ch);
}

void add_to_spell_q(struct char_data *ch, spell_t *spell, int force, char *arg)
{
  spell_t *temp, *i;

  temp = new spell_t;
  *temp = *spell;
  temp->force = force;

  if (arg) {
    temp->name = new char[strlen(arg)+1];
    strcpy(temp->name, arg);
  } else temp->name = NULL;

  if (!GET_SPELL_Q(ch)) {
    GET_SPELL_Q(ch) = temp;
    GET_SPELL_Q(ch)->next = NULL;
  } else {
    // this loop goes until i->next is NULL, or until temp is the last
    for (i = GET_SPELL_Q(ch); i->next; i = i->next);
    i->next = temp;
    temp->next = NULL;
  }
}

void delete_top_of_spell_q(struct char_data *ch)
{
  if (!GET_SPELL_Q(ch))
    return;

  spell_t *temp = GET_SPELL_Q(ch);
  GET_SPELL_Q(ch) = GET_SPELL_Q(ch)->next;

  if (temp->name)
    delete [] temp->name;
  delete temp;
}

void purge_spell_q(struct char_data *ch)
{
  while (GET_SPELL_Q(ch))
    delete_top_of_spell_q(ch);
}

bool cycle_spell_q(struct char_data *ch)
{
  spell_t *temp = GET_SPELL_Q(ch);
  spell_t spellcopy;
  bool success;

  if (!temp)
    return FALSE;

  // Make a copy of the spell data so any frees won't affect us.
  spellcopy = *temp;
  temp = &spellcopy;

  switch (temp->category) {
    case SPELL_CATEGORY_COMBAT:
      success = process_combat_target(ch, temp, temp->name, temp->force);
      break;
    case SPELL_CATEGORY_HEALTH:
      success = process_health_target(ch, temp, temp->name, temp->force);
      break;
    case SPELL_CATEGORY_ILLUSION:
      success = process_illusion_target(ch, temp, temp->name, temp->force);
      break;
    case SPELL_CATEGORY_MANIPULATION:
      success = process_manipulation_target(ch, temp, temp->name, temp->force);
      break;
    default:
      send_to_char("Your spell seems to make no sense to you whatsoever.\r\n", ch);
      mudlog("Unknown spell category in newmagic.cc", ch, LOG_SYSLOG, TRUE);
      delete_top_of_spell_q(ch);
      return FALSE;
  }

  // success determines if the mud found a target and the spell was cast
  if (success) {
    if (temp->type == SPELL_RESIST_PAIN)
      resist_drain(ch, temp->force, temp, success + 1);
    else if (temp->type != SPELL_HEAL && temp->type != SPELL_ANTIDOTE &&
        temp->type != SPELL_CURE_DISEASE && temp->type != SPELL_POISON)
      resist_drain(ch, temp->force, temp, DRAIN_LEVEL(temp->drain));
    else resist_drain(ch, temp->force, temp, success);
    delete_top_of_spell_q(ch);
    return TRUE;
  }

  delete_top_of_spell_q(ch);
  return FALSE;
}

// the actual cast command
ACMD(do_cast)
{
  spell_t *spell;
  char *s, *t;
  bool newforce = FALSE;
  int i, spellnum = 0;
  int delete_spell = 0;
  char argcopy[MAX_STRING_LENGTH];

  if (!IS_NPC(ch) && !access_level(ch, LVL_PRESIDENT) && GET_TRADITION(ch) != TRAD_SHAMANIC &&
      GET_TRADITION(ch) != TRAD_HERMETIC) {
    send_to_char("You are but a mundane, sorry.\r\n", ch);
    return;
  }

  if (GET_MAG(ch) < 100) {
    send_to_char("You must have a magic rating of at least one to cast spells.\r\n", ch);
    return;
  }

  any_one_arg(argument, arg);

  int force = atoi(arg);
  if (force > 0)
    newforce = TRUE;

  /* get: blank, spell name, target name */
  strcpy(argcopy, argument);
  s = strtok(argcopy, "'");

  if (s == NULL) {
    send_to_char("You must specify the name of the spell you wish to cast.\r\n", ch);
    return;
  }

  s = strtok(NULL, "'");

  if (s == NULL)  {
    send_to_char("Spell names must be enclosed in single quotes (').\r\n", ch);
    return;
  }

  if (!access_level(ch, LVL_PRESIDENT))
    spell = find_spell(ch, s);
  else {
    spell = find_spell(ch, s);
    if (!spell)
      {
	for (i = 3; !spellnum && i <= MAX_SPELLS; i++)
	  if (is_abbrev(s, spells[i]))
	    spellnum = i;
	
	if (!spellnum) {
	  send_to_char(ch, "'%s' doesn't seem to be a standard spell.\r\n", s);
	  return;
	}
	delete_spell = 1;
	spell = new spell_t;
	spell->name = str_dup(spells[spellnum]);
	spell->physical = grimoire[spellnum].physical;
	spell->category = grimoire[spellnum].category;
	spell->force = 10;
	spell->target = grimoire[spellnum].target;
	spell->drain = grimoire[spellnum].drain;
	spell->damage = grimoire[spellnum].damage;
	spell->effect = SPELL_EFFECT_NONE;
	spell->type = spellnum;
	spell->next = NULL;
      }
  }

  if (!spell) {
    send_to_char(ch, "You don't seem to know a spell named '%s'.\r\n", s);
    return;
  }

  if (newforce) {
    if (spell->force < force) {
      send_to_char("You can't cast a spell at a force higher than you learned it.\r\n", ch);
      return;
    }
  } else force = spell->force;

  if (ROOM_FLAGGED(ch->in_room, ROOM_NOMAGIC)) {
    send_to_char("Your magic fizzles into oblivion.\r\n", ch);
    return;
  }

  // pull off the last of the arguments and put it in t
  t = strtok(NULL, "\0");
  while(t && *t == ' ')
    t++;

  if (FIGHTING(ch) && spell->category != SPELL_CATEGORY_DETECTION) {
    add_to_spell_q(ch, spell, force, t);
    send_to_char(ch, "You begin to concentrate on casting %s.\r\n",
                 spell->name);
    if (delete_spell) {
      if (spell->name)
        delete [] spell->name;
      delete spell;
    }
    return;
  }

  bool success;
  switch (spell->category) {
    case SPELL_CATEGORY_COMBAT:
      success = process_combat_target(ch, spell, t, force);
      break;
    case SPELL_CATEGORY_DETECTION:
      success = process_detection_target(ch, spell, t, force);
      break;
    case SPELL_CATEGORY_HEALTH:
      success = process_health_target(ch, spell, t, force);
      break;
    case SPELL_CATEGORY_ILLUSION:
      success = process_illusion_target(ch, spell, t, force);
      break;
    case SPELL_CATEGORY_MANIPULATION:
      success = process_manipulation_target(ch, spell, t, force);
      break;
    default:
      send_to_char("Your spell seems to make no sense to you whatsoever.\r\n", ch);
      mudlog("Unknown spell category in newmagic.cc", ch, LOG_SYSLOG, TRUE);
      return;
  }

  // success determines if the mud found a target and the spell was cast
  if (success) {
    if (spell->type == SPELL_RESIST_PAIN)
      resist_drain(ch, force, spell, success + 1);
    else if (spell->type != SPELL_HEAL && spell->type != SPELL_ANTIDOTE &&
        spell->type != SPELL_CURE_DISEASE && spell->type != SPELL_POISON)
      resist_drain(ch, force, spell, DRAIN_LEVEL(spell->drain));
    else resist_drain(ch, force, spell, success);
    // if they are in combat, they lose a turn
    if (FIGHTING(ch) && !AFF_FLAGGED(ch, AFF_ACTION))
      SET_BIT(AFF_FLAGS(ch), AFF_ACTION);
    // also, they get a wait state for casting
    WAIT_STATE(ch, PULSE_VIOLENCE);
  }
  if (delete_spell) {
    if (spell->name)
      delete [] spell->name;
    delete spell;
  }
}

struct char_data *range_spell(struct char_data *ch, char *target, char *direction)
{
  int room = ch->in_room, nextroom, dir, sight, distance;
  struct char_data *vict;
  bool found = FALSE;

  if ((dir = search_block(direction, lookdirs, FALSE)) == -1)
    return NULL;
  dir = convert_look[dir];

  if (CAN_GO2(room, dir))
    nextroom = EXIT2(room, dir)->to_room;
  else nextroom = NOWHERE;

  sight = find_sight(ch);

  for (distance = 1; ((nextroom != NOWHERE) && (distance <= sight)); distance++) {
    for (vict = world[nextroom].people; vict; vict = vict->next_in_room) {
      if (isname(target, GET_NAME(vict)) && vict != ch && CAN_SEE(ch, vict)) {
        found = TRUE;
        break;
      }
    }

    if (found == TRUE) {
      if (IS_SET(ROOM_FLAGS(vict->in_room), ROOM_PEACEFUL)) {
        send_to_char("Nah - leave them in peace.\r\n", ch);
        return ch;
      }

      if (ROOM_FLAGGED(vict->in_room, ROOM_NOMAGIC)) {
        act("Your spell vanishes before reaching $M.", FALSE, ch, 0, vict, TO_CHAR);
        return ch;
      }

      if ( !IS_NPC(vict) && str_cmp(vict->player.name,target) )
      {
	send_to_char("You need to type the player's ENTIRE name.\n\r",ch);
	return ch;
      }

      if (!ok_damage_shopkeeper(ch, vict)) {
        send_to_char("Maybe that's not such a good idea.\r\n", ch);
        return ch;
      }

      return vict;
    }

    room = nextroom;
    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else nextroom = NOWHERE;
  }

  return NULL;
}

int spell_damage_door(struct char_data *ch, spell_t *spell, char *target)
{
  int dir;
  char type[MAX_INPUT_LENGTH], door[MAX_INPUT_LENGTH];
  bool found = FALSE;

  two_arguments(target, type, door);

  if (!spell->physical) {
    send_to_char("You can only cast that spell on living targets.\r\n", ch);
    return -1;
  }

  if (*door) {
    if ((dir = search_block(door, lookdirs, FALSE)) == -1)
      return 0;
    dir = convert_look[dir];

    int dist, nextroom, sight = find_sight(ch), room = ch->in_room;

    if (EXIT(ch, dir) && EXIT(ch, dir)->keyword &&
        isname(type, EXIT(ch, dir)->keyword) &&
        !IS_SET(EXIT(ch, dir)->exit_info, EX_DESTROYED)) {
      if (FIGHTING(ch)) {
        send_to_char("Maybe you'd better wait...\r\n", ch);
        return -1;
      } else if (!IS_SET(EXIT(ch, dir)->exit_info, EX_CLOSED)) {
        send_to_char("You can only damage closed doors!\r\n", ch);
        return -1;
      } else if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_PEACEFUL)) {
        send_to_char("Nah - leave it in peace.\r\n", ch);
        return -1;
      } else if (ROOM_FLAGGED(ch->in_room, ROOM_NOMAGIC)) {
        send_to_char(ch, "Your spell vanishes before reaching the %s.\r\n",
                     EXIT(ch, dir)->keyword ? fname(EXIT(ch, dir)->keyword)
                     : "door");
        return -1;
      }

      if (spell->category == SPELL_CATEGORY_MANIPULATION)
        damage_door(ch, ch->in_room, dir, (int)(spell->force),
                    spell->effect | DAMOBJ_MANIPULATION);
      else 
	damage_door(ch, ch->in_room, dir, (int)(spell->force),
		    spell->effect);
      return 1;
    }

    if (CAN_GO2(room, dir))
      nextroom = EXIT2(room, dir)->to_room;
    else
      nextroom = NOWHERE;

    for (dist = 1; nextroom != NOWHERE && dist <= sight; dist++) {
      if (EXIT2(nextroom, dir) && EXIT2(nextroom, dir)->keyword &&
          isname(type, EXIT2(nextroom, dir)->keyword) &&
          !IS_SET(EXIT2(nextroom, dir)->exit_info, EX_DESTROYED)) {
        if (FIGHTING(ch)) {
          send_to_char("Maybe you'd better wait...\r\n", ch);
          return -1;
        } else if (!IS_SET(EXIT2(nextroom, dir)->exit_info, EX_CLOSED)) {
          send_to_char("You can only damage closed doors!\r\n", ch);
          return -1;
        } else if (IS_SET(ROOM_FLAGS(nextroom), ROOM_PEACEFUL)) {
          send_to_char("Nah - leave it in peace.\r\n", ch);
          return -1;
        } else if (ROOM_FLAGGED(nextroom, ROOM_NOMAGIC)) {
          send_to_char(ch, "Your spell vanishes before reaching the %s.\r\n",
                       EXIT2(nextroom, dir)->keyword ?
                       fname(EXIT2(nextroom, dir)->keyword) : "door");
          return -1;
        }

        if (spell->category == SPELL_CATEGORY_MANIPULATION)
          damage_door(ch, nextroom, dir, (int)(spell->force),
                      spell->effect | DAMOBJ_MANIPULATION);
        else damage_door(ch, nextroom, dir, (int)(spell->force),
                         spell->effect);
        return 1;
      }
      room = nextroom;
      if (CAN_GO2(room, dir))
        nextroom = EXIT2(room, dir)->to_room;
      else nextroom = NOWHERE;
    }
  } else if (*type) {
    for (dir = 0; dir < NUM_OF_DIRS; dir++)
      if (EXIT(ch, dir) && EXIT(ch, dir)->keyword &&
          isname(type, EXIT(ch, dir)->keyword) &&
          !IS_SET(EXIT(ch, dir)->exit_info, EX_DESTROYED)) {
        if (FIGHTING(ch)) {
          send_to_char("Maybe you'd better wait...\r\n", ch);
          return -1;
        } else if (!IS_SET(EXIT(ch, dir)->exit_info, EX_CLOSED)) {
          send_to_char("You can only damage closed doors!\r\n", ch);
          return -1;
        } else if (IS_SET(ROOM_FLAGS(ch->in_room), ROOM_PEACEFUL)) {
          send_to_char("Nah - leave it in peace.\r\n", ch);
          return -1;
        }

        if (spell->category == SPELL_CATEGORY_MANIPULATION)
          damage_door(ch, ch->in_room, dir, (int)(spell->force),
                      spell->effect | DAMOBJ_MANIPULATION);
        else damage_door(ch, ch->in_room, dir, (int)(spell->force),
                         spell->effect);
        return 1;
      }
  }

  return 0;
}

// the processing of the spells follow by category
bool process_combat_target(char_t *ch, spell_t *spell, char *t, int force)
{
  char_t *vict = NULL;
  obj_t *obj = NULL;
  char *temp, targ1[80], targ2[80];
  int num_targets=0;
  sh_int room_num;
  int val;

  if (t)
    skip_spaces(&t);

  // first we find the appropriate target
  switch (spell->target) {
    case SPELL_TARGET_CASTER:
      if (t && strcmp(t, GET_NAME(ch))) {
        send_to_char("You can only target yourself with this spell.\r\n", ch);
        return FALSE;
      }
      process_combat_spell(ch, ch, (obj_t *)NULL, spell, force);
      break;
    case SPELL_TARGET_TOUCH:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        // here we look for a mob in the room first, then search for an item
        // in the room, then for an item in inventory
        if (!(vict = get_char_room_vis(ch, t)))
          if (!(val = spell_damage_door(ch, spell, t))) {
            if (!(obj = get_obj_in_list_vis(ch, t, world[ch->in_room].contents)))
              if (!(obj = get_obj_in_list_vis(ch, t, ch->carrying))) {
                send_to_char("You can't seem to find the target you are "
                             "looking for.\r\n", ch);                
                return FALSE;
              }
          } else return (val == 1 ? TRUE : FALSE);
      }
      if (vict && GET_POS(vict) == POS_STANDING) {
        if (resisted_test(GET_QUI(ch),
			  GET_QUI(vict) + modify_target(ch),
			  GET_QUI(vict),
			  GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      } else if (vict && AWAKE(vict)) {
        if (resisted_test(GET_QUI(ch),
			  (GET_QUI(vict) / 2) + modify_target(ch),
			  (GET_QUI(vict) / 2),
			  GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_combat_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_combat_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_combat_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_RANGE:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        any_one_arg(any_one_arg(t, targ1), targ2);
        // if there's not another argument in targ2, we assume it's not
        // an object on a mob
        if (!*targ2) {
          if (!(vict = get_char_room_vis(ch, targ1)))
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(obj = get_obj_in_list_vis(ch, targ1, world[ch->in_room].contents)) &&
                  !(obj = get_obj_in_list_vis(ch, targ1, ch->carrying))) {
                send_to_char("You can't seem to find the target you are "
                             "looking for.\r\n", ch);
                return FALSE;
              }
            } else return (val == 1 ? TRUE : FALSE);
        } else {
        /* if there's a targ2, we know it's either <obj> <victim>, <victim> <direction>
           or <door> <direction> */
          if (!(vict = range_spell(ch, targ1, targ2))) {
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(vict = get_char_room_vis(ch, targ2))) {
                send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
                return FALSE;
              } else {
              // then we search for the item equiped by the victim
                for (register int i = 0; i < NUM_WEARS; ++i)
                  if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
                    obj = GET_EQ(vict, i);
                    break; // and get out when we do
                  }
              }
            } else return (val == 1 ? TRUE : FALSE);
          } else if (vict == ch)
            return FALSE;
        }
        // finally, we make sure they live up to the restriction that
        // mana spells may only be cast on living targets.
        if (!spell->physical && obj) {
          send_to_char("You can only cast that spell on living targets.\r\n", ch);
          return FALSE;
        }
        // at the end of all this, we have an object pointed to by *obj if
        // available, and at minimum a victim pointed to by *vict
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_combat_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_combat_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_combat_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_AREA:
      // here we run through a loop and damage lots of folks (=
      // also, you can't single out objects with area effects spells
      any_one_arg(any_one_arg(t, targ1), targ2);
      if (!t) {
	vict = ch;
      }
      else {
        any_one_arg(any_one_arg(t, targ1), targ2);
        // if there's not another argument in targ2, we assume it's not
        // an object on a mob
        if (!*targ2) {
          if (!(vict = get_char_room_vis(ch, targ1)))
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(obj = get_obj_in_list_vis(ch, targ1, world[ch->in_room].contents)) &&
                  !(obj = get_obj_in_list_vis(ch, targ1, ch->carrying))) {
                send_to_char("You can't seem to find the target you are "
                             "looking for.\r\n", ch);
                return FALSE;
              }
            } else return (val == 1 ? TRUE : FALSE);
        } else {
        /* if there's a targ2, we know it's either <obj> <victim>, <victim> <direction>
           or <door> <direction> */
          if (!(vict = range_spell(ch, targ1, targ2))) {
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(vict = get_char_room_vis(ch, targ2)) 
		  ||!(vict = get_char_room_vis(ch, targ1))) {
                send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
                return FALSE;
              } else {
              // then we search for the item equiped by the victim
                for (register int i = 0; i < NUM_WEARS; ++i)
                  if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
                    obj = GET_EQ(vict, i);
                    break; // and get out when we do
                  }
              }
            } else return (val == 1 ? TRUE : FALSE);
          } else if (vict == ch)
            return FALSE;
        }
        // finally, we make sure they live up to the restriction that
        // mana spells may only be cast on living targets.
        if (!spell->physical && obj) {
          send_to_char("You can only cast that spell on living targets.\r\n", ch);
          return FALSE;
        }
        // at the end of all this, we have an object pointed to by *obj if
        // available, and at minimum a victim pointed to by *vict
      }
      
      if ( !vict )
	{
	  send_to_char("Your spell cannot find that target.\r\n", ch);
          return FALSE;
        }

      room_num = vict->in_room;

      struct char_data *tch, *next_tch;
      num_targets = 0;
      for (tch = world[room_num].people; tch; tch = next_tch) {
        next_tch = tch->next_in_room;
        if (tch == ch)
          continue;
        if (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_LEGEND)
          continue;
        if (!pk_allowed && !IS_NPC(tch) && !IS_NPC(ch))
          continue;
        if (in_group(ch, tch))
          continue;
	num_targets++;
        if (!IS_ASTRAL(ch) && !IS_ASTRAL(tch))
          process_combat_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(tch) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
           (PLR_FLAGGED(ch, PLR_PERCEIVE) && tch == FIGHTING(ch))))
          process_combat_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(ch) && (IS_ASTRAL(tch) || IS_DUAL(tch) ||
           PLR_FLAGGED(tch, PLR_PERCEIVE)))
          process_combat_spell(ch, tch, (obj_t *) NULL, spell, force);
	else
	  num_targets--;
      }
      struct obj_data *obj, *next;
      if (spell->physical) {
        for (obj = world[room_num].contents; obj; obj = next) {
          next = obj->next_content;
          damage_obj(NULL, obj, force + FORCE_PENALTY(spell), spell->effect);
	  num_targets++;
        }
        for (val = 0; val < (NUM_OF_DIRS - 1); val++)
          if (EXIT(ch, val) && EXIT(ch, val)->keyword && 
              IS_SET(EXIT(ch, val)->exit_info, EX_CLOSED))
	    {
	      damage_door(NULL, ch->in_room, val, (int)(force), spell->effect);
	      num_targets++;
	    }
      }
      if ( num_targets == 0 )
	{
	  send_to_char("Your area affect spell would hit nothing.\r\n", ch );
	  return FALSE;
	}
      break;
    default:
      send_to_char("You can't seem to target this spell for some reason.\r\n", ch);
      mudlog("Illegal target in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return FALSE;
  }

  return TRUE;
}

  // now for the fun part
void process_combat_spell(char_t *ch, char_t *vict, obj_t *obj, spell_t *spell, int force)
{
  struct obj_data *next;
  int resist = 0, success = 0, dam, i, chance;
  int pbonus, fbonus, tbonus;
  int tnum;
  int rsuccess;

  if (!obj) {
    char xbuf[MAX_STRING_LENGTH];
    // figure out if the spell is physical or mana
    if (spell->physical)
      resist = GET_BOD(vict);
    else
      resist = GET_WIL(vict);

    tbonus = totem_bonus(ch, spell);
    pbonus = magic_pool_bonus(ch, spell, force, TRUE);
    fbonus = foci_bonus(ch, spell, force, TRUE);

    xbuf[0] = ' ';
    xbuf[1] = 0;
    tnum = modify_target_rbuf(ch, xbuf);

    // we include totem bonuses and target modifiers to the test
    success = success_test(force + pbonus + fbonus + tbonus, resist + tnum );

    if (access_level(ch, LVL_OWNER) && spell->target != SPELL_TARGET_AREA)
      send_to_char(ch, "(%s) F: %d, R: %d, S: %d, D: %d\r\n",
        spell->physical ? "Physical" : "Mana", force, resist, success, 0);

    if ( 1 )
      {
	char rbuf[MAX_STRING_LENGTH];
	sprintf(rbuf,
		"ComSpell: F:%d/%d,%s%s Mag:%d, FFPT/RM %d+%d+%d+%d/%d+%d.  %d.%s",
		force, spell->force,
		spell->target == SPELL_TARGET_AREA ? " (area)" : "",
		spell->effect != SPELL_ELEMENT_NONE ? " (elem)" : "",
		GET_MAG(ch),
		force, fbonus, pbonus, tbonus,
		resist, tnum, success,
		xbuf);
	act(rbuf, 1, ch, NULL, NULL, TO_ROLLS);
      }
    if (success < 1 && AWAKE(vict)) {
      damage(ch, vict, 0, spell->type, FALSE);
      ranged_response(ch, vict);
      return;
    }

    if (spell->effect != SPELL_ELEMENT_NONE) {
      elemental_damage(ch, vict, spell, force, success);
      return;
    }

    int srvict = spell_resist(vict);
    int mtvict = modify_target(vict);

    rsuccess = success_test(resist + spell_resist(vict),
			    force + modify_target(vict));

    int basedamage;

    if (IS_ASTRAL(ch))
      basedamage = DRAIN_LEVEL(grimoire[spell->type].drain);
    else
      basedamage = grimoire[spell->type].damage;

    // here we stage the damage up or down
    int stagedamage = stage(success - rsuccess, basedamage);

    dam = convert_damage(stagedamage);

    int is_physicaldam = (((spell->type >= SPELL_STUNBALL) 
			   && (spell->type <= SPELL_STUN_TOUCH))
			  ||((spell->type >= SPELL_STUN_SHAFT) 
			   && (spell->type <= SPELL_STUN_DART)))
                         ? FALSE : TRUE;
    
    if (1)
      {
	char rbuf[MAX_STRING_LENGTH];
	sprintf( rbuf,
		 "ComSpell: %c RSr/FM %d+%d/%d+%d. %d-%d=%d, %d->%d.  %d/%d.  D: %d%c.",
		 spell->physical ? 'B' : 'W',
		 resist, srvict,
		 force, mtvict,
		 success, rsuccess, success-rsuccess,
		 basedamage, stagedamage,
		 GET_PHYSICAL(vict), GET_MENTAL(vict),
		 dam,
		 is_physicaldam ? 'P' : 'M' );

	act( rbuf, 1, ch, NULL, NULL, TO_ROLLS );
      }
    
    if (spell->physical) {
      if (spell->target == SPELL_TARGET_AREA)
	chance = 10;
      else
	chance = 2;
      for (i = 0; i < (NUM_WEARS - 1); i++)
        if (GET_EQ(vict, i) && number(1, 100) < chance) {
          damage_obj(NULL, GET_EQ(vict, i), force + FORCE_PENALTY(spell),
		     DAMOBJ_PROJECTILE);
          if (spell->target != SPELL_TARGET_AREA)
            break;
        }
      chance = (int)(chance / 2);
      for (obj = vict->carrying; obj; obj = next) {
        next = obj->next_content;
        if (number(1, 100) < chance)
          damage_obj(NULL, obj, force + FORCE_PENALTY(spell),
		     DAMOBJ_PROJECTILE);
      }
    }
    if (((spell->type >= SPELL_STUNBALL) && (spell->type <= SPELL_STUN_TOUCH))
	||((spell->type >= SPELL_STUN_SHAFT)
	   && (spell->type <= SPELL_STUN_DART)))
      damage(ch, vict, dam, spell->type, FALSE);
    else
      damage(ch, vict, dam, spell->type, TRUE);

    if (ch->in_room != vict->in_room)
      ranged_response(ch, vict);
  } else {   // end combat section to living targets
    // this is how magic affects objects
    // first we find the target number for the spell
    damage_obj(ch, obj, force + FORCE_PENALTY(spell), spell->effect);

    // gotta make em attack if they are trying to blow up an object on a mob
    if (vict && IS_NPC(vict) && !FIGHTING(vict))
      set_fighting(vict, ch);

    if (access_level(ch, LVL_OWNER) && spell->target != SPELL_TARGET_AREA)
      send_to_char(ch, "(%s) F: %d, R: %d, S: %d, D: %d\r\n",
                 spell->physical ? "Physical" : "Mana", force, resist, success, 0);
  }
}

void elemental_damage(char_t *ch, char_t *vict, spell_t *spell, int force, int success)
{
  // if the spell doesn't do greater than light damage, it won't have
  // secondary effects -- ie, the mage has to take a little risk at least

  if (spell->effect == SPELL_ELEMENT_NONE
      || DRAIN_LEVEL(spell->drain) < MODERATE)
    return;

  int modifier = 0, resist, dam, i, chance, num;

  if (spell->category == SPELL_CATEGORY_COMBAT)
    modifier = 4;
  else switch (DRAIN_LEVEL(spell->drain)) {
    case 2: modifier = 4; break;
    case 3: modifier = 2; break;
    case 4: modifier = 0; break;
    default: modifier = 4;
  }

  if (spell->effect == SPELL_ELEMENT_ICE)
    resist = GET_IMPACT(vict);
  else resist = GET_IMPACT(vict) >> 1;

  success -= success_test(resist, force + modify_target(vict));
  if (IS_ASTRAL(ch))
    dam = convert_damage(stage(success, grimoire[spell->type].drain % 10));
  else dam = convert_damage(stage(success, grimoire[spell->type].damage));

  if (spell->category == SPELL_CATEGORY_COMBAT)
    num = 0;
  else num = DAMOBJ_MANIPULATION;

  num += spell->effect;

  if (success > 0) {
    struct obj_data *obj, *next;
    if (spell->physical) {
      if (spell->target == SPELL_TARGET_AREA)
        chance = 10;
      else chance = 2;
      for (i = 0; i < (NUM_WEARS - 1); i++)
        if (GET_EQ(vict, i) && number(1, 100) < chance) {
          damage_obj(NULL, GET_EQ(vict, i), 
		     force + FORCE_PENALTY(spell), num);
          if (spell->target != SPELL_TARGET_AREA)
            break;
        }
      chance = (int)(chance / 2);
      for (obj = vict->carrying; obj; obj = next) {
        next = obj->next_content;
        if (number(1, 100) < chance)
          damage_obj(NULL, obj, force + FORCE_PENALTY(spell), num);
      }
    }
    damage(ch, vict, dam, spell->type, TRUE);

    ranged_response(ch, vict);
  }
}

// finds the target for the detection spells
bool process_detection_target(char_t *ch, spell_t *spell, char *t, int force)
{
  char_t *vict = NULL;
  obj_t *obj = NULL;
  char *temp, targ1[80], targ2[80];

  if (t)
    skip_spaces(&t);
  // first we find the appropriate target
  switch (spell->target) {
    case SPELL_TARGET_CASTER:
      if (t && strcmp(t, GET_NAME(ch))) {
        send_to_char("You can only target yourself with this spell.\r\n", ch);
        return FALSE;
      }
      process_combat_spell(ch, ch, (obj_t *)NULL, spell, force);
      break;
    case SPELL_TARGET_TOUCH:
      if (FIGHTING(ch)) {
        send_to_char("You can't cast this spell during combat.\r\n", ch);
        return FALSE;
      }
      if (!t) {
        send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
        return FALSE;
      } else {
        // for detection spells, we search the room for a character first,
        // then for an object in inventory, then an object in the room
        if (!(vict = get_char_room_vis(ch, t)))
          if (!(obj = get_obj_in_list_vis(ch, t, ch->carrying)))
            if (!(obj = get_obj_in_list_vis(ch, t, world[ch->in_room].contents))) {
              send_to_char("You can't seem to find the target you wish to cast this spell on.\r\n", ch);
              return FALSE;
            }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         PLR_FLAGGED(ch, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_RANGE:
      if (!t) {
        send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
        return FALSE;
      }
      any_one_arg(any_one_arg(t, targ1), targ2);
      if (!*targ2) {
        if (!(vict = get_char_room_vis(ch, targ1))) {
          send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
          return FALSE;
        }
      } else {
        if (!(vict = range_spell(ch, targ1, targ2))) {
	  if (!(vict = get_char_room_vis(ch, targ2))
	      ||!(vict = get_char_room_vis(ch, targ1))) {
            send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
            return FALSE;
          } else {
            for (register int i = 0; i < NUM_WEARS; ++i)
              if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
                obj = GET_EQ(vict, i);
                break;
              }
          }
        } else if (vict == ch)
          return FALSE;
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         PLR_FLAGGED(ch, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_AREA:
      if (!t) {
        send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
        return FALSE;
      }

      any_one_arg(any_one_arg(t, targ1), targ2);
      // if there's not another argument in targ2, we assume it's not
      // an object on a mob
      if (!*targ2) {
        if (!(vict = get_char_room_vis(ch, targ1)) &&
           !(obj = get_obj_in_list_vis(ch, targ1, ch->carrying)) &&
           !(obj = get_obj_in_list_vis(ch, targ1, world[ch->in_room].contents))) {
          send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
          return FALSE;
        }
      } else {  // if there's a targ2, we know it's in the form 'spell' <obj> <mob>
        // here we grab the pointer to the victim if it's there
        if (!(vict = get_char_room_vis(ch, targ2))) {
          send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
          return FALSE;
        } else { // then we search for the item equiped by the victim
          for (register int i = 0; i < NUM_WEARS; ++i)
            if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
              obj = GET_EQ(vict, i);
              break; // and get out when we do
            }
        }
      }

      if (!spell->physical && obj) {
        send_to_char("You can only cast that spell on living targets.\r\n", ch);
        return FALSE;
      }

      // there's not really any area detection spells, but if I ever come
      // up with some, I'll need to set up a loop here to proccess it
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         PLR_FLAGGED(ch, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_detection_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    default:
      send_to_char("You can't seem to target this spell for some reason.\r\n", ch);
        mudlog("Illegal target in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return FALSE;
  }

  return TRUE;
}

// and finally for the fun part of the detection effects
void process_detection_spell(char_t *ch, char_t * vict, obj_t *obj, spell_t *spell, int force)
{
  static int success;
  static int resist;
  static bool found;
  static struct affected_type af, af2;

  switch (spell->type) {
    case SPELL_ANALYZE_DEVICE:
      if (!obj) {
        send_to_char("You realize as you cast the spell that it only works on inanimate objects.\r\n", ch);
        return;
      }
      // resistance is based off the material of the object
      resist = material_ratings[GET_OBJ_MATERIAL(obj)];
      success = success_test(force + spell_bonus(ch, spell), resist + modify_target(ch));
      // if there were no successes, we just return
      if (success == 0) {
        send_to_char("You seem unable to learn anything new about it.\r\n", ch);
        return;
      }
      if (success >= 1) {
        send_to_char(ch, "%s is %s %s weighing %d.\r\n", CAP(obj->short_description),
             AN(item_types[obj->obj_flags.type_flag]),
             item_types[obj->obj_flags.type_flag], obj->obj_flags.weight);
        if (GET_OBJ_TYPE(obj) != ITEM_FOOD)
          send_to_char(ch, "It appears to be made of %s.\r\n", material_names[GET_OBJ_MATERIAL(obj)]);
      }
      if (success >= 2) {
        sprintbit(obj->obj_flags.wear_flags, wear_bits, buf1);
        send_to_char(ch, "It is useable in the following positions: %s\r\n", buf1);
        send_to_char(ch, "It has a durability rating of %d.\r\n", GET_OBJ_BARRIER(obj));
      }
      if (success >= 3) {
        if (obj->obj_flags.bitvector) {
          sprintbit(obj->obj_flags.bitvector, affected_bits, buf1);
          send_to_char(ch, "It grants following abilities: %s\r\n", buf1);
        }
        send_to_char("It has the following affections:", ch);
        for (register int i = 0; i < MAX_OBJ_AFFECT; i++)
          if (obj->affected[i].modifier) {
            strcpy(buf2, apply_types[obj->affected[i].location]);
            send_to_char(ch, "%s %+d to %s", found++ ? "," : "",
                         obj->affected[i].modifier, buf2);
          }
        if (!found)
          send_to_char(" None\r\n", ch);
        else send_to_char("\r\n", ch);
      }
      if (success >= 4) {
        switch (GET_OBJ_TYPE(obj)) {
          case ITEM_LIGHT:
            if (GET_OBJ_VAL(obj, 2) < 0)
              send_to_char("Light is permanent.\r\n", ch);
            else send_to_char(ch, "Hours left for light: %d\r\n", GET_OBJ_VAL(obj, 2));
            break;
          case ITEM_WEAPON:
            send_to_char(ch, "Power: %s, Damage: %s, Skill needed: %s\r\n",
               get_power(GET_OBJ_VAL(obj, 0)), wound_name[GET_OBJ_VAL(obj, 1)],
               spells[GET_OBJ_VAL(obj, 4)]);
            break;
          case ITEM_FIREWEAPON:
            send_to_char(ch, "Str. Minimum: %d, Str+: %d, Power: %d, Skill needed: %s\r\n",
               GET_OBJ_VAL(obj, 6), GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 0), spells[GET_OBJ_VAL(obj, 4)]);
            break;
          case ITEM_ARMOR:
            send_to_char(ch, "Ballistic rating: %d, Impact Rating: %d\r\n",
                GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1));
            break;
          case ITEM_CONTAINER:
            send_to_char(ch, "Max weight containable: %d pounds\r\n", GET_OBJ_VAL(obj, 0));
            break;
          case ITEM_DRINKCON:
            send_to_char(ch, "Max contains: %d, contains: %d, poisoned: %s, "
                        "liquid: %s\r\n", GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1),
                        GET_OBJ_VAL(obj, 3) ? "Yes" : "No", drinks[GET_OBJ_VAL(obj, 2)]);
            break;
          case ITEM_FOOD:
            send_to_char(ch, "Filling for %d hours, poisoned: %s\r\n",
                         GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 3) ? "Yes" : "No");
            break;
          case ITEM_WORKING_GEAR:
            send_to_char(ch, "Type: %s\r\n", gear_name[GET_OBJ_VAL(obj, 0)]);
            break;
          case ITEM_QUIVER:
            send_to_char(ch, "Max contains: %d, Type contains: %s\r\n", GET_OBJ_VAL(obj, 0),
                 (GET_OBJ_VAL(obj, 1) == 0 ? "Arrow" : (GET_OBJ_VAL(obj, 1) == 1 ? "Bolt" :
                 (GET_OBJ_VAL(obj, 1) == 2 ? "Shuriken" : (GET_OBJ_VAL(obj, 1) == 3 ? "Throwing knife" :
                 "Undefined")))));
            break;
          case ITEM_PATCH:
            send_to_char(ch, "Type: %s, Rating: %d\r\n",
                         patch_names[GET_OBJ_VAL(obj, 0)], GET_OBJ_VAL(obj, 1));
            break;
          case ITEM_CYBERDECK:
            send_to_char(ch, "MPCP: %d, Hardening: %d, Active: %d, Storage: %d, Load: %d\r\n",
                         GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2),
                         GET_OBJ_VAL(obj, 3), GET_OBJ_VAL(obj, 4));
            break;
          case ITEM_PROGRAM:
            if (GET_PROG_TYPE(obj) == PROG_ATTACK)
              sprintf(buf2, " DamType: %s, ", attack_types[GET_OBJ_VAL(obj, 3) - TYPE_HIT]);
            else sprintf(buf2, " ");
            send_to_char(ch, "Type: %s, Rating: %d, Size: %d,%sInstalled?: %s\r\n",
                         program_types[GET_OBJ_VAL(obj, 0)], GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2), buf2,
                        (GET_OBJ_VAL(obj, 6) ? "yes" : "no"));
            break;
          case ITEM_POTION: case ITEM_SCROLL: case ITEM_WAND: case ITEM_STAFF: case ITEM_FOCUS:
          case ITEM_SPELL_FORMULA:
            send_to_char(ch, "%s cannot ascertain detailed information about magical objects.\r\n",
                         spell->name);
            break;
        }
      }
      break;
    case SPELL_CLAIRVOYANCE:
      if (obj) {
        send_to_char("View what an item sees?\r\n", ch);
        return;
      }
      GET_WAS_IN(ch) = ch->in_room;
      char_from_room(ch);
      char_to_room(ch, vict->in_room);
      look_at_room(ch, 1);
      char_from_room(ch);
      char_to_room(ch, GET_WAS_IN(ch));
      GET_WAS_IN(ch) = NOWHERE;
      break;
    case SPELL_COMBAT_SENSE:
      if (obj) {
        send_to_char("That spell can only be cast on living targets.\r\n", ch);
        return;
      } else sustain_spell(force, ch, vict, spell, 0);
      break;
    case SPELL_DETECT_ALIGNMENT:
      if (obj) {
        send_to_char("Items are most definately neutral.\r\n", ch);
        return;
      } else sustain_spell(force, ch, vict, spell, 0);
      break;
    case SPELL_DETECT_INVIS:
      if (obj) {
        send_to_char("That just doesn't make sense.\r\n", ch);
        return;
      } else sustain_spell(force, ch, vict, spell, 0);
      break;
    case SPELL_DETECT_MAGIC:
      if (obj) {
        send_to_char("That just doesn't make sense.\r\n", ch);
        return;
      } else sustain_spell(force, ch, vict, spell, 0);
      break;
    case SPELL_ANALYZE_MAGIC:
      if (obj) {
        switch (GET_OBJ_TYPE(obj)) {
          case ITEM_SPELL_FORMULA:
            send_to_char(ch, "(%s) Category: %s, Force: %d, Target: %s, Drain %d%s\r\n"
                         "Damage: %s, Type: %s, Effect: %s, For: %s",
                        (GET_OBJ_VAL(obj, 0) ? "Physical" : "Mana"),
                        spell_categories[GET_OBJ_VAL(obj, 1)], GET_OBJ_VAL(obj, 2),
                        spell_target[GET_OBJ_VAL(obj, 3)],
                        ((GET_OBJ_VAL(obj, 2) >> 1) + (int)(GET_OBJ_VAL(obj, 4) / 10)),
                        wound_arr[GET_OBJ_VAL(obj, 4) % 10], wound_arr[GET_OBJ_VAL(obj, 5)],
                        spells[GET_OBJ_VAL(obj, 6)], elements[GET_OBJ_VAL(obj, 8)],
                        (GET_OBJ_VAL(obj, 7) ? "Shaman" : "Mage"));
            break;
          case ITEM_FOCUS:
            if (GET_OBJ_VAL(obj, VALUE_FOCUS_TYPE) == FOCI_LOCK)
              send_to_char(ch, "Spell lock, Spell: %s, Bonded by: %s",
			   (!GET_OBJ_VAL(obj, 8)
			    ? "None"
			    : spells[GET_OBJ_VAL(obj, 8)]),
			   GET_OBJ_VAL(obj, 9) > 0
			   ? get_name_by_id(GET_OBJ_VAL(obj, 9))
			   : "No one");
            else
	      sprintf(buf, "%s, Rating: %d, Spec.: %d, Bonded by: %s",
		      foci_types[GET_OBJ_VAL(obj, 0)],
                      GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 8),
                      GET_OBJ_VAL(obj, 9) > 0 
		      ? get_name_by_id(GET_OBJ_VAL(obj, 9))
		      : "No one");
            break;
          case ITEM_SCROLL: case ITEM_POTION:
            send_to_char(ch, "Force: %d, Spell: %s", GET_OBJ_VAL(obj, 0), spells[GET_OBJ_VAL(obj, 3)]);
            break;
          case ITEM_WAND: case ITEM_STAFF:
            send_to_char(ch, "Force: %d, Max: %d, Remaining: %d, Spell: %s", GET_OBJ_VAL(obj, 0),
                         GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2), spells[GET_OBJ_VAL(obj, 3)]);
            break;
        }
      } else send_to_char("You need to target an object with this spell.\r\n", ch);
      break;
    case SPELL_ANALYZE_PERSON:
      assert(vict);
      if (spell->physical)
        resist = GET_BOD(vict);
      else resist = GET_WIL(vict);
      success = resisted_test(force + spell_bonus(ch, spell), resist + modify_target(ch),
                             resist + spell_resist(vict), force + modify_target(vict));
      if (success <= 1) {
        send_to_char("You don't learn anything new.\r\n", ch);
        return;
      }
      if (success >= 2) {
        switch (GET_SEX(vict)) {
          case SEX_MALE: strcpy(buf1, "He"); break;
          case SEX_FEMALE: strcpy(buf1, "She"); break;
          default: strcpy(buf1, "It"); break;
        }
        send_to_char(ch, "%s has %s strength, %s quickness, and %s body.\r\n",
                buf1, get_attrib(GET_STR(vict)), get_attrib(GET_QUI(vict)),
                get_attrib(GET_BOD(vict)));
      }
      if (success >= 3) {
        send_to_char(ch, "%s has %s charisma, %s intelligence, %s willpower.\r\n",
                buf1, get_attrib(GET_CHA(vict)), get_attrib(GET_INT(vict)),
                get_attrib(GET_WIL(vict)));
      }
      if (success >= 5) {
        send_to_char(ch, "%s has %s reaction, %s magic.\r\n",
                buf1, get_attrib(GET_REA(vict)), get_attrib(GET_MAG(vict) / 100));
      }
      if (success >= 7) {
        send_to_char(ch, "%s knows:\r\n", buf1);
        for (int i = 100; i < MAX_SKILLS; i++)
          if (GET_SKILL(vict, i) > 0)
            send_to_char(ch, "%s\r\n", spells[i]);
      }
      break;
    default:
      send_to_char("Your spell fizzles into oblivion.\r\n", ch);
        mudlog("Illegal type in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return;
  }
}

int process_health_target(char_t *ch, spell_t *spell, char *t, int force)
{
  char_t *vict;
  char *temp, targ1[80], targ2[80], targ3[80];
  int mode = 1;

  if (t)
    skip_spaces(&t);

  // first we find the appropriate target
  switch (spell->target) {
    case SPELL_TARGET_CASTER:
      if (spell->type == SPELL_HEAL || spell->type == SPELL_ANTIDOTE || spell->type == SPELL_RESIST_PAIN ||
           spell->type == SPELL_CURE_DISEASE || spell->type == SPELL_POISON) {
        any_one_arg(t, targ1);
        if (!targ1) {
          send_to_char("At what level would you like to cast this spell?\r\n", ch);
          return FALSE;
        }
        if (is_abbrev(targ1, "light"))
          mode = LIGHT;
        else if (is_abbrev(targ1, "moderate"))
          mode = MODERATE;
        else if (is_abbrev(targ1, "serious"))
          mode = SERIOUS;
        else if (is_abbrev(targ1, "deadly"))
          mode = DEADLY;
        else {
          send_to_char("At what level would you like to cast this spell?\r\n", ch);
          return FALSE;
        }
      }
      if (t && strcmp(t, GET_NAME(ch))) {
        send_to_char("You can only target yourself with this spell.\r\n", ch);
        return FALSE;
      }
      if (mode == DEADLY && spell->type == SPELL_RESIST_PAIN) {
        send_to_char("You can't resist the effects of deadly wounds.\r\n", ch);
        return FALSE;
      }
      process_health_spell(ch, ch, mode, spell, force);
      break;
    case SPELL_TARGET_TOUCH:
      if (spell->type == SPELL_HEAL || spell->type == SPELL_ANTIDOTE ||
          spell->type == SPELL_RESIST_PAIN ||
          spell->type == SPELL_CURE_DISEASE || spell->type == SPELL_POISON) {
        any_one_arg(any_one_arg(t, targ1), targ2);
        if (!targ1 || !targ2) {
          send_to_char("Who do you wish to cast this spell on?\r\n", ch);
          return FALSE;
        }
        if (is_abbrev(targ1, "light"))
          mode = LIGHT;
        else if (is_abbrev(targ1, "moderate"))
          mode = MODERATE;
        else if (is_abbrev(targ1, "serious"))
          mode = SERIOUS;
        else if (is_abbrev(targ1, "deadly"))
          mode = DEADLY;
        else {
          send_to_char("At what level would you like to cast this spell?\r\n", ch);
          return FALSE;
        }
      } else any_one_arg(t, targ2);
      if (!(vict = get_char_room_vis(ch, targ2))) {
        send_to_char("You can't seem to find the target you wish to cast "
          "this spell on.\r\n", ch);
        return FALSE;
      }
      if (mode == DEADLY && spell->type == SPELL_RESIST_PAIN) {
        send_to_char("You can't resist the effects of deadly wounds.\r\n", ch);
        return FALSE;
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_health_spell(ch, vict, mode, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         PLR_FLAGGED(ch, PLR_PERCEIVE)))
        process_health_spell(ch, vict, mode, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_health_spell(ch, vict, mode, spell, force);
      else {
        if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_RANGE:
      if (spell->type == SPELL_HEAL || spell->type == SPELL_ANTIDOTE ||
          spell->type == SPELL_RESIST_PAIN ||
          spell->type == SPELL_CURE_DISEASE || spell->type == SPELL_POISON) {
        any_one_arg(any_one_arg(any_one_arg(t, targ1), targ2), targ3);
        if (!targ1 || !targ2) {
          send_to_char("Who do you wish to cast this spell on?\r\n", ch);
          return FALSE;
        }
        if (is_abbrev(targ1, "light"))
          mode = LIGHT;
        else if (is_abbrev(targ1, "moderate"))
          mode = MODERATE;
        else if (is_abbrev(targ1, "serious"))
          mode = SERIOUS;
        else if (is_abbrev(targ1, "deadly"))
          mode = DEADLY;
        else {
          send_to_char("At what level would you like to cast this spell?\r\n", ch);
          return FALSE;
        }
      } else any_one_arg(any_one_arg(t, targ2), targ3);
      if (!targ1 || !targ2) {
        send_to_char("Who do you wish to cast this spell on?\r\n", ch);
        return FALSE;
      }
      if (!*targ3) {
        if (!(vict = get_char_room_vis(ch, targ2))) {
          send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
          return FALSE;
        }
      } else {
        if (!(vict = range_spell(ch, targ2, targ3))) {
          if (!(vict = get_char_room_vis(ch, targ2))) {
            send_to_char("You can't seem to find the target you are looking "
                         "for.\r\n", ch);
            return FALSE;
          }
        } else if (vict == ch)
          return FALSE;
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_health_spell(ch, vict, mode, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         PLR_FLAGGED(ch, PLR_PERCEIVE)))
        process_health_spell(ch, vict, mode, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_health_spell(ch, vict, mode, spell, force);
      else {
        if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_AREA:
      send_to_char("That is not currently a valid spell type.", ch);
      mudlog("Undefined (area or ranged) health spell", ch, LOG_SYSLOG, TRUE);
      return(FALSE);
    default:
      send_to_char("You can't seem to target this spell for some reason.\r\n", ch);
      mudlog("Illegal target in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return FALSE;
  }
  return mode;
}

void process_health_spell(char_t *ch, char_t *vict, int level, spell_t *spell, int force)
{
  int success, time = (int)(level * 5/4);
  int mod = 0, min = 0;
  struct affected_type *af;

  switch (spell->type) {
    case SPELL_ANTIDOTE:
      if (affected_by_spell(vict, SPELL_POISON) != 1) {
        sprintf(buf, "%s not affected by any toxin.", (ch == vict ? "You are" : "$N is"));
        act(buf, FALSE, ch, 0, vict, TO_CHAR);
        return;
      }
      for (af = vict->affected; af; af = af->next)
        if (af->type == SPELL_POISON) {
          if (af->duration > level) {
            send_to_char(NOEFFECT, ch);
            sustain_spell(force, ch, vict, spell, time);
            return;
          }
          success = success_test(force + spell_bonus(ch, spell), af->modifier + modify_target(ch));
          if (!success) {
            send_to_char("You fail to dispel the toxin.\r\n", ch);
            sustain_spell(force, ch, vict, spell, time);
            return;
          }
          time = (int)(time / success);
          send_to_char("Your flows of magic erradicate the toxin.\r\n", ch);
          send_to_char(vict, "%s\r\n", spell_wear_off_msg[af->type]);
          affect_remove(vict, af, 1);
          sustain_spell(force, ch, vict, spell, time);
          return;
        }
      break;
    case SPELL_CURE_DISEASE:
      send_to_char("This spell is useless at the moment.\r\n", ch);
      break;
    case SPELL_HEAL:
      if (level == LIGHT) {
        mod = 1; min = (int)(GET_MAX_PHYSICAL(vict) * 4/500);
      } else if (level == MODERATE) {
        mod = 3; min = (int)(GET_MAX_PHYSICAL(vict) * 3/500);
      } else if (level == SERIOUS) {
        mod = 6; min = (int)(GET_MAX_PHYSICAL(vict) / 1000);
      } else if (level == DEADLY) {
        mod = 10; min = -(int)(GET_MAX_PHYSICAL(vict) / 100);
      }
      success = success_test(force + spell_bonus(ch, spell), (9 - (GET_ESS(vict) / 100)) + modify_target(ch));
      if (success < 1) {
        if (ch == vict)
          sprintf(buf, "You can't get your healing magic to bind with your life force!\r\n");
        else sprintf(buf, "You can't get your healing magic to bind with %s's life force!\r\n", GET_NAME(vict));
        send_to_char(buf, ch);
        sustain_spell(force, ch, vict, spell, time);
        return;
      }
      time = (int)(time / success);
      sustain_spell(force, ch, vict, spell, time);
      if ((int)(GET_PHYSICAL(vict) / 100) < min) {
        sprintf(buf, "Your healing magic isn't powerful enough to help %s.\r\n",
               (ch != vict ? GET_NAME(vict) : "yourself"));
        send_to_char(buf, ch);
        return;
      }
      LAST_HEAL(vict) = -1;
      send_to_char("A warm feeling floods your body.\r\n", vict);
      act("$n appears better.", TRUE, vict, 0, 0, TO_ROOM);
      GET_PHYSICAL(vict) = MIN(GET_MAX_PHYSICAL(vict), GET_PHYSICAL(vict) + (mod * 100));
      update_pos(vict);
      break;
    case SPELL_DECREASE_ATTRIB:
    case SPELL_INCREASE_ATTRIB:
    case SPELL_INCREASE_REFLEXES:
    case SPELL_POISON:
    case SPELL_RESIST_PAIN:
      sustain_spell(force, ch, vict, spell, level);
      break;
    case SPELL_STABILIZE:
      time = 5;
      if (GET_POS(vict) > POS_INCAP) {
        if (ch == vict)
          sprintf(buf, "You aren't in any danger of dying!");
        else sprintf(buf, "$N isn't in any danger of dying!");
        act(buf, FALSE, ch, 0, vict, TO_CHAR);
        return;
      }
      success = success_test(force + spell_bonus(ch, spell), 4 + modify_target(ch) -
                (int)(GET_PHYSICAL(vict) / 100));
      if (success) {
        if (ch == vict)
          log("ERROR: In newmagic.cc, stabilizing self!");
        act("You succeed in stabilizing $N.", FALSE, ch, 0, vict, TO_CHAR);
        act("Your wounds stop bleeding.", FALSE, vict, 0, 0, TO_CHAR);
        SET_BIT(AFF_FLAGS(vict), AFF_STABILIZE);
        time = (int)(time / success);
      } else send_to_char(NOEFFECT, ch);
      break;
    default:
      send_to_char("Your spell fizzles into oblivion.\r\n", ch);
      mudlog("Illegal type in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return;
  }
  return;
}

bool process_illusion_target(char_t *ch, spell_t *spell, char *t, int force)
{
  char_t *vict = NULL;
  obj_t *obj = NULL;
  char *temp, targ1[80], targ2[80];

  if (t)
    skip_spaces(&t);

  switch (spell->target) {
    case SPELL_TARGET_CASTER:
      if (t && strcmp(t, GET_NAME(ch))) {
        send_to_char("You can only target yourself with this spell.\r\n",
                     ch);
        return FALSE;
      }
      process_illusion_spell(ch, ch, (obj_t *)NULL, spell, force);
      break;
    case SPELL_TARGET_TOUCH:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this "
                       "spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        if (!(vict = get_char_room_vis(ch, t)))
          if (!(obj = get_obj_in_list_vis(ch, t, world[ch->in_room].contents)))
            if (!(obj = get_obj_in_list_vis(ch, t, ch->carrying))) {
              send_to_char("You can't seem to find the target you wish to cast this spell on.\r\n", ch);
              return FALSE;
            }
      }
      if (vict && GET_POS(vict) == POS_STANDING && spell->type != SPELL_INVISIBILITY && spell->type != SPELL_IMPROVED_INVIS) {
        if (resisted_test(GET_QUI(ch), GET_QUI(vict) + modify_target(ch), GET_QUI(vict), GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      } else if (vict && spell->type != SPELL_INVISIBILITY && spell->type != SPELL_IMPROVED_INVIS) {
        if (resisted_test(GET_QUI(ch), (GET_QUI(vict) / 2) + modify_target(ch),
            (GET_QUI(vict) / 2), GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_illusion_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_illusion_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_illusion_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_RANGE:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this "
                       "spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        any_one_arg(any_one_arg(t, targ1), targ2);
        if (!*targ2) {
          if (!(vict = get_char_room_vis(ch, targ1)) &&
             !(obj = get_obj_in_list_vis(ch, targ1, world[ch->in_room].contents)) &&
             !(obj = get_obj_in_list_vis(ch, targ1, ch->carrying))) {
            send_to_char("You can't seem to find the target you are looking "
                         "for.\r\n", ch);
            return FALSE;
          }
        } else {
          if (!(vict = range_spell(ch, targ1, targ2))) {
            if (!(vict = get_char_room_vis(ch, targ2))) {
              send_to_char("You can't seem to find the target you are looking "
                           "for.\r\n", ch);
              return FALSE;
            } else {
              for (register int i = 0; i < NUM_WEARS; ++i)
                if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
                  obj = GET_EQ(vict, i);
                  break; // and get out when we do
                }
            }
          } else if (vict == ch)
            return FALSE;
        }
        if (!spell->physical && obj) {
          send_to_char("You can only cast that spell on living targets.\r\n", ch);
          return FALSE;
        }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_illusion_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_illusion_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_illusion_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_AREA:
      struct char_data *tch, *next_tch;
      for (tch = world[ch->in_room].people; tch; tch = next_tch)  {
        next_tch = tch->next_in_room;
        if (tch == ch)
          continue;
        if (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_LEGEND)
          continue;
        if (!pk_allowed && !IS_NPC(tch) && !IS_NPC(ch))
          continue;
        if (in_group(ch, tch))
          continue;
        if (!IS_ASTRAL(ch) && !IS_ASTRAL(tch))
          process_illusion_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(tch) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
           (PLR_FLAGGED(ch, PLR_PERCEIVE) && tch == FIGHTING(ch))))
          process_illusion_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(ch) && (IS_ASTRAL(tch) || IS_DUAL(tch) ||
           PLR_FLAGGED(tch, PLR_PERCEIVE)))
          process_illusion_spell(ch, tch, (obj_t *) NULL, spell, force);
      }
      break;
    default:
      send_to_char("You can't seem to target this spell for some reason.\r\n", ch);
      mudlog("Illegal target in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return FALSE;
  }

  return TRUE;
}

void process_illusion_spell(char_t *ch, char_t *vict, obj_t *obj, spell_t *spell, int force)
{
  int success;

  switch (spell->type) {
    case SPELL_CHAOS:
    case SPELL_CHAOTIC_WORLD:
    case SPELL_CONFUSION:
    case SPELL_INVISIBILITY:
    case SPELL_OVERSTIMULATION:
    case SPELL_IMPROVED_INVIS:
      sustain_spell(force, ch, vict, spell, 0);
      break;
    default:
      send_to_char("Your spell fizzles into oblivion.\r\n", ch);
      mudlog("Illegal type in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return;
  }
  return;
}

bool process_manipulation_target(char_t *ch, spell_t *spell, char *t, int force)
{
  char_t *vict = NULL;
  obj_t *obj = NULL;
  char *temp, targ1[80], targ2[80];
  int i, vnum = -1, val;

  if (t)
    skip_spaces(&t);

  if (spell->type == SPELL_SHAPE_CHANGE) {
    if (ch->desc->original) {
      send_to_char("You can't shape change in your current form!\r\n", ch);
      return FALSE;
    }
    if (!t) {
      send_to_char("What do you want to transform into?\r\n", ch);
      return FALSE;
    }
    for (i = 0; *shape_forms[i] != '\n'; i++)
      if (!str_cmp(t, (char *)shape_forms[i])) {
        vnum = 50 + i;
        break;
      }
    if (vnum == -1) {
      send_to_char("What do you want to transform into?\r\n", ch);
      return FALSE;
    }
    vict = read_mobile(vnum, VIRTUAL);
    act("You feel your body morph into its new shape.", FALSE, ch, 0, 0, TO_CHAR);
    sprintf(arg, "$n disappears, only to be replaced by %s!", GET_NAME(vict));
    act(arg, TRUE, ch, 0, 0, TO_ROOM);
    SET_BIT(PLR_FLAGS(ch), PLR_SWITCHED);
    char_to_room(vict, ch->in_room);
    GET_WAS_IN(ch) = ch->in_room;
    char_from_room(ch);


    char_to_room(ch, 0);
    ch->desc->character = vict;
    ch->desc->original = ch;
    vict->desc = ch->desc;
    ch->desc = NULL;

    look_at_room(vict, 1);
    return TRUE;
  }

  switch (spell->target) {
    case SPELL_TARGET_CASTER:
      if (t && strcmp(t, GET_NAME(ch))) {
        send_to_char("You can only target yourself with this spell.\r\n", ch);
        return FALSE;
      }
      process_manipulation_spell(ch, ch, (obj_t *)NULL, spell, force);
      break;
    case SPELL_TARGET_TOUCH:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        if (!(vict = get_char_room_vis(ch, t)))
          if (!(val = spell_damage_door(ch, spell, t))) {
            if (!(obj = get_obj_in_list_vis(ch, t, world[ch->in_room].contents)))
              if (!(obj = get_obj_in_list_vis(ch, t, ch->carrying))) {
                send_to_char("You can't seem to find the target you "
                             "wish to cast this spell on.\r\n", ch);
                return FALSE;
              }
          } else return (val == 1 ? TRUE : FALSE);
      }
      if (vict && GET_POS(vict) == POS_STANDING) {
        if (resisted_test(GET_QUI(ch), GET_QUI(vict) + modify_target(ch), GET_QUI(vict), GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      } else if (vict) {
        if (resisted_test(GET_QUI(ch), (GET_QUI(vict) / 2) + modify_target(ch),
            (GET_QUI(vict) / 2), GET_QUI(ch) + modify_target(vict)) < 1) {
          act("You stumble over your own legs as you try to touch $N!", FALSE, ch, 0, vict, TO_CHAR);
          act("$n stumbles over $s legs as $e reaches to touch $N!", FALSE, ch, 0, vict, TO_NOTVICT);
          act("$n stumbles over $s legs as $e reaches to touch you!", FALSE, ch, 0, vict, TO_VICT);
          if (!FIGHTING(vict))
            set_fighting(vict, ch);
          if (!FIGHTING(ch))
            set_fighting(ch, vict);
          WAIT_STATE(ch, PULSE_VIOLENCE);
          return FALSE;
        }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_RANGE:
      if (!t) {
        if (!FIGHTING(ch)) {
          send_to_char("You must specify the target you wish to cast this spell on.\r\n", ch);
          return FALSE;
        } else vict = FIGHTING(ch);
      } else {
        any_one_arg(any_one_arg(t, targ1), targ2);
        if (!*targ2) {
          if (!(vict = get_char_room_vis(ch, targ1)))
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(obj = get_obj_in_list_vis(ch, targ1, world[ch->in_room].contents)) &&
                  !(obj = get_obj_in_list_vis(ch, targ1, ch->carrying))) {
                send_to_char("You can't seem to find the target you "
                             "are looking for.\r\n", ch);
                return FALSE;
              }
            } else return (val == 1 ? TRUE : FALSE);
        } else {
          if (!(vict = range_spell(ch, targ1, targ2))) {
            if (!(val = spell_damage_door(ch, spell, t))) {
              if (!(vict = get_char_room_vis(ch, targ2))) {
                send_to_char("You can't seem to find the target you are looking for.\r\n", ch);
                return FALSE;
              } else {
                for (i = 0; i < NUM_WEARS; ++i)
                  if (GET_EQ(vict, i) && isname(targ1, GET_EQ(vict, i)->name)) {
                    obj = GET_EQ(vict, i);
                    break; // and get out when we do
                  }
              }
            } else return (val == 1 ? TRUE : FALSE);
          } else if (vict == ch)
            return FALSE;
        }
        if (!spell->physical && obj) {
          send_to_char("You can only cast that spell on living targets.\r\n", ch);
          return FALSE;
        }
      }
      if (!vict || (!IS_ASTRAL(ch) && !IS_ASTRAL(vict)))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(vict) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
         (PLR_FLAGGED(ch, PLR_PERCEIVE) && vict == FIGHTING(ch))))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else if (IS_ASTRAL(ch) && (IS_ASTRAL(vict) || IS_DUAL(vict) ||
         PLR_FLAGGED(vict, PLR_PERCEIVE)))
        process_manipulation_spell(ch, vict, obj, spell, force);
      else {
        if (IS_ASTRAL(vict) && PLR_FLAGGED(ch, PLR_PERCEIVE))
          send_to_char("You can't initiate attacks on astral beings!\r\n", ch);
        else if (IS_ASTRAL(vict))
          send_to_char("You can't target astral beings!\r\n", ch);
        else send_to_char("You can't target physical beings!\r\n", ch);
        return FALSE;
      }
      break;
    case SPELL_TARGET_AREA:
      struct char_data *tch, *next_tch;
      for (tch = world[ch->in_room].people; tch; tch = next_tch)  {
        next_tch = tch->next_in_room;
        if (tch == ch)
          continue;
        if (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_LEGEND)
          continue;
        if (!pk_allowed && !IS_NPC(tch) && !IS_NPC(ch))
          continue;
        if (in_group(ch, tch))
          continue;
        if (!IS_ASTRAL(ch) && !IS_ASTRAL(tch))
          process_manipulation_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(tch) && (IS_ASTRAL(ch) || IS_DUAL(ch) ||
           (PLR_FLAGGED(ch, PLR_PERCEIVE) && tch == FIGHTING(ch))))
          process_manipulation_spell(ch, tch, (obj_t *) NULL, spell, force);
        else if (IS_ASTRAL(ch) && (IS_ASTRAL(tch) || IS_DUAL(tch) ||
           PLR_FLAGGED(tch, PLR_PERCEIVE)))
          process_manipulation_spell(ch, tch, (obj_t *) NULL, spell, force);
      }
      struct obj_data *obj, *next;
      if (spell->physical) {
        for (obj = world[ch->in_room].contents; obj; obj = next) {
          next = obj->next_content;
          damage_obj(NULL, obj, force + FORCE_PENALTY(spell),
		     spell->effect | DAMOBJ_MANIPULATION);
        }
        for (val = 0; val < (NUM_OF_DIRS - 1); val++)
          if (EXIT(ch, val) && EXIT(ch, val)->keyword && 
              IS_SET(EXIT(ch, val)->exit_info, EX_CLOSED))
            damage_door(ch, ch->in_room, val, (int)(force), spell->effect);
      }
      break;
    default:
      send_to_char("You can't seem to target this spell for some reason.\r\n", ch);
      mudlog("Illegal target in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return FALSE;
  }

  return TRUE;
}

void process_manipulation_spell(char_t *ch, char_t *vict, obj_t *obj, spell_t *spell, int force)
{
  struct obj_data *next;
  int resist = 0, success = 0, dam, i, low = NOWHERE, high = NOWHERE;
  int chance, room;

  switch (spell->type) {
    case SPELL_ANTI_BULLET:
    case SPELL_ANTI_SPELL:
    case SPELL_ARMOR:
    case SPELL_INFLUENCE:
    case SPELL_LIGHT:
    case SPELL_PETRIFY:
      sustain_spell(force, ch, vict, spell, 0);
      break;
    case SPELL_CLOUT:
      if (obj) {
        send_to_char("This spell can't be used on objects\r\n.", ch);
        return;
      }
      resist = GET_WIL(vict);

      success = success_test(force + spell_bonus(ch, spell), GET_IMPACT(vict));
      success -= success_test(resist + spell_resist(vict), GET_WIL(ch) + modify_target(vict));

      if (success < 1) {
        damage(ch, vict, 0, spell->type, FALSE);
        ranged_response(ch, vict);
        return;
      }
      if (IS_ASTRAL(ch))
        dam = convert_damage(stage(success, grimoire[spell->type].drain % 10));
      else dam = convert_damage(stage(success, grimoire[spell->type].damage));

      if (access_level(ch, LVL_OWNER) && spell->target != SPELL_TARGET_AREA)
        send_to_char(ch, "(%s) F: %d, R: %d, S: %d, D: %d\r\n",
               spell->physical ? "Physical" : "Mana", force, resist, success, 0);

      if (spell->target == SPELL_TARGET_AREA)
        chance = 10;
      else chance = 2;
      for (i = 0; i < (NUM_WEARS - 1); i++)
        if (GET_EQ(vict, i) && number(1, 100) < chance) {
          damage_obj(NULL, GET_EQ(vict, i), force + FORCE_PENALTY(spell),
		     DAMOBJ_PROJECTILE);
          if (spell->target != SPELL_TARGET_AREA)
            break;
        }
      chance = (int)(chance / 2);
      for (obj = vict->carrying; obj; obj = next) {
        next = obj->next_content;
        if (number(1, 100) < chance)
          damage_obj(NULL, obj, force + FORCE_PENALTY(spell),
		     DAMOBJ_PROJECTILE);
      }

      damage(ch, vict, dam, spell->type, FALSE);
      ranged_response(ch, vict);
      break;
    case SPELL_TOXIC_WAVE:
    case SPELL_ELEMENTBALL:
    case SPELL_ELEMENT_BOLT:
    case SPELL_ELEMENT_CLOUD:
    case SPELL_ELEMENT_DART:
    case SPELL_ELEMENT_MISSILE:
      if (!obj) {
        if (spell->physical)
          resist = GET_BOD(vict);
        else resist = GET_WIL(vict);

        success = success_test(force + spell_bonus(ch, spell), resist + modify_target(ch));

        if (access_level(ch, LVL_OWNER) && spell->target != SPELL_TARGET_AREA)
          send_to_char(ch, "(%s) F: %d, R: %d, S: %d, D: %d\r\n",
                       spell->physical ? "Physical" : "Mana", force, resist, success, 0);

        if (success < 1) {
          damage(ch, vict, 0, spell->type, FALSE);
          ranged_response(ch, vict);
          return;
        }
        if (spell->effect != SPELL_EFFECT_NONE) {
          elemental_damage(ch, vict, spell, force, success);
          return;
        }
        success -= success_test(resist + spell_resist(vict), force + modify_target(vict));
        if (IS_ASTRAL(ch))
          dam = convert_damage(stage(success, grimoire[spell->type].drain % 10));
        else dam = convert_damage(stage(success, grimoire[spell->type].damage));

        if (spell->target == SPELL_TARGET_AREA)
          chance = 10;
        else chance = 2;
        for (i = 0; i < (NUM_WEARS - 1); i++)
          if (GET_EQ(vict, i) && number(1, 100) < chance) {
            damage_obj(NULL, GET_EQ(vict, i), force + FORCE_PENALTY(spell),
		       DAMOBJ_PROJECTILE);
            if (spell->target != SPELL_TARGET_AREA)
              break;
          }
        chance = (int)(chance / 2);
        for (obj = vict->carrying; obj; obj = next) {
          next = obj->next_content;
          if (number(1, 100) < chance)
            damage_obj(NULL, obj, force + FORCE_PENALTY(spell),
		       DAMOBJ_PROJECTILE);
        }

        damage(ch, vict, dam, spell->type, TRUE);
        ranged_response(ch, vict);
      } else {
        damage_obj(ch, obj, force + FORCE_PENALTY(spell),
		   spell->effect | DAMOBJ_MANIPULATION);

        if (vict && IS_NPC(vict) && !FIGHTING(vict))
          set_fighting(vict, ch);
      }
      break;
    case SPELL_TELEPORT:
      for (i = zone_table[world[ch->in_room].zone].number * 100; low == NOWHERE &&

           i <= zone_table[world[ch->in_room].zone].top; i++)
        low = real_room(i);
      for (i = zone_table[world[ch->in_room].zone].top; high == NOWHERE &&
           i >= zone_table[world[ch->in_room].zone].number * 100; i--)
        high = real_room(i);
      if ((low == NOWHERE || high == NOWHERE) || low >= high) {
        send_to_char("You are unable to control the necessary forces.\r\n", ch);
        return;
      }
      act("You carefully rip a whole in space and step through it.", FALSE, ch, 0, 0, TO_CHAR);
      act("$n tears a whole in space and disappears into it.", TRUE, ch, 0, 0, TO_ROOM);
      char_from_room(ch);
      for (room = number(low, high), i = 0; i < 50 && ROOM_FLAGGED(room, ROOM_BUILDERROOM |
           ROOM_MGRROOM | ROOM_DIRECTORROOM | ROOM_PRESROOM); i++)
        room = number(low, high);
      char_to_room(ch, room);
      act("$n appears from thin air.", TRUE, ch, 0, 0, TO_ROOM);
      if (ch->desc)
        look_at_room(ch, 0);
      break;
    default:
      send_to_char("Your spell fizzles into oblivion.\r\n", ch);
      mudlog("Illegal type in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return;
  }
  return;
}

void parse_category(spell_t *spell, struct char_data *ch, struct char_data *tch,
                    struct obj_data *tobj, int level)
{
  if (!tch || (!IS_ASTRAL(ch) && !IS_ASTRAL(tch)));
  else if (IS_ASTRAL(tch) && (IS_ASTRAL(ch) || IS_DUAL(ch)));
  else if (IS_ASTRAL(ch) && (IS_ASTRAL(tch) || IS_DUAL(tch) ||
     PLR_FLAGGED(tch, PLR_PERCEIVE)));
  else return;

  switch (spell->category) {
    case SPELL_CATEGORY_COMBAT:
      process_combat_spell(ch, tch, tobj, spell, spell->force);
      break;
    case SPELL_CATEGORY_DETECTION:
      process_detection_spell(ch, tch, tobj, spell, spell->force);
      break;
    case SPELL_CATEGORY_HEALTH:
      process_health_spell(ch, tch, level, spell, spell->force);
      break;
    case SPELL_CATEGORY_ILLUSION:
      process_illusion_spell(ch, tch, tobj, spell, spell->force);
      break;
    case SPELL_CATEGORY_MANIPULATION:
      process_manipulation_spell(ch, tch, tobj, spell, spell->force);
      break;
    default:
      send_to_char("Your spell seems to make no sense to you whatsoever.\r\n", ch);
      mudlog("Unknown spell category in newmagic.cc.", ch, LOG_SYSLOG, TRUE);
      return;
  }
}

void mob_cast(struct char_data * ch, struct char_data * tch, struct obj_data * tobj,
              int spellnum, int level)
{
  spell_t *spell;

  if (GET_POS(ch) < POS_SITTING) {
    send_to_char("Not now!\r\n", ch);
    return;
  } else if (GET_MENTAL(ch) < (GET_MAX_MENTAL(ch) / 3)) {
    send_to_char("You fear casting right now would do more harm than good.\r\n", ch);
    return;
  } else if (IS_AFFECTED(ch, AFF_CHARM) && (ch->master == tch)) {
    send_to_char("You are afraid you might hurt your master!\r\n", ch);
    return;
  } else if (ROOM_FLAGGED(ch->in_room, ROOM_NOMAGIC)) {
    send_to_char("You just can't do that here!\r\n", ch);
    return;
  }

  spell = new spell_t;
  int i = strlen(spells[spellnum]);
  spell->name = new char[i+1];
  strcpy(spell->name, spells[spellnum]);
  spell->physical = grimoire[spellnum].physical;
  spell->category = grimoire[spellnum].category;
  spell->force = MIN(GET_MAG(ch) / 100, MIN(10,
         (GET_SKILL(ch, SKILL_SORCERY) ? GET_SKILL(ch, SKILL_SORCERY) : 0)));
  spell->target = grimoire[spellnum].target;
  spell->drain = grimoire[spellnum].drain;
  spell->damage = grimoire[spellnum].damage;
  spell->type = spellnum;
  spell->effect = level;
  spell->next = NULL;

  if (spell->force < 1) {
    send_to_char("You need some knowledge of sorcery to cast spells!\r\n", ch);
    if (spell->name)
      delete [] spell->name;
    delete spell;
    return;
  }

  if ((tch != ch) && spell->target == CASTER) {
    send_to_char("You can only cast this spell upon yourself!\r\n", ch);
    if (spell->name)
      delete [] spell->name;
    delete spell;
    return;
  }

  if (!tch || (!IS_ASTRAL(ch) && !IS_ASTRAL(tch)));
  else if (IS_ASTRAL(tch) && (IS_ASTRAL(ch) || IS_DUAL(ch)));
  else if (IS_ASTRAL(ch) && (IS_ASTRAL(tch) || IS_DUAL(tch) ||
     PLR_FLAGGED(tch, PLR_PERCEIVE)));
  else {
    if (IS_ASTRAL(tch))
      send_to_char("You can't target astral beings.\r\n", ch);
    else send_to_char("You can't target physical beings.\r\n", ch);
    if (spell->name)
      delete [] spell->name;
    delete spell;
    return;
  }

  if (spell->target == SPELL_TARGET_TOUCH) {
    if (GET_POS(tch) == POS_STANDING) {
      if (resisted_test(GET_QUI(ch), GET_QUI(tch) + modify_target(ch),
                        GET_QUI(tch), GET_QUI(ch) + modify_target(tch)) < 1) {
        act("You stumble over your own legs as you try to touch $N!",
            FALSE, ch, 0, tch, TO_CHAR);
        act("$n stumbles over $s legs as $e reaches to touch $N!",
            FALSE, ch, 0, tch, TO_NOTVICT);
        act("$n stumbles over $s legs as $e reaches to touch you!",
            FALSE, ch, 0, tch, TO_VICT);
        if (!FIGHTING(tch))
          set_fighting(tch, ch);
        if (!FIGHTING(ch))
          set_fighting(ch, tch);
        WAIT_STATE(ch, PULSE_VIOLENCE);
        if (spell->name)
          delete [] spell->name;
        delete spell;
        return;
      }
    } else {
      if (resisted_test(GET_QUI(ch), (GET_QUI(tch) / 2) + modify_target(ch),
          (GET_QUI(tch) / 2), GET_QUI(ch) + modify_target(tch)) < 1) {
        act("You stumble over your own legs as you try to touch $N!",
            FALSE, ch, 0, tch, TO_CHAR);
        act("$n stumbles over $s legs as $e reaches to touch $N!",
            FALSE, ch, 0, tch, TO_NOTVICT);
        act("$n stumbles over $s legs as $e reaches to touch you!",
            FALSE, ch, 0, tch, TO_VICT);
        if (!FIGHTING(tch))
          set_fighting(tch, ch);
        if (!FIGHTING(ch))
          set_fighting(ch, tch);
        WAIT_STATE(ch, PULSE_VIOLENCE);
        if (spell->name)
          delete [] spell->name;
        delete spell;
        return;
      }
    }
  }

  if (spell->target == AREA) {
    for (tch = world[ch->in_room].people; tch; tch = tch->next_in_room)
      if (tch != ch)
        parse_category(spell, ch, tch, tobj, level);
  } else parse_category(spell, ch, tch, tobj, level);

  if (spellnum != SPELL_HEAL && spellnum != SPELL_ANTIDOTE &&
      spellnum != SPELL_CURE_DISEASE && spellnum != SPELL_RESIST_PAIN &&
      spellnum != SPELL_POISON)
    resist_drain(ch, spell->force, spell, DRAIN_LEVEL(spell->drain));
  else resist_drain(ch, spell->force, spell, level);

  if (FIGHTING(ch) && !AFF_FLAGGED(ch, AFF_ACTION))
    SET_BIT(AFF_FLAGS(ch), AFF_ACTION);
  WAIT_STATE(ch, PULSE_VIOLENCE);

  if (spell->name)
    delete [] spell->name;
  delete spell;
}

void obj_magic(struct char_data * ch, struct obj_data * obj, char *argument)
{
  struct char_data *tch = NULL, *next_tch;
  struct obj_data *tobj = NULL;
  spell_t *spell = new spell_t;
  int k;

  one_argument(argument, arg);
  k = generic_find(arg, FIND_CHAR_ROOM | FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &tch, &tobj);

  if (GET_OBJ_VAL(obj, 3) >= SPELL_ANALYZE_DEVICE && GET_OBJ_VAL(obj, 3) <= MAX_SPELLS) {
    int i = strlen(spells[GET_OBJ_VAL(obj, 3)]);
    spell->name = new char[i+1];
    strcpy(spell->name, spells[GET_OBJ_VAL(obj, 3)]);
    spell->physical = grimoire[GET_OBJ_VAL(obj, 3)].physical;
    spell->category = grimoire[GET_OBJ_VAL(obj, 3)].category;
    spell->force = MAX(GET_OBJ_VAL(obj, 0), 1);
    spell->target = grimoire[GET_OBJ_VAL(obj, 3)].target;
    spell->drain = grimoire[GET_OBJ_VAL(obj, 3)].drain;
    spell->damage = grimoire[GET_OBJ_VAL(obj, 3)].damage;
    spell->type = GET_OBJ_VAL(obj, 3);
    spell->next = NULL;
  } else return;

  switch (GET_OBJ_TYPE(obj)) {
    case ITEM_STAFF:
      if (obj->use_description) {
        act(obj->use_description->description2, FALSE, ch, obj, 0, TO_ROOM);
        act(obj->use_description->description1, FALSE, ch, obj, 0, TO_CHAR);
      } else {
        act("$n taps $p three times on the ground.", FALSE, ch, obj, 0, TO_ROOM);
        act("You tap $p three times on the ground.", FALSE, ch, obj, 0, TO_CHAR);
      }
      if (GET_OBJ_VAL(obj, 2) <= 0) {
        act("It seems powerless.", FALSE, ch, obj, 0, TO_CHAR);
        act("Nothing seems to happen.", FALSE, ch, obj, 0, TO_ROOM);
      } else {
        GET_OBJ_VAL(obj, 2)--;
        for (tch = world[ch->in_room].people; tch; tch = next_tch) {
          next_tch = tch->next_in_room;
          if (ch == tch)
            continue;
          if (!IS_NPC(tch) || (IS_ASTRAL(tch) && !(IS_ASTRAL(ch) || IS_DUAL(ch) || PLR_FLAGGED(ch, PLR_PERCEIVE))))
            continue;
          parse_category(spell, ch, tch, NULL, 2);
        }
      }
      break;
    case ITEM_WAND:
      if (k == FIND_CHAR_ROOM) {
        if (tch == ch) {
          act("You point $p at yourself.", FALSE, ch, obj, 0, TO_CHAR);
          act("$n points $p at $mself.", FALSE, ch, obj, 0, TO_ROOM);
        } else {
          if (obj->use_description != NULL) {
            act(obj->use_description->description2, FALSE, ch, obj, tch, TO_ROOM);
            act(obj->use_description->description1, FALSE, ch, obj, tch, TO_CHAR);
          } else {
            act("You point $p at $N.", FALSE, ch, obj, tch, TO_CHAR);
            act("$n points $p at $N.", TRUE, ch, obj, tch, TO_ROOM);
          }
        }
      } else if (tobj != NULL) {
        if (obj->use_description != NULL) {
          act(obj->use_description->description2, TRUE, ch, obj, tobj, TO_ROOM);
          act(obj->use_description->description1, FALSE, ch, obj, tobj, TO_CHAR);
        } else {
          act("$n points $p at $P.", TRUE, ch, obj, tobj, TO_ROOM);
          act("You point $p at $P.", FALSE, ch, obj, tobj, TO_CHAR);
        }
      } else {
        act("At what should $p be pointed?", FALSE, ch, obj, NULL, TO_CHAR);
        return;
      }
      if (GET_OBJ_VAL(obj, 2) <= 0) {
        act("It seems powerless.", FALSE, ch, obj, 0, TO_CHAR);
        return;
      }
      GET_OBJ_VAL(obj, 2)--;
      parse_category(spell, ch, tch, tobj, 2);
      break;
    case ITEM_SCROLL:
      if (*arg) {
        if (!k) {
          act("There is nothing to here to affect with $p.", FALSE, ch, obj, NULL, TO_CHAR);
          return;
        }
      } else tch = ch;
      if (obj->use_description) {
        act(obj->use_description->description2, FALSE, ch, obj, NULL, TO_ROOM);
        act(obj->use_description->description1, TRUE, ch, obj, 0, TO_CHAR);
      } else {
        act("$n recites $p.", FALSE, ch, obj, NULL, TO_ROOM);
        act("You recite $p which dissolves.", TRUE, ch, obj, 0, TO_CHAR);
      }
      parse_category(spell, ch, tch, tobj, 2);
      if (obj != NULL)
        extract_obj(obj);
      break;
    case ITEM_POTION:
      tch = ch;
      if (obj->use_description) {
        act(obj->use_description->description2, FALSE, ch, obj, NULL, TO_ROOM);
        act(obj->use_description->description1, FALSE, ch, obj, NULL, TO_CHAR);
      } else {
        act("$n quaffs $p.", TRUE, ch, obj, NULL, TO_ROOM);
        act("You quaff $p.", FALSE, ch, obj, NULL, TO_CHAR);
      }
      parse_category(spell, ch, tch, tobj, 2);
      if (obj != NULL)
        extract_obj(obj);
      break;
    default:
      log("SYSERR: Unknown object_type in mag_objectmagic");
      break;
  }
}

int spell_bonus(char_t *ch, spell_t *spell)
{
  return foci_bonus(ch, spell, spell->force, TRUE)
    + magic_pool_bonus(ch, spell, spell->force, TRUE) 
    + totem_bonus(ch, spell);
}

// finds the spell by name and returns a pointer to it
spell_t *find_spell(char_t *ch, char *name)
{
  register spell_t *temp;

  for (temp = ch->spells; temp; temp = temp->next)
    if (is_abbrev(name, temp->name))
      break;

  if (temp)
    return temp;

  return NULL;
}

void write_spells(char_t *ch)
{
  if (!get_filename(GET_NAME(ch), buf2, SPELLS_FILE) || !ch->spells)
    return;

  FILE *outFile;

  if (!(outFile = fopen(buf2, "w+"))) {
    sprintf(buf, "Unable to write %s's spell file.\n", GET_NAME(ch));
    mudlog(buf, ch, LOG_SYSLOG, TRUE);
    return;
  }

  for (spell_t *temp = ch->spells; temp; temp = temp->next) {
    // here we place the name at the end of each line so it's easier
    // to read in
    fprintf(outFile, "%d %d %d %d %d %d %d %d %-30s\n", temp->physical,
            temp->category, temp->force, temp->target, temp->drain,
            temp->type, temp->damage, temp->effect, temp->name);
  }

  fclose(outFile);
}

void read_spells(char_t *ch)
{
  if (ch->spells || !get_filename(GET_NAME(ch), buf2, SPELLS_FILE))
    return;

  FILE *inFile;

  if (!(inFile = fopen(buf2, "r"))) {
    if (errno != ENOENT) {
      sprintf(buf, "Unable to open %s's spell file.\n", GET_NAME(ch));
      mudlog(buf, ch, LOG_SYSLOG, TRUE);
    }
    return;
  }

  spell_t *temp;
  char line[256];
  char name[31];
  int t[9];

  while (get_line(inFile, line) && sscanf(line, "%d %d %d %d %d %d %d %d %30[a-zA-Z0-9 ]\n",
         t, t + 1, t + 2, t + 3, t + 4, t + 5, t + 6, t + 7, name) == 9) {
    int i;

    while( name[(i = strlen(name)-1)] == ' ' && i >= 1 )
      name[i] = 0;

    // allocate a new spell
    temp = new spell_t;
    temp->name = new char[strlen(name) + 1];
    // set all the values
    temp->physical = t[0];
    temp->category = t[1];
    temp->force = t[2];
    temp->target = t[3];
    temp->drain = t[4];
    temp->type = t[5];
    temp->damage = t[6];
    temp->effect = t[7];
    strcpy(temp->name, name);

    // add the spell to the character's spell list
    temp->next = ch->spells;
    ch->spells = temp;
  }

  fclose(inFile);
}

#undef _newmagic_cc_