/*~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- ~ 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. ~ ~ ~ ~ Ack 2.2 improvements copyright (C) 1994 by Stephen Dooley ~ ~ ACK!MUD is modified Merc2.0/2.1/2.2 code (c)Stephen Zepp 1998 Ver: 4.3 ~ ~ ~ ~ In order to use any part of this PA Diku Mud, you must comply with ~ ~ both the original Diku license in 'license.doc' as well the Merc ~ ~ license in 'license.txt', and the Ack!Mud license in 'ack_license.txt'.~ ~ In particular, you may not remove any of these copyright notices. ~ ~ ~ ~ _______ _____ ~ ~ / __ /\ / ___ \ 222222 PA_MUD by Amnon Kruvi ~ ~ /______/ / / /___\ \ 2 PA_MUD is modified ~ ~ / _______/ / _______ \ 2 Ack!Mud, v4.3 ~ ~ /_/ /_/ \_\ 2 ~ ~ 2 ~ ~ 2222222 ~ ~ ~ ~ ~ ~ Years of work have been invested to create DIKU, Merc, Ack and PA. ~ ~ Please show your respect by following the licenses, and issuing ~ ~ credits where due. ~ ~ ~ ~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-*/ #if defined(macintosh) include <types.h> #else #include <sys/types.h> #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "ack.h" #ifndef DEC_LISTS_H #include "lists.h" #endif #define BOARD_DIR "boards" #define T2000 -1 /* Terminator for files... */ /* Local functions */ BOARD_DATA * load_board(OBJ_DATA * obj); void save_board(BOARD_DATA * board,CHAR_DATA *ch); void finished_editing( MESSAGE_DATA * msg, char * * dest, CHAR_DATA * ch, bool saved); /* Some locals used to manage the list of messages: */ /************************************************************************** * MAG Modified outline * * Use a directory called boards under area directory. * * Store a file for each board called board.vnum makes things a lot * * easier, and means files can be transfered easily between boards * * use a time based means to get rid of messages. * * * * Values: * * Value0 : Expiry time for a message in days. * * Value1 : Min level of player to read the board. * * Value2 : Max level of player to write to the board. * * Value3 : Board number (usually it's vnum for simplicity) * * * * Uses commands in write.c for string editing. * * This file does reading files, writing files and managing boards * * * **************************************************************************/ /************************************************************************** * Outline of BOARD.C * * ^^^^^^^^^^^^^^^^^^ * * This code was written for use in ACK! MUD. It should be easy to * * include it into a Diku Merc 2.0+ Mud. * * * * The following functions are needed: * * 1) Show the contents of a board to a player. * * 2) Show a message to a player * * 3) Add to a message & finish writing a message. * * 4) Start writing a message * * 5) Remove a message * * 6) Save the messages * * 7) Load the messages (only used at start-up) * * * * Also, the code for the commands write and read are in this file. * * WRITE checks for a board, and calls either 4) or 3) above. * * READ calls 2) above. The LOOK function was ammended to allow players * * to type 'look board' or 'look at board' which calls 1) above. * * * * MESSAGE DATA holds the vnum of the board where the message was written * * and the message info. There is no seperate save file or structure for * * each board. Instead, they are all stored together. I have used * * OBJ_TYPE 23 for boards, with the following values used: * * value 0: max number of messages allowed * * value 1: min level of player to read the board * * value 2: min level of player to write on the board * * value 3: the vnum of the board...NOT the vnum of the room... * **************************************************************************/ /************************************************************************** * Ick ick ick! Remove all the dammed builder functions, and use the * * general merc memory functions. -- Altrag * **************************************************************************/ void do_show_contents( CHAR_DATA * ch, OBJ_DATA * obj ) { /* Show the list of messages that are present on the board that ch is * looking at, indicated by board_vnum... */ MESSAGE_DATA *msg; BOARD_DATA *board; OBJ_INDEX_DATA *pObj; char buf[MAX_INPUT_LENGTH]; int cnt = 0; int board_num; pObj=obj->pIndexData; board_num=obj->value[3]; /* First find the board, and if not there, create one. */ for (board=first_board; board != NULL; board=board->next) { if (board->vnum==board_num) break; } if (board==NULL) board=load_board(obj); if ( board->vnum == OBJ_VNUM_ALLI_BOARD ) board->vnum = board_num; /* check here to see if player allowed to read board */ if (board->min_read_lev > get_trust(ch) && board->min_read_lev > ch-> level ) { send_to_char("You are not allowed to look at this board.\n\r",ch); return; } if (obj->value[4] != -1 && obj->value[4] != ch->pcdata->alliance && get_trust(ch)<84) { send_to_char( "This is an alliance board. You are not part of the alliance.\n\r", ch ); return; } send_to_char( "This is a notice board.\n\r", ch ); send_to_char( "Type READ <num> to read message <num>, and WRITE <title> to post.\n\r", ch ); send_to_char( "Type WRITE to: <player> for a private message.\n\r",ch); send_to_char( "Type DELETE <num> to delete message <num>.\n\r", ch ); send_to_char( "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\r", ch ); for ( msg = board->first_message; msg != NULL; msg = msg->next ) { cnt++; sprintf( buf, "[%3d] %12s : %s", cnt, msg->author, msg->title ); send_to_char( buf, ch ); } if ( cnt == 0 ) /* then there were no messages here */ { send_to_char( " There are no messages right now!\n\r", ch ); } send_to_char( "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\r", ch ); return; } BOARD_DATA * load_board(OBJ_DATA * obj) { FILE * board_file; char buf[255]; char * word; char letter; time_t message_time; time_t expiry_time; BOARD_DATA * board; MESSAGE_DATA * message; int vnum; OBJ_INDEX_DATA *pObj = obj->pIndexData; GET_FREE(board, board_free); LINK(board, first_board, last_board, next, prev); board->expiry_time=pObj->value[0]; board->min_read_lev=pObj->value[1]; board->min_write_lev=pObj->value[2]; board->vnum=obj->value[3]; board->first_message=NULL; board->last_message=NULL; vnum = board->vnum; fclose(fpReserve); sprintf(buf,"%s/board.%i",BOARD_DIR,vnum); if ( (board_file=fopen(buf,"r")) != NULL ) { /* Read in Optional board parameters */ for ( ; ; ) { if (feof(board_file)) break; word=fread_word(board_file); if (!str_cmp(word,"ExpiryTime")) { board->expiry_time=fread_number(board_file); fread_to_eol(board_file); } if (!str_cmp(word,"MinReadLev")) { board->min_read_lev=fread_number(board_file); fread_to_eol(board_file); } if (!str_cmp(word,"MinWriteLev")) { board->min_write_lev=fread_number(board_file); fread_to_eol(board_file); } if (!str_cmp(word,"Messages")) { fread_to_eol(board_file); break; } } if (board->expiry_time > 0) expiry_time=time(NULL)-(board->expiry_time)*3600*24; else expiry_time=0; /* Now read in messages */ for ( ; ; ) { if (feof(board_file)) break; letter=fread_letter(board_file); if (letter=='S') break; if (letter!='M') { bug("Letter in message file not M",0); break; } /* check time */ message_time=(time_t) fread_number(board_file); if (feof(board_file)) break; if (message_time < expiry_time ) { char *dumpme; dumpme = fread_string(board_file); /* author */ free_string( dumpme ); dumpme = fread_string(board_file); /* title */ free_string( dumpme ); dumpme = fread_string(board_file); /* message */ free_string( dumpme ); } else { GET_FREE(message, message_free); message->datetime=message_time; message->author=fread_string(board_file); message->title=fread_string(board_file); message->message=fread_string(board_file); LINK(message, board->first_message, board->last_message, next, prev); message->board=board; } } /* Now close file */ fclose(board_file); } fpReserve=fopen(NULL_FILE,"r"); return board; } void save_board(BOARD_DATA * board,CHAR_DATA * ch) { char buf[MAX_STRING_LENGTH]; FILE * board_file; MESSAGE_DATA * message; int vnum; fclose(fpReserve); vnum = board->vnum; sprintf(buf,"%s/board.%i",BOARD_DIR,vnum); if ( (board_file=fopen(buf,"w")) == NULL ) { send_to_char("Cannot save board, please contact an immortal.\n\r",ch); bug("Could not save file board.%i.",vnum); fpReserve=fopen(NULL_FILE,"r"); return; } fprintf(board_file,"ExpiryTime %i\n",board->expiry_time); fprintf(board_file,"MinReadLev %i\n",board->min_read_lev); fprintf(board_file,"MaxWriteLev %i\n",board->min_write_lev); /* Now print messages */ fprintf(board_file,"Messages\n"); for ( message = board->first_message; message; message = message->next ) { fprintf(board_file,"M%i\n",(int) (message->datetime)); strcpy(buf,message->author); /* Must do copy, not allowed to change string directly */ smash_tilde(buf); fprintf(board_file,"%s~\n",buf); strcpy(buf,message->title); smash_tilde(buf); fprintf(board_file,"%s~\n",buf); strcpy(buf,message->message); smash_tilde(buf); fprintf(board_file,"%s~\n",buf); } fprintf(board_file,"S\n"); fclose(board_file); fpReserve=fopen(NULL_FILE,"r"); return; } void do_delete( CHAR_DATA *ch, char * argument) { OBJ_DATA * object; BOARD_DATA * board; MESSAGE_DATA * msg; OBJ_INDEX_DATA * pObj; int vnum; int mess_num; int cnt=0; extern OBJ_DATA *map_obj[MAX_MAPS][MAX_MAPS]; if ( IS_NPC( ch ) ) { send_to_char( "NPCs may *not* delete messages. So there.\n\r", ch ); return; } if ( argument[0] == '\0' ) { send_to_char( "You need to specify a message number to delete!\n\r", ch ); send_to_char( "If it's your character you wish to delete, use the pdelete command.\n\r", ch ); return; } if ( ch == NULL || ch->desc == NULL || ch->desc->connected != CON_PLAYING ) return; for ( object = map_obj[ch->x][ch->y] ; object != NULL; object = object -> next_in_room ) { if (object->item_type == ITEM_BOARD ) if ( !NOT_IN_ROOM(ch,object) ) break; } if (object == NULL) { send_to_char("Delete on what?\n\r.",ch); return; } pObj=object->pIndexData; vnum=object->value[3]; /* First find the board, and if not there, create one. */ for (board=first_board; board != NULL; board=board->next) { if (board->vnum==vnum) break; } if (board==NULL) board=load_board(object); if ( board->vnum == OBJ_VNUM_ALLI_BOARD ) board->vnum = vnum; /* check here to see if player allowed to write to board */ if (board->min_write_lev > get_trust(ch) && board->min_write_lev > ch->level ) { send_to_char("You are not allowed to delete on this board.\n\r",ch); return; } if (object->value[4] != -1 && object->value[4] != ch->pcdata->alliance && get_trust(ch)<84) { send_to_char( "This is an alliance board. You are not part of the alliance.\n\r", ch ); return; } cnt=0; mess_num= is_number(argument) ? atoi(argument) : 0; for ( msg = board->first_message; msg != NULL; msg = msg->next ) if ( ++cnt == mess_num ) /* then this the message!!! */ break; if (msg==NULL) { send_to_char( "No such message!\n\r", ch ); return; } /* See if person is writer or is recipient */ if ( str_cmp(ch->name,msg->author) && !is_name(ch->name,msg->title) && get_trust(ch) < 86 ) { if ( object->value[4] == -1 || str_cmp(alliance_table[object->value[4]].leader,ch->name)) { send_to_char( "Not your message.\n\r",ch); return; } } /* Now delete message */ UNLINK(msg, board->first_message, board->last_message, next, prev); PUT_FREE(msg, message_free); save_board(board,ch); return; } void do_show_message( CHAR_DATA *ch ,int mess_num, OBJ_DATA * obj ) { /* Show message <mess_num> to character. * check that message vnum == board vnum */ BOARD_DATA * board; OBJ_INDEX_DATA * pObj; int vnum; MESSAGE_DATA * msg; int cnt = 0; bool mfound = FALSE; char buf[MAX_STRING_LENGTH]; char to_check[MAX_INPUT_LENGTH]; char * to_person; char private_name[MAX_INPUT_LENGTH]; pObj=obj->pIndexData; vnum=obj->value[3]; /* First find the board, and if not there, create one. */ for (board=first_board; board != NULL; board=board->next) { if (board->vnum==vnum) break; } if (board==NULL) board=load_board(obj); if ( board->vnum == OBJ_VNUM_ALLI_BOARD ) board->vnum = vnum; /* check here to see if player allowed to read board */ if ( board->min_read_lev > get_trust(ch) && board->min_read_lev > ch->level ) { send_to_char("You are not allowed to look at this board.\n\r",ch); return; } if (obj->value[4] != -1 && obj->value[4] != ch->pcdata->alliance && get_trust(ch)<84) { send_to_char( "This is an alliance board. You are not part of the alliance.\n\r", ch ); return; } for ( msg = board->first_message; msg != NULL; msg = msg->next ) { if ( ++cnt == mess_num ) /* then this the message!!! */ { mfound = TRUE; to_person = one_argument(msg->title,to_check); to_person = one_argument( to_person, private_name ); if ( !str_cmp(to_check,"to:") && str_prefix(private_name,ch->name) && str_cmp(msg->author,ch->name) && ch->trust < 84 ) { send_to_char("This is a private message.\n\r",ch); break; } sprintf( buf, "** [%d] %12s : %s ** \n\r\n\r%s\n\r", cnt, msg->author, msg->title , msg->message ); send_to_char( buf, ch ); break; } } if (!mfound) { send_to_char( "No such message!\n\r", ch ); } return; } void do_pwrite( CHAR_DATA *ch, char *argument ) { char buf[MSL]; sprintf(buf,"to: %s",argument); do_write(ch,buf); return; } void do_write( CHAR_DATA *ch, char *argument ) { OBJ_DATA * object; BOARD_DATA * board; MESSAGE_DATA * msg; OBJ_INDEX_DATA * pObj; int vnum; extern char str_empty[1]; char buf[MAX_STRING_LENGTH]; extern OBJ_DATA *map_obj[MAX_MAPS][MAX_MAPS]; if ( IS_NPC( ch ) ) { send_to_char( "NPCs may *not* write messages. So there.\n\r", ch ); return; } if ( argument[0] == '\0' ) { send_to_char( "You need to specify a header for your message!\n\r", ch ); return; } for ( object = map_obj[ch->x][ch->y] ; object != NULL; object = object -> next_in_room ) { if (object->item_type == ITEM_BOARD && !NOT_IN_ROOM(ch,object) ) break; } if (object == NULL) { send_to_char("Write on what?\n\r.",ch); return; } pObj=object->pIndexData; vnum=object->value[3]; /* First find the board, and if not there, create one. */ for (board=first_board; board != NULL; board=board->next) { if (board->vnum==vnum) break; } if (board==NULL) board=load_board(object); if ( board->vnum == OBJ_VNUM_ALLI_BOARD ) board->vnum = vnum; /* check here to see if player allowed to write to board */ if (board->min_write_lev > get_trust(ch) && board->min_write_lev > ch->level ) { send_to_char("You are not allowed to write on this board.\n\r",ch); return; } if (object->value[4] != -1 && object->value[4] != ch->pcdata->alliance && get_trust(ch)<84) { send_to_char( "This is an alliance board. You are not part of the alliance.\n\r", ch ); return; } GET_FREE(msg, message_free); /* Dont put message in list till we */ msg->datetime=time(NULL); /* we are sure we can edit. */ sprintf( buf, "%s @@a%s@@N", argument, (char *) ctime( ¤t_time ) ); if ( msg->title != NULL ) free_string( msg->title ); msg->title=str_dup(buf); if ( msg->author != NULL ) free_string( msg->author ); msg->author=str_dup(ch->name); msg->message=NULL; msg->board=board; /* Now actually run the edit prog. */ write_start(&msg->message,finished_editing,msg,ch); if (msg->message != &str_empty[0] ) { send_to_char("Editing message. Type .help for help.\n\rTo exit the editor, type @@e.@@N\n\r",ch); LINK(msg, board->first_message, board->last_message, next, prev); } else { send_to_char("Could not add message.\n\r",ch); PUT_FREE(msg, message_free); } return; } /* Deals with taking message out of list if user aborts... */ void finished_editing( MESSAGE_DATA * msg, char * * dest, CHAR_DATA * ch, bool saved) { #ifdef CHECK_VALID_BOARD MESSAGE_DATA * SrchMsg; #endif if (!saved) { #ifdef CHECK_VALID_BOARD for (SrchMsg=msg->board->messages;SrchMsg != NULL;SrchMsg=SrchMsg->next) if (SrchMsg==msg) break; if (SrchMsg==NULL) { /* Could not find this message in board list, just lose memory. */ return; } #endif UNLINK(msg, msg->board->first_message, msg->board->last_message, next, prev); PUT_FREE(msg, message_free); } else { save_board(msg->board,ch); } return; } void do_read( CHAR_DATA *ch, char *argument ) { OBJ_DATA * obj; extern OBJ_DATA *map_obj[MAX_MAPS][MAX_MAPS]; if ( ( argument[0] == '\0' ) || !is_number( argument ) ) { send_to_char( "Read what??\n\r", ch ); return; } for ( obj = map_obj[ch->x][ch->y]; obj != NULL; obj = obj->next_in_room ) { if (obj->item_type == ITEM_BOARD && !NOT_IN_ROOM(ch,obj) ) break; } if ( obj == NULL ) { send_to_char( "Read What??\n\r", ch ); return; } /* Hopefully, by now there should be a board in the room, and the * player should have supplied some sort of argument.... */ do_show_message( ch, atoi( argument ), obj ); return; } void do_edit_message( CHAR_DATA *ch ,int mess_num, OBJ_DATA * obj ) { /* Show message <mess_num> to character. * check that message vnum == board vnum */ BOARD_DATA * board; OBJ_INDEX_DATA * pObj; int vnum; MESSAGE_DATA * msg; int cnt = 0; bool mfound = FALSE; pObj=obj->pIndexData; vnum=obj->value[3]; /* First find the board, and if not there, create one. */ for (board=first_board; board != NULL; board=board->next) { if (board->vnum==vnum) break; } if (board==NULL) board=load_board(obj); if ( board->vnum == OBJ_VNUM_ALLI_BOARD ) board->vnum = vnum; /* check here to see if player allowed to read board */ if ( board->min_read_lev > get_trust(ch) && board->min_read_lev > ch->level ) { send_to_char("You are not allowed to even look at this board!\n\r",ch); return; } if (obj->value[4] != -1 && obj->value[4] != ch->pcdata->alliance && get_trust(ch)<84) { send_to_char( "This is an alliance board. You are not part of the alliance.\n\r", ch ); return; } for ( msg = board->first_message; msg != NULL; msg = msg->next ) { if ( ++cnt == mess_num ) /* then this the message!!! */ { mfound = TRUE; if ( str_cmp(msg->author,ch->name) ) { send_to_char("Not your message to edit!\n\r",ch); return; } else { build_strdup( &msg->message, "$edit", TRUE, ch); } } } if ( !mfound ) send_to_char( "No such message!\n\r", ch ); return; } void do_edit( CHAR_DATA *ch, char *argument ) { OBJ_DATA * obj; extern OBJ_DATA *map_obj[MAX_MAPS][MAX_MAPS]; if ( ( argument[0] == '\0' ) || !is_number( argument ) ) { send_to_char( "Edit what??\n\r", ch ); return; } for ( obj = map_obj[ch->x][ch->y]; obj != NULL; obj = obj->next_in_room ) { if (obj->item_type == ITEM_BOARD && !NOT_IN_ROOM(ch,obj) ) break; } if ( obj == NULL ) { send_to_char( "Edit What??\n\r", ch ); return; } /* Hopefully, by now there should be a board in the room, and the * player should have supplied some sort of argument.... */ do_edit_message( ch, atoi( argument ), obj ); return; }