AwakeMUD-0.8.18B/
AwakeMUD-0.8.18B/doc/
AwakeMUD-0.8.18B/lib/
AwakeMUD-0.8.18B/lib/etc/
AwakeMUD-0.8.18B/lib/etc/pfiles/
AwakeMUD-0.8.18B/lib/misc/
AwakeMUD-0.8.18B/lib/text/
AwakeMUD-0.8.18B/lib/text/help/
AwakeMUD-0.8.18B/lib/text/wizhelp/
AwakeMUD-0.8.18B/lib/veh/
AwakeMUD-0.8.18B/lib/world/
AwakeMUD-0.8.18B/lib/world/mob/
AwakeMUD-0.8.18B/lib/world/mtx/
AwakeMUD-0.8.18B/lib/world/qst/
AwakeMUD-0.8.18B/lib/world/shp/
AwakeMUD-0.8.18B/lib/world/veh/
#include <stdlib.h>
#include <time.h>

#include "structs.h"
#include "awake.h"
#include "db.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "newmagic.h"
#include "utils.h"
#include "screen.h"
#include "constants.h"
#include "olc.h"

#define POWER(name) void (name)(struct char_data *ch, struct char_data *spirit, struct spirit_data *spiritdata, char *arg)
#define SPELLCASTING 0
#define CONJURING 1
#define FAILED_CAST "You fail to bind the mana to your will.\r\n"

extern void die(struct char_data *ch);
extern void damage_equip(struct char_data *ch, struct char_data *vict, int power, int type);
extern void damage_obj(struct char_data *ch, struct obj_data *obj, int power, int type);
extern void check_killer(struct char_data * ch, struct char_data * vict);
extern void nonsensical_reply(struct char_data *ch);

struct char_data *find_spirit_by_id(int spiritid, long playerid)
{
  for (struct char_data *ch = character_list; ch; ch = ch->next)
    if (IS_NPC(ch) && GET_ACTIVE(ch) == playerid && GET_GRADE(ch) == spiritid)
      return ch;
  return NULL;
}

void end_sustained_spell(struct char_data *ch, struct sustain_data *sust)
{
  struct sustain_data *temp, *vsust;
  if (sust->other)
    for (vsust = GET_SUSTAINED(sust->other); vsust; vsust = vsust->next)
      if (sust->caster != vsust->caster && vsust->other == ch && vsust->idnum == sust->idnum)
      {
        if (vsust->spirit) {
          GET_SUSTAINED_FOCI(sust->other)--;
          GET_SUSTAINED_NUM(vsust->spirit--);
        }
        REMOVE_FROM_LIST(vsust, GET_SUSTAINED(sust->other), next);
        delete [] vsust;
        break;
      }
  spell_modify(sust->caster ? sust->other : ch, sust, FALSE);
  REMOVE_FROM_LIST(sust, GET_SUSTAINED(ch), next);
  if (sust->focus)
  {
    GET_SUSTAINED_FOCI(sust->caster ? ch : sust->other)--;
    GET_FOCI(sust->caster ? ch : sust->other)--;
    GET_OBJ_VAL(sust->focus, 4)--;
  }
  if (sust->spirit)
  {
    GET_SUSTAINED_FOCI(ch)--;
    GET_SUSTAINED_NUM(sust->spirit)--;
    GET_SUSTAINED(sust->spirit) = NULL;
  }
  GET_SUSTAINED_NUM(sust->caster ? ch : sust->other)--;
  send_to_char(sust->caster ? ch : sust->other, "You stop sustaining %s.\r\n", spells[sust->spell].name);
  delete [] sust;
}

void totem_bonus(struct char_data *ch, int action, int type, int &target, int &skill)
{
  extern struct time_info_data time_info;
  if (GET_TOTEM(ch) == TOTEM_OWL)
  {
    if (time_info.hours > 6 || time_info.hours < 19)
      target += 2;
    else
      skill += 2;
  } else if (GET_TOTEM(ch) == TOTEM_RAVEN)
  {
    if (ROOM_FLAGGED(ch->in_room, ROOM_INDOORS) || SECT(ch->in_room) == SPIRIT_HEARTH)
      target += 1;
  } else if (GET_TOTEM(ch) == TOTEM_SNAKE && FIGHTING(ch))
    skill--;
  else if ((GET_TOTEM(ch) == TOTEM_BAT || GET_TOTEM(ch) == TOTEM_PUMA)
           && (time_info.hours > 6 && time_info.hours < 19) && OUTSIDE(ch))
    target += 2;
  else if (GET_TOTEM(ch) == TOTEM_PUMA && world[ch->in_room].crowd > 4)
    target += 2;
  else if (GET_TOTEM(ch) == TOTEM_SCORPION && (time_info.hours > 6 && time_info.hours < 19))
    target += 2;
  else if (GET_TOTEM(ch) == TOTEM_SPIDER && OUTSIDE(ch))
    target++;

  if (action == SPELLCASTING)
  {
    type = spells[type].category;
    switch (GET_TOTEM(ch)) {
    case TOTEM_PRAIRIEDOG:
      if (type == DETECTION)
        skill += 2;
      else if (type == ILLUSION)
        skill++;
      else if (type == COMBAT)
        skill -= 2;
      break;
    case TOTEM_SPIDER:
      if (type == ILLUSION)
        skill += 2;
      break;
    case TOTEM_LEOPARD:
      if (type == COMBAT || type == HEALTH)
        skill += 2;
      break;
    case TOTEM_BEAR:
    case TOTEM_HORSE:
    case TOTEM_LIZARD: 
    case TOTEM_PYTHON:
     if (type == HEALTH)
        skill += 2;
      break;
    case TOTEM_ELK:
      if (type == HEALTH)
        skill++;
      else if (type == COMBAT)
        skill -= 2;
      break;
    case TOTEM_TURTLE:
      if (type == ILLUSION)
        skill += 2;
      else if (type == COMBAT)
        skill -= 2;
      break;
    case TOTEM_BUFFALO:
      if (type == HEALTH)
        skill += 2;
      else if (type == ILLUSION)
        skill--;
      break;
    case TOTEM_PUMA:
    case TOTEM_CAT:
    case TOTEM_PARROT:
      if (type == ILLUSION)
        skill += 2;
      break;
    case TOTEM_DOG:
    case TOTEM_EAGLE:
      if (type == DETECTION)
        skill += 2;
      break;
    case TOTEM_DOLPHIN:
    case TOTEM_FISH:
      if (type == DETECTION)
        skill += 2;
      else if (type == COMBAT)
        skill--;
      break;
    case TOTEM_GATOR:
      if (type == COMBAT || type == DETECTION)
        skill += 2;
      else if (type == ILLUSION)
        skill--;
      break;
    case TOTEM_LION:
    case TOTEM_HYENA:
      if (type == COMBAT)
        skill += 2;
      else if (type == HEALTH)
        skill--;
    case TOTEM_MOUSE:
      if (type == DETECTION || type == HEALTH)
        skill += 2;
      else if (type == COMBAT)
        skill -= 2;
      break;
    case TOTEM_RACCOON:
    case TOTEM_MONKEY:
      if (type == MANIPULATION)
        skill += 2;
      else if (type == COMBAT)
        skill--;
      break;
    case TOTEM_RAT:
    case TOTEM_JACKAL:
      if (type == DETECTION || type == ILLUSION)
        skill += 2;
      else if (type == COMBAT)
        skill--;
    case TOTEM_JAGUAR:
      if (type == DETECTION)
        skill += 2;
      else if (type == HEALTH)
        skill--;
    case TOTEM_RAVEN:
      if (type == MANIPULATION)
        skill += 2;
      break;
    case TOTEM_SHARK:
    case TOTEM_WOLF:
      if (type == COMBAT || type == DETECTION)
        skill += 2;
      break;
    case TOTEM_SNAKE:
      if (type == DETECTION || type == HEALTH || type == ILLUSION)
        skill += 2;
      break;
    case TOTEM_CRAB:
      if (type == ILLUSION)
        skill--;
      break;
    case TOTEM_CROCODILE:
      if (type == COMBAT)
        skill += 2;
      else if (type == ILLUSION)
        skill++;
      break;
    case TOTEM_BOAR:
    case TOTEM_WHALE:
      if (type == ILLUSION)
        skill--;
    case TOTEM_BADGER:
      if (type == COMBAT)
        skill += 2;
      break;    
    case TOTEM_CHEETAH:
      if (type == COMBAT)
        skill += 2;
      else if (type == HEALTH)
        skill--;
      break;
    case TOTEM_BAT:
      if (type == DETECTION || type == MANIPULATION)
        skill += 2;
      break;
    case TOTEM_BULL:
      if (type == HEALTH)
        skill +=2;
      else if (type == COMBAT || type == DETECTION)
        skill++;
      break;
    case TOTEM_SCORPION:
    case TOTEM_COBRA:
      if (type == COMBAT || type == ILLUSION)
        skill += 2;
      break;
    case TOTEM_DOVE:
      if (type == HEALTH)
        skill += 2;
      else if (type == DETECTION)
        skill++;
      break;
    case TOTEM_GOOSE:
      if (type == DETECTION)
        skill += 2;
      else if (type == COMBAT)
        skill++;
      break;
    case TOTEM_FOX:
    case TOTEM_OTTER:
      if (type == ILLUSION)
        skill += 2;
      else if (type == COMBAT)
        skill--;
      break;
    case TOTEM_GECKO:
      if (type == GET_TOTEMSPIRIT(ch))
        skill += 2;
      else if (type == COMBAT)
        skill--;
      break;
    case TOTEM_POLECAT:
      if (type == COMBAT) {
        skill++;
        if (time_info.hours < 6 || time_info.hours > 19)     
          skill++;
      } else if (type == HEALTH)
        skill--;
      break;
    case TOTEM_STAG:
      if (type == HEALTH || type == ILLUSION)
        skill += 2;
      else if (type == MANIPULATION)
        skill--;
      break;
    }
  } else if (action == CONJURING)
  {
    switch (GET_TOTEM(ch)) {
    case TOTEM_SCORPION:
      skill--;
      break;
    case TOTEM_SPIDER:
      skill++;
      break;
    case TOTEM_PRAIRIEDOG:
    case TOTEM_ELK:
    case TOTEM_POLECAT:
      if (type == SPIRIT_FOREST || type == SPIRIT_DESERT || type == SPIRIT_MOUNTAIN || type == SPIRIT_PRAIRIE)
        skill += 2;
      break;
    case TOTEM_STAG:
    case TOTEM_PYTHON:
    case TOTEM_BEAR:
    case TOTEM_BADGER:
    case TOTEM_JAGUAR:
    case TOTEM_PARROT:
      if (type == SPIRIT_FOREST)
        skill += 2;
      break;
    case TOTEM_COBRA:
      if (type == SPIRIT_FOREST)
        skill++;
      break;
    case TOTEM_JACKAL:
    case TOTEM_HORSE:
    case TOTEM_BUFFALO:
    case TOTEM_LION:
    case TOTEM_CHEETAH:
      if (type == SPIRIT_PRAIRIE)
        skill += 2;
      break;
    case TOTEM_CAT:
    case TOTEM_RAT:
    case TOTEM_RACCOON:
      if (type == SPIRIT_CITY)
        skill += 2;
      break;
    case TOTEM_DOG:
    case TOTEM_MOUSE:
      if (type == SPIRIT_FIELD || type == SPIRIT_HEARTH)
        skill += 2;
      break;
    case TOTEM_MONKEY:
      if (type == SPIRIT_FIELD || type == SPIRIT_HEARTH || type == SPIRIT_CITY)
        skill += 2;
      break;
    case TOTEM_WHALE:
    case TOTEM_DOLPHIN:
    case TOTEM_SHARK:
    case TOTEM_CRAB:
    case TOTEM_CROCODILE:
      if (type == SPIRIT_SEA)
        skill += 2;
      break;
    case TOTEM_EAGLE:
    case TOTEM_RAVEN:
    case TOTEM_BAT:
    case TOTEM_DOVE:
      if (type == SPIRIT_MIST || type == SPIRIT_STORM || type == SPIRIT_WIND)
        skill += 2;
      break;
    case TOTEM_PUMA:
      if (type == SPIRIT_MOUNTAIN)
        skill += 2;
      break;
    case TOTEM_LEOPARD:
      if (time_info.hours < 6 || time_info.hours > 19)
        skill += 2;
      break;
    case TOTEM_LIZARD:
    case TOTEM_GATOR:
    case TOTEM_SNAKE:
    case TOTEM_WOLF:
    case TOTEM_FISH:
    case TOTEM_GOOSE:
    case TOTEM_OTTER:
    case TOTEM_TURTLE:
      if (GET_TOTEMSPIRIT(ch) == type)
        skill += 2;
      break;
    }
  }
}

void end_spirit_existance(struct char_data *ch, bool message)
{
  struct char_data *tempc;
  for (struct descriptor_data *d = descriptor_list; d; d = d->next) {
    tempc = d->original ? d->original : d->character;
    if (tempc && GET_IDNUM(tempc) == GET_ACTIVE(ch))
    {
      for (struct spirit_data *spirit = GET_SPIRIT(tempc); spirit; spirit = spirit->next)
        if (spirit->id == GET_GRADE(ch)) {
          struct spirit_data *temp;
          REMOVE_FROM_LIST(spirit, GET_SPIRIT(tempc), next);
          delete [] spirit;
          break;
        }
      GET_NUM_SPIRITS(tempc)--;
      if (message && d->character)
        send_to_char(d->character, "%s returns to the metaplanes, it's existance in this world finished.\r\n", CAP(GET_NAME(ch)));
      break;
    }
  }
  extract_char(ch);
}

void elemental_fulfilled_services(struct char_data *ch, struct char_data *mob, struct spirit_data *spirit)
{
  if (spirit->services < 1 && !(MOB_FLAGGED(mob, MOB_SPIRITGUARD) || MOB_FLAGGED(mob, MOB_STUDY) || GET_SUSTAINED(mob) || GET_SUSTAINED_NUM(mob) || FIGHTING(mob)))
  {
    send_to_char(ch, "It's services fulfilled, %s departs to the metaplanes.\r\n", CAP(GET_NAME(mob)));
    end_spirit_existance(mob, FALSE);
  }
}

bool conjuring_drain(struct char_data *ch, int force)
{
  if (world[ch->in_room].background[1] != 13)
    force += world[ch->in_room].background[0] / 2;
  int drain = 0;
  if (force <= GET_CHA(ch) / 2)
    drain = LIGHT;
  else if (force <= GET_CHA(ch))
    drain = MODERATE;
  else if (force > GET_CHA(ch) * 1.5)
    drain = DEADLY;
  else
    drain = SERIOUS;
  drain = convert_damage(stage(-success_test(GET_CHA(ch), force), drain));
  if (force > GET_MAG(ch) / 100)
    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;
    }
  }
  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 conjuring and fall unconcious!\r\n", ch);
    act("$n collapses unconscious!", FALSE, ch, 0, 0, TO_ROOM);
    struct sustain_data *next;
    for (struct sustain_data *sust = GET_SUSTAINED(ch); sust; sust = next) {
      next = sust->next;
      if (sust->caster && !sust->focus && !sust->spirit)
        end_sustained_spell(ch, sust);
    }
  } else if (GET_POS(ch) == POS_DEAD)
  {
    if (FIGHTING(ch))
      stop_fighting(ch);
    send_to_char("The energy from the conjuring ritual overloads your body with energy, killing you...\r\n", ch);
    act("$n suddenly collapases, dead!", FALSE, ch, 0, 0, TO_ROOM);
    die(ch);
    return TRUE;
  }
  return FALSE;
}

void magic_perception(struct char_data *ch, int force, int spell)
{
  int base = 4 + (int)(GET_MAG(ch) / 100) - force, target = 0, skill = 0, starg = 0;
  totem_bonus(ch, SPELLCASTING, spell, starg, skill);
  if (skill > 0)
    base--;
  for (struct char_data *vict = ch->in_veh ? ch->in_veh->people : world[ch->in_room].people; vict; vict = ch->in_veh ? vict->next_in_veh : vict->next_in_room) {
    if (ch == vict)
      continue;
    target = base;
    if (GET_MAG(vict) > 0)
      target -= 2;
    if (IS_DUAL(vict) || IS_ASTRAL(vict))
      target -= 2;
    if (success_test(GET_INT(ch) + (GET_TRADITION(ch) == TRAD_ADEPT ? GET_POWER(ch, ADEPT_IMPROVED_PERCEPT) : 0), target)) {
      if (IS_DUAL(vict) || IS_ASTRAL(vict))
        act("You notice $n manipulating the astral plane.", FALSE, ch, 0, vict, TO_VICT);
      else act("You notice $n performing magic.", TRUE, ch, 0, vict, TO_VICT);
    }
  }
}

bool spell_drain(struct char_data *ch, int type, int force, int damage)
{
  char buf[MAX_STRING_LENGTH];
  int target = (int)(force / 2), success = 0;
  target += spells[type].drainpower;
  if (world[ch->in_room].background[1] != 13)
    target += (world[ch->in_room].background[0] / 2);
  if (!damage)
    damage = spells[type].draindamage;
  else
    damage += spells[type].draindamage + 3;
  magic_perception(ch, force, type);
  sprintf(buf, "Drain Test: F:%d%s T:%d Sk: %d", force, wound_name[damage], target, GET_WIL(ch) + GET_DRAIN(ch)); 
  success = success_test(GET_WIL(ch) + GET_DRAIN(ch), target);
  damage = convert_damage(stage(-success, damage));
  sprintf(ENDOF(buf), " S:%d S:%d", success, damage);
  act(buf, FALSE, ch, NULL, NULL, TO_ROLLS); 
  if (force > GET_MAG(ch) / 100 || IS_PROJECT(ch)) {
    GET_PHYSICAL(ch) -= damage *100;
    if (IS_PROJECT(ch)) {
      GET_PHYSICAL(ch->desc->original) -= damage * 100;
      AFF_FLAGS(ch->desc->original).SetBit(AFF_DAMAGED);
    }
  } else
  {
    GET_MENTAL(ch) -= damage * 100;
    if (GET_MENTAL(ch) < 0) {
      GET_PHYSICAL(ch) += GET_MENTAL(ch);
      GET_MENTAL(ch) = 0;
    }
  }
  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 spell casting and fall unconcious!\r\n", ch);
    act("$n collapses unconscious!", FALSE, ch, 0, 0, TO_ROOM);
  } else if (GET_POS(ch) == POS_DEAD)
  {
    if (FIGHTING(ch))
      stop_fighting(ch);
    send_to_char("The feedback from spell casting floods your body, killing you...\r\n", ch);
    act("$n suddenly collapases, dead!", FALSE, ch, 0, 0, TO_ROOM);
    die(ch);
    return TRUE;
  }
  return FALSE;


}

struct char_data *find_target_at_range(struct char_data *ch, char *name, char *direction)
{
  return NULL;
}
void create_sustained(struct char_data *ch, struct char_data *vict, int spell, int force, int sub, int success, int drain)
{
  struct obj_data *focus = NULL;
  for (int i = 0; !focus && i < NUM_WEARS; i++)
    if (GET_EQ(vict, i) && GET_OBJ_TYPE(GET_EQ(vict, i)) == ITEM_FOCUS && !GET_OBJ_VAL(GET_EQ(vict, i), 4) &&
        GET_OBJ_VAL(GET_EQ(vict, i), 3) == spell && GET_OBJ_VAL(GET_EQ(vict, i), 1) >= force && !GET_OBJ_VAL(GET_EQ(vict, i), 9))
      focus = GET_EQ(vict, i);
  GET_SUSTAINED_NUM(ch)++;
  if (focus)
  {
    GET_SUSTAINED_FOCI(ch)++;
    GET_OBJ_VAL(focus, 4)++;
    GET_FOCI(ch)++;
  }
  struct sustain_data *sust = new sustain_data;
  sust->spell = spell;
  sust->subtype = sub;
  sust->force = force;
  sust->success = success;
  sust->other = vict;
  sust->caster = TRUE;
  sust->drain = drain;
  sust->idnum = number(0, 1000);
  sust->next = GET_SUSTAINED(ch);
  sust->focus = focus;
  GET_SUSTAINED(ch) = sust;
  struct sustain_data *vsust = new sustain_data;
  *vsust = *sust;
  vsust->caster = FALSE;
  vsust->other = ch;
  vsust->next = GET_SUSTAINED(vict);
  vsust->focus = focus;
  GET_SUSTAINED(vict) = vsust;
  spell_modify(vict, vsust, TRUE);
}

void spell_bonus(struct char_data *ch, int spell, int &skill, int &target)
{
  if (world[ch->in_room].background[1] == AURA_POWERSITE)
    skill += world[ch->in_room].background[0];
  else target += world[ch->in_room].background[0];
  if (GET_TRADITION(ch) == TRAD_SHAMANIC)
    totem_bonus(ch, SPELLCASTING, spell, target, skill);
  else if (GET_TRADITION(ch) == TRAD_HERMETIC && GET_SPIRIT(ch) && spells[spell].category != HEALTH)
  {
    for (struct spirit_data *spirit = GET_SPIRIT(ch); spirit; spirit = spirit->next)
      if (((spells[spell].category == MANIPULATION && spirit->type == ELEM_EARTH) ||
           (spells[spell].category == COMBAT && spirit->type == ELEM_FIRE) ||
           (spells[spell].category == ILLUSION && spirit->type == ELEM_WATER) ||
           (spells[spell].category == DETECTION && spirit->type == ELEM_AIR)) &&
          spirit->called == TRUE) {
        struct char_data *mob = find_spirit_by_id(spirit->id, GET_IDNUM(ch));
        if (mob && MOB_FLAGGED(mob, MOB_AIDSORCERY)) {
          send_to_char(ch, "%s aids your spell casting.\r\n", CAP(GET_NAME(mob)));
          MOB_FLAGS(mob).RemoveBit(MOB_AIDSORCERY);
          skill += GET_LEVEL(mob);
        }
      }
  }
  int origskill = skill;
  if (GET_FOCI(ch) > 0)
  {
    for (int i = 0; i < NUM_WEARS && skill == origskill; i++)
      if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_FOCUS)
        switch (GET_OBJ_VAL(GET_EQ(ch, i), 0)) {
        case FOCI_EXPENDABLE:
          if (spells[spell].category == GET_OBJ_VAL(GET_EQ(ch, i), 3)) {
            skill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
            extract_obj(GET_EQ(ch, i));
          }
          break;
        case FOCI_SPEC_SPELL:
          if (spell == GET_OBJ_VAL(GET_EQ(ch, i), 3))
            skill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
          break;
        case FOCI_SPELL_CAT:
          if (spells[spell].category == GET_OBJ_VAL(GET_EQ(ch, i), 3))
            skill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
          break;
        }
  }
}

bool find_duplicate_spell(struct char_data *ch, struct char_data *vict, int spell, int sub)
{
  if (spells[spell].duration == INSTANT)
    return FALSE;
  struct sustain_data *sus;
  if (!vict || vict == ch)
    sus = GET_SUSTAINED(ch);
  else
    sus = GET_SUSTAINED(vict);
  for (; sus; sus = sus->next)
    if (!sus->caster && sus->spell == spell && sus->subtype == sub)
    {
      send_to_char(ch, "%s are already affected by that spell.\r\n", (!vict || vict == ch) ? "You" : "They");
      return TRUE;
    }
  return FALSE;
}

bool check_spell_victim(struct char_data *ch, struct char_data *vict, int spell)
{
  if (!vict)
    send_to_char(NOPERSON, ch);
  else if (((IS_PROJECT(ch) || IS_ASTRAL(ch)) && !(IS_DUAL(vict) || IS_ASTRAL(vict) || IS_PROJECT(vict))) ||
           ((IS_PROJECT(vict) || IS_ASTRAL(vict)) && !(IS_DUAL(ch) || IS_ASTRAL(ch) || IS_PROJECT(ch))))
    send_to_char("They aren't accessible from this plane.\r\n", ch);
  else if (spells[spell].physical && IS_ASTRAL(vict))
    send_to_char("That spell can't affect beings with no physical form.\r\n", ch);
  else
    return TRUE;
  return FALSE;
}

int reflect_spell(struct char_data *ch, struct char_data *vict, int spell, int force, int sub, int target, int &success)
{
  success -= success_test(GET_REFLECT(vict), force);
  if (success < 0) {
    success *= -1;
    success = success_test(success, target);
    send_to_char("You reflect the spell back!\r\n", vict);
    send_to_char("Your spell is reflected back at you!\r\n", ch);
    return TRUE;
  }
  return FALSE;
}

int resist_spell(struct char_data *ch, int spell, int force, int sub)
{
  int skill = 0;
  if (sub)
    skill = GET_ATT(ch, sub);
  else if (spell == SPELL_CHAOS)
    skill = GET_INT(ch);
  else if (spells[spell].physical)
    skill = GET_BOD(ch);
  else
    skill = GET_WIL(ch);
  if (GET_TRADITION(ch) == TRAD_SHAMANIC) {
    if (GET_TOTEM(ch) == TOTEM_HORSE && spells[spell].category == GET_TOTEMSPIRIT(ch))
      skill--;
    else if (GET_TOTEM(ch) == TOTEM_LEOPARD && spells[spell].category == ILLUSION)
      skill--;
  }
  if (GET_TRADITION(ch) == TRAD_ADEPT)
  {
    if (GET_POWER(ch, ADEPT_MAGIC_RESISTANCE))
      skill += GET_POWER(ch, ADEPT_MAGIC_RESISTANCE);
    if (GET_POWER(ch, ADEPT_SPELL_SHROUD) && spells[spell].category == DETECTION)
      skill += GET_POWER(ch, ADEPT_SPELL_SHROUD);
    if (GET_POWER(ch, ADEPT_TRUE_SIGHT) && spells[spell].category == ILLUSION)
      skill += GET_POWER(ch, ADEPT_TRUE_SIGHT);
  }
  skill += GET_SDEFENSE(ch);
  return success_test(skill, force);
}

void cast_combat_spell(struct char_data *ch, int spell, int force, char *arg)
{
  struct char_data *vict = NULL;
  two_arguments(arg, buf, buf1);
  bool reflected = FALSE;
  int basedamage = 0;
  if (!*buf)
  {
    send_to_char("What damage level do you wish to cast that spell at?\r\n", ch);
    return;
  } else
  {
    for (basedamage = 0; *wound_name[basedamage] != '\n'; basedamage++)
      if (is_abbrev(buf, wound_name[basedamage]))
        break;
    if (basedamage > 4 || basedamage == 0) {
      send_to_char("That is not a valid damage level, please choose between Light, Moderate, Serious and Deadly.\r\n", ch);
      return;
    }
  }
  if (*buf1)
    vict = get_char_room_vis(ch, buf1);
  if (!check_spell_victim(ch, vict, spell))
    return;
  if (ch == vict) {
    send_to_char("You can't target yourself with a combat spell!\r\n", ch);
    return;
  }
  if (world[ch->in_room].peaceful) {
    send_to_char("This room just has a peaceful, easy feeling...\r\n", ch);
    return;
  }
  int target = modify_target(ch), skill = GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), success = 0;
  spell_bonus(ch, spell, skill, target);
  if (skill == -1)
    return;
  check_killer(ch, vict);
  struct char_data *temp = vict;
  switch (spell)
  {
  case SPELL_MANABOLT:
    target += GET_WIL(vict);
    if (!IS_NPC(ch) && (IS_NPC(vict) && MOB_FLAGGED(vict, MOB_INANIMATE)) || (PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict)))
      success = -1;
    else
      success = success_test(skill, target);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target, success))) {
      vict = ch;
      ch = temp;      
    }
    success -= resist_spell(vict, spell, force, 0);
    if (success > 0) {
      int dam = convert_damage(stage(success, basedamage));
      if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n falls to the floor as ^Rblood^n erupts from every pore on $s head.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Your world turns to red as you feel blood flow from every pore on your head.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("$n grabs $s head in pain as blood flows from $s ears.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Your head is filled with immense pain as your ears begin to bleed.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n grabs ahold of $s head in pain.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("A sudden flash of pain in your head causes you to reflexivly grab it.\r\n", vict);
      } else {
        act("$n seems to flinch slightly as blood trickles from $s nose.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Slight pain fills your mind from an unknown source.\r\n", vict);
      }
      damage(ch, vict, dam, TYPE_COMBAT_SPELL, PHYSICAL);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  case SPELL_STUNBOLT:
    target += GET_WIL(vict);
    if (!IS_NPC(ch) && IS_NPC(vict) && MOB_FLAGGED(vict, MOB_INANIMATE) || (PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict)))
      success = -1;
    else
      success = success_test(skill, target);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target, success))) {
      vict = ch;
      ch = temp;      
    }
    success -= resist_spell(vict, spell, force, 0);
    if (success > 0) {
      int dam = convert_damage(stage(success, basedamage));
      if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n falls to the floor unconcious.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The world turns black around you as you suddenly fall unconcious.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("$n grabs $s head and cringes.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Your head is filled with an unbarable pressure as your vision begins to fade.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n shakes $s head forcibly as though trying to clear it.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("A wave of tiredness comes over you, but seems to clear slightly as you shake your head.\r\n", vict);
      } else  {
        act("$n recoils slightly as though hit by an invisble force.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You mind goes slighlty hazy as though you had just been punched.\r\n", vict);
      }
      damage(ch, vict, dam, TYPE_COMBAT_SPELL, MENTAL);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  case SPELL_POWERBOLT:
    target += GET_BOD(vict);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else
      success = success_test(skill, target);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target, success))) {
      vict = ch;
      ch = temp;      
    }
    success -= resist_spell(vict, spell, force, 0);
    if (success > 0) {
      int dam = convert_damage(stage(success, basedamage));
      if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n screams loudly as $s body is torn asunder by magic.", FALSE, vict, 0, 0, TO_ROOM);
        send_to_char("You begin to feel yourself tearing apart.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("$n cries out in pain as $s begins to bleed from nearly every pore on $s body.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You uncontrollable scream as an immense force tears at your body.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n grabs $s stomach as a trickle of blood comes from $s mouth.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Your torso is filled with a sharp stabbing pain as you cough up some blood.\r\n", vict);
      } else {
        act("$n grimaces, obviously afflicted with mild pain.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel a dull throb of pain flow through your body.\r\n", vict);
      }
      damage(ch, vict, dam, TYPE_COMBAT_SPELL, PHYSICAL);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  }
}

void cast_detection_spell(struct char_data *ch, int spell, int force, char *arg, struct char_data *mob)
{
  struct char_data *vict = NULL;
  if (mob)
    vict = mob;
  else if (*arg)
    vict = get_char_room_vis(ch, arg);
  if (!check_spell_victim(ch, vict, spell))
    return;
  if (find_duplicate_spell(ch, vict, spell, 0))
    return;
  int target = modify_target(ch), skill = GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), success = 0;
  spell_bonus(ch, spell, skill, target);
  if (skill == -1)
    return;
  switch (spell)
  {
  case SPELL_MINDLINK:
    success = success_test(skill, 4 + target);
    for (struct sustain_data *sust = GET_SUSTAINED(ch); sust; sust = sust->next)
      if (sust->spell == SPELL_MINDLINK) {
        send_to_char("You are already under the influence of a mindlink.\r\n", ch);
        return;
      }
    for (struct sustain_data *sust = GET_SUSTAINED(vict); sust; sust = sust->next)
      if (sust->spell == SPELL_MINDLINK) {
        send_to_char("They are already under the influence of a mindlink.\r\n", ch);
        return;
      }
    if (success > 0) {
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);    
      vict->char_specials.mindlink = ch;
      ch->char_specials.mindlink = vict;
    } else send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_COMBATSENSE:
    success = success_test(skill, 4 + target);
    if (success > 0) {
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
      send_to_char("The world seems to slow down around you as your sense of your surroundings becomes clearer.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  }

}

void cast_health_spell(struct char_data *ch, int spell, int sub, int force, char *arg, struct char_data *mob)
{
  struct char_data *vict = NULL;
  if (mob)
    vict = mob;
  else if (*arg)
    vict = get_char_room_vis(ch, arg);
  if (!check_spell_victim(ch, vict, spell))
    return;
  if (find_duplicate_spell(ch, vict, spell, sub))
    return;
  int target = modify_target(ch), skill = GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), success = 0, drain = LIGHT;
  spell_bonus(ch, spell, skill, target);
  if (skill == -1)
    return;
  bool cyber = TRUE;
  switch (spell)
  {
  case SPELL_DETOX:
    if ((GET_DRUG_STAGE(vict) == 1 || GET_DRUG_STAGE(vict) == 2) && GET_DRUG_AFFECT(vict))
      target = drug_types[GET_DRUG_AFFECT(vict)].power;
    if (!target) {
      send_to_char("They aren't affected by any drugs.\r\n", ch);
      return;
    }
    success = success_test(skill, target);
    if (success > 0 && !AFF_FLAGGED(vict, AFF_DETOX)) {
      create_sustained(ch, vict, spell, force, 0, success, spells[SPELL_STABILIZE].draindamage);
      send_to_char("You notice the affects of the drugs suddenly wear off.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_STABILIZE:
    target += 4 + ((GET_LAST_DAMAGETIME(vict) - time(0)) / SECS_PER_MUD_HOUR);
    success = success_test(skill, target);
    if (success > 0 && force >= (GET_PHYSICAL(vict) <= 0 ? -(GET_PHYSICAL(vict) / 100) : 50)) {
      create_sustained(ch, vict, spell, force, 0, success, spells[SPELL_STABILIZE].draindamage);
      send_to_char("Your condition stabilizes, you manage to grab a thin hold on life.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_RESISTPAIN:
    success = success_test(skill, target + 4);
    if (GET_PHYSICAL(vict) <= 0)
      drain = DEADLY;
    else if (GET_PHYSICAL(vict) <= 300)
      drain = SERIOUS;
    else if (GET_PHYSICAL(vict) <= 700)
      drain = MODERATE;
    if (success > 0 && !AFF_FLAGGED(ch, AFF_RESISTPAIN)) {
      create_sustained(ch, vict, spell, force, 0, success, spells[SPELL_RESISTPAIN].draindamage);
      send_to_char("Your pain begins to fade.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
      vict->points.resistpain = MIN(force, success) * 100;
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_HEALTHYGLOW:
    success = success_test(skill, 4 + target);
    if (success > 0) {
      create_sustained(ch, vict, spell, force, 0, success, spells[SPELL_HEALTHYGLOW].draindamage);
      send_to_char("You begin to feel healthier and more attractive.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_TREAT:
    if (!AFF_FLAGGED(vict, AFF_DAMAGED)) {
      send_to_char("They are beyond the help of this spell.\r\n", ch);
      return;
    }
  case SPELL_HEAL:
    success = MIN(force, success_test(skill, 10 - (int)(GET_ESS(vict) / 100) + target + (int)(GET_INDEX(ch) / 200)));
    if (GET_PHYSICAL(vict) <= 0)
      drain = DEADLY;
    else if (GET_PHYSICAL(vict) <= 300)
      drain = SERIOUS;
    else if (GET_PHYSICAL(vict) <= 700)
      drain = MODERATE;
    if (success < 1 || AFF_FLAGGED(vict, AFF_HEALED) || GET_PHYSICAL(vict) == GET_MAX_PHYSICAL(vict)) {
      send_to_char(FAILED_CAST, ch);
    } else {
      AFF_FLAGS(vict).SetBit(AFF_HEALED);
      send_to_char("A warm feeling floods your body.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
      create_sustained(ch, vict, spell, force, 0, success, drain);
      update_pos(vict);
    }
    spell_drain(ch, spell, force, drain);
    break;
  case SPELL_INCREF1:
  case SPELL_INCREF2:
  case SPELL_INCREF3:
    if (GET_REAL_REA(vict) != GET_REA(vict) || GET_INIT_DICE(ch))
      success = -1;
    else
      success = success_test(skill, GET_REA(vict) + target);
    if (success > 0) {
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
      send_to_char("The world slows down around you.\r\n", ch);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_INCREA:
    sub = REA;
  case SPELL_DECATTR:
  case SPELL_DECCYATTR:
  case SPELL_INCATTR:
  case SPELL_INCCYATTR:
    if (GET_ATT(vict, sub) != GET_REAL_ATT(vict, sub)) {
      if (GET_TRADITION(vict) == TRAD_ADEPT && sub < CHA) {
        switch (sub) {
        case BOD:
          if (BOOST(vict)[2][0] || GET_POWER(vict, ADEPT_IMPROVED_BOD))
            cyber = false;
          break;
        case QUI:
          if (BOOST(vict)[1][0] || GET_POWER(vict, ADEPT_IMPROVED_QUI))
            cyber = false;
          break;
        case STR:
          if (BOOST(vict)[0][0] || GET_POWER(vict, ADEPT_IMPROVED_STR))
            cyber = false;
          break;
        }
      } else if (GET_SUSTAINED(vict))
        for (struct sustain_data *sus = GET_SUSTAINED(vict); sus; sus = sus->next)
          if (sus->caster == FALSE && (sus->spell == SPELL_INCATTR || sus->spell == SPELL_DECATTR) && sus->subtype == sub) {
            cyber = false;
            break;
          }
      if (cyber && (spell == SPELL_DECATTR || spell == SPELL_INCATTR || spell == SPELL_INCREA)) {
        sprintf(buf, "$N's %s has been modified by technological means and is immune to this spell.\r\n", attributes[sub]);
        act(buf, TRUE, ch, 0, vict, TO_CHAR);
        return;
      }
    }
    if ((spell == SPELL_DECCYATTR || spell == SPELL_INCCYATTR) && (!cyber || GET_ATT(vict, sub) == GET_REAL_ATT(vict, sub))) {
      sprintf(buf, "$N's %s has not been modified by technological means and is immune to this spell.\r\n", attributes[sub]);
      act(buf, TRUE, ch, 0, vict, TO_CHAR);
      return;
    }
    if (spell == SPELL_INCREA)
      target += GET_REA(vict);
    else if (spell == SPELL_INCATTR || spell == SPELL_INCCYATTR)
      target += GET_ATT(vict, sub);
    else
      target += 10 - (GET_ESS(vict) / 100);
    success = (int)(success_test(skill, target) -
                    ((spell == SPELL_DECATTR || spell == SPELL_DECCYATTR) ? resist_spell(vict, spell, force, sub) : 0));
    if (success > 0) {
      create_sustained(ch, vict, spell, force, sub, success, spells[spell].draindamage);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
      send_to_char("You feel your body tingle.\r\n", vict);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  }
}

void cast_illusion_spell(struct char_data *ch, int spell, int force, char *arg, struct char_data *mob)
{
  struct char_data *vict = NULL;
  bool reflected = FALSE;
  if (mob)
    vict = mob;
  else if (*arg)
    vict = get_char_room_vis(ch, arg);
  if (find_duplicate_spell(ch, vict, spell, 0))
    return;
  int target = modify_target(ch), skill = GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), success = 0;
  spell_bonus(ch, spell, skill, target);
  if (skill == -1)
    return;
  struct char_data *temp = vict;
  switch (spell)
  {
  case SPELL_CONFUSION:
  case SPELL_CHAOS:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (spell == SPELL_CONFUSION)
      target += GET_WIL(vict);
    else
      target += GET_INT(vict);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else
      success = success_test(skill, target);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target, success))) {
      vict = ch;
      ch = temp;      
    }
    success -= resist_spell(vict, spell, force, 0);
    if (success > 0) {
      send_to_char("Coherent thought is suddenly a foreign concept.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(reflected ? vict : ch, spell, force, 0);
    break;
  case SPELL_INVIS:
  case SPELL_IMP_INVIS:
    if (!check_spell_victim(ch, vict, spell))
      return;
    success = success_test(skill, target + 4);
    if (success > 0) {
      act("You blink and suddenly $n is gone!", TRUE, vict, 0, 0, TO_ROOM);
      send_to_char("You feel your body tingle.\r\n", vict);
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_STEALTH:
    if (!check_spell_victim(ch, vict, spell))
      return;
    success = success_test(skill, target + 4);
    if (success > 0) {
      act("You successfully sustain that spell on $n.", FALSE, vict, 0, ch, TO_VICT);
      send_to_char("Your every move becomes silent.", vict);
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_SILENCE:
    success = success_test(skill, target + 4);
    if (success > 0) {
      act("The room falls silent.", FALSE, ch, 0, 0, TO_ROOM);
      act("The room falls silent.", FALSE, ch, 0, 0, TO_CHAR);
      create_sustained(ch, ch, spell, force, 0, success, spells[spell].draindamage);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  }
}

void cast_manipulation_spell(struct char_data *ch, int spell, int force, char *arg, struct char_data *mob)
{
  struct char_data *vict = NULL;
  bool reflected = FALSE;
  int basedamage = 0;
  switch (spell)
  {
  case SPELL_ACIDSTREAM:
  case SPELL_TOXICWAVE:
  case SPELL_FLAMETHROWER:
  case SPELL_FIREBALL:
  case SPELL_LIGHTNINGBOLT:
  case SPELL_BALLLIGHTNING:
  case SPELL_CLOUT:
    two_arguments(arg, buf, buf1);
    if (world[ch->in_room].peaceful) {
      send_to_char("This room just has a peaceful, easy feeling...\r\n", ch);
      return;
    }
    if (!*buf) {
      send_to_char("What damage level do you wish to cast that spell at?\r\n", ch);
      return;
    } else {
      for (basedamage = 0; *wound_name[basedamage] != '\n'; basedamage++)
        if (is_abbrev(buf, wound_name[basedamage]))
          break;
      if (basedamage > 4 || basedamage == 0) {
        send_to_char("That is not a valid damage level, please choose between Light, Moderate, Serious and Deadly.\r\n", ch);
        return;
      }
    }
    if (*buf1)
      vict = get_char_room_vis(ch, buf1);
    if (ch == vict) {
      send_to_char("You can't target yourself with a combat spell!\r\n", ch);
      return;
    }
    break;
  default:
    if (mob)
      vict = mob;
    else if (*arg)
      vict = get_char_room_vis(ch, arg);
  }
  if (find_duplicate_spell(ch, vict, spell, 0))
    return;
  int target = modify_target(ch), skill = IS_ELEMENTAL(ch) || IS_SPIRIT(ch) ? GET_LEVEL(ch) : GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), success = 0;
  if (!(IS_ELEMENTAL(ch) || IS_SPIRIT(ch)))
    spell_bonus(ch, spell, skill, target);
  if (skill == -1)
    return;
  struct char_data *temp = vict;
  switch (spell)
  {
  case SPELL_ARMOUR:
    if (!check_spell_victim(ch, vict, spell))
      return;
    success = success_test(skill, target + 6);
    if (success > 0) {
      create_sustained(ch, vict, spell, force, 0, success, spells[spell].draindamage);
      send_to_char("You feel your body tingle.\r\n", vict);
      act("You successfully sustain that spell on $N.", FALSE, ch, 0, vict, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_POLTERGEIST:
    success = success_test(skill, target + 4);
    if (success > 0) {
      create_sustained(ch, ch, spell, force, 0, success, spells[spell].draindamage);
      act("An invisible wind begins to spin small objects around the area!", FALSE, ch, 0, 0, TO_ROOM);
      act("An invisible wind begins to spin small objects around the area!", FALSE, ch, 0, 0, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_LIGHT:
    success = success_test(skill, target + 4);
    if (success > 0) {
      create_sustained(ch, ch, spell, force, 0, success, spells[spell].draindamage);
      act("Light radiates from $n, illuminating the area!", FALSE, ch, 0, 0, TO_ROOM);
      act("The area brightens as your light spell takes affect!", FALSE, ch, 0, 0, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_ICESHEET:
    if (!ch->in_room) {
      send_to_char("You can't create ice in here!\r\n", ch);
      return;
    }
    success = success_test(skill, target + 4);
    if (success > 0) {
      world[ch->in_room].icesheet[0] = (int)(3.14 * ((GET_MAG(ch) * GET_MAG(ch)) / 10000));
      world[ch->in_room].icesheet[1] = force + MIN(force, success / 2);
      act("The floor is suddenly covered in ice!", FALSE, ch, 0, 0, TO_ROOM);
      act("The floor is suddenly covered in ice!", FALSE, ch, 0, 0, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_IGNITE:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (ch == vict) {
      send_to_char("You can't target yourself with a combat spell!\r\n", ch);
      return;
    }
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else success = success_test(skill, target + 4);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target + 4, success))) {
      vict = ch;
      ch = temp;      
    }
    success -= resist_spell(vict, spell, force, 0);
    success -= GET_BOD(vict) / 2;

    if (success > 0) {
      send_to_char("You feel the room temperature sharply rise.\r\n", vict);
      act("You succeed in raising the body temperature of $N to dangerous levels.", FALSE, ch, 0, vict, TO_CHAR);
      create_sustained(ch, vict, spell, force, 0, success, MAX(1, 10 / MIN(force, success)));
      if (!FIGHTING(vict) && vict != ch && AWAKE(vict))
        set_fighting(vict, ch);
    } else {
      send_to_char("You feel your body heat slightly then return to normal.\r\n", vict);
      send_to_char("You fail to generate enough heat in your target.\r\n", ch);
    }
    spell_drain(reflected ? vict : ch, spell, force, 0);
    break;
  case SPELL_SHADOW:
    success = success_test(skill, target + 4);
    if (success > 0) {
      create_sustained(ch, ch, spell, force, 0, success, spells[spell].draindamage);
      act("Dark shadows fall over the area.", FALSE, ch, 0, 0, TO_ROOM);
      act("Dark shadows fall over the area as your spell takes affect.", FALSE, ch, 0, 0, TO_CHAR);
    } else
      send_to_char(FAILED_CAST, ch);
    spell_drain(ch, spell, force, 0);
    break;
  case SPELL_CLOUT:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (!AWAKE(vict))
      target -= 2;
    send_to_char("You feel a rush of air head towards you!", vict);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else
      success = success_test(skill, 4 + target) - success_test(GET_DEFENSE(vict) + GET_DEFENSE(vict) ? GET_POWER(vict, ADEPT_SIDESTEP) : 0, 4 + damage_modifier(vict, buf));
    if (success <= 0) {
      act("Your clout spell harmlessly disperses as $n dodges it.", FALSE, vict, 0, ch, TO_VICT);
      send_to_char("You easily dodge it!\r\n", vict);
      act("$n dodges out of the way of unseen force.", TRUE, vict, 0, ch, TO_NOTVICT);
    } else {
      success -= success_test(GET_BOD(vict) + GET_BODY(vict), force - GET_IMPACT(vict));
      int dam = convert_damage(stage(success, basedamage));
      if (!AWAKE(vict)) {
        act("$n's body recoils as though hit.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel a dull thud in the back of your mind.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n is thrown to the ground, unconcious, from an immense invisible force.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("It slams into you with the force of a freight train, knocking you to the ground, unconcious.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("$n stumbles backwards, almost losing $s footing, as $e is hit by an invisible force.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You stumble backwards, feeling groggy as the air slams into you at full force.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n steps back, and shakes $s head to clear it.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You step back as the force hits you, feeling a little worse for wear.\r\n", vict);
      } else if (dam > 0) {
        act("$n recoils, as if from a light punch.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel as though a fist strikes you across the face.\r\n", vict);
      } else {
        send_to_char("It rushes past you, not causing any damage.\r\n", vict);
        act("$n doesn't seem hurt by your clout spell.", FALSE, vict, 0, ch, TO_VICT);
      }
      damage(ch, vict, dam, TYPE_MANIPULATION_SPELL, MENTAL);
    }
    spell_drain(ch, spell, force, basedamage);
    break;
  case SPELL_FLAMETHROWER:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (!AWAKE(vict))
      target -= 2;
    else
      success -= success_test(GET_DEFENSE(vict) + GET_DEFENSE(vict) ? GET_POWER(vict, ADEPT_SIDESTEP) : 0, 4 + damage_modifier(vict, buf));
    act("$n's hands seem to spontaneously combust as $e directs a stream of flame at $N!", TRUE, ch, 0, vict, TO_ROOM);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else success = success_test(skill, target + 4);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target + 4, success))) {
      vict = ch;
      ch = temp;      
    }
    if (success <= 0) {
      act("$n dodges the flames, which disperese as they past $s.", FALSE, vict, 0, 0, TO_ROOM);
      send_to_char("You easily dodge the flames!\r\n", vict);
    } else {
      success -= success_test(GET_BOD(vict) + GET_BODY(vict) + GET_POWER(ch, ADEPT_TEMPERATURE_TOLERANCE), force - (GET_IMPACT(vict) / 2));
      int dam = convert_damage(stage(success, basedamage));
      if (!AWAKE(vict)) {
        act("$n spasms as the flames hit $m.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel a slight burning sensation in the back of your mind.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n is hit full force by the intense flames causing $m to fall to the ground, gurgling.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The flames burn intensely around you, your last memory before falling unconcious is the hideous pain.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("Screams as the flames impact $s body, horribly burning $m.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The flames crash into you, causing you great pain as they horribly burn you.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n cringes as the flames hit, patting at the spots where the flame continues to burn.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("As the flames hit you quickly pat at the spots that continue to burn, causing searing pain.\r\n", vict);
      } else if (dam > 0) {
        act("The flames burst around $m, causing seemingly little damage.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The flames burst around you causing you slight pain as it burns some of your hair.\r\n", vict);
      } else {
        act("The flames impact $n, but disperse on impact.", FALSE, vict, 0, ch, TO_ROOM);
        send_to_char("The flames rapidly disperse around you, causing only mild discomfort.\r\n", vict);
      }
      damage_equip(ch, vict, force, TYPE_FIRE);
      if (!damage(ch, vict, dam, TYPE_MANIPULATION_SPELL, PHYSICAL) && number(0, 6) <= basedamage + 1) {
        act("^RThe flames continue to burn around $m!^N", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("^RYou continue to burn!\r\n", vict);
        vict->points.fire[0] = srdice();
        vict->points.fire[1] = 0;
      }
    }
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  case SPELL_ACIDSTREAM:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (!AWAKE(vict))
      target -= 2;
    else
      success -= success_test(GET_DEFENSE(vict) + GET_DEFENSE(vict) ? GET_POWER(vict, ADEPT_SIDESTEP) : 0, 4 + damage_modifier(vict, buf));
    act("Dark clouds form around $n moments before it condenses into a dark sludge and flies towards $N!", TRUE, ch, 0, vict, TO_ROOM);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else success = success_test(skill, target + 4);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target + 4, success))) {
      vict = ch;
      ch = temp;      
    }
    if (success <= 0) {
      act("$n dodges the acid, which evaporates as it passes $s.", FALSE, vict, 0, 0, TO_ROOM);
      send_to_char("You easily dodge the acid!\r\n", vict);
    } else {
      success -= success_test(GET_BOD(vict) + GET_BODY(vict) + GET_POWER(ch, ADEPT_TEMPERATURE_TOLERANCE), force - (GET_IMPACT(vict) / 2));
      int dam = convert_damage(stage(success, basedamage));
      if (!AWAKE(vict)) {
        act("$n spasms as the acid hits $m.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel a slight burning sensation in the back of your mind.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("As the acid hits $n $e falls to the ground twitching and screaming as $s body smokes.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The fumes from the acid burning through your body fill your lungs, burning you from the inside out as you fade into unconciousness.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("The acid hits $n, $e cries out in pain as trails of smoke come from $s body.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The acid impacts you with a great force, causing you to step back as it burns through your skin.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n cringes as the acid hits.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The acid begins to burn your skin as it hits you, causing a bit of pain.\r\n", vict);
      } else if (dam > 0) {
        act("$n is splashed by the acid, causing nothing but mild irritation.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("The acid splashes against you causing a mild burning sensation.\r\n", vict);
      } else {
        act("The acid splashes on $n, but $e doesn't seem to flinch.", FALSE, vict, 0, ch, TO_ROOM);
        send_to_char("You are splashed by the acid, but it causes nothing more than a moments irritation.\r\n", vict);
      }
      damage_equip(ch, vict, force, TYPE_ACID);
      AFF_FLAGS(vict).SetBit(AFF_ACID);
      damage(ch, vict, dam, TYPE_MANIPULATION_SPELL, PHYSICAL);
    }
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  case SPELL_LIGHTNINGBOLT:
    if (!check_spell_victim(ch, vict, spell))
      return;
    check_killer(ch, vict);
    if (!AWAKE(vict))
      target -= 2;
    else
      success -= success_test(GET_DEFENSE(vict), 4 + damage_modifier(vict, buf));
    act("Lightning bursts forth from $n and heads directly towards $N!", TRUE, ch, 0, vict, TO_ROOM);
    if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(vict))
      success = -1;
    else success = success_test(skill, target + 4);
    if (success > 0 && GET_REFLECT(vict) && (reflected = reflect_spell(ch, vict, spell, force, 0, target + 4, success))) {
      vict = ch;
      ch = temp;      
    }
    if (success <= 0) {
      act("$n easily dodges it, and it vanishes to nothing.", FALSE, vict, 0, 0, TO_ROOM);
      send_to_char("You easily dodge it!\r\n", vict);
    } else {
      success -= success_test(GET_BOD(vict) + GET_BODY(vict) + GET_POWER(ch, ADEPT_TEMPERATURE_TOLERANCE), force - (GET_IMPACT(vict) / 2));
      int dam = convert_damage(stage(success, basedamage));
      if (!AWAKE(vict)) {
        act("$n's body goes into convulsions as the lightning flows through it.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel a slight burning sensation in the back of your mind.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 0) {
        act("$n is propelled backwards by the force of the lightning bolt, $s body smoking as it lands, not a sign of life from it.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You feel your body begin to spasm as the huge charge of electricity fries your nervous system.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 300) {
        act("$n is almost lifted in the air by the lightning, spasms filling $s body, as a thin trail of smoke rise from $m.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Your body is filled with pain as the lightning hits you, your limbs going into an uncontrolable seizure.\r\n", vict);
      } else if (GET_MENTAL(vict) - (dam * 100) <= 700) {
        act("$n spasms as the lightning hits $m, his body wracked with spasms as the lightning disipates.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("Pain flashes through your body as the lightning hits, your body wracked with serveral serious spasms.\r\n", vict);
      } else if (dam > 0) {
        act("$n visibly recoils as the lightning hits, but otherwise seems fine.", TRUE, vict, 0, 0, TO_ROOM);
        send_to_char("You recoil as the lightning hits you, your mind going fuzzy for a moment.\r\n", vict);
      } else {
        act("The lightning hits $n, but seems to be easily absorbed.", FALSE, vict, 0, ch, TO_ROOM);
        send_to_char("Your body absorbs the lightning without harm.\r\n", vict);
      }
      for (int i = 0; i < NUM_WEARS; i++)
        if (GET_EQ(vict, i) && (GET_OBJ_MATERIAL(GET_EQ(vict, i)) == 10 || GET_OBJ_MATERIAL(GET_EQ(vict, i)) == 11))
          damage_obj(vict, GET_EQ(vict, i), force, DAMOBJ_LIGHTNING);
      damage(ch, vict, dam, TYPE_MANIPULATION_SPELL, PHYSICAL);
    }
    spell_drain(reflected ? vict : ch, spell, force, basedamage);
    break;
  }
}

void cast_spell(struct char_data *ch, int spell, int sub, int force, char *arg)
{
  if (spells[spell].duration == SUSTAINED)
  {
    if (GET_SUSTAINED_NUM(ch) >= GET_SKILL(ch, SKILL_SORCERY)) {
      send_to_char("You cannot sustain anymore spells.\r\n", ch);
      return;
    }
  }
  switch (spells[spell].category)
  {
  case COMBAT:
    cast_combat_spell(ch, spell, force, arg);
    break;
  case DETECTION:
    cast_detection_spell(ch, spell, force, arg, NULL);
    break;
  case HEALTH:
    cast_health_spell(ch, spell, sub, force, arg, NULL);
    break;
  case ILLUSION:
    cast_illusion_spell(ch, spell, force, arg, NULL);
    break;
  case MANIPULATION:
    cast_manipulation_spell(ch, spell, force, arg, NULL);
    break;
  }
}

void mob_magic(struct char_data *ch)
{
  if (!FIGHTING(ch))
    return;
  char buf[MAX_STRING_LENGTH];
  int spell = 0, sub = 0, force, magic = GET_MAG(ch) / 100;
  if (GET_WIL(ch) <= 2)
    force = magic;
  else force = MIN(magic, number(1, 8));
  while (!spell) {
    switch (number (0, 12)) {
      case 0:
        spell = SPELL_POWERBOLT;
        break;
      case 1:
        spell = SPELL_MANABOLT;
        break;
      case 2:
        spell = SPELL_STUNBOLT;
        break;
      case 3:
        spell = SPELL_DECCYATTR;
        sub = number(0, 2);
        break;
      case 4:
        spell = SPELL_DECATTR;
        sub = number(0, 2);
        break;
      case 5:
        if (!affected_by_spell(FIGHTING(ch), SPELL_CONFUSION))
          spell = SPELL_CONFUSION;
        break;
      case 6:
        spell = SPELL_FLAMETHROWER;
        break;
      case 7:
        spell = SPELL_ACIDSTREAM;
        break;
      case 8:
        spell = SPELL_LIGHTNINGBOLT;
        break;
      case 9:
        spell = SPELL_CLOUT;
        break;
      case 10:
/*        if (!affected_by_spell(ch, SPELL_POLTERGEIST))
        spell = SPELL_POLTERGEIST;
        break;
*/      case 11:
        if (!world[ch->in_room].icesheet[1])
          spell = SPELL_ICESHEET;
        break;
      case 12:
        if (!affected_by_spell(FIGHTING(ch), SPELL_IGNITE) && !ch->points.fire[0])
          spell = SPELL_IGNITE;
        break;
    }
  }
  switch (spell) {
    case SPELL_FLAMETHROWER:
    case SPELL_MANABOLT:
    case SPELL_STUNBOLT:
    case SPELL_POWERBOLT:
    case SPELL_ACIDSTREAM:
    case SPELL_LIGHTNINGBOLT:
    case SPELL_CLOUT:
      sprintf(buf, "%s %s", wound_name[number(1, 4)], GET_CHAR_NAME(FIGHTING(ch)));
      break;
    default:
      strcpy(buf, GET_CHAR_NAME(FIGHTING(ch)));
  }
  cast_spell(ch, spell, sub, force, buf);
}


bool check_spirit_sector(rnum_t room, int spirit)
{
  if ((spirit == SPIRIT_WIND || spirit == SPIRIT_MIST || spirit == SPIRIT_STORM)) {
    if (SECT(room) == SPIRIT_HEARTH || SECT(room) == SPIRIT_FOREST || ROOM_FLAGGED(room, ROOM_INDOORS))
      return FALSE;
  } else if (SECT(room) != spirit)
    return FALSE;
  return TRUE;
}

void circle_build(struct char_data *ch, char *type, int force)
{
  if (IS_WORKING(ch))
  {
    send_to_char(TOOBUSY, ch);
    return;
  }
  if (GET_TRADITION(ch) != TRAD_HERMETIC || GET_ASPECT(ch) == ASPECT_SORCERER)
  {
    send_to_char("Only hermetic mages need to construct a hermetic circle.\r\n", ch);
    return;
  }
  if (ch->in_veh) {
    send_to_char("You can't build a lodge in a vehicle.\r\n", ch);
     return;
  }
  if (GET_NUYEN(ch) < force * force)
  {
    send_to_char(ch, "You need %d nuyen for the materials needed to construct that circle.\r\n", force * force);
    return;
  }
  int element = 0;
  for (;element < NUM_ELEMENTS; element++)
    if (is_abbrev(type, elements[element].name))
      break;
  if (element == NUM_ELEMENTS)
  {
    send_to_char("What element do you wish to dedicate this circle to?\r\n", ch);
    return;
  }
  GET_NUYEN(ch) -= force * force;
  struct obj_data *obj = read_object(115, VIRTUAL);
  GET_OBJ_VAL(obj, 1) = force;
  GET_OBJ_VAL(obj, 2) = element;
  GET_OBJ_VAL(obj, 3) = GET_IDNUM(ch);
  GET_OBJ_VAL(obj, 9) = force * 60;
  AFF_FLAGS(ch).SetBit(AFF_CIRCLE);
  GET_BUILDING(ch) = obj;
  obj_to_room(obj, ch->in_room);
  send_to_char("You begin to draw a hermetic circle.\r\n", ch);
  act("$n begins to draw a hermetic circle.\r\n", FALSE, ch, 0, 0, TO_ROOM);
}

void lodge_build(struct char_data *ch, int force)
{
  if (IS_WORKING(ch))
  {
    send_to_char(TOOBUSY, ch);
    return;
  }
  if (GET_TRADITION(ch) != TRAD_SHAMANIC)
  {
    send_to_char("Only shamans need to build a lodge.\r\n", ch);
    return;
  }
  if (ch->in_veh) {
    send_to_char("You can't build a lodge in a vehicle.\r\n", ch);
     return;
  }
  if (GET_NUYEN(ch) < force * 500)
  {
    send_to_char(ch, "You need %d nuyen worth of materials to construct that lodge.\r\n", force * 500);
    return;
  }
  if (force > GET_REAL_MAG(ch) / 50) {
    send_to_char("You can't create a lodge higher than twice your magic rating.\r\n", ch);
    return;
  }
  GET_NUYEN(ch) -= force * 500;
  struct obj_data *obj = read_object(114, VIRTUAL);
  GET_OBJ_VAL(obj, 1) = force;
  GET_OBJ_VAL(obj, 2) = GET_TOTEM(ch);
  GET_OBJ_VAL(obj, 3) = GET_IDNUM(ch);
  GET_OBJ_VAL(obj, 9) = force * 60 * 5;
  AFF_FLAGS(ch).SetBit(AFF_LODGE);
  GET_BUILDING(ch) = obj;
  obj_to_room(obj, ch->in_room);
  send_to_char(ch, "You begin to build a lodge to %s.\r\n", totem_types[GET_TOTEM(ch)]);
  act("$n begins to build a lodge.\r\n", FALSE, ch, 0, 0, TO_ROOM);
}

struct char_data *create_elemental(struct char_data *ch, int type, int force, int idnum, int trad)
{
  struct char_data *mob;
  if (trad == TRAD_HERMETIC)
    mob = read_mobile(elements[type].vnum, VIRTUAL);
  else
    mob = read_mobile(spirits[type].vnum, VIRTUAL);
  GET_REAL_BOD(mob) = force;
  GET_REAL_QUI(mob) = force;
  GET_REAL_STR(mob) = force;
  GET_REAL_CHA(mob) = force;
  GET_REAL_INT(mob) = force;
  GET_REAL_WIL(mob) = force;
  GET_ESS(mob) = force * 100;
  GET_LEVEL(mob) = force;
  GET_ACTIVE(mob) = GET_IDNUM(ch);
  GET_SPARE1(mob) = type;
  GET_SPARE2(mob) = force;
  GET_GRADE(mob) = idnum;
  if (trad == TRAD_HERMETIC)
    switch (type)
    {
    case ELEM_EARTH:
      GET_REAL_BOD(mob) += 4;
      GET_REAL_QUI(mob) -= 2;
      GET_REAL_STR(mob) += 4;
      break;
    case ELEM_FIRE:
      GET_REAL_BOD(mob)++;
      GET_REAL_QUI(mob) += 2;
      GET_REAL_STR(mob) -= 2;
      break;
    case ELEM_AIR:
      GET_REAL_BOD(mob) -= 2;
      GET_REAL_QUI(mob) += 3;
      GET_REAL_STR(mob) -= 3;
      break;
    case ELEM_WATER:
      GET_REAL_BOD(mob) += 2;
      break;
    }
  else
    switch (type)
    {
    case SPIRIT_CITY:
    case SPIRIT_FIELD:
    case SPIRIT_HEARTH:
      GET_REAL_BOD(mob)++;
      GET_REAL_QUI(mob) += 2;
      GET_REAL_STR(mob) -= 2;
      break;
    case SPIRIT_DESERT:
    case SPIRIT_FOREST:
    case SPIRIT_MOUNTAIN:
    case SPIRIT_PRAIRIE:
      GET_REAL_BOD(mob) += 4;
      GET_REAL_QUI(mob) -= 2;
      GET_REAL_STR(mob) += 4;
      break;
    case SPIRIT_MIST:
    case SPIRIT_STORM:
    case SPIRIT_WIND:
      GET_REAL_BOD(mob) -= 2;
      GET_REAL_QUI(mob) += 3;
      GET_REAL_STR(mob) -= 3;
      break;
    case SPIRIT_LAKE:
    case SPIRIT_RIVER:
    case SPIRIT_SEA:
    case SPIRIT_SWAMP:
      GET_REAL_BOD(mob) += 2;
      break;
    }
  if (ch->in_veh)
    char_to_room(mob, ch->in_veh->in_room);
  else
    char_to_room(mob, ch->in_room);
  add_follower(mob, ch);
  affect_total(mob);
  return mob;
}

ACMD(do_contest)
{
  if (GET_ASPECT(ch) == ASPECT_SORCERER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_CONJURING)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  struct char_data *mob, *caster = NULL;
  skip_spaces(&argument);
  if (!(mob = get_char_room_vis(ch, argument))) {
    send_to_char(ch, "Contest the binding of which %s?\r\n", GET_TRADITION(ch) ? "nature spirit" : "elemental");
    return;
  }
  if (GET_CHA(ch) <= GET_NUM_SPIRITS(ch)) {
    send_to_char(ch, "You cannot have any more %ss bound to you.\r\n", GET_TRADITION(ch) ? "nature spirit" : "elemental");
    return;
  }
  if (GET_TRADITION(ch) == TRAD_SHAMANIC && (GET_MOB_VNUM(mob) < 29 || GET_MOB_VNUM(mob) > 42)) {
    send_to_char("You can only contest the binding of a nature spirit.\r\n", ch);
    return;
  } else if (GET_TRADITION(ch) == TRAD_HERMETIC && (GET_MOB_VNUM(mob) < 25 || GET_MOB_VNUM(mob) > 28)) {
    send_to_char("You can only contest the binding of an elemental.\r\n", ch);
    return;
  }
  if (GET_ACTIVE(mob) == GET_IDNUM(ch)) {
    send_to_char(ch, "You already have that %s bound to you!\r\n", GET_TRADITION(ch) ? "nature spirit" : "elemental");
    return;
  }
  if (!GET_ACTIVE(mob)) {
    send_to_char("You can't bind a free spirit!\r\n", ch);
    return;
  }
  for (struct descriptor_data *d = descriptor_list; d; d = d->next)
    if (d->character && GET_IDNUM(d->character) == GET_ACTIVE(mob)) {
      caster = d->character;
      break;
    }
  if (!caster) {
    send_to_char("You fain to gain control!\r\n", ch);
    return;
  }
  int chskill = GET_SKILL(ch, SKILL_CONJURING), caskill = GET_SKILL(ch, SKILL_CONJURING) + GET_CHA(ch);
  for (int i = 0; i < NUM_WEARS; i++)
    if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(ch, i), 0) == FOCI_SPIRIT
        && GET_OBJ_VAL(GET_EQ(ch, i), 2) == GET_IDNUM(ch) && GET_OBJ_VAL(GET_EQ(ch, i), 3) == GET_SPARE1(mob) &&
        GET_OBJ_VAL(GET_EQ(ch, i), 4)) {
      chskill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
      break;
    }
  for (int i = 0; i < NUM_WEARS; i++)
    if (GET_EQ(caster, i) && GET_OBJ_TYPE(GET_EQ(caster, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(caster, i), 0) == FOCI_SPIRIT
        && GET_OBJ_VAL(GET_EQ(caster, i), 2) == GET_IDNUM(ch) && GET_OBJ_VAL(GET_EQ(caster, i), 3) == GET_SPARE1(mob) &&
        GET_OBJ_VAL(GET_EQ(caster, i), 4)) {
      chskill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
      break;
    }

  int chsuc = success_test(chskill, GET_LEVEL(mob)), casuc = success_test(caskill, GET_LEVEL(mob));
  struct spirit_data *temp;
  if (chsuc < 1 && casuc < 1) {
    for (struct spirit_data *sdata = GET_SPIRIT(caster); sdata; sdata = sdata->next)
      if (sdata->id == GET_GRADE(mob)) {
        REMOVE_FROM_LIST(sdata, GET_SPIRIT(caster), next);
        delete [] sdata;
        break;
      }
    if (GET_MOB_VNUM(mob) < 25 || GET_MOB_VNUM(mob) > 28) {
      act("$n senses an opportunity and vanishes!", TRUE, mob, 0, 0, TO_ROOM);
      extract_char(mob);
    } else {
      MOB_FLAGS(mob).SetBit(MOB_AGGRESSIVE);
      act("$n becomes uncontrolled!", TRUE, mob, 0, 0, TO_ROOM);
      GET_ACTIVE(mob) = 0;
    }
    conjuring_drain(caster, GET_LEVEL(mob));
    conjuring_drain(ch, GET_LEVEL(mob));
  } else if (chsuc > casuc) {
    send_to_char(ch, "You steal control of %s!\r\n", GET_NAME(mob));
    sprintf(buf, "$n steals control of %s!", GET_NAME(mob));
    act(buf, FALSE, ch, 0, caster, TO_VICT);
    for (struct spirit_data *sdata = GET_SPIRIT(caster); sdata; sdata = sdata->next)
      if (sdata->id == GET_GRADE(mob)) {
        REMOVE_FROM_LIST(sdata, GET_SPIRIT(caster), next);
        sdata->services = chsuc - casuc;
        sdata->next = GET_SPIRIT(ch);
        GET_SPIRIT(ch) = sdata->next;
        GET_NUM_SPIRITS(ch)++;
        GET_NUM_SPIRITS(caster)--;
        break;
      }
    GET_ACTIVE(mob) = GET_IDNUM(ch);
    conjuring_drain(caster, GET_LEVEL(mob));
    conjuring_drain(ch, GET_LEVEL(mob));
  } else {
    send_to_char("You fail to gain control!\r\n", ch);
    sprintf(buf, "$n tries to steal control of %s.", GET_NAME(mob));
    act(buf, FALSE, ch, 0, caster, TO_VICT);
    conjuring_drain(ch, GET_LEVEL(mob));
  }
}
ACMD(do_unbond)
{
  if (GET_TRADITION(ch) == TRAD_MUNDANE) {
    send_to_char("You can't unbond something with no sense of the astral plane.\r\n", ch);
    return;
  }
  struct obj_data *obj = NULL;
  struct char_data *vict;
  if (!generic_find(argument,  FIND_OBJ_INV | FIND_OBJ_EQUIP, ch, &vict, &obj)) {
    send_to_char("You don't have that item.\r\n", ch);
    return;
  }
  if (GET_OBJ_TYPE(obj) == ITEM_FOCUS && GET_OBJ_VAL(obj, 2)) {
    send_to_char("You severe the focus's bond with the astral plane.\r\n", ch);
    GET_OBJ_VAL(obj, 2) = 0;
  } else send_to_char("You can't unbond that.\r\n", ch);
}

ACMD(do_bond)
{
  if (!*argument) {
    send_to_char("What do you want to bond?\r\n", ch);
    return;
  }
  half_chop(argument, buf1, buf2);
  struct obj_data *obj;
  int karma = 0, spirit = 0;
  struct spell_data *spell = GET_SPELLS(ch);

  for (obj = ch->carrying; obj; obj = obj->next_content)
    if (isname(buf1, obj->text.keywords) || isname(buf2, GET_OBJ_NAME(obj)))
      break;
  if (!obj)
    for (int i = 0; i < NUM_WEARS && !obj; i++)
      if (GET_EQ(ch, i) && (isname(buf1, GET_EQ(ch, i)->text.keywords) || isname(buf1, GET_OBJ_NAME(GET_EQ(ch, i)))))
        obj = GET_EQ(ch, i);
  if (!obj) {
    send_to_char("You don't have that item.\r\n", ch);
    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) {
    if (GET_TRADITION(ch) == TRAD_MUNDANE)
      send_to_char(ch, "You can't bond foci.\r\n");
    else if (GET_TRADITION(ch) == TRAD_ADEPT && GET_OBJ_VAL(obj, 0) != FOCI_WEAPON)
      send_to_char("Adepts can only bond weapon foci.\r\n", ch);
    else if (IS_WORKING(ch))
      send_to_char(TOOBUSY, ch);
    else if (GET_POS(ch) > POS_SITTING)
      send_to_char("You must be sitting to perform a bonding ritual.\r\n", ch);
    else if (GET_OBJ_VAL(obj, 2) == GET_IDNUM(ch)) {
      if (GET_OBJ_VAL(obj, 9)) {
        GET_OBJ_VAL(obj, 9) = GET_OBJ_VAL(obj, 1) * 60;
        send_to_char(ch, "You restart the ritual to bond %s.\r\n", GET_OBJ_NAME(obj));
        act("$n begins a ritual to bond $o.", TRUE, ch, obj, 0, TO_ROOM);
        AFF_FLAGS(ch).SetBit(AFF_BONDING);
        ch->char_specials.programming = obj;
      } else
        send_to_char("You have already bonded this focus.\r\n", ch);
    } else {
      switch (GET_OBJ_VAL(obj, 0)) {
      case FOCI_SPEC_SPELL:
        karma = GET_OBJ_VAL(obj, 1);
        break;
      case FOCI_EXPENDABLE:
      case FOCI_SPELL_CAT:
        if (!*buf2) {
          send_to_char("Bond which spell category?\r\n", ch);
          return;
        }
        for (; spirit <= MANIPULATION; spirit++)
          if (is_abbrev(buf2, spell_category[spirit]))
            break;
        if (spirit > MANIPULATION) {
          send_to_char("That is not a valid category.\r\n", ch);
          return;
        }
        if (GET_OBJ_VAL(obj, 0) == FOCI_SPELL_CAT)
          karma = GET_OBJ_VAL(obj, 1) * 3;
        break;
      case FOCI_SPIRIT:
        if (!*buf2) {
          send_to_char("Bond which spirit type?\r\n", ch);
          return;
        }
        if (GET_TRADITION(ch) == TRAD_HERMETIC) {
          for (; spirit < NUM_ELEMENTS; spirit++)
            if (is_abbrev(buf2, elements[spirit].name))
              break;
        } else
          for (; spirit < NUM_SPIRITS; spirit++)
            if (is_abbrev(buf2, spirits[spirit].name))
              break;
        if (GET_TRADITION(ch) == TRAD_HERMETIC ? spirit == NUM_ELEMENTS : spirit == NUM_SPIRITS) {
          send_to_char(ch, "That is not a valid %s.\r\n", GET_TRADITION(ch) == TRAD_HERMETIC ? "elemental" : "spirit");
          return;
        }
        karma = GET_OBJ_VAL(obj, 1) * 2;
        break;
      case FOCI_POWER:
        karma = GET_OBJ_VAL(obj, 1) * 5;
        break;
      case FOCI_SUSTAINED:
        karma = GET_OBJ_VAL(obj, 1);
        break;
      }
      if (GET_OBJ_VAL(obj, 0) == FOCI_SUSTAINED || GET_OBJ_VAL(obj, 0) == FOCI_SPEC_SPELL) {
        for (;spell; spell = spell->next)
          if (is_abbrev(buf2, spell->name)) {
            spirit = spell->type;
            break;
          }
        if (!spell) {
          send_to_char("You don't know that spell to bond.\r\n", ch);
          return;
        }
        if (GET_OBJ_VAL(obj, 0) == FOCI_SUSTAINED && spells[spirit].duration != SUSTAINED) {
          send_to_char("You don't need to bond a sustaining foci for this spell.\r\n", ch);
          return;
        }
      }
      if (PLR_FLAGGED(ch, PLR_AUTH)) {
        if (GET_FORCE_POINTS(ch) < karma) {
          send_to_char(ch, "You don't have enough force points to bond that (Need %d).\r\n", karma);
          return;
        }
        GET_FORCE_POINTS(ch) -= karma;
        GET_OBJ_VAL(obj, 9) = 1;
      } else {
        if (GET_KARMA(ch) < karma * 100) {
          send_to_char(ch, "You don't have enough karma to bond that (Need %d).\r\n", karma);
          return;
        }
        GET_KARMA(ch) -= karma * 100;
        GET_OBJ_VAL(obj, 9) = GET_OBJ_VAL(obj, 1) * 60;
      }
      GET_OBJ_VAL(obj, 2) = GET_IDNUM(ch);
      GET_OBJ_VAL(obj, 3) = spirit;
      GET_OBJ_VAL(obj, 5) = GET_TRADITION(ch) == TRAD_HERMETIC ? 1 : 0;
      send_to_char(ch, "You begin the ritual to bond %s.\r\n", GET_OBJ_NAME(obj));
      act("$n begins a ritual to bond $o.", TRUE, ch, obj, 0, TO_ROOM);
      AFF_FLAGS(ch).SetBit(AFF_BONDING);
      ch->char_specials.programming = obj;
      return;
    }
  } else
    send_to_char("You cannot bond that item.\r\n", ch);
}

ACMD(do_release)
{
  if (GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  two_arguments(argument, buf, buf1);
  int i = 0;
  if (is_abbrev(buf, "spirit") || is_abbrev(buf, "elemental")) {
    if (GET_ASPECT(ch) == ASPECT_SORCERER || !GET_SKILL(ch, SKILL_CONJURING)) {
      send_to_char("You don't have the ability to do that.\r\n", ch);
    }
    if (!GET_SPIRIT(ch)) {
      send_to_char("You don't have any elementals bound to you.\r\n", ch);
      return;
    }
    int i;
    if (!(i = atoi(buf1)) || i > GET_NUM_SPIRITS(ch)) {
      send_to_char("Which spirit do you wish to release from your services?\r\n", ch);
      return;
    }
    for (struct spirit_data *spirit = GET_SPIRIT(ch); spirit; spirit = spirit->next)
      if (--i == 0) {
        struct spirit_data *temp;
        if (GET_TRADITION(ch) == TRAD_HERMETIC)
          send_to_char(ch, "You release %s from its obligations and it departs to the metaplanes.\r\n", GET_NAME(&mob_proto[real_mobile(elements[spirit->type].vnum)]));
        else
          send_to_char(ch, "You release %s from its obligations and it departs to the metaplanes.\r\n", GET_NAME(&mob_proto[real_mobile(spirits[spirit->type].vnum)]));
        if (spirit->called)
          for (struct char_data *mob = character_list; mob; mob = mob->next)
            if (IS_NPC(mob) && GET_ACTIVE(mob) == GET_IDNUM(ch) && GET_GRADE(mob) == spirit->id) {
              act("Freed from its services, $n returns to the metaplanes", TRUE, mob, 0, ch, TO_NOTVICT);
              extract_char(mob);
              break;
            }
        REMOVE_FROM_LIST(spirit, GET_SPIRIT(ch), next);
        delete [] spirit;
        GET_NUM_SPIRITS(ch)--;
        return;
      }
  } else if ((i = atoi(buf)) > 0) {
    if (i > GET_SUSTAINED_NUM(ch))
      send_to_char("You don't have that many spells sustained.\r\n", ch);
    else {
      struct sustain_data *sust;
      for (sust = GET_SUSTAINED(ch); sust; sust = sust->next)
        if (sust->caster && --i == 0)
          break;
      end_sustained_spell(ch, sust);
    }
    return;
  }
  send_to_char("Release what?\r\n", ch);
}

ACMD(do_cast)
{
  if (GET_ASPECT(ch) == ASPECT_CONJURER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_SORCERY)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }

  int force = 0;
  char spell_name[120], tokens[MAX_STRING_LENGTH], *s;
  struct spell_data *spell = GET_SPELLS(ch);
  if (!*argument) {
    send_to_char("Cast what spell?\r\n", ch);
    return;
  }
  strcpy(tokens, argument);
  if (strtok(tokens, "\"") && (s = strtok(NULL, "\""))) {
    strcpy(spell_name, s);
    if ((s = strtok(NULL, "\0"))) {
      skip_spaces(&s);
      strcpy(buf1, s);
    } else
      *buf1 = '\0';
    one_argument(argument, buf);
    force = atoi(buf);
  } else {
    half_chop(argument, buf, buf1);
    if (!(force = atoi(buf))) {
      strcpy(spell_name, buf);
    } else {
      half_chop(buf1, buf2, buf1);
      strcpy(spell_name, buf2);
    }
  }
  for (;spell; spell = spell->next)
    if (is_abbrev(spell_name, spell->name) && !(!str_cmp(spell_name, "heal") && spell->type == SPELL_HEALTHYGLOW))
      break;
  if (!spell) {
    send_to_char("You don't know that spell.\r\n", ch);
    return;
  }
  if (!force)
    force = spell->force;
  else if (force > spell->force) {
    send_to_char("You don't know that spell at that high a force.\r\n", ch);
    return;
  }
  if (spells[spell->type].physical && IS_PROJECT(ch)) {
    send_to_char("You can only cast mana spells on the astral plane.\r\n", ch);
    return;
  }
  cast_spell(ch, spell->type, spell->subtype, force, buf1);
  WAIT_STATE(ch, PULSE_VIOLENCE);
}

ACMD(do_conjure)
{
  if (GET_ASPECT(ch) == ASPECT_SORCERER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_CONJURING)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (IS_WORKING(ch)) {
    send_to_char(TOOBUSY, ch);
    return;
  }
  if (ch->in_veh) {
    send_to_char("There is not enough room to conjure in here.\r\n", ch);
    return;
  }
  if (IS_PROJECT(ch)) {
    send_to_char("You cannot conjure while projecting.\r\n", ch);
    return;
  }
  int force, spirit = 0;
  two_arguments(argument, buf, buf1);
  if (!(force = atoi(buf))) {
    send_to_char("What force do you wish to conjure at?\r\n", ch);
    return;
  }
  if (force > (GET_MAG(ch) / 100) * 2) {
    send_to_char("You can't conjure a spirit of force more than twice your magic rating.\r\n", ch);
    return;
  }
  if (GET_TRADITION(ch) == TRAD_HERMETIC) {
    if (GET_NUM_SPIRITS(ch) >= GET_CHA(ch)) {
      send_to_char("You have too many spirit summoned.\r\n", ch);
      return;
    }
    for (; spirit < NUM_ELEMENTS; spirit++)
      if (is_abbrev(buf1, elements[spirit].name))
        break;
    if (spirit == NUM_ELEMENTS) {
      send_to_char("What elemental do you wish to conjure?\r\n", ch);
      return;
    }
    if ((GET_ASPECT(ch) == ASPECT_ELEMFIRE && spirit != ELEM_FIRE) ||
        (GET_ASPECT(ch) == ASPECT_ELEMWATER && spirit != ELEM_WATER) ||
        (GET_ASPECT(ch) == ASPECT_ELEMAIR && spirit != ELEM_AIR) ||
        (GET_ASPECT(ch) == ASPECT_ELEMEARTH && spirit != ELEM_EARTH)) {
      send_to_char("You cannot summon elementals of that type.\r\n", ch);
      return;
    }
    bool library = FALSE, circle = FALSE;
    struct obj_data *obj;
    for (struct obj_data *obj = world[ch->in_room].contents; obj; obj = obj->next_content)
      if (GET_OBJ_TYPE(obj) == ITEM_MAGIC_TOOL)
        if (GET_OBJ_VAL(obj, 0) == TYPE_LIBRARY_CONJURE) {
          if (GET_OBJ_VAL(obj, 1) < force) {
            send_to_char("Your library isn't of a high enough rating to conjure that elemental.\r\n", ch);
            return;
          }
          library = TRUE;
        } else if (GET_OBJ_VAL(obj, 0) == TYPE_CIRCLE && !GET_OBJ_VAL(obj, 9)) {
          if (GET_OBJ_VAL(obj, 1) < force) {
            send_to_char("Your hermetic circle isn't of a high enough rating to conjure that elemental.\r\n", ch);
            return;
          } else if (GET_OBJ_VAL(obj, 2) != spirit) {
            send_to_char("That circle is for a different type of elemental.\r\n", ch);
            return;
          }
          circle = TRUE;
        }
    if (!circle || !library) {
      send_to_char("You need a conjuring library and a hermetic circle to conjure spirits.\r\n", ch);
      return;
    }
    for (obj = ch->carrying; obj; obj = obj->next_content)
      if (GET_OBJ_TYPE(obj) == ITEM_MAGIC_TOOL && GET_OBJ_VAL(obj, 0) == TYPE_SUMMONING)
        if (GET_OBJ_COST(obj) < force * 1000) {
          send_to_char("You don't have enough materials to summon at that high a force.\r\n", ch);
          return;
        } else
          break;
    if (!obj) {
      send_to_char("You need conjuring materials to conjure an elemental.\r\n", ch);
      return;
    }
    AFF_FLAGS(ch).SetBit(AFF_CONJURE);
    ch->char_specials.conjure[0] = spirit;
    ch->char_specials.conjure[1] = force;
    ch->char_specials.conjure[2] = force * 30;
    ch->char_specials.programming = obj;
    send_to_char(ch, "You begin to conjure a %s elemental.\r\n", elements[spirit].name);
  } else {
    if (GET_NUM_SPIRITS(ch)) {
      send_to_char("You already have summoned a nature spirit.\r\n", ch);
      return;
    }
    for (; spirit < NUM_SPIRITS; spirit++)
      if (is_abbrev(buf1, spirits[spirit].name))
        break;
    if (spirit == NUM_SPIRITS) {
      send_to_char("Which spirit do you wish to conjure?\r\n", ch);
      return;
    }
    if (GET_DOMAIN(ch) != ((spirit == SPIRIT_MIST || spirit == SPIRIT_STORM || spirit == SPIRIT_WIND) ? SPIRIT_SKY : spirit)) {
      send_to_char("You aren't in the correct domain to conjure that spirit.\r\n", ch);
      return;
    }
    int skill = GET_SKILL(ch, SKILL_CONJURING), target = force;
    if (world[ch->in_room].background[1] == AURA_POWERSITE)
      skill += world[ch->in_room].background[0];
    else target += world[ch->in_room].background[0];
    totem_bonus(ch, CONJURING, spirit, target, skill);
    if (GET_ASPECT(ch) == ASPECT_SHAMANIST && skill == GET_SKILL(ch, SKILL_CONJURING)) {
      send_to_char("Your totem will not let you conjure that spirit!\r\n", ch);
      return;
    }
    for (int i = 0; i < NUM_WEARS; i++)
      if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(ch, i), 0) == FOCI_SPIRIT
          && GET_OBJ_VAL(GET_EQ(ch, i), 2) == GET_IDNUM(ch) && GET_OBJ_VAL(GET_EQ(ch, i), 3) == ch->char_specials.conjure[0]
          && GET_OBJ_VAL(GET_EQ(ch, i), 4)) {
        skill += GET_OBJ_VAL(GET_EQ(ch, i), 1);
        break;
      }
    int success = success_test(skill, target);
    if (!conjuring_drain(ch, force) && AWAKE(ch)) {
      if (success < 1)
        send_to_char("You fail to conjure forth that spirit.\r\n", ch);
      else {
        struct spirit_data *spirdata = new spirit_data;
        spirdata->type = spirit;
        spirdata->force = force;
        spirdata->id = number(0, 1000);
        spirdata->services = success;
        spirdata->called = TRUE;
        spirdata->next = GET_SPIRIT(ch);
        GET_SPIRIT(ch) = spirdata;
        GET_NUM_SPIRITS(ch)++;
        struct char_data *mob = create_elemental(ch, spirit, force, spirdata->id, TRAD_SHAMANIC);
        send_to_char("The spirit hears your call and comes forth from the metaplanes.\r\n", ch);
        act("$n fades into existance on the astral plane.", TRUE, mob, 0, 0, TO_ROOM);
      }
    }
  }
}

ACMD(do_spells)
{
  if (GET_ASPECT(ch) == ASPECT_CONJURER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_SORCERY)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (!GET_SPELLS(ch)) {
    send_to_char("You don't know any spells.\r\n", ch);
    return;
  }
  send_to_char("You know the following spells:\r\n", ch);
  for (struct spell_data *spell = GET_SPELLS(ch); spell; spell = spell->next)
    send_to_char(ch, "%-50s Type: %10s Force: %d\r\n", spell->name, spells[spell->type].name, spell->force);
}

ACMD(do_learn)
{
  if (GET_ASPECT(ch) == ASPECT_CONJURER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_SORCERY)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  two_arguments(argument, buf, buf1);
  struct obj_data *obj = ch->carrying;
  struct spell_data *spell = NULL;
  int force, oldforce = 0;
  if (!*buf || !(obj = get_obj_in_list_vis(ch, buf, ch->carrying))) {
    send_to_char("Learn which spell?\r\n", ch);
    return;
  }
  if (GET_OBJ_TYPE(obj) != ITEM_SPELL_FORMULA) {
    send_to_char("You can't learn anything from that.\r\n", ch);
    return;
  }
  if (GET_OBJ_TIMER(obj) <= -2) {
    send_to_char("That spell design isn't complete.\r\n", ch); 
    return;
  }
  if ((GET_TRADITION(ch) == TRAD_HERMETIC && GET_OBJ_VAL(obj, 2)) || (GET_TRADITION(ch) == TRAD_SHAMANIC && !GET_OBJ_VAL(obj, 2))) {
    send_to_char("You don't understand this formula.\r\n", ch);
    return;
  }
  if (!*buf2 || atoi(buf1) == 0)
    force = GET_OBJ_VAL(obj, 0);
  else
    force = MIN(GET_OBJ_VAL(obj, 0), atoi(buf1));
  for (spell = GET_SPELLS(ch); spell; spell = spell->next)
    if (spell->type == GET_OBJ_VAL(obj, 1) && spell->subtype == GET_OBJ_VAL(obj, 3)) {
      if (spell->force >= force) {
        send_to_char("You already know this spell at an equal or higher force.\r\n", ch);
        return;
      } else {
        oldforce = spell->force;
        break;
      }
    }
  if (GET_KARMA(ch) < (force  - oldforce) * 100) {
    send_to_char(ch, "You don't have enough karma to learn this spell at that force! (You need %d)\r\n", force);
    return;
  }
  if ((GET_ASPECT(ch) == ASPECT_ELEMFIRE && spells[GET_OBJ_VAL(obj, 1)].category != COMBAT) ||
      (GET_ASPECT(ch) == ASPECT_ELEMEARTH && spells[GET_OBJ_VAL(obj, 1)].category != MANIPULATION) ||
      (GET_ASPECT(ch) == ASPECT_ELEMWATER && spells[GET_OBJ_VAL(obj, 1)].category != ILLUSION) ||
      (GET_ASPECT(ch) == ASPECT_ELEMAIR && spells[GET_OBJ_VAL(obj, 1)].category != DETECTION)) {
    send_to_char("Glancing over the formula you realise you can't bind mana in that fashion.\r\n", ch);
  }
  if (GET_ASPECT(ch) == ASPECT_SHAMANIST) {
    int skill = 0, target = 0;
    totem_bonus(ch, 0, GET_OBJ_VAL(obj, 1), target, skill);
    if (skill < 1) {
      send_to_char(ch, "%s forbids you from learning this spell.\r\n", totem_types[GET_TOTEM(ch)]);
    }
  }
  struct obj_data *library = world[ch->in_room].contents;
  for (;library; library = library->next_content)
    if (GET_OBJ_TYPE(library) == ITEM_MAGIC_TOOL && GET_OBJ_VAL(library, 1) >= force &&
        ((GET_TRADITION(ch) == TRAD_SHAMANIC
          && GET_OBJ_VAL(library, 0) == TYPE_LODGE && GET_OBJ_VAL(library, 3) == GET_IDNUM(ch)) ||
         (GET_TRADITION(ch) == TRAD_HERMETIC && GET_OBJ_VAL(library, 0) == TYPE_LIBRARY_SPELL)))
      break;
  if (!library) {
    send_to_char("You don't have the right tools here to learn that spell.\r\n", ch);
    return;
  }
  if (GET_TRADITION(ch) == TRAD_SHAMANIC && GET_OBJ_VAL(library, 9)) {
    send_to_char("You need to finish building that lodge before you can use it.\r\n", ch);
    return;
  }
  int skill = GET_SKILL(ch, SKILL_SORCERY);
  if (GET_TRADITION(ch) == TRAD_SHAMANIC) {
    int target = 0;
    totem_bonus(ch, 0, GET_OBJ_VAL(obj, 1), target, skill);
  } else if (GET_TRADITION(ch) == TRAD_HERMETIC && GET_SPIRIT(ch)) {
    for (struct spirit_data *spir = GET_SPIRIT(ch); spir && skill == GET_SKILL(ch, SKILL_SORCERY); spir = spir->next)
      if (spir->called) {
        struct char_data *spirit = find_spirit_by_id(spir->id, GET_IDNUM(ch));
        if (MOB_FLAGS(spirit).IsSet(MOB_STUDY)) {
          switch(spir->type) {
          case ELEM_FIRE:
            if (spells[GET_OBJ_VAL(obj, 1)].category == COMBAT) {
              skill += spir->force;
              MOB_FLAGS(spirit).RemoveBit(MOB_STUDY);
            }
            break;
          case ELEM_WATER:
            if (spells[GET_OBJ_VAL(obj, 1)].category == ILLUSION) {
              skill += spir->force;
              MOB_FLAGS(spirit).RemoveBit(MOB_STUDY);
            }
            break;
          case ELEM_AIR:
            if (spells[GET_OBJ_VAL(obj, 1)].category == DETECTION) {
              skill += spir->force;
              MOB_FLAGS(spirit).RemoveBit(MOB_STUDY);
            }
            break;
          case ELEM_EARTH:
            if (spells[GET_OBJ_VAL(obj, 1)].category == MANIPULATION) {
              skill += spir->force;
              MOB_FLAGS(spirit).RemoveBit(MOB_STUDY);
            }
            break;
          }
          elemental_fulfilled_services(ch, spirit, spir);
          break;
        }
      }
  }
  if (success_test(skill, force * 2) < 1) {
    send_to_char("You can't get your head around how to cast that spell.\r\n", ch);
    return;
  }
  if (spell) {
    struct spell_data *temp;
    REMOVE_FROM_LIST(spell, GET_SPELLS(ch), next);
    delete [] spell;
    spell = NULL;
  }
  GET_KARMA(ch) -= (force - oldforce) * 100;
  spell = new spell_data;
  if (GET_OBJ_VAL(obj, 1) == SPELL_INCATTR || GET_OBJ_VAL(obj, 1) == SPELL_INCCYATTR ||
      GET_OBJ_VAL(obj, 1) == SPELL_DECATTR || GET_OBJ_VAL(obj, 1) == SPELL_DECCYATTR) {
    strcpy(buf, spells[GET_OBJ_VAL(obj, 1)].name);
    sprintf(ENDOF(buf), " (%s)", attributes[GET_OBJ_VAL(obj, 3)]);
    spell->name = str_dup(buf);
  } else
    spell->name = str_dup(spells[GET_OBJ_VAL(obj, 1)].name);
  spell->type = GET_OBJ_VAL(obj, 1);
  spell->subtype = GET_OBJ_VAL(obj, 3);
  spell->force = force;
  spell->next = GET_SPELLS(ch);
  GET_SPELLS(ch) = spell;
  send_to_char(ch, "You spend %d karma and learn %s.\r\n", force - oldforce, spell->name);
  extract_obj(obj);
}

ACMD(do_elemental)
{
  if (GET_ASPECT(ch) == ASPECT_SORCERER || GET_TRADITION(ch) == TRAD_MUNDANE || GET_TRADITION(ch) == TRAD_ADEPT || !GET_SKILL(ch, SKILL_CONJURING)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (!GET_NUM_SPIRITS(ch)) {
    send_to_char("You don't have any elementals bound to you.\r\n", ch);
    return;
  }
  int i = 1;
  strcpy(buf, "You currently have the following elementals bound:\r\n");
  for (struct spirit_data *elem = GET_SPIRIT(ch); elem; elem = elem->next, i++) {
    if (GET_TRADITION(ch) == TRAD_SHAMANIC)
      sprintf(ENDOF(buf), "%d) %-30s (Force %d) Services %d\r\n", i, GET_NAME(&mob_proto[real_mobile(spirits[elem->type].vnum)]), elem->force, elem->services);
    else
      sprintf(ENDOF(buf), "%d) %-30s (Force %d) Services: %d%10s\r\n", i, GET_NAME(&mob_proto[real_mobile(elements[elem->type].vnum)]),
              elem->force, elem->services, elem->called ? " Present" : " ");
  }
  send_to_char(buf, ch);
}

ACMD(do_banish)
{
  if (GET_ASPECT(ch) == ASPECT_SORCERER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_CONJURING)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (IS_DUAL(ch) || IS_PROJECT(ch)) {
    send_to_char("You cannot banish while perceiving or projecting.\r\n", ch);
    return;
  }
  if (AFF_FLAGGED(ch, AFF_BANISH)) {
    if (GET_EXTRA(ch)) {
      if (FIGHTING(ch)) {
        act("You give up trying to banish $n.", FALSE, FIGHTING(ch), 0, ch, TO_VICT);
        AFF_FLAGS(FIGHTING(ch)).RemoveBit(AFF_BANISH);
      }
      stop_fighting(ch);
      AFF_FLAGS(ch).RemoveBit(AFF_BANISH);
    } else
      send_to_char(ch, "Try as you might, you can't wrest your mind away!\r\n");
    return;
  }
  struct char_data *mob;
  skip_spaces(&argument);
  if (!(mob = get_char_room_vis(ch, argument))) {
    send_to_char("Attempt to banish which spirit?\r\n", ch);
    return;
  }
  if (GET_RACE(mob) != RACE_SPIRIT && GET_RACE(mob) != RACE_ELEMENTAL) {
    send_to_char("You can only banish a nature spirit or elemental.\r\n", ch);
    return;
  }
  AFF_FLAGS(ch).SetBit(AFF_BANISH);
  AFF_FLAGS(mob).SetBit(AFF_BANISH);
  stop_fighting(ch);
  stop_fighting(mob);
  set_fighting(ch, mob);
  set_fighting(mob, ch);
  send_to_char(ch, "You begin attempting to banish %s.\r\n", GET_NAME(mob));
  act("$n begins attempting to banish $N.", FALSE, ch, 0, mob, TO_ROOM);
  GET_EXTRA(ch) = 1;
  GET_EXTRA(mob) = 0;
}

bool spirit_can_perform(int type, int order, int tradition)
{
  if (order == SERV_MATERIALIZE || order == SERV_DEMATERIAL || order == SERV_ATTACK)
    return TRUE;
  if (tradition == TRAD_HERMETIC) {
    if (order == SERV_ENGULF || order == SERV_APPEAR || order == SERV_SORCERY || order == SERV_STUDY || order == SERV_SUSTAIN || order == SERV_LEAVE)
      return TRUE;
    switch (type) {
    case ELEM_FIRE:
      if (order == SERV_FLAMEAURA || order == SERV_GUARD || order == SERV_FLAMETHROWER)
        return TRUE;
      break;
    case ELEM_AIR:
      if (order == SERV_MOVEMENT || order == SERV_BREATH || order == SERV_PSYCHOKINESIS)
        return TRUE;
      break;
    case ELEM_WATER:
    case ELEM_EARTH:
      if (order == SERV_MOVEMENT)
        return TRUE;
      break;
    }
  } else {
    if ((type != SPIRIT_STORM && type != SPIRIT_DESERT) && order == SERV_ACCIDENT)
      return TRUE;
    if ((type != SPIRIT_LAKE && type != SPIRIT_WIND) && order == SERV_CONCEAL)
      return TRUE;
    if (type != SPIRIT_STORM && order == SERV_GUARD)
      return TRUE;
    if ((type != SPIRIT_FOREST && type != SPIRIT_STORM && type != SPIRIT_MIST) && order == SERV_SEARCH)
      return TRUE;
    switch(type) {
    case SPIRIT_CITY:
      if (order == SERV_CONFUSION || order == SERV_FEAR)
        return TRUE;
      break;
    case SPIRIT_FIELD:
      break;
    case SPIRIT_HEARTH:
      if (order == SERV_CONFUSION)
        return TRUE;
      break;
    case SPIRIT_DESERT:
      if (order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_FOREST:
      if (order == SERV_CONFUSION || order == SERV_FEAR)
        return TRUE;
      break;
    case SPIRIT_MOUNTAIN:
    case SPIRIT_PRAIRIE:
      if (order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_MIST:
      if (order == SERV_CONFUSION || order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_STORM:
      if (order == SERV_CONFUSION || order == SERV_FEAR)
        return TRUE;
      break;
    case SPIRIT_WIND:
      if (order == SERV_CONFUSION || order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_LAKE:
      if (order == SERV_ENGULF || order == SERV_FEAR || order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_RIVER:
      if (order == SERV_ENGULF || order == SERV_FEAR || order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_SEA:
      if (order == SERV_CONFUSION || order == SERV_ENGULF || order == SERV_MOVEMENT)
        return TRUE;
      break;
    case SPIRIT_SWAMP:
      if (order == SERV_BINDING || order == SERV_CONFUSION || order == SERV_ENGULF || order == SERV_FEAR ||
          order == SERV_MOVEMENT)
        return TRUE;
      break;
    }
  }
  return FALSE;
}

void make_spirit_power(struct char_data *spirit, struct char_data *tch, int type)
{
  struct spirit_sustained *ssust = new spirit_sustained;
  ssust->type = type;
  ssust->caster = TRUE;
  ssust->target = tch;
  ssust->next = SPIRIT_SUST(spirit);
  SPIRIT_SUST(spirit) = ssust;
  ssust = new spirit_sustained;
  ssust->type = type;
  ssust->caster = FALSE;
  ssust->target = spirit;
  ssust->next = SPIRIT_SUST(tch);
  SPIRIT_SUST(tch) = ssust;
}

void stop_spirit_power(struct char_data *spirit, int type)
{
  struct spirit_sustained *temp;
  for (struct spirit_sustained *ssust = SPIRIT_SUST(spirit); ssust; ssust = ssust->next)
    if (ssust->type == type && ssust->caster == TRUE)
    {
      for (struct spirit_sustained *tsust = SPIRIT_SUST(ssust->target); tsust; tsust = tsust->next)
        if (tsust->type == type && tsust->target == spirit) {
          if (type == ENGULF) {
            if (IS_SPIRIT(spirit) || (IS_ELEMENTAL(spirit) && GET_SPARE1(spirit) == ELEM_WATER)) {
              act("The water surrounding $n falls away and soaks into the ground almost instantly.", TRUE, ssust->target, 0, 0, TO_ROOM);
              send_to_char("The water surrounding you suddenly vanishes allowing you to gasp for breath!", ssust->target);
            } else {
              switch (GET_SPARE1(spirit)) {
              case ELEM_FIRE:
                act("The fire engulfing $n suddenly goes out.", TRUE, ssust->target, 0, 0, TO_ROOM);
                send_to_char("The water fire surrounding you suddenly vanishes!", ssust->target);
                break;
              case ELEM_EARTH:
                act("The dirt and rock engulfing $n suddenly bursts apart, covering everyone close by.", TRUE, ssust->target, 0, 0, TO_ROOM);
                send_to_char("The earth surrounding you suddenly bursts open, allowing you to gasp for air!", ssust->target);
                break;
              case ELEM_AIR:
                act("$n begins to gasp for breath as though $s was just chocked.", TRUE, ssust->target, 0, 0, TO_ROOM);
                send_to_char("The mysterious force oppressing your lungs is suddenly gone!", ssust->target);
                break;
              }
            }
          } else if (type == CONFUSION)
            send_to_char("Your mind clears.\r\n", ssust->target);
          else if (type == CONCEAL)
            send_to_char("You suddenly feel very exposed.\r\n", ssust->target);
          else if (type == MOVEMENTUP || type == MOVEMENTDOWN)
            send_to_char("The force pushing you forward seems to lessen.\r\n", ssust->target);
          REMOVE_FROM_LIST(tsust, SPIRIT_SUST(ssust->target), next);
          delete [] tsust;
          break;
        }
      REMOVE_FROM_LIST(ssust, SPIRIT_SUST(spirit), next);
      delete [] ssust;
      break;
    }
}

POWER(spirit_appear)
{
  spiritdata->called = TRUE;
  spirit = create_elemental(ch, spiritdata->type, spiritdata->force, spiritdata->id, GET_TRADITION(ch));
  act("$n appears in the astral plane.", TRUE, spirit, 0, ch, TO_NOTVICT);
  send_to_char(ch, "%s heeds your call and arrives from the metaplanes ready to do your bidding.\r\n", CAP(GET_NAME(spirit)));
}

POWER(spirit_sorcery)
{
  if (!MOB_FLAGS(spirit).IsSet(MOB_AIDSORCERY)) {
    spiritdata->services--;
    MOB_FLAGS(spirit).SetBit(MOB_AIDSORCERY);
    send_to_char(ch, "%s will now asist you in casting a spell next time it is able.\r\n", CAP(GET_NAME(spirit)));
  } else
    send_to_char(ch, "%s will already asist in spell casting next time it is able.\r\n", CAP(GET_NAME(spirit)));

}

POWER(spirit_study)
{
  if (!MOB_FLAGS(spirit).IsSet(MOB_STUDY)) {
    spiritdata->services--;
    MOB_FLAGS(spirit).SetBit(MOB_STUDY);
    send_to_char(ch, "%s will now asist you in studying a spell next time it is able.\r\n", CAP(GET_NAME(spirit)));
  } else
    send_to_char(ch, "%s will already asist you the next time it is able.\r\n", CAP(GET_NAME(spirit)));
}

POWER(spirit_sustain)
{
  struct sustain_data *sust;
  if (GET_SUSTAINED_NUM(spirit))
    send_to_char("That spirit is already sustaining a spell.\r\n", ch);
  else {
    int i = atoi(arg);
    if (i <= 0 || i > GET_SUSTAINED_NUM(ch)) {
      send_to_char("You aren't sustaining that many spells.\r\n", ch);
      return;
    }
    for (sust = GET_SUSTAINED(ch); sust; sust = sust->next)
      if (sust->caster && --i == 0)
        break;
    if (sust->focus || sust->spirit) {
      send_to_char("You aren't sustaining that spell yourself.\r\n", ch);
      return;
    }
    if ((spells[sust->spell].category == MANIPULATION && spiritdata->type == ELEM_EARTH) ||
        (spells[sust->spell].category == COMBAT && spiritdata->type == ELEM_FIRE) ||
        (spells[sust->spell].category == ILLUSION && spiritdata->type == ELEM_WATER) ||
        (spells[sust->spell].category == DETECTION && spiritdata->type == ELEM_AIR)) {
      sust->spirit = spirit;
      GET_SUSTAINED_FOCI(ch)++;
      GET_SUSTAINED_NUM(spirit)++;
      spiritdata->services--;
      GET_SUSTAINED(spirit) = sust;
      send_to_char(ch, "%s sustains %s for you.\r\n", CAP(GET_NAME(spirit)), spells[sust->spell].name);
    } else
      send_to_char("That spirit can't sustain that type of spell.\r\n", ch);
  }
}

POWER(spirit_accident)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use accident against which target?\r\n", ch);
  else if (tch == ch)
    send_to_char("You cannot target yourself with that power.\r\n", ch);
  else if (tch == spirit)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    int success = success_test(MAX(GET_INT(tch), GET_QUI(tch)), GET_SPARE2(spirit));
    for (struct char_data *mob = world[spirit->in_room].people; mob; mob = mob->next)
      if (IS_NPC(mob) && (GET_RACE(mob) == RACE_SPIRIT || GET_RACE(mob) == RACE_ELEMENTAL) &&
          MOB_FLAGGED(mob, MOB_SPIRITGUARD)) {
        success = -1;
        break;
      }
    if (success < 1) {
      act("$n trips and stumbles.", TRUE, tch, 0, 0, TO_ROOM);
      send_to_char(tch, "You trip and stumble!");
      GET_INIT_ROLL(tch) -= 10;
    } else {
      sprintf(buf, "%s fails to cause $N to have an accident.", CAP(GET_NAME(spirit)));
      act(buf, TRUE, ch, 0, tch, TO_CHAR);
    }
    spiritdata->services--;
  }
}

POWER(spirit_binding)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use binding against which target?\r\n", ch);
  else if (tch == spirit || tch == ch)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$N suddenly becomes incapable of movement!", FALSE, spirit, 0, ch, TO_VICT);
    send_to_char("You suddenly notice you are stuck fast to the ground!", tch);
    AFF_FLAGS(tch).SetBit(AFF_BINDING);
    tch->points.binding = GET_LEVEL(spirit) * 2;
    spiritdata->services--;
  }
}

POWER(spirit_conceal)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (affected_by_power(spirit, CONCEAL)) {
    act("$N stops providing that services.", FALSE, ch, 0, spirit, TO_CHAR);
    stop_spirit_power(spirit, CONCEAL);
    return;
  }
  if (!tch)
    send_to_char("Use conceal against which target?\r\n", ch);
  else if (tch == spirit || affected_by_power(tch, CONCEAL))
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$n vanishes from sight.", FALSE, spirit, 0, ch, TO_VICT);
    send_to_char("The terrain seems to cover your tracks.", tch);
    make_spirit_power(spirit, tch, CONCEAL);
    spiritdata->services--;
  }
}

POWER(spirit_confusion)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (affected_by_power(spirit, CONFUSION)) {
    act("$N stops providing that services.", FALSE, ch, 0, spirit, TO_CHAR);
    stop_spirit_power(spirit, CONFUSION);
    return;
  }
  if (!tch)
    send_to_char("Use confusion against which target?\r\n", ch);
  else if (tch == spirit || tch == ch || affected_by_power(tch, CONFUSION))
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$n vanishes from sight.", FALSE, spirit, 0, ch, TO_VICT);
    send_to_char("The terrain seems to cover your tracks.", tch);
    make_spirit_power(spirit, tch, CONFUSION);
    spiritdata->services--;
  }
}

POWER(spirit_engulf)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (affected_by_power(spirit, ENGULF)) {
    act("$N stops providing that services.", FALSE, ch, 0, spirit, TO_CHAR);
    stop_spirit_power(spirit, ENGULF);
    return;
  }
  if (!tch)
    send_to_char("Use movement against which target?\r\n", ch);
  else if (tch == spirit || tch == ch || affected_by_power(tch, ENGULF))
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$n rushes towards $N and attempts to engulf them!", FALSE, spirit, 0, tch, TO_ROOM);
    int target = GET_QUI(spirit), targskill = get_skill(tch, SKILL_UNARMED_COMBAT, target);
    int success = success_test(GET_QUI(spirit), targskill) - success_test(targskill, GET_QUI(spirit));
    if (success < 1) {
      act("$n successfully dodges the attack!", TRUE, ch, 0, 0, TO_ROOM);
      send_to_char("You successfully dodge!\r\n", tch);
      return;
    }
    if (IS_SPIRIT(spirit) || (IS_ELEMENTAL(spirit) && GET_SPARE1(spirit) == ELEM_WATER)) {
      act("The water in the air surrounding $n seems to quickly condense and engulf $s!", TRUE, tch, 0, 0, TO_ROOM);
      send_to_char("The water in the air around you seems to condense and swallow you up!", tch);
    } else {
      switch (GET_SPARE1(spirit)) {
      case ELEM_FIRE:
        act("A strange, unnatural fire suddenly engulfs $n!", TRUE, tch, 0, 0, TO_ROOM);
        send_to_char("Fire suddenly engulfs your entire body!", tch);
        break;
      case ELEM_EARTH:
        act("The ground seems to rise up from below and encase $n!", TRUE, tch, 0, 0, TO_ROOM);
        send_to_char("The earth surrounding you suddenly bursts open, allowing you to gasp for air!", tch);
        break;
      case ELEM_AIR:
        act("$n suddenly falls to $s knees and begins to wretch!", TRUE, tch, 0, 0, TO_ROOM);
        send_to_char("A huge pressure falls on your lungs and you find it impossible to breath!", tch);
        break;
      }
    }
    make_spirit_power(spirit, tch, ENGULF);
    set_fighting(spirit, tch);
    set_fighting(tch, spirit);
    GET_EXTRA(tch) = 0;
    spiritdata->services--;
  }
}

POWER(spirit_fear)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use fear against which target?\r\n", ch);
  else if (tch == ch)
    send_to_char("You cannot target yourself with that power.\r\n", ch);
  else if (tch == spirit)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    int success = success_test(GET_SPARE2(spirit), GET_WIL(tch)) - success_test(GET_WIL(tch), GET_SPARE2(spirit));
    if (success < 1) {
      send_to_char("A dark shadow passes over your mind, but is quickly gone.\r\n", tch);
      sprintf(buf, "%s fails to instill fear in $N.", CAP(GET_NAME(spirit)));
      act(buf, FALSE, ch, 0, tch, TO_CHAR);
    } else {
      AFF_FLAGS(tch).SetBit(AFF_FEAR);
      send_to_char("An all consuming terror overcomes you!\r\n", tch);
      sprintf(buf, "%s succeeds in instilling fear in $N.", CAP(GET_NAME(spirit)));
      act(buf, FALSE, ch, 0, tch, TO_CHAR);
      extern ACMD(do_flee);
      do_flee(tch, "", 0, 0);
    }
    spiritdata->services--;
  }
}

POWER(spirit_flameaura)
{
  if (!MOB_FLAGGED(spirit, MOB_FLAMEAURA))
    act("$n's body erupts in flames.", TRUE, spirit, 0, 0, TO_ROOM);
  else
    act("The flames surrounding $n subsides.", TRUE, spirit, 0, 0, TO_ROOM);
  MOB_FLAGS(spirit).ToggleBit(MOB_FLAMEAURA);
  spiritdata->services--;
}

POWER(spirit_flamethrower)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use flamethrower against which target?\r\n", ch);
  else if (tch == ch)
    send_to_char("You cannot target yourself with that power.\r\n", ch);
  else if (tch == spirit)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    sprintf(buf, "moderate %s", arg);
    cast_spell(spirit, SPELL_FLAMETHROWER, 0, GET_LEVEL(spirit), buf);
  }
}

POWER(spirit_guard)
{
  if (!MOB_FLAGGED(spirit, MOB_SPIRITGUARD))
    act("$n begins to guard the area from accidents.", FALSE, spirit, 0, ch, TO_VICT);
  else
    act("$n stops guarding the area from accidents.", FALSE, spirit, 0, ch, TO_VICT);
  MOB_FLAGS(spirit).ToggleBit(MOB_SPIRITGUARD);
  spiritdata->services--;
}

POWER(spirit_leave)
{
  act("$n vanishes back into the metaplanes.", TRUE, spirit, 0, ch, TO_NOTVICT);
  send_to_char(ch, "%s returns to the metaplanes to await further orders.\r\n", CAP(GET_NAME(spirit)));
  spiritdata->called = FALSE;
  extract_char(spirit);
}

POWER(spirit_dematerialize)
{
  act("$n fades from the physical realm.\r\n", TRUE, spirit, 0, ch, TO_ROOM);
  MOB_FLAGS(spirit).RemoveBits(MOB_DUAL_NATURE, MOB_FLAMEAURA, ENDBIT);
  MOB_FLAGS(spirit).SetBit(MOB_ASTRAL);
  spiritdata->services--;
}

POWER(spirit_materialize)
{
  if (IS_DUAL(spirit)) {
    send_to_char(ch, "%s is already materialized.\r\n", CAP(GET_NAME(spirit)));
    return;
  }
  MOB_FLAGS(spirit).SetBit(MOB_DUAL_NATURE);
  MOB_FLAGS(spirit).RemoveBit(MOB_ASTRAL);
  spiritdata->services--;
  act("$n takes on a physical form.\r\n", TRUE, spirit, 0, ch, TO_ROOM);
}

POWER(spirit_movement)
{
  int increase = 0;
  if (affected_by_power(spirit, MOVEMENTUP)) {
    act("$N stops providing that services.", FALSE, ch, 0, spirit, TO_CHAR);
    stop_spirit_power(spirit, MOVEMENTUP);
    return;
  }
  if (affected_by_power(spirit, MOVEMENTDOWN)) {
    act("$N stops providing that services.", FALSE, ch, 0, spirit, TO_CHAR);
    stop_spirit_power(spirit, MOVEMENTDOWN);
    return;
  }
  two_arguments(arg, buf, buf1);
  if (!(*buf1 || *buf)) {
    send_to_char("Do you want to increase or decrease the movement of the target?\r\n", ch);
    return;
  }
  if (is_abbrev(buf1, "increase"))
    increase = 1;
  else if (is_abbrev(buf1, "decrease"))
    increase = -1;
  if (!increase) {
    send_to_char("You must specify increase or decrease.\r\n", ch);
    return;
  }
  struct char_data *tch = get_char_room_vis(spirit, buf);
  if (!tch)
    send_to_char("Use movement against which target?\r\n", ch);
  else if (tch == spirit || affected_by_power(tch, increase > 0 ? MOVEMENTUP : MOVEMENTDOWN))
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$n performs that service for you.", FALSE, spirit, 0, ch, TO_VICT);
    if (increase > 0)
      send_to_char("You feel something pushing you forward faster.\r\n", tch);
    else
      send_to_char("You face heavy resistance as you try and move forward.\r\n", ch);
    make_spirit_power(spirit, tch, increase > 0 ? MOVEMENTUP : MOVEMENTDOWN);
    spiritdata->services--;
  }
}

POWER(spirit_breath)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use noxious breath against which target?\r\n", ch);
  else if (tch == ch)
    send_to_char("You cannot target yourself with that power.\r\n", ch);
  else if (tch == spirit)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    act("$n turns towards $N as a cloud of noxious fumes forms around $S.", TRUE, spirit, 0, tch, TO_NOTVICT);
    act("$n lets forth a stream of noxious fumes in your direction.", FALSE, spirit, 0, tch, TO_VICT);
    int dam = convert_damage(stage(-success_test(MAX(GET_WIL(tch), GET_BOD(tch)), GET_SPARE2(spirit)), SERIOUS));
    if (dam > 0) {
      act("$n chokes and coughs.", TRUE, tch, 0, 0, TO_ROOM);
      send_to_char("The noxious fumes fill your throat and lungs, causing your vision to swim.\r\n", tch);
      damage(spirit, tch, dam, TYPE_FUMES, MENTAL);
    } else {
      act("$n waves $s hand infront of $s face, dispersing the fumes.", TRUE, tch, 0, 0, TO_ROOM);
      send_to_char("You easily resist the noxious fumes.\r\n", tch);
    }
    spiritdata->services--;
  }
}

POWER(spirit_attack)
{
  struct char_data *tch = get_char_room_vis(spirit, arg);
  if (!tch)
    send_to_char("Use attack which target?\r\n", ch);
  else if (tch == ch)
    send_to_char("Ordering your own spirit to attack you is not a good idea.\r\n", ch);
  else if (tch == spirit)
    send_to_char("The spirit refuses to perform that service.\r\n", ch);
  else {
    check_killer(ch, tch);
    set_fighting(spirit, tch);
    spiritdata->services--;
  }
}

POWER(spirit_psychokinesis)
{}

POWER(spirit_search)
{}

struct order_data services[] =
  {
    {"Appear", spirit_appear, 0
    },
    {"Sorcery", spirit_sorcery, 0},
    {"Study", spirit_study, 0},
    {"Sustain", spirit_sustain, 0},
    {"Accident", spirit_accident, 1},
    {"Binding", spirit_binding, 1},
    {"Concealment", spirit_conceal, 1},
    {"Confusion", spirit_confusion, 0},
    {"Dematerialize", spirit_dematerialize, 1},
    {"Engulf", spirit_engulf, 1},
    {"Fear", spirit_fear, 0},
    {"Flame Aura", spirit_flameaura, 1},
    {"Flamethrower", spirit_flamethrower, 1},
    {"Guard", spirit_guard, 1},
    {"Leave", spirit_leave, 0},
    {"Materialize", spirit_materialize, 0},
    {"Movement", spirit_movement, 1},
    {"Breath", spirit_breath, 1},
    {"Psychokinesis", spirit_psychokinesis, 1},
    {"Search", spirit_search, 1},
    {"Attack", spirit_attack, 1}
  };

ACMD(do_order)
{
  if (GET_ASPECT(ch) == ASPECT_SORCERER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_CONJURING)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  half_chop(argument, buf, buf1);
  struct spirit_data *spirit;
  int i, order = 0;
  if (!(i = atoi(buf)) || i > GET_NUM_SPIRITS(ch)) {
    send_to_char("Which spirit do you wish to give an order to?\r\n", ch);
    return;
  }
  for (spirit = GET_SPIRIT(ch); spirit; spirit = spirit->next)
    if (--i == 0)
      break;
  if (!*buf1) {
    send_to_char("Available Services: \r\n", ch);
    if (GET_TRADITION(ch) == TRAD_HERMETIC) {
      if (!spirit->called)
        send_to_char("  ^WAppear^n\r\n", ch);
      else {
        send_to_char("  Aid ^WSorcery^n\r\n"
                     "  Aid ^WStudy^n\r\n"
                     "  ^WSustain^n Spell\r\n"
                     "  ^WMaterialize^n\r\n"
                     "  ^WDematerialize^n\r\n"
                     "  ^WLeave^n\r\n"
                     "  ^WEngulf^n\r\n", ch);
        switch(spirit->type) {
        case ELEM_FIRE:
          send_to_char("  ^WFlame Aura^n\r\n"
                       "  ^WFlamethrower^n\r\n", ch);
          break;
        case ELEM_WATER:
        case ELEM_EARTH:
          send_to_char("  ^WMovement^n\r\n", ch);
          break;
        case ELEM_AIR:
          send_to_char("  ^WMovement^n\r\n"
                       "  Noxious ^WBreath^n\r\n"
                       "  ^WPsychokinesis^n\r\n", ch);
          break;
        }
      }
    } else {
      send_to_char("  ^WMaterialize^n\r\n"
                   "  ^WDematerialize^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_ACCIDENT, TRAD_SHAMANIC))
        send_to_char("  ^WAccident^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_BINDING, TRAD_SHAMANIC))
        send_to_char("  ^WBinding^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_CONCEAL, TRAD_SHAMANIC))
        send_to_char("  ^WConcealment^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_CONFUSION, TRAD_SHAMANIC))
        send_to_char("  ^WConfusion^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_ENGULF, TRAD_SHAMANIC))
        send_to_char("  ^WEngulf^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_FEAR, TRAD_SHAMANIC))
        send_to_char("  ^WFear^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_GUARD, TRAD_SHAMANIC))
        send_to_char("  ^WGuard^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_MOVEMENT, TRAD_SHAMANIC))
        send_to_char("  ^WMovement^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_BREATH, TRAD_SHAMANIC))
        send_to_char("  Noxious ^WBreath^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_PSYCHOKINESIS, TRAD_SHAMANIC))
        send_to_char("  ^WPsychokinesis^n\r\n", ch);
      if (spirit_can_perform(spirit->type, SERV_SEARCH, TRAD_SHAMANIC))
        send_to_char("  ^WSearch^n\r\n", ch);
    }
    send_to_char("  Attack\r\n", ch);
  } else {
    half_chop(buf1, buf, buf2);
    for (;order < NUM_SERVICES; order++)
      if (is_abbrev(buf, services[order].name) && spirit_can_perform(spirit->type, order, GET_TRADITION(ch)))
        break;
    if (order == NUM_SERVICES) {
      send_to_char("Which service do you wish to order the elemental to perform?\r\n", ch);
      return;
    }
    if (GET_TRADITION(ch) == TRAD_HERMETIC) {
      if (order == SERV_APPEAR && spirit->called) {
        send_to_char("That elemental is already here!\r\n", ch);
        return;
      } else if (!spirit->called && order != SERV_APPEAR) {
        send_to_char("That elemental is waiting on the metaplanes.\r\n", ch);
        return;
      }
    }
    struct char_data *mob = find_spirit_by_id(spirit->id, GET_IDNUM(ch));
    if (services[order].type == 1 && MOB_FLAGGED(mob, MOB_ASTRAL)) {
      send_to_char("That elemental must materialize before it can use that power.\r\n", ch);
      return;
    }
    if (spirit->services < 1 && order != SERV_LEAVE) {
      send_to_char("The spirit no longer listens to you.\r\n", ch);
      return;
    }
    ((*services[order].func) (ch, mob, spirit, buf2));
    if (order != SERV_LEAVE)
      elemental_fulfilled_services(ch, mob, spirit);
  }
}

ACMD(do_domain)
{
  if (GET_TRADITION(ch) != TRAD_SHAMANIC) {
    send_to_char("You have no sense of domain.\r\n", ch);
    return;
  }
  if (!*argument) {
    send_to_char(ch, "You are currently in the %s domain.\r\n", spirits[GET_DOMAIN(ch)].name);
    if (SECT(ch->in_room) != SPIRIT_FOREST && SECT(ch->in_room) != SPIRIT_HEARTH && !ROOM_FLAGGED(ch->in_room, ROOM_INDOORS)) {
      send_to_char(ch, "You can switch to the following domains:\r\n  %s\r\n  Sky Spirit\r\n", spirits[SECT(ch->in_room)].name);
    }
  } else {
    struct spirit_data *next;
    skip_spaces(&argument);
    int newdomain = -1;
    if (SECT(ch->in_room) != SPIRIT_FOREST && SECT(ch->in_room) != SPIRIT_HEARTH && !ROOM_FLAGGED(ch->in_room, ROOM_INDOORS)) {
      if (is_abbrev(argument, spirits[SECT(ch->in_room)].name))
        newdomain = SECT(ch->in_room);
      else if (is_abbrev(argument, "sky spirit"))
        newdomain = SPIRIT_SKY;
    }
    if (newdomain == GET_DOMAIN(ch))
      send_to_char("You are already focusing on that domain.\r\n", ch);
    else if (newdomain == -1)
      send_to_char("Which domain do you wish to focus on?\r\n", ch);
    else {
      GET_DOMAIN(ch) = newdomain;
      send_to_char(ch, "You switch your focus to the %s domain.\r\n", GET_DOMAIN(ch) == SPIRIT_SKY ? "Sky spirit" : spirits[GET_DOMAIN(ch)].name);
      for (struct spirit_data *spirit = GET_SPIRIT(ch); spirit; spirit = next) {
        next = spirit->next;
        if (GET_DOMAIN(ch) != spirit->type) {
          struct spirit_data *temp;
          struct char_data *tch = find_spirit_by_id(spirit->id, GET_IDNUM(ch));
          if (tch) {
            send_to_char(ch, "You release %s from the rest of its services.\r\n", GET_NAME(tch));
            act("$n returns to the metaplanes.", TRUE, tch, 0, ch, TO_NOTVICT);
            extract_char(tch);
          }
          REMOVE_FROM_LIST(spirit, GET_SPIRIT(ch), next);
          GET_NUM_SPIRITS(ch)--;
          delete [] spirit;
        }
      }
    }
  }
}

ACMD(do_drain)
{
  if (!GET_MAGIC(ch)) {
    send_to_char("You don't have any spell pool dice to allocate.\r\n", ch);
    return;
  }
  skip_spaces(&argument);
  int i = atoi(argument), total = GET_CASTING(ch);
  switch (subcmd) {
    case SCMD_DRAIN:
      total += GET_DRAIN(ch);
      break;
    case SCMD_SDEFENSE:
      total += GET_SDEFENSE(ch);
      break;
    case SCMD_REFLECT:
      total += GET_REFLECT(ch);
      break;
  }
  if (i > total) {
    send_to_char("You don't have that many dice to allocate.\r\n", ch);
    return;
  }
  switch (subcmd) {
    case SCMD_SDEFENSE:
      GET_SDEFENSE(ch) = ch->real_abils.spell_defense_pool = i;
      send_to_char(ch, "You allocate %d dice to your spell defense pool.\r\n", GET_SDEFENSE(ch));
      break;
    case SCMD_REFLECT:
      GET_REFLECT(ch) = ch->real_abils.reflection_pool = i;
      send_to_char(ch, "You allocate %d dice to your reflecting pool.\r\n", GET_REFLECT(ch));
      break;
    case SCMD_DRAIN:
      GET_DRAIN(ch) = ch->real_abils.drain_pool = i;
      send_to_char(ch, "You allocate %d dice to your drain pool.\r\n", GET_DRAIN(ch));
      break;
  }
  GET_CASTING(ch) = ch->real_abils.casting_pool = GET_MAGIC(ch) - GET_DRAIN(ch) - GET_REFLECT(ch) - GET_SDEFENSE(ch);
  affect_total(ch);
}

void sedit_parse(struct descriptor_data *d, char *arg)
{}

ACMD(do_deactivate)
{
  skip_spaces(&argument);
  struct obj_data *obj;
  int i;
  if (is_abbrev(argument, "pain editor")) {
    for (obj = ch->bioware; obj; obj = obj->next_content)
      if (GET_OBJ_VAL(obj, 0) == BIO_PAINEDITOR) {
        if (!GET_OBJ_VAL(obj, 3))  
          send_to_char("Your Pain Editor isn't activated.\r\n", ch);
        else {
          GET_OBJ_VAL(obj, 3) = 0;
          send_to_char("Your body starts to tingle as your perception of touch returns.\r\n", ch);
          update_pos(ch);
        }
        return;
      }
  }
  if (GET_TRADITION(ch) == TRAD_ADEPT) {
    char name[120], tokens[MAX_STRING_LENGTH], *s;
    extern int ability_cost(int abil, int level);
    strcpy(tokens, argument);
    if (strtok(tokens, "\"") && (s = strtok(NULL, "\""))) {
      strcpy(name, s);
      if ((s = strtok(NULL, "\0"))) {
        skip_spaces(&s);
        strcpy(buf1, s);
      } else
        *buf1 = '\0';
      one_argument(argument, buf);
    } else strcpy(name, argument);
      
    for (i = 0; i < ADEPT_NUMPOWER; i++)
      if (GET_POWER_TOTAL(ch, i) && is_abbrev(name, adept_powers[i]))
        break;
    if (i < ADEPT_NUMPOWER) {
      if (!GET_POWER_ACT(ch, i))
        send_to_char("You don't have that power activated.\r\n", ch);
      else {
        int total = 0;
        for (int q = GET_POWER_ACT(ch, i);q > 0; q--)
          total += ability_cost(i, q);
        GET_POWER_ACT(ch, i) = 0;
        GET_POWER_POINTS(ch) -= total;
        send_to_char(ch, "You completely deactivate your %s power.\r\n", adept_powers[i]);
        if (i == ADEPT_PERCEPTION) {
          PLR_FLAGS(ch).RemoveBit(PLR_PERCEIVE);
          send_to_char("You return to your physical senses.\r\n", ch);
        }
      }
      return;
    }
  }
  if (!GET_FOCI(ch)) {
    send_to_char("You have no foci activated!\r\n", ch);
    return;
  }
  if (!(obj = get_object_in_equip_vis(ch, argument, ch->equipment, &i)) &&
      !(obj = get_obj_in_list_vis(ch, argument, ch->carrying))) {
    send_to_char("Deactive which focus?\r\n", ch);
    return;
  }
  if (GET_OBJ_TYPE(obj) != ITEM_FOCUS)
    send_to_char("That isn't a focus.\r\n", ch);
  else if (GET_OBJ_VAL(obj, 4) < 1)
    send_to_char("That focus isn't activated.\r\n", ch);
  else {
    GET_OBJ_VAL(obj, 4) = 0;
    GET_FOCI(ch)--;
    affect_total(ch);
    send_to_char(ch, "You deactivate %s.\r\n", GET_OBJ_NAME(obj));
  }
}

ACMD(do_destroy)
{
  if (!*argument) {
    send_to_char("Destroy which magic construct?\r\n", ch);
    return;
  }
  skip_spaces(&argument);
  struct obj_data *obj;
  if (!(obj = get_obj_in_list_vis(ch, argument, world[ch->in_room].contents))) {
    send_to_char("That object isn't here.\r\n", ch);
    return;
  }
  if (GET_OBJ_TYPE(obj) == ITEM_MAGIC_TOOL && (GET_OBJ_VAL(obj, 0) == TYPE_LODGE || GET_OBJ_VAL(obj, 0) == TYPE_CIRCLE)) {
    if (GET_OBJ_VAL(obj, 0) == TYPE_LODGE) {
      send_to_char(ch, "You kick at the supports and smash the talismans.\r\n");
      act("$n roughly pulls down $o.", TRUE, ch, obj, 0, TO_ROOM);
    } else {
      send_to_char(ch, "You rub your feet along the lines making up the hermetic circle, erasing them.\r\n");
      act("$n uses $s feet to rub out $o.", TRUE, ch, obj, 0, TO_ROOM);
    }
    extract_obj(obj);
  } else
    send_to_char("You can't destroy that object.\r\n", ch);
}

ACMD(do_track)
{
  if (!IS_PROJECT(ch)) {
    send_to_char("You have to be projecting to astrally track.\r\n", ch);
    return;
  }
  if (AFF_FLAGGED(ch->desc->original, AFF_TRACKING)) {
    AFF_FLAGS(ch->desc->original).RemoveBit(AFF_TRACKING);
    send_to_char("You stop searching the astral plane.\r\n", ch);
    return;
  }
  int success;
  skip_spaces(&argument);
  if (!*argument) {
    if (HUNTING(ch->desc->original)) {
      success = success_test(MAX(GET_INT(ch), GET_MAG(ch)/100), HOURS_SINCE_TRACK(ch->desc->original));
      if (success > 0) {
        AFF_FLAGS(ch->desc->original).SetBit(AFF_TRACKING);
        send_to_char("You pick up the trail and continue tracking the astral signature.\r\n", ch);
      } else {
        HUNTING(ch->desc->original) = NULL;
        send_to_char("You seem to have lost the trail.\r\n", ch);
      }
    } else
      send_to_char("What do you wish to track the owner of?\r\n", ch);
    return;
  }
  struct char_data *vict;
  struct obj_data *obj;
  two_arguments(argument, buf, buf1);
  if (!generic_find(buf,  FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP |
                    FIND_CHAR_ROOM, ch, &vict, &obj)) {
    send_to_char(NOOBJECT, ch);
    return;
  }
  if (vict) {
    if (*buf1) {
      obj = NULL;
      two_arguments(buf1, buf2, argument);
      int i = 0;
      if (!str_cmp(buf1, "spell")) {
        int spell = 0;
        if (*buf2)
          spell = atoi(buf2);
        for (struct sustain_data *sust = GET_SUSTAINED(vict); sust; sust = sust->next)
          if (!sust->caster && !spell--) {
            vict = sust->other;
            break;
          }
      } else if (!(obj = get_object_in_equip_vis(vict, buf1, vict->equipment, &i))) {
        send_to_char("They aren't wearing that.\r\n", ch);
        return;
      }
    } else if (IS_NPC(vict) && (GET_RACE(vict) == RACE_SPIRIT || GET_RACE(vict) == RACE_ELEMENTAL) && GET_ACTIVE(vict)) {
      for (struct descriptor_data *desc = descriptor_list; desc; desc = desc->next)
        if (desc->original && GET_IDNUM(desc->original) == GET_ACTIVE(vict)) {
          vict = desc->original;
          break;
        } else if (desc->character && GET_IDNUM(desc->character) == GET_ACTIVE(vict)) {
          vict = desc->character;
          break;
        }
    } else {
      send_to_char("They are right here!\r\n", ch);
      return;
    }
  }
  if (obj) {
    vict = NULL;
    if (GET_OBJ_TYPE(obj) != ITEM_FOCUS && !(GET_OBJ_TYPE(obj) == ITEM_MAGIC_TOOL && (GET_OBJ_VAL(obj, 0) == TYPE_CIRCLE || GET_OBJ_VAL(obj, 0) == TYPE_LODGE))) {
      send_to_char("There is no astral signature present on that item.\r\n", ch);
      return;
    }
    int num = 3;
    if (GET_OBJ_TYPE(obj) == ITEM_FOCUS)
      num = 2;
    if (!GET_OBJ_VAL(obj, num)) {
      send_to_char("There is no astral signature present on that item.\r\n", ch);
      return;
    }
    for (struct descriptor_data *desc = descriptor_list; desc; desc = desc->next)
      if (desc->original && GET_IDNUM(desc->original) == GET_OBJ_VAL(obj, num)) {
        vict = desc->original;
        break;
      } else if (desc->character && GET_IDNUM(desc->character) == GET_OBJ_VAL(obj, num)) {
        vict = desc->character;
        break;
      }
  }
  success = success_test(GET_INT(ch), 4);
  if (vict && success > 0) {
    if (vict->in_room == ch->in_room) {
      send_to_char("They're right here!\r\n", ch);
      return;
    }
    HUNTING(ch->desc->original) = vict;
    AFF_FLAGS(ch->desc->original).SetBit(AFF_TRACKING);
    HOURS_SINCE_TRACK(ch->desc->original) = 0;
    HOURS_LEFT_TRACK(ch->desc->original) = MAX(1, 4 / success);
    AFF_FLAGS(vict).SetBit(AFF_TRACKED);
    send_to_char("You begin to trace that astral signature back to its owner.\r\n", ch);
  } else {
    send_to_char("You can't seem to track them down.\r\n", ch);
    return;
  }
}

ACMD(do_manifest)
{
  if (!IS_PROJECT(ch)) {
    send_to_char("You can't manifest when you're not astrally projecting.\r\n", ch);
    return;
  }
  AFF_FLAGS(ch).ToggleBit(AFF_MANIFEST);
  if (AFF_FLAGGED(ch, AFF_MANIFEST)) {
    act("$n fades slowly into view.", TRUE, ch, 0, 0, TO_ROOM);
    send_to_char("You manifest your presence on the physical plane.\r\n", ch);
  } else {
    act("$n fades from the physical plane.", FALSE, ch, 0, 0, TO_ROOM);
    send_to_char("You vanish back into the astral plane.\r\n", ch);
  }
}

ACMD(do_dispell)
{
  if (GET_ASPECT(ch) == ASPECT_CONJURER || GET_TRADITION(ch) == TRAD_ADEPT || GET_TRADITION(ch) == TRAD_MUNDANE || !GET_SKILL(ch, SKILL_SORCERY)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (!*argument) {
    send_to_char("What spell do you wish to dispell?\r\n", ch);
    return;
  }
  skip_spaces(&argument);
  struct char_data *vict;
  two_arguments(argument, buf, buf2);
  if (!(vict = get_char_room_vis(ch, buf))) {
    send_to_char(NOPERSON, ch);
    return;
  }
  int x = atoi(buf2);
  if (x <= 0) {
    send_to_char("Dispell which spell?\r\n", ch);
    return;
  }
  struct sustain_data *sust = GET_SUSTAINED(vict);
  for (;sust; sust = sust->next)
    if (!sust->caster && !--x)
      break;
  if (!sust) {
    send_to_char("They don't have that many spells cast on them.\r\n", ch);
    return;
  }
  int success = success_test(GET_SKILL(ch, SKILL_SORCERY) + MIN(GET_SKILL(ch, SKILL_SORCERY), GET_CASTING(ch)), sust->force);
  int type = sust->spell, force = sust->force;
  if (success > 0) {
    spell_modify(vict, sust, FALSE);
    sust->success -= success;
    if (sust->success < 1) {
      send_to_char("You succeed in completly dispelling that spell.\r\n", ch);
      sust->success += success;
      spell_modify(vict, sust, TRUE);
      end_sustained_spell(vict, sust);
    } else {
      spell_modify(vict, sust, TRUE);
      send_to_char("You succeed in weakening that spell.\r\n", ch);
    }
  } else
    send_to_char("You fail to weaken that spell.\r\n", ch);
  spell_drain(ch, type, force, 0);
}

ACMD(do_heal)
{
  if (GET_TRADITION(ch) != TRAD_ADEPT || !GET_POWER(ch, ADEPT_EMPATHICHEAL)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  struct char_data *vict;
  skip_spaces(&argument);
  if (!*argument) {
    send_to_char("Who do you wish to heal?\r\n", ch);
    return;
  }
  two_arguments(argument, arg, buf);
  if (!*buf)
    send_to_char("What damage level do you wish to heal them at?\r\n", ch);
  else if (GET_POS(ch) == POS_FIGHTING)
    send_to_char(TOOBUSY, ch);
  else if (!(vict = get_char_room_vis(ch, arg)))
    send_to_char(NOPERSON, ch);
  else if (GET_PHYSICAL(ch) <= 100)
    send_to_char("Succeeding in that task would surely kill you.\r\n", ch);
  else if (GET_PHYSICAL(vict) == GET_MAX_PHYSICAL(vict))
    send_to_char("They don't need your help.\r\n", ch);
  else if (GET_POS(vict) > POS_LYING)
    send_to_char("They have to be lying down to receive your help.\r\n", ch);
  else {
    int basedamage = 0;
    for (; *wound_name[basedamage] != '\n'; basedamage++)
      if (is_abbrev(buf, wound_name[basedamage]))
        break;
    if (basedamage > 4 || basedamage == 0) {
      send_to_char("That is not a valid damage level, please choose between Light, Moderate, Serious and Deadly.\r\n", ch);
      return;
    }
    int success = success_test(GET_MAG(ch) / 100, 10 - (GET_ESS(vict) / 100));
    if (success < 1) {
      send_to_char("You fail to channel your energy into that pursuit.\r\n", ch);
      return;
    }
    success = MIN(damage_array[basedamage], MIN((GET_PHYSICAL(ch) / 100) - 1, success)) * 100;
    success = MIN(GET_MAX_PHYSICAL(vict) - GET_PHYSICAL(vict), success);
    GET_PHYSICAL(vict) += success;
    GET_PHYSICAL(ch) -= success;
    WAIT_STATE(ch, 3 RL_SEC);
    act("You feel $n place $s hands on you, $s minstrations seem to cause your wounds to fade!", TRUE, ch, 0, vict, TO_VICT);
    act("You place your hands on $N, you feel $S pain and suffering transfered to your body!", TRUE, ch, 0, vict, TO_CHAR);
    act("$n places $s hands on $N seemingly transfering the wound to $mself!", TRUE, ch, 0, vict, TO_NOTVICT);
  }
}

ACMD(do_relieve)
{
  if (GET_TRADITION(ch) != TRAD_ADEPT || !GET_POWER(ch, ADEPT_PAINRELIEF)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  struct char_data *vict;
  skip_spaces(&argument);
  if (!*argument) {
    send_to_char("Who do you wish to heal?\r\n", ch);
    return;
  }
  if (GET_POS(ch) == POS_FIGHTING)
    send_to_char(TOOBUSY, ch);
  else if (!(vict = get_char_room_vis(ch, argument)))
    send_to_char(NOPERSON, ch);
  else if (GET_MENTAL(vict) == GET_MAX_MENTAL(vict))
    send_to_char("They don't need your help.\r\n", ch);
  else if (GET_POS(vict) > POS_LYING)
    send_to_char("They have to be lying down to receive your help.\r\n", ch);
  else {
    int success = success_test(GET_MAG(ch) / 100, 10 - (GET_ESS(vict) / 100));
    if (success < 1) {
      send_to_char("You fail to channel your energy into that pursuit.\r\n", ch);
      return;
    }
    if (GET_MENTAL(vict) <= 0)
      GET_MENTAL(vict) = (int)(GET_MAX_MENTAL(vict) * 0.4);
    else if (GET_MENTAL(vict) <= GET_MAX_MENTAL(vict) * 0.4)
      GET_MENTAL(vict) = (int)(GET_MAX_MENTAL(vict) * 0.7);
    else if (GET_MENTAL(vict) <= GET_MAX_MENTAL(vict) * 0.7)
      GET_MENTAL(vict) = (int)(GET_MAX_MENTAL(vict) * 0.9);
    else
      GET_MENTAL(vict) = GET_MAX_MENTAL(vict);
    act("You feel $n place $s hands on you, a tingling feeling feels your body as your pain begins to subside", TRUE, ch, 0, vict, TO_VICT);
    act("You hands warm slightly at the energy being channeled through them into $N's chakra points.", TRUE, ch, 0, vict, TO_CHAR);
    act("$n places $s hands on $N. $N appears to feel better.", TRUE, ch, 0, vict, TO_NOTVICT);
    WAIT_STATE(ch, 5 RL_SEC);
  }
}


ACMD(do_nervestrike)
{
  if (GET_TRADITION(ch) != TRAD_ADEPT || !GET_POWER(ch, ADEPT_NERVE_STRIKE)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  if (IS_NERVE(ch)) {
    IS_NERVE(ch) = FALSE;
    send_to_char("You will no longer use nerve strike.\r\n", ch);
  } else {
    IS_NERVE(ch) = TRUE;
    send_to_char("You will now use nerve strike.\r\n", ch);
  }
}
#define CH d->character

void disp_init_menu(struct descriptor_data *d)
{
  CLS(CH);
  send_to_char("1) Increase magic and learn metamagic technique\r\n"
               "2) Increase magic and change astral signature\r\n"
               "3) Shed a gaes\r\n"
               "4) Return to game\r\n"
               "Enter initiation option: ", CH);
  d->edit_mode = INIT_MAIN;
}

bool can_metamagic(struct char_data *ch, int i) 
{
  if (GET_TRADITION(ch) == TRAD_ADEPT) {
    if (i != META_CENTERING && i != META_MASKING)
      return FALSE;
    if (i == META_CENTERING && GET_METAMAGIC(ch, i) == 2)
      return TRUE;
  }
  if (GET_METAMAGIC(ch, i))
    return FALSE;
  if (GET_ASPECT(ch) == ASPECT_CONJURER && (i == META_QUICKENING || i == META_REFLECTING || i == META_ANCHORING || i == META_SHIELDING))
    return FALSE;
  if (GET_ASPECT(ch) == ASPECT_SORCERER && i == META_INVOKING)
    return FALSE;
  return TRUE;
}

void disp_gaes_menu(struct descriptor_data *d)
{
  CLS(CH);
  int x = 0;
  for (int i = 0; i < NUM_WEARS; i++)
    if (GET_EQ(CH, i) && GET_OBJ_TYPE(GET_EQ(CH, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(CH, i), 9) == GET_IDNUM(CH))
      send_to_char(CH, "%d) %s\r\n", x++, GET_OBJ_NAME(GET_EQ(CH, i)));
  send_to_char("q) Quit Initiation\r\nSelect gaes to shed: ", CH);
  d->edit_mode = INIT_GAES;
}

void disp_meta_menu(struct descriptor_data *d)
{
  CLS(CH);
  for (int i = 0; i < META_MAX; i++)
    send_to_char(CH, "%d) %s%s^n\r\n", i + 1, can_metamagic(CH, i) ? "" : "^r", metamagic[i]);
  send_to_char("q) Quit Initiation\r\nSelect ability to learn: ", CH);
  d->edit_mode = INIT_META;
}

bool init_cost(struct char_data *ch, bool spend)
{
  int karmacost = (GET_GRADE(ch) + 6) * 300, nuyencost = MIN(825000, (25000 + (25000 * 1<<GET_GRADE(ch)))), tke = 0;
  if (karmacost > GET_KARMA(ch)) {
    send_to_char("You do not have enough karma to initiate\n\r", ch);
    return FALSE;
  }
  if (nuyencost > GET_NUYEN(ch)) {
    send_to_char("You do not have enough nuyen to initiate.\r\n", ch);
    return FALSE;
  }
  switch (GET_GRADE(ch)+1) {
    case 1:
      tke = 0;
      break;
    case 2:
      tke = 100;
      break;
    case 3:
      tke = 200;
      break;
    default:
      tke = 200 + ((GET_GRADE(ch)-2)*200);
      break;
  }
  if (tke > GET_TKE(ch)) {
    send_to_char("You do not have high enough TKE to initiate.\r\n", ch);
    return FALSE;
  }
  if (spend) {
    GET_NUYEN(ch) -= nuyencost;
    GET_KARMA(ch) -= karmacost;
  }
  return TRUE;
}

ACMD(do_initiate)
{
  if (GET_TRADITION(ch) == TRAD_MUNDANE)
    nonsensical_reply(ch);
  if (subcmd == SCMD_INITIATE && init_cost(ch, FALSE)) {
    STATE(ch->desc) = CON_INITIATE;
    PLR_FLAGS(ch).SetBit(PLR_INITIATE);  
    disp_init_menu(ch->desc);
  } else if (subcmd == SCMD_POWERPOINT) {
    if (GET_TRADITION(ch) != TRAD_ADEPT)
      nonsensical_reply(ch);
    else if (GET_KARMA(ch) < 2000 || ch->points.extrapp > (int)(GET_REP(ch) / 50))
      send_to_char("You do not have enough karma to purchase a powerpoint.\r\n", ch);
    else {
      GET_KARMA(ch) -= 2000;
      GET_PP(ch) += 100;
      ch->points.extrapp++;
      send_to_char(ch, "You have purchased an additional powerpoint.\r\n");
    }
  }
}
void init_parse(struct descriptor_data *d, char *arg)
{
  struct obj_data *obj = NULL;
  int number, i;
  switch (d->edit_mode)
  { 
    case INIT_MAIN:
      switch (*arg)
      {
        case '1':
          disp_meta_menu(d);
          break;
        case '2':
          STATE(d) = CON_PLAYING;
          init_cost(CH, TRUE);
          GET_SIG(CH)++;
          GET_GRADE(CH)++;
          GET_REAL_MAG(CH) += 100;
          if (GET_TRADITION(CH) == TRAD_ADEPT)
            GET_PP(CH) += 100;
          send_to_char("You feel yourself astral reflection shift and mold itself closer to the astral plane.\r\n", CH);
          STATE(d) = CON_PLAYING;
          PLR_FLAGS(CH).RemoveBit(PLR_INITIATE);
          break;
        case '3':
          disp_gaes_menu(d);
          break;
        case '4':
          STATE(d) = CON_PLAYING;
          PLR_FLAGS(CH).RemoveBit(PLR_INITIATE);
          send_to_char("Initiation cancelled.\r\n", CH);
          break;
        default:
          disp_init_menu(d);
          break;
      }
      break;
    case INIT_GAES:
      number = atoi(arg);
      if (*arg == 'q') {
        STATE(d) = CON_PLAYING;
        send_to_char("Initiation cancelled.\r\n", CH);
      } else if (number < 0) {
        send_to_char("Invalid Response. Select gaes to shed: ", CH);
      } else {
        for (i = 0; i < NUM_WEARS; i++)
          if (GET_EQ(CH, i) && GET_OBJ_TYPE(GET_EQ(CH, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(CH, i), 9) == GET_IDNUM(CH) && --number < 0) {
            obj = GET_EQ(CH, i);
            break;
          }
        if (!obj) {
          send_to_char("You don't have that many gaesa. Select gaes to shed: ", CH);
          return;
        }
        init_cost(CH, TRUE);
        GET_GRADE(CH)++;
        GET_REAL_MAG(CH) += 100;
        GET_OBJ_VAL(obj, 9) = 0;
        if (GET_TRADITION(CH) == TRAD_ADEPT)
          GET_PP(CH) += 100;
        send_to_char(CH, "You feel your magic return from that object, once again binding to your spirit.\r\n", CH);
        STATE(d) = CON_PLAYING;
        PLR_FLAGS(CH).RemoveBit(PLR_INITIATE);
      }
      break;
    case INIT_META:
      number = atoi(arg);
      if (!number) {
        STATE(d) = CON_PLAYING;
        send_to_char("Initiation cancelled.\r\n", CH);
      } else if (--number > META_MAX) {
        send_to_char("Invalid Response. Select ability to learn: ", CH);
      } else if (!can_metamagic(CH, number)) {
        send_to_char("You can't learn that metamagic technique. Select ability to learn: ", CH);
      } else {
        init_cost(CH, TRUE);
        GET_METAMAGIC(CH, number)++;
        GET_GRADE(CH)++;
        GET_REAL_MAG(CH) += 100;
        if (GET_TRADITION(CH) == TRAD_ADEPT)
          GET_PP(CH) += 100;
        send_to_char(CH, "You feel yourself grow closer to the astral plane as you become ready to learn %s.\r\n", metamagic[number]);
        STATE(d) = CON_PLAYING;
        PLR_FLAGS(CH).RemoveBit(PLR_INITIATE);
      }
      break;
    
  }
}

ACMD(do_masking)
{
  if (GET_METAMAGIC(ch, META_MASKING) < 2) {
    nonsensical_reply(ch);
    return;
  }
  skip_spaces(&argument);
  if (!*argument) {
    if (!GET_MASKING(ch))
      send_to_char("You aren't masking anything.\r\n", ch);
    else {
      send_to_char("You are masking: \r\n", ch);
      if (IS_SET(GET_MASKING(ch), MASK_COMPLETE))
        send_to_char("  Awakened State\r\n", ch);
      if (IS_SET(GET_MASKING(ch), MASK_INIT))
        send_to_char("  Initiation Grades\r\n", ch);
      if (IS_SET(GET_MASKING(ch), MASK_DUAL))
        send_to_char("  Perceiving State\r\n", ch);
    }
  } else if (is_abbrev(argument, "initiation")) {
    TOGGLE_BIT(GET_MASKING(ch), MASK_INIT);
    send_to_char(ch, "You will %s mask your initiation grades.\r\n", IS_SET(GET_MASKING(ch), MASK_INIT) ? "now" : "no longer");
  } else if (is_abbrev(argument, "complete")) {
    TOGGLE_BIT(GET_MASKING(ch), MASK_COMPLETE);
    send_to_char(ch, "You will %s mask your awakened state.\r\n", IS_SET(GET_MASKING(ch), MASK_COMPLETE) ? "now" : "no longer");
  } else if (is_abbrev(argument, "dual")) {
    TOGGLE_BIT(GET_MASKING(ch), MASK_DUAL);
    send_to_char(ch, "You will %s mask your perceiving state.\r\n", IS_SET(GET_MASKING(ch), MASK_DUAL) ? "now" : "no longer");
  } else send_to_char("What do you wish to mask?\r\n", ch);
}

ACMD(do_focus)
{
  if (GET_TRADITION(ch) != TRAD_ADEPT || !GET_POWER(ch, ADEPT_LIVINGFOCUS)) {
    send_to_char("You don't have the ability to do that.\r\n", ch);
    return;
  }
  skip_spaces(&argument);
  if (!*argument) {
    if (GET_SUSTAINED_NUM(ch)) {
      send_to_char("You are already sustaining a spell.\r\n", ch);
      return;
    }
    struct sustain_data *spell = GET_SUSTAINED(ch);
    for (; spell; spell = spell->next)
      if (!spell->caster && !spell->spirit && !spell->focus && spells[spell->spell].duration == SUSTAINED)
        break;
    if (!spell) {
      send_to_char("You are not affected by any spells that need sustaining.\r\n", ch);
      return;
    }
    spell->spirit = ch;
    GET_SUSTAINED_NUM(ch)++;
    GET_SUSTAINED_FOCI(spell->other)++;
    for (struct sustain_data *ospell = GET_SUSTAINED(spell->other); ospell; ospell = ospell->next)
      if (ospell->idnum == spell->idnum && ospell->other == ch) {
        ospell->spirit = ch;
        break;
      }
    strcpy(buf, spells[spell->spell].name);                                                                                      
    if (spell->spell == SPELL_INCATTR || spell->spell == SPELL_INCCYATTR ||
        spell->spell == SPELL_DECATTR || spell->spell == SPELL_DECCYATTR)
          sprintf(ENDOF(buf), " (%s)", attributes[spell->subtype]);
    send_to_char(ch, "You begin to concentrate on sustaining %s.\r\n", buf);
    send_to_char(spell->other, "You feel a weight lifted from you as someone takes over sustaining your %s spell.\r\n", buf);
  } else if (!str_cmp(argument, "release")) {
    if (!GET_SUSTAINED_NUM(ch)) {
      send_to_char("You aren't sustaining any spells.\r\n", ch);
      return;
    }
    struct sustain_data *spell = GET_SUSTAINED(ch);
    for (; spell; spell = spell->next)
      if (spell->spirit == ch)
        break;
    GET_SUSTAINED_NUM(ch)--;
    GET_SUSTAINED_FOCI(spell->other)--;
    spell->spirit = NULL;
    for (struct sustain_data *ospell = GET_SUSTAINED(spell->other); ospell; ospell = ospell->next)
      if (ospell->idnum == spell->idnum && ospell->other == ch) {
        ospell->spirit = NULL;
        break;
      }
    strcpy(buf, spells[spell->spell].name);                                                                                      
    if (spell->spell == SPELL_INCATTR || spell->spell == SPELL_INCCYATTR ||
        spell->spell == SPELL_DECATTR || spell->spell == SPELL_DECCYATTR)
          sprintf(ENDOF(buf), " (%s)", attributes[spell->subtype]);
    send_to_char(ch, "You stop sustaining %s.\r\n", buf);
    send_to_char(spell->other, "You suddenly feel the burden of your %s spell fall back on to you.\r\n", buf);
  } else send_to_char("You can only release sustained spells.\r\n", ch);
}

ACMD(do_metamagic)
{
  if (GET_GRADE(ch) == 0) {
    send_to_char("You are not close enough to the astral plane to have any metamagic powers.\r\n", ch);
    return;
  }
  int i = 0;
  *buf = '\0';
  for (int x = 0; x < META_MAX; x++)
    if (GET_METAMAGIC(ch, x)) {
      i++;
      sprintf(ENDOF(buf), "  %s%s^n\r\n", GET_METAMAGIC(ch, x) == 2 ? "" : "^r", metamagic[x]);
    }
  if (i > 0)
    send_to_char(ch, "You know the following metamagic techniques:\r\n%s", buf);
  else send_to_char("You don't know any metamagic techniques.\r\n", ch);
}

ACMD(do_cleanse)
{
  if (GET_METAMAGIC(ch, META_CLEANSING) < 2) {  
    nonsensical_reply(ch);
    return;
  } 
  if (!(IS_ASTRAL(ch) || IS_DUAL(ch)))
    send_to_char("You have no sense of the astral plane.\r\n", ch);
  else if (!GET_SKILL(ch, SKILL_SORCERY))
    send_to_char("You need practical knowledge of sorcery to cleanse the astral plane.\r\n", ch);
  else if (!world[ch->in_room].background[0])
    send_to_char("The astral plane in this area is calm.\r\n", ch);
  else if (world[ch->in_room].background[0] > GET_GRADE(ch))
    send_to_char("Cleansing a disturbance of this magnitude is beyond your abilities.\r\n", ch);
  else if (world[ch->in_room].background[1] == AURA_POWERSITE)
    send_to_char("You cannot cleanse a power site.\r\n", ch);
  else {
    int success = success_test(GET_SKILL(ch, SKILL_SORCERY), world[ch->in_room].background[0] * 2), background = world[ch->in_room].background[0];
    success /= 2;
    if (success <= 0)
      send_to_char("You fail to reduce the astral disturbances in this area.\r\n", ch);
    else {
      world[ch->in_room].background[0] = MAX(0, world[ch->in_room].background[0] - success);
      if (!world[ch->in_room].background[0]) {
        send_to_char("You successfully remove all astral disturbances from the area.\r\n", ch);
        sprintf(buf, "The astral disturbances in this area completely vanish.\r\n");
      } else {
        send_to_char("You succeed in lowering the astral disturbances in the area.\r\n", ch);
        sprintf(buf, "The astral disturbances in the are seem to diminish slightly.\r\n");
      }
      for (struct char_data *targ = world[ch->in_room].people; targ; targ = targ->next_in_room)
        if (ch != targ && (IS_ASTRAL(targ) || IS_DUAL(targ)))
          send_to_char(buf, targ);
    }
    spell_drain(ch, 0, background, DEADLY);
  }
}

ACMD(do_think)
{
  struct sustain_data *spell = NULL;
  for (spell = GET_SUSTAINED(ch); spell; spell = spell->next)
    if (spell->spell == SPELL_MINDLINK)
      break;
  if (!spell || !*argument) {
    send_to_char("You think about life, the universe and everything.\r\n", ch);
    return;
  }
  skip_spaces(&argument);
  sprintf(buf, "^rYou hear $v in your mind say, \"%s\"", argument);
  act(buf, FALSE, ch, 0, ch->char_specials.mindlink, TO_VICT);
  send_to_char(ch, "You think, \"%s\"\r\n", argument);
}