tfe-1.0/area/
tfe-1.0/files/
tfe-1.0/logs/
tfe-1.0/logs/immortal/
tfe-1.0/logs/mob/
tfe-1.0/logs/object/
tfe-1.0/logs/player/
tfe-1.0/logs/room/
tfe-1.0/notes/clans/
tfe-1.0/player/
tfe-1.0/prev/
tfe-1.0/prev/area/
tfe-1.0/prev/player/
tfe-1.0/prev/rooms/
tfe-1.0/rooms/
tfe-1.0/src-gc/
tfe-1.0/src-msvc/
tfe-1.0/src-unix/
tfe-1.0/www/
tfe-1.0/www/html/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "define.h"
#include "struct.h"


/*
 *   LOCAL_CONSTANTS
 */


const char* item_type_name [] = { "other", "light", "scroll", "wand",
  "staff", "weapon", "gem", "spellbook", "treasure", "armor", "potion",
  "reagent", "furniture", "trash", "cross", "container", "lock pick", 
  "drink_container", "key", "food", "money", "key_ring", "boat",
  "corpse", "unused", "fountain", "whistle", "trap",
  "light_perm", "bandage", "bounty", "gate", "arrow", "skin",
  "body_part", "chair", "table", "book", "pipe", "tobacco", "deck_cards",
  "fire", "garrote" };

const char* item_values [] = { "unused",
  "unused | unused | life time | unused",
  "spell | level | duration | unused",
  "spell | level | duration | charges",
  "??",
  "enchantment | damdice | damside | weapon class",
  "hardness | condition",
  "??", "unused",
  "enchantment | armor value | bracer ac | unused",
  "spell | level | duration | unused",
  "charges | unused",
  "??", "??", "??",
  "capacity | container flags | key vnum | unused", 
  "pick modifier | unused",
  "capacity | contains | liquid | poisoned == -1", "??", 
  "food value | cooked? | unused | poisoned == -1",
  "??", "??", "??", "??", "??",
  "required to be -1 | unused | liquid | poisoned == -1",
  "range | unused",
  "trap flags | damdice | damside | unused",
  "unused | unused | lifetime | unused", "unused",
  "unused", "lifetime | unused", "unused | damdice | damside | unused",
  "unused", "unused",
  "seats | unused", "unused", "unused",
  "unused", "unused", "unused",
  "timer | next item | unused" };

const char *cont_flag_name [] = { "closeable", "pickproof", "closed",
  "locked", "holding" };


/*
 *   OBJ_CLSS_DATA CLASS
 */


Obj_Clss_Data :: Obj_Clss_Data( )
{
  record_new( sizeof( obj_clss_data ), MEM_OBJ_CLSS );

  oprog        = NULL;
  date         = -1;
  count        = 0;
}


Obj_Clss_Data :: ~Obj_Clss_Data( )
{
  record_delete( sizeof( obj_clss_data ), MEM_OBJ_CLSS );
}


/*
 *   OBJ_DATA
 */


int compare_vnum( obj_data* obj1, obj_data* obj2 )
{
  int a = obj1->pIndexData->vnum;
  int b = obj2->pIndexData->vnum;

  return( a < b ? -1 : ( a > b ? 1 : 0 ) );
}


Obj_Data :: Obj_Data( obj_clss_data* obj_clss )
{
  record_new( sizeof( obj_data ), MEM_OBJECT );

  valid         = OBJ_DATA;
  extra_descr   = NULL;
  array         = NULL;
  save          = NULL;
  owner         = NULL;
  source        = empty_string;
  label         = empty_string;
  position      = WEAR_NONE;
  layer         = 0;
  pIndexData    = obj_clss;

  insert( obj_list, this, 
    binary_search( obj_list, this, compare_vnum ) );
}


Obj_Data :: ~Obj_Data( )
{
  record_delete( sizeof( obj_data ), MEM_OBJECT );
  obj_list -= this;
}


/*
 *   SUPPORT FUNCTIONS
 */


bool can_extract( obj_clss_data* obj_clss, char_data* ch )
{
  custom_data*  custom;
  shop_data*      shop;
 
  for( int i = 0; i < obj_list; i++ ) 
    if( obj_list[i]->pIndexData == obj_clss ) {
      send( ch, "You must destroy all examples of %s first.\n\r",
        obj_clss );
      return FALSE;
      }

  if( has_reset( obj_clss ) ) { 
    send( ch, "You must remove all resets of that object.\n\r" );
    return FALSE;
    }

  for( shop = shop_list; shop != NULL; shop = shop->next ) 
    for( custom = shop->custom; custom != NULL; custom = custom->next ) {
      if( custom->item == obj_clss ) {
        send( ch, "A custom in room %d creates that item.\n\r",
          shop->room->vnum );
        return FALSE;
        }
      for( int i = 0; i < MAX_INGRED; i++ ) 
        if( custom->ingred[i] == obj_clss ) {
          send( ch,
            "A custom in room %d requires that item as an ingrediant.\n\r",
            shop->room->vnum );
          return FALSE;
	  }
      }

  return TRUE;
}        
  

/*
 *   LOW LEVEL OBJECT ROUTINES
 */


bool is_same( obj_data* obj1, obj_data* obj2 )
{
  obj_array*  array;

  if( obj1->pIndexData != obj2->pIndexData )
    return FALSE;

  if(  !is_empty( obj1->contents ) || !is_empty( obj2->contents )
    || !is_empty( obj1->affected ) || !is_empty( obj2->affected )
    || !is_empty( obj1->events )   || !is_empty( obj2->events )
    || obj1->extra_flags[0] != obj2->extra_flags[0] 
    || obj1->extra_flags[1] != obj2->extra_flags[1] 
    || obj1->timer != obj2->timer
    || obj1->condition != obj2->condition 
    || obj1->materials != obj2->materials
    || obj1->rust != obj2->rust
    || obj1->owner != obj2->owner
    || obj1->weight != obj2->weight )
    return FALSE;

  for( int i = 0; i < 4; i++ )
    if( obj1->value[i] != obj2->value[i] )
      return FALSE;

  if( obj2->save != NULL ) {
    if( obj1->save == NULL ) {
      obj1->save = obj2->save;
      obj2->save = NULL;
      array      = &obj1->save->save_list;
      for( int i = 0; ; i++ ) {
        if( i >= array->size ) 
          panic( "Is_Same: Object not in save array." );       
        if( obj2 == array->list[i] ) {
          array->list[i] = obj1;
          break;
	  }
        }
      }
    else if( obj1->save != obj2->save )
      return FALSE;
    }

  return( !strcmp( obj1->label, obj2->label ) );
}


/*
 *   OWNERSHIP
 */


bool Obj_Data :: Belongs( char_data* ch )
{
  return( owner == NULL || ( ch != NULL && ch->pcdata != NULL
    && ch->pcdata->pfile == owner ) );
}


void set_owner( pfile_data* pfile, thing_array& array )
{
  obj_data* obj;

  for( int i = 0; i < array; i++ ) {
    if( ( obj = object( array[i] ) ) != NULL ) {
      if( obj->owner == NULL
        && obj->pIndexData->item_type != ITEM_MONEY )
        obj->owner = pfile; 
      set_owner( pfile, obj->contents );
      }
    }
}


void set_owner( obj_data* obj, pfile_data* buyer )
{
  obj_data* content;

  if( obj->pIndexData->item_type != ITEM_MONEY )
    obj->owner = buyer;

  for( int i = 0; i < obj->contents; i++ )
    if( ( content = object( obj->contents[i] ) ) != NULL )
      set_owner( content, buyer );
}

  
void set_owner( obj_data* obj, char_data* buyer, char_data* seller )
{
  obj_data* content;

  if( obj->Belongs( seller ) && obj->pIndexData->item_type != ITEM_MONEY )
    obj->owner = ( ( buyer != NULL && buyer->pcdata != NULL )
      ? buyer->pcdata->pfile : NULL );

  for( int i = 0; i < obj->contents; i++ )
    if( ( content = object( obj->contents[i] ) ) != NULL ) 
      set_owner( content, buyer, seller );
}


/*
 *   MISC ROUTINES
 */


void condition_abbrev( char* tmp, obj_data* obj, char_data* ch )
{
  const char* abbrev [] = { "wls", "dmg", "vwn", "wrn", "vsc", "scr",
    "rea", "goo", "vgo", "exc" };

  int i;

  i = 1000*obj->condition/obj->pIndexData->durability; 
  i = range( 0, i/100, 9 );

  sprintf( tmp, "%s%s%s", i > 4 ? ( i > 7 ? blue( ch ) : green( ch ) ) 
    : ( i > 2 ? yellow( ch ) : red( ch ) ), abbrev[i], normal( ch ) );

  return;
}


void age_abbrev( char* tmp, obj_data*, char_data* )
{
  sprintf( tmp, "   " );

  return;
}


const char* obj_data :: condition_name( char_data* ch, bool ansi )
{
  static char  tmp  [ ONE_LINE ];
  int            i;
  const char*  txt;

  i = 1000*condition/pIndexData->durability; 
  i = range( 0, i/100, 9 );

       if( i == 0 ) txt = "worthless";
  else if( i == 1 ) txt = "damaged";
  else if( i == 2 ) txt = "very worn";
  else if( i == 3 ) txt = "worn";
  else if( i == 4 ) txt = "very scratched";
  else if( i == 5 ) txt = "scratched";
  else if( i == 6 ) txt = "reasonable";
  else if( i == 7 ) txt = "good";
  else if( i == 8 ) txt = "very good";
  else              txt = "excellent";

  if( !ansi || ch->pcdata == NULL || ch->pcdata->terminal != TERM_ANSI )
    return txt;

  sprintf( tmp, "%s%s%s", i > 4 ? ( i > 7 ? blue( ch ) : green( ch ) ) 
    : ( i > 2 ? yellow( ch ) : red( ch ) ), txt, normal( ch ) );

  return tmp;
}


/*
 *   WEIGHT/NUMBER ROUTINES
 */


int Obj_Data :: Cost( )
{
  int   cost  = pIndexData->cost;
  int      i;

  if( ( pIndexData->item_type == ITEM_WEAPON
    || pIndexData->item_type == ITEM_ARMOR )
    && is_set( extra_flags, OFLAG_IDENTIFIED ) ) {
    if( value[0] < 0 )
      return 0;
    cost += cost*sqr( value[0] )/2;
    }

  if( ( pIndexData->item_type != ITEM_WEAPON
    && pIndexData->item_type != ITEM_ARMOR )
    || !is_set( pIndexData->extra_flags, OFLAG_RANDOM_METAL ) )
    return cost;

  for( i = MAT_BRONZE; i <= MAT_ADAMANTINE; i++ )
    if( is_set( &pIndexData->materials, i ) ) 
      return cost*material_table[i].cost;

  return cost;
}


/*
 *   OBJECT UTILITY ROUTINES
 */


void enchant_object( obj_data* obj )
{
  int i;

  if( obj->pIndexData->item_type == ITEM_WAND
    || obj->pIndexData->item_type == ITEM_STAFF ) 
    obj->value[3] = number_range( 0, obj->pIndexData->value[3] );

  if( ( obj->pIndexData->item_type == ITEM_WEAPON
    || obj->pIndexData->item_type == ITEM_ARMOR )
    && !is_set( obj->extra_flags, OFLAG_NO_ENCHANT ) ) {
    if( ( i = number_range( 0, 1000 ) ) >= 900 ) {
      obj->value[0] = ( i > 950 ? ( i > 990 ? ( i == 1000 ? 3 : 2 ) : 1 )
        : ( i < 910 ? ( i == 900 ? -3 : -2 ) : -1 ) );
      set_bit( obj->extra_flags, OFLAG_MAGIC );
      if( obj->value[0] < 0 )
        set_bit( obj->extra_flags, OFLAG_NOREMOVE );
      }
    }
}


void rust_object( obj_data* obj, int chance )
{
  int i;

  if( obj->metal( )
    && !is_set( obj->extra_flags, OFLAG_RUST_PROOF )
    && number_range( 0, 100 ) < chance ) {
    i = number_range( 0, 1000 );
    obj->rust = ( i > 700 ? 1 : ( i > 400 ? 2 : 3 ) );   
    }

  if( obj->pIndexData->item_type == ITEM_WEAPON
    || obj->pIndexData->item_type == ITEM_ARMOR ) {
    obj->age = number_range( 0, obj->pIndexData->durability/25-1 );
    obj->condition = number_range( 1, repair_condition( obj ) );
    }
}


void set_alloy( obj_data* obj, int level )
{
  int metal;

  if( !is_set( obj->pIndexData->extra_flags, OFLAG_RANDOM_METAL ) )
    return;

  for( metal = MAT_BRONZE; metal <= MAT_ADAMANTINE; metal++ )
    if( is_set( &obj->materials, metal ) )
      break;

  if( metal > MAT_ADAMANTINE ) {
    metal = MAT_BRONZE;
    for( ; ; ) {
      if( metal == MAT_ADAMANTINE
        || number_range( 0, level+75 ) > level-10*(metal-MAT_BRONZE) )
        break;
      metal++;
      }
    set_bit( &obj->materials, metal );
    }

  if( obj->pIndexData->item_type == ITEM_ARMOR )
    obj->value[1] = obj->pIndexData->value[1]-MAT_BRONZE+metal;
}


obj_data* create( obj_clss_data* obj_clss, int number )
{
  obj_data* obj;

  if( obj_clss == NULL ) {
    roach( "Create_object: NULL obj_clss." );
    return NULL;
    }

  obj = new obj_data( obj_clss );

  obj->singular = obj_clss->singular;
  obj->plural   = obj_clss->plural;
  obj->after    = obj_clss->after;
  obj->before   = obj_clss->before;

  obj->extra_flags[0] = obj_clss->extra_flags[0];
  obj->extra_flags[1] = obj_clss->extra_flags[1];
  
  if( is_set( &obj_clss->size_flags, SFLAG_CUSTOM ) )
    obj->size_flags  = -1;
  else
    obj->size_flags = obj_clss->size_flags;

  obj->value[0]   = obj_clss->value[0];
  obj->value[1]   = obj_clss->value[1];
  obj->value[2]   = obj_clss->value[2];
  obj->value[3]   = obj_clss->value[3];
  obj->weight     = obj_clss->weight;
  obj->condition  = obj_clss->durability;
  obj->materials  = obj_clss->materials;
  obj->age   = 0; 
  obj->rust       = 0;
  obj->timer      = 0; 

  if( number > 0 )
    obj_clss->count += number;
  else
    number = -number;

  obj->number     = number;
  obj->selected   = number;

  switch( obj_clss->item_type ) {
    case ITEM_MONEY:
      obj->value[0]  = obj->pIndexData->cost;
      break;
    case ITEM_FIRE:
    case ITEM_GATE:
      obj->timer = obj->value[0];
      break;
    }

  if( obj_clss->item_type != ITEM_ARMOR && obj_clss->item_type != ITEM_WEAPON
    && !strcmp( obj_clss->before, obj_clss->after )
    && obj_clss->plural[0] != '{' && obj_clss->singular[0] != '{' )
    set_bit( obj->extra_flags, OFLAG_IDENTIFIED ); 

  return obj;
}


obj_data* duplicate( obj_data* copy, int num )
{
  obj_data*            obj;
  obj_clss_data*  obj_clss  = copy->pIndexData;

  obj = new obj_data( obj_clss );

  char* string_copy [] = { copy->singular, copy->plural,
    copy->before, copy->after }; 
  char** string_obj [] = { &obj->singular, &obj->plural,
    &obj->before, &obj->after }; 
  char* string_index [] = { obj_clss->singular,
    obj_clss->plural, obj_clss->before, obj_clss->after }; 

  for( int i = 0; i < 4; i++ ) 
    *string_obj[i] = ( string_copy[i] != string_index[i] 
      ? alloc_string( string_copy[i], MEM_OBJECT ) : string_index[i] );
  
  obj->age            = copy->age;
  obj->extra_flags[0] = copy->extra_flags[0];
  obj->extra_flags[1] = copy->extra_flags[1];
  obj->size_flags     = copy->size_flags;
  obj->value[0]       = copy->value[0];
  obj->value[1]       = copy->value[1];
  obj->value[2]       = copy->value[2];
  obj->value[3]       = copy->value[3];
  obj->weight         = copy->weight;
  obj->condition      = copy->condition;
  obj->rust           = copy->rust;
  obj->timer          = copy->timer; 
  obj->materials      = copy->materials;
  obj->owner          = copy->owner;

  obj->temp     = copy->temp;
  obj->number   = num;   
  obj->selected = num;   

  obj_clss->count += num;

  if( copy->save != NULL ) {
    copy->save->save_list += obj;
    obj->save = copy->save;
    }

  return obj;
}


/*
 *   OBJECT TRANSFER FUNCTIONS
 */


void drop_contents( obj_data* obj )
{
  /*
  int               i;
  list_data*     list;
  room_data*     room;
  obj_data*   content;

  if( obj->array == NULL
    || ( room = obj->array->room( ) ) == NULL )
    return;

  list = content_list( obj );

  for( i = 0; i < list->length; i++ ) {
    content = (obj_data*) list->pntr[i];
    if( content->timer == -2 || content->where != obj
      || obj->array != room )
      continue;
    remove( content, content->number );
    put_obj( content, room );
    }

  delete list;
  */
  return;
}


/*
 *   OBJECT EXTRACTION ROUTINES
 */


void Obj_Data :: Extract( int i )
{
  if( i < number ) {
    remove_weight( this, i );
    number -= i;
    if( boot_stage == 2 )
      pIndexData->count -= i;
    return;
    }

  if( i > number ) {
    roach( "Extract( Obj ): number > amount." );
    roach( "-- Obj = %s", this );
    roach( "-- Number = %d", i ); 
    roach( "-- Amount = %d", number );
    }

  Extract( );
}


void Obj_Data :: Extract( )
{
  obj_array*    array;
  int               i;

  if( !Is_Valid( ) ) {
    roach( "Extracting invalid object." );
    roach( "-- Valid = %d", valid );
    roach( "--   Obj = %s", this );
    return;
    }

  if( this->array != NULL ) 
    From( number );

  extract( contents );

  if( boot_stage == 2 )
    pIndexData->count -= number;

  clear_queue( this );
  stop_events( this );

  if( save != NULL ) {
    array = &save->save_list;
    for( i = 0; ; i++ ) {
      if( i >= array->size ) 
        panic( "Extract: Object not found in save array." );
      if( this == array->list[i] ) {
        array->list[i] = NULL;
        save = NULL;
        break;
        }
      }
    }

  delete_list( affected );
  free_string( source, MEM_OBJECT );

  if( singular != pIndexData->singular )
    free_string( singular, MEM_OBJECT );
  if( plural != pIndexData->plural )
    free_string( plural,   MEM_OBJECT );
  if( after != pIndexData->after )
    free_string( after,    MEM_OBJECT );
  if( before != pIndexData->before )
    free_string( before,   MEM_OBJECT );

  timer      = -2;
  valid      = -1;

  extracted += this;
}


/*
 *   DISK ROUTINES
 */


void fix( obj_clss_data* obj_clss )
{
  if( obj_clss->item_type == ITEM_SCROLL ) 
    set_bit( &obj_clss->materials, MAT_PAPER );

  for( int i = 0; i < MAX_ANTI; i++ )
    if( !strncasecmp( anti_flags[i], "unused", 6 ) )
      remove_bit( &obj_clss->anti_flags, i );

  return;
}


void load_objects( void )
{
  FILE*                 fp;
  obj_clss_data*  obj_clss;
  oprog_data*        oprog;
  char              letter;
  int                    i;
  int                 vnum;

  echo( "Loading Objects ...\n\r" );
  vzero( obj_index_list, MAX_OBJ_INDEX );

  fp = open_file( AREA_DIR, OBJECT_FILE, "r", TRUE );

  if( strcmp( fread_word( fp ), "#OBJECTS" ) ) 
    panic( "Load_objects: header not found" );

  for( ; ; ) {
    letter = fread_letter( fp );

    if( letter != '#' ) 
      panic( "Load_objects: # not found." );

    if( ( vnum = fread_number( fp ) ) == 0 )
      break;
   
    if( vnum < 0 || vnum >= MAX_OBJ_INDEX ) 
      panic( "Load_objects: vnum out of range." );

    if( obj_index_list[vnum] != NULL ) 
      panic( "Load_objects: vnum %d duplicated.", vnum );

    obj_clss = new obj_clss_data;
 
    obj_index_list[vnum]  = obj_clss;
    obj_clss->vnum        = vnum;
    obj_clss->fakes       = vnum;

    obj_clss->singular         = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->plural           = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->before           = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->after            = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->long_s           = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->long_p           = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->prefix_singular  = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->prefix_plural    = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->creator          = fread_string( fp, MEM_OBJ_CLSS );
    obj_clss->last_mod         = fread_string( fp, MEM_OBJ_CLSS );      

    obj_clss->item_type       = fread_number( fp );
    obj_clss->fakes           = fread_number( fp );
    obj_clss->extra_flags[0]  = fread_number( fp );
    obj_clss->extra_flags[1]  = fread_number( fp );
    obj_clss->wear_flags      = fread_number( fp );
    obj_clss->anti_flags      = fread_number( fp );
    obj_clss->restrictions    = fread_number( fp );
    obj_clss->size_flags      = fread_number( fp );
    obj_clss->materials       = fread_number( fp );

    obj_clss->affect_flags[0] = fread_number( fp );
    obj_clss->affect_flags[1] = fread_number( fp );
    obj_clss->affect_flags[2] = fread_number( fp );
    obj_clss->layer_flags     = fread_number( fp );

    obj_clss->value[0]      = fread_number( fp );
    obj_clss->value[1]      = fread_number( fp );
    obj_clss->value[2]      = fread_number( fp );
    obj_clss->value[3]      = fread_number( fp );

    obj_clss->weight        = fread_number( fp );
    obj_clss->cost          = fread_number( fp );
    obj_clss->level         = fread_number( fp );
    obj_clss->limit         = fread_number( fp );
    obj_clss->repair        = fread_number( fp );
    obj_clss->durability    = fread_number( fp );
    obj_clss->blocks        = fread_number( fp );
    obj_clss->light         = fread_number( fp );

    obj_clss->date          = fread_number( fp );

    read_affects( fp, obj_clss ); 
    read_extra( fp, obj_clss->extra_descr );

    fread_letter( fp );

    for( ; ; ) {
      int number = fread_number( fp );

      if( number == -1 )
        break;

      oprog = new oprog_data;
      append( obj_clss->oprog, oprog );

      oprog->trigger  = number;
      oprog->obj_vnum = fread_number( fp );
      oprog->command  = fread_string( fp, MEM_OPROG );
      oprog->target   = fread_string( fp, MEM_OPROG );
      oprog->code     = fread_string( fp, MEM_OPROG );

      read_extra( fp, oprog->data );
      }       

    fix( obj_clss );
    }

  fclose( fp );

  for( i = 0; i < MAX_OBJ_INDEX; i++ ) 
    if( obj_index_list[i] != NULL )
      for( oprog = obj_index_list[i]->oprog; oprog != NULL;
        oprog = oprog->next )
        if( oprog->obj_vnum > 0 )
          oprog->obj_act = get_obj_index( oprog->obj_vnum );
 
  return;
}