/*************************************************************************** * 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 ); }