/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * 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. * ***************************************************************************/ #include <glib.h> #if defined(macintosh) #include <types.h> #else #include <sys/types.h> #endif #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <merc.h> #include <colordef.h> #include <tables.h> #include <recycle.h> extern GMemChunk *note_mem_chunk; #define L_SUP (MAX_LEVEL - 1) DECLARE_DO_FUN (do_help); int has_read_access args((CHAR_DATA *ch, BOARD_DATA *board)); BOARD_DATA boards[MAX_BOARD] = { { "Changes", "Changes to Lurf II", 0, 10, "all", DEF_INCLUDE,99, NULL, FALSE }, { "News", "What's New on Lurf II", 0, 10, "all", DEF_INCLUDE,99, NULL, FALSE }, { "Penalty", "Penalties", 7, 7, "imm", DEF_INCLUDE,21, NULL, FALSE }, { "Help", "Help for Newbie Players", 0, 2, "all", DEF_INCLUDE,21, NULL, FALSE }, { "General", "IC General Discussion", 0, 2, "all", DEF_NORMAL ,21, NULL, FALSE }, { "Ideas", "Suggestion for Improvement", 0, 2, "all", DEF_NORMAL ,60, NULL, FALSE }, { "Bugs", "Report a BUG to Spiral", 2, 2, "Coder imm", DEF_INCLUDE,60, NULL, FALSE }, { "OOC", "Out of Character Notes", 0, 2, "all", DEF_INCLUDE,30, NULL, FALSE }, { "Immortal", "Immortal Only Board", 7, 7, "imm", DEF_INCLUDE,60, NULL, FALSE }, { "Personal", "Personal Messages", 0, 2, "all", DEF_EXCLUDE,28, NULL, FALSE }, { "Tocode", "Many Coding Projects", 11, 11, "imm", DEF_NORMAL ,28, NULL, FALSE }, { "Roleplay", "Roleplay Issue Discussions", 0, 2, "all imm", DEF_NORMAL ,21, NULL, FALSE }, { "Build", "Admin Builder's Board", 10, 10, "all", DEF_EXCLUDE,60, NULL, FALSE }, { "Quest", "Quest Related Board", 0, 2, "all", DEF_INCLUDE,21, NULL, FALSE }, { "Justify", "PK Justify (get back -1 RP)", 0, 2, "imm", DEF_INCLUDE,21, NULL, FALSE }, { "VampireRP", "Vampire Role Play", 980, 980, "imm", DEF_INCLUDE,60, NULL, FALSE }, { "MageRP", "Mage Role Play", 970, 970, "imm", DEF_INCLUDE,60, NULL, FALSE }, { "WWFRP", "Werewolf RolePlay", 960, 960, "imm", DEF_INCLUDE,60, NULL, FALSE } }; #define WWF_BOARD 960 #define MAGE_BOARD 970 #define VAMPIRE_BOARD 980 /* The prompt that the character is given after finishing a note with ~ or END */ const char * szFinishPrompt = "( R )estart, ( C )ontinue, ( V )iew, ( P )ost or ( F )orget it?"; long last_note_stamp = 0; /* To generate unique timestamps on notes */ #define BOARD_NOACCESS -1 #define BOARD_NOTFOUND -1 static bool next_board (CHAR_DATA *ch); /* recycle a note */ void free_note (NOTE_DATA *note) { note->next = note_free; note_free = note; } /* allocate memory for a new note or recycle */ NOTE_DATA *new_note () { NOTE_DATA *note; if (note_free) { note = note_free; note_free = note_free->next; } else { note = g_chunk_new (NOTE_DATA, note_mem_chunk); note->sender = g_string_new(""); note->to_list = g_string_new(""); note->date = g_string_new(""); note->subject = g_string_new(""); note->text = g_string_new(""); } /* Zero all the field - Envy does not gurantee zeroed memory */ note->next = NULL; note->sender = g_string_assign(note->sender,""); note->expire = 0; note->to_list = g_string_assign(note->to_list,""); note->subject = g_string_assign(note->subject,""); note->date = g_string_assign(note->date,""); note->date_stamp = 0; note->text = g_string_assign(note->text,""); return note; } /* append this note to the given file */ static void append_note (FILE *fp, NOTE_DATA *note) { fprintf (fp, "Sender %s~\n", note->sender->str); fprintf (fp, "Date %s~\n", note->date->str); fprintf (fp, "Stamp %ld\n", note->date_stamp); fprintf (fp, "Expire %ld\n", note->expire); fprintf (fp, "To %s~\n", note->to_list->str); fprintf (fp, "Subject %s~\n", note->subject->str); fprintf (fp, "Text\n%s~\n\n", note->text->str); } /* Save a note in a given board */ void finish_note (BOARD_DATA *board, NOTE_DATA *note) { FILE *fp; NOTE_DATA *p; char filename[200]; /* The following is done in order to generate unique date_stamps */ if (last_note_stamp >= current_time) note->date_stamp = ++last_note_stamp; else { note->date_stamp = current_time; last_note_stamp = current_time; } if (board->note_first) /* are there any notes in there now? */ { for (p = board->note_first; p->next; p = p->next ) ; /* empty */ p->next = note; } else /* nope. empty list. */ board->note_first = note; /* append note to note file */ sprintf (filename, "%s%s", NOTE_DIR, board->short_name); fp = fopen (filename, "a"); if (!fp) { bug ("Could not open one of the note files in append mode",0); board->changed = TRUE; /* set it to TRUE hope it will be OK later? */ return; } append_note (fp, note); fclose (fp); } /* Find the number of a board */ int board_number (const BOARD_DATA *board) { int i; for (i = 0; i < MAX_BOARD; i++) if (board == &boards[i]) return i; return -1; } /* 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_cmp (boards[i].short_name, name)) return i; return -1; } /* Remove list from the list. Do not free note */ static 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: could not find note.",0); else p->next = note->next; } } /* Find the nth note on a board. Return NULL if ch has no access to that note */ static NOTE_DATA* find_note (CHAR_DATA *ch, BOARD_DATA *board, int num) { int count = 0; NOTE_DATA *p; for (p = board->note_first; p ; p = p->next) if (++count == num) break; if ( (count == num) && is_note_to (ch, p)) return p; else return NULL; } /* save a single board */ static void save_board (BOARD_DATA *board) { FILE *fp; char filename[200]; char buf[200]; NOTE_DATA *note; sprintf (filename, "%s%s", NOTE_DIR, board->short_name); fp = fopen (filename, "w"); if (!fp) { sprintf (buf, "Error writing to: %s", filename); bug (buf, 0); } else { for (note = board->note_first; note ; note = note->next) append_note (fp, note); fclose (fp); } } /* Show one not to a character */ static void show_note_to_char (CHAR_DATA *ch, NOTE_DATA *note, int num, char *bshort_name) { char buf[4*MAX_STRING_LENGTH]; /* Ugly colors ? */ sprintf (buf, "[" BOLD "%4d" NO_COLOR "] " BOLD YELLOW "%s" NO_COLOR ": " GREEN "%s" NO_COLOR "\n\r" BOLD YELLOW "Date" NO_COLOR ": %s\n\r" BOLD YELLOW "To" NO_COLOR ": %s\n\r" BOLD YELLOW "Board" NO_COLOR ": %s\n\r" GREEN "-------------------------------------------------------------------------------" NO_COLOR "\n\r" "%s\n\r", num, note->sender->str, note->subject->str, note->date->str, note->to_list->str, bshort_name, note->text->str); send_to_char(buf,ch); } /* Show one not to a character Zmud Reader Version */ static void show_note_to_charZ (CHAR_DATA *ch, NOTE_DATA *note, int num, char *bshort_name) { char buf[4*MAX_STRING_LENGTH]; /* Ugly colors ? */ sprintf (buf, "[" BOLD "%4d" NO_COLOR "] " BOLD YELLOW "%s" NO_COLOR ": " GREEN "%s" NO_COLOR "\n\r" BOLD YELLOW "Date" NO_COLOR ": %s\n\r" BOLD YELLOW "To" NO_COLOR ": %s\n\r" BOLD YELLOW "Board" NO_COLOR ": %s\n\r" GREEN "-------------------------------------------------------------------------------" NO_COLOR "\n\r" "%s\n\r", num, note->sender->str, note->subject->str, note->date->str, note->to_list->str, bshort_name, note->text->str); send_to_char(buf,ch); } /* Save changed boards */ void save_notes () { int i; for (i = 0; i < MAX_BOARD; i++) if (boards[i].changed) /* only save changed boards */ save_board (&boards[i]); } /* Load a single board */ static void load_board (BOARD_DATA *board) { FILE *fp, *fp_archive; NOTE_DATA *last_note; char filename[200]; sprintf (filename, "%s%s", NOTE_DIR, board->short_name); fp = fopen (filename, "r"); /* Silently return */ if (!fp) return; /* Start note fetching. copy of db.c:load_notes() */ last_note = NULL; for ( ; ; ) { NOTE_DATA *pnote; char letter; do { letter = getc( fp ); if ( feof(fp) ) { fclose( fp ); return; } } while ( isspace(letter) ); ungetc( letter, fp ); pnote = new_note(); if ( str_cmp( fread_word( fp ), "sender" ) ) break; pnote->sender = g_string_assign(pnote->sender, fread_string( fp )); if ( str_cmp( fread_word( fp ), "date" ) ) break; pnote->date = g_string_assign(pnote->date, fread_string( fp )); if ( str_cmp( fread_word( fp ), "stamp" ) ) break; pnote->date_stamp = fread_number( fp ); if ( str_cmp( fread_word( fp ), "expire" ) ) break; pnote->expire = fread_number( fp ); if ( str_cmp( fread_word( fp ), "to" ) ) break; pnote->to_list = g_string_assign(pnote->to_list, fread_string( fp )); if ( str_cmp( fread_word( fp ), "subject" ) ) break; pnote->subject = g_string_assign(pnote->subject, fread_string( fp )); if ( str_cmp( fread_word( fp ), "text" ) ) break; pnote->text = g_string_assign(pnote->text, fread_string( fp )); pnote->next = NULL; /* jic */ /* Should this note be archived right now ? */ if (pnote->expire < current_time) { char archive_name[200]; sprintf (archive_name, "%s%s.old", NOTE_DIR, board->short_name); fp_archive = fopen (archive_name, "a"); if (!fp_archive) bug ("Could not open archive boards for writing",0); else { append_note (fp_archive, pnote); fclose (fp_archive); /* it might be more efficient to close this later */ } free_note (pnote); board->changed = TRUE; continue; } if ( board->note_first == NULL ) board->note_first = pnote; else last_note->next = pnote; last_note = pnote; } bug( "Load_notes: bad key word.", 0 ); return; /* just return */ } /* Initialize structures. Load all boards. */ void load_boards () { int i; for (i = 0; i < MAX_BOARD; i++) load_board (&boards[i]); } /* Returns TRUE if the specified note is address to ch */ bool is_note_to (CHAR_DATA *ch, NOTE_DATA *note) { if (note->to_list == NULL) return FALSE; if (!str_cmp (ch->name->str, note->sender->str)) return TRUE; if (is_full_name ("all", note->to_list->str)) return TRUE; if (is_full_name(sect_table[ch->sect].pretty_name,note->to_list->str)) return TRUE; if (is_full_name(sect_table[ch->sect].alias,note->to_list->str)) return TRUE; if (is_full_name(iclan_table[ch->clan].pretty_name,note->to_list->str)) return TRUE; if (IS_IMMORTAL(ch) && ( is_full_name ("admin", note->to_list->str) || is_full_name ("imm", note->to_list->str) || is_full_name ("imms", note->to_list->str) || is_full_name ("immortal", note->to_list->str) || is_full_name ("god", note->to_list->str) || is_full_name ("gods", note->to_list->str) || is_full_name ("immortals", note->to_list->str))) return TRUE; if ((get_trust(ch) == MAX_LEVEL) && ( is_full_name ("imp", note->to_list->str) || is_full_name ("imps", note->to_list->str) || is_full_name ("impl", note->to_list->str) || is_full_name ("implementor", note->to_list->str) || is_full_name ("implementors", note->to_list->str))) return TRUE; if (is_full_name (ch->name->str, note->to_list->str)) return TRUE; if ((get_trust(ch) >= LEVEL_HIGHJUDGE) && ( is_full_name ("coder", note->to_list->str))) return TRUE; if ((ch->level <= 2) && ( is_full_name ("mortals", note->to_list->str) || is_full_name ("morts", note->to_list->str) || is_full_name ("mortal", note->to_list->str))) return TRUE; if ((ch->level >= 3) && ( is_full_name ("avatars", note->to_list->str) || is_full_name ("avatar", note->to_list->str))) return TRUE; if ( (IS_SWWF(ch) || IS_CLASS(ch,CLASS_WEREWOLF)) && ( is_name( "wwf", note->to_list->str ) || is_name( "wwfs", note->to_list->str ) || is_name( "werewolf", note->to_list->str ) || is_name( "werewolves", note->to_list->str ))) return TRUE; if ( IS_CLASS(ch,CLASS_VAMPIRE) && ( is_name( "vampire", note->to_list->str ) || is_name( "vampires", note->to_list->str ))) return TRUE; if ( IS_CLASS(ch,CLASS_MAGE) && ( is_name( "mage", note->to_list->str ) || is_name( "mages", note->to_list->str ))) return TRUE; /* Allow a note to e.g. 40 to send to characters level 40 and above */ if (is_number(note->to_list->str) && get_trust(ch) >= atoi(note->to_list->str)) return TRUE; return FALSE; } /* Return the number of unread notes 'ch' has in 'board' */ /* Returns BOARD_NOACCESS if ch has no access to board */ int unread_notes (CHAR_DATA *ch, BOARD_DATA *board) { NOTE_DATA *note; time_t last_read; int count = 0; last_read = ch->pcdata->last_note[board_number(board)]; if (has_read_access(ch,board) != 0) return BOARD_NOACCESS; for (note = board->note_first; note; note = note->next) if (is_note_to(ch, note) && ((long)last_read < (long)note->date_stamp)) count++; return count; } int has_read_access(CHAR_DATA *ch, BOARD_DATA *board){ if (board->read_level == WWF_BOARD && !IS_CLASS(ch,CLASS_SWWF) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->read_level == MAGE_BOARD && !IS_CLASS(ch,CLASS_MAGE) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->read_level == VAMPIRE_BOARD && !IS_CLASS(ch,CLASS_VAMPIRE) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->read_level <= MAX_LEVEL && board->read_level > get_trust(ch)) return BOARD_NOACCESS; return 0; } int has_write_access(CHAR_DATA *ch, BOARD_DATA *board){ if (board->write_level == WWF_BOARD && !IS_CLASS(ch,CLASS_SWWF) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->write_level == MAGE_BOARD && !IS_CLASS(ch,CLASS_MAGE) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->write_level == VAMPIRE_BOARD && !IS_CLASS(ch,CLASS_VAMPIRE) && !IS_IMMORTAL(ch)) return BOARD_NOACCESS; if (board->write_level <= MAX_LEVEL && board->write_level > get_trust(ch)) return BOARD_NOACCESS; return 0; } /* * COMMANDS */ /* Start writing a note */ static void do_nwrite (CHAR_DATA *ch, char *argument) { char *strtime; char buf[200]; if (IS_NPC(ch)) /* NPC cannot post notes */ return; if (has_write_access(ch,ch->pcdata->board) == BOARD_NOACCESS) { send_to_char ("You cannot post notes on this board.\n\r",ch); return; } /* continue previous note, if any text was written*/ if (ch->pcdata->in_progress && (ch->pcdata->in_progress->text->len == 0)) { send_to_char ("Note in progress cancelled because you did not manage to write any text \n\r" "before losing link.\n\r\n\r",ch); free_note (ch->pcdata->in_progress); ch->pcdata->in_progress = NULL; } if (!ch->pcdata->in_progress) { ch->pcdata->in_progress = new_note(); ch->pcdata->in_progress->sender = g_string_assign(ch->pcdata->in_progress->sender, ch->name->str); /* convert to ascii. ctime returns a string which last character is \n, so remove that */ strtime = ctime (¤t_time); strtime[strlen(strtime)-1] = '\0'; ch->pcdata->in_progress->date = g_string_assign(ch->pcdata->in_progress->date, strtime); } act (BOLD GREEN "$n starts writing a note." NO_COLOR , ch, NULL, NULL, TO_ROOM); /* Begin writing the note ! */ if (ch->pcdata->in_progress->text->len > 0) sprintf (buf, "You are now %s a new note on the " BOLD "%s" NO_COLOR " board.\n\r" "If you are using tintin, type #verbose to turn off alias expansion!\n\r\n\r", "continuing", ch->pcdata->board->short_name); else sprintf (buf, "You are now %s a new note on the " BOLD "%s" NO_COLOR " board.\n\r" "If you are using tintin, type #verbose to turn off alias expansion!\n\r\n\r", "posting", ch->pcdata->board->short_name); send_to_char (buf,ch); sprintf (buf, BOLD YELLOW "From" NO_COLOR ": %s\n\r\n\r", ch->name->str); send_to_char (buf,ch); if (ch->pcdata->in_progress->text->len == 0) /* Are we continuing an old note or not? */ { switch (ch->pcdata->board->force_type) { case DEF_NORMAL: sprintf (buf, "If you press Return, default recipient \"" BOLD "%s" NO_COLOR "\" will be chosen.\n\r", ch->pcdata->board->names); break; case DEF_INCLUDE: sprintf (buf, "The recipient list MUST include \"" BOLD "%s" NO_COLOR "\". If not, it will be added automatically.\n\r", ch->pcdata->board->names); break; case DEF_EXCLUDE: sprintf (buf, "The recipient of this note must NOT include: \"" BOLD "%s" NO_COLOR "\".", ch->pcdata->board->names); break; } send_to_char (buf,ch); send_to_char ("\n\r" YELLOW "To" NO_COLOR ": ",ch); ch->desc->connected = CON_NOTE_TO; /* nanny takes over from here */ } else /* we are continuing, print out all the fields and the note so far*/ { sprintf (buf, "To" NO_COLOR ": %s\n\r" "Expires: %s\n\r" "Subject: %s\n\r", ch->pcdata->in_progress->to_list->str, ctime(&ch->pcdata->in_progress->expire), ch->pcdata->in_progress->subject->str); send_to_char (buf,ch); send_to_char (BOLD GREEN "Your note so far:\n\r" NO_COLOR,ch); send_to_char (ch->pcdata->in_progress->text->str,ch); send_to_char ("\n\rEnter text. Type "RED" ~ "NO_COLOR" or "RED" END "NO_COLOR" on an empty line to end note, " RED "<<<" NO_COLOR " to delete a line.\n\r" "--------------------------------------------------------------------------------\n\r",ch); ch->desc->connected = CON_NOTE_TEXT; } } /* Read next note in current group. If no more notes, go to next board */ static void do_nread (CHAR_DATA *ch, char *argument) { NOTE_DATA *p; int count = 0, number; time_t *last_note = &ch->pcdata->last_note[board_number(ch->pcdata->board)]; if (!str_cmp(argument, "again")) { /* read last note again */ } else if (!str_cmp(argument, "ZMUD")) { char buf[200]; count = 1; for (p = ch->pcdata->board->note_first; p ; p = p->next, count++) if ((p->date_stamp > *last_note) && is_note_to(ch,p)) { send_to_char("\n\rNOTE START:\n\r",ch); show_note_to_charZ (ch,p,count,ch->pcdata->board->short_name); /* Advance if new note is newer than the currently newest for that char */ *last_note = UMAX (*last_note, p->date_stamp); send_to_char("\n\rNOTE END:\n\r",ch); return; } send_to_char ("No new notes in this board.\n\r",ch); if (next_board (ch)) sprintf (buf, "Changed to next board with unread notes, %s.\n\r", ch->pcdata->board->short_name); else sprintf (buf, "There are no more boards with unread notes.\n\r"); send_to_char (buf,ch); return; } else if (is_number (argument)) { number = atoi(argument); for (p = ch->pcdata->board->note_first; p; p = p->next) if (++count == number) break; if (!p || !is_note_to(ch, p)) send_to_char ("No such note.\n\r",ch); else { show_note_to_char (ch,p,count,ch->pcdata->board->short_name); *last_note = UMAX (*last_note, p->date_stamp); } } else /* just next one */ { char buf[200]; count = 1; for (p = ch->pcdata->board->note_first; p ; p = p->next, count++) if ((p->date_stamp > *last_note) && is_note_to(ch,p)) { show_note_to_char (ch,p,count,ch->pcdata->board->short_name); /* Advance if new note is newer than the currently newest for that char */ *last_note = UMAX (*last_note, p->date_stamp); return; } send_to_char ("No new notes in this board.\n\r",ch); if (next_board (ch)) sprintf (buf, "Changed to next board with unread notes, %s.\n\r", ch->pcdata->board->short_name); else sprintf (buf, "There are no more boards with unread notes.\n\r"); send_to_char (buf,ch); } } /* Remove a note */ static void do_nremove (CHAR_DATA *ch, char *argument) { NOTE_DATA *p; if (!is_number(argument)) { send_to_char ("Remove which note?\n\r",ch); return; } p = find_note (ch, ch->pcdata->board, atoi(argument)); if (!p) { send_to_char ("No such note.\n\r",ch); return; } if (str_cmp(ch->name->str,p->sender->str) && (get_trust(ch) < LEVEL_JUDGE)) { send_to_char ("You are not authorized to remove this note.\n\r",ch); return; } unlink_note (ch->pcdata->board,p); free_note (p); send_to_char ("Note removed!\n\r",ch); save_board(ch->pcdata->board); /* save the board */ } /* List all notes or if argument given, list N of the last notes */ /* Shows REAL note numbers! */ static void do_nlist (CHAR_DATA *ch, char *argument) { int count= 0, show = 0, lower = 0, upper = 0, num = 0, has_shown = 0; time_t last_note; NOTE_DATA *p; char buf[MAX_STRING_LENGTH]; //BUFFER *prgstrShow; char arg1[MAX_STRING_LENGTH]; char arg2[MAX_STRING_LENGTH]; bool fAuthorRestrict = FALSE; bool fShowRange = FALSE; bool fShowLast = FALSE; /* Parse arguments and determine mode */ argument = one_argument (argument, arg1); argument = one_argument (argument, arg2); //prgstrShow = new_buf(); for ( ;; ) { if (arg1[0] == '\0') /* no args? Go on without modification... */ break; if (is_number(arg1)) /* first argument could be a number or a name */ { if (arg2[0] == '\0') /* it's just a single number, they want last X */ { show = atoi(arg1); fShowLast = TRUE; for (p = ch->pcdata->board->note_first; p; p = p->next) if (is_note_to(ch,p)) count++; break; } else /* we have a number and another arg... */ { if (is_number(arg2)) /* okay, we have two numbers--a range */ { lower = atoi(arg1); upper = atoi(arg2); fShowRange = TRUE; break; } else /* do it right next time, dumbass */ { send_to_char("Syntax: note list <author> | show all notes by 'author'\n\r" " note list <start#> <end#> | show notes start# to end#\n\r" " note list <#> | show last # notes\n\r", ch); return; } } } else /* we have a name */ { fAuthorRestrict = TRUE; break; } } //add_buf(prgstrShow,"{RNotes on this board:{x\n\r{RNum> Author Subject{x\n\r"); send_to_char("{RNotes on this board:{x\n\r{RNum> Author Subject{x\n\r",ch); last_note = ch->pcdata->last_note[board_number (ch->pcdata->board)]; for (p = ch->pcdata->board->note_first; p; p = p->next) { num++; if (is_note_to(ch,p)) { has_shown++; /* note that we want to see X VISIBLE note, not just last X */ if ((fShowLast == FALSE && fAuthorRestrict == FALSE && fShowRange == FALSE) /* plain note list */ || (fShowLast && (count-show) < has_shown) /* don't show unless it's one of the last n messages */ || (fShowRange && num >= lower && num <= upper) /* show range between lower and upper */ || (fAuthorRestrict && !str_cmp(arg1, p->sender->str))) /* note is from arg1 */ { sprintf (buf, BOLD "%3d" NO_COLOR ">" BLUE BOLD "%c" NO_COLOR YELLOW BOLD "%-13s" NO_COLOR YELLOW " %s" NO_COLOR " \n\r", num, last_note < p->date_stamp ? '*' : ' ', p->sender->str, p->subject->str); send_to_char(buf,ch); //add_buf(prgstrShow,buf); } } } //page_to_char(buf_string(prgstrShow),ch); //free_buf(prgstrShow); } /* catch up with some notes */ static void do_ncatchup (CHAR_DATA *ch, char *argument) { NOTE_DATA *p; /* Find last note */ for (p = ch->pcdata->board->note_first; p && p->next; p = p->next); if (!p) send_to_char ("Alas, there are no notes in that board.\n\r",ch); else { ch->pcdata->last_note[board_number(ch->pcdata->board)] = p->date_stamp; send_to_char ("All messages skipped.\n\r",ch); } } /* Dispatch function for backwards compatibility */ void do_note (CHAR_DATA *ch, char *argument) { char arg[MAX_INPUT_LENGTH]; if (IS_NPC(ch)) return; argument = one_argument (argument, arg); if ((!arg[0]) || (!str_cmp(arg, "read"))) /* 'note' or 'note read X' */ do_nread (ch, argument); else if (!str_cmp (arg, "list")) do_nlist (ch, argument); else if (!str_cmp (arg, "write")) do_nwrite (ch, argument); else if (!str_cmp (arg, "remove")) do_nremove (ch, argument); else if (!str_cmp (arg, "purge")) send_to_char ("Obsolete.\n\r",ch); else if (!str_cmp (arg, "archive")) send_to_char ("Obsolete.\n\r",ch); else if (!str_cmp (arg, "catchup")) do_ncatchup (ch, argument); else do_help (ch, "note"); } /* Show all accessible boards with their numbers of unread messages OR change board. New board name can be given as a number or as a name (e.g. board personal or board 4 */ void do_board (CHAR_DATA *ch, char *argument) { int i, count, number; char buf[200]; if (IS_NPC(ch)) return; if (!argument[0]) /* show boards */ { int unread; count = 1; send_to_char (BOLD RED "Num Name Unread Description" NO_COLOR "\n\r" RED "--- ------------ ------ --------------------------------------------------------" NO_COLOR "\n\r",ch); for (i = 0; i < MAX_BOARD; i++) { unread = unread_notes (ch,&boards[i]); /* how many unread notes? */ if (unread != BOARD_NOACCESS) { sprintf (buf, BOLD "%2d" NO_COLOR "> " GREEN BOLD "%12s" NO_COLOR " [%s%4d" NO_COLOR "] " YELLOW "%s" NO_COLOR "\n\r", count, boards[i].short_name, unread ? RED : GREEN, unread, boards[i].long_name); send_to_char (buf,ch); count++; } /* if has access */ } /* for each board */ sprintf (buf, "\n\rYou current board is " BOLD "%s" NO_COLOR ".\n\r", ch->pcdata->board->short_name); send_to_char (buf,ch); /* Inform of rights */ if (has_read_access(ch,ch->pcdata->board) == BOARD_NOACCESS) send_to_char ("You cannot read or write on this board.\n\r",ch); else if (has_write_access(ch,ch->pcdata->board) == BOARD_NOACCESS) send_to_char ("You can only read notes from this board.\n\r",ch); else send_to_char ("You can both read and write on this board.\n\r",ch); return; } /* if empty argument */ if (ch->pcdata->in_progress) { send_to_char ("Please finish your interrupted note first.\n\r",ch); return; } /* Change board based on its number */ if (is_number(argument)) { count = 0; number = atoi(argument); for (i = 0; i < MAX_BOARD; i++) if (unread_notes(ch,&boards[i]) != BOARD_NOACCESS) if (++count == number) break; if (count == number) /* found the board.. change to it */ { ch->pcdata->board = &boards[i]; sprintf (buf, "Current board changed to " BOLD "%s" NO_COLOR ". %s.\n\r",boards[i].short_name, (has_write_access(ch,ch->pcdata->board) == BOARD_NOACCESS) ? "You can only read here" : "You can both read and write here"); send_to_char (buf,ch); } else /* so such board */ send_to_char ("No such board.\n\r",ch); return; } /* Non-number given, find board with that name */ for (i = 0; i < MAX_BOARD; i++) if (!str_cmp(boards[i].short_name, argument)) break; if (i == MAX_BOARD) { send_to_char ("No such board.\n\r",ch); return; } /* Does ch have access to this board? */ if (unread_notes(ch,&boards[i]) == BOARD_NOACCESS) { send_to_char ("No such board.\n\r",ch); return; } ch->pcdata->board = &boards[i]; sprintf (buf, "Current board changed to " BOLD "%s" NO_COLOR ". %s.\n\r",boards[i].short_name, (has_write_access(ch,ch->pcdata->board) == BOARD_NOACCESS) ? "You can only read here" : "You can both read and write here"); send_to_char (buf,ch); } /* Send a note to someone on the personal board */ void personal_message (const char *sender, const char *to, const char *subject, const int expire_days, const char *text) { make_note ("Personal", sender, to, subject, expire_days, text); } void make_note (const char* board_name, const char *sender, const char *to, const char *subject, const int expire_days, const char *text) { int board_index = board_lookup (board_name); BOARD_DATA *board; NOTE_DATA *note; char *strtime; if (board_index == BOARD_NOTFOUND) { bug ("make_note: board not found",0); return; } if (strlen(text) > MAX_NOTE_TEXT) { bug ("make_note: text too long (%d bytes)", strlen(text)); return; } board = &boards [board_index]; note = new_note(); /* allocate new note */ note->sender = g_string_assign(note->sender,sender); note->to_list = g_string_assign(note->to_list,to); note->subject = g_string_assign(note->subject,subject); note->expire = current_time + expire_days * 60 * 60 * 24; note->text = g_string_assign(note->text,text); /* convert to ascii. ctime returns a string which last character is \n, so remove that */ strtime = ctime (¤t_time); strtime[strlen(strtime)-1] = '\0'; note->date = g_string_assign(note->date,strtime); finish_note (board, note); } /* tries to change to the next board with unread messages */ static bool next_board (CHAR_DATA *ch) { int i = board_number (ch->pcdata->board) + 1; if (i >= MAX_BOARD) i = 0; while ((i != board_number (ch->pcdata->board)) && ( (unread_notes(ch,&boards[i]) == BOARD_NOACCESS) || (unread_notes(ch,&boards[i]) == 0))) { i++; if (i >= MAX_BOARD) i = 0; } if (i == board_number (ch->pcdata->board)) return FALSE; else { ch->pcdata->board = &boards[i]; return TRUE; } } void handle_con_note_to (DESCRIPTOR_DATA *d, char * argument) { char buf [MAX_INPUT_LENGTH]; CHAR_DATA *ch = d->character; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; bug ("nanny: In CON_NOTE_TO, but no note in progress",0); return; } str_cpy (buf, argument); smash_tilde (buf); /* change ~ to - as we save this field as a string later */ switch (ch->pcdata->board->force_type) { case DEF_NORMAL: /* default field */ if (!buf[0]) /* empty string? */ { ch->pcdata->in_progress->to_list = g_string_assign(ch->pcdata->in_progress->to_list,ch->pcdata->board->names); sprintf (buf, "Assumed default recipient: " BOLD "%s" NO_COLOR "\n\r", ch->pcdata->board->names); send_to_char (buf, ch); } else ch->pcdata->in_progress->to_list = g_string_assign(ch->pcdata->in_progress->to_list,buf); break; case DEF_INCLUDE: /* forced default */ if (!is_full_name (ch->pcdata->board->names, buf)) { strcat (buf, " "); strcat (buf, ch->pcdata->board->names); ch->pcdata->in_progress->to_list = g_string_assign(ch->pcdata->in_progress->to_list,buf); sprintf (buf, "\n\rYou did not specify %s as recipient, so it was automatically added.\n\r" "New To: %s\n\r", ch->pcdata->board->names, ch->pcdata->in_progress->to_list->str); send_to_char (buf, ch); } else ch->pcdata->in_progress->to_list = g_string_assign(ch->pcdata->in_progress->to_list,buf); break; case DEF_EXCLUDE: /* forced exclude */ if (!buf[0]) { send_to_char ("You must specify a recipient.\n\r" BOLD YELLOW "To" NO_COLOR ": ", ch); return; } if (is_full_name (ch->pcdata->board->names, buf)) { sprintf (buf, "You are not allowed to send notes to %s on this board. Try again.\n\r" BOLD YELLOW "To" NO_COLOR ": ", ch->pcdata->board->names); send_to_char (buf, ch); return; /* return from nanny, not changing to the next state! */ } else ch->pcdata->in_progress->to_list = g_string_assign(ch->pcdata->in_progress->to_list,buf); break; } send_to_char ("\n\rSubject: ", ch); d->connected = CON_NOTE_SUBJECT; } void handle_con_note_subject (DESCRIPTOR_DATA *d, char * argument) { char buf [MAX_INPUT_LENGTH]; CHAR_DATA *ch = d->character; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; bug ("nanny: In CON_NOTE_SUBJECT, but no note in progress",0); return; } str_cpy (buf, argument); smash_tilde (buf); /* change ~ to - as we save this field as a string later */ /* Do not allow empty subjects */ if (!buf[0]) { write_to_buffer (d, "Please find a meaningful subject!\n\r",0); send_to_char (BOLD YELLOW "Subject" NO_COLOR ": ", ch); } else if (strlen(buf)>60) { send_to_char ("No, no. This is just the Subject. You're note writing the note yet. Twit.\n\r",ch); } else /* advance to next stage */ { ch->pcdata->in_progress->subject = g_string_assign(ch->pcdata->in_progress->subject,buf); if (IS_IMMORTAL(ch)) /* immortals get to choose number of expire days */ { sprintf (buf,"\n\rHow many days do you want this note to expire in?\n\r" "Press Enter for default value for this board, "RED "%d" NO_COLOR " days.\n\r" "Expire: ", ch->pcdata->board->purge_days); send_to_char (buf, ch); d->connected = CON_NOTE_EXPIRE; } else { ch->pcdata->in_progress->expire = current_time + ch->pcdata->board->purge_days * 24L * 3600L; sprintf (buf, "This note will expire %s\r",ctime(&ch->pcdata->in_progress->expire)); send_to_char (buf,ch); send_to_char ( "\n\rEnter text. Type " RED "~" NO_COLOR " or " RED "END" NO_COLOR " on an empty line to end note, " RED "<<<" NO_COLOR " to delete a line.\n\r" "--------------------------------------------------------------------------------\n\r",ch); d->connected = CON_NOTE_TEXT; } } } void handle_con_note_expire(DESCRIPTOR_DATA *d, char * argument) { CHAR_DATA *ch = d->character; char buf[MAX_STRING_LENGTH]; time_t expire; int days; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; bug ("nanny: In CON_NOTE_EXPIRE, but no note in progress",0); return; } /* Numeric argument. no tilde smashing */ str_cpy (buf, argument); if (!buf[0]) /* assume default expire */ days = ch->pcdata->board->purge_days; else /* use this expire */ { if (!is_number(buf)) { send_to_char ("Write the number of days!\n\r",ch); send_to_char ("Expire: ",ch); return; } else { days = atoi (buf); if (days <= 0) { send_to_char ( "This is a positive MUD. Use positive numbers only! :)\n\r",ch); send_to_char ( "Expire: ",ch); return; } } } expire = current_time + (days*24L*3600L); /* 24 hours, 3600 seconds */ ch->pcdata->in_progress->expire = expire; /* note that ctime returns XXX\n so we only need to add an \r */ send_to_char ( "\n\rEnter text. Type " RED "~" NO_COLOR " or " RED "END" NO_COLOR " on an empty line to end note, " RED "<<<" NO_COLOR " to delete a line.\n\r" "--------------------------------------------------------------------------------\n\r",ch); d->connected = CON_NOTE_TEXT; } void handle_con_note_text (DESCRIPTOR_DATA *d, char * argument) { CHAR_DATA *ch = d->character; GString *buf; char letter[4*MAX_STRING_LENGTH]; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; bug ("nanny: In CON_NOTE_TEXT, but no note in progress",0); return; } /* First, check for EndOfNote marker */ buf = g_string_new(argument); if ((!str_cmp(buf->str, "~")) || (!str_cmp(buf->str, "END"))) { send_to_char ("\n\r\n\r",ch); send_to_char (szFinishPrompt, ch); send_to_char ("\n\r", ch); d->connected = CON_NOTE_FINISH; return; } /* delete last line if '<<<' was on a single line */ if (!str_cmp(buf->str, "<<<")) { int len; bool found = FALSE; if (ch->pcdata->in_progress->text->len == 0 || ch->pcdata->in_progress->text->str[0] == '\0') { send_to_char("No lines left to remove.\n\r",ch); return; } buf = g_string_assign(buf,ch->pcdata->in_progress->text->str); for (len = buf->len; len > 0; len--) { if (buf->str[len] == '\n') /* notes here have \r\n line endings... */ { if (!found) /* back it up */ { if (len > 0) len--; found = TRUE; } else /* found the second one */ { buf->str[len + 1] = '\0'; ch->pcdata->in_progress->text = g_string_assign(ch->pcdata->in_progress->text,buf->str); send_to_char("Previous line removed.\n\r",ch); return; } } } ch->pcdata->in_progress->text = g_string_assign(ch->pcdata->in_progress->text,buf->str); send_to_char("All lines removed--note empty.\n\r", ch); return; } smash_tilde (buf->str); /* smash it now */ /* Check for too long lines. Do not allow lines longer than 80 chars */ if (buf->len > MAX_LINE_LENGTH) { send_to_char ("Too long line rejected. Do NOT go over 80 characters!\n\r",ch); return; } /* Not end of note. Copy current text into temp buffer, add new line, and copy back */ /* How would the system react to str_cpy( , NULL) ? */ if (ch->pcdata->in_progress->text->len > 0) { str_cpy (letter, ch->pcdata->in_progress->text->str); } else str_cpy (letter, ""); /* Check for overflow */ if ((strlen(letter) + buf->len) > MAX_NOTE_TEXT) { /* Note too long, take appropriate steps */ send_to_char ("Note too long!\n\r", ch); free_note (ch->pcdata->in_progress); ch->pcdata->in_progress = NULL; /* important */ d->connected = CON_PLAYING; return; } /* Add new line to the buffer */ strcat (letter, buf->str); strcat (letter, "\r\n"); /* new line. \r first to make note files better readable */ /* allocate dynamically */ ch->pcdata->in_progress->text = g_string_assign(ch->pcdata->in_progress->text,letter); g_string_free(buf,TRUE); } void handle_con_note_finish (DESCRIPTOR_DATA *d, char * argument) { char log_buf[MSL]; CHAR_DATA *ch = d->character; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; bug ("nanny: In CON_NOTE_FINISH, but no note in progress",0); return; } switch (tolower(argument[0])) { case 'c': /* keep writing */ send_to_char ("Continuing note...\n\r",ch); d->connected = CON_NOTE_TEXT; break; case 'v': /* view note so far */ if (ch->pcdata->in_progress->text) { send_to_char (GREEN "Text of your note so far:" NO_COLOR "\n\r",ch); send_to_char ( ch->pcdata->in_progress->text->str, ch); } else send_to_char ("You haven't written a thing!\n\r\n\r",ch); send_to_char ( szFinishPrompt, ch); send_to_char ("\n\r",ch); break; case 'p': /* post note */ finish_note (ch->pcdata->board, ch->pcdata->in_progress); send_to_char ("Note posted.\n\r",ch); d->connected = CON_PLAYING; /* remove AFK status */ ch->pcdata->in_progress = NULL; act (BOLD GREEN "$n finishes $s note." NO_COLOR , ch, NULL, NULL, TO_ROOM); sprintf(log_buf,"%s has posted a note on the %s board.",ch->name->str,ch->pcdata->board->short_name); logchan(log_buf, NULL, NULL,WIZ_BOARD,0, LEVEL_IMMORTAL); break; case 'f': send_to_char ("Note cancelled!\n\r",ch); free_note (ch->pcdata->in_progress); ch->pcdata->in_progress = NULL; d->connected = CON_PLAYING; /* remove afk status */ break; case 'r': /* clear note and keep writing */ send_to_char ("Clearing and restarting note text...\n\r",ch); ch->pcdata->in_progress->text = g_string_assign(ch->pcdata->in_progress->text,""); d->connected = CON_NOTE_TEXT; break; default: /* invalid response */ send_to_char ("Huh? Valid answers are:\n\r\n\r",ch); send_to_char (szFinishPrompt, ch); send_to_char ("\n\r",ch); } } void strip_note( CHAR_DATA *ch, OBJ_DATA *obj) { char buf[MAX_STRING_LENGTH]; char buf2[MAX_STRING_LENGTH]; BOARD_DATA *board; NOTE_DATA *note; char *strtime; int board_index; board_index = board_lookup (g_strdup("immortal")); board = &boards [board_index]; note = new_note(); /* allocate new note */ note->sender = g_string_assign(note->sender,"artifact strip code"); sprintf(buf,"immortal %s",ch->name->str); note->to_list = g_string_assign(note->to_list,buf); note->subject = g_string_assign(note->subject,"Someone lost a ARTI!"); note->expire = current_time + 60 * 60 * 60 * 24; sprintf(buf,"%s lost %s, vnum %d\n\r",ch->name->str,obj->short_descr->str,obj->pIndexData->vnum); sprintf(buf2,"Object Logged off: %s Time Object Logged on: %s\n\r",(char *) ctime(&obj->last_seen_on),(char *) ctime( ¤t_time )); strcat(buf, buf2); note->text = g_string_assign(note->text,buf); /* convert to ascii. ctime returns a string which last character is \n, so remove that */ strtime = ctime (¤t_time); strtime[strlen(strtime)-1] = '\0'; note->date = g_string_assign(note->date,strtime); finish_note (board, note); } void decap_note( CHAR_DATA *ch, CHAR_DATA *victim, int Decap_Type, int Decap_OutCome) { char buf[MAX_STRING_LENGTH]; char buf2[MAX_STRING_LENGTH]; char buf3[MAX_STRING_LENGTH]; BOARD_DATA *board; NOTE_DATA *note; char *strtime; int board_index; board_index = board_lookup (g_strdup("immortal")); board = &boards [board_index]; note = new_note(); /* allocate new note */ note->sender = g_string_assign(note->sender,"Decapitation Notifcation"); sprintf(buf,"immortal %s",ch->name->str); note->to_list = g_string_assign(note->to_list,buf); sprintf(buf2,"%s was decapitated by %s",victim->name->str,ch->name->str); note->subject = g_string_assign(note->subject,buf2); note->expire = current_time + 60 * 60 * 60 * 24; switch (Decap_Type) { case DECAPT_TEAR: sprintf(buf2,"Torn"); break; case DECAPT_DECAP: sprintf(buf2,"Decapitated"); break; case DECAPT_DIAB: sprintf(buf2,"Diablorised"); break; default: sprintf(buf2,"Error in code."); break; } switch (Decap_OutCome) { case DECAPO_NO_STAT: sprintf(buf3,"No status"); break; case DECAPO_FOR_STAT: sprintf(buf3,"Status"); break; case DECAPO_FOR_ARTI: sprintf(buf3,"Artifact"); break; case DECAPO_RP: sprintf(buf3,"Rp Loss"); break; default: sprintf(buf3,"Error in outcome"); break; } sprintf(buf,"%s was %s by %s for %s\n\r",victim->name->str,buf2,ch->name->str,buf3); note->text = g_string_assign(note->text,buf); /* convert to ascii. ctime returns a string which last character is \n, so remove that */ strtime = ctime (¤t_time); strtime[strlen(strtime)-1] = '\0'; note->date = g_string_assign(note->date,strtime); finish_note (board, note); }