area/
build/testing/
log/
player/
player/backup/
/***************************************************************************
 *  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 (&current_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 (&current_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( &current_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 (&current_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 (&current_time);
	strtime[strlen(strtime)-1] = '\0';

	note->date = g_string_assign(note->date,strtime);

	finish_note (board, note);

}