Difference for server/spell_util.c from version 1.79 to 1.80


version 1.79 version 1.80
Line 1
 
Line 1
 /*  /*
  * static char *rcsid_spell_util_c =   * static char *rcsid_spell_util_c =
  *   "$Id: spell_util.c,v 1.79 2003/09/03 12:25:06 tchize Exp $";   *   "$Id: spell_util.c,v 1.80 2003/09/13 05:02:13 mwedel Exp $";
  */   */
   
   
Line 37
 
Line 37
 #endif  #endif
 #include <sounds.h>  #include <sounds.h>
   
 archetype *spellarch[NROFREALSPELLS];  extern char *spell_mapping[];
   
   /* This returns a random spell from 'ob'.  If skill is set, then
    * the spell must be of this skill, it can be NULL in which case all
    * matching spells are used.
    */
   object *find_random_spell_in_ob(object *ob, char *skill)
   {
       int k=0,s;
       object *tmp;
   
       for (tmp=ob->inv; tmp; tmp=tmp->below)
    if (tmp->type == SPELL && (!skill || tmp->skill == skill)) k++;
   
       /* No spells, no need to progess further */
       if (!k) return NULL;
   
       s = RANDOM() % k;
   
       for (tmp=ob->inv; tmp; tmp=tmp->below)
    if (tmp->type == SPELL && (!skill || tmp->skill == skill)) {
        if (!s) return tmp;
        else s--;
    }
       /* Should never get here, but just in case */
       return NULL;
   }
   
   /* Relatively simple function that gets used a lot.
    * Basically, it sets up the skill pointer for the spell being
    * cast.  If op is really casting the spell, then the skill
    * is whatever skill the spell requires.
    * if instead caster (rod, horn, wand, etc) is casting the skill,
    * then they get exp for the skill that you need to use for
    * that object (use magic device).
    */
   void set_spell_skill(object *op, object *caster, object *spob, object *dest)
   {
       if (dest->skill) FREE_AND_CLEAR_STR(dest->skill);
       if (caster == op && spob->skill)
    dest->skill = add_refcount(spob->skill);
       else if (caster->skill)
    dest->skill = add_refcount(caster->skill);
   }
   
   /* init_spells: This should really be called check_spells, as that
    * is what it does.  It goes through the spells looking for any
    * obvious errors.  This was most useful in debugging when re-doing
    * all the spells to catch simple errors.  To use it all the time
    * will result in it spitting out messages that aren't really errors.
    */
 void init_spells() {  void init_spells() {
   #ifdef SPELL_DEBUG
   static int init_spells_done = 0;    static int init_spells_done = 0;
   int i;    int i;
       archetype *at;
   
   if (init_spells_done)    if (init_spells_done)
     return;      return;
   LOG(llevDebug, "Initializing spells...");      LOG(llevDebug, "Checking spells...\n");
   init_spell_param(); /*  peterm:  read the spell parameter file--newspells.c */  
   init_spells_done = 1;      for (at=first_archetype; at; at=at->next) {
   for (i = 0; i < NROFREALSPELLS; i++)   if (at->clone.type == SPELL) {
     if (spells[i].archname) {       if (at->clone.skill) {
       if ((spellarch[i] = find_archetype(spells[i].archname)) == NULL)   for (i=1; i<NUM_SKILLS; i++)
         LOG(llevError,       if (!strcmp(skill_names[i], at->clone.skill)) break;
             "Spell %s needs arch %s, your archetype file is out of date.\n",   if (i==NUM_SKILLS) {
             spells[i].name,spells[i].archname);       LOG(llevError,"Spell %s has improper associated skill %s\n", at->name, at->clone.skill);
     } else   }
       spellarch[i] = (archetype *) NULL;       }
   LOG(llevDebug, "done.\n");       /* other_arch is already checked for in the loader */
    }
       }
   
       i=0;
       while (spell_mapping[i]) {
    if (!find_archetype(spell_mapping[i])) {
        LOG(llevError,"Unable to find spell mapping %s (%i)\n", spell_mapping[i], i);
    }
    i++;
       }
       LOG(llevDebug, "Checking spells completed.\n");
   #endif
 }  }
   
   /* Dumps all the spells - now also dumps skill associated with the spell.
    * not sure what this would be used for, as the data seems pretty
    * minimal, but easy enough to keep around.
    */
 void dump_spells()  void dump_spells()
 {  {
     int i;      archetype *at;
   
     for (i = 0; i < NROFREALSPELLS; i++) {  
       char *name1 = NULL, *name2 = NULL;  
       if (spellarch[i]) {  
         name1 = spellarch[i]->name;  
   
         if (spellarch[i]->clone.other_arch)      for (at=first_archetype; at; at=at->next) {
           name2 = spellarch[i]->clone.other_arch->name;   if (at->clone.type == SPELL) {
        fprintf(stderr, "%s:%s:%s:%s:%d\n", at->clone.name?at->clone.name:"null",
        at->name, at->clone.other_arch?at->clone.other_arch->name:"null",
        at->clone.skill?at->clone.skill:"null", at->clone.level);
       }        }
       fprintf(stderr, "%s:%s:%s\n", spells[i].name, (name1?name1:"null"),   
       (name2?name2:"null"));  
     }      }
 }  }
   
 void spell_effect (int spell_type, int x, int y, mapstruct *map,  /* pretty basic function - basically just takes
    * an object, sets the x,y, and calls insert_ob_in_map
    */
   
   void spell_effect (object *spob, int x, int y, mapstruct *map,
  object *originator)   object *originator)
 {  {
   
   if (spellarch[spell_type] != (archetype *) NULL) {      if (spob->other_arch !=  NULL) {
     object *effect = arch_to_object(spellarch[spell_type]);   object *effect = arch_to_object(spob->other_arch);
   
     effect->x = x;      effect->x = x;
     effect->y = y;      effect->y = y;
Line 90
 
Line 157
   }    }
 }  }
   
 spell *find_spell(int spelltype) {  
   if(spelltype<0||spelltype>NROFREALSPELLS)  
     return NULL;  
   return &spells[spelltype];  
 }  
   
 /*  /*
  * base_level: level before considering attuned/repelled paths   * This function takes a caster and spell and presents the
    * effective level the caster needs to be to cast the spell.
    * basically, it just adjusts the spell->level with attuned/repelled
    * spellpaths.  Was called path_level_mod
    *
    * caster is person casting hte spell.
    * spell is the spell object.
  * Returns modified level.   * Returns modified level.
  */   */
 int path_level_mod (object *caster, int base_level, int spell_type)  int min_casting_level(object *caster, object *spell)
 {  {
  spell *s = find_spell(spell_type);  
  int new_level;   int new_level;
   
  if (caster->path_denied & s->path)      if (caster->path_denied & spell->path_attuned) {
  {  
    /* This case is not a bug, just the fact that this function is     /* This case is not a bug, just the fact that this function is
     * usually called BEFORE checking for path_deny. -AV      * usually called BEFORE checking for path_deny. -AV
    */
   #if 0
    LOG (llevError, "BUG: path_level_mod (arch %s, name %s): casting denied "     LOG (llevError, "BUG: path_level_mod (arch %s, name %s): casting denied "
    "spell\n", caster->arch->name, caster->name); */        "spell\n", caster->arch->name, caster->name);
   #endif
    return 1;     return 1;
  }   }
  new_level = base_level      new_level = spell->level
              + ((caster->path_repelled & s->path) ? -5 : 0)               + ((caster->path_repelled & spell->path_attuned) ? +2 : 0)
              + ((caster->path_attuned & s->path) ? 5 : 0);               + ((caster->path_attuned & spell->path_attuned) ? -2 : 0);
  return (new_level < 1) ? 1 : new_level;   return (new_level < 1) ? 1 : new_level;
 }  }
   
 int casting_level (object *caster, int spell_type)  
 {  
   return path_level_mod (caster, SK_level (caster), spell_type);  
 }  
   
   /* This function returns the effective level the spell
    * is being cast at.
    * Note that I changed the repelled/attuned bonus to 2 from 5.
    * This is because the new code compares casting_level against
    * min_caster_level, so the difference is effectively 4
    */
   
 int check_spell_known (object *op, int spell_type)  int caster_level(object *caster, object *spell)
 {  {
       int level= caster->level;
   
       /* If this is a player, try to find the matching skill */
       if (caster->type == PLAYER && spell->skill) {
   int i;    int i;
   for(i=0; i < (int)op->contr->nrofknownspells; i++)  
     if(op->contr->known_spells[i]==spell_type)  
       return 1;  
   return 0;  
 }  
   
    for (i=0; i < NUM_SKILLS; i++)
        if (caster->contr->last_skill_ob[i] &&
    caster->contr->last_skill_ob[i]->skill == spell->skill) {
    level = caster->contr->last_skill_ob[i]->level;
    break;
        }
       }
       /* Got valid caster level.  Now adjust for attunement */
       level += ((caster->path_repelled & spell->path_attuned) ? -2 : 0)
                + ((caster->path_attuned & spell->path_attuned) ? 2 : 0);
       return level;
   }
   
 /*  /* The following function scales the spellpoint cost of
  * cast_spell():   * a spell by it's increased effectiveness.  Some of the
  * Fires spell "type" in direction "dir".   * lower level spells become incredibly vicious at high
  * If "ability" is true, the spell is the innate ability of a monster.   * levels.  Very cheap mass destruction.  This function is
  * (ie, don't check for blocks_magic(), and don't add AT_MAGIC to attacktype.   * intended to keep the sp cost related to the effectiveness.
  *   * op is the player/monster
  * op is the creature that is owner of the object that is casting the spell   * caster is what is casting the spell, can be op.
  * caster is the actual object (wand, potion) casting the spell. can be   * spell is the spell object.
  *    same as op.   * Note that it is now possible for a spell to cost both grace and
  * dir is the direction to cast in.   * mana.  In that case, we return which ever value is higher.
  * ability is true if it is an ability casting the spell.  These can be  
  *    cast in no magic areas.  
  * item is the type of object that is casting the spell.  
  * stringarg is any options that are being used.  
  */   */
   
 /* Oct 95 - added cosmetic touches for MULTIPLE_GODS hack -b.t. */  int SP_level_spellpoint_cost(object *caster, object *spell, int flags)
   {
       int sp, grace, level = caster_level(caster, spell);
   
 int cast_spell(object *op,object *caster,int dir,int type,int ability,SpellTypeFrom item,char *stringarg) {      if (settings.spellpoint_level_depend == TRUE) {
     char *godname;   if (spell->stats.sp && spell->stats.maxsp) {
     spell *s=find_spell(type);       sp= (int) (spell->stats.sp *
     int success=0,bonus, mflags;        (1.0 + MAX(0,
     int duration=SP_PARAMETERS[type].bdur;  /*  get the base duration */   (float)(level-spell->level)/ (float)spell->stats.maxsp)));
    }
    else sp = spell->stats.sp;
    sp *= PATH_SP_MULT(caster,spell);
   
    if (spell->stats.grace && spell->stats.maxgrace) {
        grace= (int) (spell->stats.grace *
         (1.0 + MAX(0,
    (float)(level-spell->level)/ (float)spell->stats.maxgrace)));
    }
    else grace = spell->stats.grace;
    grace *= PATH_SP_MULT(caster,spell);
       } else {
    sp = spell->stats.sp * PATH_SP_MULT(caster,spell);
    grace = spell->stats.grace * PATH_SP_MULT(caster,spell);
       }
       if (flags == SPELL_HIGHEST)
    return MAX(sp, grace);
       else if (flags == SPELL_GRACE)
    return grace;
       else if (flags == SPELL_MANA)
    return sp;
       else {
    LOG(llevError, "SP_level_spellpoint_cost: Unknown flags passed: %d\n", flags);
    return 0;
       }
   }
   
     if(!strcmp((godname=determine_god(op)),"none")) godname="A random spirit";   
   
     /* It looks like the only properties we ever care about from the casting  /* SP_level_dam_adjust: Returns adjusted damage based on the caster.
      * object (caster) is spell paths and level.   * spob is the spell we are adjusting.
      */       */
     if (!caster && item==spellNormal)  int SP_level_dam_adjust(object *caster, object *spob)
  caster=op;  {
       int level = caster_level (caster, spob);
       int adj = level - min_casting_level(caster, spob);
   
     if(s==NULL) {      if(adj < 0) adj=0;
  LOG(llevError,"Error, unknown spell: %d\n",type);      if (spob->dam_modifier)
  return 0;   adj/=spob->dam_modifier;
     }      else adj=0;
     if(item==spellPotion) {  /*  if the potion casts an onself spell,       return adj;
      don't use the facing direction (given by apply.c)*/  
  if( spells[type].onself) dir = 0;  
     }      }
   
     /* Do a bunch of sanity checks to see if the player can cast the spell.  /* Adjust the strength of the spell based on level.
      * wizards bypass these checks.  If your using a wand or potion, these   * This is basically the same as SP_level_dam_adjust above,
      * checks don't apply either.  If your doing a summon spell and   * but instead looks at the level_modifier value.
      * have a golem, special work is needed there also.  
      */       */
   int SP_level_duration_adjust(object *caster, object *spob)
   {
       int level = caster_level (caster, spob);
       int adj = level - min_casting_level(caster, spob);
   
     if(!(QUERY_FLAG(op, FLAG_WIZ))&& (op->type==PLAYER) && (op->contr->shoottype==range_magic) &&      if(adj < 0) adj=0;
      (item!=spellPotion) && (!(IS_SUMMON_SPELL(type)&&op->contr->golem!=NULL))) {      if(spob->duration_modifier)
  if( !spells[type].cleric && op->stats.sp<SP_level_spellpoint_cost(op,caster,type)) {   adj/=spob->duration_modifier;
      new_draw_info(NDI_UNIQUE, 0,op,"You don't have enough mana.");      else adj=0;
      return 0;  
       return adj;
  }   }
  else if(spells[type].cleric && op->stats.grace<SP_level_spellpoint_cost(op,caster,type))  
   /* Adjust the strength of the spell based on level.
    * This is basically the same as SP_level_dam_adjust above,
    * but instead looks at the level_modifier value.
    */
   int SP_level_range_adjust(object *caster, object *spob)
  {   {
      /* it's possible for grace to go negative       int level = caster_level (caster, spob);
       * Fine - let grace go negative, but how negative it is should really      int adj = level - min_casting_level(caster, spob);
       * put a limit on things - in the old method, chance was the same  
       * no matter how negative it was.      if(adj < 0) adj=0;
       * Instead of subtracting 10 from the roll, add in grace (which is      if(spob->range_modifier)
       * negative).  This puts a real limit on things.   adj/=spob->range_modifier;
       else adj=0;
   
       return adj;
   }
   
   /* Checks to see if player knows the spell.  If the name is the same
    * as an existing spell, we presume they know it.
    * returns 1 if they know the spell, 0 if they don't.
       */        */
      if(random_roll(0, op->stats.Wis-1, op, PREFER_HIGH) + op->stats.grace -  object *check_spell_known (object *op, char *name)
         10*SP_level_spellpoint_cost(op,caster,type)/op->stats.maxgrace >0) {  {
      new_draw_info_format(NDI_UNIQUE, 0,op,       object *spop;
  "%s grants your prayer, though you are unworthy.",godname);  
       for (spop=op->inv; spop; spop=spop->below)
    if (spop->type == SPELL && !strcmp(spop->name, name)) return spop;
   
       return NULL;
      }       }
      else {  
  prayer_failure(op,op->stats.grace,SP_level_spellpoint_cost(op,caster,type));  
      new_draw_info_format(NDI_UNIQUE, 0,op,"%s ignores your prayer.",godname);  /* 
      return 0;   * Look at object 'op' and see if they know the spell
    * spname. This is pretty close to check_spell_known
    * above, but it uses a looser matching mechanism.
    * returns the matching spell object, or NULL.
    * If we match multiple spells but don't get an
    * exact match, we also return NULL.
    */
   
   object *lookup_spell_by_name(object *op,char *spname) {
       object *spob1=NULL, *spob2=NULL, *spob;
       int nummatch=0;
   
       if(spname==NULL) return NULL;
   
       /* Try to find the spell.  We store the results in spob1
        * and spob2 - spob1 is only taking the length of
        * the past spname, spob2 uses the length of the spell name.
        */
       for (spob = op->inv; spob; spob=spob->below) {
    if (spob->type == SPELL) {
        if (!strncmp(spob->name, spname, strlen(spname))) {
    nummatch++;
    spob1 = spob;
        } else if (!strncmp(spob->name, spname, strlen(spob->name))) {
    /* if spells have ambiguous names, it makes matching
    * really difficult.  (eg, fire and fireball would
    * fall into this category).  It shouldn't be hard to
    * make sure spell names don't overlap in that fashion.
    */
    if (spob2)
        LOG(llevError,"Found multiple spells with overlapping base names: %s, %s\n",
    spob2->name, spob->name);
    spob2 = spob;
      }       }
  }   }
     }      }
       /* if we have best match, return it.  Otherwise, if we have one match
     if (caster->path_denied & s->path) {       * on the loser match, return that, otehrwise null
  new_draw_info(NDI_UNIQUE, 0,op, "You are unable to cast that spell.");       */
  return 0;      if (spob2) return spob2;
       if (spob1 && nummatch == 1) return spob1;
       return NULL;
     }      }
   
     /* If it is an ability, assume that the designer of the archetype knows  /* reflwall - decides weither the (spell-)object sp_op will
      * what they are doing.   * be reflected from the given mapsquare. Returns 1 if true.
    * (Note that for living creatures there is a small chance that
    * reflect_spell fails.)
    * Caller should be sure it passes us valid map coordinates
    * eg, updated for tiled maps.
      */       */
     if (item==spellNormal && !ability && SK_level(caster) < s->level && !QUERY_FLAG(op,FLAG_WIZ)) {  int reflwall(mapstruct *m,int x,int y, object *sp_op) {
  new_draw_info(NDI_UNIQUE, 0,op, "You lack enough skill to cast that spell.");      object *op;
   
       if(OUT_OF_REAL_MAP(m,x,y)) return 0;
       for(op=get_map_ob(m,x,y);op!=NULL;op=op->above)
    if(QUERY_FLAG(op, FLAG_REFL_SPELL) && (!QUERY_FLAG(op, FLAG_ALIVE) ||
           sp_op->type==LIGHTNING || (rndm(0, 99)) < 90-(sp_op->level/10)))
    return 1;
   
  return 0;   return 0;
     }      }
   
     if (settings.casting_time == TRUE) {  /* cast_create_object: creates object new_op in direction dir
  if (op->casting==-1) { /* begin the casting */   * or if that is blocked, beneath the player (op).
      if (item == spellNormal&&!ability){   * we pass 'caster', but don't use it for anything.
  op->casting = s->time*PATH_TIME_MULT(op,s);   * This is really just a simple wrapper function .
  op->spell = s;   * returns the direction that the object was actually placed
  /* so no one cast a spell and switchs to get lower casting times!!! */   * in.
  op->spelltype = type;  
  op->spell_state = 1;  
  /* put the stringarg into the object struct so that when the  
  * spell is actually cast, it knows about the stringarg.  
  * necessary for the invoke command spells.    
  */   */
  if(stringarg) {  int cast_create_obj(object *op,object *caster,object *new_op, int dir)
    op->spellarg = strdup_local(stringarg);    {
       if(dir &&
          (get_map_flags(op->map,NULL, op->x+freearr_x[dir],op->y+freearr_y[dir], NULL, NULL) & (P_BLOCKED | P_OUT_OF_MAP))) {
    new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
    new_draw_info(NDI_UNIQUE, 0,op,"You cast it at your feet.");
    dir = 0;
  }   }
  else op->spellarg=NULL;      new_op->x=op->x+freearr_x[dir];
  return 0;      new_op->y=op->y+freearr_y[dir];
       if (dir == 0)
    insert_ob_in_map(new_op,op->map,op,INS_BELOW_ORIGINATOR);
       else
    insert_ob_in_map(new_op,op->map,op,0);
       return dir;
      }       }
  } else if (op->casting != 0) {  
      if (op->type == PLAYER )  /* Returns true if it is ok to put spell *op on the space/may provided.
  new_draw_info(NDI_UNIQUE, 0,op,"You are casting!");   * immune_stop is basically the attacktype of the spell (why
    * passed as a different value, not sure of).  If immune_stop
    * has the AT_MAGIC bit set, and there is a counterwall
    * on the space, the object doesn't get placed.  if immune stop
    * does not have AT_MAGIC, then counterwalls do not effect the spell.
    *
    */
   
   int ok_to_put_more(mapstruct *m,sint16 x,sint16 y,object *op,int immune_stop) {
       object *tmp;
       int mflags;
       mapstruct *mp;
   
       mp = m;
       mflags = get_map_flags(m, &mp, x, y, &x, &y);
   
       if (mflags & P_OUT_OF_MAP) return 0;
   
       if(mflags & P_WALL) return 0;
   
       for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above) {
    /* If there is a counterspell on the space, and this
    * object is using magic, don't progess.  I believe we could
    * leave this out and let in progress, and other areas of the code
    * will then remove it, but that would seem to to use more
    * resources, and may not work as well if a player is standing
    * on top of a counterwall spell (may hit the player before being
    * removed.)  On the other hand, it may be more dramatic for the
    * spell to actually hit the counterwall and be sucked up.
    */
    if ((tmp->attacktype & AT_COUNTERSPELL) &&
        (tmp->type != PLAYER) && !QUERY_FLAG(tmp,FLAG_MONSTER) &&
        (tmp->type != WEAPON) && (tmp->type != BOW) &&
        (tmp->type != ARROW) && (tmp->type != GOLEM) &&
        (immune_stop & AT_MAGIC)) return 0;
   
    /* This is to prevent 'out of control' spells.  Basically, this
    * limits one spell effect per space per spell.  This is definately
    * needed for performance reasons, and just for playability I believe.
    * there are no such things as multispaced spells right now, so
    * we don't need to worry about the head.
    */
    if ((tmp->stats.maxhp == op->stats.maxhp) && (tmp->type == op->type) &&
        (tmp->subtype == op->subtype))
      return 0;       return 0;
   
    /* Perhaps we should also put checks in for no magic and unholy
    * ground to prevent it from moving along?
    */
  }   }
       /* If it passes the above tests, it must be OK */
       return 1;
     }      }
   
     /*  ban removed on clerical spells in no-magic areas */  /* fire_arch_from_position: fires an archetype.
     mflags = get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL);   * op: person firing the object.
    * caster: object casting the spell.
    * x, y: where to fire the spell (note, it then uses op->map for the map
    *   for these coordinates, which is probably a really bad idea.
    * dir: direction to fire in.
    * spell: spell that is being fired.  It uses other_arch for the archetype
    * to fire.
    * returns 0 on failure, 1 on success.
    */
   
     if (!ability && item != spellPotion &&   int fire_arch_from_position (object *op, object *caster, sint16 x, sint16 y,
  ( (!s->cleric && (mflags & P_NO_MAGIC)) ||   int dir, object *spell)
     (s->cleric && (mflags & P_NO_CLERIC)))) {  {
       object *tmp;
       int mflags;
       mapstruct *m;
   
  if (op->type!=PLAYER)      if(spell->other_arch==NULL)
      return 0;       return 0;
   
  if(s->cleric)       m = op->map;
      new_draw_info_format(NDI_UNIQUE, 0,op,"This ground is unholy!  %s ignores you.",godname);      mflags = get_map_flags(m, &m, x, y, &x, &y);
  else      if (mflags & P_OUT_OF_MAP) {
  switch(op->contr->shoottype) {  
      case range_magic:  
  new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your spellcasting.");  
  break;  
      case range_misc:  
  new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your item.");  
  break;  
      case range_golem:  
  new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your scroll.");  
  break;  
      default:  
  break;  
  }  
  return 0;   return 0;
     }      }
     if(item == spellNormal && op->type==PLAYER && s->cleric &&  
    random_roll(0, 99, op, PREFER_HIGH) < s->level/(float)MAX(1,op->level) *  
    cleric_chance[op->stats.Wis]) {  
   
  play_sound_player_only(op->contr, SOUND_FUMBLE_SPELL,0,0);      tmp=arch_to_object(spell->other_arch);
  new_draw_info(NDI_UNIQUE, 0,op,"You fumble the spell.");  
  if (settings.casting_time == TRUE) {      if(tmp==NULL)
      op->casting = -1;  
      op->spell_state = 1;  
  }  
  if(s->sp==0) /* Shouldn't happen... */  
      return 0;       return 0;
  return(random_roll(1, SP_level_spellpoint_cost(op,caster,type), op, PREFER_LOW));  
     }  
     if(settings.spell_encumbrance == TRUE && item == spellNormal &&  
        op->type==PLAYER && (!s->cleric) ) {  
   
  int failure = random_roll(0, 199, op, PREFER_LOW) -  
      op->contr->encumbrance +op->level -s->level +35;  
   
  if( failure < 0) {      tmp->stats.dam=spell->stats.dam+SP_level_dam_adjust(caster,spell);
      new_draw_info(NDI_UNIQUE, 0,op,"You bungle the spell because you have too much heavy equipment in use.");      tmp->duration=spell->duration+SP_level_duration_adjust(caster,spell);
      if (settings.spell_failure_effects == TRUE)      /* code in time.c uses food for some things, duration for others */
  spell_failure(op,failure,SP_level_spellpoint_cost(op,caster,type));      tmp->stats.food = tmp->duration;
      return(random_roll(0, SP_level_spellpoint_cost(op,caster,type), op, PREFER_LOW));      tmp->range=spell->range+SP_level_range_adjust(caster,spell);
  }      tmp->attacktype = spell->attacktype;
     }      tmp->x=x;
       tmp->y=y;
       tmp->direction=dir;
       if (get_owner (op) != NULL)
    copy_owner (tmp, op);
       else
    set_owner (tmp, op);
       tmp->level = caster_level (caster, spell);
       set_spell_skill(op, caster, spell, tmp);
    
     /*      /* needed for AT_HOLYWORD,AT_GODPOWER stuff */
      * This is a simplification of the time it takes to cast a spell.      if(tmp->attacktype&AT_HOLYWORD || tmp->attacktype&AT_GODPOWER) {
      * In the future, the time will have to be spent before the   if(!tailor_god_spell(tmp,op)) return 0;
      * spell takes effect, and the caster can possibly be disturbed.  
      * (maybe that should depend upon the spell cast?)  
      */  
     if (settings.casting_time == TRUE) {  
  if (item == spellNormal && !ability ){  
      op->casting = -1;  
      op->spell_state = 1;  
      s = op->spell; /* set s to the cast spell */  
      type = op->spelltype;  
      stringarg = op->spellarg;  
  }  
     } else {  
  /* It seems the that the patch that added spell casting times  
  * increased the time value of most spells by about 10.  So divide  
  * by 10 to get back to more normal use.  
  */  
  op->speed_left -= (s->time*PATH_TIME_MULT(op,s) / 10) * FABS(op->speed);  
     }      }
       if(QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
    SET_ANIMATION(tmp, dir);
   
     switch((enum spellnrs) type) {      if ((tmp = insert_ob_in_map (tmp, m, op,0)) == NULL)
  case SP_BULLET:   return 1;
  case SP_LARGE_BULLET:  
  case SP_CAUSE_LIGHT:  
  case SP_CAUSE_HEAVY:  
  case SP_CAUSE_MEDIUM:  
  case SP_CAUSE_CRITICAL:  
      success = fire_arch(op,caster,dir,spellarch[type],type,1);  
      break;  
   
  case SP_HOLY_ORB:  
      success = fire_arch(op,caster,dir,spellarch[type],type,0);  
      break;  
   
  case SP_S_FIREBALL:  
  case SP_M_FIREBALL:  
  case SP_L_FIREBALL:  
  case SP_S_SNOWSTORM:  
  case SP_M_SNOWSTORM:  
  case SP_L_SNOWSTORM:  
  case SP_HELLFIRE:  
  case SP_POISON_CLOUD:  
  case SP_M_MISSILE:  
  case SP_S_MANABALL:  
  case SP_M_MANABALL:  
  case SP_L_MANABALL:  
  case SP_BALL_LIGHTNING:  
  case SP_DIVINE_SHOCK:  
  case SP_POISON_FOG:  
      success = fire_arch(op,caster,dir,spellarch[type],type, !ability);  
      break;  
   
  case SP_VITRIOL:  
      success = fire_arch(op,caster,dir,spellarch[type],type,0);  
      break;  
   
  case SP_MASS_CONFUSION:  
  case SP_SHOCKWAVE:  
  case SP_COLOR_SPRAY:  
  case SP_FACE_OF_DEATH:  
  case SP_COUNTER_SPELL:  
  case SP_BURNING_HANDS:  
  case SP_PARALYZE:  
  case SP_SLOW:  
  case SP_ICESTORM:  
  case SP_FIREBREATH:  
  case SP_LARGE_ICESTORM:  
  case SP_BANISHMENT:   
  case SP_MANA_BLAST:  
  case SP_WINDSTORM:  
  case SP_PEACE:  
  case SP_SPIDERWEB:  
  case SP_VITRIOL_SPLASH:  
  case SP_WRATHFUL_EYE:  
      success = cast_cone(op,caster,dir,duration,type,spellarch[type],!ability);  
      break;  
   
  case SP_TURN_UNDEAD:  
      if(QUERY_FLAG(op,FLAG_UNDEAD)) { /* the undead *don't* cast this */  
  new_draw_info(NDI_UNIQUE, 0,op,"Your undead nature prevents you from turning undead!");  
  success=0;  
  break;  
      }  
      /* else fall through to holy word below */  
  case SP_HOLY_WORD:  
      success = cast_cone(op,caster,dir,duration+(turn_bonus[op->stats.Wis]/5),type,  
  spellarch[type],0);  
      break;  
   
  case SP_HOLY_WRATH:  
  case SP_INSECT_PLAGUE:  
  case SP_RETRIBUTION:  
      success = cast_smite_spell(op,caster,dir,type);  
      break;  
   
  case SP_SUNSPEAR:  
  case SP_FIREBOLT:  
  case SP_FROSTBOLT:  
  case SP_S_LIGHTNING:  
  case SP_L_LIGHTNING:  
  case SP_FORKED_LIGHTNING:  
  case SP_STEAMBOLT:  
  case SP_MANA_BOLT:  
      success = fire_bolt(op,caster,dir,type,!ability);  
      break;  
   
  case SP_BOMB:      move_spell_effect(tmp);
      success = create_bomb(op,caster,dir,type,"bomb");  
      break;  
   
  case SP_GOLEM:      return 1;
  case SP_FIRE_ELEM:  }
  case SP_WATER_ELEM:  
  case SP_EARTH_ELEM:  
  case SP_AIR_ELEM:  
  case SP_MYSTIC_FIST:  
      success = summon_monster(op,caster,dir,spellarch[type],type);  
      break;  
   
  case SP_FINGER_DEATH:   
      success = finger_of_death(op,caster,dir);  
      break;  
   
  case SP_SUMMON_AVATAR:   
  case SP_HOLY_SERVANT: {  
      archetype *spat = find_archetype((type==SP_SUMMON_AVATAR)?"avatar":"holy_servant");  
      success = summon_avatar(op,caster,dir,spat,type);  
      break;  
      }   
   
  case SP_CONSECRATE:   /*****************************************************************************
      success = cast_consecrate(op);    *
      break;   * Code related to rods - perhaps better located in another file?
    *
  case SP_SUMMON_CULT:   ****************************************************************************/
      success = summon_cult_monsters(op,dir);  
      break;  
   
  case SP_PET:  
      success = summon_pet(op,dir, item);  
      break;  
   
  case SP_D_DOOR:  
      /* dimension door needs the actual caster, because that is what is  
       * moved.  
       */  
      success = dimension_door(op,dir);  
      break;  
   
  case SP_DARKNESS:  
  case SP_WALL_OF_THORNS:  
  case SP_CHAOS_POOL:  
  case SP_COUNTERWALL:  
  case SP_FIRE_WALL:  
  case SP_FROST_WALL:  
  case SP_EARTH_WALL:  
      success = magic_wall(op,caster,dir,type);  
      break;  
   
  case SP_MAGIC_MAPPING:  
      if(op->type==PLAYER) {  
  spell_effect(SP_MAGIC_MAPPING, op->x, op->y, op->map, op);  
  draw_map(op);  
  success=1;  
      }  
      break;  
   
  case SP_FEAR:  
      if(op->type==PLAYER)  
  bonus=fear_bonus[op->stats.Cha];  
      else  
  bonus=caster->head==NULL?caster->level/3+1:caster->head->level/3+1;  
      success = cast_cone(op,caster,dir,duration+bonus,SP_FEAR,spellarch[type],!ability);  
      break;  
   
  case SP_WOW:  
      success = cast_wow(op,dir,ability, item);  
      break;  
   
  case SP_DESTRUCTION:  
      success = cast_destruction(op,caster,5+op->stats.Int,AT_MAGIC);  
      break;  
   
  case SP_PERCEIVE:  
      success = perceive_self(op);  
      break;  
   
  case SP_WOR:  
      success = cast_wor(op,caster);  
      break;  
   
  case SP_INVIS:  
  case SP_INVIS_UNDEAD:  
  case SP_IMPROVED_INVIS:  
      success = cast_invisible(op,caster,type);  
      break;  
   
  case SP_PROBE:  
      success = probe(op,dir);  
      break;  
   
  case SP_CREATE_FOOD:  
      success = cast_create_food(op,caster,dir,stringarg);  
      break;  
   
  case SP_EARTH_DUST:  
      success = cast_earth2dust(op,caster);  
      break;  
   
  case SP_REGENERATION:  
  case SP_BLESS:  
  case SP_CURSE:  
  case SP_HOLY_POSSESSION:  
  case SP_STRENGTH:  
  case SP_DEXTERITY:  
  case SP_CONSTITUTION:  
  case SP_CHARISMA:  
  case SP_ARMOUR:  
  case SP_IRONWOOD_SKIN:  
  case SP_PROT_COLD:  
  case SP_PROT_FIRE:  
  case SP_PROT_ELEC:  
  case SP_PROT_POISON:  
  case SP_PROT_SLOW:  
  case SP_PROT_DRAIN:  
  case SP_PROT_PARALYZE:  
  case SP_PROT_ATTACK:  
  case SP_PROT_MAGIC:  
  case SP_PROT_CONFUSE:  
  case SP_PROT_CANCEL:  
  case SP_PROT_DEPLETE:  
  case SP_LEVITATE:  
  case SP_HEROISM:  
  case SP_CONFUSION:  
  case SP_XRAY:  
  case SP_DARK_VISION:  
  case SP_RAGE:  
      success = cast_change_attr(op,caster,dir,type);  
      break;  
   
  case SP_RESTORATION:  
  case SP_HEAL:  
  case SP_MINOR_HEAL:  
  case SP_MED_HEAL:  
  case SP_MAJOR_HEAL:  
  case SP_CURE_POISON:  
  case SP_CURE_CONFUSION:  
  case SP_CURE_BLINDNESS:  
  case SP_CURE_DISEASE:  
      success = cast_heal(op,dir,type);  
      break;  
   
  case SP_REGENERATE_SPELLPOINTS:  
      success = cast_regenerate_spellpoints(op);  
      break;  
   
  case SP_SMALL_SPEEDBALL:  
  case SP_LARGE_SPEEDBALL:  
      success = cast_speedball(op,dir,type);  
      break;  
   
  case SP_POLYMORPH:  
 #ifdef NO_POLYMORPH  
  /* Not great, but at least provide feedback so if players do have  
  * polymorph (ie, find it as a preset item or left over from before  
  * it was disabled), they get some feedback.  
  */  
  new_draw_info(NDI_UNIQUE, 0,op,"The spell fizzles");  
  success = 0;  
 #else  
  success = cast_polymorph(op,dir);  
 #endif  
  break;  
   
  case SP_CHARGING:  
      success = recharge(op);  
      break;  
   
  case SP_CANCELLATION:  
      success = fire_cancellation(op,dir,spellarch[type],!ability);  
      break;  
   
  case SP_ALCHEMY:  
      success = alchemy(op);  
      break;  
   
  case SP_REMOVE_CURSE:  
  case SP_REMOVE_DAMNATION:  
      success = remove_curse(op, type, item);  
      break;  
   
  case SP_IDENTIFY:  
      success = cast_identify(op);  
      break;  
   
  case SP_DETECT_MAGIC:  
  case SP_DETECT_MONSTER:  
  case SP_DETECT_EVIL:  
  case SP_DETECT_CURSE:  
  case SP_SHOW_INVIS:  
      success = cast_detection(op, type);  
      break;  
   
  case SP_AGGRAVATION:  
      aggravate_monsters(op);  
      success = 1;  
      break;  
   
  case SP_METEOR_SWARM:  
      success = 1;  
      fire_swarm(op, caster, dir, spellarch[type], SP_METEOR,  
         die_roll(3, 3, op, PREFER_HIGH) +  
         SP_level_strength_adjust(op,caster, type), 0);  
      break;  
   
  case SP_BULLET_SWARM:  
      success = 1;  
      fire_swarm(op, caster, dir, spellarch[type], SP_BULLET,  
         die_roll(3, 3, op, PREFER_HIGH) +  
         SP_level_strength_adjust(op,caster, type), 0);  
      break;  
   
  case SP_BULLET_STORM:  
      success = 1;  
      fire_swarm(op, caster, dir, spellarch[type], SP_LARGE_BULLET,  
         die_roll(3, 3, op, PREFER_HIGH) +  
         SP_level_strength_adjust(op,caster, type), 0);  
      break;  
   
  case SP_CAUSE_MANY:  
      success = 1;  
      fire_swarm(op, caster, dir, spellarch[type], SP_CAUSE_HEAVY,  
         die_roll(3, 3, op, PREFER_HIGH) +  
         SP_level_strength_adjust(op,caster, type), 0);  
      break;  
   
  case SP_MISSILE_SWARM:  
      success = 1;  
      fire_swarm(op, caster, dir, spellarch[type], SP_M_MISSILE,  
         die_roll(3, 3, op, PREFER_HIGH) +  
         SP_level_strength_adjust(op,caster, type), 0);  
      break;  
   
  case SP_METEOR:  
      success = fire_arch(op,caster,dir,find_archetype("meteor"),type,0);  
      break;  
   
  case SP_RAISE_DEAD:  
  case SP_RESURRECTION:  
      success = cast_raise_dead_spell(op,dir,type, NULL);  
      break;  
   
  case SP_IMMUNE_COLD:  
  case SP_IMMUNE_FIRE:  
  case SP_IMMUNE_ELEC:  
  case SP_IMMUNE_POISON:  
  case SP_IMMUNE_SLOW:  
  case SP_IMMUNE_DRAIN:  
  case SP_IMMUNE_PARALYZE:  
  case SP_IMMUNE_ATTACK:  
  case SP_IMMUNE_MAGIC:  
  case SP_INVULNERABILITY:  
  case SP_PROTECTION:  
  case SP_HASTE:  
      success=cast_change_attr(op,caster,dir,type);  
      break;  
   
   
  case SP_BUILD_DIRECTOR:  
  case SP_BUILD_BWALL:  
  case SP_BUILD_LWALL:  
  case SP_BUILD_FWALL:  
      success=create_the_feature(op,caster,dir,type);  
      break;  
   
  case SP_RUNE_FIRE:   
  case SP_RUNE_FROST:  
  case SP_RUNE_SHOCK:   
  case SP_RUNE_BLAST:   
  case SP_RUNE_DEATH:   
  case SP_RUNE_ANTIMAGIC:  
      success = write_rune(op,dir,0,caster->level,s->archname);  
      break;  
   
  case SP_RUNE_DRAINSP:  
      success = write_rune(op,dir,SP_MAGIC_DRAIN,caster->level,s->archname);  
      break;  
   
  case SP_RUNE_TRANSFER:  
      success= write_rune(op,dir,SP_TRANSFER,caster->level,s->archname);  
      break;  
   
  case SP_TRANSFER:  
      success = cast_transfer(op,dir);  
      break;  
   
  case SP_MAGIC_DRAIN:  
      success= drain_magic(op,dir);  
      break;  
   
  case SP_DISPEL_RUNE:  
      success = dispel_rune(op,dir,0);  /* 0 means no risk of detonating rune */  
      break;  
   
  case SP_SUMMON_EVIL_MONST:  
      if(op->type==PLAYER) return 0;  
      success = summon_hostile_monsters(op,op->stats.maxhp,op->race);  
      break;  
   
  case SP_REINCARNATION: {  
      object * dummy;  
      if(stringarg==NULL) {  
  new_draw_info(NDI_UNIQUE, 0,op,"Reincarnate WHO?");  
  success=0;  
  break;  
      }  
      dummy = get_object();  
      dummy->name = add_string(stringarg);  
      success = cast_raise_dead_spell(op,dir,type, dummy);  
      free_object(dummy);  
  }  
  break;  
   
  case SP_RUNE_MAGIC:  
  case SP_GLYPH:  
      return cast_generic_rune(op, caster, dir, stringarg, type);  
      break;  
   
  case SP_RUNE_MARK:  
      if(caster->type == PLAYER)  
  success=write_rune(op,dir,0,-2,stringarg);  
      else success= 0;  
      /* free the spell arg */  
      if(settings.casting_time == TRUE && stringarg) {  
  free(stringarg);  
  stringarg=NULL;  
      }  
      break;  
   
  case SP_LIGHT:  
      success = cast_light(op,caster,dir);  
      break;  
   
  case SP_DAYLIGHT:  
      success = cast_daylight(op);   
      break;   
   
  case SP_NIGHTFALL:  
      success = cast_nightfall(op);  
      break;  
   
  case SP_FAERY_FIRE:  
      success = cast_faery_fire(op,caster);  
      break;  
   
  case SP_SUMMON_FOG:  
      success = summon_fog(op,caster,dir,type);  
      break;  
   
  case SP_PACIFY:  
      cast_pacify(op,caster,spellarch[type],type);  
      success = 1;  
      break;  
   
  case SP_COMMAND_UNDEAD:  
      cast_charm_undead(op,caster,spellarch[type],type);  
      success = 1;  
      break;  
   
  case SP_CHARM:  
      cast_charm(op,caster,spellarch[type],type);  
      success = 1;  
      break;  
   
  case SP_CREATE_MISSILE:  
      success = cast_create_missile(op,caster,dir,stringarg);  
      break;  
   
  case SP_CAUSE_COLD:  
  case SP_CAUSE_EBOLA:  
  case SP_CAUSE_FLU:  
  case SP_CAUSE_PLAGUE:  
  case SP_CAUSE_LEPROSY:  
  case SP_CAUSE_SMALLPOX:  
  case SP_CAUSE_PNEUMONIC_PLAGUE:  
  case SP_CAUSE_ANTHRAX:  
  case SP_CAUSE_TYPHOID:  
  case SP_CAUSE_RABIES:  
      success = cast_cause_disease(op,caster,dir,spellarch[type],type);  
      break;  
   
  case SP_DANCING_SWORD:  
  case SP_STAFF_TO_SNAKE:   
  case SP_ANIMATE_WEAPON:  
      success = animate_weapon(op,caster,dir,spellarch[type],type);  
      break;  
   
  case SP_SANCTUARY:  
  case SP_FLAME_AURA:  
      success = create_aura(op,caster,spellarch[type],type,0);  
      break;  
   
  case SP_CONFLICT:  
      success = cast_cause_conflict(op,caster,spellarch[type],type);  
      break;  
   
  case SP_TOWN_PORTAL:  
      success= cast_create_town_portal (op,caster,dir);  
      break;  
     }  
   
     play_sound_map(op->map, op->x, op->y, SOUND_CAST_SPELL_0 + type);  
     /* free the spell arg */  
     if(settings.casting_time == TRUE && stringarg) {  
  free(stringarg);stringarg=NULL;  
     }  
   
     if (settings.spellpoint_level_depend == TRUE)  
  return success?SP_level_spellpoint_cost(op,caster,type):0;  
     else  
  return success?(s->sp*PATH_SP_MULT(op,s)):0;  
 }  
   
   
 int cast_create_obj(object *op,object *caster,object *new_op, int dir)  
 {  
     if(dir &&  
        (get_map_flags(op->map,NULL, op->x+freearr_x[dir],op->y+freearr_y[dir], NULL, NULL) & (P_BLOCKED | P_OUT_OF_MAP))) {  
  new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");  
  new_draw_info(NDI_UNIQUE, 0,op,"You cast it at your feet.");  
  dir = 0;  
     }  
     new_op->x=op->x+freearr_x[dir];  
     new_op->y=op->y+freearr_y[dir];  
     if (dir == 0)  
  insert_ob_in_map(new_op,op->map,op,INS_BELOW_ORIGINATOR);  
     else   
  insert_ob_in_map(new_op,op->map,op,0);  
     return dir;  
 }  
   
 int summon_monster(object *op,object *caster,int dir,archetype *at,int spellnum) {  
     object *tmp;  
     sint16 x, y;  
     mapstruct *m;  
   
     if(op->type==PLAYER)  
  if (op->contr->golem!=NULL && op->contr->golem_count == op->contr->golem->count) {  
      control_golem(op->contr->golem,dir);  
      return 0;  
  }  
   
     if(!dir)  
  dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);  
   
     m = op->map;  
     x = op->x+freearr_x[dir];  
     y = op->y+freearr_y[dir];  
   
     if((dir==-1) || get_map_flags(m, &m, x, y, &x, &y) & (P_BLOCKED | P_OUT_OF_MAP)) {  
  new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");  
  return 0;  
     }  
   
     tmp=arch_to_object(at);  
     if(op->type==PLAYER) {  
  CLEAR_FLAG(tmp, FLAG_MONSTER);  
  SET_FLAG(tmp, FLAG_FRIENDLY);  
  tmp->stats.exp=0;  
  add_friendly_object(tmp);  
  tmp->type=GOLEM;  
  /* Don't see any point in setting this when monsters summon monsters: */  
  set_owner(tmp,op);  
  op->contr->golem=tmp;  
  op->contr->golem_count = tmp->count;  
  /* give the player control of the golem */  
  op->contr->shoottype=range_golem;  
     } else {  
  if(QUERY_FLAG(op, FLAG_FRIENDLY)) {  
      object *owner = get_owner(op);  
      if (owner != NULL) { /* For now, we transfer ownership */  
  set_owner (tmp, owner);  
  tmp->move_type = PETMOVE;  
  add_friendly_object (tmp);  
  SET_FLAG (tmp, FLAG_FRIENDLY);  
      }  
  }  
  SET_FLAG(tmp, FLAG_MONSTER);  
     }  
   
     /* make the speed positive.*/  
     tmp->speed = FABS(tmp->speed);  
   
     /*  This sets the level dependencies on dam and hp for monsters */  
     /* players can't cope with too strong summonings. */  
     /* but monsters can.  reserve these for players. */  
     if(op->type==PLAYER) {  
  tmp->stats.hp = SP_PARAMETERS[spellnum].bdur +  
                     10 * SP_level_strength_adjust(op,caster,spellnum);  
  tmp->stats.dam= SP_PARAMETERS[spellnum].bdam +  
                     2* SP_level_dam_adjust(op,caster,spellnum);  
  tmp->speed += .02 * SP_level_dam_adjust(op,caster,spellnum);  
  tmp->speed = MIN(tmp->speed, 1.0);  
     }  
     tmp->stats.wc -= SP_level_dam_adjust(op,caster,spellnum);  
   
     /* limit the speed to 0.3 for non-players, 1 for players. */  
   
     if(tmp->stats.dam<0) tmp->stats.dam=127;  /*seen this go negative!*/  
     /*  make experience increase in proportion to the strength of the summoned creature. */  
     tmp->stats.exp *= SP_level_spellpoint_cost(op,caster,spellnum)/spells[spellnum].sp;  
     tmp->speed_left= -1;  
     tmp->x=x;  
     tmp->y=y;  
     tmp->direction=dir;  
     insert_ob_in_map(tmp,m,op,0);  
     return 1;  
 }  
   
   
 /* Returns true if it is ok to put spell *op on the space/may provided.  
  * Not sure what immune_stop was supposed to do - the only functions that  
  * current call this are move_cone and explosion, and both are just  
  * passing op->attacktype on.  
  * My guess judging from the old code is that if the space has a monster  
  * immune to immune_stop, the spell should not be placed.  But that  
  * doesn't make a lot of sense to me (a dragon immune to fire should not  
  * cause a fireball not to explode  
  *  
  * 0.94.2 - rewrote this to include the wall check instead of the calling  
  * function doing so.  
  */  
   
 static int ok_to_put_more(mapstruct *m,sint16 x,sint16 y,object *op,int immune_stop) {  
     object *tmp;  
     int mflags;  
     mapstruct *mp;  
   
     mp = m;  
     mflags = get_map_flags(m, &mp, x, y, &x, &y);  
   
     if (mflags & P_OUT_OF_MAP) return 0;  
   
     if(mflags & P_WALL) return 0;  
   
     for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above) {  
  /* If there is a counterspell on the space, and this  
  * object is using magic, don't progess.  I believe we could  
  * leave this out and let in progress, and other areas of the code  
  * will then remove it, but that would seem to to use more  
  * resources, and may not work as well if a player is standing  
  * on top of a counterwall spell (may hit the player before being  
  * removed.)  On the other hand, it may be more dramatic for the  
  * spell to actually hit the counterwall and be sucked up.  
  *  
  * We should probably check the type of the object, just in case  
  * a player somehow gets a counterspell attacktype.  
  */  
  if ((tmp->attacktype & AT_COUNTERSPELL) &&  
      (tmp->type != PLAYER) && !QUERY_FLAG(tmp,FLAG_MONSTER) &&  
      (tmp->type != WEAPON) && (tmp->type != BOW) &&  
      (tmp->type != ARROW) && (tmp->type != GOLEM) &&  
      (immune_stop & AT_MAGIC)) return 0;  
   
  /* This is to prevent 'out of control' spells.  Basically, this  
  * limits one spell effect per space per spell.  This is definately  
  * needed for performance reasons, and just for playability I believe.  
  * there are no such things as multispaced spells right now, so  
  * we don't need to worry about the head.  
  */  
  if ((tmp->stats.maxhp == op->stats.maxhp) && (tmp->type == op->type))  
      return 0;  
   
  /* Perhaps we should also put checks in for no magic and unholy  
  * ground to prevent it from moving along?  
  */  
     }  
     /* If it passes the above tests, it must be OK */  
     return 1;  
 }  
   
   
   
   
   
   
 int fire_bolt(object *op,object *caster,int dir,int type,int magic) {  
     object *tmp=NULL;  
     int mflags;  
   
     if (!spellarch[type])  
  return 0;  
   
     tmp=arch_to_object(spellarch[type]);  
     if(tmp==NULL)  
  return 0;  
   
     /*  peterm:  level dependency for bolts  */  
     tmp->stats.dam = SP_PARAMETERS[type].bdam + SP_level_dam_adjust(op,caster,type);  
     tmp->stats.hp = SP_PARAMETERS[type].bdur + SP_level_strength_adjust(op,caster,type);  
     if(magic)  
  tmp->attacktype|=AT_MAGIC;  
   
     tmp->x=op->x,tmp->y=op->y;  void regenerate_rod(object *rod) {
     tmp->direction=dir;      if (rod->stats.hp < rod->stats.maxhp) {
     if(QUERY_FLAG(tmp, FLAG_IS_TURNABLE))   rod->stats.hp+= 1 + rod->stats.maxhp/10;
  SET_ANIMATION(tmp, dir);  
     set_owner(tmp,op);  
     tmp->level = SK_level (caster);  
     tmp->x+=DIRX(tmp);  
     tmp->y+=DIRY(tmp);  
     tmp->map = op->map;  
   
     mflags = get_map_flags(tmp->map, &tmp->map, tmp->x, tmp->y, &tmp->x, &tmp->y);   if (rod->stats.hp > rod->stats.maxhp)
     if (mflags & P_OUT_OF_MAP) {       rod->stats.hp = rod->stats.maxhp;
  free_object(tmp);  
  return 0;  
     }  
     if (mflags & P_WALL) {  
  if(!QUERY_FLAG(tmp, FLAG_REFLECTING)) {  
      free_object(tmp);  
      return 0;  
  }  
  tmp->x=op->x,tmp->y=op->y;  
  tmp->direction=absdir(tmp->direction+4);  
  tmp->map = op->map;  
     }      }
     if ((tmp = insert_ob_in_map(tmp,tmp->map,op,0)) != NULL)  
     move_bolt (tmp);  
     return 1;  
 }  }
   
 /*  peterm  added a type field to fire_arch.  Needed it for making  
     fireball etall level dependent.  
     Later added a ball-lightning firing routine.  
  * dir is direction, at is spell we are firing.  Type is index of spell  
  * array.  If magic is 1, then add magical attacktype to spell.  
  * op is either the owner of the spell (player who gets exp) or the  
  * casting object owned by the owner.  caster is the casting object.  
  */  
   
 int fire_arch (object *op, object *caster, int dir, archetype *at, int type,  void drain_rod_charge(object *rod) {
  int magic)      rod->stats.hp -= SP_level_spellpoint_cost(rod, rod->inv, SPELL_HIGHEST);
 {  
  return fire_arch_from_position (op, caster, op->x, op->y, dir, at,  
                                  type, magic);  
 }  }
   
 int fire_arch_from_position (object *op, object *caster, sint16 x, sint16 y,  
  int dir, archetype *at, int type, int magic)  
 {  
   object *tmp, *env;  
    
   if(at==NULL)  
     return 0;  
   for(env=op;env->env!=NULL;env=env->env);  
   if (env->map == NULL)  
     return 0;  
   tmp=arch_to_object(at);  
   if(tmp==NULL)  
     return 0;  
   tmp->stats.sp=type;  
   tmp->stats.dam=SP_PARAMETERS[type].bdam+SP_level_dam_adjust(op,caster,type);  
   tmp->stats.hp=SP_PARAMETERS[type].bdur+SP_level_strength_adjust(op,caster,type);  
   tmp->x=x, tmp->y=y;  
   tmp->direction=dir;  
   if (get_owner (op) != NULL)  
     copy_owner (tmp, op);  
   else  
     set_owner (tmp, op);  
   tmp->level = casting_level (caster, type);  
   
   /* needed for AT_HOLYWORD,AT_GODPOWER stuff */  
   if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER) {  
        if(!tailor_god_spell(tmp,op)) return 0;   
   } else {  
     if(magic)  
  tmp->attacktype|=AT_MAGIC;  
   }  
   if(QUERY_FLAG(tmp, FLAG_IS_TURNABLE))  
     SET_ANIMATION(tmp, dir);  
   
   if ((tmp = insert_ob_in_map (tmp, op->map, op,0)) == NULL)  /* this function is commonly used to find a friendly target for
     return 1;   * spells such as heal or protection or armour
   switch(type) {   * op is what is looking for the target (which can be a player),
     case SP_M_MISSILE:   * dir is the direction we are looking in.  Return object found, or
     move_missile(tmp);   * NULL if no good object.
  break;   */
   case SP_POISON_FOG:  
   case SP_DIVINE_SHOCK:  
     case SP_BALL_LIGHTNING:  
  tmp->stats.food=SP_PARAMETERS[type].bdur +  
                       4*SP_level_strength_adjust(op,caster,type);  
  move_ball_lightning(tmp);  
  break;  
     default:  
     move_fired_arch(tmp);  
   }  
   return 1;  
 }  
   
 int  object *find_target_for_friendly_spell(object *op,int dir) {
 cast_cone(object *op, object *caster,int dir, int strength, int spell_type,archetype *spell_arch, int magic)  
 {  
     object *tmp;      object *tmp;
     int i,success=0,range_min= -1,range_max=1;  
   
     if(!dir) {  
  range_min= -3;  
  range_max=4;  
  strength/=2;  
     }  
   
     for(i=range_min;i<=range_max;i++) {  
  int x=op->x+freearr_x[absdir(dir+i)],  
      y=op->y+freearr_y[absdir(dir+i)];  
   
  if(get_map_flags(op->map,NULL, x,y, NULL, NULL) & (P_WALL | P_OUT_OF_MAP))  
      continue;  
   
  success=1;      /* I don't really get this block - if op isn't a player or rune,
  tmp=arch_to_object(spell_arch);       * we then make the owner of this object the target.
  set_owner(tmp,op);       * The owner could very well be no where near op.
  copy_owner(tmp,op);  
  tmp->level = casting_level (caster, spell_type);  
  tmp->x=x,tmp->y=y;  
   
  /* holy word stuff */                  
  if((tmp->attacktype&AT_HOLYWORD)||(tmp->attacktype&AT_GODPOWER)) {  
             if(!tailor_god_spell(tmp,op)) return 0;    
  } else /* god/holy word isnt really 'magic' */  
      if(magic)  
  tmp->attacktype|=AT_MAGIC;  /* JWI cone attacks should be considered  
                                      magical in nature ;) */  
  if(dir)  
      tmp->stats.sp=dir;  
  else  
      tmp->stats.sp=i;  
   
  tmp->stats.hp=strength+SP_level_strength_adjust(op,caster,spell_type);  
  tmp->stats.dam=SP_PARAMETERS[spell_type].bdam +  
                   SP_level_dam_adjust(op,caster,spell_type);   
  tmp->stats.maxhp=tmp->count;  
   
  if ( ! QUERY_FLAG (tmp, FLAG_FLYING))  
      LOG (llevDebug, "cast_cone(): arch %s doesn't have flying 1\n",  
  spell_arch->name);  
   
  if (( ! QUERY_FLAG (tmp, FLAG_WALK_ON) || ! QUERY_FLAG (tmp, FLAG_FLY_ON))  
      && tmp->stats.dam)  
   
  LOG (llevDebug, "cast_cone(): arch %s doesn't have walk_on 1 and "  
            "fly_on 1\n", spell_arch->name);  
   
  insert_ob_in_map(tmp,op->map,op,0);  
  if(tmp->other_arch) cone_drop(tmp);  
     }  
     return success;  
 }  
   
 /* this function checks to see if the cone pushes objects as well  
  * as flies over and damages them   
  */   */
       if(op->type!=PLAYER && op->type!=RUNE) {
 void check_cone_push(object *op) {   tmp=get_owner(op);
     object *tmp, *tmp2; /* object on the map */   /* If the owner does not exist, or is not a monster, than apply the spell
     int weight_move;   * to the caster.
   
     weight_move = 1000 + 1000 * op->level;  
   
     for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)  
     {  
  int num_sections = 1;  
   
  /* don't move parts of objects */  
  if(tmp->head) continue;  
   
  /* don't move floors or immobile objects */  
  if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)||(!QUERY_FLAG(tmp,FLAG_ALIVE)&&QUERY_FLAG(tmp,FLAG_NO_PICK))) continue;  
   
  /* count the object's sections */  
  for(tmp2 = tmp; tmp2!=NULL;tmp2=tmp2->more) num_sections++;  
   
  if(rndm(0, weight_move-1) > tmp->weight/num_sections) {  /* move it. */  
      /* move_object is really for monsters, but looking at   
       * the move_object function, it appears that it should  
       * also be safe for objects.  
       * This does return if successful or not, but  
       * I don't see us doing anything useful with that information  
       * right now.  
       */        */
      move_object(tmp, absdir(op->stats.sp));   if(!tmp || !QUERY_FLAG(tmp,FLAG_MONSTER)) tmp=op;
  }   }
       else {
    if (out_of_map(op->map, op->x+freearr_x[dir],op->y+freearr_y[dir]))
        tmp=NULL;
    else  {
        for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
          tmp!=NULL; tmp=tmp->above)
    if(tmp->type==PLAYER)
        break;
     }      }
 }  }
       /* didn't find a player there, look in current square for a player */
 /* drops an object based on what is in the cone's "other_arch" */      if(tmp==NULL)
 void cone_drop(object *op) {   for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
   object *new_ob = arch_to_object(op->other_arch);       if(tmp->type==PLAYER)
   new_ob->x = op->x;   break;
   new_ob->y = op->y;      return tmp;
   new_ob->stats.food = op->stats.hp;  
   new_ob->level = op->level;  
   set_owner(new_ob,op->owner);  
   if(op->chosen_skill) {  
     new_ob->chosen_skill=op->chosen_skill;  
     new_ob->exp_obj = op->chosen_skill->exp_obj;  
   }    }
   insert_ob_in_map(new_ob,op->map,op,0);  
      
 }  
   
   
 void move_cone(object *op) {  /* raytrace:
     int i;   * spell_find_dir(map, x, y, exclude) will search first the center square
     tag_t tag;   * then some close squares in the given map at the given coordinates for
    * live objects.
    * It will not consider the object given as exclude (= caster) among possible
    * live objects. If the caster is a player, the spell will go after
    * monsters/generators only. If not, the spell will hunt players only.
    * It returns the direction toward the first/closest live object if it finds
    * any, otherwise -1.
    * note that exclude can be NULL, in which case all bets are off.
    */
   
     /* if no map then hit_map will crash so just ignore object */  int spell_find_dir(mapstruct *m, int x, int y, object *exclude) {
     if (! op->map) {      int i,max=SIZEOFFREE;
  LOG(llevError,"Tried to move_cone object %s without a map.\n",      sint16 nx,ny;
      op->name ? op->name : "unknown");      int owner_type=0, mflags;
         op->speed = 0;      object *tmp;
         update_ob_speed (op);      mapstruct *mp;
  return;  
     }  
   
     /* lava saves it's life, but not yours  :) */      if (exclude && exclude->head)
     if (QUERY_FLAG(op, FLAG_LIFESAVE)) {   exclude = exclude->head;
  hit_map(op,0,op->attacktype);      if (exclude && exclude->type)
  return;   owner_type = exclude->type;
     }  
   
     /* If no owner left, the spell dies out. */      for(i=rndm(1, 8);i<max;i++) {
     if(get_owner(op)==NULL) {   nx = x + freearr_x[i];
  remove_ob(op);   ny = y + freearr_y[i];
  free_object(op);   mp = m;
  return;   mflags = get_map_flags(m, &mp, nx, ny, &nx, &ny);
     }   if (mflags & (P_OUT_OF_MAP | P_BLOCKSVIEW)) continue;
   
     /* Hit map returns 1 if it hits a monster.  If it does, set   tmp=get_map_ob(mp,nx,ny);
      * food to 1, which will stop the cone from progressing.  
      */  
     tag = op->count;  
     op->stats.food |= hit_map(op,0,op->attacktype);  
     /* Check to see if we should push anything.  
      * Cones with AT_PHYSICAL push whatever is in them to some  
      * degree.  */  
     if(op->attacktype & AT_PHYSICAL) check_cone_push(op);  
     if (was_destroyed (op, tag))  
         return;  
   
     if((op->stats.hp-=2)<0) {   while(tmp!=NULL && (((owner_type==PLAYER &&
  if(op->stats.exp) {       !QUERY_FLAG(tmp,FLAG_MONSTER) && !QUERY_FLAG(tmp,FLAG_GENERATOR)) ||
      op->speed = 0;              (owner_type!=PLAYER && tmp->type!=PLAYER)) ||
      update_ob_speed(op);       (tmp == exclude || (tmp->head && tmp->head == exclude))))
      op->stats.exp=0;       tmp=tmp->above;
      op->stats.sp=0; /* so they will join */  
  } else {   if(tmp!=NULL && can_see_monsterP(m,x,y,i))
      remove_ob(op);       return freedir[i];
      free_object(op);  
  }   }
  return;      return -1;  /* flag for "keep going the way you were" */
     }      }
   
     if(op->stats.food)   return;  
   
     op->stats.food=1;  
   
     for(i= -1;i<2;i++) {  /* put_a_monster: puts a monster named monstername near by
  int x=op->x+freearr_x[absdir(op->stats.sp+i)],   * op.  This creates the treasures for the monsters, and
      y=op->y+freearr_y[absdir(op->stats.sp+i)];   * also deals with multipart monsters properly.
    */
   
  if(ok_to_put_more(op->map,x,y,op,op->attacktype)) {  void put_a_monster(object *op,char *monstername) {
      object *tmp=arch_to_object(op->arch);      object *tmp,*head=NULL,*prev=NULL;
             copy_owner (tmp, op);      archetype *at;
      tmp->x=x, tmp->y=y;      int dir;
   
      /* added to make face of death work,and counterspell */      /* Handle cases where we are passed a bogus mosntername */
      tmp->level = op->level;  
   
      /* holy word stuff */      if((at=find_archetype(monstername))==NULL) return;
      if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER)   
  if(!tailor_god_spell(tmp,op)) return;  
   
      tmp->stats.sp=op->stats.sp,tmp->stats.hp=op->stats.hp+1;  
      tmp->stats.maxhp=op->stats.maxhp;  
      tmp->stats.dam = op->stats.dam;  
      tmp->attacktype=op->attacktype;  
      insert_ob_in_map(tmp,op->map,op,0);  
      if(tmp->other_arch) { cone_drop(tmp);}  
  }  
     }  
 }  
   
 void fire_a_ball (object *op, int dir, int strength)      /* find a free square nearby
 {       * first we check the closest square for free squares
   object *tmp;       */
   
   if ( ! op->other_arch) {      dir=find_first_free_spot(at,op->map,op->x,op->y);
     LOG (llevError, "BUG: fire_a_ball(): no other_arch\n");      if(dir!=-1) {
     return;   /* This is basically grabbed for generate monster.  Fixed 971225 to
   }   * insert multipart monsters properly
   if ( ! dir) {   */
     LOG (llevError, "BUG: fire_a_ball(): no direction\n");   while (at!=NULL) {
     return;       tmp=arch_to_object(at);
   }       tmp->x=op->x+freearr_x[dir]+at->clone.x;
   tmp = arch_to_object (op->other_arch);       tmp->y=op->y+freearr_y[dir]+at->clone.y;
   set_owner(tmp,op);       if (head) {
   tmp->direction=dir;   tmp->head=head;
   tmp->x=op->x,tmp->y=op->y;   prev->more=tmp;
   tmp->speed = 1;  
   update_ob_speed(tmp);  
   tmp->stats.hp=strength;  
   tmp->level = op->level;  
   SET_ANIMATION(tmp, dir);  
   SET_FLAG(tmp, FLAG_FLYING);  
   if ((tmp = insert_ob_in_map (tmp, op->map, op,0)) != NULL)  
     move_fired_arch (tmp);  
 }  }
        if (!head) head=tmp;
 void explosion(object *op) {       prev=tmp;
     object *tmp;       at=at->more;
     mapstruct *m=op->map; /* In case we free b */  
     int i;  
   
     if(--(op->stats.hp)<0) {  
  remove_ob(op);  
  free_object(op);  
  return;  
     }      }
     hit_map(op,0,op->attacktype);  
   
     if(op->stats.hp>2&&!op->value) {   if (head->randomitems)
  op->value=1;       create_treasure(head->randomitems, head, GT_INVISIBLE, op->map->difficulty,0);
  for(i=1;i<9;i++) {  
      int dx,dy;  
   
      dx=op->x+freearr_x[i];  
      dy=op->y+freearr_y[i];  
      /* ok_to_put_more already does things like checks for walls,   
       * out of map, etc.  
       */  
      if(ok_to_put_more(op->map,dx,dy,op,op->attacktype)) {  
  tmp=get_object();  
  copy_object(op,tmp); /* This is probably overkill on slow computers.. */  
  tmp->state=0;  
  tmp->speed_left= -0.21;  
  tmp->stats.hp--;  
  tmp->value=0;  
  tmp->x=dx;  
  tmp->y=dy;  
  insert_ob_in_map(tmp,m,op,0);  
      }  
  }  
     }  
 }  
   
    insert_ob_in_map(head,op->map,op,0);
   
 void forklightning(object *op, object *tmp) {   /* thought it'd be cool to insert a burnout, too.*/
     int new_dir=1;  /* direction or -1 for left, +1 for right 0 if no new bolt */   tmp=get_archetype("burnout");
     int t_dir; /* stores temporary dir calculation */   tmp->map = op->map;
    tmp->x=op->x+freearr_x[dir];
     /* pick a fork direction.  tmp->stats.Con is the left bias   tmp->y=op->y+freearr_y[dir];
      * i.e., the chance in 100 of forking LEFT   insert_ob_in_map(tmp,op->map,op,0);
      * Should start out at 50, down to 25 for one already going left  
      * down to 0 for one going 90 degrees left off original path  
      */  
      
     if(rndm(0, 99) < tmp->stats.Con)  /* fork left */  
  new_dir = -1;  
      
     /* check the new dir for a wall and in the map*/  
     t_dir = absdir(tmp->direction + new_dir);  
   
     if(get_map_flags(tmp->map,NULL, tmp->x + freearr_x[t_dir],tmp->y + freearr_y[t_dir],  
       NULL, NULL) & (P_WALL | P_OUT_OF_MAP))  
  new_dir = 0;  
   
     if(new_dir) { /* OK, we made a fork */  
  object *new_bolt = get_object();  
   
  copy_object(tmp,new_bolt);  
   
  /* reduce chances of subsequent forking */  
  new_bolt->stats.Dex -= 10;    
  tmp->stats.Dex -= 10;  /* less forks from main bolt too */  
  new_bolt->stats.Con += 25 * new_dir; /* adjust the left bias */  
  new_bolt->speed_left = -0.1;  
  new_bolt->direction = t_dir;  
  new_bolt->stats.hp++;  
  new_bolt->x+=DIRX(new_bolt);  
  new_bolt->y+=DIRY(new_bolt);  
  new_bolt->stats.dam /= 2;  /* reduce daughter bolt damage */  
  new_bolt->stats.dam++;  
  tmp->stats.dam /= 2;  /* reduce father bolt damage */  
  tmp->stats.dam++;  
  new_bolt = insert_ob_in_map(new_bolt,op->map,op,0);  
  update_turn_face(new_bolt);  
     }      }
 }  }
   
 /* reflwall - decides weither the (spell-)object sp_op will  /* peterm:  function which summons hostile monsters and
  * be reflected from the given mapsquare. Returns 1 if true.   * places them in nearby squares.
  * (Note that for living creatures there is a small chance that   * op is the summoner.
  * reflect_spell fails.)   * n is the number of monsters.
  * Caller should be sure it passes us valid map coordinates   * monstername is the name of the monster.
  * eg, updated for tiled maps.   * returns the number of monsters, which is basically n.
    * it should really see how many it successfully replaced and
    * return that instead.
    * Note that this is not used by any spells (summon evil monsters
    * use to call this, but best I can tell, that spell/ability was
    * never used.  This is however used by various failures on the
    * players part (alchemy, reincarnation, etc)
  */   */
 int reflwall(mapstruct *m,int x,int y, object *sp_op) {  
     object *op;  
   
     if(OUT_OF_REAL_MAP(m,x,y)) return 0;  
     for(op=get_map_ob(m,x,y);op!=NULL;op=op->above)  
  if(QUERY_FLAG(op, FLAG_REFL_SPELL) && (!QUERY_FLAG(op, FLAG_ALIVE) ||  
         sp_op->type==LIGHTNING || (rndm(0, 99)) < 90-sp_op->level/10))  
  return 1;  
   
     return 0;  
 }  
   
 void move_bolt(object *op) {  int summon_hostile_monsters(object *op,int n,char *monstername){
     object *tmp;      int i;
     int r, mflags;      for(i=0;i<n;i++)
     sint16 x, y;   put_a_monster(op,monstername);
     mapstruct *m;  
   
     if(--(op->stats.hp)<0) {      return n;
  remove_ob(op);  
  free_object(op);  
  return;  
     }      }
     hit_map(op,0,op->attacktype);  
   
     if(!op->value&&--(op->stats.exp)>0) {  
  op->value=1;  
  if(!op->direction)  
      return;  
   
  x = op->x+DIRX(op);  /*  Some local definitions for shuffle-attack */
  y = op->y+DIRY(op);      struct {
  m = op->map;   int attacktype;
  mflags = get_map_flags(m, &m, x, y, &x, &y);   int face;
       } ATTACKS[22] = {
    {AT_PHYSICAL,0},
    {AT_PHYSICAL,0},  /*face = explosion*/
    {AT_PHYSICAL,0},
    {AT_MAGIC,1},
    {AT_MAGIC,1},   /* face = last-burnout */
    {AT_MAGIC,1},
    {AT_FIRE,2},
    {AT_FIRE,2},  /* face = fire....  */
    {AT_FIRE,2},
    {AT_ELECTRICITY,3},
    {AT_ELECTRICITY,3},  /* ball_lightning */
    {AT_ELECTRICITY,3},
    {AT_COLD,4},
    {AT_COLD,4},  /* face=icestorm*/
    {AT_COLD,4},
    {AT_CONFUSION,5},
    {AT_POISON,7},
    {AT_POISON,7}, /* face = acid sphere.  generator */
    {AT_POISON,7},  /* poisoncloud face */
    {AT_SLOW,8},
    {AT_PARALYZE,9},
    {AT_FEAR,10}  };
   
  if (mflags & P_OUT_OF_MAP) return;  
   
  r=reflwall(m, x, y, op);  
  /* We are about to run into something - we may bounce */  
  if((mflags & P_WALL) || r ) { /* We're about to bounce */  
      if(!QUERY_FLAG(op, FLAG_REFLECTING))  
  return;  
   
      op->value=0;  /* shuffle_attack:  peterm
      /* Since walls don't run diagonal, if the bolt is in   * This routine shuffles the attack of op to one of the
       * one of 4 main directions, it just reflects back in the   * ones in the list.  It does this at random.  It also
       * opposite direction.  However, if the bolt is travelling   *  chooses a face appropriate to the attack that is
       * on the diagonal, it is trickier - eg, a bolt travelling   *  being committed by that square at the moment. 
       * northwest bounces different if it hits a north/south   *  right now it's being used by color spray and create pool of
       * wall (bounces to northeast) vs an east/west (bounces   *  chaos.
       * to the southwest.   * This could really be a better implementation - the
    * faces and attacktypes above are hardcoded, which is never
    * good.  The faces refer to faces in the animation sequence.
    * Not sure how to do better - but not having it hardcoded
    * would be nice.
    * I also fixed a bug here in that attacktype was |= -
    * to me, that would be that it would quickly get all
    * attacktypes, which probably wasn't the intent.  MSW 2003-06-03
       */        */
      if(op->direction&1)  void shuffle_attack(object *op,int change_face)
  op->direction=absdir(op->direction+4);  {
      else {      int i;
  int left, right;      i=rndm(0, 21);
   
  left = get_map_flags(op->map, NULL, op->x+freearr_x[absdir(op->direction-1)],  
         op->y+freearr_y[absdir(op->direction-1)], NULL, NULL) & P_WALL;  
   
  right = get_map_flags(op->map,NULL, op->x+freearr_x[absdir(op->direction+1)],      op->attacktype=ATTACKS[i].attacktype|AT_MAGIC;
                               op->y+freearr_y[absdir(op->direction+1)], NULL, NULL) & P_WALL;  
   
  if(left==right)      if(change_face) {
      op->direction=absdir(op->direction+4);   SET_ANIMATION(op, ATTACKS[i].face);
  else if(left)  
      op->direction=absdir(op->direction+2);  
  else if(right)  
      op->direction=absdir(op->direction-2);  
      }  
      update_turn_face(op); /* A bolt *must* be IS_TURNABLE */  
      return;  
  }  
  else { /* Create a copy of this object and put it ahead */  
      tmp=get_object();  
      copy_object(op,tmp);  
      tmp->speed_left= -0.1;  
      tmp->value=0;  
      tmp->stats.hp++;  
      tmp->x+=DIRX(tmp),tmp->y+=DIRY(tmp);  
      tmp = insert_ob_in_map(tmp,op->map,op,0);  
   
      /* New forking code.  Possibly create forks of this object  
       * going off in other directions.   
       */  
   
      if(rndm(0, 99)< tmp->stats.Dex) {  /* stats.Dex % of forking */  
  forklightning(op,tmp);  
      }  
      if (tmp) {  
  if ( ! tmp->stats.food) {  
      tmp->stats.food = 1;  
      move_bolt (tmp);  
  } else {  
      tmp->stats.food = 0;  
  }   }
      } /* if tmp */  
  } /* copy object and move it along */  
     } /* if move bolt along */  
 }  }
   
   
 /* updated this to allow more than the golem 'head' to attack */  /* prayer_failure: This is called when a player fails
 /* op is the golem to be moved. */   * at casting a prayer.
    * op is the player.
    * failure is basically how much grace they had.
    * power is how much grace the spell would normally take to cast.
    */
   
 void move_golem(object *op) {  void prayer_failure(object *op, int failure,int power)
     int made_attack=0;  { 
       char *godname;
     object *tmp;      object *tmp;
     tag_t tag;  
   
     if(QUERY_FLAG(op, FLAG_MONSTER))      if(!strcmp((godname=determine_god(op)),"none")) godname="Your spirit";
  return; /* Has already been moved */  
   
     if(get_owner(op)==NULL) {      if(failure<= -20 && failure > -40) /* wonder */
  LOG(llevDebug,"Golem without owner destructed.\n");      {
  remove_ob(op);   new_draw_info_format(NDI_UNIQUE, 0,op,"%s gives a sign to renew your faith.",godname);
  free_object(op);   tmp = get_archetype(SPELL_WONDER);
  return;   cast_cone(op,op,0,tmp);
     }   free_object(tmp);
     /* It would be nice to have a cleaner way of what message to print  
      * when the golem expires than these hard coded entries.  
      */  
     if(--op->stats.hp<0) {  
  char buf[MAX_BUF];  
  if(op->exp_obj && op->exp_obj->stats.Wis) {  
      if(op->inv)   
  strcpy(buf,"Your staff stops slithering around and lies still.");  
      else  
  sprintf(buf,"Your %s departed this plane.",op->name);  
  } else if (!strncmp(op->name,"animated ",9)) {  
      sprintf(buf,"Your %s falls to the ground.",op->name);  
  } else {  
      sprintf(buf,"Your %s dissolved.",op->name);  
  }  
  new_draw_info(NDI_UNIQUE, 0,op->owner,buf);  
  remove_friendly_object(op);  
  op->owner->contr->golem=NULL;  
  remove_ob(op);  
  free_object(op);  
  return;  
     }      }
   
     /* Do golem attacks/movement for single & multisq golems.       else if (failure <= -40 && failure > -60) /* confusion */
      * Assuming here that op is the 'head' object. Pass only op to       {
      * move_ob (makes recursive calls to other parts)    new_draw_info(NDI_UNIQUE, 0,op,"Your diety touches your mind!");
      * move_ob returns 0 if the creature was not able to move.   confuse_player(op,op,99);
      */  
     tag = op->count;  
     if(move_ob(op,op->direction,op)) return;  
     if (was_destroyed (op, tag))  
         return;  
   
     for(tmp=op;tmp;tmp=tmp->more) {   
  int x=tmp->x+freearr_x[op->direction],y=tmp->y+freearr_y[op->direction];  
  object *victim;  
   
  if (out_of_map(op->map,x,y)) continue;  
   
  for(victim=get_map_ob(op->map,x,y);victim;victim=victim->above)  
      if(QUERY_FLAG(victim,FLAG_ALIVE)) break;  
   
  /* We used to call will_hit_self to make sure we don't  
  * hit ourselves, but that didn't work, and I don't really  
  * know if that was more efficient anyways than this.  
  * This at least works.  Note that victim->head can be NULL,  
  * but since we are not trying to dereferance that pointer,  
  * that isn't a problem.  
  */  
  if(victim && victim!=op && victim->head!=op) {  
   
      /* for golems with race fields, we don't attack  
       * aligned races */  
   
      if(victim->race&&op->race&&strstr(op->race,victim->race)) {  
  if(op->owner) new_draw_info_format(NDI_UNIQUE, 0,op->owner,  
  "%s avoids damaging %s.",op->name,victim->name);  
      } else if (op->exp_obj   
         && victim == op->owner) {  
  if(op->owner) new_draw_info_format(NDI_UNIQUE, 0,op->owner,  
  "%s avoids damaging you.",op->name);  
      } else {  
  /* I think using hit_map here is just wrong -  
  * we are not attacking a space - we have a specific  
  * creature we are attacking, attack_ob seems more  
  * appropriate.  
  */  
   
  attack_ob(victim,op);  
 /* hit_map(tmp,op->direction,op->attacktype);*/  
  made_attack=1;  
      }       }
  } /* If victim */      else if (failure <= -60 && failure> -150) /* paralysis */
       {
    new_draw_info_format(NDI_UNIQUE, 0,op,"%s requires you to pray NOW.",godname);
    new_draw_info(NDI_UNIQUE, 0,op,"You comply, ignoring all else.");
    paralyze_player(op,op,99);
     }      }
     if(made_attack) update_object(op,UP_OBJ_FACE);      else if (failure <= -150) /* blast the immediate area */
       {
    tmp = get_archetype(GOD_POWER);
    new_draw_info_format(NDI_UNIQUE, 0,op,"%s smites you!",godname);
    cast_magic_storm(op,tmp, power);
    free_object(tmp);
 }  }
   
 void control_golem(object *op,int dir) {  
   op->direction=dir;  
 }  }
   
   /*
 void move_missile(object *op) {   * spell_failure()  handles the various effects for differing degrees
     int i, mflags;   * of failure badness.
     object *owner;   * op is the player that failed.
     sint16 new_x, new_y;   * failure is a random value of how badly you failed.
    * power is how many spellpoints you'd normally need for the spell.
     owner = get_owner(op);   * skill is the skill you'd need to cast the spell.
     /* It'd make things nastier if this wasn't here - spells cast by  
      * monster that are then killed would continue to survive  
      */       */
     if (owner == NULL) {  
  remove_ob(op);  
  free_object(op);  
  return;  
     }  
   
     new_x = op->x + DIRX(op);  
     new_y = op->y + DIRY(op);  
   
     mflags = get_map_flags(op->map, NULL, new_x, new_y, NULL, NULL);  
   
     if (mflags & P_BLOCKED) {  
  tag_t tag = op->count;  
  hit_map (op, op->direction, AT_MAGIC);  
  if ( ! was_destroyed (op, tag)) {  
      remove_ob (op);  
      free_object(op);  
  }  
  return;  
     }  
   
     remove_ob(op);  void spell_failure(object *op, int failure,int power, object *skill)
     if ( ! op->direction || (mflags & (P_WALL | P_OUT_OF_MAP))) {  
  free_object(op);  
  return;  
     }  
     op->x = new_x;  
     op->y = new_y;  
     i=spell_find_dir(op->map, op->x, op->y, get_owner(op));  
     if(i > 0 && i != op->direction){  
  op->direction=absdir(op->direction+((op->direction-i+8)%8<4?-1:1));  
  SET_ANIMATION(op, op->direction);  
     }  
     insert_ob_in_map(op,op->map,op,0);  
 }  
   
 void explode_object(object *op)  
 {  {
   tag_t op_tag = op->count;  
   object *tmp;    object *tmp;
   
   if (op->other_arch == NULL) {      if (settings.spell_failure_effects == FALSE)
     LOG (llevError, "BUG: explode_object(): op without other_arch\n");  
     remove_ob (op);  
     free_object (op);  
     return;  
   }  
   
   if (op->env) {  
     object *env;  
     for (env = op; env->env != NULL; env = env->env) ;  
     if (env->map == NULL || out_of_map (env->map, env->x, env->y)) {  
       LOG (llevError, "BUG: explode_object(): env out of map\n");  
       remove_ob (op);  
       free_object (op);  
       return;  
     }  
     remove_ob (op);  
     op->x = env->x;  
     op->y = env->y;  
     insert_ob_in_map(op, env->map, op, INS_NO_MERGE | INS_NO_WALK_ON);  
   } else if (out_of_map (op->map, op->x, op->y)) {  
      LOG (llevError, "BUG: explode_object(): op out of map\n");  
      remove_ob (op);  
      free_object (op);  
      return;  
   }  
   
   if (op->attacktype) {  
     hit_map (op, 0, op->attacktype);  
     if (was_destroyed (op, op_tag))  
       return;  
   }  
   
   /*  peterm:  hack added to make fireballs and other explosions level  
    *  dependent:  
    */  
   /*  op->stats.sp stores the spell which made this object here. */  
   tmp = arch_to_object (op->other_arch);  
   switch(tmp->type) {  
   case POISONCLOUD:  
   case FBALL: {  
   
     tmp->stats.dam += SP_level_dam_adjust(op,op,op->stats.sp);  
     if(op->attacktype&AT_MAGIC)  
       tmp->attacktype|=AT_MAGIC;  
     copy_owner (tmp, op);  
     if(op->stats.hp)  
       tmp->stats.hp=op->stats.hp;  
     tmp->stats.maxhp=op->count; /* Unique ID */  
     tmp->x = op->x;  
     tmp->y = op->y;  
   
     /* needed for AT_HOLYWORD stuff -b.t. */  
     if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER)   
       if ( ! tailor_god_spell (tmp, op)) {  
  remove_ob (op);  
  free_object (op);  
  return;   return;
       }  
   
     /* Prevent recursion */      if (failure<=-20 && failure > -40) /* wonder */
     CLEAR_FLAG (op, FLAG_WALK_ON);  
     CLEAR_FLAG (op, FLAG_FLY_ON);  
   
     insert_ob_in_map (tmp, op->map, op,0);  
     break;  
   }  
   case CONE:   
     {      {
       int type = tmp->stats.sp;   new_draw_info(NDI_UNIQUE, 0,op,"Your spell causes an unexpected effect.");
    tmp = get_archetype(SPELL_WONDER);
       if(!type) type = op->stats.sp;   cast_cone(op,op,0,tmp);
       copy_owner(tmp,op);  
       cast_cone(op,op,0,SP_PARAMETERS[type].bdur,type,op->other_arch,op->attacktype&AT_MAGIC);  
       /* don't need this anymore. */  
       free_object(tmp);        free_object(tmp);
       break;  
     }  
   }    }
   
   /* remove the firebullet */      else if (failure <= -40 && failure > -60) /* confusion */
   if ( ! was_destroyed (op, op_tag)) {      {
     remove_ob (op);   new_draw_info(NDI_UNIQUE, 0,op,"Your magic recoils on you, making you confused!");
     free_object (op);   confuse_player(op,op,99);
   }    }
           else if (failure <= -60 && failure> -80) /* paralysis */
       {
    new_draw_info(NDI_UNIQUE, 0,op,"Your magic stuns you!");
    paralyze_player(op,op,99);
 }  }
       else if (failure <= -80) /* blast the immediate area */
 void check_fired_arch (object *op)  
 {  {
     tag_t op_tag = op->count, tmp_tag;  
     object *tmp;      object *tmp;
     int dam, mflags;   /* Safety check to make sure we don't get any mana storms in scorn */
    if (get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL) & P_NO_MAGIC) {
     mflags = get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL);       new_draw_info(NDI_UNIQUE, 0, op, "The magic warps and you are turned inside out!");
        hit_player(tmp,9998,op,AT_INTERNAL);
   
     if ( ! (mflags & P_BLOCKED))   } else {
         return;       new_draw_info(NDI_UNIQUE, 0,op,"You lose control of the mana!  The uncontrolled magic blasts you!");
        tmp=get_archetype(LOOSE_MANA);
        tmp->level=skill->level;
        tmp->x=op->x;
        tmp->y=op->y;
   
     if (op->other_arch) {       /* increase the area of destruction a little for more powerful spells */
         explode_object (op);       tmp->range+=isqrt(power);
         return;  
     }  
   
     /* If nothing alive on this space, no reason to do anything further */       if (power>25) tmp->stats.dam = 25 + isqrt(power);
     if (!(mflags & P_IS_ALIVE)) return;       else tmp->stats.dam=power; /* nasty recoils! */
   
     for (tmp = get_map_ob (op->map,op->x,op->y); tmp != NULL; tmp = tmp->above)       tmp->stats.maxhp=tmp->count;
     {       insert_ob_in_map(tmp,op->map,NULL,0);
         if (QUERY_FLAG (tmp, FLAG_ALIVE)) {  
             tmp_tag = tmp->count;  
             dam = hit_player (tmp, op->stats.dam, op, op->attacktype);  
             if (was_destroyed (op, op_tag) || ! was_destroyed (tmp, tmp_tag)  
                 || (op->stats.dam -= dam) < 0)  
             {  
        if(!QUERY_FLAG(op,FLAG_REMOVED)) {   
                 remove_ob (op);  
                 free_object(op);  
                 return;  
        }  
             }  
         }          }
     }      }
 }  }
   
   /* This is where the main dispatch when someone casts a spell.
    *
    * op is the creature that is owner of the object that is casting the spell -
    *    eg, the player or monster.
    * caster is the actual object (wand, potion) casting the spell. can be
    *    same as op.
    * dir is the direction to cast in.  Note in some cases, if the spell
    *    is self only, dir really doesn't make a difference.
    * spell_ob is the spell object that is being cast.  From that,
    *    we can determine what to do.
    * stringarg is any options that are being used.  It can be NULL.  Almost
    *    certainly, only players will set it.  It is basically used as optional
    *    parameters to a spell (eg, item to create, information for marking runes,
    *    etc.
    * returns 1 on successful cast, or 0 on error.  These values should really
    * be swapped, so that 0 is sucessful, and non zero is failure, with a code
    * of what it failed.
    *
    * Note that this function is really a dispatch routine that calls other
    * functions - it just blindly returns what ever value those functions
    * return.  So if your writing a new function that is called from this,
    * it shoudl also return 1 on success, 0 on failure.
    *
    * if it is a player casting the spell (op->type == PLAYER, op == caster),
    * this function will decrease teh mana/grace appropriately.  For other
    * objects, the caller should do what it considers appropriate.
    */
   
   int cast_spell(object *op, object *caster,int dir,object *spell_ob, char *stringarg) {
   
       char *godname;
       int success=0,mflags, cast_level=0, old_shoottype;
       object *skill=NULL;
   
 void move_fired_arch (object *op)      if (op->contr) old_shoottype = op->contr->shoottype;
 {  
     tag_t op_tag = op->count;  
     sint16 new_x, new_y;  
     int mflags;  
     mapstruct *m;  
   
   
     /* peterm:  added to make comet leave a trail of burnouts       if (!spell_ob) {
  it's an unadulterated hack, but the effect is cool. */   LOG(llevError,"cast_spell: null spell object passed\n");
     if(op->stats.sp == SP_METEOR) {   return 0;
       replace_insert_ob_in_map("fire_trail",op);      }
       if (was_destroyed (op, op_tag))      if(!strcmp((godname=determine_god(op)),"none")) godname="A random spirit";
         return;  
     } /* end addition.  */  
   
     new_x = op->x + DIRX(op);      /* the caller should set caster to op if appropriate */
     new_y = op->y + DIRY(op);      if (!caster) {
     m = op->map;   LOG(llevError,"cast_spell: null caster object passed\n");
     mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);   return 0;
       }
   
     if (mflags & P_OUT_OF_MAP) {      /* if caster is a spell casting object, this normally shouldn't be
         remove_ob (op);       * an issue, because they don't have any spellpaths set up.
         free_object (op);       */
         return;      if (caster->path_denied & spell_ob->path_attuned) {
    new_draw_info(NDI_UNIQUE, 0,op, "That spell path is denied to you.");
    return 0;
     }      }
   
     if ( ! op->direction || (mflags & P_WALL)) {      /* if it is a player casting the spell, and they are really casting it
         if (op->other_arch) {       * (vs it coming from a wand, scroll, or whatever else), do some
             explode_object (op);       * checks.  If its the wizard, skip the skips - wizards can do special
         } else {       * things.  Likewise, we let monsters do special things - eg, they
             remove_ob (op);       * don't need the skill, bypass level checks, etc. The monster function
             free_object (op);       * should take care of that.
        */
       if (op->type == PLAYER && op == caster && !QUERY_FLAG(op, FLAG_WIZ)) {
    cast_level = caster_level(caster, spell_ob);
    if (spell_ob->skill) {
        skill = find_skill_by_name(op, spell_ob->skill);
        if (!skill) {
    new_draw_info_format(NDI_UNIQUE, 0,op,"You need the skill %s to cast %s.",
    spell_ob->skill, spell_ob->name);
    return 0;
        }
        if (min_casting_level(op, spell_ob) > cast_level) {
    new_draw_info(NDI_UNIQUE, 0,op, "You lack enough skill to cast that spell.");
    return 0;
         }          }
         return;  
     }      }
   
     remove_ob (op);   if (SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA) >  op->stats.sp) {
     op->x = new_x;       new_draw_info(NDI_UNIQUE, 0,op,"You don't have enough mana.");
     op->y = new_y;       return 0;
     if ((op = insert_ob_in_map (op, m, op,0)) == NULL)  
         return;  
   
     if (reflwall (op->map, op->x, op->y, op)) {  
         op->direction = absdir (op->direction + 4);  
         update_turn_face (op);  
     } else {  
         check_fired_arch (op);  
     }      }
    if (SP_level_spellpoint_cost(caster,spell_ob, SPELL_GRACE) >  op->stats.grace) {
        if(random_roll(0, op->stats.Wis-1, op, PREFER_HIGH) + op->stats.grace -
           10*SP_level_spellpoint_cost(caster,spell_ob, SPELL_GRACE)/op->stats.maxgrace >0) {
    new_draw_info_format(NDI_UNIQUE, 0,op,
    "%s grants your prayer, though you are unworthy.",godname);
 }  }
        else {
    prayer_failure(op,op->stats.grace,SP_level_spellpoint_cost(caster,spell_ob, SPELL_GRACE));
 void drain_rod_charge(object *rod) {   new_draw_info_format(NDI_UNIQUE, 0,op,"%s ignores your prayer.",godname);
   rod->stats.hp -= spells[rod->stats.sp].sp;   return 0;
   if (QUERY_FLAG(rod, FLAG_ANIMATE))  
     fix_rod_speed(rod);  
 }  }
   
 void fix_rod_speed(object *rod) {  
   rod->speed = (FABS(rod->arch->clone.speed)*rod->stats.hp) /  
                (float)rod->stats.maxhp;  
   if (rod->speed < 0.02)  
     rod->speed = 0.02;  
   update_ob_speed(rod);  
 }  }
   
    /* player/monster is trying to cast the spell.  might fumble it */
 /*  this function is commonly used to find a friendly target for   if (spell_ob->stats.grace && random_roll(0, 99, op, PREFER_HIGH) <
 spells such as heal or protection or armour  */         (spell_ob->level/(float)MAX(1,op->level) *cleric_chance[op->stats.Wis])) {
        play_sound_player_only(op->contr, SOUND_FUMBLE_SPELL,0,0);
 object *find_target_for_friendly_spell(object *op,int dir)        new_draw_info(NDI_UNIQUE, 0,op,"You fumble the spell.");
 { object *tmp;       if (settings.casting_time == TRUE) {
   if(op->type!=PLAYER&&op->type!=RUNE) {   op->casting_time = -1;
     tmp=get_owner(op);  
     /* If the owner does not exist, or is not a monster, than apply the spell  
      * to the caster.  
      */  
     if(!tmp || !QUERY_FLAG(tmp,FLAG_MONSTER)) tmp=op;  
   }    }
   else {       return(random_roll(1, SP_level_spellpoint_cost(caster,spell_ob, SPELL_GRACE), op, PREFER_LOW));
     if (out_of_map(op->map, op->x+freearr_x[dir],op->y+freearr_y[dir]))   } else if (spell_ob->stats.sp) {
  tmp=NULL;       int failure = random_roll(0, 199, op, PREFER_HIGH) -
     else  {   op->contr->encumbrance +op->level - spell_ob->level +35;
       for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);  
         tmp!=NULL;       if( failure < 0) {
         tmp=tmp->above)   new_draw_info(NDI_UNIQUE, 0,op,"You bungle the spell because you have too much heavy equipment in use.");
       if(tmp->type==PLAYER)   if (settings.spell_failure_effects == TRUE)
         break;       spell_failure(op,failure,
      SP_level_spellpoint_cost(caster,spell_ob, SPELL_MANA),
      skill);
    op->contr->shoottype = old_shoottype;
    return(random_roll(0, SP_level_spellpoint_cost(caster,spell_ob, SPELL_MANA), op, PREFER_LOW));
     }      }
   }    }
   if(tmp==NULL)               /* didn't find a player there, look in current square for a player */  
     for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)  
       if(tmp->type==PLAYER)  
         break;  
   return tmp;  
 }  }
   
       mflags = get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL);
   
 /*  peterm: ball lightning mover.      /* See if we can cast a spell here.  If the caster and op are
  *  ball lightning automatically seeks out a victim, if       * not alive, then this would mean that the mapmaker put the
  *  it sees any monsters close enough.       * objects on the space - presume that they know what they are
  */       * doing.
 void move_ball_lightning(object *op) {       */
     int i,nx,ny,j,dam_save,dir, mflags;      if (spell_ob->type == SPELL && caster->type != POTION &&
     object *owner;   (QUERY_FLAG(caster, FLAG_ALIVE) || QUERY_FLAG(op, FLAG_ALIVE)) &&
     mapstruct *m;   (((mflags & P_NO_MAGIC) && spell_ob->stats.sp) ||
       ((mflags & P_NO_CLERIC) && spell_ob->stats.grace))) {
   
     owner = get_owner(op);   if (op->type!=PLAYER)
        return 0;
   
     /* Only those attuned to PATH_ELEC may use ball lightning with AT_GODPOWER */   if ((mflags & P_NO_CLERIC) && spell_ob->stats.grace)
     if(owner && (!(owner->path_attuned & PATH_ELEC))&& (op->attacktype & AT_GODPOWER)) {       new_draw_info_format(NDI_UNIQUE, 0,op,"This ground is unholy!  %s ignores you.",godname);
  remove_ob(op);   else
  free_object(op);   switch(op->contr->shoottype) {
  new_draw_info_format(NDI_UNIQUE,0,owner,"The ball lightning dispells immediately.  Perhaps you need attunement to the spell path?");       case range_magic:
  return;   new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your spellcasting.");
    break;
        case range_misc:
    new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your item.");
    break;
        case range_golem:
    new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your scroll.");
    break;
        default:
    break;
    }
    return 0;
     }      }
   
     /* the following logic makes sure that the ball      if (caster == op && settings.casting_time == TRUE && spell_ob->type == SPELL) {
      * doesn't move into a wall, and makes   if (op->casting_time==-1) { /* begin the casting */
      * sure that it will move along a wall to try and       op->casting_time = spell_ob->casting_time*PATH_TIME_MULT(op,spell_ob);
      * get at it's victim.         op->spell = spell_ob;
      */       /* put the stringarg into the object struct so that when the
         * spell is actually cast, it knows about the stringarg.
     dir = 0;        * necessary for the invoke command spells. 
   
     if(!(rndm(0, 3)))  
  j = rndm(0, 1);  
     else j=0; /* ? j wasn't being assigned to anything before */  
   
     for(i = 1; i < 9; i++) {  
  /* i bit 0: alters sign of offset  
  * otther bits (i / 2): absolute value of offset  
  */   */
        if(stringarg) {
  int offset = ((i ^ j) & 1) ? (i / 2) : - (i / 2);   op->spellarg = strdup_local(stringarg); 
  int tmpdir = absdir (op->direction + offset);  
   
  nx = op->x + freearr_x[tmpdir];  
  ny = op->y + freearr_y[tmpdir];  
  if ( ! (get_map_flags(op->map, NULL, nx, ny, NULL, NULL) & (P_WALL | P_OUT_OF_MAP))) {  
      dir = tmpdir;  
      break;  
  }   }
        else op->spellarg=NULL;
        return 0;
     }      }
     if (dir == 0) {   else if (op->casting_time != 0) {
  nx = op->x;       if (op->type == PLAYER )
  ny = op->y;   new_draw_info(NDI_UNIQUE, 0,op,"You are casting!");
        return 0;
    } else {    /* casting_time == 0 */
        op->casting_time = -1;
        spell_ob = op->spell;
        stringarg = op->spellarg;
     }      }
       } else {
     remove_ob(op);   /* Take into account how long it takes to cast the spell.
     op->y=ny;   * if the player is casting it, then we use the time in
     op->x=nx;   * the spell object.  If it is a spell object, have it
     insert_ob_in_map(op,op->map,op,0);   * take two ticks.  Things that cast spells on the players
     * behalf (eg, altars, and whatever else) shouldn't cost
     dam_save = op->stats.dam;  /* save the original dam: we do halfdam on    * the player any time.
                                 surrounding squares */  
   
     /* loop over current square and neighbors to hit. */  
     for(j=0;j<9;j++) {  
  sint16 hx,hy;  /* hit these squares */  
  object *new_ob;  
   
  hx = nx+freearr_x[j];  
  hy = ny+freearr_y[j];  
   
  m = op->map;  
  mflags = get_map_flags(m, &m, hx, hy, &hx, &hy);  
   
  if (mflags & P_OUT_OF_MAP) continue;  
   
  /* first, don't ever, ever hit the owner.  Don't hit out  
  * of the map either.  
  */   */
    if (caster == op) {
  if((mflags & P_IS_ALIVE) && (!owner || owner->x!=hx || owner->y!=hy || owner->map != m)) {       op->speed_left -= spell_ob->casting_time*PATH_TIME_MULT(op,spell_ob) * FABS(op->speed);
      if(j) op->stats.dam = dam_save/2;   } else if (caster->type == WAND || caster->type == HORN ||
      hit_map(op,j,op->attacktype);      caster->type == ROD || caster->type == POTION ||
       caster->type == SCROLL) {
  }       op->speed_left -= 2 * FABS(op->speed);
  if(!(mflags & P_WALL) && op->other_arch) { /* insert the other arch */  
      new_ob = arch_to_object(op->other_arch);  
      new_ob->x = hx;  
      new_ob->y = hy;  
      insert_ob_in_map(new_ob,m,op,0);  
  }  
     }  
   
     /* restore to the center location and damage*/  
     op->stats.dam = dam_save;  
     i=spell_find_dir(op->map,op->x,op->y,get_owner(op));  
   
     if(i>=0) { /* we have a preferred direction!  */  
  /* pick another direction if the preferred dir is blocked. */  
  if(get_map_flags(op->map,NULL, nx + freearr_x[i], ny + freearr_y[i],NULL,NULL) & P_WALL) {  
      i= absdir(i + rndm(0, 2) -1);  /* -1, 0, +1 */  
  }  
  op->direction=i;  
     }      }
 }  }
   
       if (op->type == PLAYER && op == caster) {
    op->stats.grace -= SP_level_spellpoint_cost(caster,spell_ob, SPELL_GRACE);
    op->stats.sp -= SP_level_spellpoint_cost(caster,spell_ob, SPELL_MANA);
       }
   
       /* We want to try to find the skill to properly credit exp.
 /* peterm:       * for spell casting objects, the exp goes to the skill the casting
    do LOS stuff for ball lightning.  Go after the closest VISIBLE monster.       * object requires.
    Basically, we step back until dir is 0 and fail if we're blocked on the  
    way.  
   
 */  
   
 int reduction_dir[SIZEOFFREE][3] = {  
   {0,0,0}, /* 0 */   
   {0,0,0}, /* 1 */  
   {0,0,0}, /* 2 */  
   {0,0,0}, /* 3 */  
   {0,0,0}, /* 4 */  
   {0,0,0}, /* 5 */  
   {0,0,0}, /* 6 */  
   {0,0,0}, /* 7 */  
   {0,0,0}, /* 8 */  
   {8,1,2}, /* 9 */  
   {1,2,-1}, /* 10 */  
   {2,10,12}, /* 11 */  
   {2,3,-1}, /* 12 */  
   {2,3,4}, /* 13 */  
   {3,4,-1}, /* 14 */  
   {4,14,16}, /* 15 */  
   {5,4,-1}, /* 16 */  
   {4,5,6}, /* 17 */  
   {6,5,-1}, /* 18 */  
   {6,20,18}, /* 19 */  
   {7,6,-1}, /* 20 */  
   {6,7,8}, /* 21 */  
   {7,8,-1}, /* 22 */  
   {8,22,24}, /* 23 */  
   {8,1,-1}, /* 24 */  
   {24,9,10}, /* 25 */  
   {9,10,-1}, /* 26 */  
   {10,11,-1}, /* 27 */  
   {27,11,29}, /* 28 */  
   {11,12,-1}, /* 29 */  
   {12,13,-1}, /* 30 */  
   {12,13,14}, /* 31 */  
   {13,14,-1}, /* 32 */  
   {14,15,-1}, /* 33 */  
   {33,15,35}, /* 34 */  
   {16,15,-1}, /* 35 */  
   {17,16,-1}, /* 36 */  
   {18,17,16}, /* 37 */  
   {18,17,-1}, /* 38 */  
   {18,19,-1}, /* 39 */  
   {41,19,39}, /* 40 */  
   {19,20,-1}, /* 41 */  
   {20,21,-1}, /* 42 */  
   {20,21,22}, /* 43 */  
   {21,22,-1}, /* 44 */  
   {23,22,-1}, /* 45 */  
   {45,47,23}, /* 46 */  
   {23,24,-1}, /* 47 */  
   {24,9,-1}}; /* 48 */  
   
 /* Recursive routine to step back and see if we can  
  * find a path to that monster that we found.  If not,  
  * we don't bother going toward it.  Returns 1 if we  
  * can see a direct way to get it  
  * Modified to be map tile aware -.MSW  
  */   */
        if (op != caster && !skill && caster->skill) {
    skill = find_skill_by_name(op, caster->skill);
 int can_see_monsterP(mapstruct *m, int x, int y,int dir) {   if (!skill) {
     sint16 dx, dy;       new_draw_info_format(NDI_UNIQUE, 0,op,"You lack the skill %s to use the %s",
     int mflags;     caster->skill, query_name(caster));
        return 0;
     if(dir<0) return 0;  /* exit condition:  invalid direction */   }
    change_skill(op, skill, 0);    /* needed for proper exp credit */
     dx = x + freearr_x[dir];  
     dy = y + freearr_y[dir];  
   
     mflags = get_map_flags(m, &m, dx, dy, &dx, &dy);  
   
     if (mflags & (P_OUT_OF_MAP | P_WALL)) return 0;  
   
     /* yes, can see. */  
     if(dir < 9) return 1;  
     return can_see_monsterP(m, x, y, reduction_dir[dir][0]) |  
  can_see_monsterP(m,x,y, reduction_dir[dir][1]) |  
  can_see_monsterP(m,x,y, reduction_dir[dir][2]);  
 }  }
      
       switch(spell_ob->subtype) {
    /* The order of case statements is same as the order they show up
    * in in spells.h.
    */
    case SP_RAISE_DEAD:
        success = cast_raise_dead_spell(op,caster, spell_ob, dir,stringarg);
        break;
      
    case SP_RUNE:
        success = write_rune(op,caster, spell_ob, dir,stringarg);
        break;
    
 /* raytrace:   case SP_MAKE_MARK:
  * spell_find_dir(map, x, y, exclude) will search first the center square       success = write_mark(op, spell_ob, stringarg);
  * then some close squares in the given map at the given coordinates for       break;
  * live objects.  
  * It will not consider the object given as exclude (= caster) among possible  
  * live objects. If the caster is a player, the spell will go after  
  * monsters/generators only. If not, the spell will hunt players only.  
  * It returns the direction toward the first/closest live object if it finds  
  * any, otherwise -1.  
  */  
   
 int spell_find_dir(mapstruct *m, int x, int y, object *exclude) {   case SP_BOLT:
     int i,max=SIZEOFFREE;       success = fire_bolt(op,caster,dir,spell_ob,skill);
     sint16 nx,ny;       break;
     int owner_type=0, mflags;  
     object *tmp;  
     mapstruct *mp;  
   
     if (exclude && exclude->head)   case SP_BULLET:
  exclude = exclude->head;       success = fire_bullet(op, caster, dir, spell_ob);
     if (exclude && exclude->type)       break;
  owner_type = exclude->type;  
      
     for(i=rndm(1, 8);i<max;i++) {   case SP_CONE:
  nx = x + freearr_x[i];       success = cast_cone(op, caster, dir, spell_ob);
  ny = y + freearr_y[i];       break;
  mp = m;  
  mflags = get_map_flags(m, &mp, nx, ny, &nx, &ny);  
  if (mflags & P_OUT_OF_MAP) continue;  
   
  tmp=get_map_ob(mp,nx,ny);   case SP_BOMB:
        success = create_bomb(op,caster,dir, spell_ob);
        break;
   
  while(tmp!=NULL && (((owner_type==PLAYER &&   case SP_WONDER:
      !QUERY_FLAG(tmp,FLAG_MONSTER) && !QUERY_FLAG(tmp,FLAG_GENERATOR)) ||       success = cast_wonder(op,caster, dir,spell_ob);
             (owner_type!=PLAYER && tmp->type!=PLAYER)) ||       break;
      (tmp == exclude || (tmp->head && tmp->head == exclude))))  
      tmp=tmp->above;  
   
  if(tmp!=NULL && can_see_monsterP(m,x,y,i) && !(mflags & P_BLOCKSVIEW))   case SP_SMITE:
      return freedir[i];       success = cast_smite_spell(op,caster, dir,spell_ob);
     }       break;
     return -1;  /* flag for "keep going the way you were" */  
 }  
   
    case SP_MAGIC_MISSILE:
        success = fire_arch_from_position(op, caster, op->x + freearr_x[dir],
        op->y + freearr_y[dir], dir, spell_ob);
        break;
   
 /* peterm:  */   case SP_SUMMON_GOLEM:
        success = summon_golem(op, caster, dir, spell_ob);
        old_shoottype = range_golem;
        break;
   
 /*  peterm:  the following defines the parameters for all the    case SP_DIMENSION_DOOR:
 spells.         /* dimension door needs the actual caster, because that is what is
     bdam:  base damage or hp of spell or summoned monster        * moved.
   bdur:  base duration of spell or base range  
   ldam:  levels you need over the min for the spell to gain one dam  
   ldur:  levels you need over the min for the spell to gain one dur  
 */  */
        success = dimension_door(op,caster, spell_ob, dir);
        break;
   
    case SP_MAGIC_MAPPING:
        if(op->type==PLAYER) {
    spell_effect(spell_ob, op->x, op->y, op->map, op);
    draw_magic_map(op);
    success=1;
        }
        else success=0;
        break;
   
 /*  The following adjustments to spell strength are done in the   case SP_MAGIC_WALL:
 philosophy that the longer one knows a spell, the better one       success = magic_wall(op,caster,dir,spell_ob);
 should get at it.  So the more experience levels you are above       break;
 the minimum for knowing a spell, the more effective it becomes.   
 most of the following adjustments are for damage only, some are  
 for turning undead and whatnot.    
   
   The following functions assume that casting the spell is not   case SP_DESTRUCTION:
 denied.  Denied spells have an undefined path level modifier.       success = cast_destruction(op,caster,spell_ob);
 There wouldn't be a meaningful result anyway.       break;
   
   The arrays are defined in spells.h*/   case SP_PERCEIVE_SELF:
        success = perceive_self(op);
        break;
   
 /* July 1995 - I changed the next 3 functions slightly by replacing   case SP_WORD_OF_RECALL:
  * the casters level (op->level) with the skill level (SK_level(op))        success = cast_word_of_recall(op,caster,spell_ob);
  * instead for when we have compiled with ALLOW_SKILLS - b.t.        break;
  */  
 /* now based on caster's level instead of on op's level and caster's *  
  * path modifiers. --DAMN */  
   
 int SP_level_dam_adjust(object *op, object *caster, int spell_type)   case SP_INVISIBLE:
 {       success = cast_invisible(op,caster,spell_ob);
     int level = casting_level (caster, spell_type);       break;
     int adj = (level-spells[spell_type].level);  
     if(adj < 0) adj=0;  
     if(SP_PARAMETERS[spell_type].ldam)  
  adj/=SP_PARAMETERS[spell_type].ldam;  
     else adj=0;  
  return adj;  
 }  
   
 /* July 1995 - changed slightly (SK_level) for ALLOW_SKILLS - b.t. */   case SP_PROBE:
 /* now based on caster's level instead of on op's level and caster's *       success = probe(op,caster, spell_ob, dir);
  * path modifiers. --DAMN */       break;
 int SP_level_strength_adjust(object *op, object *caster, int spell_type)  
 {  
     int level = casting_level (caster, spell_type);  
     int adj = (level-spells[spell_type].level);  
     if(adj < 0) adj=0;  
     if(SP_PARAMETERS[spell_type].ldur)  
  adj/=SP_PARAMETERS[spell_type].ldur;  
     else adj=0;  
  return adj;  
 }  
   
 /*  The following function scales the spellpoint cost of   case SP_HEALING:
 a spell by it's increased effectiveness.  Some of the       success = cast_heal(op,caster, spell_ob, dir);
 lower level spells become incredibly vicious at high       break;
 levels.  Very cheap mass destruction.  This function is  
 intended to keep the sp cost related to the effectiveness. */  
   
 /* July 1995 - changed slightly (SK_level) for ALLOW_SKILLS - b.t. */  
   
 int SP_level_spellpoint_cost(object *op, object *caster, int spell_type)  
 {  
   spell *s=find_spell(spell_type);  
   int level = casting_level (caster, spell_type);  
   int sp;  
   
   if (settings.spellpoint_level_depend == TRUE) {   case SP_CREATE_FOOD:
       if (SP_PARAMETERS[spell_type].spl)       success = cast_create_food(op,caster,spell_ob, dir,stringarg);
    sp= (int) (spells[spell_type].sp *        break;
       (1.0 + (MAX(0, (float)(level-spells[spell_type].level)/  
       (float)SP_PARAMETERS[spell_type].spl ))));  
       else  
    sp= spells[spell_type].sp;  
       sp *= PATH_SP_MULT(caster,s);  
       return MIN(sp,(spells[spell_type].sp + 50));  
   } else {  
       return s->sp*PATH_SP_MULT(caster,s);  
   }  
 }  
   
    case SP_EARTH_TO_DUST:
        success = cast_earth_to_dust(op,caster,spell_ob);
        break;
   
    case SP_CHANGE_ABILITY:
        success = cast_change_ability(op,caster,spell_ob, dir);
        break;
   
 /*  move_swarm_spell:  peterm    case SP_BLESS:
  *  This is an implementation of the swarm spell.  It was written for       success = cast_bless(op,caster,spell_ob, dir);
  * meteor swarm, but it could be used for any swarm.  A swarm spell       break;
  * is a special type of object that casts swarms of other types  
  * of spells.  Which spell it casts is flexible.  It fires the spells  
  * from a set of squares surrounding the caster, in a given direction.   
  */  
   
 void move_swarm_spell(object *op)   case SP_CURSE:
 {       success = cast_curse(op,caster,spell_ob, dir);
     static int cardinal_adjust[9] = { -3, -2, -1, 0, 0, 0, 1, 2, 3 };       break;
     static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 };  
     sint16 target_x, target_y, origin_x, origin_y;  
     int basedir, adjustdir;  
   
     if(op->stats.hp == 0 || get_owner (op) == NULL) {   case SP_SUMMON_MONSTER:
  remove_ob(op);       success = summon_object(op,caster,spell_ob, dir);
  free_object(op);       break;
  return;  
     }  
     op->stats.hp--;  
   
     basedir = op->direction;   case SP_CHARGING:
     if(basedir == 0) {       success = recharge(op, caster, spell_ob);
         /* spray in all directions! 8) */       break;
         basedir = rndm(1, 8);  
     }  
   
     /* new offset calculation to make swarm element distribution   case SP_POLYMORPH:
      * more uniform   #ifdef NO_POLYMORPH
        /* Not great, but at least provide feedback so if players do have
         * polymorph (ie, find it as a preset item or left over from before
          * it was disabled), they get some feedback.
      */       */
     if(op->stats.hp) {       new_draw_info(NDI_UNIQUE, 0,op,"The spell fizzles");
         if(basedir & 1) {        success = 0;
             adjustdir = cardinal_adjust[rndm(0, 8)];  #else
         } else {       success = cast_polymorph(op,caster,spell_ob, dir);
             adjustdir = diagonal_adjust[rndm(0, 9)];  #endif
         }       break;
     } else {  
         adjustdir = 0;  /* fire the last one from forward. */  
     }  
   
     target_x = op->x + freearr_x[absdir(basedir + adjustdir)];   case SP_ALCHEMY:
     target_y = op->y + freearr_y[absdir(basedir + adjustdir)];       success = alchemy(op, caster, spell_ob);
        break;
   
     /* back up one space so we can hit point-blank targets, but this   case SP_REMOVE_CURSE:
      * necessitates extra out_of_map check below        success = remove_curse(op, caster, spell_ob);
      */       break;
     origin_x = target_x - freearr_x[basedir];  
     origin_y = target_y - freearr_y[basedir];  
   
    /*  for level dependence, we need to know what spell is fired.  */   case SP_IDENTIFY:
    /*  that's stored in op->stats.sp  by fire_swarm  */       success = cast_identify(op, caster, spell_ob);
    if ( !(get_map_flags(op->map, NULL, target_x, target_y, NULL, NULL) & P_WALL)       break;
         && ! out_of_map(op->map, origin_x, origin_y))  
        fire_arch_from_position (op, op, origin_x, origin_y, basedir,  
                                 op->other_arch, op->stats.sp, op->magic);  
 }  
   
    case SP_DETECTION:
        success = cast_detection(op, caster, spell_ob);
        break;
   
    case SP_MOOD_CHANGE:
        success = mood_change(op, caster, spell_ob);
        break;
   
 /*  fire_swarm:  peterm */   case SP_MOVING_BALL:
 /*  The following routine creates a swarm of objects.  It actually       if (spell_ob->path_repelled &&
     sets up a specific swarm object, which then fires off all   (spell_ob->path_repelled & caster->path_attuned) != spell_ob->path_repelled) {
     the parts of the swarm.     new_draw_info_format(NDI_UNIQUE, 0, op,
    "You lack the proper attunement to cast %s", spell_ob->name);
    success = 0;
        } else
    success = fire_arch_from_position(op,caster,
    op->x + freearr_x[dir], op->y + freearr_y[dir],
      dir, spell_ob);
        break;
   
   Interface:   case SP_SWARM:
     op:  the owner       success = fire_swarm(op, caster, spell_ob, dir);
     caster: the caster (owner, wand, rod, scroll)       break;
     dir: the direction everything will be fired in  
     swarm_type:  the archetype that will be fired  
     spell_type:  the spell type of the archetype that's fired  
     n:  the number to be fired.  
 */  
          
    case SP_CHANGE_MANA:
        success = cast_transfer(op,caster, spell_ob, dir);
        break;
   
 void fire_swarm (object *op, object *caster, int dir, archetype *swarm_type,   case SP_DISPEL_RUNE:
  int spell_type, int n, int magic)       /* in rune.c */
 {       success = dispel_rune(op,caster, spell_ob, skill, dir);
   object *tmp;       break;
   tmp=get_archetype("swarm_spell");  
   tmp->x=op->x;   case SP_CREATE_MISSILE:
   tmp->y=op->y;            success = cast_create_missile(op,caster,spell_ob, dir,stringarg);
   set_owner(tmp,op);       /* needed so that if swarm elements kill, caster gets xp.*/       break;
   tmp->level=casting_level(caster, spell_type);   /*needed later, to get level dep. right.*/  
   tmp->stats.sp=spell_type;  /* needed later, see move_swarm_spell */  
   tmp->attacktype = swarm_type->clone.attacktype;  
   if (tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER) {  
     if ( ! tailor_god_spell (tmp, op))  
       return;  
   }  
   tmp->magic = magic;  
   tmp->stats.hp=n;     /* n in swarm*/  
   tmp->other_arch=swarm_type;  /* the archetype of the things to be fired*/  
   tmp->direction=dir;   
   tmp->invisible=1;  
   insert_ob_in_map(tmp,op->map,op,0);  
 }  
   
 /* create an aura spell object and put it in the player's inventory.  */   case SP_CONSECRATE:
        success = cast_consecrate(op, caster, spell_ob);
        break;
   
 int create_aura(object *op, object *caster, archetype *aura_arch, int spell_type,   case SP_ANIMATE_WEAPON:
  int magic)        success = animate_weapon(op, caster, spell_ob, dir);
 {       old_shoottype = range_golem;
     int refresh=0;       break;
     object *new_aura;  
   
     new_aura = present_arch_in_ob(aura_arch, op);   case SP_LIGHT:
     if (new_aura) refresh=1;       success = cast_light(op, caster, spell_ob, dir);
     else new_aura = arch_to_object(aura_arch);       break;
   
     new_aura->stats.food = SP_PARAMETERS[spell_type].bdur +   case SP_CHANGE_MAP_LIGHT:
                   10* SP_level_strength_adjust(op,caster,spell_type);       success = cast_change_map_lightlevel(op, caster, spell_ob);
     new_aura->stats.dam = SP_PARAMETERS[spell_type].bdam       break;
                   +SP_level_dam_adjust(op,caster,spell_type);  
   
     set_owner(new_aura,op);   case SP_FAERY_FIRE:
     if(magic) new_aura->attacktype|=AT_MAGIC;       success = cast_destruction(op,caster,spell_ob);
        break;
   
     if(new_aura->owner) {   case SP_CAUSE_DISEASE:
  new_aura->chosen_skill = op->chosen_skill;       success = cast_cause_disease(op, caster, spell_ob, dir);
  if(new_aura->chosen_skill) new_aura->exp_obj = op->chosen_skill->exp_obj;       break;
     }  
     new_aura->level = SK_level(caster);  
     if (refresh)   
  new_draw_info(NDI_UNIQUE, 0, op, "You recast the spell while in effect.");  
     else  
  insert_ob_in_ob(new_aura, op);  
     return 1;  
 }  
   
 /*  look_up_spell_by_name:  peterm   case SP_AURA:
     this function attempts to find the spell spname in spells[].       success = create_aura(op, caster, spell_ob);
     if it doesn't exist, or if the op cannot cast that spname,       break;
     -1 is returned.  */  
   
    case SP_TOWN_PORTAL:
        success= cast_create_town_portal (op,caster,spell_ob, dir);
        break;
   
 int look_up_spell_by_name(object *op,char *spname) {   default:
     int numknown;       LOG(llevError,"cast_spell: Unhandled spell subtype %d\n",
     int spnum;   spell_ob->subtype);
     int plen;  
     int spellen;  
     int i;  
   
     if(spname==NULL) return -1;  
     if(op==NULL) numknown=NROFREALSPELLS;  
  else  
  if(QUERY_FLAG(op, FLAG_WIZ)) numknown=NROFREALSPELLS;  
      else numknown = op->contr->nrofknownspells;  
     plen=strlen(spname);  
     for(i=0;i<numknown;i++) {  
  if(op==NULL) spnum=i;  
  else  
      if(QUERY_FLAG(op,FLAG_WIZ)) spnum=i;  
  else  spnum = op->contr->known_spells[i];  
   
  spellen=strlen(spells[spnum].name);      }
   
  if(strncmp(spname,spells[spnum].name,MIN(spellen,plen)) == 0 )       /* FIXME - we need some better sound suppport */
      return spnum;  /*    play_sound_map(op->map, op->x, op->y, SOUND_CAST_SPELL_0 + type);*/
       /* free the spell arg */
       if(settings.casting_time == TRUE && stringarg) {
    free(stringarg);
    stringarg=NULL;
     }      }
     return -1;      /* perhaps a bit of a hack, but if using a wand, it has to change the skill
        * to something like use_magic_item, but you really want to be able to fire
        * it again.
        */
       if (op->contr) op->contr->shoottype = old_shoottype;
   
       return success;
 }  }
   
   
   /* This is called from time.c/process_object().  That function
    * calls this for any SPELL_EFFECT type objects.  This function
    * then dispatches them to the appropriate specific routines.
    */
   void move_spell_effect(object *op) {
   
       switch (op->subtype) {
    case SP_BOLT:
        move_bolt(op);
        break;
   
 void put_a_monster(object *op,char *monstername) {   case SP_BULLET:
   object *tmp,*head=NULL,*prev=NULL;       move_bullet(op);
   archetype *at;       break;
   int dir;  
      
   /* find a free square nearby */   case SP_EXPLOSION:
   /* first we check the closest square for free squares */       explosion(op);
   if((at=find_archetype(monstername))==NULL) return;       break;
   dir=find_first_free_spot(at,op->map,op->x,op->y);  
   if(dir!=-1) {  
     /* This is basically grabbed for generate monster.  Fixed 971225 to  
      * insert multipart monsters properly  
      */  
     while (at!=NULL) {  
       tmp=arch_to_object(at);  
       tmp->x=op->x+freearr_x[dir]+at->clone.x;  
       tmp->y=op->y+freearr_y[dir]+at->clone.y;  
       if (head) {  
         tmp->head=head;  
         prev->more=tmp;  
       }  
       if (!head) head=tmp;  
       prev=tmp;  
       at=at->more;  
     }  
   
     if (HAS_RANDOM_ITEMS(head))   case SP_CONE:
       create_treasure(head->randomitems, head, GT_INVISIBLE, op->map->difficulty,0);       move_cone(op);
     insert_ob_in_map(head,op->map,op,0);       break;
   
     /* thought it'd be cool to insert a burnout, too.*/   case SP_BOMB:
     tmp=get_archetype("burnout");       animate_bomb(op);
     tmp->map = op->map;       break;
     tmp->x=op->x+freearr_x[dir];  
     tmp->y=op->y+freearr_y[dir];  
     insert_ob_in_map(tmp,op->map,op,0);  
   }  
 }  
   
    case SP_MAGIC_MISSILE:
        move_missile(op);
        break;
   
 /*  Some local definitions for shuffle-attack */   case SP_WORD_OF_RECALL:
     struct {       execute_word_of_recall(op);
  int attacktype;       break;
  int face;  
     } ATTACKS[22] = {  
  {AT_PHYSICAL,0},  
  {AT_PHYSICAL,0},  /*face = explosion*/  
  {AT_PHYSICAL,0},  
  {AT_MAGIC,1},  
  {AT_MAGIC,1},   /* face = last-burnout */  
  {AT_MAGIC,1},  
  {AT_FIRE,2},  
  {AT_FIRE,2},  /* face = fire....  */  
  {AT_FIRE,2},  
  {AT_ELECTRICITY,3},  
  {AT_ELECTRICITY,3},  /* ball_lightning */  
  {AT_ELECTRICITY,3},  
  {AT_COLD,4},   
  {AT_COLD,4},  /* face=icestorm*/  
  {AT_COLD,4},  
  {AT_CONFUSION,5},  
  {AT_POISON,7},  
  {AT_POISON,7}, /* face = acid sphere.  generator */  
  {AT_POISON,7},  /* poisoncloud face */  
  {AT_SLOW,8},  
  {AT_PARALYZE,9},  
  {AT_FEAR,10}  };  
   
    case SP_MOVING_BALL:
        move_ball_spell(op);
        break;
   
    case SP_SWARM:
        move_swarm_spell(op);
        break;
   
 /*  shuffle_attack:  peterm */   case SP_AURA:
 /*  This routine shuffles the attack of op to one of the       move_aura(op);
    ones in the list.  It does this at random.  It also       break;
    chooses a face appropriate to the attack that is  
    being committed by that square at the moment.    
     right now it's being used by color spray and create pool of  
     chaos.  */  
   
 void shuffle_attack(object *op,int change_face)  
 {  
     int i;  
     i=rndm(0, 21);  
     op->attacktype|=ATTACKS[i].attacktype|AT_MAGIC;  
     if(change_face) {  
  SET_ANIMATION(op, ATTACKS[i].face);  
     }      }
 }  }
   
   /* this checks to see if something special should happen if
    * something runs into the object.
    */
   void check_spell_effect(object *op) {
   
 /*  the following function reads from the file 'spell_params' in       switch (op->subtype) {
 the lib dir, and resets the array in memory to reflect the values   case SP_BOLT:
 in spell_parameters.  The format in there MUST be:       move_bolt(op);
 spell name  
 spell_number bdam bdur ldam ldur  
  for  
 base damage of spell, base duration of spell, level-dependency for damage  
 level-dependency for duration--examples  
 magic bullet  
 0 0 0 0  
 large icestorm  
 0 1 1 1  
 small fireball  
 1 0 0 8  
 ....  
   
 The parameters have different effects for different spells.  
 Please refer to the documentation.  
 */  
 void init_spell_param()  
 {  
   FILE *spell_params;  
   char fname[MAX_BUF];  
   char spell_name[50];  
   char spell_attrib[50];  
   int bdam,bdur,ldam,ldur;  
   int sp;  
   int level;  
   int spellindex;  
   int spl;  /*  the spellpoint level dependency */  
   
   sprintf(fname,"%s/spell_params",settings.confdir);  
   if(! (spell_params=fopen(fname,"r")))  
     {  
  perror(fname);  
  return;   return;
     }  
    
   while(!feof(spell_params))   case SP_BULLET:
     {       check_bullet(op);
  fgets(spell_name,49,spell_params);       return;
  spellindex=look_up_spell_by_name(NULL,spell_name);  
  if(spellindex==-1) {  
      fprintf(stderr,"\nUnrecognized spell: %s",spell_name);  
      continue;  
  }  
  fgets(spell_attrib,49,spell_params);  
  sscanf(spell_attrib,"%d %d %d %d %d %d %d",&level,&sp,&bdam,&bdur,&ldam,&ldur,&spl);  
  spells[spellindex].sp=sp;  
  spells[spellindex].level=level;  
  SP_PARAMETERS[spellindex].bdam=bdam;  
  SP_PARAMETERS[spellindex].bdur=bdur;  
         SP_PARAMETERS[spellindex].ldam=ldam;  
         SP_PARAMETERS[spellindex].ldur=ldur;  
  SP_PARAMETERS[spellindex].spl=spl;  
     }  
     fclose(spell_params);  
 }  
   
 /* get_pointed_target() - this is used by finger of death  
  * and the 'smite' spells. Returns the pointer to the first  
  * monster in the direction which is pointed to by op. b.t.  
  */  
    
 object *get_pointed_target(object *op, int dir) {  
     object *target;  
     sint16 x,y;  
     int dist, mflags;  
     mapstruct *mp;  
   
     if (dir==0) return NULL;  
     /* limit of 20 is arbitrary - really, we should get this  
      * from spell parameter or something.  But there has to be  
      * some upper limit due to the nature of tiled maps.  
      * Note also that the check for no magic is perhaps bogus  
      * if this function is also used for cleric spells.  
      */  
     for (dist=1; dist<20; dist++) {  
  x = op->x + freearr_x[dir] * dist;  
  y = op->y + freearr_y[dir] * dist;  
  mp = op->map;  
  mflags = get_map_flags(op->map, &mp, x, y, &x, &y);  
   
  if (mflags & (P_OUT_OF_MAP | P_WALL | P_NO_MAGIC)) return NULL;  
  if (mflags & P_IS_ALIVE) {  
      for(target=get_map_ob(mp,x,y); target; target=target->above) {  
  if(QUERY_FLAG(target->head?target->head:target,FLAG_MONSTER)) {  
      return target;  
  }  
      }  
  }  
     }  
     return NULL;  
 }  }
   
 /* cast_smite_arch() - the priest points to a creature and causes  
  * a 'godly curse' to decend. I generalized this a bit so that several  
  * spells will be possible to use w/ this code (eg fire_arch, cast_cone).  
  * -b.t.  
  */  
   
 int cast_smite_spell (object *op, object *caster,int dir, int type) {  
    object *effect, *target = get_pointed_target(op,dir);  
    object *god = find_god(determine_god(op));  
   
     /* if we don't worship a god, or target a creature  
      * of our god, the spell will fail.    
      */  
    if(!target || QUERY_FLAG(target,FLAG_REFL_SPELL)  
       ||!god  
       ||(target->title&&!strcmp(target->title,god->name))  
       ||(target->race&&strstr(target->race,god->race))  
    ) {  
         new_draw_info(NDI_UNIQUE,0,op,"Your request is unheeded.");  
         return 0;  
    }     }
   
    if (spellarch[type] != (archetype *) NULL)  /* This is called by move_apply.  Basically, if someone
       effect = arch_to_object(spellarch[type]);   * moves onto a spell effect and the walk_on or fly_on flags
    else   * are set, this is called.  This should only be called for
       return 0;   * objects of the appropraite type.
    */
   void apply_spell_effect(object *spell, object *victim)
   {
       switch (spell->subtype) {
    case SP_CONE:
        if (QUERY_FLAG(victim, FLAG_ALIVE) && spell->speed && spell->attacktype)
    hit_player(victim, spell->stats.dam, spell, spell->attacktype);
        break;
   
   /* tailor the effect by priest level and worshipped God */   case SP_MAGIC_MISSILE:
    effect->level = casting_level (caster, type);       if (QUERY_FLAG (victim, FLAG_ALIVE)) {
    if(effect->attacktype&AT_HOLYWORD||effect->attacktype&AT_GODPOWER) {   tag_t spell_tag = spell->count;
         if(tailor_god_spell(effect,op))   hit_player (victim, spell->stats.dam, spell, spell->attacktype);
            new_draw_info_format(NDI_UNIQUE,0,op,   if ( ! was_destroyed (spell, spell_tag)) {
                 "%s answers your call!",determine_god(op));       remove_ob (spell);
         else {       free_object (spell);
            new_draw_info(NDI_UNIQUE,0,op,"Your request is ignored.");  
            return 0;  
  }   }
    }     }
        break;
    
    /* size of the area of destruction */   case SP_MOVING_BALL:
    effect->stats.hp=SP_PARAMETERS[type].bdur +       if (QUERY_FLAG (victim, FLAG_ALIVE))
                 SP_level_strength_adjust(op,caster,type);   hit_player (victim, spell->stats.dam, spell, spell->attacktype);
    /* how much woe to inflict :) */       else if (victim->material || victim->materialname)
    effect->stats.dam=SP_PARAMETERS[type].bdam +   save_throw_object (victim, spell->attacktype, spell);
                 SP_level_dam_adjust(op,caster,type);       break;
    if(effect->stats.dam<0) effect->stats.dam = 127;      }
    effect->stats.maxhp=effect->count; /*??*/  
    set_owner(effect,op);  
    
    /* ok, tell it where to be, and insert! */  
    effect->x=target->x;effect->y=target->y;  
    insert_ob_in_map(effect,op->map,op,0);  
    
    return 1;  
 }  }
   


Legend:
line(s) removed in v.1.79 
line(s) changed
 line(s) added in v.1.80

File made using version 1.98 of cvs2html by leaf at 2011-07-21 17:50