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