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