buddha/bin/
buddha/cnf/
buddha/doc/cwg/
buddha/lib/
buddha/lib/etc/
buddha/lib/house/
buddha/lib/misc/
buddha/lib/pfiles/
buddha/lib/plralias/ZZZ/
buddha/lib/plrobjs/
buddha/lib/plrobjs/ZZZ/
buddha/lib/world/
buddha/lib/world/gld/
buddha/src/
buddha/src/doc/
/* ******************************************************************** *
 * FILE        : assemblies.c                  Copyright (C) 1999 Geoff Davis
*
 * USAGE: Implementation for assembly engine.                          *
 * -------------------------------------------------------------------- *
 * 1999 MAY 07 gdavis/azrael@laker.net Initial implementation.         *
 * ******************************************************************** */

#define __ASSEMBLIES_C__

#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"

#include "assemblies.h"
#include "comm.h"
#include "constants.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"


/* Local global variables. */
long           g_lNumAssemblies = 0;
ASSEMBLY       *g_pAssemblyTable = NULL;


/* External global variables. */
extern struct obj_data *obj_proto;
extern struct room_data *world;


void assemblyBootAssemblies( void )
{
  char         szLine[ MAX_STRING_LENGTH ] = { '\0' };
  char         szTag[ MAX_STRING_LENGTH ] = { '\0' };
  char         szType[ MAX_STRING_LENGTH ] = { '\0' };
  int          iExtract = 0;
  int          iInRoom = 0;
  int          iType = 0;
  long         lLineCount = 0;
  long         lPartVnum = NOTHING;
  long         lVnum = NOTHING;
  FILE         *pFile = NULL;

  if( (pFile = fopen( ASSEMBLIES_FILE, "rt" )) == NULL )
  {
    log( "SYSERR: assemblyBootAssemblies(): Couldn't open file '%s' for "
      "reading.", ASSEMBLIES_FILE );
    return;
  }

  while( !feof( pFile ) )
  {
    lLineCount += get_line( pFile, szLine );
    half_chop( szLine, szTag, szLine );

    if( *szTag == '\0' )
      continue;

    if( str_cmp( szTag, "Component" ) == 0 )
    {
      if( sscanf( szLine, "#%ld %d %d", &lPartVnum, &iExtract, &iInRoom ) != 3
)
      {
       log( "SYSERR: bootAssemblies(): Invalid format in file %s, line %ld: "
         "szTag=%s, szLine=%s.", ASSEMBLIES_FILE, lLineCount, szTag, szLine );
      }
      else if( !assemblyAddComponent( lVnum, lPartVnum, iExtract, iInRoom ) )
      {
       log( "SYSERR: bootAssemblies(): Could not add component #%ld to "
         "assembly #%ld.", lPartVnum, lVnum );
      }
    }
    else if( str_cmp( szTag, "Vnum" ) == 0 )
    {
      if( sscanf( szLine, "#%ld %s", &lVnum, szType ) != 2 )
      {
       log( "SYSERR: bootAssemblies(): Invalid format in file %s, "
         "line %ld.", ASSEMBLIES_FILE, lLineCount );
       lVnum = NOTHING;
      }
      else if( (iType = search_block( szType, AssemblyTypes, TRUE )) < 0 )
      {
       log( "SYSERR: bootAssemblies(): Invalid type '%s' for assembly "
         "vnum #%ld at line %ld.", szType, lVnum, lLineCount );
       lVnum = NOTHING;
      }
      else if( !assemblyCreate( lVnum, iType ) )
      {
       log( "SYSERR: bootAssemblies(): Could not create assembly for vnum "
         "#%ld, type %s.", lVnum, szType );
       lVnum = NOTHING;
      }
    }
    else
    {
      log( "SYSERR: Invalid tag '%s' in file %s, line #%ld.", szTag,
       ASSEMBLIES_FILE, lLineCount );
    }

    *szLine = '\0';
    *szTag = '\0';
  }

  fclose( pFile );
}

void assemblySaveAssemblies( void )
{
  char         szType[ MAX_STRING_LENGTH ] = { '\0' };
  long         i = 0;
  long         j = 0;
  ASSEMBLY     *pAssembly = NULL;
  FILE         *pFile = NULL;

  if( (pFile = fopen( ASSEMBLIES_FILE, "wt" )) == NULL )
  {
    log( "SYSERR: assemblySaveAssemblies(): Couldn't open file '%s' for "
      "writing.", ASSEMBLIES_FILE );
    return;
  }

  for( i = 0; i < g_lNumAssemblies; i++)
  {
    pAssembly = (g_pAssemblyTable + i);
    sprinttype(pAssembly->uchAssemblyType,AssemblyTypes,szType, sizeof(szType));
    fprintf( pFile, "Vnum                #%ld %s\n", pAssembly->lVnum, szType );

    for( j = 0; j < pAssembly->lNumComponents; j++ )
    {
      fprintf( pFile, "Component           #%ld %d %d\n",
       pAssembly->pComponents[ j ].lVnum,
       (pAssembly->pComponents[ j ].bExtract ? 1 : 0),
       (pAssembly->pComponents[ j ].bInRoom ? 1 : 0) );
    }

    if( i < g_lNumAssemblies - 1 )
      fprintf( pFile, "\n" );
  }

  fclose( pFile );
}

void assemblyListToChar( struct char_data *pCharacter )
{
  char         szBuffer[ MAX_STRING_LENGTH ] = { '\0' };
  char         szAssmType[ MAX_INPUT_LENGTH ] = { '\0' };
  long         i = 0;                  // Outer iterator.
  long         j = 0;                  // Inner iterator.
  long         lRnum = 0;              // Object rnum for obj_proto indexing.

  if( pCharacter == NULL )
  {
    log( "SYSERR: assemblyListAssembliesToChar(): NULL 'pCharacter'." );
    return;
  }
  else if( g_pAssemblyTable == NULL )
  {
    send_to_char(pCharacter, "No assemblies exist.\r\n");
    return;
  }

  /* Send out a "header" of sorts. */
  send_to_char(pCharacter, "The following assemblies exists:\r\n");

  for( i = 0; i < g_lNumAssemblies; i++ )
  {
    if( (lRnum = real_object( g_pAssemblyTable[ i ].lVnum )) < 0 )
    {
      send_to_char(pCharacter, "[-----] ***RESERVED***\r\n");
      log( "SYSERR: assemblyListToChar(): Invalid vnum #%ld in assembly table.", g_pAssemblyTable[i].lVnum);
    }
    else
    {
      sprinttype(g_pAssemblyTable[ i ].uchAssemblyType, AssemblyTypes, szAssmType, sizeof(szAssmType));
      sprintf( szBuffer, "[%5ld] %s (%s)\r\n", g_pAssemblyTable[ i ].lVnum,
       obj_proto[ lRnum ].short_description, szAssmType );
      send_to_char(pCharacter, szBuffer);

      for( j = 0; j < g_pAssemblyTable[ i ].lNumComponents; j++ )
      {
       if( (lRnum = real_object( g_pAssemblyTable[ i ].pComponents[ j ].lVnum )) < 0 )
       {
         send_to_char(pCharacter, " -----: ***RESERVED***\r\n");
         log( "SYSERR: assemblyListToChar(): Invalid component vnum #%ld in assembly for vnum #%ld.",
           g_pAssemblyTable[ i ].pComponents[ j ].lVnum, g_pAssemblyTable[ i ].lVnum );
       }
       else
       {
         sprintf( szBuffer, " %5ld: %-20.20s Extract=%-3.3s InRoom=%-3.3s\r\n",+           g_pAssemblyTable[ i ].pComponents[ j ].lVnum,
           obj_proto[ lRnum ].short_description,
           (g_pAssemblyTable[ i ].pComponents[ j ].bExtract ? "Yes" : "No"),
           (g_pAssemblyTable[ i ].pComponents[ j ].bInRoom  ? "Yes" : "No") );
         send_to_char(pCharacter, szBuffer);
       }
      }
    }
  }
}

bool assemblyAddComponent( long lVnum, long lComponentVnum, bool bExtract, bool bInRoom )
{
  ASSEMBLY     *pAssembly = NULL;
  COMPONENT    *pNewComponents = NULL;

  if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: assemblyAddComponent(): Invalid 'lVnum' #%ld.", lVnum );
    return (FALSE);
  }
  else if( real_object( lComponentVnum ) < 0 )
  {
    log( "SYSERR: assemblyAddComponent(): Invalid 'lComponentVnum' #%ld.",
      lComponentVnum );
    return (FALSE);
  }
  /* Removed as of 1.02.29 release */
  /* else if( assemblyHasComponent( lVnum, lComponentVnum ) )
  {
    log( "SYSERR: assemblyAddComponent(): Assembly for vnum #%ld already "
      "has component vnum #%ld.", lVnum, lComponentVnum );
    return (FALSE);
  } */

  /* Create a new component table with room for one more entry. */
  CREATE( pNewComponents, COMPONENT, pAssembly->lNumComponents + 1 );

  if( pAssembly->pComponents != NULL )
  {
    /* Copy the old table over to the new. */
    memmove( pNewComponents, pAssembly->pComponents, pAssembly->lNumComponents
* sizeof( COMPONENT ) );
    free( pAssembly->pComponents );
  }

  /*
   * Assign the new component table and setup the new component entry. Then
   * add increment the number of components.
   */

  pAssembly->pComponents = pNewComponents;
  pAssembly->pComponents[ pAssembly->lNumComponents ].lVnum = lComponentVnum;
  pAssembly->pComponents[ pAssembly->lNumComponents ].bExtract = bExtract;
  pAssembly->pComponents[ pAssembly->lNumComponents ].bInRoom = bInRoom;
  pAssembly->lNumComponents += 1;

  return (TRUE);
}

bool assemblyCheckComponents( long lVnum, struct char_data *pCharacter )
{
  bool         bOk = TRUE;
  long         i = 0;
  long         lRnum = 0;
  struct obj_data **ppComponentObjects = NULL;
  ASSEMBLY     *pAssembly = NULL;

  if( pCharacter == NULL )
  {
    log( "SYSERR: NULL assemblyCheckComponents(): 'pCharacter'." );
    return (FALSE);
  }
  else if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: NULL assemblyCheckComponents(): Invalid 'lVnum' #%ld.", lVnum );
    return (FALSE);
  }

  if( pAssembly->pComponents == NULL )
    return (FALSE);
  else if( pAssembly->lNumComponents <= 0 )
    return (FALSE);

  CREATE( ppComponentObjects, struct obj_data*, pAssembly->lNumComponents );

  for( i = 0; i < pAssembly->lNumComponents && bOk; i++ )
  {
    if( (lRnum = real_object( pAssembly->pComponents[ i ].lVnum )) < 0 )
      bOk = FALSE;
    else
    {
      if( pAssembly->pComponents[ i ].bInRoom )
      {
       if( (ppComponentObjects[ i ] = get_obj_in_list_num( lRnum,
         world[ IN_ROOM( pCharacter ) ].contents )) == NULL )
         bOk = FALSE;
       else
         obj_from_room( ppComponentObjects[ i ] );
      }
      else
      {
       if( (ppComponentObjects[ i ] = get_obj_in_list_num( lRnum,
         pCharacter->carrying )) == NULL )
         bOk = FALSE;
       else
         obj_from_char( ppComponentObjects[ i ] );
      }
    }
  }


  for( i = 0; i < pAssembly->lNumComponents; i++ )
  {
    if( ppComponentObjects[ i ] == NULL )
      continue;

    if( pAssembly->pComponents[ i ].bExtract && bOk )
      extract_obj( ppComponentObjects[ i ] );
    else if( pAssembly->pComponents[ i ].bInRoom )
      obj_to_room( ppComponentObjects[ i ], IN_ROOM( pCharacter ) );
    else
      obj_to_char( ppComponentObjects[ i ], pCharacter );
  }

  free( ppComponentObjects );

  return (bOk);
}

bool assemblyCreate( long lVnum, int iAssembledType )
{
  long         lBottom = 0;
  long         lMiddle = 0;
  long         lTop = 0;
  ASSEMBLY     *pNewAssemblyTable = NULL;

  if( lVnum < 0 )
    return (FALSE);
  else if( iAssembledType < 0 || iAssembledType >= MAX_ASSM )
    return (FALSE);

  if( g_pAssemblyTable == NULL )
  {
    CREATE( g_pAssemblyTable, ASSEMBLY, 1 );
    g_lNumAssemblies = 1;
  }
  else
  {
    lTop = g_lNumAssemblies - 1;

    for( ;; )
    {
      lMiddle = (lBottom + lTop) / 2;

      if( g_pAssemblyTable[ lMiddle ].lVnum == lVnum )
       return (FALSE);
      else if( lBottom >= lTop )
       break;
      else if( g_pAssemblyTable[ lMiddle ].lVnum > lVnum )
       lTop = lMiddle - 1;
      else
       lBottom = lMiddle + 1;
    }

    if( g_pAssemblyTable[ lMiddle ].lVnum <= lVnum )
      lMiddle += 1;

    CREATE( pNewAssemblyTable, ASSEMBLY, g_lNumAssemblies + 1 );

    if( lMiddle > 0 )
      memmove( pNewAssemblyTable, g_pAssemblyTable, lMiddle * sizeof( ASSEMBLY
) );

    if( lMiddle <= g_lNumAssemblies - 1 )
      memmove( pNewAssemblyTable + lMiddle + 1, g_pAssemblyTable + lMiddle, (g_lNumAssemblies - lMiddle) * sizeof( ASSEMBLY ) );

    free( g_pAssemblyTable );
    g_pAssemblyTable = pNewAssemblyTable;
    g_lNumAssemblies += 1;
  }

  g_pAssemblyTable[ lMiddle ].lNumComponents = 0;
  g_pAssemblyTable[ lMiddle ].lVnum = lVnum;
  g_pAssemblyTable[ lMiddle ].pComponents = NULL;
  g_pAssemblyTable[ lMiddle ].uchAssemblyType = (unsigned char) iAssembledType;
  return (TRUE);
}

bool assemblyDestroy( long lVnum )
{
  long         lIndex = 0;
  ASSEMBLY     *pNewAssemblyTable = NULL;

  /* Find the real number of the assembled vnum. */
  if( g_pAssemblyTable == NULL || (lIndex = assemblyGetAssemblyIndex( lVnum ))
< 0 )
  {
    log( "SYSERR: assemblyDestroy(): Invalid 'lVnum' #%ld.", lVnum );
    return (FALSE);
  }

  /* Deallocate component array. */
  if( g_pAssemblyTable[ lIndex ].pComponents != NULL )
    free( g_pAssemblyTable[ lIndex ].pComponents );

  if( g_lNumAssemblies > 1 )
  {
    /* Create a new table, the same size as the old one less one item. */
    CREATE( pNewAssemblyTable, ASSEMBLY, g_lNumAssemblies - 1 );

    /* Copy all assemblies before the one removed into the new table. */
    if( lIndex > 0 )
      memmove( pNewAssemblyTable, g_pAssemblyTable, lIndex * sizeof( ASSEMBLY ) );

    /* Copy all assemblies after the one removed into the new table. */
    if( lIndex < g_lNumAssemblies - 1 )
    {
      memmove( pNewAssemblyTable + lIndex, g_pAssemblyTable + lIndex + 1, (g_lNumAssemblies - lIndex - 1) *
       sizeof( ASSEMBLY ) );
    }
  }

  /* Deallocate the old table. */
  free( g_pAssemblyTable );

  /* Decrement the assembly count and assign the new table. */
  g_lNumAssemblies -= 1;
  g_pAssemblyTable = pNewAssemblyTable;

  return (TRUE);
}

bool assemblyHasComponent( long lVnum, long lComponentVnum )
{
  ASSEMBLY     *pAssembly = NULL;

  if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: assemblyHasComponent(): Invalid 'lVnum' #%ld.", lVnum );
    return (FALSE);
  }

  return (assemblyGetComponentIndex( pAssembly, lComponentVnum ) >= 0);
}

bool assemblyRemoveComponent( long lVnum, long lComponentVnum )
{
  long         lIndex = 0;
  ASSEMBLY     *pAssembly = NULL;
  COMPONENT    *pNewComponents = NULL;

  if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: assemblyRemoveComponent(): Invalid 'lVnum' #%ld.", lVnum );
    return (FALSE);
  }
  else if( (lIndex = assemblyGetComponentIndex( pAssembly, lComponentVnum )) <
0 )
  {
    log( "SYSERR: assemblyRemoveComponent(): Vnum #%ld is not a "
      "component of assembled vnum #%ld.", lComponentVnum, lVnum );
    return (FALSE);
  }

  if( pAssembly->pComponents != NULL && pAssembly->lNumComponents > 1 )
  {
    CREATE( pNewComponents, COMPONENT, pAssembly->lNumComponents - 1 );

    if( lIndex > 0 )
      memmove( pNewComponents, pAssembly->pComponents, lIndex * sizeof( COMPONENT ) );

    if( lIndex < pAssembly->lNumComponents - 1 )
    {
      memmove( pNewComponents + lIndex, pAssembly->pComponents + lIndex + 1,
       (pAssembly->lNumComponents - lIndex - 1) * sizeof( COMPONENT ) );
    }
  }

  free( pAssembly->pComponents );
  pAssembly->pComponents = pNewComponents;
  pAssembly->lNumComponents -= 1;

  return (TRUE);
}

int assemblyGetType( long lVnum )
{
  ASSEMBLY     *pAssembly = NULL;

  if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: assemblyGetType(): Invalid 'lVnum' #%ld.", lVnum );
    return (-1);
  }

  return ((int) pAssembly->uchAssemblyType);
}

long assemblyCountComponents( long lVnum )
{
  ASSEMBLY     *pAssembly = NULL;

  if( (pAssembly = assemblyGetAssemblyPtr( lVnum )) == NULL )
  {
    log( "SYSERR: assemblyCountComponents(): Invalid 'lVnum' #%ld.", lVnum );
    return (0);
  }

  return (pAssembly->lNumComponents);
}

long assemblyFindAssembly( const char *pszAssemblyName )
{
  long         i = 0;
  long         lRnum = NOTHING;

  if( g_pAssemblyTable == NULL )
    return (-1);
  else if( pszAssemblyName == NULL || *pszAssemblyName == '\0' )
    return (-1);

  for( i = 0; i < g_lNumAssemblies; i++ )
  {
    if( (lRnum = real_object( g_pAssemblyTable[ i ].lVnum )) < 0 )
      log( "SYSERR: assemblyFindAssembly(): Invalid vnum #%ld in assembly table.", g_pAssemblyTable[i].lVnum );
    else if( isname( pszAssemblyName, obj_proto[ lRnum ].name ) )
      return (g_pAssemblyTable[ i ].lVnum);
  }

  return (-1);
}

long assemblyGetAssemblyIndex( long lVnum )
{
  long         lBottom = 0;
  long         lMiddle = 0;
  long         lTop = 0;

  lTop = g_lNumAssemblies - 1;

  for( ;; )
  {
    lMiddle = (lBottom + lTop) / 2;

    if( g_pAssemblyTable[ lMiddle ].lVnum == lVnum )
      return (lMiddle);
    else if( lBottom >= lTop )
      return (-1);
    else if( g_pAssemblyTable[ lMiddle ].lVnum > lVnum )
      lTop = lMiddle - 1;
    else
      lBottom = lMiddle + 1;
  }
}

long assemblyGetComponentIndex( ASSEMBLY *pAssembly, long lComponentVnum )
{
  long         i = 0;

  if( pAssembly == NULL )
    return (-1);

  for( i = 0; i < pAssembly->lNumComponents; i++ )
  {
    if( pAssembly->pComponents[ i ].lVnum == lComponentVnum )
      return (i);
  }

  return (-1);
}

ASSEMBLY* assemblyGetAssemblyPtr( long lVnum )
{
  long         lIndex = 0;

  if( g_pAssemblyTable == NULL )
    return (NULL);

  if( (lIndex = assemblyGetAssemblyIndex( lVnum )) >= 0 )
    return (g_pAssemblyTable + lIndex);

  return (NULL);
}

#undef __ASSEMBLIES_C__