/*
 * static char *rcsid_object_c =
 *   "$Id: loader.c,v 1.38 1997/11/11 10:48:34 master Exp $";
 */

/*
    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 <global.h>
#include <loader.h>

/* All the variable names.  Each entry should correspond to the 
 * V_ vlaue in loader.h (that is, the 20'th value in this array should
 * correspond to the 20'th V_(value) in loader.h)  The V_??? get
 * used as indexes into this array.
 */

/* If you change this list (add/delete/rename), also update the list
 * in crossedit/Attr.c, around line 360.  That list is the variables
 * that can be set in crossedit for various objects.
 */
static char *variable_const[NR_OF_VARIABLES] = {
  "Object","name","race","slaying","msg","endmsg",
  "Inventory","arch","other_arch","More",
  "anim","mina","end","last_heal","last_sp","last_grace","last_eat",
  "speed","speed_left","slow_move",
  "face","Str","Dex","Con","Wis","Cha","Int","Pow","hp","maxhp","sp","maxsp",
  "grace","maxgrace",
  "exp","food","dam","wc","ac","x","y","nrof","level","direction",
  "type","material","value", "weight","carrying",
  "immune","protected","attacktype","vulnerable",
  "path_attuned","path_repelled","path_denied",
  "invisible","magic","state","alive","applied","unpaid","need_an","need_ie",
  "no_pick","no_pass","walk_on","walk_off","fly_on","fly_off","is_animated",
  "flying","monster",
  "friendly","generator","is_thrown","auto_apply","treasure",
  "apply_once","see_invisible","can_roll","is_turning","is_turnable",
  "is_used_up","identified","reflecting","changing","splitting","hitback",
  "startequip","blocksview","editable","undead","scared","unaggressive",
  "reflect_missile","reflect_spell","no_magic",
  "wiz","was_wiz","no_fix_player","tear_down", "luck",
  "run_away","pass_thru",
  "can_pass_thru","pick_up","anim_speed","container","no_drop",
  "no_pretext","will_apply","random_movement", "can_apply",
  "can_cast_spell","can_use_scroll","can_use_wand","can_use_bow",
  "can_use_armour","can_use_weapon","can_use_ring","has_ready_wand",
  "has_ready_bow","xrays","is_floor","lifesave","no_strength",
  "sleep","stand_still","random_move","only_attack","armour",
  "attack_movement","move_state","confused","stealth","connected",
  "cursed","damned","see_anywhere","known_magical","known_cursed",
  "can_use_skill","been_applied","title","has_ready_rod","can_use_rod",
  "has_ready_horn","can_use_horn","expmul",
  "unique","make_invisible","inv_locked",
  "is_wooded","is_hilly","has_ready_skill","has_ready_weapon",
  "no_skill_ident","glow_radius","is_blind","can_see_in_dark",
  "is_cauldron","randomitems","is_dust", "no_steal",
#ifdef NPC_PROG
  "npc_status","npc_program",
#endif
};

char *variables[NR_OF_VARIABLES];

/* This array equates the FLAG_ values with the V_ values.  Use -1 to
 * put gaps in the array that should not be processed.
 * The order matches the order of the define values in 'define.h'.
 */

int flag_links[NUM_FLAGS+1][2] ={
{FLAG_ALIVE, V_ALIVE}, {FLAG_WIZ, V_WIZ},
{-1, -1}, {-1, -1},	/* REMOVED and FREED flags */
{FLAG_WAS_WIZ, V_WAS_WIZ}, {FLAG_APPLIED, V_APPLIED},
{FLAG_UNPAID, V_UNPAID}, {FLAG_AN, V_NEED_AN},
{FLAG_NO_PICK, V_NO_PICK}, {FLAG_WALK_ON, V_WALK_ON},
{FLAG_NO_PASS, V_NO_PASS},{FLAG_ANIMATE, V_IS_ANIMATED},
{FLAG_SLOW_MOVE, -1}, {FLAG_FLYING, V_FLYING},
{FLAG_MONSTER, V_MONSTER}, {FLAG_FRIENDLY, V_FRIENDLY},
{FLAG_GENERATOR, V_GENERATOR}, {FLAG_IS_THROWN, V_IS_THROWN},
{FLAG_AUTO_APPLY, V_AUTO_APPLY}, {FLAG_TREASURE, V_TREASURE},
{FLAG_APPLY_ONCE, V_APPLY_ONCE},{FLAG_SEE_INVISIBLE, V_SEE_INVISIBLE},
{FLAG_CAN_ROLL, V_CAN_ROLL}, {FLAG_IS_TURNING, V_IS_TURNING}, 
{FLAG_IS_TURNABLE, V_IS_TURNABLE},{FLAG_WALK_OFF, V_WALK_OFF},
{FLAG_FLY_ON, V_FLY_ON},{FLAG_FLY_OFF, V_FLY_OFF}, 
{FLAG_IS_USED_UP, V_IS_USED_UP}, {FLAG_IDENTIFIED, V_IDENTIFIED},
{FLAG_REFLECTING, V_REFLECTING}, {FLAG_CHANGING, V_CHANGING},
{FLAG_SPLITTING, V_SPLITTING}, {FLAG_HITBACK, V_HITBACK},
{FLAG_STARTEQUIP, V_STARTEQUIP}, {FLAG_BLOCKSVIEW, V_BLOCKSVIEW},
{FLAG_UNDEAD, V_UNDEAD}, {FLAG_SCARED, V_SCARED},
{FLAG_UNAGGRESSIVE, V_UNAGGRESSIVE}, {FLAG_REFL_MISSILE, V_REFLECT_MISSILE},
{FLAG_REFL_SPELL, V_REFLECT_SPELL}, {FLAG_NO_MAGIC, V_NO_MAGIC},
{FLAG_NO_FIX_PLAYER, V_NO_FIX_PLAYER}, {FLAG_NEED_IE, V_NEED_IE}, 
{FLAG_TEAR_DOWN, V_TEAR_DOWN}, {FLAG_RUN_AWAY, V_RUN_AWAY},
{FLAG_PASS_THRU, V_PASS_THRU}, {FLAG_CAN_PASS_THRU, V_CAN_PASS_THRU},
{FLAG_PICK_UP, V_PICK_UP}, {FLAG_UNIQUE, V_UNIQUE},
{FLAG_NO_DROP, V_NO_DROP}, {FLAG_NO_PRETEXT, V_NO_PRETEXT},
{FLAG_CAST_SPELL, V_CAN_CAST_SPELL}, {FLAG_USE_SCROLL, V_CAN_USE_SCROLL},
{FLAG_USE_WAND, V_CAN_USE_WAND},{FLAG_USE_BOW, V_CAN_USE_BOW},
{FLAG_USE_ARMOUR, V_CAN_USE_ARMOUR},{FLAG_USE_WEAPON, V_CAN_USE_WEAPON},
{FLAG_USE_RING, V_CAN_USE_RING}, {FLAG_READY_WAND, V_HAS_READY_WAND},
{FLAG_READY_BOW, V_HAS_READY_BOW}, {FLAG_XRAYS, V_XRAYS},
{-1, -1 /*NO_APPLY*/},  {FLAG_IS_FLOOR, V_IS_FLOOR},
{FLAG_LIFESAVE, V_LIFESAVE}, {FLAG_NO_STRENGTH, V_NO_STRENGTH},
{FLAG_SLEEP, V_SLEEP}, {FLAG_STAND_STILL, V_STAND_STILL},
{FLAG_RANDOM_MOVE, V_RANDOM_MOVEMENT}, {FLAG_ONLY_ATTACK, V_ONLY_ATTACK},
{FLAG_CONFUSED, V_CONFUSED}, {FLAG_STEALTH, V_STEALTH},
{-1, -1 /*WIZPASS*/}, {-1, -1 /*IS_LINKED */},
{FLAG_CURSED, V_CURSED}, {FLAG_DAMNED, V_DAMNED},
{FLAG_SEE_ANYWHERE, V_SEE_ANYWHERE}, {FLAG_KNOWN_MAGICAL, V_KNOWN_MAGICAL},
{FLAG_KNOWN_CURSED, V_KNOWN_CURSED}, {FLAG_CAN_USE_SKILL, V_CAN_USE_SKILL},
{FLAG_BEEN_APPLIED, V_BEEN_APPLIED},  {FLAG_READY_ROD, V_HAS_READY_ROD},
{FLAG_USE_ROD, V_CAN_USE_ROD}, {FLAG_READY_HORN, V_HAS_READY_HORN}, 
{FLAG_USE_HORN, V_CAN_USE_HORN},{FLAG_MAKE_INVIS,V_MAKE_INVIS},
{FLAG_INV_LOCKED,V_INV_LOCKED},{FLAG_IS_WOODED,V_IS_WOODED},
{FLAG_IS_HILLY,V_IS_HILLY},{FLAG_READY_SKILL,V_HAS_READY_SKILL},
{FLAG_READY_WEAPON,V_HAS_READY_WEAPON},{FLAG_NO_SKILL_IDENT,V_NO_SKILL_IDENT},
{FLAG_BLIND,V_BLIND},{FLAG_SEE_IN_DARK,V_SEE_IN_DARK},
{FLAG_IS_CAULDRON,V_IS_CAULDRON},{FLAG_DUST,V_DUST},{FLAG_NO_STEAL,V_NO_STEAL}
};

void save_double(char *buf,char *name,double v)
{
  char tbuf[200];

  sprintf(tbuf,"%s %f\n",name,v);
  strcat(buf,tbuf);
}

/*
 * Initialises the array of variable-names.  Needed before any
 * objects can be loaded.  Called by init_library().
 */

void init_vars() {
  int i;
  for(i=0;i<NR_OF_VARIABLES;i++)
    variables[i]=add_string(variable_const[i]);
}

/*
 * Searches through all possible variables to find any that matches
 * the given string.  An index to the variable_const[] array is returned,
 * or -1 on failure.
 */

int get_variable(char *name) {
  int i;
  char *tmp=find_string(name);
  for(i=0;i<NR_OF_VARIABLES;i++)
    if(variables[i]==tmp)
      return i;
  return -1;
}

/*
 * Returns a pointer to a static string which contains all variables
 * which are different in the two given objects.  op is the what object
 * the different values will be taken from.  This function is
 * typically used to dump objects (op2=empty object), or to save objects
 * (op2 is the objects original archetype)
 */

char *get_ob_diff(object *op,object *op2) {/* I plan to optimize this heavily */
  static char buf2[HUGE_BUF];
  static char buf[HUGE_BUF];
  int tmp;

  buf[0]='\0';
  if(op->name && op->name!=op2->name) {
    sprintf(buf2,"name %s\n",op->name);
    strcat(buf,buf2);
  }
  if(op->title && op->title!=op2->title) {
    sprintf(buf2,"title %s\n", op->title);
    strcat(buf, buf2);
  }
  if(op->race && op->race!=op2->race) {
    sprintf(buf2,"race %s\n",op->race);
    strcat(buf,buf2);
  }
  if(op->slaying && op->slaying!=op2->slaying) {
    sprintf(buf2,"slaying %s\n",op->slaying);
    strcat(buf,buf2);
  }
  if(op->msg && op->msg!=op2->msg) {
    strcat(buf,"msg\n");
    strcat(buf,op->msg);
    strcat(buf,"endmsg\n");
  }
  if(op->other_arch!=op2->other_arch&&op->other_arch!=NULL && 
     op->other_arch->name) {
    sprintf(buf2,"other_arch %s\n",op->other_arch->name);
    strcat(buf,buf2);
  }
  if(op->face!=op2->face) {
      sprintf(buf2,"%s %s\n",variable_const[V_FACE], 
		op->face->name);
      strcat(buf,buf2);
  }
  if(op->stats.Str!=op2->stats.Str)
    save_long(buf,variable_const[V_STR],op->stats.Str);
  if(op->stats.Dex!=op2->stats.Dex)
    save_long(buf,variable_const[V_DEX],op->stats.Dex);
  if(op->stats.Con!=op2->stats.Con)
    save_long(buf,variable_const[V_CON],op->stats.Con);
  if(op->stats.Wis!=op2->stats.Wis)
    save_long(buf,variable_const[V_WIS],op->stats.Wis);
  if(op->stats.Pow!=op2->stats.Pow)
    save_long(buf,variable_const[V_POW],op->stats.Pow);
  if(op->stats.Cha!=op2->stats.Cha)
    save_long(buf,variable_const[V_CHA],op->stats.Cha);
  if(op->stats.Int!=op2->stats.Int)
    save_long(buf,variable_const[V_INT],op->stats.Int);
  if(op->stats.hp!=op2->stats.hp)
    save_long(buf,variable_const[V_HP],op->stats.hp);
  if(op->stats.maxhp!=op2->stats.maxhp)
    save_long(buf,variable_const[V_MAXHP],op->stats.maxhp);
  if(op->stats.sp!=op2->stats.sp)
    save_long(buf,variable_const[V_SP],op->stats.sp);
  if(op->stats.maxsp!=op2->stats.maxsp)
    save_long(buf,variable_const[V_MAXSP],op->stats.maxsp);
  if(op->stats.grace!=op2->stats.grace)
    save_long(buf,variable_const[V_GRACE],op->stats.grace);
  if(op->stats.maxgrace!=op2->stats.maxgrace)
    save_long(buf,variable_const[V_MAXGRACE],op->stats.maxgrace);
  if(op->stats.exp!=op2->stats.exp)
    save_long(buf,variable_const[V_EXP],op->stats.exp);
  if(op->expmul!=op2->expmul) 
    save_double(buf,variable_const[V_EXPMUL],op->expmul);
  if(op->stats.food!=op2->stats.food)
    save_long(buf,variable_const[V_FOOD],op->stats.food);
  if(op->stats.dam!=op2->stats.dam)
    save_long(buf,variable_const[V_DAM],op->stats.dam);
  if(op->stats.luck!=op2->stats.luck)
    save_long(buf,variable_const[V_LUCK],op->stats.luck);
  if(op->stats.wc!=op2->stats.wc)
    save_long(buf,variable_const[V_WC],op->stats.wc);
  if(op->stats.ac!=op2->stats.ac)
    save_long(buf,variable_const[V_AC],op->stats.ac);
  if(op->armour!=op2->armour)
    save_long(buf,variable_const[V_ARMOUR],op->armour);
  if(op->x!=op2->x) 
    save_long(buf,variable_const[V_X],op->x);
  if(op->y!=op2->y)
    save_long(buf,variable_const[V_Y],op->y);
  if(op->speed!=op2->speed) {
    sprintf(buf2,"speed %f\n",op->speed);
    strcat(buf,buf2);
  }
  if(op->speed > 0 && op->speed_left!=op2->speed_left) {
    sprintf(buf2,"speed_left %f\n",op->speed_left);
    strcat(buf,buf2);
  }
  if(op->move_status != op2->move_status)
    save_long(buf,variable_const[V_MOVE_STATUS],op->move_status);
  if(op->move_type != op2->move_type)
    save_long(buf,variable_const[V_ATT_MOVE],op->move_type);
  if(op->nrof!=op2->nrof)
    save_long(buf,variable_const[V_NROF],op->nrof);
  if(op->level!=op2->level)
    save_long(buf,variable_const[V_LEVEL],op->level);
  if(op->direction!=op2->direction)
    save_long(buf,variable_const[V_DIRECTION],op->direction);
  if(op->type!=op2->type)
    save_long(buf,variable_const[V_TYPE],op->type);
  if(op->immune!=op2->immune)
    save_long(buf,variable_const[V_IMMUNE],op->immune);
  if(op->protected!=op2->protected)
    save_long(buf,variable_const[V_PROTECTED],op->protected);
  if(op->attacktype!=op2->attacktype)
    save_long(buf,variable_const[V_ATTACKTYPE],op->attacktype);
  if(op->vulnerable!=op2->vulnerable)
    save_long(buf,variable_const[V_VULNERABLE],op->vulnerable);
  if(op->path_attuned!=op2->path_attuned)
    save_long(buf,variable_const[V_PATH_ATTUNED],op->path_attuned);
  if(op->path_repelled!=op2->path_repelled)
    save_long(buf,variable_const[V_PATH_REPELLED],op->path_repelled);
  if(op->path_denied!=op2->path_denied)
    save_long(buf,variable_const[V_PATH_DENIED],op->path_denied);
  if(op->material!=op2->material)
    save_long(buf,variable_const[V_MATERIAL],op->material);
  if(op->value!=op2->value)
    save_long(buf,variable_const[V_VALUE],op->value);
  if(op->carrying!=op2->carrying)
    save_long(buf,variable_const[V_CARRYING],op->carrying);
  if(op->weight!=op2->weight)
    save_long(buf,variable_const[V_WEIGHT],op->weight);
  if(op->invisible!=op2->invisible)
    save_long(buf,variable_const[V_INVISIBLE],op->invisible);
  if(op->state!=op2->state)
    save_long(buf,variable_const[V_STATE],op->state);
  if(op->magic!=op2->magic)
    save_long(buf,variable_const[V_MAGIC],op->magic);
  if(op->last_heal!=op2->last_heal)
    save_long(buf,variable_const[V_LAST_HEAL],op->last_heal);
  if(op->last_sp!=op2->last_sp)
    save_long(buf,variable_const[V_LAST_SP],op->last_sp);
  if(op->last_grace!=op2->last_grace)
    save_long(buf,variable_const[V_LAST_GRACE],op->last_grace);
  if(op->last_eat!=op2->last_eat)
    save_long(buf,variable_const[V_LAST_EAT],op->last_eat);
  if(QUERY_FLAG(op,FLAG_IS_LINKED) && (tmp = get_button_value(op)))
    save_long(buf,variable_const[V_CONNECTED],tmp);
  if(op->glow_radius!=op2->glow_radius) 
    save_long(buf,variable_const[V_GLOW_RADIUS],op->glow_radius);
  if (op->randomitems!=op2->randomitems) {
    sprintf(buf2,"randomitems %s\n",(op->randomitems?op->randomitems->name:"none"));
    strcat(buf,buf2);
  }
#ifdef NPC_PROG
  if(op->npc_status!=op2->npc_status)
    save_long(buf,variable_const[V_NPC_STATUS],op->npc_status);
  if(op->npc_program!=op2->npc_program)
    save_long(buf,variable_const[V_NPC_PROGRAM],op->npc_program);
#endif


/* Eneq(@csd.uu.se): Handles run_away and pick_up */
  if(op->run_away!=op2->run_away)
    save_long(buf,variable_const[V_RUN_AWAY],op->run_away);
  if(op->pick_up!=op2->pick_up)
    save_long(buf,variable_const[V_PICK_UP],op->pick_up);
  if(op->weight_limit!=op2->weight_limit)
    save_long(buf,variable_const[V_CONTAINER],op->weight_limit);


/* Vick (@bern.docs.uu.se) @921107 -> Handle 'will_apply' &
   'random_movement.*/

  if (op->will_apply!=op2->will_apply)
    save_long(buf,variable_const[V_WILL_APPLY],op->will_apply);

/* Mol(@meryl.csd.uu.se) 921108  Handle can_apply */
  if (op->can_apply!=op2->can_apply)
    save_long(buf,variable_const[V_CAN_APPLY],op->can_apply);

  for (tmp=0; tmp <= NUM_FLAGS; tmp++) {
    if ((flag_links[tmp][0]!=-1) && (QUERY_FLAG(op, flag_links[tmp][0]) != 
	QUERY_FLAG(op2, flag_links[tmp][0]))) {
	    if (flag_links[tmp][0]==FLAG_SLOW_MOVE) {
		sprintf(buf2,"%s %f\n",variable_const[V_SLOW_MOVE],SLOW_PENALTY(op));
		strcat(buf,buf2);
	    }
	    else 
	        BUFADD(QUERY_FLAG(op, flag_links[tmp][0]), flag_links[tmp][1]);
	}
  }
  if(buf[0]=='\0')
    return NULL;
  return buf;
}

/*
 * Dumps all variables in an object to a file.
 * If bit 0 of flag is set, unpaid objects will be saved.  As of now,
 * the only place this is not set is when saving the player.
 * If bit 1 of flag is set, don't remove the object after save.  As of now,
 * all of the callers are setting this.
 */

void save_object(FILE *fp,object *op, int flag) {
    archetype *at;
    char *cp;
    object *tmp,*old;

    /* Even if the object does have an owner, it would seem that we should
     * still save it.
     */
    if(op->owner!=NULL || fp == NULL)
	return;

    /* If it is unpaid and we don't want to save those, just return. */
    if(!(flag&1)&&(QUERY_FLAG(op, FLAG_UNPAID))) {
	return;
    }

    if((at=op->arch)==NULL) at=empty_archetype;
    fprintf(fp,"arch %s\n",at->name);

    if((cp=get_ob_diff(op,&at->clone))!=NULL)
	fputs(cp,fp);	/* We really should do some status checking on this */

    /* Eneq(@csd.uu.se): Added this to allow containers being saved with contents*/

    old=NULL;

    if (flag & 2 )
	for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
	    save_object(fp,tmp,flag);

    /* Slightly different logic because tmp/op will be removed by
     * the save_object we call.  So we just keep looking at op->inv
     * until there is nothing left.  In theory, the variable old
     * should not be needed, as recursive loops shouldn't happen.
     */
    else while ((tmp=op->inv)!=NULL) {
	if(old==tmp) {
	    LOG(llevError,"Recursive loop in inventory\n");
	    break;
	}
	save_object(fp,tmp,flag); 
	old=tmp;
    }
   
    if (!(flag&2)) {
	remove_ob(op);
	free_object (op);
    }

    fprintf(fp,"end\n");
}


/*
 * set_variable() expects buf to be a line with two arguments, the first
 * being a variable and the second being the value (which can be anything from
 * an integer to a string). 
 * The object given will have the given variable modified.
 *
 * -1 will be returned if there is no such variable
 * 0 will be returned if the variable is to be ignored
 * 1 will be returned if the variable was "anim"
 * 2 will be returned if the variable was "end"
 * 3 will be returned if the variable was "Inventory"
 * 4 will be returned if the variable was "More"
 * 5 will be returned if the variable was "msg"
 * 6 will be returned if the variable was "endmsg"
 * otherwise 0 will be returned
 */

int set_variable(object *op,char *buf) {
  char *vbp;
  int value,tmp,var;

  if((vbp=strchr(buf,'\n'))!=NULL) /* Change newline into end of string */
    *vbp='\0';
  else
    LOG(llevDebug, "Line too long.\n");
  if((vbp=strchr(buf,' '))==NULL) /* No argument to the variable */
    value=0,vbp=NULL;
  else {
    *vbp='\0',vbp++;
    value=atol(vbp);
  }
  switch(var=get_variable(buf)) {
  case V_ARCH:
    op->arch=find_archetype(vbp);
    if(op->arch!=NULL)
      copy_object(&op->arch->clone,op);
    break;
  case V_OTHER_ARCH:
    op->other_arch=find_archetype(vbp);
    break;
  case V_RACE:
    if(op->race!=NULL)
      free_string(op->race);
    op->race=add_string(vbp);
    break;
  case V_SLAYING:
    if(op->slaying!=NULL)
      free_string(op->slaying);
    op->slaying=add_string(vbp);
    break;
  case V_MSG:
    return 5;
  case V_MSGEND:
    return 6;
  case V_INVENTORY:
    return 3;
  case V_MORE:
    return 4;
  case V_ANIM:
    if(op->arch==NULL) {
      LOG(llevError,"Got animation in object without archetype:\n");
      return 0;
    }
    SET_FLAG(op,FLAG_ANIMATE);
    return 1;
  case V_END:
    return 2;
  case V_OBJECT:
    if(vbp==NULL) {
      LOG(llevError,"Object lacks name.\n");
      return 0;
    }
    if(op->arch!=NULL)
      op->arch->name=add_string(vbp);
    op->name=add_string(vbp);
    break;
  case V_NAME:
    if(*vbp=='\0') {
      LOG(llevError,"name without name\n");
      break;
    }
    if(op->name!=NULL)
      free_string(op->name);
    op->name=add_string(vbp);
    break;
  case V_TITLE:
    if(*vbp=='\0') {
      LOG(llevError, "title without name\n");
      break;
    }
    if(op->title!=NULL)
      free_string(op->title);
    op->title = add_string(vbp);
    break;
  case V_SPEED:
    sscanf(vbp,"%f",&op->speed);
    if (op->speed<0)
      op->speed_left=op->speed_left-RANDOM()%100/100.0;
    update_ob_speed(op);
    break;
  case V_SPEED_LEFT:
    sscanf(vbp,"%f",&op->speed_left);
    break;
  case V_SLOW_MOVE:
    {
      float f;
      sscanf(vbp,"%f",&f);
      SET_SLOW_PENALTY(op,f);
      SET_FLAG(op,FLAG_SLOW_MOVE);
    }
    break;
/* Eneq(@csd.uu.se): Added handleof archetype-field anim_speed */

  case V_ANIM_SPEED:
    op->anim_speed = (unsigned char) value;
    break;
/* kholland @ sunlab.cit.cornell.edu added move_type and status */
  case V_ATT_MOVE:
    op->move_type = (unsigned short) value;
    break;
  case V_MOVE_STATUS:
    op->move_status = (signed long) value;
    break;
/*
 * Eneq(@csd.uu.se): Added handle of container <no> where no is containers
 * weight limit, and no_drop which makes an item undroppable
 */
  case V_CONTAINER:
    op->weight_limit=(signed long) value;
    break;
  case V_FACE:
    op->face = &new_faces[FindFace(vbp,0)];
    break;
  case V_STR:
    op->stats.Str=(signed char) value;
    break;
  case V_DEX:
    op->stats.Dex=(signed char) value;
    break;
  case V_CON:
    op->stats.Con=(signed char) value;
    break;
  case V_WIS:
    op->stats.Wis=(signed char) value;
    break;
  case V_POW:
    op->stats.Pow=(signed char) value;
    break;
  case V_CHA:
    op->stats.Cha=(signed char) value;
    break;
  case V_INT:
    op->stats.Int=(signed char) value;
    break;
  case V_HP:
    op->stats.hp=(signed short) value;
    break;
  case V_MAXHP:
    op->stats.maxhp=(signed short) value;
    break;
  case V_SP:
    op->stats.sp=(signed short) value;
    break;
  case V_MAXSP:
    op->stats.maxsp=(signed short) value;
    break;
  case V_GRACE:
    op->stats.grace=(signed short) value;
    break;
  case V_MAXGRACE:
    op->stats.maxgrace=(signed short) value;
    break;
  case V_EXP:
    op->stats.exp=(signed long) value;
    break;
  case V_EXPMUL:
    sscanf(vbp,"%lf",&(op->expmul));
    break;
  case V_FOOD:
    op->stats.food=(sint16) value;
    break;
  case V_DAM:
    op->stats.dam=(sint16) value;
    break;
  case V_LUCK:
    op->stats.luck=(signed char) value;
    break;
  case V_WC:
    op->stats.wc=(signed char) value;
    break;
  case V_AC:
    op->stats.ac=(signed char) value;
    break;
  case V_ARMOUR:
    op->armour=(signed char) value;
    break;
  case V_X:
    op->x=(signed short) value,
    op->ox=(signed short) value;
    break;
  case V_Y:
    op->y=(signed short) value,
    op->oy=(signed short) value;
    break;
  case V_NROF:
    op->nrof=(unsigned long) value;
    break;
  case V_LEVEL:
    op->level=(signed char) value;
    break;
  case V_DIRECTION:
    op->direction=(signed char) value;
    break;
  case V_TYPE:
    op->type=(unsigned char) value;
    break;
  case V_IMMUNE:
    op->immune=(unsigned int) value;
    break;
  case V_PROTECTED:
    op->protected=(unsigned int) value;
    break;
  case V_ATTACKTYPE:
    op->attacktype=(unsigned int) value;
    break;
  case V_VULNERABLE:
    op->vulnerable=(unsigned int) value;
    break;
  case V_PATH_ATTUNED:
    op->path_attuned=(uint32) value;
    break;
  case V_PATH_REPELLED:
    op->path_repelled=(uint32) value;
    break;
  case V_PATH_DENIED:
    op->path_denied=(uint32) value;
    break;
  case V_MATERIAL:
    op->material=(uint16) value;
    break;
  case V_VALUE:
    op->value=(signed long) value;
    break;
  case V_CARRYING:
    op->carrying=(signed long) value;
    break;
  case V_WEIGHT:
    op->weight=(signed long) value;
    break;
  case V_INVISIBLE:
    op->invisible=(signed short) value;
    break;
  case V_MAGIC:
    op->magic=(unsigned char) value;
    break;
  case V_STATE:
    op->state=(unsigned char) value;
    break;
  case V_LAST_HEAL:
    op->last_heal=(signed short) value;
    break;
  case V_LAST_SP:
    op->last_sp=(signed short) value;
    break;
  case V_LAST_GRACE:
    op->last_grace=(signed short) value;
    break;
  case V_LAST_EAT:
    op->last_eat=(signed short) value;
    break;
  case V_CONNECTED:
    add_button_link(op, op->map, value);
    break;
#ifdef NPC_PROG
  case V_NPC_STATUS:
    op->npc_status=(unsigned short) value;
    break;
  case V_NPC_PROGRAM:
    op->npc_program=(unsigned short) value;
    break;
#endif

  case V_EDITABLE:
    op->arch->editable = value;
    break;

/* Eneq(@csd.uu.se): Handle PICK_UP */

  case V_PICK_UP:
    op->pick_up=(unsigned char) value;
    break;

/* Eneq(@csd.uu.se): Handle RUN_AWAY, (CAN_)PASS_THRU. */

  case V_RUN_AWAY:
    op->run_away = (unsigned short) value;
    break;

/* Vick's (vick@bern.docs.uu.se) patch 921107 : will_apply, which
   enables a monster to apply things on the ground. */

  case V_WILL_APPLY:
    op->will_apply = (unsigned char) value;
    break;

  case V_GLOW_RADIUS:
    op->glow_radius = (signed short) value;
    break;

/* Note: With the new FLAG value method, most all of these bits are
 * set in the default area.  The only ones not set there are
 * special cases (those needing to check the object, print debugging
 * messages, or SET/CLEAR multiple flags.
 */

  case V_WIZ:
    if (value) {
	SET_FLAG(op, FLAG_WIZ);
	SET_FLAG(op, FLAG_WAS_WIZ);
	SET_FLAG(op, FLAG_WIZPASS);
    }
    else {
	CLEAR_FLAG(op, FLAG_WIZ);
	CLEAR_FLAG(op, FLAG_WIZPASS);
    }
    break;

/* Mol(@csd.docs.uu.se) patch 921108 : can_apply, is similair to will_apply,
   but applies thing it has picked up. */

  case V_CAN_APPLY:
    op->can_apply = (unsigned char) value;
    break;

  case V_FRIENDLY:
    if(value) {
      SET_FLAG(op,FLAG_FRIENDLY);
      if (op->type != PLAYER) {
        LOG(llevDebug, "Adding friendly object %s.\n",op->name);
        add_friendly_object(op);
      }
    }
    else
      CLEAR_FLAG(op,FLAG_FRIENDLY);
    break;

  case V_IDENTIFIED:
    if(value) {
      SET_FLAG(op,FLAG_IDENTIFIED);
      CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL);
    }
    else
      CLEAR_FLAG(op,FLAG_IDENTIFIED);
    break;

  case V_WAS_WIZ:
    if(value)
      SET_FLAG(op,FLAG_WAS_WIZ);
    else
      CLEAR_FLAG(op,FLAG_WAS_WIZ);
    break;
  case V_RANDOMITEMS:
    op->randomitems=find_treasurelist(vbp);
    break;
    

 /* In the default statement, it will handle all cases above.
  * First search through the flag_links, to match corresponding
  * variables.  If it doesn't find anything, then print the error messages.
  */
  default:
    for (tmp=0; tmp<=NUM_FLAGS; tmp++) {
	if (flag_links[tmp][1]==var) {
	    if (value) SET_FLAG(op, flag_links[tmp][0]);
	    else CLEAR_FLAG(op, flag_links[tmp][0]);
	    return 0;
	}
    }
    sprintf(errmsg,"Warning, unknown (or oldfashioned) variable: %s",buf);
    return -1;
  }
  return 0;
}

/*
 * Loads an object from the given file-pointer.
 * Variables will be read and parsed and patched into the object
 * until the string "end" is reached, or the end of the file.
 * If EOF is reached, it returnes false, otherwise true.
 */

int load_object(FILE *fp, object *op) {
  char buf[MAX_BUF];
  char msgbuf[HUGE_BUF];
  Fontindex faces[MAX_ANIMATIONS];
  int position=1; /* 1 = standalone, 2 = inventory, 3 = head/tail link */
  int anim_start=0,msg_start=0;
  object *tmp;

  buf[MAX_BUF - 1] = '\0';
  while(fgets(buf,sizeof(buf),fp)!=NULL) {
    if(*buf=='#')
      continue;
    if(anim_start) {
      if(!strncmp("mina",buf,4)) {
        op->arch->animations=anim_start-1,anim_start=0;
        op->arch->faces = (Fontindex *) malloc
	    (sizeof(Fontindex) * (op->arch->animations+1));
        memcpy((void *)op->arch->faces,(void *)faces,
               (op->arch->animations+1)*sizeof(Fontindex));
      } else {
	  faces[anim_start - 1] = FindFace(buf,0);
	  anim_start++;
      }
      continue;
    }
    if(msg_start&&strncmp(buf,"endmsg",6)) {
      strcat(msgbuf,buf);
      continue;
    }
    /* found an arch in the object.  Must be part of the
     * objects inventory.
     */
    if(!strncmp(buf,"arch ",5)&&op->arch!=NULL) {
        object *otmp;

        tmp=get_object();
	set_variable(tmp, buf);
        load_object(fp, tmp);
        if (op->inv!=NULL) {
            for (otmp=op->inv; otmp->below!=NULL; otmp=otmp->below);
            otmp->below=tmp;
        }
	else op->inv=tmp;
        return load_object (fp, op);
    }
    switch(set_variable(op,buf)) {
    case -1:
      LOG(llevError,"%s\n",errmsg);
      break;
    case 1:
      anim_start=1;
      break;
    case 2:
      return position;
    case 3:
      position=2;
      break;
    case 4:
      position=3;
      break;
    case 5:
      msg_start=1;
      msgbuf[0]='\0';
      break;
    case 6:
      msg_start=0;
      op->msg=add_string(msgbuf);
	/* Only reason to worry about this is if we might be in danger
	 * of going beyond the limits of the buffer.
	 */
      if (strlen(op->msg) > (HUGE_BUF/2))
	LOG(llevDebug, "\n\tWarning message length > %d (max allowed=%d): %d\n>%.80s<\n",
	    HUGE_BUF/2, HUGE_BUF,
	    strlen(op->msg),op->msg);
      break;
    }
  }
  return 0;
}


/*
 * Loads an object from the given file-pointer.
 * Variables will be read and parsed and patched into the object
 * until the string "end" is reached, or the end of the file.
 * If EOF is reached, it returnes false, otherwise true.
 */

enum LoadState { unknown, arch, anim, message };

object *LoadObject (FILE *fp, char *inbuf) {
    char buf[256];
    char msgbuf[HUGE_BUF];
    Fontindex faces[MAX_ANIMATIONS];
    int anim_start = 0;
    object *op = NULL;
    enum LoadState state = unknown;
    
   if (inbuf!=NULL) {
     if (strncmp("arch", inbuf, 4))
	LOG (llevError, "parse error in inbuf: %s\n (expecting arch)", inbuf);
      else {
	state = arch;
	op = get_object();
	set_variable (op, inbuf);
      }
   }

    while(fgets(buf,sizeof(buf),fp)!=NULL) {
	if(*buf=='#')
	    continue;

	switch (state) {
	  case unknown:
	    if (strncmp ("arch", buf, 4))
		LOG (llevError, "parse error in: %s\n (expecting arch)", buf);
	    else {
		state = arch;
		op = get_object();
		set_variable (op, buf);
	    }
	    break;

	  case arch:
	    if(!strncmp (buf, "arch ", 5)) {
		object *tmp;
		tmp = LoadObject(fp,buf);
		/* If we don't have an arch or name, chances are the object
		 * is one that has been removed or isn't available.  Just
		 * drop it then (can't really dump it - no information
		 * to show.)
		 */
		if (!tmp->arch && !tmp->name) {
		    LOG(llevDebug,"Dropping null object.\n");
		    free_object(tmp);
		}
		else
		    (void) insert_ob_in_ob (tmp, op);
		break;
	    }

	    switch(set_variable (op, buf)) {
	      case -1:
		LOG(llevError,"%s\n",errmsg);
		break;
		
	      case 0:
		break;

	      case 1:
		state = anim;
		anim_start = 0;
		break;

	      case 2:
		return op;

	      case 5:
		state = message;
		msgbuf[0] = '\0';
		break;

	      default:
		LOG(llevError,"LoadObject()\n");
		break;
	    }       
	    break;

	  case anim:
	    if(!strncmp ("mina", buf, 4)) {
		op->arch->animations = anim_start;
		op->arch->faces = (Fontindex *) malloc (sizeof (Fontindex) * anim_start);
		memcpy(op->arch->faces, faces, anim_start * sizeof (Fontindex));
		state = arch;
		break;
	    }
	    faces[anim_start++] = FindFace(buf,0);
	    break;

	  case message:
	    if(!strncmp (buf, "endmsg", 6)) {
		op->msg = add_string (msgbuf);
		state = arch;
		break;
	    }
	    strcat(msgbuf,buf);
	}
    }
    if (op)
	free_object (op);

    return NULL;
}

