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