/***************************************************************************** * DikuMUD (C) 1990, 1991 by: * * Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, * * and Katja Nyboe. * *---------------------------------------------------------------------------* * MERC 2.1 (C) 1992, 1993 by: * * Michael Chastain, Michael Quan, and Mitchell Tse. * *---------------------------------------------------------------------------* * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. * * Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, * * gorog, Grishnakh, Nivek, Tricops, and Fireblade. * *---------------------------------------------------------------------------* * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. * * Their contributions are greatly appreciated. * *---------------------------------------------------------------------------* * LoP (C) 2006, 2007, 2008, 2009 by: the LoP team. * *---------------------------------------------------------------------------* * Special boards module * *****************************************************************************/ #include <ctype.h> #include <stdio.h> #include <string.h> #include "h/mud.h" void show_obj( CHAR_DATA *ch, OBJ_DATA *obj ); void decrease_gold( CHAR_DATA *ch, int bamount, int amount ); bool has_gold( CHAR_DATA *ch, int billions, int amount ); void free_note( NOTE_DATA *pnote ); /* Defines for voting on notes. -- Narn */ #define VOTE_NONE 0 #define VOTE_OPEN 1 #define VOTE_CLOSED 2 #define VOTE_YES 0 #define VOTE_NO 1 #define VOTE_ABSTAIN 2 typedef struct board_data BOARD_DATA; struct board_data { BOARD_DATA *next, *prev; NOTE_DATA *first_note, *last_note; char *note_file; /* Filename to save notes to */ char *read_group; /* Can restrict a board to a */ char *post_group; /* council, clan, guild etc */ char *extra_readers; /* Can give read rights to players */ char *extra_removers; /* Can give remove rights to players */ short min_read_level; /* Minimum level to read a note */ short min_post_level; /* Minimum level to post a note */ short min_remove_level; /* Minimum level to remove a note */ short max_posts; /* Maximum amount of notes allowed */ short num_posts; /* Number of notes on this board */ }; BOARD_DATA *first_board, *last_board; typedef struct auction_data AUCTION_DATA; struct auction_data { AUCTION_DATA *next, *prev; char *auctstr; int count; int type; }; AUCTION_DATA *first_auction, *last_auction; bool is_note_to( CHAR_DATA *ch, NOTE_DATA *pnote ); void note_attach( CHAR_DATA *ch ); void note_remove( BOARD_DATA *board, NOTE_DATA *pnote ); void do_note( CHAR_DATA *ch, char *arg_passed, bool IS_MAIL ); bool can_read( CHAR_DATA *ch, BOARD_DATA *board ) { if( !board ) return false; if( get_trust( ch ) >= board->min_read_level ) return true; if( board->read_group ) { if( ch->pcdata->clan && !str_cmp( ch->pcdata->clan->name, board->read_group ) ) return true; if( ch->pcdata->council && !str_cmp( ch->pcdata->council->name, board->read_group ) ) return true; if( ch->pcdata->nation && !str_cmp( ch->pcdata->nation->name, board->read_group ) ) return true; } if( board->extra_readers ) { if( is_name( ch->name, board->extra_readers ) ) return true; } return false; } bool can_remove( CHAR_DATA *ch, BOARD_DATA *board ) { if( get_trust( ch ) >= board->min_remove_level ) return true; if( board->extra_removers ) { if( is_name( ch->name, board->extra_removers ) ) return true; } return false; } bool can_post( CHAR_DATA *ch, BOARD_DATA *board ) { if( !board ) return false; if( get_trust( ch ) >= board->min_post_level ) return true; if( board->post_group ) { if( ch->pcdata->clan && !str_cmp( ch->pcdata->clan->name, board->post_group ) ) return true; if( ch->pcdata->council && !str_cmp( ch->pcdata->council->name, board->post_group ) ) return true; if( ch->pcdata->nation && !str_cmp( ch->pcdata->nation->name, board->post_group ) ) return true; } return false; } BOARD_DATA *get_board( CHAR_DATA *ch, int bnum ) { BOARD_DATA *board; int bcount = 0; for( board = first_board; board; board = board->next ) { if( !can_read( ch, board ) && !can_post( ch, board ) && !can_remove( ch, board ) ) continue; if( ++bcount == bnum ) return board; } return NULL; } void free_board( BOARD_DATA *board ) { NOTE_DATA *pnote, *next_note; STRFREE( board->extra_readers ); STRFREE( board->extra_removers ); STRFREE( board->read_group ); STRFREE( board->post_group ); STRFREE( board->extra_readers ); STRFREE( board->extra_removers ); STRFREE( board->note_file ); for( pnote = board->first_note; pnote; pnote = next_note ) { next_note = pnote->next; UNLINK( pnote, board->first_note, board->last_note, next, prev ); free_note( pnote ); } UNLINK( board, first_board, last_board, next, prev ); DISPOSE( board ); } void free_boards( void ) { BOARD_DATA *board, *board_next; for( board = first_board; board; board = board_next ) { board_next = board->next; free_board( board ); } } /* board commands. */ void write_boards_txt( void ) { BOARD_DATA *tboard; FILE *fp; if( !( fp = fopen( BOARD_FILE, "w" ) ) ) { bug( "%s: can't open %s for writing!", __FUNCTION__, BOARD_FILE ); return; } for( tboard = first_board; tboard; tboard = tboard->next ) { if( !tboard->note_file ) continue; fprintf( fp, "Filename %s~\n", tboard->note_file ); if( tboard->min_read_level ) fprintf( fp, "Min_read_level %d\n", tboard->min_read_level ); if( tboard->min_post_level ) fprintf( fp, "Min_post_level %d\n", tboard->min_post_level ); if( tboard->min_remove_level ) fprintf( fp, "Min_remove_level %d\n", tboard->min_remove_level ); if( tboard->max_posts ) fprintf( fp, "Max_posts %d\n", tboard->max_posts ); if( tboard->read_group ) fprintf( fp, "Read_group %s~\n", tboard->read_group ); if( tboard->post_group ) fprintf( fp, "Post_group %s~\n", tboard->post_group ); if( tboard->extra_readers ) fprintf( fp, "Extra_readers %s~\n", tboard->extra_readers ); if( tboard->extra_removers ) fprintf( fp, "Extra_removers %s~\n", tboard->extra_removers ); fprintf( fp, "End\n" ); } fclose( fp ); fp = NULL; } BOARD_DATA *find_board( CHAR_DATA *ch ) { BOARD_DATA *board; if( ( board = get_board( ch, ch->pcdata->onboard ) ) ) return board; return NULL; } bool is_note_to( CHAR_DATA *ch, NOTE_DATA *pnote ) { if( !str_cmp( ch->name, pnote->sender ) ) return true; if( is_name( "all", pnote->to_list ) ) return true; if( is_immortal( ch ) && ( is_name( "imm", pnote->to_list ) || is_name( "immortal", pnote->to_list ) ) ) return true; if( is_avatar( ch ) && ( is_name( "av", pnote->to_list ) || is_name( "avatar", pnote->to_list ) ) ) return true; if( is_name( ch->name, pnote->to_list ) ) return true; if( is_clanned( ch ) && is_name( ch->pcdata->clan->name, pnote->to_list ) ) return true; if( is_nationed( ch ) && is_name( ch->pcdata->nation->name, pnote->to_list ) ) return true; if( is_counciled( ch ) && is_name( ch->pcdata->council->name, pnote->to_list ) ) return true; return false; } void note_attach( CHAR_DATA *ch ) { NOTE_DATA *pnote; if( ch->pnote ) return; CREATE( pnote, NOTE_DATA, 1 ); pnote->next = pnote->prev = NULL; pnote->sender = QUICKLINK( ch->name ); pnote->to_list = NULL; pnote->subject = NULL; pnote->text = NULL; pnote->first_vote = pnote->last_vote = NULL; pnote->obj = NULL; pnote->first_bid = pnote->last_bid = NULL; pnote->first_read = pnote->last_read = NULL; ch->pnote = pnote; } void gnote_attach( CHAR_DATA *ch ) { NOTE_DATA *gnote; if( !ch || !ch->pcdata || ch->pcdata->gnote ) return; CREATE( gnote, NOTE_DATA, 1 ); gnote->next = gnote->prev = NULL; gnote->sender = QUICKLINK( ch->name ); gnote->to_list = NULL; gnote->subject = NULL; gnote->text = NULL; gnote->first_vote = gnote->last_vote = NULL; gnote->obj = NULL; gnote->first_bid = gnote->last_bid = NULL; gnote->first_read = gnote->last_read = NULL; ch->pcdata->gnote = gnote; } void write_board( BOARD_DATA *board ) { FILE *fp; char filename[256]; NOTE_DATA *pnote; VOTE_DATA *vote; BID_DATA *bid; READ_DATA *read; snprintf( filename, sizeof( filename ), "%s%s", BOARD_DIR, board->note_file ); if( !board->first_note ) { remove_file( filename ); return; } /* Rewrite entire list. */ if( !( fp = fopen( filename, "w" ) ) ) { perror( filename ); return; } for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( !pnote->text || !pnote->sender || !pnote->posttime ) continue; if( pnote->sender ) fprintf( fp, "Sender %s~\n", pnote->sender ); if( pnote->posttime ) fprintf( fp, "PostTime %ld\n", pnote->posttime ); if( pnote->to_list ) fprintf( fp, "To %s~\n", pnote->to_list ); if( pnote->subject ) fprintf( fp, "Subject %s~\n", pnote->subject ); if( pnote->voting ) fprintf( fp, "Voting %d\n", pnote->voting ); for( vote = pnote->first_vote; vote; vote = vote->next ) fprintf( fp, "Vote %s~ %d\n", vote->name, vote->vote ); for( bid = pnote->first_bid; bid; bid = bid->next ) fprintf( fp, "NBid %s~ %d %d\n", bid->name, bid->bbid, bid->bid ); for( read = pnote->first_read; read; read = read->next ) fprintf( fp, "Read %s~\n", read->name ); if( pnote->aclosed ) fprintf( fp, "%s\n", "AClosed" ); if( pnote->acanceled ) fprintf( fp, "%s\n", "ACanceled" ); if( pnote->sfor || pnote->sforb ) fprintf( fp, "NSFor %d %d\n", pnote->sforb, pnote->sfor ); /* Save the obj here */ if( pnote->obj ) { if( pnote->autowin || pnote->autowinb ) fprintf( fp, "NAutoWin %d %d\n", pnote->autowinb, pnote->autowin ); fwrite_obj( NULL, pnote->obj, fp, 0, OS_AUCTION, false ); } fprintf( fp, "Text\n%s~\n", strip_cr( pnote->text ) ); fprintf( fp, "%s", "End\n\n" ); } fclose( fp ); fp = NULL; } BID_DATA *has_bidded( NOTE_DATA *pnote, CHAR_DATA *ch ) { BID_DATA *bid; if( !pnote ) return NULL; for( bid = pnote->first_bid; bid; bid = bid->next ) { if( !str_cmp( bid->name, ch->name ) ) return bid; } return NULL; } BID_DATA *check_high_bid( NOTE_DATA *pnote ) { BID_DATA *bid, *ubid = NULL; for( bid = pnote->first_bid; bid; bid = bid->next ) { if( !ubid ) ubid = bid; if( bid->bbid > ubid->bbid ) ubid = bid; if( bid->bbid == ubid->bbid && bid->bid > ubid->bid ) ubid = bid; } return ubid; } void show_bids( NOTE_DATA *pnote, CHAR_DATA *ch ) { BID_DATA *bid, *hbid = NULL; if( !( hbid = check_high_bid( pnote ) ) ) { send_to_char( "There are currently no bids.\r\n", ch ); return; } ch_printf( ch, "%s has the highest bid of %d.\r\n", hbid->name, hbid->bid ); for( bid = pnote->first_bid; bid; bid = bid->next ) { if( bid == hbid ) continue; ch_printf( ch, "%s bidded %s.\r\n", bid->name, show_big_nums( bid->bbid, bid->bid ) ); } } READ_DATA *has_read( NOTE_DATA *pnote, CHAR_DATA *ch ) { READ_DATA *read; if( !pnote ) return NULL; for( read = pnote->first_read; read; read = read->next ) { if( !str_cmp( read->name, ch->name ) ) return read; } return NULL; } int get_new_notes( BOARD_DATA *board, CHAR_DATA *ch ) { NOTE_DATA *note; READ_DATA *read; int nnew = 0; if( !ch ) return nnew; /* If NULL check all boards */ if( !board ) { for( board = first_board; board; board = board->next ) { if( !can_read( ch, board ) ) continue; for( note = board->first_note; note; note = note->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, note ) ) continue; if( !( read = has_read( note, ch ) ) ) nnew++; } } } else { if( can_read( ch, board ) ) { for( note = board->first_note; note; note = note->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, note ) ) continue; if( !( read = has_read( note, ch ) ) ) nnew++; } } } return nnew; } void show_unread_notes( CHAR_DATA *ch ) { int nnew = get_new_notes( NULL, ch ); if( nnew > 0 ) ch_printf( ch, "&[board]There %s &[board2]%d &[board]note%s on the boards you haven't read.\r\n", nnew == 1 ? "is" : "are", nnew, nnew != 1 ? "s" : "" ); } void add_read( NOTE_DATA *pnote, CHAR_DATA *ch ) { READ_DATA *read; if( !pnote ) return; if( ( read = has_read( pnote, ch ) ) ) return; CREATE( read, READ_DATA, 1 ); read->name = STRALLOC( ch->name ); LINK( read, pnote->first_read, pnote->last_read, next, prev ); } void add_bid( NOTE_DATA *pnote, CHAR_DATA *ch, int bamount, int amount ) { BID_DATA *bid; if( !pnote ) return; if( ( bid = has_bidded( pnote, ch ) ) ) { bid->bid = amount; return; } CREATE( bid, BID_DATA, 1 ); bid->name = STRALLOC( ch->name ); bid->bbid = bamount; bid->bid = amount; LINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); } /* Have they voted on it already? */ VOTE_DATA *has_voted( NOTE_DATA *pnote, CHAR_DATA *ch ) { VOTE_DATA *vote; for( vote = pnote->first_vote; vote; vote = vote->next ) { if( !str_cmp( vote->name, ch->name ) ) return vote; } return NULL; } void add_vote( NOTE_DATA *pnote, CHAR_DATA *ch, short type ) { VOTE_DATA *vote; if( !pnote ) return; /* See if they already voted if so update the note and the way they voted on it */ if( ( vote = has_voted( pnote, ch ) ) ) { if( vote->vote == VOTE_YES ) --pnote->yesvotes; if( vote->vote == VOTE_NO ) --pnote->novotes; if( vote->vote == VOTE_ABSTAIN ) --pnote->abstentions; vote->vote = type; return; } CREATE( vote, VOTE_DATA, 1 ); vote->name = STRALLOC( ch->name ); vote->vote = type; LINK( vote, pnote->first_vote, pnote->last_vote, next, prev ); } void free_vote( VOTE_DATA *vote ) { if( !vote ) return; STRFREE( vote->name ); DISPOSE( vote ); } void free_votes( NOTE_DATA *pnote ) { VOTE_DATA *vote, *nextvote; if( !pnote ) return; for( vote = pnote->first_vote; vote; vote = nextvote ) { nextvote = vote->next; UNLINK( vote, pnote->first_vote, pnote->last_vote, next, prev ); free_vote( vote ); } } void free_bid( BID_DATA *bid ) { if( !bid ) return; STRFREE( bid->name ); DISPOSE( bid ); } void free_bids( NOTE_DATA *pnote ) { BID_DATA *bid, *nextbid; if( !pnote ) return; for( bid = pnote->first_bid; bid; bid = nextbid ) { nextbid = bid->next; UNLINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); free_bid( bid ); } } void free_read( READ_DATA *read ) { if( !read ) return; STRFREE( read->name ); DISPOSE( read ); } void free_reads( NOTE_DATA *pnote ) { READ_DATA *read, *nextread; if( !pnote ) return; for( read = pnote->first_read; read; read = nextread ) { nextread = read->next; UNLINK( read, pnote->first_read, pnote->last_read, next, prev ); free_read( read ); } } void free_note( NOTE_DATA *pnote ) { STRFREE( pnote->text ); STRFREE( pnote->subject ); STRFREE( pnote->to_list ); STRFREE( pnote->sender ); pnote->obj = NULL; free_bids( pnote ); free_votes( pnote ); free_reads( pnote ); DISPOSE( pnote ); } void note_remove( BOARD_DATA *board, NOTE_DATA *pnote ) { if( !board ) { bug( "%s: null board", __FUNCTION__ ); return; } if( !pnote ) { bug( "%s: null pnote", __FUNCTION__ ); return; } UNLINK( pnote, board->first_note, board->last_note, next, prev ); --board->num_posts; free_note( pnote ); write_board( board ); } CMDF( do_noteroom ) { BOARD_DATA *board; char arg[MSL], arg_passed[MSL]; mudstrlcpy( arg_passed, argument, sizeof( arg_passed ) ); switch( ch->substate ) { case SUB_WRITING_NOTE: do_note( ch, arg_passed, false ); break; default: argument = one_argument( argument, arg ); if( !str_cmp( arg, "write" ) || !str_cmp( arg, "to" ) || !str_cmp( arg, "subject" ) || !str_cmp( arg, "show" ) ) { do_note( ch, arg_passed, false ); return; } if( !( board = find_board( ch ) ) ) { send_to_char( "There is no bulletin board here to look at.\r\n", ch ); return; } do_note( ch, arg_passed, false ); return; } } void do_note( CHAR_DATA *ch, char *arg_passed, bool IS_MAIL ) { char arg[MIL]; const char *color1, *color2; BOARD_DATA *board; int vnum, anum, first_list; NOTE_DATA *pnote = NULL; bool mfound = false; if( is_npc( ch ) ) return; if( !ch->desc ) { bug( "%s: no descriptor", __FUNCTION__ ); return; } switch( ch->substate ) { default: break; case SUB_EDITING_NOTE: if( !( pnote = ( NOTE_DATA * ) ch->dest_buf ) ) { bug( "%s: NULL ch->dest_buf", __FUNCTION__ ); return; } if( !( board = find_board( ch ) ) ) { bug( "%s: NULL BOARD", __FUNCTION__ ); return; } STRFREE( pnote->text ); pnote->text = copy_buffer( ch ); stop_editing( ch ); write_board( board ); return; case SUB_WRITING_NOTE: if( ch->dest_buf != ch->pcdata->gnote ) { bug( "%s: ch->dest_buf != ch->pcdata->gnote", __FUNCTION__ ); return; } STRFREE( ch->pcdata->gnote->text ); ch->pcdata->gnote->text = copy_buffer( ch ); stop_editing( ch ); return; } color1 = color_str( AT_NOTE, ch ); color2 = color_str( AT_NOTE2, ch ); set_char_color( AT_NOTE, ch ); arg_passed = one_argument( arg_passed, arg ); if( arg == NULL || arg[0] == '\0' || !str_cmp( arg, "list" ) ) { if( !( board = find_board( ch ) ) ) { send_to_char( "There is no board here to look at.\r\n", ch ); return; } if( !can_read( ch, board ) ) { send_to_char( "You can't make any sense of the cryptic scrawl on this board...\r\n", ch ); return; } if( ( first_list = atoi( arg_passed ) ) ) { if( first_list < 1 ) { send_to_char( "You can't read a note before 1!\r\n", ch ); return; } } set_pager_color( AT_NOTE, ch ); vnum = 0; ch_printf( ch, "Notes on the %s board.\r\n", board->note_file ? board->note_file : "(Not Set)" ); if( !board->first_note ) { ch_printf( ch, "%sThere are no notes on this board.\r\n", color2 ); return; } for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( !is_note_to( ch, pnote ) && get_trust( ch ) < PERM_IMM ) continue; vnum++; if( first_list && vnum < first_list ) continue; if( vnum > ( first_list + 15 ) ) { send_to_pager( "Only 15 notes are displayed at a time.\r\n", ch ); break; } mfound = true; pager_printf( ch, "%s%3d%s> %s%10.10s %s%12.12s %sto %s%-12.12s %s: %s%15.15s%3s\r\n", color2, vnum, color1, color2, shorttime( pnote->posttime ), color2, pnote->sender ? pnote->sender : "(No Sender)", color1, color2, pnote->to_list, color1, color2, pnote->subject ? pnote->subject : "(No Subject)", ( pnote->subject && strlen( pnote->subject ) > 15 ) ? "..." : "" ); if( pnote->voting != VOTE_NONE ) { pager_printf( ch, " %sVoting (%s%s%s):", color1, color2, pnote->voting == VOTE_OPEN ? "Open" : "Closed", color1 ); if( pnote->yesvotes ) pager_printf( ch, " %sYes: %s%3d", color1, color2, pnote->yesvotes ); if( pnote->novotes ) pager_printf( ch, " %sNo: %s%3d", color1, color2, pnote->novotes ); if( pnote->abstentions ) pager_printf( ch, " %sAbstain: %s%3d", color1, color2, pnote->abstentions ); send_to_pager( "\r\n", ch ); } } act( AT_ACTION, "$n glances over the notes.", ch, NULL, NULL, TO_CANSEE ); return; } if( !str_cmp( arg, "read" ) ) { if( !( board = find_board( ch ) ) ) { send_to_char( "There is no board here to look at.\r\n", ch ); return; } if( !can_read( ch, board ) ) { send_to_char( "You can't make any sense of the cryptic scrawl on this board...\r\n", ch ); return; } if( is_number( arg_passed ) ) anum = atoi( arg_passed ); else { send_to_char( "Note read which number?\r\n", ch ); return; } set_pager_color( AT_NOTE, ch ); vnum = 0; for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, pnote ) ) continue; if( ++vnum != anum ) continue; pager_printf( ch, "%s[%s%3d%s] %s%s%s: %s%s\r\n%s\r\n%sTo: %s%s\r\n", color1, color2, vnum, color1, color2, pnote->sender, color1, color2, pnote->subject, distime( pnote->posttime ), color1, color2, pnote->to_list ); if( pnote->yesvotes || pnote->novotes || pnote->abstentions ) pager_printf( ch, "%sVotes: Yes: %s%d %sNo: %s%d %sAbstain: %s%d\r\n", color1, color2, pnote->yesvotes, color1, color2, pnote->novotes, color1, color2, pnote->abstentions ); pager_printf( ch, "%s~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~`~.~\r\n", color1 ); pager_printf( ch, "%s%s", color2, pnote->text ); add_read( pnote, ch ); write_board( board ); act( AT_ACTION, "$n reads a note.", ch, NULL, NULL, TO_CANSEE ); return; } send_to_char( "No such note.\r\n", ch ); return; } /* Voting added by Narn, June '96 */ if( !str_cmp( arg, "vote" ) ) { char arg2[MIL]; arg_passed = one_argument( arg_passed, arg2 ); if( !( board = find_board( ch ) ) ) { send_to_char( "There is no bulletin board here.\r\n", ch ); return; } if( !can_read( ch, board ) ) { send_to_char( "You can't vote on this board.\r\n", ch ); return; } if( is_number( arg2 ) ) anum = atoi( arg2 ); else { send_to_char( "Note vote which number?\r\n", ch ); return; } vnum = 1; for( pnote = board->first_note; pnote && vnum < anum; pnote = pnote->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, pnote ) ) continue; vnum++; } if( !pnote ) { send_to_char( "No such note.\r\n", ch ); return; } /* * If you're the author of the note and can read the board you can open * and close voting, if you can read it and voting is open you can vote. */ if( !str_cmp( arg_passed, "open" ) ) { if( str_cmp( ch->name, pnote->sender ) ) { send_to_char( "You aren't the author of this note.\r\n", ch ); return; } pnote->voting = VOTE_OPEN; act( AT_ACTION, "$n opens voting on a note.", ch, NULL, NULL, TO_CANSEE ); send_to_char( "Voting opened.\r\n", ch ); write_board( board ); return; } if( !str_cmp( arg_passed, "close" ) ) { if( str_cmp( ch->name, pnote->sender ) ) { send_to_char( "You aren't the author of this note.\r\n", ch ); return; } pnote->voting = VOTE_CLOSED; act( AT_ACTION, "$n closes voting on a note.", ch, NULL, NULL, TO_CANSEE ); send_to_char( "Voting closed.\r\n", ch ); write_board( board ); return; } if( pnote->voting != VOTE_OPEN ) { send_to_char( "Voting is not open on this note.\r\n", ch ); return; } if( !str_cmp( arg_passed, "yes" ) ) { ++pnote->yesvotes; add_vote( pnote, ch, VOTE_YES ); act( AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_CANSEE ); send_to_char( "Ok.\r\n", ch ); write_board( board ); return; } if( !str_cmp( arg_passed, "no" ) ) { ++pnote->novotes; add_vote( pnote, ch, VOTE_NO ); act( AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_CANSEE ); send_to_char( "Ok.\r\n", ch ); write_board( board ); return; } if( !str_cmp( arg_passed, "abstain" ) ) { ++pnote->abstentions; add_vote( pnote, ch, VOTE_ABSTAIN ); act( AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_CANSEE ); send_to_char( "Ok.\r\n", ch ); write_board( board ); return; } /* Lets display the results if we get this far */ { int voted, col; VOTE_DATA *vote; for( voted = 0; voted < 3; voted++ ) { col = 0; pager_printf( ch, "%s Votes:\r\n", voted == 0 ? "Yes" : voted == 1 ? "No" : "Abstain" ); for( vote = pnote->first_vote; vote; vote = vote->next ) { if( vote->vote == voted ) { pager_printf( ch, " %10.10s", vote->name ); if( ++col == 3 ) { col = 0; send_to_pager( "\r\n", ch ); } } } if( col != 0 ) send_to_pager( "\r\n", ch ); } } return; } if( !str_cmp( arg, "write" ) ) { if( ch->substate == SUB_RESTRICTED ) { send_to_char( "You can't write a note from within another command.\r\n", ch ); return; } gnote_attach( ch ); ch->substate = SUB_WRITING_NOTE; ch->dest_buf = ch->pcdata->gnote; start_editing( ch, ch->pcdata->gnote->text ); return; } if( !str_cmp( arg, "edit" ) ) { if( !( board = find_board( ch ) ) ) { send_to_char( "There is no board here to look at.\r\n", ch ); return; } if( !can_read( ch, board ) ) { send_to_char( "You can't make any sense of the cryptic scrawl on this board...\r\n", ch ); return; } if( is_number( arg_passed ) ) anum = atoi( arg_passed ); else { send_to_char( "Note edit which note?\r\n", ch ); return; } set_pager_color( AT_NOTE, ch ); vnum = 0; for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, pnote ) ) continue; if( ++vnum != anum ) continue; if( get_trust( ch ) < PERM_IMM && str_cmp( ch->name, pnote->sender ) ) { send_to_char( "You can't edit that note.\r\n", ch ); return; } ch->substate = SUB_EDITING_NOTE; ch->dest_buf = pnote; start_editing( ch, pnote->text ); act( AT_ACTION, "$n starts editing a note.", ch, NULL, NULL, TO_CANSEE ); return; } send_to_char( "No such note.\r\n", ch ); return; } if( !str_cmp( arg, "subject" ) ) { if( !arg_passed || arg_passed[0] == '\0' ) { send_to_char( "What do you wish the subject to be?\r\n", ch ); return; } gnote_attach( ch ); if( !ch->pcdata->gnote ) { send_to_char( "You have no note in progress\r\n", ch ); return; } STRSET( ch->pcdata->gnote->subject, arg_passed ); send_to_char( "Ok.\r\n", ch ); return; } if( !str_cmp( arg, "to" ) ) { if( !arg_passed || arg_passed[0] == '\0' ) { send_to_char( "Please specify an addressee.\r\n", ch ); return; } arg_passed[0] = UPPER( arg_passed[0] ); if( !str_cmp( arg_passed, "all" ) || valid_pfile( arg_passed ) ) { gnote_attach( ch ); if( !ch->pcdata->gnote ) { send_to_char( "You have no note in progress\r\n", ch ); return; } STRSET( ch->pcdata->gnote->to_list, arg_passed ); send_to_char( "Ok.\r\n", ch ); return; } else { send_to_char( "No player exists by that name.\r\n", ch ); return; } } if( !str_cmp( arg, "show" ) ) { if( !ch->pcdata->gnote ) { send_to_char( "You have no note to show\r\n", ch ); return; } ch_printf( ch, "To: %s\r\n", ch->pcdata->gnote->to_list ? ch->pcdata->gnote->to_list : "(Not Set)" ); ch_printf( ch, "Subject: %s\r\n", ch->pcdata->gnote->subject ? ch->pcdata->gnote->subject : "(Not Set)" ); ch_printf( ch, "%s\r\n", ch->pcdata->gnote->text ? ch->pcdata->gnote->text : "(Not Set)" ); return; } if( !str_cmp( arg, "post" ) ) { if( !ch->pcdata->gnote ) { send_to_char( "You have no note to show\r\n", ch ); return; } if( !( board = find_board( ch ) ) ) { send_to_char( "There is no bulletin board here to post your note on.\r\n", ch ); return; } if( !can_post( ch, board ) ) { send_to_char( "A magical force prevents you from posting your note here...\r\n", ch ); return; } if( board->num_posts >= board->max_posts ) { send_to_char( "There is no room on this board to post your note.\r\n", ch ); return; } if( !ch->pcdata->gnote->to_list ) { send_to_char( "You haven't put who the note is to.\r\n", ch ); return; } if( !ch->pcdata->gnote->subject ) { send_to_char( "You haven't put a subject for the note.\r\n", ch ); return; } if( !ch->pcdata->gnote->text ) { send_to_char( "You haven't put any text in the note.\r\n", ch ); return; } CREATE( pnote, NOTE_DATA, 1 ); pnote->posttime = current_time; pnote->text = STRALLOC( ch->pcdata->gnote->text ); STRFREE( ch->pcdata->gnote->text ); pnote->to_list = STRALLOC( ch->pcdata->gnote->to_list ); STRFREE( ch->pcdata->gnote->to_list ); pnote->subject = STRALLOC( ch->pcdata->gnote->subject ); STRFREE( ch->pcdata->gnote->subject ); pnote->sender = STRALLOC( ch->pcdata->gnote->sender ); STRFREE( ch->pcdata->gnote->sender ); DISPOSE( ch->pcdata->gnote ); ch->pcdata->gnote = NULL; pnote->voting = 0; pnote->yesvotes = 0; pnote->novotes = 0; pnote->abstentions = 0; pnote->first_vote = pnote->last_vote = NULL; pnote->first_bid = pnote->last_bid = NULL; pnote->first_read = pnote->last_read = NULL; pnote->obj = NULL; pnote->autowinb = 0; pnote->autowin = 0; pnote->aclosed = false; pnote->acanceled = false; pnote->sforb = 0; pnote->sfor = 0; LINK( pnote, board->first_note, board->last_note, next, prev ); board->num_posts++; write_board( board ); /* Send the message about a note being posted to the mud */ act( AT_ACTION, "$n posts a note.", ch, NULL, NULL, TO_OTHERS ); send_to_char( "You post your note on the board.\r\n", ch ); return; } if( !str_cmp( arg, "clear" ) ) { if( !ch->pcdata->gnote ) { send_to_char( "You have no note in progress\r\n", ch ); return; } STRFREE( ch->pcdata->gnote->text ); STRFREE( ch->pcdata->gnote->subject ); STRFREE( ch->pcdata->gnote->to_list ); STRFREE( ch->pcdata->gnote->sender ); ch->pcdata->gnote->obj = NULL; free_votes( ch->pcdata->gnote ); free_bids( ch->pcdata->gnote ); free_reads( ch->pcdata->gnote ); DISPOSE( ch->pcdata->gnote ); ch->pcdata->gnote = NULL; send_to_char( "Note cleared.\r\n", ch ); return; } if( !str_cmp( arg, "remove" ) ) { if( !( board = find_board( ch ) ) ) { send_to_char( "There is no board here to take a note from!\r\n", ch ); return; } if( !is_number( arg_passed ) ) { send_to_char( "Note remove which number?\r\n", ch ); return; } if( !can_read( ch, board ) ) { send_to_char( "You can't make any sense of what's posted here, let alone remove anything!\r\n", ch ); return; } anum = atoi( arg_passed ); vnum = 0; for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( get_trust( ch ) < PERM_IMM && !is_note_to( ch, pnote ) ) continue; if( ++vnum != anum ) continue; if( !is_note_to( ch, pnote ) && !can_remove( ch, board ) ) { send_to_char( "You aren't able to remove that note!\r\n", ch ); return; } note_remove( board, pnote ); ch_printf( ch, "Note %d removed.\r\n", vnum ); /* Send the message to the mud */ act( AT_ACTION, "$n removes a note.", ch, NULL, NULL, TO_OTHERS ); return; } send_to_char( "No such note.\r\n", ch ); return; } send_to_char( "Huh? Type 'help note' for usage.\r\n", ch ); } BOARD_DATA *read_board( FILE *fp ) { BOARD_DATA *board; const char *word; bool fMatch; char letter; do { letter = getc( fp ); if( feof( fp ) ) { fclose( fp ); return NULL; } } while( isspace( letter ) ); ungetc( letter, fp ); CREATE( board, BOARD_DATA, 1 ); for( ;; ) { word = feof( fp ) ? "End" : fread_word( fp ); fMatch = false; switch( UPPER( word[0] ) ) { case '*': fMatch = true; fread_to_eol( fp ); break; case 'E': KEY( "Extra_readers", board->extra_readers, fread_string( fp ) ); KEY( "Extra_removers", board->extra_removers, fread_string( fp ) ); if( !str_cmp( word, "End" ) ) { board->num_posts = 0; board->first_note = NULL; board->last_note = NULL; board->next = NULL; board->prev = NULL; return board; } break; case 'F': KEY( "Filename", board->note_file, fread_string( fp ) ); break; case 'M': KEY( "Min_read_level", board->min_read_level, fread_number( fp ) ); KEY( "Min_post_level", board->min_post_level, fread_number( fp ) ); KEY( "Min_remove_level", board->min_remove_level, fread_number( fp ) ); KEY( "Max_posts", board->max_posts, fread_number( fp ) ); break; case 'P': KEY( "Post_group", board->post_group, fread_string( fp ) ); break; case 'R': KEY( "Read_group", board->read_group, fread_string( fp ) ); break; } if( !fMatch ) { bug( "%s: no match: %s", __FUNCTION__, word ); fread_to_eol( fp ); } } free_board( board ); return NULL; } NOTE_DATA *read_note( FILE *fp ) { NOTE_DATA *pnote; VOTE_DATA *vote; BID_DATA *bid; READ_DATA *read; const char *word; bool fMatch; char letter; /* Have to see if we are at the end of the file */ do { letter = getc( fp ); if( feof( fp ) ) { fclose( fp ); return NULL; } } while( isspace( letter ) ); ungetc( letter, fp ); CREATE( pnote, NOTE_DATA, 1 ); pnote->yesvotes = 0; pnote->novotes = 0; pnote->abstentions = 0; pnote->first_vote = pnote->last_vote = NULL; pnote->first_bid = pnote->last_bid = NULL; pnote->first_read = pnote->last_read = NULL; pnote->obj = NULL; pnote->autowinb = 0; pnote->autowin = 0; pnote->aclosed = false; pnote->acanceled = false; pnote->sforb = 0; pnote->sfor = 0; for( ;; ) { word = feof( fp ) ? "End" : fread_word( fp ); fMatch = false; switch( UPPER( word[0] ) ) { case '*': fMatch = true; fread_to_eol( fp ); break; case '#': if( !strcmp( word, "#OBJECT" ) ) /* Objects */ { fread_obj( NULL, pnote, fp, OS_AUCTION ); fMatch = true; break; } break; case 'A': if( !str_cmp( word, "AClosed" ) ) { pnote->aclosed = true; fMatch = true; break; } if( !str_cmp( word, "ACanceled" ) ) { pnote->acanceled = true; fMatch = true; break; } KEY( "AutoWin", pnote->autowin, fread_number( fp ) ); break; case 'B': if( !str_cmp( word, "Bid" ) ) { CREATE( bid, BID_DATA, 1 ); bid->name = fread_string( fp ); bid->bbid = 0; bid->bid = fread_number( fp ); if( !valid_pfile( bid->name ) ) free_bid( bid ); else LINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); fMatch = true; break; } break; case 'E': if( !strcmp( word, "End" ) ) { pnote->next = NULL; pnote->prev = NULL; return pnote; } break; case 'N': if( !str_cmp( word, "NSFor" ) ) { pnote->sforb = fread_number( fp ); pnote->sfor = fread_number( fp ); fMatch = true; break; } if( !str_cmp( word, "NAutoWin" ) ) { pnote->autowinb = fread_number( fp ); pnote->autowin = fread_number( fp ); fMatch = true; break; } if( !str_cmp( word, "NBid" ) ) { CREATE( bid, BID_DATA, 1 ); bid->name = fread_string( fp ); bid->bbid = fread_number( fp ); bid->bid = fread_number( fp ); if( !valid_pfile( bid->name ) ) free_bid( bid ); else LINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); fMatch = true; break; } break; case 'P': KEY( "PostTime", pnote->posttime, fread_time( fp ) ); break; case 'R': if( !str_cmp( word, "Read" ) ) { CREATE( read, READ_DATA, 1 ); read->name = fread_string( fp ); if( !valid_pfile( read->name ) ) free_read( read ); else LINK( read, pnote->first_read, pnote->last_read, next, prev ); fMatch = true; break; } break; case 'S': KEY( "SFor", pnote->sfor, fread_number( fp ) ); KEY( "Sender", pnote->sender, fread_string( fp ) ); KEY( "Subject", pnote->subject, fread_string( fp ) ); break; case 'T': KEY( "To", pnote->to_list, fread_string( fp ) ); KEY( "Text", pnote->text, fread_string( fp ) ); break; case 'V': if( !str_cmp( word, "Vote" ) ) { CREATE( vote, VOTE_DATA, 1 ); vote->name = fread_string( fp ); vote->vote = fread_number( fp ); if( !valid_pfile( vote->name ) ) free_vote( vote ); else { LINK( vote, pnote->first_vote, pnote->last_vote, next, prev ); if( vote->vote == VOTE_NO ) pnote->novotes++; if( vote->vote == VOTE_YES ) pnote->yesvotes++; if( vote->vote == VOTE_ABSTAIN ) pnote->abstentions++; } fMatch = true; break; } KEY( "Voting", pnote->voting, fread_number( fp ) ); break; } if( !fMatch ) { bug( "%s: no match: %s", __FUNCTION__, word ); fread_to_eol( fp ); } } free_note( pnote ); return NULL; } /* Load boards file. */ void load_boards( void ) { FILE *board_fp, *note_fp; BOARD_DATA *board; NOTE_DATA *pnote; char notefile[256]; first_board = last_board = NULL; if( !( board_fp = fopen( BOARD_FILE, "r" ) ) ) return; while( ( board = read_board( board_fp ) ) ) { LINK( board, first_board, last_board, next, prev ); snprintf( notefile, sizeof( notefile ), "%s%s", BOARD_DIR, board->note_file ); log_string( notefile ); if( ( note_fp = fopen( notefile, "r" ) ) ) { while( ( pnote = read_note( note_fp ) ) ) { LINK( pnote, board->first_note, board->last_note, next, prev ); board->num_posts++; } } } } void board_stat( CHAR_DATA *ch, BOARD_DATA *board ) { if( !board ) { send_to_char( "No such board to see.\r\n", ch ); return; } ch_printf( ch, "\r\n&GFilename: &W%-15.15s &GRead: &W%s &GPost: &W%s &GRemove: &W%s\r\n", board->note_file, perms_flag[board->min_read_level], perms_flag[board->min_post_level], perms_flag[board->min_remove_level] ); ch_printf( ch, "&GMaxpost: &W%-3d\r\n", board->max_posts ); ch_printf( ch, "&GPosts: &W%d\r\n", board->num_posts ); ch_printf( ch, "&GRead_group: &W%s\r\n", board->read_group ? board->read_group : "None" ); ch_printf( ch, "&GPost_group: &W%s\r\n", board->post_group ? board->post_group : "None" ); ch_printf( ch, "&GExtra_readers: &W%s\r\n", board->extra_readers ? board->extra_readers : "None" ); ch_printf( ch, "&GExtra_removers: &W%s\r\n", board->extra_removers ? board->extra_removers : "None" ); } CMDF( do_bset ) { BOARD_DATA *board; char arg1[MIL], arg2[MIL], buf[MSL]; int value, bcount = 0; bool found, create = false; argument = one_argument( argument, arg1 ); argument = one_argument( argument, arg2 ); set_char_color( AT_NOTE, ch ); if( arg1 == NULL || arg1[0] == '\0' ) { send_to_char( "Usage: bset <board filename> [create]\r\n", ch ); send_to_char( "Usage: bset <board filename> <field> <value>\r\n", ch ); send_to_char( "\r\nField being one of:\r\n", ch ); send_to_char( " post read remove post_group extra_removers\r\n", ch ); send_to_char( " maxpost read_group filename extra_readers\r\n", ch ); return; } value = atoi( argument ); found = false; if( !str_cmp( arg2, "create" ) ) create = true; bcount = atoi( arg1 ); if( !( board = get_board( ch, bcount ) ) ) { bcount = 0; for( board = first_board; board; board = board->next ) { if( !can_read( ch, board ) && !can_post( ch, board ) && !can_remove( ch, board ) ) continue; bcount++; if( !str_cmp( board->note_file, arg1 ) ) break; } if( !board && !create ) { send_to_char( "No such board.\r\n", ch ); return; } } if( arg2 == NULL || arg2[0] == '\0' ) { board_stat( ch, board ); return; } if( create ) { if( board ) { send_to_char( "There is already such a board.\r\n", ch ); return; } arg1[0] = UPPER( arg1[0] ); if( !can_use_path( ch, BOARD_DIR, arg1 ) ) return; CREATE( board, BOARD_DATA, 1 ); if( !board ) { bug( "%s: failed to CREATE board.", __FUNCTION__ ); return; } LINK( board, first_board, last_board, next, prev ); board->note_file = STRALLOC( arg1 ); board->first_note = NULL; board->last_note = NULL; board->read_group = NULL; board->post_group = NULL; board->extra_readers = NULL; board->extra_removers = NULL; write_boards_txt( ); ch_printf( ch, "%s board created.\r\n", board->note_file ); return; } if( !str_cmp( arg2, "read" ) ) { board->min_read_level = URANGE( 0, value, ( PERM_MAX - 1 ) ); write_boards_txt( ); ch_printf( ch, "Read level set to %s.\r\n", perms_flag[board->min_read_level] ); return; } if( !str_cmp( arg2, "read_group" ) ) { STRSET( board->read_group, argument ); write_boards_txt( ); ch_printf( ch, "Read_group %s.\r\n", board->read_group ? "set" : "cleared" ); return; } if( !str_cmp( arg2, "post_group" ) ) { STRSET( board->post_group, argument ); write_boards_txt( ); ch_printf( ch, "Post_group %s.\r\n", board->post_group ? "set" : "cleared" ); return; } if( !str_cmp( arg2, "extra_removers" ) ) { if( !argument || argument[0] == '\0' ) { send_to_char( "No names specified.\r\n", ch ); return; } if( !str_cmp( argument, "none" ) ) STRFREE( board->extra_removers ); else { if( board->extra_removers ) snprintf( buf, sizeof( buf ), "%s %s", board->extra_removers, argument ); else snprintf( buf, sizeof( buf ), "%s", argument ); STRSET( board->extra_removers, buf ); } write_boards_txt( ); send_to_char( "Done. (extra removers set)\r\n", ch ); return; } if( !str_cmp( arg2, "extra_readers" ) ) { if( !argument || argument[0] == '\0' ) { send_to_char( "No names specified.\r\n", ch ); return; } if( !str_cmp( argument, "none" ) ) STRFREE( board->extra_readers ); else { if( board->extra_readers ) snprintf( buf, sizeof( buf ), "%s %s", board->extra_readers, argument ); else snprintf( buf, sizeof( buf ), "%s", argument ); STRSET( board->extra_readers, buf ); } write_boards_txt( ); send_to_char( "Done. (extra readers set)\r\n", ch ); return; } if( !str_cmp( arg2, "filename" ) ) { char filename[1024]; if( !argument || argument[0] == '\0' ) { send_to_char( "No filename specified.\r\n", ch ); return; } argument = capitalize( argument ); if( !can_use_path( ch, BOARD_DIR, argument ) ) return; if( board->note_file ) { snprintf( filename, sizeof( filename ), "%s%s", BOARD_DIR, board->note_file ); if( !remove( filename ) ) send_to_char( "Old board file deleted.\r\n", ch ); } STRSET( board->note_file, argument ); write_boards_txt( ); send_to_char( "Done. (board's filename set)\r\n", ch ); return; } if( !str_cmp( arg2, "post" ) ) { board->min_post_level = URANGE( 0, value, ( PERM_MAX - 1 ) ); write_boards_txt( ); ch_printf( ch, "Post set to %s. (minimum posting level)\r\n", perms_flag[board->min_post_level] ); return; } if( !str_cmp( arg2, "remove" ) ) { board->min_remove_level = URANGE( 0, value, ( PERM_MAX - 1 ) ); write_boards_txt( ); ch_printf( ch, "Remove set to %s. (minimum remove level)\r\n", perms_flag[board->min_remove_level] ); return; } if( !str_cmp( arg2, "maxpost" ) ) { board->max_posts = URANGE( 1, value, 999 ); write_boards_txt( ); ch_printf( ch, "Maxpost set to %d. (maximum number of posts)\r\n", board->max_posts ); return; } do_bset( ch, (char *)"" ); } CMDF( do_boards ) { BOARD_DATA *board; int bcount = 0, nnew = 0; const char *b1, *b2; if( !ch ) return; b1 = color_str( AT_BOARD, ch ); b2 = color_str( AT_BOARD2, ch ); set_char_color( AT_BOARD, ch ); if( !first_board ) { send_to_char( "There are no boards yet.\r\n", ch ); return; } if( !argument || argument[0] == '\0' ) { for( board = first_board; board; board = board->next ) { if( !can_read( ch, board ) && !can_post( ch, board ) && !can_remove( ch, board ) ) continue; pager_printf( ch, "%s%d%s> %s%-15.15s %sPosts: %s%3d", b2, ++bcount, b1, b2, board->note_file ? board->note_file : "(Not Set)", b1, b2, board->num_posts ); nnew = get_new_notes( board, ch ); if( nnew > 0 ) pager_printf( ch, "%s(%s%3d%s)", b1, b2, nnew, b1 ); else send_to_pager( " ", ch ); if( is_immortal( ch ) ) pager_printf( ch, " %sRead: %s%4s %sPost: %s%4s %sRmv: %s%4s %sMax: %s%3d", b1, b2, perms_flag[board->min_read_level], b1, b2, perms_flag[board->min_post_level], b1, b2, perms_flag[board->min_remove_level], b1, b2, board->max_posts ); send_to_pager( "\r\n", ch ); } return; } bcount = atoi( argument ); if( !( board = get_board( ch, bcount ) ) ) { bcount = 0; for( board = first_board; board; board = board->next ) { if( !can_read( ch, board ) && !can_post( ch, board ) && !can_remove( ch, board ) ) continue; bcount++; if( !str_cmp( board->note_file, argument ) ) break; } if( !board ) { send_to_char( "No such board to switch to.\r\n", ch ); return; } } ch->pcdata->onboard = bcount; ch_printf( ch, "Switched to board %s%d%s> %s%s.\r\n", b2, ch->pcdata->onboard, b1, b2, board->note_file ? board->note_file : "(Not Set)" ); ch_printf( ch, "%sYou %s%s %sread the messages on this board.\r\n", b1, b2, can_read( ch, board ) ? "can" : "can't", b1 ); ch_printf( ch, "%sYou %s%s %spost messages on this board.\r\n", b1, b2, can_post( ch, board ) ? "can" : "can't", b1 ); ch_printf( ch, "%sYou %s%s %sremove messages on this board.\r\n", b1, b2, can_remove( ch, board ) ? "can" : "can't", b1 ); } /* Find the auction board */ BOARD_DATA *get_auction_board( void ) { BOARD_DATA *board = NULL; for( board = first_board; board; board = board->next ) { if( !str_cmp( board->note_file, "Auction" ) ) break; } return board; } int count_auctions( CHAR_DATA *ch ) { BOARD_DATA *board = get_auction_board( ); NOTE_DATA *pnote, *pnote_next = NULL; int count = 0; if( !board || !ch ) return 0; for( pnote = board->first_note; pnote; pnote = pnote_next ) { pnote_next = pnote->next; if( pnote->aclosed ) continue; if( !str_cmp( pnote->sender, ch->name ) ) count++; } return count; } void check_auction( CHAR_DATA *ch ) { BOARD_DATA *board = get_auction_board( ); NOTE_DATA *pnote, *pnote_next = NULL; BID_DATA *bid, *bid_next = NULL, *chbid = NULL; int count = 0; bool asave = false; if( !board || !ch ) return; for( pnote = board->first_note; pnote; pnote = pnote_next ) { pnote_next = pnote->next; ++count; chbid = check_high_bid( pnote ); /* Closed and no bets give object back to seller */ if( pnote->aclosed ) { /* If it was canceled return object to seller */ if( pnote->acanceled ) { if( pnote->obj && !str_cmp( pnote->sender, ch->name ) ) { set_char_color( AT_AUCTION, ch ); act( AT_ACTION, "The auctioneer materializes before you, and hands you $p.", ch, pnote->obj, NULL, TO_CHAR ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m $p.", ch, pnote->obj, NULL, TO_ROOM ); obj_to_char( pnote->obj, ch ); pnote->obj = NULL; pnote->sforb = 0; pnote->sfor = 0; pnote->acanceled = false; if( !pnote->first_bid ) { UNLINK( pnote, board->first_note, board->last_note, next, prev ); free_note( pnote ); --board->num_posts; asave = true; continue; } asave = true; } } if( ( pnote->sfor || pnote->sforb ) && !str_cmp( pnote->sender, ch->name ) ) { set_char_color( AT_AUCTION, ch ); if( !can_hold_gold( ch->bgold, ch->gold, pnote->sforb, pnote->sfor ) ) { send_to_char( "The auctioneer has some gold for you, but you can't hold that much gold currently.\r\n", ch ); return; } act_printf( AT_ACTION, ch, NULL, NULL, TO_CHAR, "The auctioneer materializes before you, and hands you %s gold.", show_big_nums( pnote->sforb, pnote->sfor ) ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m some gold.", ch, NULL, NULL, TO_ROOM ); increase_gold( ch, pnote->sforb, pnote->sfor ); asave = true; } if( !pnote->first_bid ) { if( !pnote->obj && !pnote->sfor && !pnote->sforb ) { UNLINK( pnote, board->first_note, board->last_note, next, prev ); free_note( pnote ); --board->num_posts; asave = true; continue; } if( !pnote->acanceled && pnote->obj && !str_cmp( pnote->sender, ch->name ) ) { set_char_color( AT_AUCTION, ch ); act( AT_ACTION, "The auctioneer materializes before you, and hands you $p.", ch, pnote->obj, NULL, TO_CHAR ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m $p.", ch, pnote->obj, NULL, TO_ROOM ); obj_to_char( pnote->obj, ch ); pnote->obj = NULL; UNLINK( pnote, board->first_note, board->last_note, next, prev ); free_note( pnote ); --board->num_posts; asave = true; continue; } } } for( bid = pnote->first_bid; bid; bid = bid_next ) { bid_next = bid->next; if( str_cmp( bid->name, ch->name ) ) continue; /* Autowin has been reached handle it correctly */ if( ( pnote->autowin > 0 || pnote->autowinb > 0 ) && compare_big_nums( pnote->autowinb, pnote->autowin, bid->bbid, bid->bid ) ) { to_channel_printf( "auction", PERM_ALL, "%s has won the auction for %s.", ch->name, pnote->subject ); pnote->sforb = bid->bbid; pnote->sfor = bid->bid; pnote->aclosed = true; asave = true; } /* No more bidding give out what it should to the character */ if( pnote->aclosed ) { if( chbid && bid == chbid && !pnote->acanceled ) { /* give the object and remove them from the list */ if( pnote->obj ) { set_char_color( AT_AUCTION, ch ); act( AT_ACTION, "The auctioneer materializes before you, and hands you $p.", ch, pnote->obj, NULL, TO_CHAR ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m $p.", ch, pnote->obj, NULL, TO_ROOM ); obj_to_char( pnote->obj, ch ); pnote->obj = NULL; UNLINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); free_bid( bid ); asave = true; continue; } else /* object is already gone */ { set_char_color( AT_AUCTION, ch ); if( !can_hold_gold( ch->bgold, ch->gold, bid->bbid, bid->bid ) ) { send_to_char( "The auctioneer has some gold for you, but you can't hold that much gold currently.\r\n", ch ); continue; } act_printf( AT_ACTION, ch, NULL, NULL, TO_CHAR, "The auctioneer materializes before you, and hands you %s gold.", show_big_nums( bid->bbid, bid->bid ) ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m some gold.", ch, NULL, NULL, TO_ROOM ); increase_gold( ch, bid->bbid, bid->bid ); UNLINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); free_bid( bid ); asave = true; continue; } } else { set_char_color( AT_AUCTION, ch ); if( !can_hold_gold( ch->bgold, ch->gold, bid->bbid, bid->bid ) ) { send_to_char( "The auctioneer has some gold for you, but you can't hold that much gold currently.\r\n", ch ); continue; } act_printf( AT_ACTION, ch, NULL, NULL, TO_CHAR, "The auctioneer materializes before you, and hands you %s gold.", show_big_nums( bid->bbid, bid->bid ) ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m some gold.", ch, NULL, NULL, TO_ROOM ); increase_gold( ch, bid->bbid, bid->bid ); UNLINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); free_bid( bid ); asave = true; continue; } } /* Not highest bidder so go ahead and refund their bid */ if( chbid && bid != chbid ) { set_char_color( AT_AUCTION, ch ); if( !can_hold_gold( ch->bgold, ch->gold, bid->bbid, bid->bid ) ) { send_to_char( "The auctioneer has some gold for you, but you can't hold that much gold currently.\r\n", ch ); continue; } act_printf( AT_ACTION, ch, NULL, NULL, TO_CHAR, "The auctioneer materializes before you, and hands you %s gold.", show_big_nums( bid->bbid, bid->bid ) ); act( AT_ACTION, "The auctioneer materializes before $n, and hands $m some gold.", ch, NULL, NULL, TO_ROOM ); increase_gold( ch, bid->bbid, bid->bid ); UNLINK( bid, pnote->first_bid, pnote->last_bid, next, prev ); free_bid( bid ); asave = true; continue; } } } /* save the auction board if you need to */ if( asave ) { write_board( board ); save_char_obj( ch ); } } void set_auction_list( OBJ_DATA *list, CHAR_DATA *ch ) { AUCTION_DATA *auction; OBJ_DATA *obj, *tmpobj = NULL; char astr[MSL * 2]; for( obj = list; obj; obj = obj->next_content ) { if( obj->wear_loc == WEAR_NONE && ( can_see_obj( ch, obj ) || ( is_obj_stat( obj, ITEM_INVIS ) && is_obj_stat( obj, ITEM_GLOW ) && !is_obj_stat( obj, ITEM_BURIED ) && !is_obj_stat( obj, ITEM_HIDDEN ) ) ) && ( obj->item_type != ITEM_TRAP || IS_AFFECTED( ch, AFF_DETECTTRAPS ) ) ) { snprintf( astr, sizeof( astr ), "%s", format_obj_to_char( obj, ch, true ) ); if( obj->in_obj ) { tmpobj = obj->in_obj; mudstrlcpy( astr, " ", sizeof( astr ) ); while( tmpobj->in_obj ) { mudstrlcat( astr, " ", sizeof( astr ) ); tmpobj = tmpobj->in_obj; } mudstrlcat( astr, format_obj_to_char( obj, ch, true ), sizeof( astr ) ); } for( auction = first_auction; auction; auction = auction->next ) { if( !strcmp( auction->auctstr, astr ) ) { auction->count += obj->count; break; } } CREATE( auction, AUCTION_DATA, 1 ); auction->auctstr = STRALLOC( astr ); auction->type = obj->item_type; auction->count = obj->count; LINK( auction, first_auction, last_auction, next, prev ); if( obj->first_content ) { set_auction_list( obj->first_content, ch ); } } } } void show_auction_list( CHAR_DATA *ch ) { AUCTION_DATA *auction, *auction_next; for( auction = first_auction; auction; auction = auction_next ) { auction_next = auction->next; switch( auction->type ) { default: set_char_color( AT_OBJECT, ch ); break; case ITEM_BLOOD: set_char_color( AT_BLOOD, ch ); break; case ITEM_MONEY: case ITEM_TREASURE: set_char_color( AT_YELLOW, ch ); break; case ITEM_COOK: case ITEM_FOOD: case ITEM_FISH: set_char_color( AT_HUNGRY, ch ); break; case ITEM_DRINK_CON: case ITEM_FOUNTAIN: set_char_color( AT_THIRSTY, ch ); break; case ITEM_FIRE: set_char_color( AT_FIRE, ch ); break; case ITEM_SCROLL: case ITEM_WAND: case ITEM_STAFF: set_char_color( AT_MAGIC, ch ); break; } send_to_char( auction->auctstr, ch ); if( auction->count != 1 ) ch_printf( ch, " (%d)", auction->count ); send_to_char( "\r\n", ch ); UNLINK( auction, first_auction, last_auction, next, prev ); STRFREE( auction->auctstr ); DISPOSE( auction ); } } void auction_update( void ) { CHAR_DATA *ch; for( ch = first_char; ch; ch = ch->next ) { if( is_npc( ch ) ) continue; check_auction( ch ); } } CMDF( do_auction ) { BOARD_DATA *board = NULL; NOTE_DATA *pnote = NULL; BID_DATA *chbid = NULL; OBJ_DATA *obj = NULL; char arg[MSL]; int count = 0, start = 0, shown = 0, bcount = 0; if( !ch ) return; if( !( board = get_auction_board( ) ) ) { send_to_char( "There is no auction board to use.\r\n", ch ); return; } argument = one_argument( argument, arg ); if( arg == NULL || arg[0] == '\0' || !str_cmp( arg, "list" ) ) { if( !board->first_note ) { send_to_char( "There is nothing currently being auctioned.\r\n", ch ); return; } argument = one_argument( argument, arg ); if( is_number( arg ) ) start = atoi( arg ); for( pnote = board->first_note; pnote; pnote = pnote->next ) { chbid = check_high_bid( pnote ); ++count; if( start > 0 && count < start ) continue; if( shown == 0 ) pager_printf( ch, "### %12.12s %25.25s %12.12s %25s %15.15s\r\n", "Seller", "AutoWin", "High Bidder", "Bid", "Object" ); pager_printf( ch, "%3d> %12.12s %25s", count, pnote->sender ? pnote->sender : "(No Sender)", show_big_nums( pnote->autowinb, pnote->autowin ) ); pager_printf( ch, " %12.12s %25s %15.15s%3s\r\n", chbid ? chbid->name : "(No Bidder)", chbid ? show_big_nums( chbid->bbid, chbid->bid ) : "0", pnote->subject ? pnote->subject : "(No Subject)", ( pnote->subject && strlen( pnote->subject ) > 15 ) ? "..." : "" ); if( ++shown > 15 ) { if( pnote->next ) send_to_pager( "Only 15 auctions are displayed at a time.\r\n", ch ); break; } } send_to_char( "Usage: auction\r\n", ch ); send_to_char( "Usage: auction <#>\r\n", ch ); send_to_char( "Usage: auction <#> close\r\n", ch ); send_to_char( "Usage: auction <#> bid <amount>\r\n", ch ); send_to_char( "Usage: auction <object> <autowin>\r\n", ch ); send_to_char( "Usage: auction list <start #>\r\n", ch ); return; } if( is_number( arg ) ) { int unote = atoi( arg ); argument = one_argument( argument, arg ); for( pnote = board->first_note; pnote; pnote = pnote->next ) { if( ++count < unote ) continue; if( !pnote->obj ) continue; if( !str_cmp( arg, "close" ) ) { if( str_cmp( pnote->sender, ch->name ) && !is_immortal( ch ) ) { send_to_char( "Only the seller may close an auction.\r\n", ch ); return; } if( ( chbid = check_high_bid( pnote ) ) ) { pnote->sforb = chbid->bbid; pnote->sfor = chbid->bid; } to_channel_printf( "auction", PERM_ALL, "%s has closed the auction for %s.", ch->name, pnote->subject ); pnote->aclosed = true; STRSET( pnote->subject, "Closed" ); auction_update( ); write_board( board ); return; } if( !str_cmp( arg, "cancel" ) ) { if( str_cmp( pnote->sender, ch->name ) && !is_immortal( ch ) ) { send_to_char( "Only the seller may cancel an auction.\r\n", ch ); return; } to_channel_printf( "auction", PERM_ALL, "%s has canceled the auction for %s.", ch->name, pnote->subject ); pnote->aclosed = true; pnote->acanceled = true; STRSET( pnote->subject, "Canceled" ); auction_update( ); write_board( board ); return; } if( !str_cmp( arg, "bid" ) ) { if( !str_cmp( pnote->sender, ch->name ) ) { send_to_char( "You can't bid on something your auctioning.\r\n", ch ); return; } chbid = check_high_bid( pnote ); argument = one_argument( argument, arg ); if( !is_number( arg ) ) { send_to_char( "You have to enter an amount to bid.\r\n", ch ); return; } get_big_num( arg, &bcount, &count ); if( ( bcount <= 0 && count <= 0 ) || !has_gold( ch, bcount, count ) ) { ch_printf( ch, "You can't bid %d gold.\r\n", count ); return; } if( chbid ) { if( !str_cmp( chbid->name, ch->name ) ) { send_to_char( "You already have the highest bid.\r\n", ch ); return; } if( compare_big_nums( chbid->bbid, ( chbid->bid + 99 ), bcount, count ) ) { ch_printf( ch, "The lowest bid you can make on it currently is %s.\r\n", show_big_nums( chbid->bbid, ( chbid->bid + 100 ) ) ); return; } } add_bid( pnote, ch, bcount, count ); decrease_gold( ch, bcount, count ); to_channel_printf( "auction", PERM_ALL, "%s has bidded %s gold on %s.", ch->name, show_big_nums( bcount, count ), pnote->subject ); auction_update( ); write_board( board ); return; } if( pnote->obj ) { show_obj( ch, pnote->obj ); if( pnote->obj->first_content ) { set_auction_list( pnote->obj->first_content, ch ); show_auction_list( ch ); } } else send_to_char( "This auction has already been closed.\r\n", ch ); show_bids( pnote, ch ); add_read( pnote, ch ); return; } send_to_char( "No such number on auction to look at.\r\n", ch ); return; } if( !( obj = get_obj_carry( ch, arg ) ) ) { send_to_char( "You aren't carrying that.\r\n", ch ); return; } if( obj->timer > 0 ) { send_to_char( "You can't auction objects that are decaying.\r\n", ch ); return; } if( count_auctions( ch ) >= sysdata.maxauction ) { ch_printf( ch, "You can only have up to %d auctions going at a time.\r\n", sysdata.maxauction ); return; } argument = one_argument( argument, arg ); count = 0; if( arg != NULL && arg[0] != '\0' && is_number( arg ) ) { get_big_num( arg, &bcount, &count ); if( count <= 0 && bcount <= 0 ) { send_to_char( "You can't auction an object with an auto win of 0 or lower.\r\n", ch ); return; } } separate_obj( obj ); obj_from_char( obj ); CREATE( pnote, NOTE_DATA, 1 ); pnote->next = pnote->prev = NULL; pnote->sender = QUICKLINK( ch->name ); pnote->to_list = STRALLOC( "all" ); pnote->subject = QUICKLINK( obj->short_descr ); pnote->text = STRALLOC( "auctioning\r\n" ); pnote->first_vote = pnote->last_vote = NULL; pnote->first_bid = pnote->last_bid = NULL; pnote->obj = obj; pnote->posttime = current_time; pnote->voting = 0; pnote->yesvotes = 0; pnote->novotes = 0; pnote->abstentions = 0; pnote->first_vote = pnote->last_vote = NULL; pnote->first_bid = pnote->last_bid = NULL; pnote->first_read = pnote->last_read = NULL; pnote->autowinb = bcount; pnote->autowin = count; board->num_posts++; LINK( pnote, board->first_note, board->last_note, next, prev ); write_board( board ); to_channel_printf( "auction", PERM_ALL, "%s has auctioned %s.", ch->name, pnote->subject ); }