/*************************************************************************** * 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 "merc.h" #include "interp.h" char *string_linedel( char *, int ); char *string_lineadd( char *, char *, int ); char *numlineas( char * ); BOARD_DATA boards[MAX_BOARD] = { // Short, Desc, Read, Write, Def Recip, Forced, Expire, NULL, FALSE { "General", "General discussion", 0, 2, "all", DEF_INCLUDE,21, NULL, FALSE }, { "Ideas", "Suggestion for improvement", 0, 2, "all", DEF_NORMAL, 60, NULL, FALSE }, { "Announce", "Announcements from Immortals", 0, LEVEL_IMMORTAL, "all", DEF_NORMAL, 60, NULL, FALSE }, { "Bugs", "Typos, bugs, errors", 0, 1, "imm", DEF_NORMAL, 60, NULL, FALSE }, { "Personal", "Personal messages", 0, 1, "all", DEF_EXCLUDE,28, NULL, FALSE }, { "Security", "PR/Security", LEVEL_IMMORTAL, LEVEL_IMMORTAL, "imm", DEF_NORMAL, 31, NULL, FALSE } }; const char * szFinishPrompt = "(`wC`n)ontinue, (`wV`n)iew, (`wP`n)ost or (`wF`n)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) { if (note->sender) free_string (note->sender); if (note->to_list) free_string (note->to_list); if (note->subject) free_string (note->subject); if (note->date) /* was note->datestamp for some reason */ free_string (note->date); if (note->text) free_string (note->text); 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 alloc_mem(note, NOTE_DATA, 1); /* Zero all the fields - does not gurantee zeroed memory */ note->next = NULL; note->sender = NULL; note->expire = 0; note->to_list = NULL; note->subject = NULL; note->date = NULL; note->date_stamp = 0; note->text = NULL; 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); fprintf (fp, "Date %s~\n", note->date); fprintf (fp, "Stamp %ld\n", note->date_stamp); fprintf (fp, "Expire %ld\n", note->expire); fprintf (fp, "To %s~\n", note->to_list); fprintf (fp, "Subject %s~\n", note->subject); fprintf (fp, "Text\n%s~\n\n", note->text); } /* 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) { mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: Could not open note file in append mode"); 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) mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: unlink_note: could not find note."); 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]; NOTE_DATA *note; sprintf (filename, "%s%s", NOTE_DIR, board->short_name); fp = fopen (filename, "w"); if (!fp) mudlogf(BRF, LVL_CODER, TRUE, "SYSERR: Error writing to: %s", filename); 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 buf[4*MAX_STRING_LENGTH]; /* Ugly colors ? */ sprintf( buf, "`yTo`w:`n %s\r\n" "`yDate`w:`n %s\r\n" "[`w%3d`n] %s :: %s\r\n" "`G===============================`n\r\n" "%s\r\n", note->to_list, note->date, num, note->sender, note->subject, note->text); 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; 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 = (NOTE_DATA *)alloc_perm(sizeof(*pnote)); if ( str_cmp( fread_word( fp ), "sender" ) ) break; pnote->sender = fread_string( fp ); if ( str_cmp( fread_word( fp ), "date" ) ) break; 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 = fread_string( fp ); if ( str_cmp( fread_word( fp ), "subject" ) ) break; pnote->subject = fread_string( fp ); if ( str_cmp( fread_word( fp ), "text" ) ) break; 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) mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: Could not open archive boards for writing."); 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; } mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: load_notes: bad key word."); 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 (!str_cmp (ch->name, note->sender)) return TRUE; if (is_exact_name ("all", note->to_list)) return TRUE; if (IS_IMMORTAL(ch) && ( is_exact_name ("imm", note->to_list) || is_exact_name ("imms", note->to_list) || is_exact_name ("immortal", note->to_list) || is_exact_name ("god", note->to_list) || is_exact_name ("gods", note->to_list) || is_exact_name ("immortals", note->to_list))) return TRUE; if ((get_trust(ch) == MAX_LEVEL) && ( is_exact_name ("imp", note->to_list) || is_exact_name ("imps", note->to_list) || is_exact_name ("implementor", note->to_list) || is_exact_name ("implementors", note->to_list))) return TRUE; if (is_exact_name (ch->name, note->to_list)) return TRUE; /* Allow a note to e.g. 40 to send to characters level 40 and above */ if (is_number(note->to_list) && get_trust(ch) >= atoi(note->to_list)) 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; if (board->read_level > get_trust(ch)) return BOARD_NOACCESS; last_read = ch->pcdata->last_note[board_number(board)]; 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; } /* * COMMANDS */ // Created to substitute do_board while entering the game - Mend ACMD(do_checknote) { int i, count=0, unread=0; for (i = 0; i < MAX_BOARD; i++) { unread = unread_notes(ch, &boards[i]); if (unread != BOARD_NOACCESS) count += unread; } if (count < 1) send_to_char("You have no new notes on the board.\r\n",ch); else ch->Send( "You have %d unread note%s on the board. Type 'board'.\r\n", count, (count != 1) ? "s" : ""); } /* Start writing a note */ //static void do_nwrite (CHAR_DATA *ch, char *argument) ACMD(do_nwrite) { char *strtime; char buf[200]; if (IS_NPC(ch)) /* NPC cannot post notes */ return; if (get_trust(ch) < ch->pcdata->board->write_level) { 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)) { 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 = str_dup (ch->name); /* 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 = str_dup (strtime); } act("`g$n starts writing a note.`n", ch, NULL, NULL, TO_ROOM); if (!IS_NPC(ch)) SET_BIT(PLR_FLAGS(ch), PLR_WRITING); /* Begin writing the note ! */ sprintf (buf, "You are now %s a new note on the `w%s`n board.\n\r" "If you are using tintin, type #verbose to turn off alias expansion!\n\r\n\r", ch->pcdata->in_progress->text ? "continuing" : "posting", ch->pcdata->board->short_name); send_to_char (buf,ch); sprintf (buf, "`yFrom`w:`n %s\n\r\n\r", ch->name); send_to_char (buf,ch); if (!ch->pcdata->in_progress->text) /* 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 `w%s`n will be chosen.\n\r", ch->pcdata->board->names); break; case DEF_INCLUDE: sprintf (buf, "The recipient list MUST include `w%s`n. 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: `w%s`n.", ch->pcdata->board->names); break; } send_to_char (buf,ch); send_to_char ("\n\r`yTo`w:`n ",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, "`yTo`w:`n %s\n\r" "`yExpires`w:`n %s\n\r" "`ySubject`w:`n %s\n\r", ch->pcdata->in_progress->to_list, ctime(&ch->pcdata->in_progress->expire), ch->pcdata->in_progress->subject); send_to_char (buf,ch); send_to_char ("`gYour note so far:`n\n\r",ch); send_to_char (ch->pcdata->in_progress->text,ch); send_to_char ("\n\r" "Enter text. Type `w/s`n on an empty line to end note.\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 */ ACMD(do_nread) { 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 (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); *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); /* 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, %s.\n\r", ch->pcdata->board->short_name); else sprintf (buf, "There are no more boards.\n\r"); send_to_char (buf,ch); } } /* Remove a note */ ACMD(do_nremove) { 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,p->sender) && (get_trust(ch) < MAX_LEVEL)) { 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! */ ACMD(do_nlist) { int count= 0, show = 0, num = 0, has_shown = 0; time_t last_note; NOTE_DATA *p; char buf[MAX_STRING_LENGTH]; if (is_number(argument)) /* first, count the number of notes */ { show = atoi(argument); for (p = ch->pcdata->board->note_first; p; p = p->next) if (is_note_to(ch,p)) count++; } send_to_char( "`wNotes on this board:`n\n\r" "`rNum> Author Subject`n\n\r" "`G============================`n\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 (!show || ((count-show) < has_shown)) { sprintf (buf, "`w%3d`n> `b%c `y%-13s %s`n\n\r", num, last_note < p->date_stamp ? '*' : ' ', p->sender, p->subject); send_to_char (buf,ch); } } } } /* catch up with some notes */ ACMD(do_ncatchup) { 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 mesages skipped.\n\r",ch); } } /* Dispatch function for backwards compatibility */ ACMD(do_note) { char arg[MAX_INPUT_LENGTH]; if (IS_NPC(ch)) return; argument = one_argument(argument, arg); if ((!arg[0]) || (!str_prefix(arg, "read"))) do_nread(ch, argument, 0); else if (!str_prefix(arg, "list")) do_nlist(ch, argument, 0); else if (!str_prefix(arg, "write")) do_nwrite(ch, argument, 0); else if (!str_prefix(arg, "remove")) do_nremove(ch, argument, 0); else if (!str_prefix(arg, "purge")) send_to_char("Obsolete.\n\r",ch); else if (!str_prefix(arg, "archive")) send_to_char("Obsolete.\n\r",ch); else if (!str_prefix(arg, "catchup")) do_ncatchup(ch, argument, 0); else do_help(ch, "note", 0); } /* 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 */ ACMD(do_board) { int i, count, number; char buf[200]; if (IS_NPC(ch)) return; if (!argument[0]) /* show boards */ { int unread; count = 1; send_to_char( "`rNum Name Unread Description`n\n\r" "`G=== ============ ====== ===========`n\n\r",ch); for (i = 0; i < MAX_BOARD; i++) { unread = unread_notes (ch,&boards[i]); /* how many unread notes? */ if (unread != BOARD_NOACCESS) { ch->Send("`w%2d`n> `w%12s `n[%s%4d`n] `y%s`n\n\r", count, boards[i].short_name, unread ? "`r" : "`g", unread, boards[i].long_name); count++; } /* if has access */ } /* for each board */ ch->Send("\n\rYou current board is `w%s.`n\n\r", ch->pcdata->board->short_name); /* Inform of rights */ if (ch->pcdata->board->read_level > get_trust(ch)) ch->Send("You cannot read nor write notes on this board.\n\r"); else if (ch->pcdata->board->write_level > get_trust(ch)) ch->Send("You can only read notes from this board.\n\r"); else ch->Send("You can both read and write on this board.\n\r"); 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 `w%s.`n %s.\n\r",boards[i].short_name, (get_trust(ch) < boards[i].write_level) ? "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 number.\n\r",ch); return; } /* Non-number given, find board with that name */ for (i = 0; i < MAX_BOARD; i++) { if (boards[i].short_name == NULL) break; if (LOWER(argument[0]) == LOWER(boards[i].short_name[0]) && !str_prefix(argument, boards[i].short_name)) 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 `w%s.`n %s.\n\r",boards[i].short_name, (get_trust(ch) < boards[i].write_level) ? "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) { mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: make_note: board not found."); return; } if (strlen(text) > MAX_NOTE_TEXT) { mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: make_note: text too long (%d bytes)", strlen(text)); return; } board = &boards [board_index]; note = new_note(); /* allocate new note */ note->sender = str_dup (sender); note->to_list = str_dup(to); note->subject = str_dup (subject); note->expire = current_time + expire_days * 60 * 60 * 24; note->text = str_dup (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 = str_dup (strtime); finish_note (board, note); } /* tries to change to the next accessible board */ static bool next_board (CHAR_DATA *ch) { int i = board_number (ch->pcdata->board) + 1; while ((i < MAX_BOARD) && (unread_notes(ch,&boards[i]) == BOARD_NOACCESS)) i++; if (i == MAX_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; mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: nanny: In CON_NOTE_TO, but no note in progress."); return; } strcpy (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 = str_dup (ch->pcdata->board->names); sprintf (buf, "`yAssumed default recipient`w: %s`n\n\r", ch->pcdata->board->names); send_to_char(buf,ch); } else ch->pcdata->in_progress->to_list = str_dup (buf); break; case DEF_INCLUDE: /* forced default */ if (!is_exact_name (ch->pcdata->board->names, buf)) { strcat (buf, " "); strcat (buf, ch->pcdata->board->names); ch->pcdata->in_progress->to_list = str_dup(buf); sprintf (buf, "\n\rYou did not specify %s as recipient, so it was automatically added.\n\r" "`yNew To`w:`n %s\n\r", ch->pcdata->board->names, ch->pcdata->in_progress->to_list); send_to_char(buf,ch); } else ch->pcdata->in_progress->to_list = str_dup (buf); break; case DEF_EXCLUDE: /* forced exclude */ if (!buf[0]) { send_to_char("You must specify a recipient.\n\r" "`yTo`w:`n", ch); return; } if (is_exact_name (ch->pcdata->board->names, buf)) { sprintf (buf, "You are not allowed to send notes to %s on this board. Try again.\n\r" "`yTo`w:`n ", 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 = str_dup (buf); break; } send_to_char("\n\r`ySubject`w:`n ", 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; mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: nanny: In CON_NOTE_SUBJECT, but no note in progress."); return; } strcpy(buf, argument); /* change ~ to - as we save this field as a string later */ smash_tilde(buf); /* Do not allow empty subjects */ if (!buf[0]) { write_to_buffer (d, "Please find a meaningful subject!\n\r",0); send_to_char("`ySubject`w:`n ", ch); } else if (strlen(buf)>60) { write_to_buffer (d, "No, no. This is just the Subject. You're not writing the note yet. Twit.\n\r",0); } else /* advance to next stage */ { ch->pcdata->in_progress->subject = str_dup(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, `w%d`n days.\n\r" "`yExpire`w:`n ", 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)); write_to_buffer (d,buf,0); send_to_char("\n\r" "Enter text. Type `w/s`n on an empty line to end note.\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; mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: nanny: In CON_NOTE_EXPIRE, but no note in progress."); return; } /* Numeric argument. no tilde smashing */ strcpy (buf, argument); if (!buf[0]) /* assume default expire */ days = ch->pcdata->board->purge_days; else /* use this expire */ if (!is_number(buf)) { write_to_buffer (d,"Write the number of days!\n\r",0); send_to_char("`yExpire`w:`n ",ch); return; } else { days = atoi (buf); if (days <= 0) { write_to_buffer (d, "This is a positive MUD. Use positive numbers only! :)\n\r",0); send_to_char("`yExpires`w:`n",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\r" "Enter text. Type `w/s`n on an empty line to end note.\n\r" "=====================================================\n\r",ch); d->connected = CON_NOTE_TEXT; } #define PARSE_REPLACE 0 #define PARSE_HELP 1 #define PARSE_DELETE 2 #define PARSE_INSERT 3 #define PARSE_EDIT 4 #define PARSE_LIST_NORM 5 #define PARSE_LIST_NUM 6 void parse_note_text(int command, char *string, CHAR_DATA *ch) { char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; switch (command) { case PARSE_HELP: send_to_char("Edit help (commands on blank line):\n\r", ch); send_to_char("/r 'old' 'new' - replace a substring\n\r", ch); send_to_char(" (requires '', \"\")\n\r", ch); send_to_char("/h - get help (this info)\n\r", ch); send_to_char("/l - show string so far\n\r", ch); send_to_char("/n - show numbered string so far\n\r", ch); send_to_char("/f - (word wrap) string\n\r", ch); send_to_char("/c - clear string so far\n\r", ch); send_to_char("/d# - delete line number <num>\n\r", ch); send_to_char("/i# <str> - insert <str> on line <num>\n\r", ch); send_to_char("/e# <str> - replace line <num> with <str>\n\r", ch); send_to_char("/s - end string\n\r", ch); break; case PARSE_EDIT: string = one_argument(string, arg1); if (arg1[0] == '\0') { send_to_char("You must specify a line number.\n\r", ch); return; } ch->pcdata->in_progress->text = string_linedel(ch->pcdata->in_progress->text, atoi(arg1)); ch->pcdata->in_progress->text = string_lineadd(ch->pcdata->in_progress->text, string, atoi(arg1)); send_to_char("Line replaced.\n\r", ch); break; case PARSE_DELETE: ch->pcdata->in_progress->text = string_linedel(ch->pcdata->in_progress->text, atoi(string)); send_to_char("Line deleted.\n\r", ch); break; case PARSE_REPLACE: string = first_arg(string, arg1, FALSE); string = first_arg(string, arg2, FALSE); if (arg1[0] == '\0') { ch->Send("Usage: /r 'old string' 'new string'\n\r"); return; } ch->pcdata->in_progress->text = string_replace(ch->pcdata->in_progress->text, arg1, arg2); ch->Send("'%s' replaced with '%s'.\n\r", arg1, arg2); break; case PARSE_INSERT: string = first_arg(string, arg1, FALSE); ch->pcdata->in_progress->text = string_lineadd(ch->pcdata->in_progress->text, string, atoi(arg1)); send_to_char("Line inserted.\n\r", ch); break; case PARSE_LIST_NORM: ch->Send("String so far:\n\r"); ch->Send(ch->pcdata->in_progress->text); break; case PARSE_LIST_NUM: ch->Send("String so far:\n\r"); ch->Send(numlineas(ch->pcdata->in_progress->text)); break; default: ch->Send("Invalid command.\n\r"); mudlogf(BRF, LVL_CODER, TRUE, "SYSERR: invalid command passed to parse_note_text"); break; } } void handle_con_note_text (DESCRIPTOR_DATA *d, char *argument) { bool terminate = FALSE; int i = 2, j = 0, action = 0; CHAR_DATA *ch = d->character; char buf[MAX_STRING_LENGTH]; char actions[MAX_INPUT_LENGTH]; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: nanny: In CON_NOTE_TEXT, buf no note in progress."); return; } smash_tilde(argument); if ((action = (*argument == '/'))) { while (argument[i] != '\0') { actions[j] = argument[i]; i++; j++; } actions[j] = '\0'; *argument = '\0'; switch (argument[1]) { case 's': terminate = TRUE; *argument = '\0'; break; case 'c': send_to_char("String cleared.\n\r", ch); free_string(ch->pcdata->in_progress->text); ch->pcdata->in_progress->text = str_dup(""); return; case 'l': parse_note_text(PARSE_LIST_NORM, actions, ch); return; case 'n': parse_note_text(PARSE_LIST_NUM, actions, ch); return; case 'r': parse_note_text(PARSE_REPLACE, actions, ch); return; case 'f': ch->pcdata->in_progress->text = format_string(ch->pcdata->in_progress->text); send_to_char("String formatted.\n\r", ch); return; case 'd': parse_note_text(PARSE_DELETE, actions, ch); return; case 'i': parse_note_text(PARSE_INSERT, actions, ch); return; case 'e': parse_note_text(PARSE_EDIT, actions, ch); return; case 'h': parse_note_text(PARSE_HELP, actions, ch); return; default: send_to_char("Invalid command.\n\r", ch); return; } } if (terminate) { ch->Send("\n\r%s", szFinishPrompt); d->connected = CON_NOTE_FINISH; return; } if (ch->pcdata->in_progress->text) { strcpy(buf, ch->pcdata->in_progress->text); free_string(ch->pcdata->in_progress->text); ch->pcdata->in_progress->text = NULL; } else strcpy(buf, ""); if ((strlen(argument) + strlen (buf)) > MAX_NOTE_TEXT) { ch->Send("Note too long, bailing out!\n\r"); free_note(ch->pcdata->in_progress); ch->pcdata->in_progress = NULL; d->connected = CON_PLAYING; return; } smash_tilde(argument); strcat(buf, argument); strcat(buf, "\n\r"); ch->pcdata->in_progress->text = str_dup(buf); } void handle_con_note_finish (DESCRIPTOR_DATA *d, char * argument) { CHAR_DATA *ch = d->character; if (!ch->pcdata->in_progress) { d->connected = CON_PLAYING; mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: nanny: In CON_NOTE_FINISH, but no note in progress."); return; } switch(tolower(argument[0])) { case 'c': /* keep writing */ send_to_char("Continuing note...\n\r\n\r", ch); send_to_char(ch->pcdata->in_progress->text, ch); d->connected = CON_NOTE_TEXT; break; case 'v': /* view note so far */ if (ch->pcdata->in_progress->text) { send_to_char("Text of your note so far:\n\r\n\r",ch); send_to_char(ch->pcdata->in_progress->text, ch); write_to_buffer(d, "\r\n", 0); } else write_to_buffer (d,"You haven't written a thing!\n\r\n\r",0); send_to_char(szFinishPrompt,ch); break; case 'p': /* post note */ finish_note(ch->pcdata->board, ch->pcdata->in_progress); write_to_buffer(d, "Note posted.\n\r",0); if (!IS_NPC(ch)) REMOVE_BIT(PLR_FLAGS(ch), PLR_WRITING); d->connected = CON_PLAYING; ch->pcdata->in_progress = NULL; act("`g$n finishes $s note.`n", ch, NULL, NULL, TO_ROOM); break; case 'f': write_to_buffer(d, "Note cancelled!\n\r",0); free_note(ch->pcdata->in_progress); ch->pcdata->in_progress = NULL; if (!IS_NPC(ch)) REMOVE_BIT(PLR_FLAGS(ch), PLR_WRITING); d->connected = CON_PLAYING; break; default: /* invalid response */ write_to_buffer(d, "Huh? Valid answers are:\n\r\n\r",0); send_to_char(szFinishPrompt, ch); } }