[CF-Devel] Summary of the last 2 day's diffs

Peter Mardahl peterm at alfven.EECS.Berkeley.EDU
Tue Nov 7 20:12:13 CST 2000


? config.log
? config.cache
? config.status
? Makefile
? common/Makefile
? crossedit/Makefile
? crossedit/crossedit
? crossedit/Cnv/Makefile
? crossedit/Cnv/test
? crossedit/bitmaps/Makefile
? crossedit/doc/Makefile
? crossedit/include/Makefile
? doc/Makefile
? doc/playbook/Makefile
? doc/playbook-html/Makefile
? doc/spoiler/Makefile
? doc/spoiler-html/Makefile
? include/Makefile
? include/autoconf.h
? lib/Makefile
? random_maps/Makefile
? random_maps/random_map
? server/Makefile
? server/crossfire
? socket/Makefile
? utils/Makefile
Index: CHANGES
===================================================================
RCS file: /home/cvs/CVS/crossfire/CHANGES,v
retrieving revision 1.131
retrieving revision 1.133
diff -c -r1.131 -r1.133
*** CHANGES	2000/11/04 06:40:50	1.131
--- CHANGES	2000/11/07 00:43:29	1.133
***************
*** 18,23 ****
--- 18,163 ----
  else.  With this, include the file(s) that you changed.
  ------------------------------------------------------------------------------
  
+ PeterM:  2000-11-06
+ server/spell_util.c	crash bug removed from move ball lightning:
+ ball lightning mover improved in general.  --PeterM
+ 
+ --------------------------------------------
+ --- BEGIN stop_item() / attack fix patch ---  Jan Echternach 2000-11-06
+ --------------------------------------------
+ 
+ Summary of this patch:  Provide new stop_item() function for properly
+ handling "moving" items on a map (flying arrows, thrown objects,
+ active cones); obsoletes the workarounds.  Fix some attack bugs (with
+ possible server crashes) - many attackers attacked while being
+ removed, even though much code could only handle the attacker being on
+ the same map as the victim (not even the attacker being in the
+ victim's inventory, e.g. poison).
+ 
+ common/object.c: insert_ob_in_map():  Call check_walk_on() after
+ update_object() because the latter needs to update some very
+ important flags used by a lot of code before this code is called
+ by check_walk_on().
+ 
+ common/object.c: get_split_ob():  Completed support for splitting
+ removed objects.
+ 
+ common/object.c: decrease_ob_nr(): Rewrote this function: Added
+ support for removed objects.  Removing an object completely didn't
+ always update the environment's weight properly.  Removing an amount
+ of 0 from an object with nrof == 0 destroyed the object.  Improved
+ performance a bit (don't need to update the player's weight if an
+ object below the player is modified).
+ 
+ server/apply.c: move_apply():  Added recursion limit.
+ 
+ server/apply.c: move_apply(): THROWN_OBJ and ARROW:  Use new
+ hit_with_arrow() function.
+ 
+ server/apply.c: move_apply(): FBULLET and BULLET:  Call
+ check_fired_arch() only when the victim blocks the square (FLAG_NO_PASS
+ or FLAG_ALIVE).
+ 
+ server/attack.c: save_throw_object():  Use new stop_item() function
+ before trying to modify an object.  Added originator to all
+ insert_ob_in_map() calls.
+ 
+ server/attack.c: hit_map():  Added log message if the hitter is not on
+ a map.  Added was_destroyed() check after calling save_throw_object().
+ 
+ server/attack.c: get_attack_mode() and abort_attack():  New functions
+ to determine the type of attack (simple or full attack).
+ 
+ server/attack.c: attack_ob():  Now only attack_ob_simple() with default
+ damage and weapon class values.
+ 
+ server/attack.c: attack_ob_simple():  Contains most of old attack_ob(),
+ but takes damage and weapon class as parameters, needed for
+ hit_with_arrow().  Uses get_attack_mode() and abort_attack() to
+ support new "simple" attack mode.
+ 
+ server/attack.c: stick_arrow() and hit_with_arrow():  Moved those parts
+ of old stop_arrow() and move_arrow() that dealt with attacking a
+ victim over here.
+ 
+ server/attack.c: hit_player():  Uses get_attack_mode() and
+ abort_attack() to support new "simple" attack mode.
+ 
+ server/attack.c: paralyze_player():  Use insert_ob_in_map_simple()
+ instead of insert_ob_in_map() because the callers are not prepared for
+ this function triggering any complex machinery, and
+ insert_ob_in_map_simple() should be enough for plain visual effects.
+ 
+ server/attack.c: thrown_item_effect():  Don't need to deal with thrown
+ objects, hit_with_arrow() already does everything necessary.
+ 
+ server/attack.c: adj_attackroll():  Added log message if hitter and
+ victim are not on the same map.
+ 
+ server/c_object.c:  Fixed two typos in "can't pick up xxx" messages.
+ 
+ server/c_object.c: pick_up_object():  Removed can_pick() check because
+ pick_up() already checks this.  Added support for picking up removed
+ objects because the stop_item() in pick_up() can now call us with
+ removed objects.  Moved FLAG_STARTEQUIP check to pick_up().  Fixed
+ weight limit lookup for monsters that have strength > MAX_STAT.
+ 
+ server/c_object.c: pick_up():  Use new stop_item() function.
+ 
+ server/player.c: fire_bow() and server/skills.c: do_throw():  Removed
+ FLAG_NO_PICK workaround for flying objects.  stop_item() now handles
+ this correctly.
+ 
+ server/rune.c: spring_trap():  Fix unlimited trap recursion.  Since
+ traps are triggered by move_apply(), the result of triggering a trap
+ may trigger the trap again before it is deactivated.
+ 
+ server/rune.c: spring_trap():  Runes that don't cast spells can only be
+ triggered by living objects.
+ 
+ server/spell_effect.c: move_cancellation():  Don't call hit_map() with
+ a removed attacker.
+ 
+ server/spell_util.c: cast_cone():  Only print warning message about
+ cones that don't have FLAG_FLY/WALK_ON if the cone does any damage.
+ 
+ server/spell_util.c: move_cone():  Removed workaround for cones in ice
+ cubes.  stop_item() now handles this correctly.
+ 
+ server/spell_util.c: explode_object(): Rewrote this function: New
+ interface - exploding object must not be removed when the function is
+ called, it must have an other_arch (i.e. it must be able to explode),
+ and it will be gone for sure when the function terminates.  Removed
+ some code with no effect.  Don't call any attack functions with
+ removed attackers.
+ 
+ server/spell_util.c: check_fired_arch():  Rewrote this function.
+ 
+ server/spell_util.c: move_fired_arch():  Rewrote most of this function:
+ Don't attack with removed attackers.  Call check_fired_arch() instead
+ of duplicating its code.
+ 
+ server/spell_util.c: move_ball_lightning():  Don't attack with removed
+ attackers.  Rewrote the movement algorithm to simplify this task.
+ 
+ server/time.c: stop_item() and fix_stopped_item:  New functions.
+ 
+ server/time.c: fix_stopped_arrow():  Contains most of the old
+ stop_arrow() function.  Unlike stop_arrow(), it takes an arrow that is
+ not removed.  Removed FLACK_NO_PICK workaround.
+ 
+ server/time.c: stop_arrow():  Stops arrows and thrown objects and puts
+ them on the map.  Sticking objects into targets is no longer handled
+ by stop_arrow(), but by hit_with_arrow().
+ 
+ server/time.c: move_arrow():  Don't attack with removed attackers.
+ 
+ socket/item.c: esrv_move_object():  Removed misleading comment.
+ 
+ ------------------------------------------
+ --- END stop_item() / attack fix patch ---  Jan Echternach 2000-11-06
+ ------------------------------------------
+ 
  server/time.c:  Add call to esrv_update_item to update the face for
  the client after the player chooses a class.  MSW 11/3/2000
  
Index: common/object.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/common/object.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -c -r1.8 -r1.9
*** common/object.c	2000/06/27 03:34:34	1.8
--- common/object.c	2000/11/06 23:06:47	1.9
***************
*** 1,6 ****
  /*
   * static char *rcsid_object_c =
!  *   "$Id: object.c,v 1.8 2000/06/27 03:34:34 cvs Exp $";
   */
  
  /*
--- 1,6 ----
  /*
   * static char *rcsid_object_c =
!  *   "$Id: object.c,v 1.9 2000/11/06 23:06:47 jec Exp $";
   */
  
  /*
***************
*** 1476,1495 ****
    else
      set_map_ob(op->map,op->x,op->y,op);   /* Tell the map that we're here */
  
-   /* Only check this if we are the head of the object */
-   if (!op->head) {
-       mapstruct *map=op->map;
-       if (check_walk_on(op, originator))
-         return NULL;
- 
-       /* If we are a multi part object, lets work our way through the check
-        * walk on's.
-        */
-       for (tmp=op->more; tmp!=NULL; tmp=tmp->more)
-           if (check_walk_on (op, originator))
-             return NULL;
- 
-   }
    if(op->type==PLAYER)
      op->contr->do_los=1;
    for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
--- 1476,1481 ----
***************
*** 1512,1520 ****
--- 1498,1525 ----
  #endif 
      /* Don't know if moving this to the end will break anything.  However,
       * we want to have update_look set above before calling this.
+      *
+      * check_walk_on() must be after this because code called from
+      * check_walk_on() depends on correct map flags (so functions like
+      * blocked() and wall() work properly), and these flags are updated by
+      * update_object().
       */
      update_object(op);
  
+     /* Only check this if we are the head of the object */
+     if ( ! op->head) {
+         mapstruct *map=op->map;
+         if (check_walk_on(op, originator))
+           return NULL;
+ 
+         /* If we are a multi part object, lets work our way through the check
+          * walk on's.
+          */
+         for (tmp=op->more; tmp!=NULL; tmp=tmp->more)
+             if (check_walk_on (op, originator))
+               return NULL;
+     }
+ 
      return op;
  }
  
***************
*** 1528,1533 ****
--- 1533,1539 ----
  
  object *get_split_ob(object *orig_ob,int nr) {
      object *newob;
+     int is_removed = (QUERY_FLAG (orig_ob, FLAG_REMOVED) != 0);
  
      if(orig_ob->nrof<nr) {
  	sprintf(errmsg,"There are only %d %ss.",
***************
*** 1537,1546 ****
      newob=get_object();
      copy_object(orig_ob,newob);
      if((orig_ob->nrof-=nr)<1) {
! 	remove_ob(orig_ob);
  	free_object(orig_ob);
      }
!     else if(!QUERY_FLAG(orig_ob,FLAG_REMOVED)){
  	if(orig_ob->env!=NULL)
  	    sub_weight (orig_ob->env,orig_ob->weight*nr);
  	if (orig_ob->env == NULL && orig_ob->map->in_memory!=MAP_IN_MEMORY) {
--- 1543,1553 ----
      newob=get_object();
      copy_object(orig_ob,newob);
      if((orig_ob->nrof-=nr)<1) {
! 	if ( ! is_removed)
!             remove_ob(orig_ob);
  	free_object(orig_ob);
      }
!     else if ( ! is_removed) {
  	if(orig_ob->env!=NULL)
  	    sub_weight (orig_ob->env,orig_ob->weight*nr);
  	if (orig_ob->env == NULL && orig_ob->map->in_memory!=MAP_IN_MEMORY) {
***************
*** 1562,1625 ****
   * Return value: 'op' if something is left, NULL if the amount reached 0
   */
  
! object *decrease_ob_nr(object *op,int i)
  {
!   object *tmp;
!   
!   /* nrof is split out into a long so it is handled ok on 64 bit machines.
!    * I think it might work to just do op->nrof-=i and cast that to an sint32
!    * to see if it is negative.  But if so, we might as well just nrof in
!    * the object structure an sint32, since that would still set a maximum
!    * value.
!    */
!   long nrof=op->nrof;
  
!   nrof -= i;
!   if (nrof<0) nrof=0;
!   op->nrof = nrof;
! 
!   if(!op->nrof) {
!     op->nrof=0;	/* just to be sure that remove_ob handles weight properly */
!     /* We don't have remove object send delete to the new client, because
!      * in many areas, more intelligent strategies are employed.  However, if
!      * we are decreasing the number, we can be sure that the object is gone
!      * for good, so send a delete down the line.  Need to check for
!      * environment before we call remove_ob, since that deletes the
!      * environment.
!      */
!     if(op->env!=NULL) {
!       tmp=is_player_inv (op->env);
!       if(tmp!=NULL) {
! 	(*esrv_del_item_func)(tmp->contr, op->count);
! 	(*esrv_update_item_func)(UPD_WEIGHT, tmp, tmp);
!       }
!     } else {
!       for(tmp=op->above;tmp!=NULL;tmp=tmp->above)
!         if(tmp->type==PLAYER) {
! 	    (*esrv_del_item_func)(tmp->contr, op->count);
! 	    (*esrv_update_item_func)(UPD_WEIGHT, tmp, tmp);
! 	}
      }
!     remove_ob(op);
!     free_object(op);
!     return NULL;
!   }
!   else
!     if(op->env!=NULL) {
!       sub_weight(op->env,op->weight*i);
!       tmp=is_player_inv (op->env);
!       if(tmp!=NULL) {
! 	    (*esrv_send_item_func)(tmp, op);
! 	    (*esrv_update_item_func)(UPD_WEIGHT, tmp, tmp);
!       }
      } else {
!       for(tmp=op->above;tmp!=NULL;tmp=tmp->above)
!         if(tmp->type==PLAYER) {
! 	    (*esrv_send_item_func)(tmp, op);
! 	    (*esrv_update_item_func)(UPD_WEIGHT, tmp, tmp);
! 	}
      }
-     return op;
  }
  
  /*
--- 1569,1630 ----
   * Return value: 'op' if something is left, NULL if the amount reached 0
   */
  
! object *decrease_ob_nr (object *op, int i)
  {
!     object *tmp;
  
!     if (i == 0)   /* objects with op->nrof require this check */
!         return op;
! 
!     if (i > op->nrof)
!         i = op->nrof;
! 
!     if (QUERY_FLAG (op, FLAG_REMOVED))
!     {
!         op->nrof -= i;
      }
!     else if (op->env != NULL)
!     {
!         tmp = is_player_inv (op->env);
!         if (i < op->nrof) {
!             sub_weight (op->env, op->weight * i);
!             op->nrof -= i;
!             if (tmp) {
!                 (*esrv_send_item_func) (tmp, op);
!                 (*esrv_update_item_func) (UPD_WEIGHT, tmp, tmp);
!             }
!         } else {
!             remove_ob (op);
!             op->nrof = 0;
!             if (tmp) {
!                 (*esrv_del_item_func) (tmp->contr, op->count);
!                 (*esrv_update_item_func) (UPD_WEIGHT, tmp, tmp);
!             }
!         }
!     }
!     else
!     {
!         if (i < op->nrof) {
!             op->nrof -= i;
!         } else {
!             remove_ob (op);
!             op->nrof = 0;
!         }
!         for (tmp = op->above; tmp != NULL; tmp = tmp->above)
!             if (tmp->type == PLAYER) {
!                 if (op->nrof)
!                     (*esrv_send_item_func) (tmp, op);
!                 else
!                     (*esrv_del_item_func) (tmp->contr, op->count);
!             }
!     }
! 
!     if (op->nrof) {
!         return op;
      } else {
!         free_object (op);
!         return NULL;
      }
  }
  
  /*
Index: include/sproto.h
===================================================================
RCS file: /home/cvs/CVS/crossfire/include/sproto.h,v
retrieving revision 1.16
retrieving revision 1.17
diff -c -r1.16 -r1.17
*** include/sproto.h	2000/10/30 22:09:59	1.16
--- include/sproto.h	2000/11/06 23:06:47	1.17
***************
*** 45,53 ****
  extern void apply_lighter ( object *who, object *lighter );
  extern void scroll_failure ( object *op, int failure, int power );
  extern int did_make_save_item ( object *op, int type );
! extern void save_throw_object ( object *op, int type );
  extern int hit_map ( object *op, int dir, int type );
  extern int attack_ob ( object *op, object *hitter );
  extern void tear_down_wall ( object *op );
  extern int hit_player_attacktype ( object *op, object *hitter, int dam, uint32 attacktype, int magic );
  extern int hit_player ( object *op, int dam, object *hitter, int type );
--- 45,54 ----
  extern void apply_lighter ( object *who, object *lighter );
  extern void scroll_failure ( object *op, int failure, int power );
  extern int did_make_save_item ( object *op, int type );
! extern void save_throw_object ( object *op, int type, object *originator );
  extern int hit_map ( object *op, int dir, int type );
  extern int attack_ob ( object *op, object *hitter );
+ extern object *hit_with_arrow ( object *op, object *victim );
  extern void tear_down_wall ( object *op );
  extern int hit_player_attacktype ( object *op, object *hitter, int dam, uint32 attacktype, int magic );
  extern int hit_player ( object *op, int dam, object *hitter, int type );
***************
*** 57,63 ****
  extern void blind_player ( object *op, object *hitter, int dam );
  extern void paralyze_player ( object *op, object *hitter, int dam );
  extern void deathstrike_player ( object *op, object *hitter, int *dam );
- extern object *thrown_item_effect ( object *hitter, object *victim );
  extern int adj_attackroll ( object *hitter, object *target );
  extern int is_aimed_missile ( object *op );
  extern int checkbanned ( char *login, char *host );
--- 58,63 ----
***************
*** 124,130 ****
  extern int command_rskill ( object *pl, char *params );
  extern int command_apply ( object *op, char *params );
  extern int sack_can_hold ( object *pl, object *sack, object *op, int nrof );
- extern void pick_up_object ( object *pl, object *op, object *tmp, int nrof );
  extern void pick_up ( object *op, object *alt );
  extern int command_take ( object *op, char *params );
  extern void put_object_in_sack ( object *op, object *sack, object *tmp, long nrof );
--- 124,129 ----
***************
*** 576,582 ****
  extern void move_golem ( object *op );
  extern void control_golem ( object *op, int dir );
  extern void move_missile ( object *op );
! extern int explode_object ( object *op );
  extern void check_fired_arch ( object *op );
  extern void move_fired_arch ( object *op );
  extern void drain_rod_charge ( object *rod );
--- 575,581 ----
  extern void move_golem ( object *op );
  extern void control_golem ( object *op, int dir );
  extern void move_missile ( object *op );
! extern void explode_object ( object *op );
  extern void check_fired_arch ( object *op );
  extern void move_fired_arch ( object *op );
  extern void drain_rod_charge ( object *rod );
***************
*** 618,624 ****
  extern void move_detector ( object *op );
  extern void animate_trigger ( object *op );
  extern void move_hole ( object *op );
! extern void stop_arrow ( object *op, object *tmp );
  extern void move_arrow ( object *op );
  extern void change_object ( object *op );
  extern void move_teleporter ( object *op );
--- 617,624 ----
  extern void move_detector ( object *op );
  extern void animate_trigger ( object *op );
  extern void move_hole ( object *op );
! extern object *stop_item ( object *op );
! extern object *fix_stopped_arrow ( object *op );
  extern void move_arrow ( object *op );
  extern void change_object ( object *op );
  extern void move_teleporter ( object *op );
Index: server/apply.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/apply.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -c -r1.21 -r1.22
*** server/apply.c	2000/10/30 22:09:59	1.21
--- server/apply.c	2000/11/06 23:06:47	1.22
***************
*** 1,6 ****
  /*
   * static char *rcsid_apply_c =
!  *   "$Id: apply.c,v 1.21 2000/10/30 22:09:59 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
--- 1,6 ----
  /*
   * static char *rcsid_apply_c =
!  *   "$Id: apply.c,v 1.22 2000/11/06 23:06:47 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
***************
*** 1085,1090 ****
--- 1085,1102 ----
   */
  void move_apply (object *trap, object *victim, object *originator)
  {
+   static int recursion_depth = 0;
+ 
+   /* move_apply() is the most likely candidate for causing unwanted and
+    * possibly unlimited recursion. */
+   if (recursion_depth >= 5) {
+     LOG (llevError, "WARNING: move_apply(): aborting recursion "
+          "[trap arch %s, name %s; victim arch %s, name %s]\n",
+          trap->arch->name, trap->name, victim->arch->name, victim->name);
+     return;
+   }
+   recursion_depth++;
+ 
    switch (trap->type)
    {
    case PLAYERMOVER:
***************
*** 1101,1131 ****
  	if (victim->speed_left<-50.0) victim->speed_left=-50.0;
  /*	fprintf(stderr,"apply, playermove, player speed_left=%f\n", victim->speed_left);*/
      }
!     return;
  
    case SPINNER:
      if(victim->direction) {
        victim->direction=absdir(victim->direction-trap->stats.sp);
        update_turn_face(victim);
      }
!     return;
  
    case DIRECTOR:
      if(victim->direction) {
        victim->direction=trap->stats.sp;
        update_turn_face(victim);
      }
!     return;
  
    case BUTTON:
    case PEDESTAL:
      update_button(trap);
!     return;
  
    case ALTAR:
      /* sacrifice victim on trap */
      apply_altar (trap, victim, originator);
!     return;
  
    case MMISSILE:
      if (QUERY_FLAG (victim, FLAG_ALIVE)) {
--- 1113,1143 ----
  	if (victim->speed_left<-50.0) victim->speed_left=-50.0;
  /*	fprintf(stderr,"apply, playermove, player speed_left=%f\n", victim->speed_left);*/
      }
!     goto leave;
  
    case SPINNER:
      if(victim->direction) {
        victim->direction=absdir(victim->direction-trap->stats.sp);
        update_turn_face(victim);
      }
!     goto leave;
  
    case DIRECTOR:
      if(victim->direction) {
        victim->direction=trap->stats.sp;
        update_turn_face(victim);
      }
!     goto leave;
  
    case BUTTON:
    case PEDESTAL:
      update_button(trap);
!     goto leave;
  
    case ALTAR:
      /* sacrifice victim on trap */
      apply_altar (trap, victim, originator);
!     goto leave;
  
    case MMISSILE:
      if (QUERY_FLAG (victim, FLAG_ALIVE)) {
***************
*** 1136,1166 ****
            free_object (trap);
        }
      }
!     return;
  
    case THROWN_OBJ:
    case ARROW:
!     if(QUERY_FLAG(victim, FLAG_ALIVE)&&trap->speed) {
!       tag_t trap_tag = trap->count;
!       if(attack_ob(victim,trap)) {
! 	/* There can be cases where a monster 'kills' an arrow.  Typically
! 	 * happens for things like black puddings that have hitback properties.
! 	 */
! 	if ( ! was_destroyed (trap, trap_tag)) {
! 	    remove_ob(trap);
! 	    stop_arrow(trap,victim);
! 	}
!       }
!     }
!     return;
  
    case CANCELLATION:
    case BALL_LIGHTNING:
      if (QUERY_FLAG (victim, FLAG_ALIVE))
        hit_player (victim, trap->stats.dam, trap, trap->attacktype);
      else if (victim->material)
!       save_throw_object (victim, trap->attacktype);
!     return;
  
    case CONE:
      if(QUERY_FLAG(victim, FLAG_ALIVE)&&trap->speed) {
--- 1148,1171 ----
            free_object (trap);
        }
      }
!     goto leave;
  
    case THROWN_OBJ:
+     if (trap->inv == NULL)
+       goto leave;
+     /* fallthrough */
    case ARROW:
!     if (QUERY_FLAG (victim, FLAG_ALIVE) && trap->speed)
!       hit_with_arrow (trap, victim);
!     goto leave;
  
    case CANCELLATION:
    case BALL_LIGHTNING:
      if (QUERY_FLAG (victim, FLAG_ALIVE))
        hit_player (victim, trap->stats.dam, trap, trap->attacktype);
      else if (victim->material)
!       save_throw_object (victim, trap->attacktype, trap);
!     goto leave;
  
    case CONE:
      if(QUERY_FLAG(victim, FLAG_ALIVE)&&trap->speed) {
***************
*** 1168,1179 ****
        if (attacktype)
          hit_player(victim,trap->stats.dam,trap,attacktype);
      }
!     return;
  
    case FBULLET:
    case BULLET:
!     check_fired_arch(trap);
!     return;
  
    case TRAPDOOR:
      {
--- 1173,1186 ----
        if (attacktype)
          hit_player(victim,trap->stats.dam,trap,attacktype);
      }
!     goto leave;
  
    case FBULLET:
    case BULLET:
!     if (QUERY_FLAG (victim, FLAG_NO_PASS)
!         || QUERY_FLAG (victim, FLAG_ALIVE))
!       check_fired_arch (trap);
!     goto leave;
  
    case TRAPDOOR:
      {
***************
*** 1185,1191 ****
            if(!QUERY_FLAG(ab,FLAG_FLYING))
              tot += (ab->nrof ? ab->nrof : 1) * ab->weight + ab->carrying;
          if(!(trap->value=(tot>trap->weight)?1:0))
!           return;
  	SET_ANIMATION(trap, trap->value);
          update_object(trap);
        }
--- 1192,1198 ----
            if(!QUERY_FLAG(ab,FLAG_FLYING))
              tot += (ab->nrof ? ab->nrof : 1) * ab->weight + ab->carrying;
          if(!(trap->value=(tot>trap->weight)?1:0))
!           goto leave;
  	SET_ANIMATION(trap, trap->value);
          update_object(trap);
        }
***************
*** 1199,1238 ****
          new_draw_info(NDI_UNIQUE, 0,ab,"You fall into a trapdoor!");
          transfer_ob(ab,(int)EXIT_X(trap),(int)EXIT_Y(trap),0,ab);
        }
!       return;
      }
  
    case CONVERTER:
      convert_item (victim, trap);
!     return;
  
    case TRIGGER_BUTTON:
    case TRIGGER_PEDESTAL:
    case TRIGGER_ALTAR:
      check_trigger (trap, victim);
!     return;
  
    case DEEP_SWAMP:
      walk_on_deep_swamp (trap, victim);
!     return;
  
    case CHECK_INV:
      check_inv (victim, trap);
!     return;
  
    case HOLE:
      /* Hole not open? */
      if(trap->stats.wc > 0)
!       return;
      /* Is this a multipart monster and not the head?  If so, return.
       * Processing will happen if the head runs into the pit
       */
      if (victim->head)
!       return;
      play_sound_map (victim->map, victim->x, victim->y, SOUND_FALL_HOLE);
      new_draw_info (NDI_UNIQUE, 0, victim, "You fall through the hole!\n");
      transfer_ob (victim, EXIT_X (trap), EXIT_Y (trap), 1, victim);
!     return;
  
    case EXIT:
      if (victim->type == PLAYER && EXIT_PATH (trap)) {
--- 1206,1245 ----
          new_draw_info(NDI_UNIQUE, 0,ab,"You fall into a trapdoor!");
          transfer_ob(ab,(int)EXIT_X(trap),(int)EXIT_Y(trap),0,ab);
        }
!       goto leave;
      }
  
    case CONVERTER:
      convert_item (victim, trap);
!     goto leave;
  
    case TRIGGER_BUTTON:
    case TRIGGER_PEDESTAL:
    case TRIGGER_ALTAR:
      check_trigger (trap, victim);
!     goto leave;
  
    case DEEP_SWAMP:
      walk_on_deep_swamp (trap, victim);
!     goto leave;
  
    case CHECK_INV:
      check_inv (victim, trap);
!     goto leave;
  
    case HOLE:
      /* Hole not open? */
      if(trap->stats.wc > 0)
!       goto leave;
      /* Is this a multipart monster and not the head?  If so, return.
       * Processing will happen if the head runs into the pit
       */
      if (victim->head)
!       goto leave;
      play_sound_map (victim->map, victim->x, victim->y, SOUND_FALL_HOLE);
      new_draw_info (NDI_UNIQUE, 0, victim, "You fall through the hole!\n");
      transfer_ob (victim, EXIT_X (trap), EXIT_Y (trap), 1, victim);
!     goto leave;
  
    case EXIT:
      if (victim->type == PLAYER && EXIT_PATH (trap)) {
***************
*** 1240,1285 ****
  	  new_draw_info (NDI_NAVY, 0, victim, trap->msg);
        enter_exit (victim, trap);
      }
!     return;
  
    case ENCOUNTER:
  #ifdef RANDOM_ENCOUNTERS
      if (victim->type == PLAYER && QUERY_FLAG (trap, FLAG_IS_FLOOR))
        random_encounter (victim, trap);
  #endif
!     return;
  
    case SHOP_MAT:
      apply_shop_mat (trap, victim);
!     return;
  
    /* Drop a certain amount of gold, and have one item identified */
    case IDENTIFY_ALTAR:
      apply_id_altar (victim, trap, originator);
!     return;
  
    case SIGN:
      apply_sign (victim, trap);
!     return;
  
    case CONTAINER:
      if (victim->type==PLAYER)
        (void) esrv_apply_container (victim, trap);
      else
        (void) apply_container (victim, trap);
!     return;
  
    case RUNE:
      if (trap->level && QUERY_FLAG (victim, FLAG_ALIVE))
        spring_trap (trap, victim);
!     return;
  
    default:
      LOG (llevDebug, "name %s, arch %s, type %d with fly/walk on/off not "
           "handled in move_apply()\n", trap->name, trap->arch->name,
           trap->type);
!     return;
    }
  }
  
  
--- 1247,1295 ----
  	  new_draw_info (NDI_NAVY, 0, victim, trap->msg);
        enter_exit (victim, trap);
      }
!     goto leave;
  
    case ENCOUNTER:
  #ifdef RANDOM_ENCOUNTERS
      if (victim->type == PLAYER && QUERY_FLAG (trap, FLAG_IS_FLOOR))
        random_encounter (victim, trap);
  #endif
!     goto leave;
  
    case SHOP_MAT:
      apply_shop_mat (trap, victim);
!     goto leave;
  
    /* Drop a certain amount of gold, and have one item identified */
    case IDENTIFY_ALTAR:
      apply_id_altar (victim, trap, originator);
!     goto leave;
  
    case SIGN:
      apply_sign (victim, trap);
!     goto leave;
  
    case CONTAINER:
      if (victim->type==PLAYER)
        (void) esrv_apply_container (victim, trap);
      else
        (void) apply_container (victim, trap);
!     goto leave;
  
    case RUNE:
      if (trap->level && QUERY_FLAG (victim, FLAG_ALIVE))
        spring_trap (trap, victim);
!     goto leave;
  
    default:
      LOG (llevDebug, "name %s, arch %s, type %d with fly/walk on/off not "
           "handled in move_apply()\n", trap->name, trap->arch->name,
           trap->type);
!     goto leave;
    }
+ 
+  leave:
+   recursion_depth--;
  }
  
  
***************
*** 2509,2515 ****
  	 */
  	strcpy(item_name, item->name);
  
! 	save_throw_object(item,AT_FIRE);
  	/* Change to check count and not freed, since the object pointer
  	 * may have gotten recycled
  	 */
--- 2519,2525 ----
  	 */
  	strcpy(item_name, item->name);
  
! 	save_throw_object(item,AT_FIRE,who);
  	/* Change to check count and not freed, since the object pointer
  	 * may have gotten recycled
  	 */
Index: server/attack.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/attack.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -c -r1.17 -r1.18
*** server/attack.c	2000/10/31 12:04:58	1.17
--- server/attack.c	2000/11/06 23:06:47	1.18
***************
*** 1,6 ****
  /*
   * static char *rcsid_attack_c =
!  *   "$Id: attack.c,v 1.17 2000/10/31 12:04:58 peterm Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
--- 1,6 ----
  /*
   * static char *rcsid_attack_c =
!  *   "$Id: attack.c,v 1.18 2000/11/06 23:06:47 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
***************
*** 86,98 ****
   * calling cancellation, etc.)
   */
  
! void save_throw_object(object *op, int type) {
! 
!     if (!did_make_save_item(op, type)) {
  	object *env=op->env;
  	int x=op->x,y=op->y;
  	mapstruct *m=op->map;
  
  	/* Hacked the following so that type LIGHTER will work. 
  	 * Also, objects which are potenital "lights" that are hit by 
  	 * flame/elect attacks will be set to glow. "lights" are any 
--- 86,103 ----
   * calling cancellation, etc.)
   */
  
! void save_throw_object (object *op, int type, object *originator)
! {
!     if ( ! did_make_save_item (op, type))
!     {
  	object *env=op->env;
  	int x=op->x,y=op->y;
  	mapstruct *m=op->map;
  
+         op = stop_item (op);
+         if (op == NULL)
+             return;
+ 
  	/* Hacked the following so that type LIGHTER will work. 
  	 * Also, objects which are potenital "lights" that are hit by 
  	 * flame/elect attacks will be set to glow. "lights" are any 
***************
*** 105,111 ****
             &&op->other_arch&&op->glow_radius) { 
  		char *arch=op->other_arch->name;
  
! 		decrease_ob_nr(op,1);
  		if((op = get_archetype(arch))!=NULL) {
                     if(env) {  
  			op->x=env->x,op->y=env->y;
--- 110,118 ----
             &&op->other_arch&&op->glow_radius) { 
  		char *arch=op->other_arch->name;
  
! 		op = decrease_ob_nr (op, 1);
!                 if (op)
!                     fix_stopped_item (op, m, originator);
  		if((op = get_archetype(arch))!=NULL) {
                     if(env) {  
  			op->x=env->x,op->y=env->y;
***************
*** 114,131 ****
  			    esrv_send_item(env, op);
                     } else { 
                        op->x=x,op->y=y;
!                       insert_ob_in_map(op,m,NULL);
  		   }
  		}
  		return;
          }
          if(type&AT_CANCELLATION) {          /* Cancellation. */
                cancellation(op);
                return;  
          }
! 	if(op->nrof>1)
! 	      decrease_ob_nr(op,RANDOM()%op->nrof);
! 	else {
  	    if (op->env) {
  		object *tmp= is_player_inv(op->env);
  
--- 121,141 ----
  			    esrv_send_item(env, op);
                     } else { 
                        op->x=x,op->y=y;
!                       insert_ob_in_map(op,m,originator);
  		   }
  		}
  		return;
          }
          if(type&AT_CANCELLATION) {          /* Cancellation. */
                cancellation(op);
+               fix_stopped_item (op, m, originator);
                return;  
          }
! 	if(op->nrof>1) {
! 	      op = decrease_ob_nr(op,RANDOM()%op->nrof);
!               if (op)
!                   fix_stopped_item (op, m, originator);
! 	} else {
  	    if (op->env) {
  		object *tmp= is_player_inv(op->env);
  
***************
*** 134,140 ****
  		    esrv_update_item(UPD_WEIGHT, tmp, tmp);
  		}
  	    }
! 	    remove_ob(op);
  	    free_object(op);
  	}
  	if(type&(AT_FIRE|AT_ELECTRICITY)) {
--- 144,151 ----
  		    esrv_update_item(UPD_WEIGHT, tmp, tmp);
  		}
  	    }
! 	    if ( ! QUERY_FLAG (op, FLAG_REMOVED))
!                 remove_ob(op);
  	    free_object(op);
  	}
  	if(type&(AT_FIRE|AT_ELECTRICITY)) {
***************
*** 144,150 ****
                  insert_ob_in_ob(op,env);
  	      } else { 
  		op->x=x,op->y=y;
! 	      	insert_ob_in_map(op,m,NULL);
  	      }
  	}
  	return;
--- 155,161 ----
                  insert_ob_in_ob(op,env);
  	      } else { 
  		op->x=x,op->y=y;
! 	      	insert_ob_in_map(op,m,originator);
  	      }
  	}
  	return;
***************
*** 155,166 ****
          archetype *at = find_archetype("icecube");
          if (at == NULL)
            return;
          if ((tmp = present_arch(at,op->map,op->x,op->y)) == NULL) {
            tmp = arch_to_object(at);
            tmp->x=op->x,tmp->y=op->y;
!           insert_ob_in_map(tmp,op->map,NULL);
          }
!         remove_ob(op);
          (void) insert_ob_in_ob(op,tmp);
          return;
      }
--- 166,181 ----
          archetype *at = find_archetype("icecube");
          if (at == NULL)
            return;
+         op = stop_item (op);
+         if (op == NULL)
+             return;
          if ((tmp = present_arch(at,op->map,op->x,op->y)) == NULL) {
            tmp = arch_to_object(at);
            tmp->x=op->x,tmp->y=op->y;
!           insert_ob_in_map(tmp,op->map,originator);
          }
!         if ( ! QUERY_FLAG (op, FLAG_REMOVED))
!             remove_ob(op);
          (void) insert_ob_in_ob(op,tmp);
          return;
      }
***************
*** 177,183 ****
      LOG (llevError, "BUG: hit_map(): free object\n");
      return 0;
    }
!   
    if (op->head) op=op->head;
  
    op_tag = op->count;
--- 192,204 ----
      LOG (llevError, "BUG: hit_map(): free object\n");
      return 0;
    }
! 
!   if (QUERY_FLAG (op, FLAG_REMOVED) || op->env != NULL) {
!     LOG (llevError, "BUG: hit_map(): hitter (arch %s, name %s) not on a map\n",
!          op->arch->name, op->name);
!     return 0;
!   }
! 
    if (op->head) op=op->head;
  
    op_tag = op->count;
***************
*** 232,238 ****
        next_tag = next->count;
  
      if (QUERY_FLAG (tmp, FLAG_FREED)) {
! 	LOG (llevError, "BUG: hit_map(): found free object\n");
  	break;
      }
  
--- 253,259 ----
        next_tag = next->count;
  
      if (QUERY_FLAG (tmp, FLAG_FREED)) {
! 	LOG (llevError, "BUG: hit_map(): found freed object\n");
  	break;
      }
  
***************
*** 248,254 ****
        if (was_destroyed (op, op_tag))
          break;
      } else if (tmp->material) {
!       save_throw_object(tmp,type);
      }
    }
  #ifdef NO_CONE_PROPOGATE
--- 269,277 ----
        if (was_destroyed (op, op_tag))
          break;
      } else if (tmp->material) {
!       save_throw_object(tmp,type,op);
!       if (was_destroyed (op, op_tag))
!         break;
      }
    }
  #ifdef NO_CONE_PROPOGATE
***************
*** 308,348 ****
    return &messages;
  }
  
! /*
!  * attack_ob() returns 1 on a hit, and 0 on a miss.
!  * op is what is being attacked, hitter is what is hitting (Arrow, player,
!  * whatever)
   */
  
! int attack_ob(object *op,object *hitter) {
!     int roll,dam=0;
      char buf[MAX_BUF];
      uint32 type;
      att_msg *msg;
!     char *op_name;
      signed char luck=0;
  
!     if(op->head!=NULL)
! 	op=op->head;
!     if(op->name==NULL) {
! 	if(settings.debug >= llevDebug) {
! 	    dump_object(op);
! 	    LOG(llevDebug,"Object without name tried to attack.\n%s\n",errmsg);
! 	    /* we don't NEED to print this a zillion times, so GIVE IT A NAME. */
! 	    op->name = add_string(op->arch->name);
! 	    
! 	}
! 	if (QUERY_FLAG(op, FLAG_REMOVED) && !QUERY_FLAG(op, FLAG_FREED))
! 	    free_object(op);
! 	return 1;
!     }
  
      /*
       * A little check to make it more difficult to dance forward and back
       * to avoid ever being hit by monsters.
       */
!     if (QUERY_FLAG(op, FLAG_MONSTER) && op->speed_left > -(FABS(op->speed))*0.3) {
! 
  	/* Decrease speed BEFORE calling process_object.  Otherwise, an
  	 * infinite loop occurs, with process_object calling move_monster,
  	 * which then gets here again.  By decreasing the speed before
--- 331,408 ----
    return &messages;
  }
  
! 
! static int get_attack_mode (object **target, object **hitter,
! 	int *simple_attack)
! {
!     if (QUERY_FLAG (*target, FLAG_FREED) || QUERY_FLAG (*hitter, FLAG_FREED)) {
!         LOG (llevError, "BUG: get_attack_mode(): freed object\n");
!         return 1;
!     }
!     if ((*target)->head)
!         *target = (*target)->head;
!     if ((*hitter)->head)
!         *hitter = (*hitter)->head;
!     if ((*hitter)->env != NULL || (*target)->env != NULL) {
!         *simple_attack = 1;
!         return 0;
!     }
!     if (QUERY_FLAG (*target, FLAG_REMOVED)
!         || QUERY_FLAG (*hitter, FLAG_REMOVED)
!         || (*hitter)->map == NULL || (*hitter)->map != (*target)->map)
!     {
!         LOG (llevError, "BUG: hitter (arch %s, name %s) with no relation to "
!              "target\n", (*hitter)->arch->name, (*hitter)->name);
!         return 1;
!     }
!     *simple_attack = 0;
!     return 0;
! }
! 
! static int abort_attack (object *target, object *hitter, int simple_attack)
! {
! /* Check if target and hitter are still in a relation similar to the one
!  * determined by get_attack_mode().  Returns true if the relation has changed.
   */
+     int new_mode;
  
!     if (hitter->env == target || target->env == hitter)
!         new_mode = 1;
!     else if (QUERY_FLAG (hitter, FLAG_REMOVED)
!              || QUERY_FLAG (target, FLAG_REMOVED)
!              || hitter->map == NULL || hitter->map != target->map)
!         return 1;
!     else
!         new_mode = 0;
!     return new_mode != simple_attack;
! }
! 
! static void thrown_item_effect (object *, object *);
! 
! static int attack_ob_simple (object *op, object *hitter, int base_dam,
! 	int base_wc)
! {
!     int simple_attack, roll, dam=0;
      char buf[MAX_BUF];
      uint32 type;
      att_msg *msg;
!     char *op_name = NULL;
      signed char luck=0;
+     tag_t op_tag, hitter_tag;
  
!     if (get_attack_mode (&op, &hitter, &simple_attack))
!         goto error;
! 
!     op_tag = op->count;
!     hitter_tag = hitter->count;
  
      /*
       * A little check to make it more difficult to dance forward and back
       * to avoid ever being hit by monsters.
       */
!     if ( ! simple_attack && QUERY_FLAG (op, FLAG_MONSTER)
!         && op->speed_left > -(FABS(op->speed))*0.3)
!     {
  	/* Decrease speed BEFORE calling process_object.  Otherwise, an
  	 * infinite loop occurs, with process_object calling move_monster,
  	 * which then gets here again.  By decreasing the speed before
***************
*** 350,371 ****
  	 */
  	op->speed_left--;
  	process_object(op);
! 	if (QUERY_FLAG(op, FLAG_FREED))
! 	return 1;
      }
  
      add_refcount(op_name = op->name);
-     if(hitter->head!=NULL)
- 	hitter=hitter->head;
  
-     if (hitter->name==NULL) {
- 	if(settings.debug >= llevDebug) {
- 	    dump_object(hitter);
- 	    LOG(llevDebug,"Object without name tried to attack.\n%s\n",errmsg);
- 	}
- 	return 1;
-     }
- 
      /*  BROKEN:  the luck code.  If you look carefully, luck has these effects:
  	positive luck adds to the damage YOU take and to YOUR likelihood
  	of getting HIT.  This is intolerable.  I am setting "luck" in this
--- 410,422 ----
  	 */
  	op->speed_left--;
  	process_object(op);
! 	if (was_destroyed (op, op_tag) || was_destroyed (hitter, hitter_tag)
!             || abort_attack (op, hitter, simple_attack))
! 		goto error;
      }
  
      add_refcount(op_name = op->name);
  
      /*  BROKEN:  the luck code.  If you look carefully, luck has these effects:
  	positive luck adds to the damage YOU take and to YOUR likelihood
  	of getting HIT.  This is intolerable.  I am setting "luck" in this
***************
*** 382,392 ****
  	roll=RANDOM()%20+1+luck;
  
      /* Adjust roll for various situations. */
!     roll += adj_attackroll(hitter,op); 
  
      /* See if we hit the creature */
!     if(roll==(20+luck)||op->stats.ac>=hitter->stats.wc-roll) {
! 	int hitdam=hitter->stats.dam+luck;
  #ifdef CASTING_TIME
  	if ((hitter->type == PLAYER)&&(hitter->casting > -1)){
  	    hitter->casting = -1;
--- 433,444 ----
  	roll=RANDOM()%20+1+luck;
  
      /* Adjust roll for various situations. */
!     if ( ! simple_attack)
!         roll += adj_attackroll(hitter,op); 
  
      /* See if we hit the creature */
!     if(roll==(20+luck)||op->stats.ac>=base_wc-roll) {
! 	int hitdam = base_dam + luck;
  #ifdef CASTING_TIME
  	if ((hitter->type == PLAYER)&&(hitter->casting > -1)){
  	    hitter->casting = -1;
***************
*** 399,432 ****
  	    if (op->type == PLAYER)  {
  		new_draw_info(NDI_UNIQUE, 0,op,"You were hit and lost your spell!");
  		new_draw_info_format(NDI_ALL|NDI_UNIQUE,5,NULL,
! 		    "%s was hit by %s and lost a spell.",op->name,hitter->name);
  	    }
  	}
  #endif
! 	/* If you hit something, the victim should *always* wake up.
! 	 * Before, invisible hitters could avoid doing this. 
! 	 * -b.t. */
! 	if(QUERY_FLAG(op,FLAG_SLEEP)) CLEAR_FLAG(op,FLAG_SLEEP);
! 
! 	/* If the victim can't see the attacker, it may alert others
! 	 * for help. */
! 	if(op->type!=PLAYER&&!can_see_enemy(op,hitter)
! 	   &&!get_owner(op)&&RANDOM()%(op->stats.Int+1))
! 	    npc_call_help(op);
! 
! 	/* if you were hidden and hit by a creature, you are discovered*/
! 	if(op->hide && QUERY_FLAG(hitter,FLAG_ALIVE)) {
! 	    make_visible(op);
! 	    if(op->type==PLAYER) new_draw_info(NDI_UNIQUE, 0,op,
! 		"You were hit by a wild attack. You are no longer hidden!");
! 	}
! 
! 	/* thrown items (hitter) will have various effects
! 	 * when they hit the victim.  For things like thrown daggers,
! 	 * this sets 'hitter' to the actual dagger, and not the
! 	 * wrapper object.
! 	 */
! 	if((hitter=thrown_item_effect(hitter,op))==NULL) goto leave;
  
  	/* Need to do at least 1 damage, otherwise there is no point
  	 * to go further and it will cause FPE's below.
--- 451,494 ----
  	    if (op->type == PLAYER)  {
  		new_draw_info(NDI_UNIQUE, 0,op,"You were hit and lost your spell!");
  		new_draw_info_format(NDI_ALL|NDI_UNIQUE,5,NULL,
! 		    "%s was hit by %s and lost a spell.",op_name,hitter->name);
  	    }
  	}
  #endif
! 	if ( ! simple_attack)
!         {
!             /* If you hit something, the victim should *always* wake up.
!              * Before, invisible hitters could avoid doing this. 
!              * -b.t. */
!             if (QUERY_FLAG (op, FLAG_SLEEP))
!                 CLEAR_FLAG(op,FLAG_SLEEP);
! 
!             /* If the victim can't see the attacker, it may alert others
!              * for help. */
!             if (op->type != PLAYER && ! can_see_enemy (op, hitter)
!                 && ! get_owner (op) && RANDOM() % (op->stats.Int + 1))
!                 npc_call_help (op);
! 
!             /* if you were hidden and hit by a creature, you are discovered*/
!             if (op->hide && QUERY_FLAG (hitter, FLAG_ALIVE)) {
!                 make_visible (op);
!                 if (op->type == PLAYER)
!                     new_draw_info (NDI_UNIQUE, 0, op,
!                                    "You were hit by a wild attack. "
!                                    "You are no longer hidden!");
!             }
! 
!             /* thrown items (hitter) will have various effects
!              * when they hit the victim.  For things like thrown daggers,
!              * this sets 'hitter' to the actual dagger, and not the
!              * wrapper object.
!              */
!             thrown_item_effect (hitter, op);
!             if (was_destroyed (hitter, hitter_tag)
!                 || was_destroyed (op, op_tag)
!                 || abort_attack (op, hitter, simple_attack))
!                 goto leave;
!         }
  
  	/* Need to do at least 1 damage, otherwise there is no point
  	 * to go further and it will cause FPE's below.
***************
*** 436,453 ****
  	type=hitter->attacktype;
  	if(!type) type=AT_PHYSICAL;
  	/* Handle monsters that hit back */
! 	if (QUERY_FLAG(op, FLAG_HITBACK) && QUERY_FLAG(hitter, FLAG_ALIVE)) {
  	    if (op->attacktype & AT_ACID && hitter->type==PLAYER)
  		new_draw_info(NDI_UNIQUE, 0,hitter,"You are splashed by acid!\n");
  	    hit_player(hitter, RANDOM()%(op->stats.dam+1), op, op->attacktype);
! 	    if (QUERY_FLAG(op, FLAG_FREED)) goto leave;
  	}
  
  	/* In the new attack code, it should handle multiple attack
  	 * types in its area, so remove it from here.
  	 */
  	dam=hit_player(op, (RANDOM()%hitdam)+1, hitter, type);
! 	if (QUERY_FLAG(op, FLAG_FREED))
  	    goto leave;
      } /* end of if hitter hit op */
      /* if we missed, dam=0 */
--- 498,521 ----
  	type=hitter->attacktype;
  	if(!type) type=AT_PHYSICAL;
  	/* Handle monsters that hit back */
! 	if ( ! simple_attack && QUERY_FLAG (op, FLAG_HITBACK)
! 	    && QUERY_FLAG (hitter, FLAG_ALIVE))
! 	{
  	    if (op->attacktype & AT_ACID && hitter->type==PLAYER)
  		new_draw_info(NDI_UNIQUE, 0,hitter,"You are splashed by acid!\n");
  	    hit_player(hitter, RANDOM()%(op->stats.dam+1), op, op->attacktype);
! 	    if (was_destroyed (op, op_tag)
!                 || was_destroyed (hitter, hitter_tag)
!                 || abort_attack (op, hitter, simple_attack))
!                 goto leave;
  	}
  
  	/* In the new attack code, it should handle multiple attack
  	 * types in its area, so remove it from here.
  	 */
  	dam=hit_player(op, (RANDOM()%hitdam)+1, hitter, type);
! 	if (was_destroyed (op, op_tag) || was_destroyed (hitter, hitter_tag)
!             || abort_attack (op, hitter, simple_attack))
  	    goto leave;
      } /* end of if hitter hit op */
      /* if we missed, dam=0 */
***************
*** 492,502 ****
  	new_draw_info(NDI_BLACK, 0, hitter->owner, buf);
      }
  
! leave:
!     free_string(op_name);
      return dam;
  }
  
  void tear_down_wall(object *op)
  {
      int perc=0;
--- 560,698 ----
  	new_draw_info(NDI_BLACK, 0, hitter->owner, buf);
      }
  
!     goto leave;
! 
!   error:
!     dam = 1;
!     goto leave;
! 
!   leave:
!     if (op_name)
!         free_string (op_name);
      return dam;
  }
  
+ int attack_ob (object *op, object *hitter)
+ {
+     if (hitter->head)
+         hitter = hitter->head;
+     return attack_ob_simple (op, hitter, hitter->stats.dam, hitter->stats.wc);
+ }
+ 
+ /* op is the arrow, tmp is what is stopping the arrow.
+  *
+  * Returns 1 if op was inserted into tmp's inventory, 0 otherwise.
+  */
+ static int stick_arrow (object *op, object *tmp)
+ {
+     /* If the missile hit a player, we insert it in their inventory.
+      * However, if the missile is heavy, we don't do so (assume it falls
+      * to the ground after a hit).  What a good value for this is up to
+      * debate - 5000 is 5 kg, so arrows, knives, and other light weapons
+      * stick around.
+      */
+     if (op->weight <= 5000 && tmp->stats.hp >= 0) {
+ 	if(tmp->head != NULL)
+ 	    tmp = tmp->head;
+         remove_ob (op);
+ 	op = insert_ob_in_ob(op,tmp);
+ 	if (tmp->type== PLAYER)
+ 	    esrv_send_item (tmp, op);
+         return 1;
+     } else
+ 	return 0;
+ }
+ 
+ /* hit_with_arrow() disassembles the missile, attacks the victim and
+  * reassembles the missile.
+  *
+  * It returns a pointer to the reassembled missile, or NULL if the missile
+  * isn't available anymore.
+  */
+ object *hit_with_arrow (object *op, object *victim)
+ {
+     object *container, *hitter;
+     int hit_something;
+     tag_t victim_tag, hitter_tag;
+     sint16 victim_x, victim_y;
+ 
+     /* Disassemble missile */
+     if (op->inv) {
+         container = op;
+         hitter = op->inv;
+         remove_ob (hitter);
+         insert_ob_in_map_simple (hitter, container->map);
+         /* Note that we now have an empty THROWN_OBJ on the map.  Code that
+          * might be called until this THROWN_OBJ is either reassembled or
+          * removed at the end of this function must be able to deal with empty
+          * THROWN_OBJs. */
+     } else {
+         container = NULL;
+         hitter = op;
+     }
+ 
+     /* Try to hit victim */
+     victim_x = victim->x;
+     victim_y = victim->y;
+     victim_tag = victim->count;
+     hitter_tag = hitter->count;
+     hit_something = attack_ob_simple (victim, hitter, op->stats.dam,
+                                       op->stats.wc);
+     /* Arrow attacks door, rune of summoning is triggered, demon is put on
+      * arrow, move_apply() calls this function, arrow sticks in demon,
+      * attack_ob_simple() returns, and we've got an arrow that still exists
+      * but is no longer on the map. Ugh. (Beware: Such things can happen at
+      * other places as well!) */
+     if (was_destroyed (hitter, hitter_tag) || hitter->env != NULL) {
+         if (container) {
+             remove_ob (container);
+             free_object (container);
+         }
+         return NULL;
+     }
+ 
+     /* Missile hit victim */
+     if (hit_something)
+     {
+         /* Stop arrow */
+         if (container == NULL) {
+             hitter = fix_stopped_arrow (hitter);
+             if (hitter == NULL)
+                 return NULL;
+         } else {
+             remove_ob (container);
+             free_object (container);
+         }
+ 
+         /* Try to stick arrow into victim */
+         if ( ! was_destroyed (victim, victim_tag)
+              && stick_arrow (hitter, victim))
+             return NULL;
+ 
+         /* Else try to put arrow on victim's map square */
+         if ((victim_x != hitter->x || victim_y != hitter->y)
+             && ! wall (hitter->map, victim_x, victim_y))
+         {
+             remove_ob (hitter);
+             hitter->x = victim_x;
+             hitter->y = victim_y;
+             insert_ob_in_map (hitter, hitter->map, hitter);
+         } else {
+             /* Else leave arrow where it is */
+             merge_ob (hitter, NULL);
+         }
+         return NULL;
+     }
+ 
+     /* Missile missed victim - reassemble missile */
+     if (container) {
+         remove_ob (hitter);
+         insert_ob_in_ob (hitter, container);
+     }
+     return op;
+ }
+ 
+ 
  void tear_down_wall(object *op)
  {
      int perc=0;
***************
*** 743,768 ****
      char buf[MAX_BUF];
      object *old_hitter=NULL; /* this is used in case of servant monsters */ 
      int maxdam=0,ndam,attacktype=1,attacknum,magic=(type & AT_MAGIC);
!     tag_t hitter_tag;
  
!     if (QUERY_FLAG (op, FLAG_FREED) || QUERY_FLAG (hitter, FLAG_FREED)) {
!         LOG (llevError, "BUG: hit_player(): freed object\n");
          return 0;
!     }
      hitter_tag = hitter->count;
  
!     if(op->head!=NULL) {
! 	if(op->head==op) {
! 	    LOG(llevError,"Recursive head error!\n");
! 	    return 0;
! 	}
! #if 0
! 	/* To paralyze/slow a creature, we must hit its head with the attacktype.
! 	 * If we are going to do this, this should probably be expanded.
! 	 */
! 	if(type&AT_PARALYZE || type&AT_SLOW)
! 	    return 0;
! #else
  	/* slow and paralyze must hit the head.  But we don't want to just
  	 * return - we still need to process other attacks the spell still
  	 * might have.  So just remove the paralyze and slow attacktypes,
--- 939,958 ----
      char buf[MAX_BUF];
      object *old_hitter=NULL; /* this is used in case of servant monsters */ 
      int maxdam=0,ndam,attacktype=1,attacknum,magic=(type & AT_MAGIC);
!     int body_attack = op && op->head;   /* Did we hit op's head? */
!     int simple_attack;
!     tag_t op_tag, hitter_tag;
  
!     if (get_attack_mode (&op, &hitter, &simple_attack))
          return 0;
! 
!     if (QUERY_FLAG (op, FLAG_WIZ))
!         return 0;
! 
!     op_tag = op->count;
      hitter_tag = hitter->count;
  
!     if (body_attack) {
  	/* slow and paralyze must hit the head.  But we don't want to just
  	 * return - we still need to process other attacks the spell still
  	 * might have.  So just remove the paralyze and slow attacktypes,
***************
*** 776,794 ****
  	    type &= ~(AT_PARALYZE | AT_SLOW);
  	    if (!type || type==AT_MAGIC) return 0;
  	}
- #endif
- 	op=op->head;
      }
  
!     if(op->type==DOOR && op->inv && op->inv->type==RUNE) {
! 	spring_trap(op->inv,hitter);
!         if (was_destroyed (hitter, hitter_tag))
!             return 0;
      }
  
!     /* If its already dead, or we're the wizard, don't attack it - no point */
!     if(QUERY_FLAG(op,FLAG_WIZ)||!QUERY_FLAG(op,FLAG_ALIVE)||op->stats.hp<0)
  	return 0;
  
  #ifdef ATTACK_DEBUG
      LOG(llevDebug,"hit player: attacktype %d, dam %d\n", type, dam);
--- 966,994 ----
  	    type &= ~(AT_PARALYZE | AT_SLOW);
  	    if (!type || type==AT_MAGIC) return 0;
  	}
      }
  
!     if ( ! simple_attack && op->type == DOOR) {
!         object *tmp;
!         for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
!             if (tmp->type == RUNE) {
!                 spring_trap (tmp, hitter);
!                 if (was_destroyed (hitter, hitter_tag)
!                     || was_destroyed (op, op_tag)
!                     || abort_attack (op, hitter, simple_attack))
!                     return 0;
!                 break;
!             }
      }
  
!     if ( ! QUERY_FLAG (op, FLAG_ALIVE) || op->stats.hp < 0) {
!         /* FIXME: If a player is killed by a rune in a door, the
!          * was_destroyed() check above doesn't return, and might get here.
!          */
!         LOG (llevDebug, "victim (arch %s, name %s) already dead in "
!              "hit_player()\n", op->arch->name, op->name);
  	return 0;
+     }
  
  #ifdef ATTACK_DEBUG
      LOG(llevDebug,"hit player: attacktype %d, dam %d\n", type, dam);
***************
*** 1212,1218 ****
      if((tmp=present(PARAIMAGE,op->map,op->x,op->y))==NULL) {
        tmp=clone_arch(PARAIMAGE);
        tmp->x=op->x,tmp->y=op->y;
!       insert_ob_in_map(tmp,op->map,NULL);
      }
      op->speed_left-=(float)FABS(op->speed)*(dam*3);
      tmp->stats.food+=(signed short) (dam*3)/op->speed;
--- 1412,1422 ----
      if((tmp=present(PARAIMAGE,op->map,op->x,op->y))==NULL) {
        tmp=clone_arch(PARAIMAGE);
        tmp->x=op->x,tmp->y=op->y;
!       /* We can't use insert_ob_in_map() (which can trigger various things)
!        * unless a lot of was_destroyed() checks are added in our callers.
!        * But this is just a simple visual effect anyway.
!        */
!       insert_ob_in_map_simple(tmp,op->map);
      }
      op->speed_left-=(float)FABS(op->speed)*(dam*3);
      tmp->stats.food+=(signed short) (dam*3)/op->speed;
***************
*** 1274,1295 ****
  
  /* thrown_item_effect() - handles any special effects of thrown
   * items (like attacking living creatures--a potion thrown at a
!  * monster). We return the hitter item for further
!  * possible (ie physical) attacks. Other posibilities
!  * include spilling containers, and lighting stuff on fire
!  * with thrown torches.
   */
! object *thrown_item_effect( object *hitter, object *victim) {
!   object *tmp=hitter;
!  
!   if(hitter->type==THROWN_OBJ) tmp = hitter->inv;
!   if(!tmp) return hitter;
   
    if(!QUERY_FLAG(hitter,FLAG_ALIVE)) {
!     switch (tmp->type) {
        case POTION:
          if(QUERY_FLAG(victim,FLAG_ALIVE)&&!QUERY_FLAG(victim,FLAG_UNDEAD)
! 	  &&!(victim->immune&AT_MAGIC)) (void) apply_potion(victim,tmp);
          break;
        case FOOD:
  	/* cursed food is (often) poisonous....but it won't 'explode'
--- 1478,1494 ----
  
  /* thrown_item_effect() - handles any special effects of thrown
   * items (like attacking living creatures--a potion thrown at a
!  * monster).
   */
! static void thrown_item_effect (object *hitter, object *victim)
! {
!   tag_t tag = hitter->count;
   
    if(!QUERY_FLAG(hitter,FLAG_ALIVE)) {
!     switch (hitter->type) {
        case POTION:
          if(QUERY_FLAG(victim,FLAG_ALIVE)&&!QUERY_FLAG(victim,FLAG_UNDEAD)
! 	  &&!(victim->immune&AT_MAGIC)) (void) apply_potion(victim,hitter);
          break;
        case FOOD:
  	/* cursed food is (often) poisonous....but it won't 'explode'
***************
*** 1299,1320 ****
          break;
        case POISON: /* poison drinks */
          if(QUERY_FLAG(victim,FLAG_ALIVE)&&!QUERY_FLAG(victim,FLAG_UNDEAD)
! 	  &&!(victim->immune&AT_POISON)) apply_poison(victim,tmp);
          break;
        case CONTAINER: 
          /* spill_container(victim,RANDOM()%(hitter->stats.dam+1)); */
          break;
-       default:
-         break;
      }
- #if 0
-     /* glow objects (torches) are on fire.. */
-     if(!tmp->type&&tmp->glow_radius>0) {
-     }
- #endif
    }
-  
-   return tmp;
  }
  
  /* adj_attackroll() - adjustments to attacks by various conditions */
--- 1498,1510 ----
          break;
        case POISON: /* poison drinks */
          if(QUERY_FLAG(victim,FLAG_ALIVE)&&!QUERY_FLAG(victim,FLAG_UNDEAD)
! 	  &&!(victim->immune&AT_POISON)) apply_poison(victim,hitter);
          break;
        case CONTAINER: 
          /* spill_container(victim,RANDOM()%(hitter->stats.dam+1)); */
          break;
      }
    }
  }
  
  /* adj_attackroll() - adjustments to attacks by various conditions */
***************
*** 1324,1331 ****
    int adjust=0;
  
    /* safety */
!   if(!target||!hitter||!hitter->map||!target->map||hitter->map!=target->map)
      return 0;
  
    /* aimed missiles use the owning object's sight */
    if(is_aimed_missile(hitter)) {
--- 1514,1524 ----
    int adjust=0;
  
    /* safety */
!   if(!target||!hitter||!hitter->map||!target->map||hitter->map!=target->map) {
!     LOG (llevError, "BUG: adj_attackroll(): hitter and target not on same "
!          "map\n");
      return 0;
+   }
  
    /* aimed missiles use the owning object's sight */
    if(is_aimed_missile(hitter)) {
Index: server/c_object.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/c_object.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -c -r1.12 -r1.13
*** server/c_object.c	2000/11/03 05:42:56	1.12
--- server/c_object.c	2000/11/06 23:06:47	1.13
***************
*** 1,6 ****
  /*
   * static char *rcsid_c_object_c =
!  *   "$Id: c_object.c,v 1.12 2000/11/03 05:42:56 cvs Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
--- 1,6 ----
  /*
   * static char *rcsid_c_object_c =
!  *   "$Id: c_object.c,v 1.13 2000/11/06 23:06:47 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
***************
*** 246,259 ****
   * tmp is the object to pick up, nrof is the number to
   * pick up (0 means all of them)
   */
! void pick_up_object (object *pl, object *op, object *tmp, int nrof)
  {
      /* buf needs to be big (more than 256 chars) because you can get
       * very long item names.
       */
      char buf[HUGE_BUF];
      object *env=tmp->env;
!     uint32 weight;
  
      /* IF the player is flying & trying to take the item out of a container 
       * that is in his inventory, let him.  tmp->env points to the container 
--- 246,260 ----
   * tmp is the object to pick up, nrof is the number to
   * pick up (0 means all of them)
   */
! static void pick_up_object (object *pl, object *op, object *tmp, int nrof)
  {
      /* buf needs to be big (more than 256 chars) because you can get
       * very long item names.
       */
      char buf[HUGE_BUF];
      object *env=tmp->env;
!     uint32 weight, effective_weight_limit;
!     int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
  
      /* IF the player is flying & trying to take the item out of a container 
       * that is in his inventory, let him.  tmp->env points to the container 
***************
*** 265,302 ****
  	new_draw_info(NDI_UNIQUE, 0,pl, "You are levitating, you can't reach the ground!");
  	return;
      }
-     if(!can_pick(pl,tmp)) {
- 	if (tmp->name!=NULL) {
- 	    sprintf(buf,"You can't pick up a %s", tmp->name);
- 	    new_draw_info(NDI_UNIQUE, 0,pl, buf);
- 	}
- 	else
- 	    new_draw_info(NDI_UNIQUE, 0,pl,"You can't take that!");
- 	return;
-     }
      if (QUERY_FLAG (tmp, FLAG_NO_DROP))
  	return;
      if(QUERY_FLAG(tmp,FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) {
  	new_draw_info(NDI_UNIQUE, 0,pl, "The object disappears in a puff of smoke!");
  	new_draw_info(NDI_UNIQUE, 0,pl, "It must have been an illusion.");
  	if (pl->type==PLAYER) esrv_del_item (pl->contr, tmp->count);
! 	remove_ob(tmp);
  	free_object(tmp);
  	return;
      }
-     
-     /* startequip items are not allowed to be put into containers: */
-     if (pl->type == PLAYER && op->type == CONTAINER &&
- 	QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
-       new_draw_info(NDI_UNIQUE, 0,pl,"This object cannot be put into containers!");
-       return;
-     }
      
!     if (nrof==0 || nrof>tmp->nrof) nrof=(tmp->nrof?tmp->nrof:1);
      /* Figure out how much weight this object will add to the player */
      weight = tmp->weight * nrof;
      if (tmp->inv) weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
!     if ((pl->weight + pl->carrying +weight) > weight_limit[pl->stats.Str]) {
  	new_draw_info(NDI_UNIQUE, 0,pl,"That item is too heavy for you to pick up.");
  	return;
      }
--- 266,293 ----
  	new_draw_info(NDI_UNIQUE, 0,pl, "You are levitating, you can't reach the ground!");
  	return;
      }
      if (QUERY_FLAG (tmp, FLAG_NO_DROP))
  	return;
      if(QUERY_FLAG(tmp,FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) {
  	new_draw_info(NDI_UNIQUE, 0,pl, "The object disappears in a puff of smoke!");
  	new_draw_info(NDI_UNIQUE, 0,pl, "It must have been an illusion.");
  	if (pl->type==PLAYER) esrv_del_item (pl->contr, tmp->count);
! 	if ( ! QUERY_FLAG (tmp, FLAG_REMOVED))
!             remove_ob (tmp);
  	free_object(tmp);
  	return;
      }
      
!     if (nrof > tmp_nrof || nrof == 0)
! 	nrof = tmp_nrof;
      /* Figure out how much weight this object will add to the player */
      weight = tmp->weight * nrof;
      if (tmp->inv) weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
!     if (pl->stats.Str <= MAX_STAT)
!         effective_weight_limit = weight_limit[pl->stats.Str];
!     else
!         effective_weight_limit = weight_limit[MAX_STAT];
!     if ((pl->weight + pl->carrying + weight) > effective_weight_limit) {
  	new_draw_info(NDI_UNIQUE, 0,pl,"That item is too heavy for you to pick up.");
  	return;
      }
***************
*** 307,313 ****
  	SET_FLAG(tmp, FLAG_WAS_WIZ);
  #endif
  
!     if(nrof != tmp->nrof && !(nrof == 1 && tmp->nrof == 0)) {
  	object *tmp2 = tmp;
          tag_t tmp2_tag = tmp2->count;
  	tmp = get_split_ob (tmp, nrof);
--- 298,304 ----
  	SET_FLAG(tmp, FLAG_WAS_WIZ);
  #endif
  
!     if (nrof != tmp_nrof) {
  	object *tmp2 = tmp;
          tag_t tmp2_tag = tmp2->count;
  	tmp = get_split_ob (tmp, nrof);
***************
*** 317,323 ****
  	}
  	/* Tell a client what happened rest of objects */
  	if (pl->type == PLAYER) {
!             if (was_destroyed (tmp2, tmp2_tag))
  		esrv_del_item (pl->contr, tmp2_tag);
  	    else
  		esrv_send_item (pl, tmp2);
--- 308,314 ----
  	}
  	/* Tell a client what happened rest of objects */
  	if (pl->type == PLAYER) {
! 	    if (was_destroyed (tmp2, tmp2_tag))
  		esrv_del_item (pl->contr, tmp2_tag);
  	    else
  		esrv_send_item (pl, tmp2);
***************
*** 327,335 ****
  	 * - we are moving all the items from the container to elsewhere,
  	 * so it needs to be deleted.
  	 */
! 	if (tmp->env && pl->type==PLAYER) 
! 	    esrv_del_item (pl->contr, tmp->count);
! 	remove_ob(tmp); /* Unlink it */
      }
      if(QUERY_FLAG(tmp, FLAG_UNPAID))
  	(void) sprintf(buf,"%s will cost you %s.", query_name(tmp),
--- 318,328 ----
  	 * - we are moving all the items from the container to elsewhere,
  	 * so it needs to be deleted.
  	 */
!         if ( ! QUERY_FLAG (tmp, FLAG_REMOVED)) {
! 	    if (tmp->env && pl->type==PLAYER) 
! 	        esrv_del_item (pl->contr, tmp->count);
! 	    remove_ob(tmp); /* Unlink it */
! 	}
      }
      if(QUERY_FLAG(tmp, FLAG_UNPAID))
  	(void) sprintf(buf,"%s will cost you %s.", query_name(tmp),
***************
*** 360,377 ****
  void pick_up(object *op,object *alt) 
  /* modified slightly to allow monsters use this -b.t. 5-31-95 */
  {
!     object *tmp=NULL;
      int count;
  
!     if(alt)
! 	tmp=alt;
!     else if(op->below==NULL || !can_pick(op, tmp)) {
! 	new_draw_info(NDI_UNIQUE, 0,op,"There is nothing to pick up here.");
! 	return;
!     } else
! 	tmp=op->below;
  
!     if (op->type==PLAYER && op->contr->count && op->contr->count <tmp->nrof)
  	count=op->contr->count;
      else
  	count=tmp->nrof;
--- 353,394 ----
  void pick_up(object *op,object *alt) 
  /* modified slightly to allow monsters use this -b.t. 5-31-95 */
  {
!     int need_fix_tmp = 0;
!     object *tmp;
!     mapstruct *tmp_map;
      int count;
+     tag_t tag;
  
!     /* Decide which object to pick. */
!     if (alt)
!     {
!         if ( ! can_pick (op, alt)) {
!             new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up a %s.",
!                                   alt->name);
! 	    goto leave;
!         }
!         tmp = alt;
!     }
!     else
!     {
!         if (op->below == NULL || ! can_pick (op, op->below)) {
!              new_draw_info (NDI_UNIQUE, 0, op,
!                             "There is nothing to pick up here.");
!              goto leave;
!         }
!         tmp = op->below;
!     }
! 
!     /* Try to catch it. */
!     tmp_map = tmp->map;
!     tmp = stop_item (tmp);
!     if (tmp == NULL)
!         goto leave;
!     need_fix_tmp = 1;
!     if ( ! can_pick (op, tmp))
!         goto leave;
  
!     if (op->type==PLAYER)
  	count=op->contr->count;
      else
  	count=tmp->nrof;
***************
*** 380,386 ****
      if (op->container) {
  	alt = op->container;
  	if (alt != tmp->env && !sack_can_hold (op, alt, tmp,count))
! 	    return;
      } else { /* non container pickup */
  	for (alt=op->inv; alt; alt=alt->below)
  	    if (alt->type==CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) &&
--- 397,403 ----
      if (op->container) {
  	alt = op->container;
  	if (alt != tmp->env && !sack_can_hold (op, alt, tmp,count))
! 	    goto leave;
      } else { /* non container pickup */
  	for (alt=op->inv; alt; alt=alt->below)
  	    if (alt->type==CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) &&
***************
*** 405,415 ****
  #ifdef PICKUP_DEBUG
      printf ("Pick_up(): %s picks %s (%d) and inserts it %s.\n",op->name, tmp->name,  op->contr->count, alt->name);
  #endif
!     if(op->type==PLAYER) { 
!        pick_up_object(op, alt, tmp, op->contr->count);
         op->contr->count=0;
!     } else 
!        pick_up_object(op, alt, tmp, tmp->nrof);
  }
  
  
--- 422,448 ----
  #ifdef PICKUP_DEBUG
      printf ("Pick_up(): %s picks %s (%d) and inserts it %s.\n",op->name, tmp->name,  op->contr->count, alt->name);
  #endif
! 
!     /* startequip items are not allowed to be put into containers: */
!     if (op->type == PLAYER && alt->type == CONTAINER
! 	&& QUERY_FLAG (tmp, FLAG_STARTEQUIP))
!     {
!         new_draw_info (NDI_UNIQUE, 0, op,
!                        "This object cannot be put into containers!");
!         goto leave;
!     }
! 
!     tag = tmp->count;
!     pick_up_object (op, alt, tmp, count);
!     if (was_destroyed (tmp, tag) || tmp->env)
!         need_fix_tmp = 0;
!     if (op->type == PLAYER)
         op->contr->count=0;
!     goto leave;
! 
!   leave:
!     if (need_fix_tmp)
!         fix_stopped_item (tmp, tmp_map, op);
  }
  
  
***************
*** 450,456 ****
  	for (tmp=op->below; tmp!=NULL; tmp=tmp->next)
  	    if (!tmp->invisible) {
  		char buf[MAX_BUF];
! 		sprintf(buf,"You can't pick up a %s",
  		    tmp->name? tmp->name:"null");
  		new_draw_info(NDI_UNIQUE, 0,op, buf);
  		break;
--- 483,489 ----
  	for (tmp=op->below; tmp!=NULL; tmp=tmp->next)
  	    if (!tmp->invisible) {
  		char buf[MAX_BUF];
! 		sprintf(buf,"You can't pick up a %s.",
  		    tmp->name? tmp->name:"null");
  		new_draw_info(NDI_UNIQUE, 0,op, buf);
  		break;
Index: server/player.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/player.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -c -r1.18 -r1.19
*** server/player.c	2000/10/30 22:09:59	1.18
--- server/player.c	2000/11/06 23:06:47	1.19
***************
*** 1,6 ****
  /*
   * static char *rcsid_player_c =
!  *   "$Id: player.c,v 1.18 2000/10/30 22:09:59 jec Exp $";
   */
  
  /*
--- 1,6 ----
  /*
   * static char *rcsid_player_c =
!  *   "$Id: player.c,v 1.19 2000/11/06 23:06:47 jec Exp $";
   */
  
  /*
***************
*** 974,980 ****
    arrow->level = SK_level (op);
  
    arrow->map = op->map;
-   SET_FLAG(arrow, FLAG_NO_PICK);
    SET_FLAG(arrow, FLAG_FLYING);
    SET_FLAG(arrow, FLAG_FLY_ON);
    SET_FLAG(arrow, FLAG_WALK_ON);
--- 974,979 ----
Index: server/rune.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/rune.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -c -r1.8 -r1.9
*** server/rune.c	2000/06/09 00:17:22	1.8
--- server/rune.c	2000/11/06 23:06:47	1.9
***************
*** 1,6 ****
  /*
   * static char *rcsid_rune_c =
!  *   "$Id: rune.c,v 1.8 2000/06/09 00:17:22 cvs Exp $";
   */
  
  /*
--- 1,6 ----
  /*
   * static char *rcsid_rune_c =
!  *   "$Id: rune.c,v 1.9 2000/11/06 23:06:47 jec Exp $";
   */
  
  /*
***************
*** 211,220 ****
     object *env;
     tag_t trap_tag = trap->count;
  
!   trap->stats.hp--;  /*decrement detcount */
    /*  get the spell number from the name in the slaying field, and set
        that as the spell to be cast. */
!   if((spell_in_rune=look_up_spell_by_name(NULL,trap->slaying))!=-1) trap->stats.sp=spell_in_rune;
    if(victim) if(victim->type==PLAYER) new_draw_info(NDI_UNIQUE, 0,victim,trap->msg);
    /*  Flash an image of the trap on the map so the poor sod
     *   knows what hit him.  */
--- 211,233 ----
     object *env;
     tag_t trap_tag = trap->count;
  
!   /* Prevent recursion */
!   if (trap->stats.hp <= 0)
!     return;
! 
    /*  get the spell number from the name in the slaying field, and set
        that as the spell to be cast. */
!   if ((spell_in_rune = look_up_spell_by_name (NULL, trap->slaying)) != -1)
!     trap->stats.sp=spell_in_rune;
! 
!   /* Only living objects can trigger runes that don't cast spells, as
!    * doing direct damage to a non-living object doesn't work anyway.
!    * Typical example is an arrow attacking a door.
!    */
!   if ( ! QUERY_FLAG (victim, FLAG_ALIVE) && ! trap->stats.sp)
!     return;
! 
!   trap->stats.hp--;  /*decrement detcount */
    if(victim) if(victim->type==PLAYER) new_draw_info(NDI_UNIQUE, 0,victim,trap->msg);
    /*  Flash an image of the trap on the map so the poor sod
     *   knows what hit him.  */
***************
*** 239,245 ****
      cast_spell(trap,trap,trap->direction,trap->stats.sp,1,spellNormal,NULL);
    }
  
!   if(!trap->stats.hp) {
      trap->type=98;  /* make the trap impotent */
      trap->stats.food=20;  /* make it stick around until its spells are gone */
      SET_FLAG(trap,FLAG_IS_USED_UP);
--- 252,258 ----
      cast_spell(trap,trap,trap->direction,trap->stats.sp,1,spellNormal,NULL);
    }
  
!   if (trap->stats.hp <= 0) {
      trap->type=98;  /* make the trap impotent */
      trap->stats.food=20;  /* make it stick around until its spells are gone */
      SET_FLAG(trap,FLAG_IS_USED_UP);
Index: server/skills.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/skills.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -c -r1.8 -r1.9
*** server/skills.c	2000/06/09 12:01:47	1.8
--- server/skills.c	2000/11/06 23:06:47	1.9
***************
*** 1,6 ****
  /*
   * static char *rcsid_skills_c =
!  *   "$Id: skills.c,v 1.8 2000/06/09 12:01:47 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
--- 1,6 ----
  /*
   * static char *rcsid_skills_c =
!  *   "$Id: skills.c,v 1.9 2000/11/06 23:06:47 jec Exp $";
   */
  /*
      CrossFire, A Multiplayer game for X-windows
***************
*** 1603,1609 ****
      throw_ob->speed_left = 0;
      throw_ob->map = op->map;
  
-     SET_FLAG(throw_ob, FLAG_NO_PICK);
      SET_FLAG(throw_ob, FLAG_FLYING);
      SET_FLAG(throw_ob, FLAG_FLY_ON);
      SET_FLAG(throw_ob, FLAG_WALK_ON);
--- 1603,1608 ----
Index: server/spell_effect.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/spell_effect.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -c -r1.24 -r1.25
*** server/spell_effect.c	2000/10/20 19:19:39	1.24
--- server/spell_effect.c	2000/11/06 23:06:47	1.25
***************
*** 1,6 ****
  /*
   * static char *rcsid_spell_effect_c =
!  *   "$Id: spell_effect.c,v 1.24 2000/10/20 19:19:39 peterm Exp $";
   */
  
  
--- 1,6 ----
  /*
   * static char *rcsid_spell_effect_c =
!  *   "$Id: spell_effect.c,v 1.25 2000/11/06 23:06:47 jec Exp $";
   */
  
  
***************
*** 1540,1551 ****
    if (at)
      for(i=1;i<9;i++)
        fire_arch(op,op,i,at,0,0);
!   remove_ob(op);
!   op->x=env->x;
!   op->y=env->y;
!   if(!explode_object(op)) /* Boom 8) */
!     LOG(llevError,"Error: bomb refused to go off.\n");
!   return;
  }
  
  
--- 1540,1546 ----
    if (at)
      for(i=1;i<9;i++)
        fire_arch(op,op,i,at,0,0);
!   explode_object(op);
  }
  
  
***************
*** 1586,1593 ****
      insert_ob_in_map(op,op->map,op);
      return;
    }
!   hit_map(op, 0, op->attacktype);
!   insert_ob_in_map(op,op->map,op);
  }
  
  void cancellation(object *op)
--- 1581,1588 ----
      insert_ob_in_map(op,op->map,op);
      return;
    }
!   if ((op = insert_ob_in_map (op, op->map, op)) != NULL)
!     hit_map (op, 0, op->attacktype);
  }
  
  void cancellation(object *op)
Index: server/spell_util.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/spell_util.c,v
retrieving revision 1.24
retrieving revision 1.26
diff -c -r1.24 -r1.26
*** server/spell_util.c	2000/10/31 22:11:46	1.24
--- server/spell_util.c	2000/11/07 00:43:30	1.26
***************
*** 1,6 ****
  /*
   * static char *rcsid_spell_util_c =
!  *   "$Id: spell_util.c,v 1.24 2000/10/31 22:11:46 peterm Exp $";
   */
  
  /*
--- 1,6 ----
  /*
   * static char *rcsid_spell_util_c =
!  *   "$Id: spell_util.c,v 1.26 2000/11/07 00:43:30 peterm Exp $";
   */
  
  /*
***************
*** 1103,1109 ****
      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))
        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);
--- 1103,1110 ----
      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);
***************
*** 1115,1131 ****
      int i;
      tag_t tag;
  
-     if (op->env) {
-         /* handle flowers in icecubes */
-         op->speed = 0;
-         update_ob_speed (op);
-         return;
-     }
- 
      /* if no map then hit_map will crash so just ignore object */
      if (! op->map) {
  	LOG(llevError,"Tried to move_cone object %s without a map.\n",
  	    op->name ? op->name : "unknown");
  	return;
      }
  
--- 1116,1127 ----
      int i;
      tag_t tag;
  
      /* if no map then hit_map will crash so just ignore object */
      if (! op->map) {
  	LOG(llevError,"Tried to move_cone object %s without a map.\n",
  	    op->name ? op->name : "unknown");
+         op->speed = 0;
+         update_ob_speed (op);
  	return;
      }
  
***************
*** 1478,1524 ****
    }
    insert_ob_in_map(op,op->map,op);
  }
- 
- int explode_object(object *op) {
-   object *tmp, *victim, *env;
  
!   if(out_of_map(op->map,op->x,op->y))  /*  peterm:  check for out of map obj's.*/
!     {
!       return 0;
!     }
!   for(env=op;env->env!=NULL;env=env->env);
!   if (env->map == NULL)
!     return 0;
!   if(op->other_arch==NULL)
!     return 0;
!   tmp=arch_to_object(op->other_arch);
  
!   /* peterm: Hack added to make objects be able to both hit for damage and
!     then explode.  */
!   if(op->attacktype){
!       for(victim=get_map_ob(op->map,op->x,op->y);victim!=NULL;victim=victim->above)
!         if(QUERY_FLAG(victim,FLAG_ALIVE))
!           break;
!       hit_map(op,0,op->attacktype);
  
! #if 0
!     /* Hit_map will also do a hit_player for us.  Leaving this call in
!      * effectively doubles the amount of damage the bullet is doing.
!      */
!       /* Should hit_map also be doing this?  Why call hit_player
!        * again?  Also, make sure victim has not been killed - it
!        * is possible that hit_map killed the object.
!        */
!       if(victim!=NULL && !QUERY_FLAG(victim,FLAG_FREED))
! 	 hit_player(victim,op->stats.dam,op,op->attacktype);
! #endif
      }
  
    /*  peterm:  hack added to make fireballs and other explosions level
     *  dependent:
     */
- 
    /*  op->stats.sp stores the spell which made this object here. */
    tmp->stats.dam += SP_level_dam_adjust(op,op,op->stats.sp);
    if(op->attacktype&AT_MAGIC)
      tmp->attacktype|=AT_MAGIC;
--- 1474,1524 ----
    }
    insert_ob_in_map(op,op->map,op);
  }
  
! void explode_object(object *op)
! {
!   tag_t op_tag = op->count;
!   object *tmp;
!   mapstruct *map;
  
!   if (op->other_arch == NULL) {
!     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_simple (op, env->map);
+   } 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);
    tmp->stats.dam += SP_level_dam_adjust(op,op,op->stats.sp);
    if(op->attacktype&AT_MAGIC)
      tmp->attacktype|=AT_MAGIC;
***************
*** 1526,1573 ****
    if(op->stats.hp)
      tmp->stats.hp=op->stats.hp;
    tmp->stats.maxhp=op->count; /* Unique ID */
!   tmp->x=env->x,tmp->y=env->y;
  
  #ifdef MULTIPLE_GODS /* needed for AT_HOLYWORD stuff -b.t. */
    if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER) 
!           if(!tailor_god_spell(tmp,op)) return 0;   
  #endif
  
!   if (wall(env->map,env->x,env->y))
!     tmp->x-=DIRX(env),tmp->y-=DIRY(env);
!   if (out_of_map(env->map, env->x, env->y))
!     free_object(tmp);
!   else
!     insert_ob_in_map(tmp,env->map,op);
!   free_object(op);
!   return 1;
  }
  
! void check_fired_arch(object *op) {
!   if(blocked(op->map,op->x,op->y)) {
      object *tmp;
!     remove_ob(op);
!     if(out_of_map(op->map,op->x,op->y)) {
! 	free_object(op);
! 	return;
      }
!     if(explode_object(op))
!       return;
!     for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
!       if(QUERY_FLAG(tmp, FLAG_ALIVE))
!         break;
!     if(tmp!=NULL)
!       op->stats.dam-=hit_player(tmp,op->stats.dam,op,op->attacktype);
!     if(blocked(op->map,op->x,op->y)) {
!       free_object(op);
!       return;
      }
-     insert_ob_in_map(op,op->map,op);
-   }
  }
  
! void move_fired_arch(object *op) {
!     remove_ob(op);
  
      /* peterm:  added to make comet leave a trail of burnouts 
  	it's an unadulterated hack, but the effect is cool.	*/
--- 1526,1588 ----
    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;
  
  #ifdef MULTIPLE_GODS /* 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;
!     }
  #endif
  
!   /* Prevent recursion */
!   CLEAR_FLAG (op, FLAG_WALK_ON);
!   CLEAR_FLAG (op, FLAG_FLY_ON);
! 
!   insert_ob_in_map (tmp, op->map, op);
!   if ( ! was_destroyed (op, op_tag)) {
!     remove_ob (op);
!     free_object (op);
!   }
  }
  
! void check_fired_arch (object *op)
! {
!     tag_t op_tag = op->count, tmp_tag;
      object *tmp;
!     int dam;
! 
!     if ( ! blocked (op->map, op->x, op->y))
!         return;
! 
!     if (op->other_arch) {
!         explode_object (op);
!         return;
      }
! 
!     for (tmp = get_map_ob (op->map,op->x,op->y); tmp != NULL; tmp = tmp->above)
!     {
!         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)
!             {
!                 remove_ob (op);
!                 free_object(op);
!                 return;
!             }
!         }
      }
  }
  
! void move_fired_arch (object *op)
! {
!     tag_t op_tag = op->count;
!     int new_x, new_y;
  
      /* peterm:  added to make comet leave a trail of burnouts 
  	it's an unadulterated hack, but the effect is cool.	*/
***************
*** 1576,1636 ****
  
          tmp1->x = op->x; tmp1->y = op->y;
          insert_ob_in_map(tmp1,op->map,op);
      } /* end addition.  */
  
!     op->x+=DIRX(op),op->y+=DIRY(op);
!     if(!op->direction||wall(op->map,op->x,op->y)) {
! 	if(explode_object(op))
! 	    return;
! 	free_object(op);
! 	return;
      }
  
!     if(reflwall(op->map,op->x,op->y)) {
! 	op->direction=absdir(op->direction+4);
! 	if ((op = insert_ob_in_map(op,op->map,op)) != NULL)
! 	    update_turn_face(op);
! 	return;
      }
-     if(blocked(op->map,op->x,op->y)) {
- 	object *tmp;
  
! 	if(out_of_map(op->map,op->x,op->y)) {
! 	    free_object(op);
! 	    return;
! 	}
! 
! 	if(explode_object(op))
! 	    return;
! 
! 	for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
! 	    if(QUERY_FLAG(tmp, FLAG_ALIVE))
! 		break;
! 
! 	if(tmp!=NULL) {
! 	    /* Certain items, like speedballs, have attacktype ghosthit.
! 	     * hit_player wants to remove the object after it hits the player.
! 	     * Since it is already removed, just don't make it ghosthit, and
! 	     * remove it here
! 	     */
! 
! 	    if (op->attacktype & AT_GHOSTHIT) {
! 		hit_player(tmp,op->stats.dam,op,(op->attacktype & ~AT_GHOSTHIT));
! 		free_object(op);
! 		return;
! 	    }
! 	    else
! 		op->stats.dam-=hit_player(tmp,op->stats.dam,op,op->attacktype);
! 	}
! 	/* I guess this can be applicable if the object blocking the
! 	 * space was destroyed?
! 	 */
! 	if(blocked(op->map,op->x,op->y)) {
! 	    free_object(op);
! 	    return;
! 	}
!     } /* if space is blocked */
!     insert_ob_in_map(op,op->map,op);
  }
  
  
--- 1591,1629 ----
  
          tmp1->x = op->x; tmp1->y = op->y;
          insert_ob_in_map(tmp1,op->map,op);
+         if (was_destroyed (op, op_tag))
+             return;
      } /* end addition.  */
  
!     new_x = op->x + DIRX(op);
!     new_y = op->y + DIRY(op);
!     if (out_of_map (op->map, new_x, new_y)) {
!         remove_ob (op);
!         free_object (op);
!         return;
      }
  
!     if ( ! op->direction || wall (op->map, new_x, new_y)) {
!         if (op->other_arch) {
!             explode_object (op);
!         } else {
!             remove_ob (op);
!             free_object (op);
!         }
!         return;
      }
  
!     remove_ob (op);
!     op->x = new_x;
!     op->y = new_y;
!     if ((op = insert_ob_in_map (op, op->map, op)) == NULL)
!         return;
!     if (reflwall (op->map, op->x, op->y)) {
!         op->direction = absdir (op->direction + 4);
!         update_turn_face (op);
!     } else {
!         check_fired_arch (op);
!     }
  }
  
  
***************
*** 1683,1727 ****
  /*  peterm: ball lightning mover.  */
  /*  ball lightning automatically seeks out a victim, if
      it sees any monsters close enough.  */
- 
  void move_ball_lightning(object *op) {
!     int i,nx,ny,tx,ty,j,dam_save;
      object *owner;
  	 
      owner = get_owner(op);
-     remove_ob(op);
  
  	 /* Only those attuned to PATH_ELEC may use ball lightning with AT_GODPOWER */
!     if((!(owner->path_attuned & PATH_ELEC))&& (op->attacktype & AT_GODPOWER)) {
        free_object(op);
        new_draw_info_format(NDI_UNIQUE,0,owner,"The ball lightning dispells immediately.  Perhaps you need attunement to the spell path?");
        return;
      }
  
!     nx=op->x+DIRX(op);
!     ny=op->y+DIRY(op);
!     ty=op->y;
!     tx=op->x;  /*  the following logic makes sure that the ball
! 		    doesn't move into a wall, and makes
! 		    sure that it will move along a wall to try and
! 		    get at it's victim.  */
!     if(!wall(op->map, nx, ny)&&!blocks_view(op->map,nx,ny)) {
! 	tx=nx;
! 	ty=ny;
      }
!     else
!     {  i=RANDOM()%2;
! 	if(i) {
! 	if(!wall(op->map,op->x,ny)&&!blocks_view(op->map,op->x,ny)) ty=ny;
! 	else if(!wall(op->map,nx,op->y)&&!blocks_view(op->map,nx,op->y)) tx=nx;
! 	}
! 	else {
! 	if(!wall(op->map,nx,op->y)&&!blocks_view(op->map,nx,op->y)) tx=nx;
! 	else if(!wall(op->map,op->x,ny)&&!blocks_view(op->map,op->x,ny)) ty=ny;
! 	}
      }
!     op->y=ty;
!     op->x=tx;
      insert_ob_in_map(op,op->map,op);
  	 
      dam_save = op->stats.dam;  /* save the original dam: we do halfdam on 
--- 1676,1725 ----
  /*  peterm: ball lightning mover.  */
  /*  ball lightning automatically seeks out a victim, if
      it sees any monsters close enough.  */
  void move_ball_lightning(object *op) {
!     int i,nx,ny,tx,ty,j,dam_save,dir;
      object *owner;
  	 
      owner = get_owner(op);
  
  	 /* Only those attuned to PATH_ELEC may use ball lightning with AT_GODPOWER */
!     if(owner && (!(owner->path_attuned & PATH_ELEC))&& (op->attacktype & AT_GODPOWER)) {
!       remove_ob(op);
        free_object(op);
        new_draw_info_format(NDI_UNIQUE,0,owner,"The ball lightning dispells immediately.  Perhaps you need attunement to the spell path?");
        return;
      }
+ 
+     /*  the following logic makes sure that the ball
+ 	doesn't move into a wall, and makes
+ 	sure that it will move along a wall to try and
+ 	get at it's victim.  */
+     dir = 0;
  
!     if(!(RANDOM() %4))
!       j = RANDOM() %2;
!     for(i = 1; i < 9; i++) {
!       /* i bit 0: alters sign of offset
!        * otther bits (i / 2): absolute value of offset
!        */
! 
!       int offset = ((i ^ j) & 1) ? (i / 2) : - (i / 2);
!       int tmpdir = absdir (op->direction + offset);
!       nx = op->x + freearr_x[tmpdir];
!       ny = op->y + freearr_y[tmpdir];
!       if ( ! wall (op->map, nx, ny) && ! blocks_view (op->map, nx, ny)) {
! 	dir = tmpdir;
! 	break;
!       }
      }
!     if (dir == 0) {
!       nx = op->x;
!       ny = op->y;
      }
! 
!     remove_ob(op);
!     op->y=ny;
!     op->x=nx;
      insert_ob_in_map(op,op->map,op);
  	 
      dam_save = op->stats.dam;  /* save the original dam: we do halfdam on 
***************
*** 1731,1751 ****
      for(j=0;j<9;j++) {
        int hx,hy;  /* hit these squares */
  
!       hx = tx+freearr_x[j]; hy = ty+freearr_y[j];
        
        /* first, don't ever, ever hit the owner.  Don't hit out
  	 of the map either.*/
!       if(! (owner->x==hx && owner->y==hy) && !out_of_map(op->map,hx,hy)) {
! 	op->x = hx;
! 	op->y = hy;
  	if(j) op->stats.dam = dam_save/2;
  		  
! 	if(blocked(op->map,op->x,op->y)) hit_map(op,0,op->attacktype);
        }
      }
      /* restore to the center location and damage*/
-     op->y = ty;
-     op->x = tx;
      op->stats.dam = dam_save;
      i=spell_find_dir(op->map,op->x,op->y,get_owner(op));
  
--- 1729,1745 ----
      for(j=0;j<9;j++) {
        int hx,hy;  /* hit these squares */
  
!       hx = nx+freearr_x[j]; hy = ny+freearr_y[j];
        
        /* first, don't ever, ever hit the owner.  Don't hit out
  	 of the map either.*/
!       if(! (owner && owner->x==hx && owner->y==hy) && !out_of_map(op->map,hx,hy)) {
  	if(j) op->stats.dam = dam_save/2;
  		  
! 	if(blocked(op->map,hx,hy)) hit_map(op,j,op->attacktype);
        }
      }
      /* 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));
  
***************
*** 1759,1764 ****
--- 1753,1760 ----
        op->direction=i;
      }
  }
+ 
+ 
  
  /* peterm:
     do LOS stuff for ball lightning.  Go after the closest VISIBLE monster.
Index: server/time.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/server/time.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -c -r1.13 -r1.14
*** server/time.c	2000/11/04 06:40:50	1.13
--- server/time.c	2000/11/06 23:06:47	1.14
***************
*** 1,6 ****
  /*
   * static char *rcsid_time_c =
!  *    "$Id: time.c,v 1.13 2000/11/04 06:40:50 cvs Exp $";
   */
  
  /*
--- 1,6 ----
  /*
   * static char *rcsid_time_c =
!  *    "$Id: time.c,v 1.14 2000/11/06 23:06:47 jec Exp $";
   */
  
  /*
***************
*** 441,469 ****
  }
  
  
! /* stop_arrow() - what to do when a non-living flying object
!  * has to stop. Sept 96 - I added in thrown object code in 
!  * here too. -b.t. 
!  * op is arrow, tmp is what is stopping the arrow (can be NULL)
   */
! 
! void stop_arrow(object *op,object *tmp) {
  
!     if(wall(op->map,op->x,op->y))
! 	op->x-=DIRX(op),op->y-=DIRY(op);
!     if(wall(op->map, op->x, op->y)) {
! 	free_object(op);
! 	return;
      }
  
      if(RANDOM() % 100 < op->stats.food) {
  	/* Small chance of breaking */
  	free_object(op);
! 	return;
      }
  
      op->direction=0;
-     CLEAR_FLAG(op, FLAG_NO_PICK);
      CLEAR_FLAG(op, FLAG_WALK_ON);
      CLEAR_FLAG(op, FLAG_FLY_ON);
      CLEAR_FLAG(op, FLAG_FLYING);
--- 441,519 ----
  }
  
  
! /* stop_item() returns a pointer to the stopped object.  The stopped object
!  * may or may not have been removed from maps or inventories.  It will not
!  * have been merged with other items.
!  *
!  * This function assumes that only items on maps need special treatment.
!  *
!  * If the object can't be stopped, or it was destroyed while trying to stop
!  * it, NULL is returned.
!  *
!  * fix_stopped_item() should be used if the stopped item should be put on
!  * the map.
   */
! object *stop_item (object *op)
! {
!     if (op->map == NULL)
!         return op;
  
!     switch (op->type)
!     {
!     case THROWN_OBJ:
!         {
!             object *payload = op->inv;
!             if (payload == NULL)
!                 return NULL;
!             remove_ob (payload);
!             remove_ob (op);
!             free_object (op);
!             return payload;
!         }
! 
!     case ARROW:
!         if (op->speed >= MIN_ACTIVE_SPEED)
!             op = fix_stopped_arrow (op);
!         return op;
! 
!     case CONE:
!         if (op->speed < MIN_ACTIVE_SPEED) {
!             return op;
!         } else {
!             return NULL;
! 	}
! 
!     default:
!         return op;
      }
+ }
  
+ /* fix_stopped_item() - put stopped item where stop_item() had found it.
+  * Inserts item into the old map, or merges it if it already is on the map.
+  *
+  * 'map' must be the value of op->map before stop_item() was called.
+  */
+ void fix_stopped_item (object *op, mapstruct *map, object *originator)
+ {
+     if (map == NULL)
+         return;
+     if (QUERY_FLAG (op, FLAG_REMOVED))
+         insert_ob_in_map (op, map, originator);
+     else if (op->type == ARROW)
+         merge_ob (op, NULL);   /* only some arrows actually need this */
+ }
+ 
+ 
+ object *fix_stopped_arrow (object *op)
+ {
      if(RANDOM() % 100 < op->stats.food) {
  	/* Small chance of breaking */
+         remove_ob (op);
  	free_object(op);
! 	return NULL;
      }
  
      op->direction=0;
      CLEAR_FLAG(op, FLAG_WALK_ON);
      CLEAR_FLAG(op, FLAG_FLY_ON);
      CLEAR_FLAG(op, FLAG_FLYING);
***************
*** 475,537 ****
      op->stats.sp = 0;
      op->stats.hp = 0;
      op->face=op->arch->clone.face;
- 
-     /* this happens for thrown objects, which 'carry' the
-      * real object in inventory. */ 
-     if(op->inv) {
- 	object *old=op,*new=op->inv;
- 	remove_ob(op->inv);
- 	new->map = old->map; 
- 	free_object(old);
- 	op = new;
-     }
- 
-     /* If the missile hit a player, we insert it in their inventory.
-      * However, if the missile is heavy, we don't do so (assume it falls
-      * to the ground after a hit).  What a good value for this is up to
-      * debate - 5000 is 5 kg, so arrows, knives, and other light weapons
-      * stick around.
-      */
-     if(op->weight <= 5000 && tmp!=NULL&&tmp->stats.hp>=0) {
- 	if(tmp->head != NULL)
- 	    tmp = tmp->head;
- 	op = insert_ob_in_ob(op,tmp);
- 	if (tmp->type== PLAYER)
- 	    esrv_send_item (tmp, op);
-     } else
- 	insert_ob_in_map(op,op->map,op);
      op->owner=NULL; /* So that stopped arrows will be saved */
  }
  
  
  /* Move an arrow along its course.  op is the arrow or thrown object.
   */
  
  void move_arrow(object *op) {
      object *tmp;
  
      if(op->map==NULL) {
! 	LOG(llevDebug,"Arrow had no map.\n");
  	remove_ob(op);
  	free_object(op);
  	return;
      }
  
-     remove_ob(op);
- 
      /* we need to stop thrown objects at some point. Like here. */ 
      if(op->type==THROWN_OBJ) {
  	if(op->last_sp-- < 0) { 
! 	    stop_arrow(op, NULL); 
  	    return; 
  	}
      }
  
  
!     if(wall(op->map,op->x+DIRX(op),op->y+DIRY(op))) {
  	/* if the object doesn't reflect, stop the arrow from moving */
  	if(!QUERY_FLAG(op, FLAG_REFLECTING) || !(RANDOM()%20)) {
! 	    stop_arrow(op,NULL);
  	    return;
  	} else {    /* object is reflected */
  	    /* If one of the major directions (n,s,e,w), just reverse it */
--- 525,633 ----
      op->stats.sp = 0;
      op->stats.hp = 0;
      op->face=op->arch->clone.face;
      op->owner=NULL; /* So that stopped arrows will be saved */
+     update_object (op);
+     return op;
  }
  
+ /* stop_arrow() - what to do when a non-living flying object
+  * has to stop. Sept 96 - I added in thrown object code in 
+  * here too. -b.t. 
+  *
+  * Returns a pointer to the stopped object (which will have been removed
+  * from maps or inventories), or NULL if was destroyed.
+  */
+ 
+ static void stop_arrow (object *op)
+ {
+     if (op->inv) {
+ 	object *payload = op->inv;
+ 	remove_ob (payload);
+         insert_ob_in_map (payload, op->map, payload);
+         remove_ob (op);
+ 	free_object (op);
+     } else {
+         op = fix_stopped_arrow (op);
+         if (op)
+             merge_ob (op, NULL);
+     }
+ }
  
  /* Move an arrow along its course.  op is the arrow or thrown object.
   */
  
  void move_arrow(object *op) {
      object *tmp;
+     sint16 new_x, new_y;
+     int was_reflected;
  
      if(op->map==NULL) {
! 	LOG (llevError, "BUG: Arrow had no map.\n");
  	remove_ob(op);
  	free_object(op);
  	return;
      }
  
      /* we need to stop thrown objects at some point. Like here. */ 
      if(op->type==THROWN_OBJ) {
+         if (op->inv == NULL)
+             return;
  	if(op->last_sp-- < 0) { 
! 	    stop_arrow (op);
  	    return; 
  	}
      }
  
+     /* Calculate target map square */
+     new_x = op->x + DIRX(op);
+     new_y = op->y + DIRY(op);
+     was_reflected = 0;
+ 
+     /* See if there is any living object on target map square */
+     tmp = out_of_map (op->map, new_x, new_y)
+             ? NULL : get_map_ob (op->map, new_x, new_y);
+     while (tmp != NULL && ! QUERY_FLAG (tmp, FLAG_ALIVE))
+ 	tmp = tmp->above;
+ 
+     if (tmp != NULL)
+     {
+         /* Found living object, but it is reflecting the missile.  Update
+          * as below.
+          */
+         if (QUERY_FLAG (tmp, FLAG_REFL_MISSILE))
+         {
+             int number = op->face->number;
+ 
+             op->direction = absdir (op->direction + 4);
+             op->state = 0;
+             if (GET_ANIM_ID (op)) {
+                 number += 4;
+                 if (number > GET_ANIMATION (op, 8))
+                     number -= 8;
+                 op->face = &new_faces[number];
+             }
+             if (wall (op->map, new_x, new_y)) {
+                 /* Target is standing on a wall.  Let arrow turn around before
+                  * the wall. */
+                 new_x = op->x;
+                 new_y = op->y;
+             }
+             was_reflected = 1;   /* skip normal movement calculations */
+         }
+         else
+         {
+             /* Attack the object. */
+             op = hit_with_arrow (op, tmp);
+             if (op == NULL)
+                 return;
+         }
+     }
  
!     if ( ! was_reflected && wall (op->map, new_x, new_y))
!     {
  	/* if the object doesn't reflect, stop the arrow from moving */
  	if(!QUERY_FLAG(op, FLAG_REFLECTING) || !(RANDOM()%20)) {
! 	    stop_arrow (op);
  	    return;
  	} else {    /* object is reflected */
  	    /* If one of the major directions (n,s,e,w), just reverse it */
***************
*** 566,572 ****
  		else if(!right)
  		    op->direction=absdir(op->direction+1);
  		else {		/* is this possible? */
! 		    stop_arrow(op,NULL);
  		    return;
  		}
  	    }
--- 662,668 ----
  		else if(!right)
  		    op->direction=absdir(op->direction+1);
  		else {		/* is this possible? */
! 		    stop_arrow (op);
  		    return;
  		}
  	    }
***************
*** 577,624 ****
  	} /* object is reflected */
      } /* object ran into a wall */
  
!     op->x+=DIRX(op),op->y+=DIRY(op);
!     tmp=get_map_ob(op->map,op->x,op->y);
! 
!     /* See if there is any living object on this space */
!     while(tmp!=NULL&&!QUERY_FLAG(tmp, FLAG_ALIVE))
! 	tmp=tmp->above;
! 
!     /* Nothing alive?  Insert arrow and return */
!     if(tmp==NULL) {
! 	insert_ob_in_map(op,op->map,op);
! 	return;
!     }
! 
!     /* Found living object, but it is reflecting the missile.  Update
!      * as below.
!      */
!     if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE)) {
! 	int number = op->face->number;
! 
! 	op->direction=absdir(op->direction+4),op->state=0;
! 	if(GET_ANIM_ID(op)) {
! 	    number+=4;
! 	    if(number > GET_ANIMATION(op, 8))
! 		number-=8;
! 	    op->face = &new_faces[number];
! 	}
! 	insert_ob_in_map(op,op->map,op);
! 	return;
!     }
! 
!     /* Attach the object.  IF successful, stop the arrow */
!     if(attack_ob(tmp,op))
! 	stop_arrow(op,tmp);
!     else {
! 	/* if we miss, insert it back into the map.  If no direction,
! 	 * stop the arrow.
! 	 */
! 	if(op->direction)
! 	    insert_ob_in_map(op,op->map,op);
! 	else 
! 	    stop_arrow(op,NULL);
!     }
  }
  
  /* This routine doesnt seem to work for "inanimate" objects that
--- 673,683 ----
  	} /* object is reflected */
      } /* object ran into a wall */
  
!     /* Move the arrow. */
!     remove_ob (op);
!     op->x = new_x;
!     op->y = new_y;
!     insert_ob_in_map (op, op->map, op);
  }
  
  /* This routine doesnt seem to work for "inanimate" objects that
Index: socket/item.c
===================================================================
RCS file: /home/cvs/CVS/crossfire/socket/item.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -c -r1.6 -r1.7
*** socket/item.c	2000/11/03 05:42:56	1.6
--- socket/item.c	2000/11/06 23:06:48	1.7
***************
*** 1,7 ****
  
  /*
   * static char *rcsid_item_c =
!  *    "$Id: item.c,v 1.6 2000/11/03 05:42:56 cvs Exp $";
   */
  
  /*
--- 1,7 ----
  
  /*
   * static char *rcsid_item_c =
!  *    "$Id: item.c,v 1.7 2000/11/06 23:06:48 jec Exp $";
   */
  
  /*
***************
*** 710,717 ****
  	}
  	return;
      } else if (to == pl->count) {     /* pick it up to the inventory */
- 	/* pick_up_object (pl, pl, op, nrof);*/
- 	/* Using pick_up will cause objects to get put into containers. */
  	pl->contr->count = nrof;
  	pick_up(pl, op);
  	return ;
--- 710,715 ----

    
    


More information about the crossfire mailing list