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