FlCodebase3.1/
FlCodebase3.1/bounty/
FlCodebase3.1/challenge/
FlCodebase3.1/clans/
FlCodebase3.1/gods/
FlCodebase3.1/mobprogs/
FlCodebase3.1/player/
FlCodebase3.1/savemud/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Envy Diku Mud, you must comply with   *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  In particular, you may not remove either of these copyright notices.   *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  * 
 *                                                                         *
 *      ROM 2.4 is copyright 1993-1998 Russ Taylor                         *
 *      ROM has been brought to you by the ROM consortium                  *
 *          Russ Taylor (rtaylor@hypercube.org)                            *
 *          Gabrielle Taylor (gtaylor@hypercube.org)                       *
 *          Brian Moore (zump@rom.org)                                     *
 *      By using this code, you have agreed to follow the terms of the     *
 *      ROM license, in the file Rom24/doc/rom.license                     *
 *                                                                         *
 * Code Adapted and Improved by Abandoned Realms Mud                       *
 * and Aabahran: The Forsaken Lands Mud by Virigoth                        *
 *                                                                         *
 * Continued Production of this code is available at www.flcodebase.com    *
 ***************************************************************************/

/* Written by Virigoth sometime circa april 2000 for FORSAKEN LANDS mud.*/
/* This is the implementation of the selectable skills code		*/
/* NOT TO BE USED OR REPLICATED WITHOUT EXPLICIT PERMISSION OF AUTHOR	*/
/* umplawny@cc.umanitoba.ca						*/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
#include "tome.h"
#include "interp.h"
#include "recycle.h"
#include "cabal.h"
#include "vote.h"
#include "save_mud.h"




//topic struct
struct tome_topic_s {
  char		subject[MIL];		//name of topic
  int		topicSize;		//number of entries in this topic

  TOME_DATA*	tomes;			//list of entries in this topic
  TOME_DATA*	last;			//ptr to last tome in list
};


//LOCAL GLOBALS
static TOME_TOPIC	topic_list[MAX_TOPIC];
static int		lastTopic;		//number of topics used
static int		maxTome;		//number of tomes


//PRIVATE FUNCTIONS



//gives handle to topic, 0 on fail
int TopicHandle( char* subject ){
  int i = 0;

  if (IS_NULLSTR(subject))
    return i;

  for (i = 0; i < MAX_TOPIC; i++){
    if (LOWER(subject[0]) != LOWER(topic_list[i].subject[0]))
      continue;
    else if (!strcasecmp(subject, topic_list[i].subject))
      return (i + 1);
  }
  return (0);
}

//gives pointer to topic from handle
TOME_TOPIC* HandleToTopic( int handle ){
  if (handle < 1)
    return NULL;
  else if (topic_list[handle - 1].topicSize < 1)
    return NULL;
  else
    return (&topic_list[handle - 1]);
}

//gives handle based on pointer
int TopicToHandle( TOME_TOPIC* topic ){
  if (topic == NULL)
    return 0;
  else
    return ((topic - topic_list) / sizeof( TOME_TOPIC ) + 1);
}

//Allocates tome with intent TO FREE IT LATER
//Use AddTome for adding tomes to global list
TOME_DATA* NewTome(){
  TOME_DATA* tome = (TOME_DATA*) malloc(sizeof(TOME_DATA));

  if (tome == NULL){
    bug("tome.c:NewTome> malloc returned NULL", 0);
    return NULL;
  }
  
  //reset the tome data
  memset(tome, 0, sizeof(TOME_DATA));
  tome->canFree = TRUE;

  //return pointer
  return (tome);
}

//Free a tome allocated with NewTome (not with alloc_perm)
void FreeTome( TOME_DATA* tome ){
  if (tome == NULL)
    return;

  //free common things between perm/malloc tomes
  if (!IS_NULLSTR(tome->text))
    free_string(tome->text);

  //Beyond this only malloc tomes
  if (!tome->canFree){
    return;
  }
  //free the mallocated data
  free( tome );
}
  
//Adds a new tome with given author, title, and text under given subject
TOME_DATA* AddTome(char* subject, char* title, char* author, char* text){
  int handle = TopicHandle( subject );
  TOME_TOPIC* topic = HandleToTopic( handle );
  TOME_DATA* tome;

  if (topic == NULL){
    //try to add a new topic
    if (lastTopic >= MAX_TOPIC){
      bug("tome.c:AddTome> Cannot add topic, MAX_TOPIC of %d reached.", lastTopic);
      return NULL;
    }
    else
      topic = &topic_list[lastTopic++];
  }

  //add the new tome and try to attatch it to topic
  tome = (TOME_DATA*) alloc_perm(sizeof(TOME_DATA));

  if (tome == NULL){
    bug("tome.c:AddTome> alloc_perm returned NULL", 0);
    return NULL;
  }
  
  //reset the tome data
  memset(tome, 0, sizeof(TOME_DATA));
  
  if (topic->tomes == NULL){
    topic->last = tome;
    topic->tomes = tome;
  }
  else{
    topic->last->next = tome;
    topic->last = tome;
  }
  tome->topic = topic;
  topic->topicSize++;
  
  //copy stuff over
  strcpy(tome->title, title);
  strcpy(tome->author, author);
  strcpy(topic->subject, subject);
  tome->text = str_dup(text);

  return (tome);
}

//composes list of subjects with number of tomes in each
char* GetSubjectList(){
  static char list[MSL];
  char buf[4 * MIL];
  int i = 0;

  list[0] = 0;

  for (i = 0; i < lastTopic; i++){
    sprintf( buf, "%2d. %-28s [%2d]",
	     i + 1,
	     topic_list[i].subject,
	     topic_list[i].topicSize);
    //do 2 per line
    if (i % 2 == 0)
      strcat(buf, " " );
    else
      strcat(buf, "\n\r" );

    //add onto the list
    strcat(list, buf );
  }
  if (IS_NULLSTR(list)){
    sprintf(list, "None.\n\r");
  }
  else if (i % 2 != 0)
    strcat(list, "\n\r");
  return (list);
}

//composes list of titles in the subject
char* GetTitleList(char* subject){
  static char list[MSL];
  char buf[4 * MIL];
  TOME_TOPIC* topic;
  TOME_DATA* tome;
  int i = 0;

  list[0] = 0;

  if ( (topic = HandleToTopic(TopicHandle(subject))) != NULL){
    tome = topic->tomes;
    for (; tome; tome = tome->next, i++){
      sprintf( buf, "%2d. %-28s",
	       i + 1,
	       tome->title);
      //do 2 per line
      if (i % 2 == 0)
	strcat(buf, " " );
      else
	strcat(buf, "\n\r" );

      //add onto the list
      strcat(list, buf );
    }
  }
  if (IS_NULLSTR(list)){
    sprintf(list, "None.\n\r");
  }
  else if (i % 2 != 0)
    strcat(list, "\n\r");
  
  return (list);
}


//echos avaliability of a new tome
void EchoTome( TOME_DATA* newtome ){
  DESCRIPTOR_DATA *d;
  char buf[MIL];

  sprintf(buf, "A scribe hands you a note: <%s> %s is now avaliable in The Library.\n\r",
	  newtome->topic->subject, newtome->title);

  for ( d = descriptor_list; d; d = d->next ){
    if ( d->connected == CON_PLAYING )
      send_to_char( buf, d->character );
  }

  //Create a note on the subject
  sprintf(buf, warn_table[WARN_TOME].subj, newtome->topic->subject, newtome->title);
  do_hal("all", buf, warn_table[WARN_TOME].text, NOTE_NEWS);
}

//creates a new tome into pcdata for editing
void reset_tome( CHAR_DATA* ch ){
  ch->pcdata->ptome = NewTome();
  if (ch->pcdata->ptome == NULL)
    send_to_char("Error!\n\r", ch);
  else{
    //set author
    sprintf(ch->pcdata->ptome->author, "%s%s%s%s%s",
	    ch->pCabal ? get_crank(ch) : "",
	    ch->pCabal ? " " : "",
	    ch->name,
	    IS_NULLSTR(ch->pcdata->family)? "" : " ",
	    IS_NULLSTR(ch->pcdata->family)? "" : ch->pcdata->family);
    send_to_char("New Tome begun.\n\r", ch);
  }
}


//Saves a single tome
void WriteTome( FILE* fp, TOME_DATA* tome ){
  fprintf( fp, "%s~\n%s~\n%s~\n\n",
	   tome->author,
	   tome->title,
	   tome->text);
}

//reads a single tome
void ReadTome( FILE* fp, TOME_DATA* tome ){
  char* buf;

  buf = fread_string( fp );
  strcpy( tome->author, buf );
  free_string( buf );

  buf = fread_string( fp );
  strcpy( tome->title, buf );
  free_string( buf );

  tome->text = fread_string( fp );
}

//Saves a single topic
void WriteTopic( FILE* fp, TOME_TOPIC* topic ){
  TOME_DATA* tome = topic->tomes;
  int i = 0;

  fprintf(fp, "%s~ %d\n", topic->subject, topic->topicSize);

  for (; i < topic->topicSize; i++, tome = tome->next){
    WriteTome( fp, tome );
  }
}

//Reads a single topic
void ReadTopic( FILE* fp ){
  TOME_DATA tome;
  int i = 0, topicSize;
  char* buf;

  buf = fread_string( fp );
  topicSize = fread_number( fp );
  for (i = 0; i < topicSize; i++){
    ReadTome( fp, &tome );
    AddTome( buf, tome.title, tome.author, tome.text );
    free_string(tome.text);
  }
  free_string( buf );
}
  
//Gets tome based on number of topic/tome
TOME_DATA* GetTome  (int sub, int tom ){
  TOME_TOPIC* topic;
  TOME_DATA* tome;
  int i = 1;

  if (sub < 1 || tom < 1)
    return NULL;

  topic = HandleToTopic( sub );
  if (topic == NULL){
    return NULL;
  }
  else
    tome = topic->tomes;

  //find the tom'th topic in topic
  for (tome = topic->tomes; tome && i < tom && i < topic->topicSize; tome = tome->next, i++);

  return tome;
}

//INTERFACE FUNCTIONS

//Reads the library
void LoadTomes(){
  FILE* fp;
  char path[MIL];
  int i= 0;
  int lastTopic = 0;

  fclose( fpReserve );
  sprintf(path, "..%s", TOME_FILE);

  //try to open file for write
  if ( ( fp = fopen( path, "r" ) ) == NULL ){
    perror( path );
    fpReserve = fopen( NULL_FILE, "r" );
    return;
  }

  //size of library
  lastTopic = fread_number( fp );

  //run through topics reading
  for (i = 0; i < lastTopic; i++){
    ReadTopic( fp );
  }
  fclose( fp );
  fpReserve = fopen( NULL_FILE, "r" );
}


//Saves the library
void SaveTomes(){
  FILE* fp;
  char path[MIL];
  int i= 0;
  int size = 0;

  fclose( fpReserve );
  sprintf(path, "..%s", TOME_TEMP_FILE);

  //try to open file for write
  if ( ( fp = fopen( path, "w" ) ) == NULL ){
    perror( path );
    fclose (fp);
    fpReserve = fopen( NULL_FILE, "r" );
    return;
  }

  //size of library
  //run through topics counting
  for (i = 0; i < lastTopic; i++){
    if (topic_list[i].topicSize < 1)
      continue;
    else
      size++;
  }
  fprintf( fp, "%d\n", size);

  //run through topics writting
  for (i = 0; i < lastTopic; i++){
    if (topic_list[i].topicSize < 1)
      continue;
    else
      WriteTopic( fp, &topic_list[i] );
  }
  fclose( fp );
  fpReserve = fopen( NULL_FILE, "r" );

  //copy the file over
  rename(TOME_TEMP_FILE, TOME_FILE);
}

//resets tome data for further loading/use
void InitTomes(){

  //zero the topic array
  memset( &topic_list, 0, MAX_TOPIC * sizeof(TOME_TOPIC));

  //zero the statistics
  lastTopic = 0;
  maxTome = 0;

  //load saved data
  LoadTomes();
}
void do_read_tome( CHAR_DATA *ch, char *argument ){
  char arg[MIL];
  char buf[MIL];
  BUFFER* output;
  argument = one_argument(argument, arg );

  if (IS_NULLSTR(arg)){
    output = new_buf();
    sprintf(buf, "The Library of Aabahran has tomes in following subjects:\n\r");
    add_buf(output, buf );
    add_buf(output, GetSubjectList());

    sprintf(buf, "Use \"tome <subject #>\" for list of tomes in given subject.\n\r");
    add_buf(output, buf );
    page_to_char(buf_string(output), ch);
    free_buf( output );
  }
  else{
    TOME_TOPIC* topic;
    int sub = 0;
    int tom = 0;

    sub = atoi( arg );
    tom = atoi( argument );

    topic = HandleToTopic( sub );
    if (topic == NULL){
      sendf(ch, "Topic number %d is not avaliable.\n\r", sub );
      return;
    }

    //LIST TOMES IN TOPICS
    if (IS_NULLSTR(argument)){
      output = new_buf();
      sprintf(buf, "Following Tomes with subject \"%s\" are avaliable:\n\r", topic->subject);
      add_buf(output, buf );
      add_buf(output, GetTitleList( topic->subject ));
      
      sprintf(buf, "Use \"tome <subject #> <tome #>\" to read.\n\r");
      add_buf(output, buf );
      page_to_char(buf_string(output), ch);
      free_buf( output );
    }
    //SHOW TOME (HERALDS ANYWHERE, REST ONLY IN HERALD/LIBRARY)
    else{
      TOME_DATA* tome;

      /* CHECK IF WE CAN READ THIS HERE */
      if (!IS_SET(ch->in_room->room_flags2, ROOM_LIBRARY)
	  && (ch->in_room->pCabal == NULL
	      || !IS_CABAL(ch->in_room->pCabal, CABAL_HISTORY))
	  && (ch->pCabal == NULL 
	      || !IS_CABAL(ch->pCabal, CABAL_HISTORY))){
	send_to_char("You may only read tomes in library, or Herald Cabal.\n\r", ch);
	return;
      }
      if ( (tome = GetTome(sub, tom)) == NULL){
	sendf(ch, "That selection seems to be unavailable.\n\r");
	return;
      }
      output = new_buf();
      sprintf(buf, "[%s] : ", tome->topic->subject );
      add_buf(output, buf );
      
      if (IS_NULLSTR(tome->title))
	sprintf(buf, "%s by ", "TITLE" );
      else
	sprintf(buf, "%s by ", tome->title );
      add_buf(output, buf );
      
      sprintf(buf, "%s\n\r", tome->author );
      add_buf(output, buf );
      
      if (!IS_NULLSTR(tome->text))
	add_buf(output, tome->text );
      
      page_to_char(buf_string(output), ch);
      free_buf( output );    
      return;
    }
  }
}

void do_write_tome( CHAR_DATA *ch, char *argument ){
  char arg[MIL];

  if (ch == NULL)
    return;
  else if (IS_NPC(ch) 
	   || (!IS_IMMORTAL(ch) 
	       && get_skill(ch, skill_lookup("scribe")) < 2)
	   ){
    send_to_char("You may not scribe Tomes.\n\r", ch);
    return;
  }

  //get first command
  argument = one_argument( argument, arg );

/* NOTHING */
  if (IS_NULLSTR(arg)){
    BUFFER* output;
    char buf[MIL];
    TOME_DATA* tome = ch->pcdata->ptome;
    if (tome == NULL){
      send_to_char("scribe <new/clear/title/subject>\n\r", ch);
      return;
    }
    output = new_buf();
    sprintf(buf, "Tome: " );
    add_buf(output, buf );

    if (IS_NULLSTR(tome->title))
      sprintf(buf, "%s by ", "TITLE" );
    else
      sprintf(buf, "%s by ", tome->title );
    add_buf(output, buf );

    sprintf(buf, "%s\n\r", tome->author );
    add_buf(output, buf );

    if (!IS_NULLSTR(tome->text))
      add_buf(output, tome->text );

    page_to_char(buf_string(output), ch);
    free_buf( output );    
    return;
  }
/* CREATE */
  else if (!str_prefix(arg, "new") || !str_prefix(arg, "create")){
    if (ch->pcdata->ptome != NULL){
      send_to_char("You are already working on a tome!\n\r", ch);
      return;
    }
    reset_tome( ch );
    return;
  }
/* DELETE */
  else if (!str_prefix(arg, "delete")){
    TOME_TOPIC* topic;
    TOME_DATA* tome = NULL, *prev;
    int sub = 0;
    int tom = 0;
    
    argument = one_argument(argument, arg );
 
    if (get_trust(ch) < CREATOR){
      send_to_char("Creator and above only.\n\r", ch);
      return;
    }
    else if (IS_NULLSTR(argument)){
      send_to_char("delete <subject #> <tome #>\n\r", ch);
      return;
    }
    sub = atoi( arg );
    tom = atoi( argument );

    topic = HandleToTopic( sub );
    if (topic == NULL){
      sendf(ch, "Topic number %d is not avaliable.\n\r", sub );
      return;
    }
    if ( (tome = GetTome(sub, tom)) == NULL){
      sendf(ch, "That selection seems to be unavailable.\n\r");
      return;
    }

    //find previous
    prev = tome->topic->tomes;
    if (prev == tome){
      tome->topic->tomes = prev->next;
      if (tome->topic->last == prev)
	tome->topic->last = prev->next;
      tome->next = NULL;
    }
    else{
      while (prev && prev->next != tome ){
	prev = prev->next;
      }
      if (prev == NULL){
	send_to_char("Error, should have found the tome but didn't.\n\r", ch);
	return;
      }
      if (tome->topic->last == tome)
	tome->topic->last = prev;
      prev->next = prev->next->next;
    }
    tome->topic->topicSize--;
    FreeTome( tome );
    send_to_char("Deleted.\n\r", ch);
    return;
  }
/* EDIT */
  else if (!str_prefix(arg, "edit")){
    TOME_TOPIC* topic;
    TOME_DATA* tome = NULL;
    int sub = 0;
    int tom = 0;
    
    argument = one_argument(argument, arg );
 
    if (get_trust(ch) < CREATOR){
      send_to_char("Creator and above only.\n\r", ch);
      return;
    }
    else if (IS_NULLSTR(argument)){
      send_to_char("edit <subject #> <tome #>\n\r", ch);
      return;
    }
    sub = atoi( arg );
    tom = atoi( argument );

    topic = HandleToTopic( sub );
    if (topic == NULL){
      sendf(ch, "Topic number %d is not avaliable.\n\r", sub );
      return;
    }
    if ( (tome = GetTome(sub, tom)) == NULL){
      sendf(ch, "That selection seems to be unavailable.\n\r");
      return;
    }
    string_append(ch, &tome->text);   
  }
/* CLEAR */
  else if (!str_prefix(arg, "clear") || !str_prefix(arg, "delete")){
    if (ch->pcdata->ptome == NULL){
      send_to_char("You are not working on a tome!\n\r", ch);
      return;
    }
    FreeTome(ch->pcdata->ptome);
    ch->pcdata->ptome = NULL;
    send_to_char("Ok.\n\r", ch);
    return;    
  }
/* TITLE */
  else if (!str_prefix(arg, "title")){
    if (ch->pcdata->ptome == NULL){
      reset_tome( ch );
      if (ch->pcdata->ptome == NULL)
	return;
    }
    else if (IS_NULLSTR(argument)){
      send_to_char("What will be the title of the tome?\n\r", ch);
      return;
    }
    else if (strlen(argument) > 28){
      send_to_char("The title must be under 28 characters long.\n\r", ch);
      return;
    }
    strcpy(ch->pcdata->ptome->title, argument);
    send_to_char("Ok.\n\r", ch);
  }
  /* TEXT */
  else if (!str_prefix(arg, "text")){
    if (ch->pcdata->ptome == NULL){
      reset_tome( ch );
      if (ch->pcdata->ptome == NULL)
	return;
    }
    send_to_char("Enter the contents of the tome.\n\r", ch);
    string_append(ch, &ch->pcdata->ptome->text);
    return;
  }
/* SEND/POST/SUBJECT */
  else if (!str_prefix(arg, "send") 
	   || !str_prefix(arg, "post")
	   || !str_prefix(arg, "print")
	   || !str_prefix(arg, "subject")
	   ){
    TOME_DATA* tome, *newtome;
    TOME_TOPIC* topic;
    char subject[MIL];

    if ((tome = ch->pcdata->ptome) == NULL){
      send_to_char("You are not working on a tome!\n\r", ch);
      return;
    }
    else if (IS_NULLSTR(argument)){
      send_to_char("Print the Tome under what subject?\n\r", ch);
      return;
    }
    else if (IS_NULLSTR(tome->title)){
      send_to_char("You lack the title.\n\r", ch);
      return;
    }
    else if (IS_NULLSTR(tome->text)){
      send_to_char("You lack the body.\n\r", ch);
      return;
    }
    //Convert numeric subject if required
    if ( (topic = HandleToTopic(atoi(argument))) != NULL)
      strcpy(subject, topic->subject);
    else
      strcpy(subject, argument);

    /* Immortals avoid the vote */
    if (IS_IMMORTAL(ch)){
      if ( (newtome = AddTome(subject, tome->title, tome->author, tome->text)) == NULL){
	send_to_char("Error adding the tome!\n\r", ch);
	return;
      }
      EchoTome( newtome );
    }
    else if (ch->pCabal == NULL){
      send_to_char("You must be part of a cabal to scribe a tome.\n\r", ch);
      return;
    }
    else{
      const int cost = 150;
      CABAL_DATA* pc = get_parent( ch->pCabal );
      char buf[3 * MIL];
      /*  cost first */

      if ( cost > GET_CP( ch)){
	sendf( ch, "You will need at least %d %s%s to scribe this tome.\n\r", 
	       cost,
	       ch->pCabal->currency, (cost) == 1 ? "" : "s");
	return;
      }
      /* cabal coffers are not affected by urgency */
      else if (mud_data.mudport != TEST_PORT
	       && (pc == NULL || cost > GET_CAB_CP( pc )) ){
	sendf( ch, "[%s] will require at least %d %s%s in its coffers to scribe this tome.\n\r", 
	       pc->who_name,
	       cost,
	       pc->currency, (cost) == 1 ? "" : "s" );
	return;
      }
      else{
	VOTE_DATA* pVote;
	  /* ready to go */
	  //merge subject/title to be seperated later using : as seperator
	  sprintf(buf, "%s:%s", subject, tome->title);

	pVote = create_vote(ch, ch->name, buf, tome->text, VOTE_TOME, ch->pCabal->vnum, 0, 0, 0, 0);
	if (pVote == NULL){
	  bug("do_write_tome: send: error creating vote.", 0);
	  send_to_char("Error!\n\r", ch);
	  return;
	}
	/* check urgent flag */
	add_vote( pVote );	
	save_mud();
	//subtract the cost
	CP_GAIN( ch, -cost, TRUE );
	CPS_CAB_GAIN( pc, -(cost * CPTS));
	send_to_char("Ok.\n\r", ch);
      }
    }
    FreeTome(tome);
    ch->pcdata->ptome = NULL;
    SaveTomes();
    return;
  }
  else
    send_to_char("scribe <new/clear/title/send>\n\r", ch);
}

void do_print( CHAR_DATA* ch, char* argument ){
  OBJ_DATA* obj;
  TOME_DATA* tome = NULL;
  CHAR_DATA* mob;
  char arg[MIL];
  char buf[MIL];
  int sub = 0;
  int tom = 0;

  if (!IS_SET(ch->in_room->room_flags2, ROOM_LIBRARY)){
    send_to_char("You may request for Tomes to be printed only in the library.\n\r", ch);
    return;
  }
  //Look for a mob
  for (mob = ch->in_room->people; mob; mob = mob->next_in_room){
    if (IS_NPC(mob) && IS_SET(mob->act, ACT_NONCOMBAT)
	&& mob->leader == NULL
	&& mob->master == NULL){
      break;
    }
  }
  if (mob == NULL){
    send_to_char("There is no one here to take your order.\n\r", ch);
    return;
  }
  argument = one_argument(argument, arg);

  if (!can_see(mob, ch)){
    do_say(mob, "I don't deal with people I can't see.");
    return;
  }
  if (IS_NULLSTR(arg)){
    do_say(mob, "I'm sorry, print which subject and tome number?");
    return;
  }
  sub = atoi( arg );
  tom = atoi( argument );

  if (sub < 1 || tom < 1){
    do_say(mob, "I'm sorry, print which subject and tome number?");
    return;
  }
  
  if ( (tome = GetTome(sub, tom)) == NULL){
    sprintf( buf, "We do not have a Tome under subject %d, index %d.", sub, tom);
    do_say(mob, buf );
    return;
  }

  /* cost of 1k */
  if (ch->gold < 1000){
    sprintf(buf, "You seem to lack the gold %s.", PERS2(ch));
    do_say(mob, buf );
    return;
  }
  ch->gold -= 1000;
  act("$n takes 1000 gold from you.", mob, NULL, ch, TO_VICT);

  //make the object
  obj = create_object( get_obj_index( OBJ_VNUM_TOME ), 0);

  //set name
  sprintf(buf, obj->name, tome->title );
  free_string(obj->name );
  obj->name = str_dup( buf );

  //set short desc
  sprintf(buf, obj->short_descr, tome->title, tome->author );
  free_string(obj->short_descr );
  obj->short_descr = str_dup( buf );

  //set long desc
  sprintf(buf, obj->description, tome->title, tome->author );
  free_string(obj->description );
  obj->description = str_dup( buf );

  //add extra desc based on tome
  obj->extra_descr = new_extra_descr();
  obj->extra_descr->keyword = str_dup(tome->title);
  obj->extra_descr->description = str_dup(tome->text);
  obj->extra_descr->next = NULL;

  obj_to_char( obj, ch );
  
  act("$n quickly retrieves a copy of your request.", mob, NULL, ch, TO_VICT);
  act("$n quickly retrieves a copy of $N's request.", mob, NULL, ch, TO_NOTVICT);
  act("$n gives $p to you.", mob, obj, ch, TO_VICT );
  act("$n gives $p to $N.", mob, obj, ch, TO_NOTVICT );
}