/*___________________________________________________________________________*
)()( DalekenMUD 1.12 (C) 2000 )()(
`][' by Martin Thomson, Lee Brooks, `]['
|| Ken Herbert and David Jacques ||
|| ----------------------------------------------------------------- ||
|| Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, ||
|| David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. ||
|| Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael ||
|| Chastain, Michael Quan, and Mitchell Tse. ||
|| Original Diku Mud copyright (C) 1990, 1991 ||
|| by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, ||
|| Tom Madsen, and Katja Nyboe. ||
|| ----------------------------------------------------------------- ||
|| Any use of this software must follow the licenses of the ||
|| creators. 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. ||
|| ----------------------------------------------------------------- ||
|| note.c ||
|| Note writing and notice board code. ||
*_/<>\_________________________________________________________________/<>\_*/
#include "mud.h"
#include "db.h"
bool is_note_to args( ( CHAR_DATA *ch, NOTE_DATA * pnote ) );
void note_attach args( ( CHAR_DATA *ch ) );
void note_remove args( ( CHAR_DATA *ch, NOTE_DATA * pnote ) );
void note_show args( ( CHAR_DATA *ch, NOTE_DATA * pnote, int vnum ) );
void save_board args( ( BOARD_DATA *board ) );
struct board_data board_table [MAX_BOARD] =
{
{
"general", "General discussion board", 0, 2,
"all", "", 21
},
{
"personal", "Personal messages", 0, 1,
"", "all", 28
},
{
"immortal", "Messages from Immortals", 0, L_APP,
"", "", 60
},
{
"quest", "Questing announcements/info", 0, 50,
"", "", 21
},
{
"religion", "Religion messages", 0, 5,
"religion", "all", 21
},
{
"clan", "Clan messages", 0, 5,
"clan order guild", "all", 21
},
{
"ideas", "Suggestions for improvements", 0, 2,
"", "", 60
},
{
"bugs", "Typos, bugs, errors", 0, 1,
"", "", 60
}
};
/*
* Find a board number based on a string.
*/
int board_lookup( const char *name )
{
int i;
for( i = 0; i < MAX_BOARD; i++ )
if( !str_prefix( name, board_table[i].short_name ) )
return i;
return -1;
}
/*
* Find the number of a board.
*/
int board_number( const BOARD_DATA *board )
{
int i;
for ( i = 0; i < MAX_BOARD; i++ )
if ( board == &board_table[i] )
return i;
return -1;
}
void note_attach( CHAR_DATA *ch )
{
NOTE_DATA *pnote;
if( ch->pcdata->note )
return;
if( !note_free )
{
pnote = alloc_perm( sizeof( *ch->pcdata->note ) );
}
else
{
pnote = note_free;
note_free = note_free->next;
}
pnote->next = NULL;
pnote->sender = str_dup( ch->name );
pnote->date = str_dup( "" );
pnote->to_list = str_dup( "" );
pnote->subject = str_dup( "" );
pnote->text = str_dup( "" );
pnote->expire = 0;
ch->pcdata->note = pnote;
return;
}
/*
* Recycle a note.
*/
void free_note( NOTE_DATA *pnote )
{
if( pnote->sender )
free_string( pnote->sender );
if( pnote->to_list )
free_string( pnote->to_list );
if( pnote->subject )
free_string( pnote->subject );
if( pnote->date )
free_string( pnote->date );
if( pnote->text )
free_string( pnote->text );
pnote->next = note_free;
note_free = pnote;
return;
}
/*
* Remove list from the list. Do not free note.
*/
void unlink_note( BOARD_DATA *board, NOTE_DATA *note )
{
NOTE_DATA *p;
if( board->note_first == note )
board->note_first = note->next;
else
{
for( p = board->note_first; p && p->next != note; p = p->next )
;
if( !p )
bug( "Unlink_note: couldn't find note." );
else
p->next = note->next;
}
return;
}
void note_remove( CHAR_DATA *ch, NOTE_DATA *pnote )
{
BOARD_DATA *board;
NOTE_DATA *prev;
const char *to_list;
char to_new [ MAX_INPUT_LENGTH ];
char to_one [ MAX_INPUT_LENGTH ];
/*
* Build a new to_list.
* Strip out this recipient.
*/
to_new[0] = '\0';
to_list = pnote->to_list;
while( *to_list != '\0' )
{
to_list = one_argument( to_list, to_one );
if( to_one[0] != '\0' && str_cmp( ch->name, to_one ) )
{
strcat( to_new, " " );
strcat( to_new, to_one );
}
}
/*
* Just a simple recipient removal?
*/
if( str_cmp( ch->name, pnote->sender ) && to_new[0] != '\0' )
{
free_string( pnote->to_list );
pnote->to_list = str_dup( to_new + 1 );
return;
}
board = ch->pcdata->board;
/*
* Remove note from linked list.
*/
if( pnote == board->note_first )
{
board->note_first = pnote->next;
}
else
{
for( prev = board->note_first; prev; prev = prev->next )
{
if ( prev->next == pnote )
break;
}
if( !prev )
{
bug( "Note_remove: pnote not found." );
return;
}
prev->next = pnote->next;
}
free_note( pnote );
save_board( board );
return;
}
bool is_note_to( CHAR_DATA *ch, NOTE_DATA *pnote )
{
const char *argument;
char word[MAX_INPUT_LENGTH];
if( !str_cmp( ch->name, pnote->sender ) )
return TRUE;
argument = pnote->to_list;
while( argument[0] != '\0' )
{
argument = one_argument( argument, word );
if( word[0] == '!' && !str_cmp( ch->name, &word[1] ) )
return FALSE;
if( !str_cmp( "all", word ) || !str_cmp( ch->name, word ) )
return TRUE;
if( IS_IMMORTAL( ch ) && ( !str_cmp( "immortal", word )
|| !str_cmp( "immortals", word )
|| !str_cmp( "imm", word )
|| !str_cmp( "immort", word ) ) )
return TRUE;
if( IS_NPC( ch ) )
continue;
if( !str_cmp( word, "religion" ) && ch->pcdata->religion )
return TRUE;
if( ( !str_cmp( word, "clan" ) || !str_cmp( word, "clan-all" ) )
&& ch->pcdata->clan
&& ch->pcdata->clan->clan_type == CLAN_NORMAL )
return TRUE;
if( ( !str_cmp( word, "guild" ) || !str_cmp( word, "guild-all" ) )
&& ch->pcdata->clan
&& ch->pcdata->clan->clan_type == CLAN_GUILD )
return TRUE;
if( ( !str_cmp( word, "order" ) || !str_cmp( word, "order-all" ) )
&& ch->pcdata->clan
&& ch->pcdata->clan->clan_type == CLAN_ORDER )
return TRUE;
if( !str_prefix( "religion-", word ) && ch->pcdata->religion
&& !str_cmp( &word[9], ch->pcdata->religion->name ) )
return TRUE;
else if( !str_prefix( "clan-", word ) && ch->pcdata->clan
&& !str_cmp( &word[5], ch->pcdata->religion->name ) )
return TRUE;
if( is_number( word )
&& get_trust( ch ) >= atoi( word ) )
return TRUE;
}
return FALSE;
}
/*
* Construct a clan list depending on who the note is targeted at.
*/
void note_target( CHAR_DATA *ch )
{
const char *argument;
char word[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
buf[0] = '\0';
argument = ch->pcdata->note->to_list;
while( argument[0] != '\0' )
{
argument = one_argument( argument, word );
strcat( buf, " " );
strcat( buf, word );
if( !str_cmp( word, "religion" ) && ch->pcdata->religion )
{
strcat( buf, "-" );
strcat( buf, ch->pcdata->religion->name );
}
else if( !str_cmp( word, "clan" ) && ch->pcdata->clan )
{
strcat( buf, "-" );
strcat( buf, ch->pcdata->religion->name );
}
}
free_string( ch->pcdata->note->to_list );
ch->pcdata->note->to_list = str_dup( &buf[1] );
}
int unread_notes( CHAR_DATA *ch, BOARD_DATA *board )
{
NOTE_DATA *note;
time_t last_read;
int count = 0;
if( board->read_level > get_trust( ch ) )
return -1;
last_read = ch->pcdata->last_note[ board_number( board ) ];
for( note = board->note_first; note; note = note->next )
if( is_note_to( ch, note ) && last_read < note->date_stamp )
count++;
return count;
}
int total_notes( CHAR_DATA *ch, BOARD_DATA *board )
{
NOTE_DATA *note;
int count = 0;
if( board->read_level > get_trust( ch ) )
return -1;
for( note = board->note_first; note; note = note->next )
if( is_note_to( ch, note ) )
count++;
return count;
}
bool next_board( CHAR_DATA *ch )
{
int i = board_number( ch->pcdata->board ) + 1;
while( i < MAX_BOARD && unread_notes( ch, &board_table[i] ) == -1 )
i++;
if( i == MAX_BOARD )
return FALSE;
ch->pcdata->board = &board_table[i];
return TRUE;
}
void note_show( CHAR_DATA *ch, NOTE_DATA * pnote, int vnum )
{
char buf1[MAX_STRING_LENGTH * 2];
char buf[MAX_STRING_LENGTH];
time_t *last_note = &ch->pcdata->last_note[board_number(ch->pcdata->board)];
strcpy( buf1, LINE_SEPARATOR );
sprintf( buf, "&b[&c%3d&b] &bTo: &c%s\n\r",
vnum, pnote->to_list );
strcat( buf1, buf );
sprintf( buf, "&bDate: &c%s&b &bSender: &c%s\n\r",
pnote->date, pnote->sender );
strcat( buf1, buf );
sprintf( buf, "&rSubject: &y%s\n\r",
pnote->subject );
strcat( buf1, buf );
strcat( buf1, LINE_SEPARATOR );
strcat( buf1, pnote->text );
strcat( buf1, "&n" );
send_to_char( buf1, ch );
*last_note = UMAX( *last_note, pnote->date_stamp );
}
void do_board( CHAR_DATA *ch, const char *argument )
{
int i;
int count;
char buf [ MAX_INPUT_LENGTH ];
if( IS_NPC( ch ) )
return;
if( argument[0] == '\0' )
{
int unread;
count = 1;
send_to_char( "\n\r&B-----------------------------[ &WBULLETIN "
"BOARDS&B ]-------------------------------&x\n\r", ch );
for ( i = 0; i < MAX_BOARD; i++ )
{
unread = unread_notes( ch, &board_table[i] );
if( unread == -1 )
continue;
sprintf( buf, "&g[%2d] %c%c %s%4d/%4d &c%s%-12s&g - %s\n\r",
count,
( board_table[i].read_level <= get_trust( ch ) )
? 'R' : ' ',
( board_table[i].write_level <= get_trust( ch ) )
? 'W' : ' ',
( unread == 0 ) ? "&g" : "&r",
unread, total_notes( ch, &board_table[i] ),
( !strcmp( ch->pcdata->board->short_name,
board_table[i].short_name ) ) ? "&B*&c" : " ",
board_table[i].short_name, board_table[i].long_name );
send_to_char( buf, ch );
count++;
}
send_to_char( LINE_SEPARATOR, ch );
return;
}
i = atoi( argument ) - 1;
if( is_number( argument ) )
{
if( i < 0 || i >= MAX_BOARD )
{
send_to_char( "No such board.\n\r", ch );
return;
}
}
else
{
if( ( i = board_lookup( argument ) ) == -1 )
{
send_to_char( "No such board.\n\r", ch );
return;
}
}
if( board_table[i].read_level > get_trust( ch ) )
{
send_to_char( "No such board.\n\r", ch );
return;
}
ch->pcdata->board = &board_table[i];
sprintf( buf, "&gCurrent board changed to &c%s&g. You can %s here.\n\r",
board_table[i].short_name,
( get_trust( ch ) < board_table[i].write_level )
? "only read" : "both read and write" );
send_to_char( buf, ch );
return;
}
/*
* Date stamp idea comes from Alander of ROM
*/
void do_note( CHAR_DATA *ch, const char *argument )
{
NOTE_DATA *pnote;
BOARD_DATA *board;
char buf[MAX_STRING_LENGTH];
char arg[MAX_INPUT_LENGTH];
int vnum;
int anum;
time_t *last_note;
if( IS_NPC( ch ) )
return;
argument = one_argument( argument, arg );
if( arg[0] == '\0' )
strcpy( arg, "read" );
board = ch->pcdata->board;
last_note = &ch->pcdata->last_note[board_number(board)];
if( !str_cmp( arg, "list" ) )
{
char buf1[MAX_STRING_LENGTH * 2];
anum = 0;
vnum = 0;
sprintf( buf1, "&gListing of the %s board:\n\r", board->short_name );
strcat( buf1, LINE_SEPARATOR );
for( pnote = board->note_first; pnote; pnote = pnote->next )
{
vnum++;
if( is_note_to( ch, pnote ) )
{
anum++;
sprintf( buf, "&b[&c%3d%s&b] &r%s: &y%s&n\n\r",
vnum,
( pnote->date_stamp > *last_note
&& str_cmp( pnote->sender, ch->name ) ) ? "&rN" : " ",
pnote->sender, pnote->subject );
strcat( buf1, buf );
}
}
sprintf( buf, "%d/%d visible notes on this board.&n\n\r",
anum, vnum );
strcat( buf1, buf );
send_to_char( buf1, ch );
return;
}
if( !str_cmp( arg, "read" ) )
{
bool fAll;
if( !str_cmp( argument, "all" ) )
{
fAll = TRUE;
anum = 0;
}
else if( argument[0] == '\0' || !str_prefix( argument, "next" ) )
/* read next unread note */
{
vnum = 0;
for( pnote = board->note_first; pnote; pnote = pnote->next )
{
vnum++;
if( is_note_to( ch, pnote )
&& str_cmp( ch->name, pnote->sender )
&& *last_note < pnote->date_stamp )
break;
}
if( !pnote ) /* Ensure they are caught up */
{
*last_note = current_time;
}
if( pnote )
{
note_show( ch, pnote, vnum );
return;
}
send_to_char( "&gNo new notes in this board", ch );
if( next_board( ch ) )
charprintf( ch, "; changed to &c%s&g board",
ch->pcdata->board->short_name );
send_to_char( ".&n\n\r", ch );
return;
}
else if( is_number( argument ) )
{
fAll = FALSE;
anum = atoi( argument );
}
else
{
send_to_char( "Note read which number?\n\r", ch );
return;
}
vnum = 0;
for( pnote = board->note_first; pnote; pnote = pnote->next )
{
vnum++;
if( is_note_to( ch, pnote ) )
{
if( fAll )
{
*last_note = UMAX( *last_note, pnote->date_stamp );
}
else if( vnum == anum )
{
note_show( ch, pnote, vnum );
return;
}
}
}
if( fAll )
send_to_char( "&gAll notes caught up.&n\n\r", ch );
else
send_to_char( "No such note.\n\r", ch );
return;
}
if( !str_cmp( arg, "write" ) || !str_cmp( arg, "edit" ) )
{
note_attach( ch );
string_edit( ch, &ch->pcdata->note->text );
return;
}
if( !str_cmp( arg, "expire" ) && IS_IMMORTAL( ch ) )
{
if( !is_number( argument ) )
{
send_to_char( "Note expire which number of days?\n\r", ch );
return;
}
anum = atoi( argument );
ch->pcdata->note->expire = current_time + anum * 24 * 3600;
send_to_char( "Expiry time set.\n\r", ch );
return;
}
if( !str_cmp( arg, "+" ) )
{
note_attach( ch );
strcpy( buf, ch->pcdata->note->text );
if( strlen( buf ) + strlen( argument ) >= MAX_STRING_LENGTH - 200 )
{
send_to_char( "Note too long.\n\r", ch );
return;
}
strcat( buf, argument );
strcat( buf, "\n\r" );
free_string( ch->pcdata->note->text );
ch->pcdata->note->text = str_dup( buf );
send_to_char( "Line appended to note.\n\r", ch );
return;
}
if( !str_cmp( arg, "subject" ) )
{
note_attach( ch );
free_string( ch->pcdata->note->subject );
ch->pcdata->note->subject = str_dup( argument );
send_to_char( "Subject set.\n\r", ch );
return;
}
if( !str_cmp( arg, "to" ) )
{
note_attach( ch );
free_string( ch->pcdata->note->to_list );
ch->pcdata->note->to_list = str_dup( argument );
send_to_char( "Note recipient(s) set.\n\r", ch );
return;
}
if( !str_cmp( arg, "catchup" ) )
{
if( get_trust( ch ) < ch->pcdata->board->write_level )
{
send_to_char( "You can't ignore the notes on this board.\n\r", ch );
return;
}
*last_note = current_time;
send_to_char( "All messages skipped.\n\r", ch );
return;
}
if( !str_cmp( arg, "clear" ) )
{
if ( ch->pcdata->note )
{
free_note( ch->pcdata->note );
ch->pcdata->note = NULL;
}
send_to_char( "Note cleared.\n\r", ch );
return;
}
if( !str_cmp( arg, "show" ) )
{
if( !ch->pcdata->note )
{
send_to_char( "You have no note in progress.\n\r", ch );
return;
}
free_string( ch->pcdata->note->date );
ch->pcdata->note->date = str_dup( "now" );
note_show( ch, ch->pcdata->note, 0 );
free_string( ch->pcdata->note->date );
ch->pcdata->note->date = str_dup( "" );
return;
}
if( !str_cmp( arg, "post" ) || !str_prefix( arg, "send" ) )
{
FILE *fp;
char strsave [ MAX_INPUT_LENGTH ];
if( !ch->pcdata->note )
{
send_to_char( "You have no note in progress.\n\r", ch );
return;
}
if( get_trust( ch ) < ch->pcdata->board->write_level )
{
send_to_char( "You can't post notes on this board.\n\r", ch );
return;
}
if( ch->pcdata->note->to_list[0] == '\0' )
{
free_string( ch->pcdata->note->to_list );
if( board->include[0] != '\0' )
{
ch->pcdata->note->to_list = str_dup( board->include );
}
else
{
ch->pcdata->note->to_list = str_dup( "all" );
}
charprintf( ch, "Default recipient '&g%s&n' chosen.\n\r",
ch->pcdata->note->to_list );
}
if( board->include[0] != '\0' )
{
if( !is_name( board->include, ch->pcdata->note->to_list ) )
{
sprintf( buf, "%s %s", ch->pcdata->note->to_list,
board->include );
free_string( ch->pcdata->note->to_list );
ch->pcdata->note->to_list = str_dup( buf );
charprintf( ch, "Included mandatory recipient(s) '&g%s&n'.\n\r",
board->include );
}
}
if( board->exclude[0] != '\0' )
{
if( is_name( board->exclude, ch->pcdata->note->to_list ) )
{
charprintf( ch, "The recipient may not include '&y%s&n'.\n\r",
board->exclude );
return;
}
}
note_target( ch );
if( !str_cmp( ch->pcdata->note->to_list, "" ) )
{
send_to_char(
"You need to provide a recipient (name, all, or immortal).\n\r",
ch );
return;
}
if( !str_cmp( ch->pcdata->note->subject, "" ) )
{
send_to_char( "You need to provide a subject.\n\r", ch );
return;
}
ch->pcdata->note->next = NULL;
ch->pcdata->note->date = str_dup( myctime( ¤t_time ) );
ch->pcdata->note->date_stamp = current_time;
if( !ch->pcdata->note->expire )
ch->pcdata->note->expire = current_time +
board->purge_days * 24 * 3600;
if( !board->note_first )
{
board->note_first = ch->pcdata->note;
}
else
{
for( pnote = board->note_first; pnote->next; pnote = pnote->next )
;
pnote->next = ch->pcdata->note;
}
pnote = ch->pcdata->note;
ch->pcdata->note = NULL;
sprintf( strsave, "%s%s", NOTE_DIR, board->short_name );
if ( !( fp = open_file( strsave, "a", FALSE ) ) )
{
perror( board->short_name );
}
else
{
write_next_item( fp, "Note", pnote );
close_file( fp );
}
{ /* notify everyone */
DESCRIPTOR_DATA *d;
char buf[MAX_STRING_LENGTH];
sprintf( buf, "&g'I have a note here from %s&g'.&n\n\r",
ch->name );
for( d = descriptor_list; d; d = d->next )
{
if( is_note_to( CH( d ), pnote ) )
{
send_to_char( "Just before your dog kills him,\n\r the postman says: ", d->character );
send_to_char( buf, d->character );
}
}
}
charprintf( ch, "Note posted. This note will expire %s.\n\r",
myctime( &pnote->expire ) );
return;
}
if( !str_cmp( arg, "remove" ) )
{
if( !is_number( argument ) )
{
send_to_char( "Note remove which number?\n\r", ch );
return;
}
anum = atoi( argument );
vnum = 0;
for( pnote = board->note_first; pnote; pnote = pnote->next )
{
vnum++;
if( is_note_to( ch, pnote ) && vnum == anum )
{
note_remove( ch, pnote );
send_to_char( "Ok.\n\r", ch );
return;
}
}
send_to_char( "No such note.\n\r", ch );
return;
}
send_to_char( "Huh? Type 'help note' for usage.\n\r", ch );
return;
}
void load_notes( )
{
FILE *fpArch;
NOTE_DATA *new_note, *pnotelast;
char strsave[ MAX_INPUT_LENGTH ];
int i, entry;
for( i = 0; i < MAX_BOARD; i++ )
{
sprintf( strsave, "%s%s", NOTE_DIR, board_table[i].short_name );
if( !( fpArea = open_file( strsave, "r", TRUE ) ) )
{
bug( "Couldn't open board for reading" );
continue;
}
pnotelast = NULL;
for( ;; )
{
new_note = read_next_item( fpArea, &entry );
if( entry < 0 || !new_note )
break;
if( new_note->expire < current_time )
{
sprintf( strsave, "%s%s.old", NOTE_DIR, board_table[i].short_name );
if( !( fpArch = open_file( strsave, "a", FALSE ) ) )
bug( "Load_notes: couldn't open arch boards for writing." );
else
{
write_next_item( fpArch, "Note", new_note );
close_file( fpArch );
}
free_note( new_note );
board_table[i].changed = TRUE;
continue;
}
if( !board_table[i].note_first )
board_table[i].note_first = new_note;
else
pnotelast->next = new_note;
pnotelast = new_note;
}
}
fpArea = NULL;
strArea[0] = '\0';
}
void save_board( BOARD_DATA *board )
{
FILE *fp;
NOTE_DATA *note;
char strsave[ MAX_INPUT_LENGTH ];
sprintf( strsave, "%s%s", NOTE_DIR, board->short_name );
if( !( fp = open_file( strsave, "w", TRUE ) ) )
perror( board->short_name );
else
{
for( note = board->note_first; note; note = note->next )
write_next_item( fp, "Note", note );
close_file( fp );
}
}
void save_notes( void )
{
int i;
for( i = 0; i < MAX_BOARD; i++ )
save_board( &board_table[i] );
}