/*
 * static char *rcsid_object_c =
 *   "$Id: object.c,v 1.53 1998/01/05 10:23:49 master Exp master $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1994 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to master@rahul.net
*/

/* Eneq(@csd.uu.se): Added weight-modifiers in environment of objects.
   sub/add_weight will transcend the environment updating the carrying
   variable. */

#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <global.h>
#include <object.h>
#include <funcpoint.h>
#include <skills.h> 

object objarray[STARTMAX]; /* All objects, allocated this way at first */
object *objects;           /* Pointer to the list of used objects */
object *free_objects;      /* Pointer to the list of unused objects */
object *active_objects;	/* List of active objects that need to be processed */

int nroffreeobjects = STARTMAX;  /* How many OBs allocated and free (free) */
int nrofallocobjects = STARTMAX; /* How many OBs allocated (free + used) */

int freearr_x[SIZEOFFREE]=
  {0,0,1,1,1,0,-1,-1,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-2,-2,-2,-1,
   0,1,2,3,3,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1};
int freearr_y[SIZEOFFREE]=
  {0,-1,-1,0,1,1,1,0,-1,-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,
   -3,-3,-3,-3,-2,-1,0,1,2,3,3,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3};
int maxfree[SIZEOFFREE]=
  {0,9,10,13,14,17,18,21,22,25,26,27,30,31,32,33,36,37,39,39,42,43,44,45,
  48,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49};
int freedir[SIZEOFFREE]= {
  0,1,2,3,4,5,6,7,8,1,2,2,2,3,4,4,4,5,6,6,6,7,8,8,8,
  1,2,2,2,2,2,3,4,4,4,4,4,5,6,6,6,6,6,7,8,8,8,8,8};


/* Moved this out of define.h and in here, since this is the only file
 * it is used in.  Also, make it an inline function for cleaner
 * design.
 *
 * Function examines the 2 objects given to it, and returns true if
 * they can be merged together.
 *
 * Note that this function appears a lot longer than the macro it
 * replaces - this is mostly for clarity - a decent compiler should hopefully
 * reduce this to the same efficiency.
 *
 * Check nrof variable *before* calling CAN_MERGE()
 *
 * Improvements made with merge:  Better checking on potion, and also
 * check weight
 */


INLINE int CAN_MERGE(object *ob1, object *ob2) {

    /* A couple quicksanity checks */
    if ((ob1 == ob2) || (ob1->type != ob2->type)) return 0;

    if (ob1->speed != ob2->speed) return 0;
    /* Note sure why the following is the case - either the object has to
     * be animated or have a very low speed.  Is this an attempted monster
     * check?
     */
    if (!QUERY_FLAG(ob1,FLAG_ANIMATE) && FABS((ob1)->speed) > 0.001 ) return 0;

    /* the 0x400000 on flags2 is FLAG_INV_LOCK.  I don't think something
     * being locked in inventory should prevent merging.
     */
    if ((ob1->arch != ob2->arch) || (ob1->stats.sp != ob2->stats.sp) ||
	(ob1->flags[0] != ob2->flags[0]) || (ob1->flags[1] != ob2->flags[1]) ||
	((ob1->flags[2] & ~0x400000) != (ob2->flags[2] & ~ 0x400000)) ||
	(ob1->flags[3] != ob2->flags[3]) || (ob1->magic != ob2->magic) ||
	(ob1->name != ob2->name) || (ob1->title != ob2->title) ||
	(ob1->msg != ob2->msg) || (ob1->stats.food != ob2->stats.food) ||
	(ob1->protected != ob2->protected) || (ob1->immune != ob2->immune) ||
	(ob1->vulnerable != ob2->vulnerable) || (ob1->weight != ob2->weight))
	    return 0;

    switch (ob1->type) {
	case SCROLL:
	    if (ob1->level != ob2->level) return 0;
	    break;
	case POTION:
	    /* This should compare the value of the stats, and not the pointer
	     * itself.  There can be cases were potions seem to loose their
	     * plus
	     */
	    if (memcmp(&ob1->stats,&ob2->stats, sizeof(living))) return 0;
	    break;
    }
    /* Everything passes, must be OK. */
    return 1;
}

/*
 * sum_weight() is a recursive function which calculates the weight
 * an object is carrying.  It goes through in figures out how much
 * containers are carrying, and sums it up.
 */
signed long sum_weight(object *op) {
  signed long sum;
  object *inv;
  for(sum = 0, inv = op->inv; inv != NULL; inv = inv->below) {
    if (inv->inv)
	sum_weight(inv);
    sum += inv->carrying + (inv->nrof ? inv->weight * inv->nrof : inv->weight);
  }
  if (op->type == CONTAINER && op->stats.Str)
    sum = (sum * (100 - op->stats.Str))/100;
  if(op->carrying != sum)
    op->carrying = sum;
  return sum;
}


/*
 * Eneq(@csd.uu.se): Since we can have items buried in a character we need
 * a better check.  We basically keeping traversing up until we can't
 * or find a player.
 */

object *is_player_inv (object *op) { 
    for (;op!=NULL&&op->type!=PLAYER; op=op->env)
      if (op->env==op)
	op->env = NULL;
    return op;
}

/*
 * Used by: Crossedit: dump. Server DM commands: dumpbelow, dump.
 *	Some error messages.
 * The result of the dump is stored in the static global errmsg array.
 */

void dump_object2(object *op) {
  char *cp;
/*  object *tmp;*/

  if(op->arch!=NULL) {
      strcat(errmsg,"arch ");
      strcat(errmsg,op->arch->name?op->arch->name:"(null)");
      strcat(errmsg,"\n");
      if((cp=get_ob_diff(op,&empty_archetype->clone))!=NULL)
	strcat(errmsg,cp);
#if 0
      /* Don't dump player diffs - they are too long, mostly meaningless, and
       * will overflow the buffer.
       * Changed so that we don't dump inventory either.  This may
       * also overflow the buffer.
       */
      if(op->type!=PLAYER && (cp=get_ob_diff(op,&empty_archetype->clone))!=NULL)
        strcat(errmsg,cp);
      for (tmp=op->inv; tmp; tmp=tmp->below)
        dump_object2(tmp);
#endif
      strcat(errmsg,"end\n");
  } else {
      strcat(errmsg,"Object ");
      if (op->name==NULL) strcat(errmsg, "(null)");
      else strcat(errmsg,op->name);
      strcat(errmsg,"\n");
#if 0
      if((cp=get_ob_diff(op,&empty_archetype->clone))!=NULL)
        strcat(errmsg,cp);
      for (tmp=op->inv; tmp; tmp=tmp->below)
        dump_object2(tmp);
#endif
      strcat(errmsg,"end\n");
  }
}

/*
 * Dumps an object.  Returns output in the static global errmsg array.
 */

void dump_object(object *op) {
  if(op==NULL) {
    strcpy(errmsg,"[NULL pointer]");
    return;
  }
  errmsg[0]='\0';
  dump_object2(op);
}

/*
 * This is really verbose...Can be triggered by the P key while in DM mode.
 * All objects are dumped to stderr (or alternate logfile, if in server-mode)
 */

void dump_all_objects() {
  object *op;
  for(op=objects;op!=NULL;op=op->next) {
    dump_object(op);
    fprintf(logfile, "Object %d\n:%s\n", op->count, errmsg);
  }
}

/*
 * animate_object(object) updates the face-variable of an object.
 * If the object is the head of a multi-object, all objects are animated.
 * If the object has the IS_TURNING() flag, that is taken into consideration.
 */

void animate_object(object *op) {
  if(op->arch==NULL) {
    dump_object(op);
    LOG(llevError,"Tried to animate object without archetype:\n%s\n", errmsg);
    return;
  }
  if(!op->arch->animations) {
    LOG(llevError,"Archtype lacks animation.\n");
    dump_arch(op->arch);
  }
  if((unsigned char) ++op->state >=
     (int) (QUERY_FLAG(op,FLAG_IS_TURNING) ? op->arch->animations/2 : op->arch->animations))
    op->state=0;
  op->face=&new_faces[
	op->arch->faces[op->state+(QUERY_FLAG(op,FLAG_IS_TURNING)?
           ((op->head!=NULL?op->head:op)->value?op->arch->animations/2:0):0)]];
  if(op->face==blank_face)
    op->invisible=1;
#if 1
  else if(QUERY_FLAG((&op->arch->clone),FLAG_ALIVE)) {
    if(op->face->number==0) {
      op->invisible=1;
      CLEAR_FLAG(op, FLAG_ALIVE);
    } else {
      op->invisible=0;
      SET_FLAG(op, FLAG_ALIVE);
    }
  }
#endif
  update_object(op);
  if(op->more)
    animate_object(op->more);
}

/*
 * get_nearest_part(multi-object, object 2) returns the part of the
 * multi-object 1 which is closest to the second object.
 * If it's not a multi-object, it is returned.
 */

object *get_nearest_part(object *op,object *pl) {
  object *tmp,*closest;
  int last_dist,i;
  if(op->more==NULL)
    return op;
  for(last_dist=distance(op,pl),closest=op,tmp=op->more;tmp!=NULL;tmp=tmp->more)
    if((i=distance(tmp,pl))<last_dist)
      closest=tmp,last_dist=i;
  return closest;
}

/*
 * Returns the object which has the count-variable equal to the argument.
 */

object *find_object(int i) {
  object *op;
  for(op=objects;op!=NULL;op=op->next)
    if(op->count==i)
      break;
 return op;
}

/*
 * Returns the first object which has a name equal to the argument.
 * Used only by the patch command, but not all that useful.
 * Enables features like "patch <name-of-other-player> food 999"
 */

object *find_object_name(char *str) {
  char *name=add_string(str);
  object *op;
  for(op=objects;op!=NULL;op=op->next)
    if(op->name==name)
      break;
  free_string(name);
  return op;
}

void free_all_object_data() {
    object *op, *next;

    for (op=objects; op!=NULL; op=next) {
	next=op->next;
	if (!op->head && !QUERY_FLAG(op,FLAG_REMOVED))
		remove_ob(op);
	if (!op->head && !QUERY_FLAG(op,FLAG_FREED))
		free_object(op);
    }
#if 0
    /* In theory, we should do this.  In practice, it is fairly difficult
     * because objects are created in groups of 100, and there is no record
     * of those starting pointers.
     */
    for (op=free_objects; op!=NULL; ) {
	next=op->next;
	free(op);
	nrofallocobjects--;
	nroffreeobjects--;
	last=op;
	op=next;
    }
#endif
    LOG(llevDebug,"%d allocated objects, %d free objects, STARMAX=%d\n", 
	nrofallocobjects, nroffreeobjects,STARTMAX);
}

/*
 * Returns the object which this object marks as being the owner.
 * A id-scheme is used to avoid pointing to objects which have been
 * freed and are now reused.  If this is detected, the owner is
 * set to NULL, and NULL is returned.
 * (This scheme should be changed to a refcount scheme in the future)
 */

object *get_owner(object *op) {
  if(op->owner==NULL)
    return NULL;
  if(!QUERY_FLAG(op->owner,FLAG_FREED) && op->owner->count==op->ownercount)
    return op->owner;
  op->owner=NULL,op->ownercount=0;
  return NULL;
}

/*
 * Sets the owner of the first object to the second object.
 * Also checkpoints a backup id-scheme which detects freeing (and reusage)
 * of the owner object.
 * See also get_owner()
 */

void set_owner(object *op, object *owner) {
  if(owner==NULL||op==NULL)
    return;
  /* next line added to allow objects which own objects */ 
  while(owner->owner&&owner!=owner->owner) owner=owner->owner;
    op->owner=owner;

  op->ownercount=owner->count;
  owner->refcount++;

#ifdef ALLOW_SKILLS /* set the pointers in op to inherit owners skill, exp_obj */ 
  if(owner->type==PLAYER&&owner->chosen_skill) {
        op->chosen_skill = owner->chosen_skill;
        op->exp_obj = owner->chosen_skill->exp_obj;

	/* unfortunately, we can't allow summoned monsters skill use
         * because we will need the chosen_skill field to pick the
         * right skill/stat modifiers for calc_skill_exp(). See
         * hit_player() in server/attack.c -b.t. */
	if(QUERY_FLAG(op,FLAG_CAN_USE_SKILL))
	    CLEAR_FLAG(op,FLAG_CAN_USE_SKILL);
	if(QUERY_FLAG(op,FLAG_READY_SKILL))
	    CLEAR_FLAG(op,FLAG_READY_SKILL);

  } else if(op->type!=PLAYER && QUERY_FLAG(op,FLAG_READY_SKILL))
               CLEAR_FLAG(op,FLAG_READY_SKILL);
#endif
}

/*
 * Resets vital variables in an object
 */

void reset_object(object *op) {
  op->name = NULL;
  op->title = NULL;
  op->race = NULL;
  op->slaying = NULL;
  op->msg = NULL;
  clear_object(op);
}
/*
 * clear_object() frees everything allocated by an object, and also
 * clears all variables and flags to default settings.
 */

void clear_object(object *op) {
   int tmp;

  if(op->name!=NULL) {
    free_string(op->name);
    op->name=NULL;
  }
  if(op->title != NULL) {
    free_string(op->title);
    op->title = NULL;
  }
  if(op->race!=NULL) {
    free_string(op->race);
    op->race=NULL;
  }
  if(op->slaying!=NULL) {
    free_string(op->slaying);
    op->slaying=NULL;
  }
  if(op->msg!=NULL) {
    free_string(op->msg);
    op->msg=NULL;
  }
  for (tmp=0; tmp<=(NUM_FLAGS/32); tmp++)
	op->flags[tmp] = 0;
  op->map=NULL;
  op->below=NULL;
  op->above=NULL;
  op->owner=NULL;
  op->refcount=0;
  op->inv=NULL;
  op->env=NULL;
  op->container=NULL;
  op->more=NULL;
  op->head=NULL;
  op->arch=NULL;
  op->other_arch=NULL;
  op->enemy=NULL;
  op->contr = NULL;
  op->sk_list = NULL;
  op->weight=0,op->carrying=0,op->weight_limit=0;
  op->anim_speed=0;
  op->level=0;
  op->value=op->run_away=0;
  op->invisible=0;
  op->last_heal=0,op->last_sp=0,op->last_grace=0,op->last_eat=0;
  op->nrof=0;
  op->ownercount=0;
  op->immune=0,op->protected=0,op->attacktype=0,op->vulnerable=0;
  op->stats.exp=0;
  op->x=0; op->y=0; op->ox=0; op->oy=0;
  op->stats.dam=0,op->stats.wc=0,op->stats.ac=0;
  op->armour=0;
  op->stats.luck=0;
  op->stats.food=0;
  op->stats.sp=op->stats.maxsp=op->stats.hp=op->stats.maxhp=0;
  op->stats.grace=op->stats.maxgrace=0;
  op->expmul=1.0;
  op->direction=0;
  op->glow_radius=0;
#ifdef NPC_PROG
  op->npc_status=0;
  op->npc_program=0;
#endif
  op->stats.Str=op->stats.Dex=op->stats.Con=0;
  op->stats.Wis=op->stats.Cha=op->stats.Int=op->stats.Pow=0;
  op->material=op->magic=op->state=op->type=0;
  op->face = blank_face;

  /* The object should already have been removed from the speed list
   * before this function is called
   */
  op->speed=op->speed_left=0;
  op->pick_up=0;
  op->can_apply=0;
  op->will_apply=0;
  op->move_status = 0;
  op->move_type = 0;
  op->path_repelled = 0;
  op->path_attuned = 0;
  op->path_denied = 0;
#ifdef CASTING_TIME
  op->casting = -1;
  op->spelltype = 0;
  op->spell = NULL;
#endif
  op->randomitems=NULL;
  op->spellitem = NULL;

}

/*
 * copy object first frees everything allocated by the second object,
 * and then copies the contends of the first object into the second
 * object, allocating what needs to be allocated.
 */

void copy_object(object *op2, object *op) {
  int is_freed=QUERY_FLAG(op,FLAG_FREED),is_removed=QUERY_FLAG(op,FLAG_REMOVED);

  if(op->name!=NULL)
    free_string(op->name);
  if(op->title!=NULL)
    free_string(op->title);
  if(op->race!=NULL)
    free_string(op->race);
  if(op->slaying!=NULL)
    free_string(op->slaying);
  if(op->msg!=NULL)
    free_string(op->msg);
  (void) memcpy((void *)((char *) op +offsetof(object,name)),
                (void *)((char *) op2+offsetof(object,name)),
                sizeof(object)-offsetof(object, name));
  if(is_freed)
    SET_FLAG(op,FLAG_FREED);
  if(is_removed)
    SET_FLAG(op,FLAG_REMOVED);
  if(op->name!=NULL)
    add_refcount(op->name);
  if(op->title!=NULL)
    add_refcount(op->title);
  if(op->race!=NULL)
    add_refcount(op->race);
  if(op->slaying!=NULL)
    add_refcount(op->slaying);
  if(op->msg!=NULL)
    add_refcount(op->msg);
  if(op2->speed<0)
    op->speed_left=op2->speed_left-RANDOM()%200/100.0;
  update_ob_speed(op);
}

/*
 * expand_objects() allocates more objects for the list of unused objects.
 * It is called from get_object() if the unused list is empty.
 */

void expand_objects() {
  int i;
  object *new;
  new = (object *) CALLOC(OBJ_EXPAND,sizeof(object));

  if(new==NULL)
    fatal(OUT_OF_MEMORY);
  free_objects=new;
  new[0].prev=NULL;
  new[0].next= &new[1],
  SET_FLAG(&(new[0]), FLAG_REMOVED);
  SET_FLAG(&(new[0]), FLAG_FREED);

  for(i=1;i<OBJ_EXPAND-1;i++) {
    new[i].next= &new[i+1],
    new[i].prev= &new[i-1],
    SET_FLAG(&(new[i]), FLAG_REMOVED);
    SET_FLAG(&(new[i]), FLAG_FREED);
  }
  new[OBJ_EXPAND-1].prev= &new[OBJ_EXPAND-2],
  new[OBJ_EXPAND-1].next=NULL,
  SET_FLAG(&(new[OBJ_EXPAND-1]), FLAG_REMOVED);
  SET_FLAG(&(new[OBJ_EXPAND-1]), FLAG_FREED);

  nrofallocobjects += OBJ_EXPAND;
  nroffreeobjects += OBJ_EXPAND;
}

/*
 * get_object() grabs an object from the list of unused objects, makes
 * sure it is initialised, and returns it.
 * If there are no free objects, expand_objects() is called to get more.
 */

object *get_object() {
  object *op;
  if(free_objects==NULL) {
    expand_objects();
  }
  op=free_objects;
  if(!QUERY_FLAG(op,FLAG_FREED)) {
    LOG(llevError,"Fatal: Getting busy object.\n");
  }
  free_objects=op->next;
  if(free_objects!=NULL)
    free_objects->prev=NULL;
  op->count= ++ob_count;
  op->name=NULL;
  op->title=NULL;
  op->race=NULL;
  op->slaying=NULL;
  op->msg=NULL;
  op->next=objects;
  op->prev=NULL;
  op->active_next = NULL;
  op->active_prev = NULL;
  if(objects!=NULL)
    objects->prev=op;
  objects=op;
  clear_object(op);
  SET_FLAG(op,FLAG_REMOVED);
  nroffreeobjects--;
  return op;
}

/*
 * If an object with the IS_TURNABLE() flag needs to be turned due
 * to the closest player being on the other side, this function can
 * be called to update the face variable, _and_ how it looks on the map.
 */

void update_turn_face(object *op) {
  if(!QUERY_FLAG(op,FLAG_IS_TURNABLE)||op->arch==NULL)
    return;
  op->face=&new_faces[op->arch->faces[op->direction]];
  update_object(op);
}

/*
 * Updates the speed of an object.  If the speed changes from 0 to another
 * value, or vice versa, then add/remove the object from the active list.
 * This function needs to be called whenever the speed of an object changes.
 */

void update_ob_speed(object *op) {
    extern int arch_init;

    /* No reason putting the archetypes objects on the speed list,
     * since they never really need to be updated.
     */

    if (QUERY_FLAG(op, FLAG_FREED) && op->speed) {
	LOG(llevError,"Object %s is freed but has speed.\n", op->name);
#ifdef MANY_CORES
	abort();
#endif
	op->speed = 0;
    }
    if (arch_init) {
	return;
    }
    if (op->speed) {
	/* If already on active list, don't do anything */
	if (op->active_next || op->active_prev || op==active_objects)
	    return;

	op->active_next = active_objects;
	if (op->active_next!=NULL)
		op->active_next->active_prev = op;
	active_objects = op;
    }
    else {
	/* If not on the active list, nothing needs to be done */
	if (!op->active_next && !op->active_prev && op!=active_objects)
	    return;

	if (op->active_prev==NULL) {
	    active_objects = op->active_next;
	    if (op->active_next!=NULL)
		op->active_next->active_prev = NULL;
	}
	else {
	    op->active_prev->active_next = op->active_next;
	    if (op->active_next)
		op->active_next->active_prev = op->active_prev;
	}
	op->active_next = NULL;
	op->active_prev = NULL;
    }
}


/*
 * update_object() updates the array which represents the map.
 * It takes into account invisible objects (and represent squares covered
 * by invisible objects by whatever is below them (unless it's another
 * invisible object, etc...))
 * If the object being updated is beneath a player, the look-window
 * of that player is updated (this might be a suboptimal way of
 * updating that window, though, since update_object() is called _often_)
 */

void update_object(object *op) {
  object *tmp;

  if(op->env!=NULL) {
    tmp=is_player_inv(op->env);
    /* If we are in ericserver mode, I think we should send an update
     * or something.  Ideally, client should handle animations on its
     * own.
     */
    if (tmp && tmp->contr->eric_server<=0)
      (*draw_inventory_faces_func)(tmp);
    return;
  }

 /* Can be null if the player has quit but window still exists. */
  if (op->map->map != NULL)
      update_position (op->map, op->x, op->y);

  if(op->more!=NULL)
    update_object(op->more);
}


/* This updates how the map looks at a specified space.  We use
 * some time saving techniques instead of space - each map has 2 or
 * 3 arrays - one for floors, one for top object, and one for an
 * additional object (if DOUBLE_FLOOR patch).  Changed things around
 * so that we now have a visibility flag, and if that is set, then the
 * highest object with that visibility gets put into the addition object
 * slot.
 */
void update_position (mapstruct *m, int x, int y) {
    object *tmp, *last = NULL;
    unsigned int flags = 0;
    MapLook f=blank_look,floor=blank_look;
#ifdef DOUBLE_FLOOR
    MapLook f1 = blank_look;
#endif

    for (tmp = get_map_ob (m, x, y); tmp; last = tmp, tmp = tmp->above) {

	/* This call is needed in order to update objects the player
	 * is standign in that have animations (ie, grass, fire, etc).
	 * However, it also causes the look window to be re-drawn
	 * 3 times each time the player moves, because many of the
	 * functions the move_player calls eventualy call this.
	 *
	 * Always put the player down for drawing.
	 */
	if (tmp->type==PLAYER) {
	    if (tmp->contr->eric_server<=0)
		(*draw_look_faces_func)(tmp);
	    f.face = tmp->face;
	}
	else if (QUERY_FLAG(tmp,FLAG_IS_FLOOR)) {
#ifdef DOUBLE_FLOOR
	    /* can't see anything that is below the floor, so reset it. */
	    f1=blank_look;
#endif
	    if (!tmp->invisible)
		floor.face = tmp->face;
	}

#ifdef DOUBLE_FLOOR
	else if (f1.face->visibility < tmp->face->visibility && !tmp->invisible)
		f1.face = tmp->face;
#endif
	if (tmp==tmp->above) {
	    LOG(llevError, "Error in structure of map\n");
	    exit (-1);
	}
      
	if (QUERY_FLAG(tmp,FLAG_ALIVE))
	    flags |= P_IS_ALIVE;
	if (QUERY_FLAG(tmp,FLAG_NO_PASS))
	    flags |= P_NO_PASS;
	if (QUERY_FLAG(tmp,FLAG_NO_MAGIC))
	    flags |= P_NO_MAGIC;
	if (QUERY_FLAG(tmp,FLAG_DAMNED))
	    flags |= P_NO_CLERIC;
	if (QUERY_FLAG(tmp,FLAG_PASS_THRU))
	    flags |= P_PASS_THRU;
	if (QUERY_FLAG(tmp,FLAG_BLOCKSVIEW))
	    flags |= P_BLOCKSVIEW;
    }
    if (last && f.face==blank_face) f.face = last->face;

    /* If not using the editor, lets find the top most non
       inivisible face. Stop once we find a floor.*/
    if (!editor) {
	while (last && (last->invisible || !last->face->number) &&
	      !QUERY_FLAG(last, FLAG_IS_FLOOR))
	    last = last->below;

	if (last!=NULL) f.face = last->face;
	else f.face = blank_face;
    }

/* Basically, we want to try and set the floors first, since that some
 * object that comes along later can be put in the top spot.
 */
#ifdef DOUBLE_FLOOR
    if (f.face == f1.face)
	f1.face = blank_face;
    set_map_floor2(m, x, y, &f1);
#endif

    f.flags = flags;
    set_map_floor(m,x,y,&floor);
    set_map (m, x, y, &f);

    return;
}

/*
 * free_object() frees everything allocated by an object, removes
 * it from the list of used objects, and puts it on the list of
 * free objects.  The IS_FREED() flag is set in the object.
 * The object must have been removed by remove_ob() first for
 * this function to succeed.
 */

void free_object(object *ob) {
  object *tmp,*op;
  if(QUERY_FLAG(ob,FLAG_FRIENDLY)) {
    LOG(llevMonster,"Warning: tried to free friendly object.\n");
    (*remove_friendly_object_func)(ob);
  }
  if(QUERY_FLAG(ob,FLAG_FREED)) {
    dump_object(ob);
    LOG(llevError,"Trying to free freed object.\n%s\n",errmsg);
    return;
  }
  if(ob->more!=NULL) {
    free_object(ob->more);
    ob->more=NULL;
  }
  if (ob->inv) {
    if (ob->map==NULL || ob->map->in_memory!=MAP_IN_MEMORY ||
       wall(ob->map,ob->x,ob->y))
    {
      op=ob->inv;
      while(op!=NULL) {
        tmp=op->below;
        remove_ob(op);
        free_object(op);
        op=tmp;
      }
    }
    else {
      op=ob->inv;
      while(op!=NULL) {
        tmp=op->below;
        remove_ob(op);
        if(QUERY_FLAG(op,FLAG_STARTEQUIP)||QUERY_FLAG(op,FLAG_NO_DROP) ||
	  op->type==RUNE)
          free_object(op);
        else {
          op->x=ob->x,op->y=ob->y;
          insert_ob_in_map(op,ob->map); /* Insert in same map as the envir */
        }
        op=tmp;
      }
    }
  }
  /* Remove object from the active list */
  ob->speed = 0;
  update_ob_speed(ob);

  SET_FLAG(ob, FLAG_FREED);
  /* First free the object from the used objects list: */
  if(ob->prev==NULL) {
    objects=ob->next;
    if(objects!=NULL)
      objects->prev=NULL;
  }
  else {
    ob->prev->next=ob->next;
    if(ob->next!=NULL)
      ob->next->prev=ob->prev;
  }
  
  /* Now link it with the free_objects list: */
  ob->prev=NULL;
  ob->next=free_objects;
  if(free_objects!=NULL)
    free_objects->prev=ob;
  free_objects=ob;
  if(ob->name!=NULL) {
    free_string(ob->name);
    ob->name=NULL;
  }
  if(ob->title!=NULL) {
    free_string(ob->title);
    ob->title=NULL;
  }
  if(ob->race!=NULL) {
    free_string(ob->race);
    ob->race=NULL;
  }
  if(ob->slaying!=NULL) {
    free_string(ob->slaying);
    ob->slaying=NULL;
  }
  if(ob->msg!=NULL) {
    free_string(ob->msg);
    ob->msg=NULL;
  }
  nroffreeobjects++;
}

/*
 * count_free() returns the number of objects on the list of free objects.
 */

int count_free() {
  int i=0;
  object *tmp=free_objects;
  while(tmp!=NULL)
    tmp=tmp->next, i++;
  return i;
}

/*
 * count_used() returns the number of objects on the list of used objects.
 */

int count_used() {
  int i=0;
  object *tmp=objects;
  while(tmp!=NULL)
    tmp=tmp->next, i++;
  return i;
}

/*
 * count_active() returns the number of objects on the list of active objects.
 */

int count_active() {
  int i=0;
  object *tmp=active_objects;
  while(tmp!=NULL)
    tmp=tmp->active_next, i++;
  return i;
}

/*
 * sub_weight() recursively (outwards) subtracts a number from the
 * weight of an object (and what is carried by it's environment(s)).
 */

void sub_weight (object *op, signed long weight) {
  while (op != NULL) {
    if (op->type == CONTAINER) {
      weight=(signed long)(weight*(100-op->stats.Str)/100);
    }
    op->carrying-=weight;
    op = op->env;
  }
}

/* remove_ob(op):
 *   This function removes the object op from the linked list of objects
 *   which it is currently tied to.  When this function is done, the
 *   object will have no environment.  If the object previously had an
 *   environment, the x and y coordinates will be updated to
 *   the previous environment.
 *   Beware: This function is called from the editor as well!
 */

void remove_ob(object *op) {
  object *tmp,*last=NULL;
  object *otmp;

  if(QUERY_FLAG(op,FLAG_REMOVED)) {
    dump_object(op);
    LOG(llevError,"Trying to remove removed object.\n%s\n",errmsg);

/* Might as well dump core here.  In most cases, the function calling
 * remove_ob makes assumptions about success, and the validity of the
 * next, above, and below pointers, and a core dump would probably result
 * shortly anyways.
 */
#ifdef MANY_CORES
    abort();
#else
    if(op->map!=NULL && op->map->in_memory == MAP_IN_MEMORY) {
      op->map->need_refresh=1;
      LOG(llevDebug,"Marked map %s for refresh.\n",op->map->path);
    }
#endif
    if(get_map_ob(op->map,op->x,op->y)==op)
    { /* we are at bottom [Smart! -Frank] */
       if (op->below)
	  set_map_ob(op->map,op->x,op->y,op->below);
       else if (op->above)
	  set_map_ob(op->map,op->x,op->y,op->above);
       else 
	  set_map_ob(op->map,op->x,op->y,NULL);
    }
    return; /* Why did you comment this away at DoCS?? It just makes it worse */
  }
  if(op->more!=NULL)
    remove_ob(op->more);
  SET_FLAG(op, FLAG_REMOVED);
  if(op->env!=NULL) {
    if(op->nrof)
      sub_weight(op->env, op->weight*op->nrof);
    else
      sub_weight(op->env, op->weight+op->carrying);
    if ((otmp=is_player_inv(op->env))!=NULL && otmp->contr && 
	!QUERY_FLAG(otmp,FLAG_NO_FIX_PLAYER))
      fix_player(otmp);
    if(op->above!=NULL)
      op->above->below=op->below;
    else
      op->env->inv=op->below;
    if(op->below!=NULL)
      op->below->above=op->above;
    op->x=op->env->x,op->y=op->env->y;
    op->ox=op->x,op->oy=op->y;
    op->map=op->env->map;
    op->above=NULL,op->below=NULL;
    if(otmp && otmp->contr && !otmp->contr->eric_server) {
	    (*draw_inventory_func)(otmp);
    }
    op->env=NULL;
    return;
  }
  if (op->map == NULL) return;
  if(out_of_map(op->map,op->x,op->y))
    op->x=0,op->y=0,op->ox=0,op->oy=0;
  if(op->above!=NULL) {                /* Don't change map, we're not on top */
    op->above->below=op->below;        /* Link above with below */
    if(op->below!=NULL) {              /* Something below us? */
      op->below->above=op->above;      /* Yes, link above with below */
      op->below=NULL;
    }
    else {                             /* Nothing below, tell the floor what */
      if(get_map_ob(op->map,op->x,op->y)!=op) {
        dump_object(op);
        LOG(llevError,"Object squizez another object:\n%s\n",errmsg);
        dump_object(get_map_ob(op->map,op->x,op->y));
        LOG(llevError,"%s\n",errmsg);
      }
      set_map_ob(op->map,op->x,op->y,op->above);  /* goes on above it. */
    }
    op->above=NULL;                      
  }
  else if(op->below!=NULL) {
    op->below->above=NULL,             /* It is now on top */
    op->below=NULL;
  } else {
      if(get_map_ob(op->map,op->x,op->y)!=op) {
        LOG(llevError,"Object squizez another object:\n");
        dump_object(op);
        LOG(llevError,"%s\n",errmsg);
        dump_object(get_map_ob(op->map,op->x,op->y));
        LOG(llevError,"%s\n",errmsg);
      }
    set_map_ob(op->map,op->x,op->y,NULL);           /* No more objects here */
  }
  for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above) {
    /* No point updating the players look faces if he is the object
     * being removed.
     */
    if(tmp->type==PLAYER && tmp!=op) {
	/* If a container that the player is currently using somehow gets
	 * removed (most likely destroyed), update the player view
	 * appropriately.
	 */
	if (tmp->container==op) {
	    CLEAR_FLAG(op, FLAG_APPLIED);
	    tmp->container=NULL;
	}
      (*draw_look_func)(tmp);
    }
    if(QUERY_FLAG(op,FLAG_FLYING)?QUERY_FLAG(tmp,FLAG_FLY_OFF):
      QUERY_FLAG(tmp,FLAG_WALK_OFF))
        (*apply_func)(op,tmp);

    /* Eneq(@csd.uu.se): Fixed this to skip tmp->above=tmp */

    if(tmp->above == tmp)
      tmp->above = NULL;
    last=tmp;
  }  
  if(last==NULL)	/* No more objects present, draw the background */
    set_map(op->map,op->x,op->y,&blank_look);
  else			/* Redraw the object at the top */
    update_object(last);

  if(QUERY_FLAG(op,FLAG_BLOCKSVIEW)	/* Should be harmless from editor, */
#ifdef USE_LIGHTING
     ||(op->glow_radius>0) 
#endif
  ) 
    update_all_los(op->map);	/* first_player is no longer set there */

}

/*
 * merge_ob(op,top):
 *
 * This function goes through all objects below and including top, and
 * merges op to the first matching object.
 * If top is NULL, it is calculated.
 * Returns pointer to object if it succeded in the merge, otherwise NULL
 */

object *merge_ob(object *op, object *top) {
  if(!op->nrof)
    return 0;
  if(top==NULL)
    for(top=op;top!=NULL&&top->above!=NULL;top=top->above);
  for(;top!=NULL;top=top->below) {
    if(top==op)
      continue;
    if (CAN_MERGE(op,top))
    {
      top->nrof+=op->nrof;
      CLEAR_FLAG(top,FLAG_STARTEQUIP);
      op->weight = 0; /* Don't want any adjustements now */
      remove_ob(op);
      free_object(op);
      return top;
    }
  }
  return NULL;
}


/* This is a simple version of the insert_ob_in_map function below. 
 * We do stuff similar to insert_ob_in_map, but don't merge objects
 * or check apply.  This is needed by at least the polymorph function -
 * it expects the next object below it to still be around, and if the
 * object gets merged, it gets removed
 */

void insert_ob_in_map_simple(object *op, mapstruct *m)
{
  object *tmp, *top;

  if(m==NULL) {
    dump_object(op);
    LOG(llevError,"Trying to insert in null-map!\n%s\n",errmsg);
    return;
  }
  if(out_of_map(m,op->x,op->y)) {
    dump_object(op);
    LOG(llevError,"Trying to insert object outside the map.\n%s\n", errmsg);
    return;
  }
  if(!QUERY_FLAG(op,FLAG_REMOVED)) {
    dump_object(op);
    LOG(llevError,"Trying to insert (map) inserted object.\n%s\n", errmsg);
    return;
  }
  if(op->more!=NULL)
    insert_ob_in_map_simple(op->more,m);
  CLEAR_FLAG(op,FLAG_REMOVED);
  op->ox=op->x,op->oy=op->y;
  op->map=m;
  CLEAR_FLAG(op,FLAG_APPLIED); /* hack for fixing F_APPLIED in items of dead people */
  CLEAR_FLAG(op, FLAG_INV_LOCKED);
  CLEAR_FLAG(op, FLAG_NO_STEAL);
  /* If anything below then */
  if((top=get_map_ob(op->map,op->x,op->y))!=NULL) {
    /*
     * There is at least one object present, so we must figure out the order.
     * First figure out what is on top;
     */

    while (top->above != NULL)
      top = top->above;

    tmp = top;

    /*
     * Rule 0: SEE_ANYWHERE() objects stay on top of the top.
     */
    while(tmp != NULL && QUERY_FLAG(tmp,FLAG_SEE_ANYWHERE) &&
       !QUERY_FLAG(op,FLAG_SEE_ANYWHERE))
          top = tmp, tmp = tmp->below;

    /*
     * Rule 1: Alive objects stay on top.
     */
    while(tmp != NULL && QUERY_FLAG(tmp,FLAG_ALIVE) &&
	!QUERY_FLAG(op,FLAG_ALIVE) && !QUERY_FLAG(op,FLAG_FLYING) &&
          !QUERY_FLAG(op,FLAG_SEE_ANYWHERE))
      top = tmp, tmp = tmp->below;

    /*
     * Link the new object in the two-way list:
     */

    if (top == tmp) {
      /*
       * Simple case, insert new object above tmp.
       */
      if (tmp->above != NULL)
        LOG(llevError, "insert_ob_in_map: top == tmp && tmp->above != NULL.\n");
      else {
        tmp->above = op;
        op->below = tmp;
        op->above = (object *) NULL;
      }
    } else {
      /*
       * Not so simple case, insert between top and tmp.
       */
      if (tmp == NULL) { /* Insert at bottom of map */
        top->below = op;
        op->above = top;
        op->below = (object *) NULL;
        set_map_ob(op->map, op->x, op->y, op);
      } else { /* Insert between top and tmp */
        top->below = op;
        op->above = top;
        tmp->above = op;
        op->below = tmp;
      }
    }
  }
  else
    set_map_ob(op->map,op->x,op->y,op);   /* Tell the map that we're here */
  update_object(op);
  /* Only check this if we are the head of the object */
  if (!op->head) {
      check_walk_on(op);
      if(QUERY_FLAG(op,FLAG_FREED))	/* check_walk_on() killed it */
	return;

      /* If we are a multi part object, lets work our way through the check
       * walk on's.  See if the object has been freed after each check.
       */
      for (tmp=op->more; tmp!=NULL; tmp=tmp->more) {
          check_walk_on(op);
          if(QUERY_FLAG(op,FLAG_FREED))	/* check_walk_on() killed it */
		return;
      }

  }
  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)
    switch(tmp->type) {
    case PLAYER:
      (*draw_look_func)(tmp);
      break;
#if 0
    case BUTTON:
    case PEDESTAL:
      if (op!=tmp)
        update_button(tmp);
      break;
#endif
    }
}

/*
 * insert_ob_in_map(op, map):
 * This function inserts the object in the two-way linked list
 * which represents what is on a map.
 * The second argument specifies the map, and the x and y variables
 * in the object about to be inserted specifies the position.
 */

void insert_ob_in_map(object *op,mapstruct *m) {
  object *tmp, *top;

  if(m==NULL) {
    dump_object(op);
    LOG(llevError,"Trying to insert in null-map!\n%s\n",errmsg);
    return;
  }
  if(out_of_map(m,op->x,op->y)) {
    dump_object(op);
    LOG(llevError,"Trying to insert object outside the map.\n%s\n", errmsg);
    return;
  }
  if(!QUERY_FLAG(op,FLAG_REMOVED)) {
    dump_object(op);
    LOG(llevError,"Trying to insert (map) inserted object.\n%s\n", errmsg);
    return;
  }
  if(op->more!=NULL) {
    insert_ob_in_map(op->more,m);
  }
  CLEAR_FLAG(op,FLAG_REMOVED);
  if(op->nrof)
    for(tmp=get_map_ob(m,op->x,op->y);tmp!=NULL;tmp=tmp->above)
      if (CAN_MERGE(op,tmp))
      {
        op->nrof+=tmp->nrof;
        CLEAR_FLAG(op,FLAG_STARTEQUIP);
        remove_ob(tmp);
        free_object(tmp);
      }
  op->ox=op->x,op->oy=op->y;
  op->map=m;
  CLEAR_FLAG(op,FLAG_APPLIED); /* hack for fixing F_APPLIED in items of dead people */
  CLEAR_FLAG(op, FLAG_INV_LOCKED);
  CLEAR_FLAG(op, FLAG_NO_STEAL);
  /* If there are other objects, then */
  if((top=get_map_ob(op->map,op->x,op->y))!=NULL) {
    /*
     * There is at least one object present, so we must figure out the order.
     * First figure out what is on top;
     */

    while (top->above != NULL)
      top = top->above;

    tmp = top;

    /* Re-did this with nested if statements.  In this way, each check does
     * not need to check for the previous state - to me, it makes the code
     * cleaner, and perhaps faster.
     */
    if (!QUERY_FLAG(op, FLAG_SEE_ANYWHERE)) {
      /* See anywhere objects stay on top. */
      while(tmp != NULL && QUERY_FLAG(tmp,FLAG_SEE_ANYWHERE))
          top = tmp, tmp = tmp->below;

      if (!QUERY_FLAG(op, FLAG_ALIVE) && !QUERY_FLAG(op,FLAG_FLYING)) {
	/* Alive objects also stay on to. */
	while(tmp != NULL && QUERY_FLAG(tmp,FLAG_ALIVE))
	  top = tmp, tmp = tmp->below;

        /* Have object 'fall below' other objects that block view, except in
         * cases where the object has a high visibility (high meaning >0)
         */
        if (blocks_view(op->map, op->x, op->y) && (op->face &&
                !op->face->visibility)) {
	  while (tmp != NULL && QUERY_FLAG(tmp, FLAG_BLOCKSVIEW) &&
	    !QUERY_FLAG(op, FLAG_BLOCKSVIEW))
	      top = tmp, tmp = tmp->below;
	}
      }
    }

    /*
     * Link the new object in the two-way list:
     */

    if (top == tmp) {
      /*
       * Simple case, insert new object above tmp.
       */
      if (tmp->above != NULL)
        LOG(llevError, "insert_ob_in_map: top == tmp && tmp->above != NULL.\n");
      else {
        tmp->above = op;
        op->below = tmp;
        op->above = (object *) NULL;
      }
    } else {
      /*
       * Not so simple case, insert between top and tmp.
       */
      if (tmp == NULL) { /* Insert at bottom of map */
        top->below = op;
        op->above = top;
        op->below = (object *) NULL;
        set_map_ob(op->map, op->x, op->y, op);
      } else { /* Insert between top and tmp */
        top->below = op;
        op->above = top;
        tmp->above = op;
        op->below = tmp;
      }
    }
  }
  else
    set_map_ob(op->map,op->x,op->y,op);   /* Tell the map that we're here */
  update_object(op);

  /* Only check this if we are the head of the object */
  if (!op->head) {
      mapstruct *map=op->map;
      check_walk_on(op);
      /* check_walk_on() might killed it.  We need to compare maps because
       * if it was freed and a new map was loaded because it was freed
       * (sacrifice for altar), the object will get recycled in the new map
       * and no longer be free.
       */
      if(QUERY_FLAG(op,FLAG_FREED) || map!=op->map)
	return;

      /* If we are a multi part object, lets work our way through the check
       * walk on's.  See if the object has been freed after each check.
       */
      for (tmp=op->more; tmp!=NULL; tmp=tmp->more) {
          check_walk_on(op);
          if(QUERY_FLAG(op,FLAG_FREED))	/* check_walk_on() killed it */
		return;
      }

  }
  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)
    switch(tmp->type) {
    case PLAYER:
      (*draw_look_func)(tmp);
      break;
/* Commented out for 0.93.4 msw - hopefully, it doesn't break anything.  I 
 * don't think it should - the button stuff should be handled on by the 
 * check_walk_on code above.  In fact, this code has some side effects in that
 * buttons that don't have walk_on set would still be activated by this.
 */
#if 0
    case BUTTON:
    case PEDESTAL:
      if (op!=tmp)
        update_button(tmp);
      break;
#endif
    }
  /* build up linked list of light sources in each map. We do
   * this even for non-dark maps, as down the line we might make
   * a spell/effect which effects the global light of any map.
   * -b.t.
   */
#ifdef USE_LIGHTING
        if(op->glow_radius>0&&light_not_listed(op)) {
		add_light_to_map(op,m);
		update_all_los(m);
	} else if(m->darkness&&(op->glow_radius>0||op->lights)) /* a light moved, check los */
		update_all_los(m);
#endif 

}

/*
 * get_split_ob(ob,nr) splits up ob into two parts.  The part which
 * is returned contains nr objects, and the remaining parts contains
 * the rest (or is removed and freed if that number is 0).
 * On failure, NULL is returned, and the reason put into the
 * global static errmsg array.
 */

object *get_split_ob(object *orig_ob,int nr) {
  object *tmp, *otmp;
  if(orig_ob->nrof<nr) {
    sprintf(errmsg,"There are only %d %ss.",
            orig_ob->nrof?orig_ob->nrof:1, orig_ob->name);
    return NULL;
  }
  tmp=get_object();
  copy_object(orig_ob,tmp);
  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((otmp=is_player_inv (orig_ob->env)) && otmp->contr->eric_server <= 0)
      (*draw_inventory_func)(otmp);
    else if (orig_ob->env == NULL) {
      object *tmp;
      if (orig_ob->map->in_memory!=MAP_IN_MEMORY) {
        strcpy(errmsg, "Tried to split object whose map is not in memory.");
	LOG(llevDebug,
            "Error, Tried to split object whose map is not in memory.\n");
        return NULL;
      }
      for(tmp=get_map_ob(orig_ob->map,orig_ob->x,orig_ob->y);
          tmp!=NULL;tmp=tmp->above)
        if(tmp->type==PLAYER && tmp->contr->eric_server <= 0)
          (*draw_look_func)(tmp);
    }
  }
  tmp->nrof=nr;
  return tmp;
}

/*
 * decrease_ob_nr(object, number) decreases a specified number from
 * the amount of an object.  If the amount reaches 0, the object
 * is subsequently removed and freed.
 */

void 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.
     */
    tmp=is_player_inv (op->env);
    if(tmp!=NULL && tmp->contr->eric_server)
	(*esrv_del_item_func)(tmp->contr->eric_server, op->count);
    remove_ob(op);
    free_object(op);
  }
  else
    if(op->env!=NULL) {
      sub_weight(op->env,op->weight*i);
      tmp=is_player_inv (op->env);
      if(tmp!=NULL) {
	if (tmp->contr->eric_server)
	    (*esrv_send_item_func)(tmp, op);
	else
	    (*draw_inventory_func)(tmp);
      }
    } else {
      for(tmp=op->above;tmp!=NULL;tmp=tmp->above)
        if(tmp->type==PLAYER) 
	  if (tmp->contr->eric_server)
	    (*esrv_send_item_func)(tmp, op);
	  else
	    (*draw_look_func)(tmp);
    }
}

/*
 * add_weight(object, weight) adds the specified weight to an object,
 * and also updates how much the environment(s) is/are carrying.
 */

void add_weight (object *op, signed long weight) {
  while (op!=NULL) {
    if (op->type == CONTAINER) {
      weight=(signed long)(weight*(100-op->stats.Str)/100);
    }
    op->carrying+=weight;
    op=op->env;
  }
}

/*
 * insert_ob_in_ob(op,environment):
 *   This function inserts the object op in the linked list
 *   inside the object environment.
 *
 * Eneq(@csd.uu.se): Altered insert_ob_in_ob to make things picked up enter 
 * the inventory at the last position or next to other objects of the same
 * type.
 * Frank: Now sorted by type, archetype and magic!
 *
 * The function returns now pointer to inserted item, and return value can 
 * be != op, if items are merged. -Tero
 */

object *insert_ob_in_ob(object *op,object *where) {
  object *tmp, *otmp;
  int sort_inventory=0;

  if(!QUERY_FLAG(op,FLAG_REMOVED)) {
    dump_object(op);
    LOG(llevError,"Trying to insert (ob) inserted object.\n%s\n", errmsg);
    return op;
  }
  if(where==NULL) {
    dump_object(op);
    LOG(llevError,"Trying to put object in NULL.\n%s\n", errmsg);
    return op;
  }
  if (where->head) {
    LOG(llevDebug, 
	"Warning: Tried to insert object wrong part of multipart object.\n");
    where = where->head;
  }
  if (op->more) {
    LOG(llevError, "Tried to insert multipart object %s (%d)\n",
        op->name, op->count);
    return op;
  }
  CLEAR_FLAG(op, FLAG_REMOVED);
  if(op->nrof) {
    for(tmp=where->inv;tmp!=NULL;tmp=tmp->below)
      if ( CAN_MERGE(tmp,op) ) {
	/* return the original object and remove inserted object
           (client needs the original object) */
        tmp->nrof += op->nrof;
	/* Weight handling gets pretty funky.  Since we are adding to
	 * tmp->nrof, we need to increase the weight.
	 */
	add_weight (where, op->weight*op->nrof);
        SET_FLAG(op, FLAG_REMOVED);
        free_object(op); /* free the inserted object */
        op = tmp;
        remove_ob (op); /* and fix old object's links */
        CLEAR_FLAG(op, FLAG_REMOVED);
	break;
      }

    /* I assume combined objects have no inventory
     * We add the weight - this object could have just been removed
     * (if it was possible to merge).  calling remove_ob will subtract
     * the weight, so we need to add it in again, since we actually do
     * the linking below
     */
    add_weight (where, op->weight*op->nrof);
  } else
    add_weight (where, (op->weight+op->carrying));

  otmp=is_player_inv(where);
  if (otmp&&otmp->contr!=NULL) {
    if (!QUERY_FLAG(otmp,FLAG_NO_FIX_PLAYER))
      fix_player(otmp);
    sort_inventory = otmp->contr->sort_inventory;
  }

  op->map=NULL;
  op->env=where;
  op->above=NULL;
  op->below=NULL;
  op->x=0,op->y=0;
  op->ox=0,op->oy=0;

#ifdef USE_LIGHTING /* reset the light list and los of the players on the map */
  if(op->glow_radius>0&&where->map)
  {
#ifdef DEBUG_LIGHTS
      LOG(llevDebug, " insert_ob_in_ob(): got %s to insert in map/op\n",
	op->name);
#endif /* DEBUG_LIGHTS */ 
      add_light_to_list(op,where);
      if(light_not_listed(op)) add_light_to_map(op,where->map);
      if(where->map->darkness&&where->map->players) update_all_los(where->map);
  }
#endif /* USE_LIGHTING */ 

  if (where->inv==NULL)
      where->inv=op;
  else {
    tmp = where->inv;
    if (! tmp->invisible || where->type==PLAYER) {
      /*
       * Rule 1: Move to the similar type
       */
      while (tmp->below != NULL &&
             tmp->type != op->type)
        tmp = tmp->below;

/*	LOG(llevDebug,"insert_ob_in_ob: Found same type at %s\n",query_base_name(tmp));*/

      /*
       * Rule 2: Within similar type, sort by archetype
       */
      while (tmp->below != NULL &&
             tmp->below->type == op->type &&
             tmp->arch != op->arch)
        tmp = tmp->below;

/*	LOG(llevDebug,"insert_ob_in_ob: Found same arch at %s\n",query_base_name(tmp));*/
	if (!sort_inventory) {
	    /*
	     * Rule 3: Within similar type & archetype, sort by magic
	     * First check a special case, then scroll down until
	     * we are below items with more magic.
	     */
	    if (tmp->type == op->type &&
		tmp->arch == op->arch &&
		abs(tmp->magic) < abs(op->magic))
			tmp = tmp->above; /* Beware: tmp might be NULL now. */
	    else {
		while (tmp != NULL &&
		   tmp->below != NULL &&
		   tmp->below->type == op->type &&
		   tmp->below->arch == op->arch &&
		   abs(tmp->magic) > abs(op->magic))
			tmp = tmp->below;

		/*
		 * Rule 4: Insert what seems like a similar item below.
		 */
		while (tmp != NULL &&
		   tmp->below != NULL &&
		   tmp->below->type == op->type &&
		   tmp->below->arch == op->arch &&
		   abs(tmp->below->magic) == abs(op->magic))
			tmp = tmp->below;
	    }
	} else { /* If we do sort inventory */
	    /*
	     * Rule 5: Try to sort alphabetically.  This is probably a costly
	     * operation.  It might be worthwhile to store the complete strings
	     * in the object structure
	     */
	    char obuf[MAX_BUF],*nbuf;

	    strcpy(obuf, query_base_name(op));

	    /* We insert after tmp.  As such, we may want to insert before
	     * the first object that we have matched so far, so lets back up
	     */
	    if (tmp->type == op->type &&
		tmp->arch == op->arch &&
		abs(tmp->magic) <= abs(op->magic) &&
		(strcmp(obuf, query_base_name(tmp))<0))
		    tmp = tmp->above; /* Beware: tmp might be NULL now. */
	    else {
		while (tmp != NULL &&
		   tmp->below != NULL &&
		   tmp->below->type == op->type &&
		   tmp->below->arch == op->arch &&
		   abs(tmp->below->magic) == abs(op->magic)) {
			nbuf=query_base_name(tmp->below);
			if (strcmp(obuf,nbuf)>0) {
				tmp = tmp->below;
			}
			else break;
		}
	    }
	} /*end of inventory sort */
    } /* if this is a playr */
    /*
     * Now link the new object into the two-way list, below tmp.
     */
    if (tmp == NULL) {
      op->below = where->inv;
      op->below->above = op;
      where->inv = op;
    } else if (tmp->below != NULL) {
      tmp->below->above = op;
      op->below = tmp->below;
      tmp->below = op;
      op->above = tmp;
    } else {
      tmp->below = op;
      op->above = tmp;
    }
  }
  tmp=is_player_inv(where);
  if (tmp && tmp->contr && tmp->contr->eric_server <= 0) {
      (*draw_inventory_func)(tmp);
  }
  return op;
}

/*
 * Checks if any objects which has the WALK_ON() (or FLY_ON() if the
 * object is flying) flag set, will be auto-applied by the insertion
 * of the object into the map (applying is instantly done).
 * Any speed-modification due to SLOW_MOVE() of other present objects
 * will affect the speed_left of the object.
 */
 /* 4-21-95 added code to check if appropriate skill was readied - this will
  * permit faster movement by the player through this terrain. -b.t.
  */

void check_walk_on(object *op) {
  object *tmp;
  if(QUERY_FLAG(op,FLAG_NO_APPLY))
    return;
  for(tmp=op->below;tmp!=NULL;tmp=tmp->below) {
    if (tmp==tmp->below)	/* if we have a map loop, exit */
       break;
    if(QUERY_FLAG(tmp,FLAG_SLOW_MOVE)&&!QUERY_FLAG(op,FLAG_FLYING)) { 
	if(op->type==PLAYER && op->chosen_skill) { 
             if((QUERY_FLAG(tmp,FLAG_IS_HILLY) && op->contr->shoottype == range_skill 
		&& op->chosen_skill->stats.sp==SK_CLIMBING) 
	        || (QUERY_FLAG(tmp,FLAG_IS_WOODED) && op->contr->shoottype == range_skill
		&& op->chosen_skill->stats.sp==SK_WOODSMAN))
      		    op->speed_left -= (SLOW_PENALTY(tmp)*FABS(op->speed))/4;
             else 
		op->speed_left -= SLOW_PENALTY(tmp)*FABS(op->speed);
	} else 
		op->speed_left -= SLOW_PENALTY(tmp)*FABS(op->speed);
    }
    if(QUERY_FLAG(op,FLAG_FLYING)?QUERY_FLAG(tmp,FLAG_FLY_ON):
      QUERY_FLAG(tmp,FLAG_WALK_ON)) {
#if 0
/* the function that called this (a bit up the line) will draw the
 * window.  So don't do it here.
 */
      if(op->type==PLAYER)
        (*draw_func)(op);
#endif
      if((*apply_func)(op,tmp)==2)
        break;
      }
  }
}

/*
 * present_arch(arch, map, x, y) searches for any objects with
 * a matching archetype at the given map and coordinates.
 * The first matching object is returned, or NULL if none.
 */

object *present_arch(archetype *at, mapstruct *m, int x, int y) {
  object *tmp;
  if(m==NULL || out_of_map(m,x,y)) {
    LOG(llevError,"Present_arch called outside map.\n");
    return NULL;
  }
  for(tmp=get_map_ob(m,x,y); tmp != NULL; tmp = tmp->above)
    if(tmp->arch == at)
      return tmp;
  return NULL;
}

/*
 * present(type, map, x, y) searches for any objects with
 * a matching type variable at the given map and coordinates.
 * The first matching object is returned, or NULL if none.
 */

object *present(unsigned char type,mapstruct *m, int x,int y) {
  object *tmp;
  if(out_of_map(m,x,y)) {
    LOG(llevError,"Present called outside map.\n");
    return NULL;
  }
  for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above)
    if(tmp->type==type)
      return tmp;
  return NULL;
}

/*
 * present_in_ob(type, object) searches for any objects with
 * a matching type variable in the inventory of the given object.
 * The first matching object is returned, or NULL if none.
 */

object *present_in_ob(unsigned char type,object *op) {
  object *tmp;
  for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
    if(tmp->type==type)
      return tmp;
  return NULL;
}

/*
 * present_arch_in_ob(archetype, object) searches for any objects with
 * a matching archetype in the inventory of the given object.
 * The first matching object is returned, or NULL if none.
 */

object *present_arch_in_ob(archetype *at, object *op)  {
  object *tmp;
  for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
    if( tmp->arch == at)
      return tmp;
  return NULL;
}

/*
 * set_cheat(object) sets the cheat flag (WAS_WIZ) in the object and in
 * all it's inventory (recursively).
 * If checksums are used, a player will get set_cheat called for
 * him/her-self and all object carried by a call to this function.
 */

void set_cheat(object *op) {
  object *tmp;
  SET_FLAG(op, FLAG_WAS_WIZ);
  if(op->inv)
    for(tmp = op->inv; tmp != NULL; tmp = tmp->below)
      set_cheat(tmp);
}

/*
 * find_free_spot(archetype, map, x, y, start, stop) will search for
 * a spot at the given map and coordinates which will be able to contain
 * the given archetype.  start and stop specifies how many squares
 * to search (see the freearr_x/y[] definition).
 * It returns a random choice among the alternatives found.
 */

int find_free_spot(archetype *at, mapstruct *m,int x,int y,int start,int stop) {
  int i,index=0;
  static int altern[SIZEOFFREE];
  for(i=start;i<stop;i++) {
    if(!arch_blocked(at,m,x+freearr_x[i],y+freearr_y[i]))
      altern[index++]=i;
    else if(wall(m,x+freearr_x[i],y+freearr_y[i])&&maxfree[i]<stop)
      stop=maxfree[i];
  }
  if(!index) return 0;
  return altern[RANDOM()%index];
}

/*
 * find_first_free_spot(archetype, mapstruct, x, y) works like
 * find_free_spot(), but it will search max number of squares.
 * But it will return the first available spot, not a random choice.
 * Changed 0.93.2: Have it return -1 if there is no free spot available.
 */

int find_first_free_spot(archetype *at, mapstruct *m,int x,int y) {
  int i;
  for(i=0;i<SIZEOFFREE;i++)
    if(!arch_blocked(at,m,x+freearr_x[i],y+freearr_y[i]))
      return i;
  return -1;
}

/*
 * find_dir(map, x, y, exclude) will search some close squares in the
 * given map at the given coordinates for live objects.
 * It will not considered the object given as exlude among possible
 * live objects.
 * It returns the direction toward the first/closest live object if finds
 * any, otherwise 0.
 */

int find_dir(mapstruct *m, int x, int y, object *exclude) {
  int i,max=SIZEOFFREE;
  object *tmp;
  if (exclude && exclude->head)
    exclude = exclude->head;

  for(i=1;i<max;i++) {
    if(wall(m, x+freearr_x[i],y+freearr_y[i]))
      max=maxfree[i];
    else {
      tmp=get_map_ob(m,x+freearr_x[i],y+freearr_y[i]);
      while(tmp!=NULL && ((tmp!=NULL&&!QUERY_FLAG(tmp,FLAG_MONSTER)&&
	tmp->type!=PLAYER) || (tmp == exclude || 
	(tmp->head && tmp->head == exclude))))
	        tmp=tmp->above;
      if(tmp!=NULL)
        return freedir[i];
    }
  }
  return 0;
}

/*
 * distance(object 1, object 2) will return the square of the
 * distance between the two given objects.
 */

int distance(object *ob1,object *ob2) {
  int i;
  i= (ob1->x - ob2->x)*(ob1->x - ob2->x)+
         (ob1->y - ob2->y)*(ob1->y - ob2->y);
  return i;
}

/*
 * find_dir_2(delta-x,delta-y) will return a direction in which
 * an object which has subtracted the x and y coordinates of another
 * object, needs to travel toward it.
 */

int find_dir_2(int x, int y) {
  int q;
  if(!y)
    q= -300*x;
  else
    q=x*100/y;
  if(y>0) {
    if(q < -242)
      return 3 ;
    if (q < -41)
      return 2 ;
    if (q < 41)
      return 1 ;
    if (q < 242)
      return 8 ;
    return 7 ;
  }
  if (q < -242)
    return 7 ;
  if (q < -41)
    return 6 ;
  if (q < 41)
    return 5 ;
  if (q < 242)
    return 4 ;
  return 3 ;
}

/*
 * absdir(int): Returns a number between 1 and 8, which represent
 * the "absolute" direction of a number (it actually takes care of
 * "overflow" in previous calculations of a direction).
 */

int absdir(int d) {
  while(d<1) d+=8;
  while(d>8) d-=8;
  return d;
}

/*
 * dirdiff(dir1, dir2) returns how many 45-degrees differences there is
 * between two directions (which are expected to be absolute (see absdir())
 */

int dirdiff(int dir1, int dir2) {
  int d;
  d = abs(dir1 - dir2);
  if(d>4)
    d = 8 - d;
  return d;
}

/*
 * can_pick(picker, item): finds out if an object is possible to be
 * picked up by the picker.  Returnes 1 if it can be
 * picked up, otherwise 0.
 *
 * Cf 0.91.3 - don't let WIZ's pick up anything - will likely cause
 * core dumps if they do.
 */

int can_pick(object *who,object *item) {
  return /*QUERY_FLAG(who,FLAG_WIZ)||*/
         (item->weight>0&&!QUERY_FLAG(item,FLAG_NO_PICK)&&
	 !QUERY_FLAG(item,FLAG_ALIVE)&&
          (who->type==PLAYER||item->weight<who->weight/3));
}


/*
 * create clone from object to another
 */
object *ObjectCreateClone (object *asrc) {
    object *dst = NULL,*tmp,*src,*part,*prev, *item;

    if(!asrc) return NULL;
    src = asrc;
    if(src->head)
        src = src->head;

    prev = NULL;
    for(part = src; part; part = part->more) {
        tmp = get_object();
        copy_object(part,tmp);
        tmp->x -= src->x;
        tmp->y -= src->y;
        if(!part->head) {
            dst = tmp;
            tmp->head = NULL;
        } else {
            tmp->head = dst;
        }
        tmp->more = NULL;
        if(prev) 
            prev->more = tmp;
        prev = tmp;
    }
    /*** copy inventory ***/
    for(item = src->inv; item; item = item->below) {
	(void) insert_ob_in_ob(ObjectCreateClone(item),dst);
    }

    return dst;
}

/*** end of object.c ***/
