#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include "include.h"
DECLARE_DO_FUN(do_help);
/*
* Original note board system (c) 1995-96 Erwin S. Andreasen
*/
#define L_SUP (MAX_LEVEL - 1) /* if not already defined */
#define IMPLEMENTOR MAX_LEVEL
#define L_IMM 151
#define true TRUE
#define false FALSE
NOTE_DATA *note_free;
void printf_to_desc(DESCRIPTOR_DATA * d, char *fmt, ...)
{
char buf[MSL];
va_list args;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
send_to_desc(buf, d);
}
BOARD_DATA boards[MAX_BOARD] = {
{"{CAnnounce{x", "Announcements from immortals", 0, L_IMM, IMPLEMENTOR, "all",
DEF_NORMAL, 60, NULL, false}
,
{"{RGeneral{x", "General discussion", 0, 2, IMPLEMENTOR, "all", DEF_INCLUDE,
21, NULL,
false}
,
{"{BIdeas{x", "Suggestion for improvement", 0, 2, IMPLEMENTOR, "all",
DEF_NORMAL, 60,
NULL, false}
,
{"{GBugs{x", "Bugs and oddities", 0, 1, L_IMM, "imm", DEF_NORMAL, 60, NULL,
false}
,
{"{YPersonal{x", "Personal messages", 0, 1, IMPLEMENTOR, "all", DEF_EXCLUDE,
28, NULL,
false},
{"{CCoders{x", "Code discussion", 0, L_IMM, IMPLEMENTOR, "imm", DEF_NORMAL, 60, NULL, false },
};
/* The prompt that the character is given after finishing a note with ~ or END */
const char *szFinishPrompt =
"{c({CC{c)ontinue, ({CV{c)iew, ({CP{c)ost or ({CF{c)orget it?{x";
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);
int get_total_unread(CHAR_DATA * ch);
int get_total_notes(CHAR_DATA * ch);
int get_max_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
note = (NOTE_DATA *) alloc_mem(sizeof(NOTE_DATA));
/* Zero all the field - Envy 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 = file_open(filename, "a");
append_note(fp, note);
file_close(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];
NOTE_DATA *note;
sprintf(filename, "%s/%s", NOTE_DIR, board->short_name);
fp = file_open(filename, "w");
for (note = board->note_first; note; note = note->next)
append_note(fp, note);
file_close(fp);
}
/* Show one not to a character */
static void show_note_to_char(CHAR_DATA * ch, NOTE_DATA * note, int num)
{
/* Ugly colors ? */
printf_to_char(ch,
"{c[{W%4d{c] {C%s{x: {c%s{x\n\r"
"{CDate{x: %s\n\r"
"{CTo{x: %s\n\r"
"{b==========================================================================={x\n\r"
"%s\n\r",
num, note->sender, note->subject,
note->date, note->to_list, note->text);
}
/* 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);
if(!file_exists(filename) )
return;
fp = file_open(filename, "r");
/* 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))
{
file_close(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) && (pnote->expire != 0))
{
char archive_name[200];
sprintf(archive_name, "%s/%s.old", NOTE_DIR, board->short_name);
fp_archive = file_open(archive_name, "a");
append_note(fp_archive, pnote);
file_close(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;
}
file_close(fp);
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 (!str_cmp(ch->name, note->sender))
return true;
if (is_full_name("all", note->to_list))
return true;
if (IS_IMMORTAL(ch) && (is_full_name("imm", note->to_list) ||
is_full_name("imms", note->to_list) ||
is_full_name("immortal", note->to_list) ||
is_full_name("god", note->to_list) ||
is_full_name("gods", note->to_list) ||
is_full_name("immortals", note->to_list) ||
is_full_name("admin", note->to_list) ||
is_full_name("admins", note->to_list)))
return true;
if ((get_trust(ch) == MAX_LEVEL) && (is_full_name("imp", note->to_list) ||
is_full_name("imps", note->to_list)
|| is_full_name("implementor",
note->to_list) ||
is_full_name("implementors",
note->to_list)))
return true;
if (is_full_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;
if (ch->clan && is_full_name(player_clan(ch),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;
}
int total_notes(CHAR_DATA * ch, BOARD_DATA * board)
{
NOTE_DATA *note;
int count = 0;
if (board->read_level > get_trust(ch))
return BOARD_NOACCESS;
for (note = board->note_first; note; note = note->next)
if (is_note_to(ch, note))
count++;
return count;
}
/*
* 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 (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.{x", ch, NULL, NULL, TO_ROOM);
info( ch, 0, "{G[INFO]:{x {R%s starts writing a note.{x", ch->name );
if (!IS_SET(ch->comm,COMM_AFK))
SET_BIT(ch->comm,COMM_AFK);
/* Begin writing the note ! */
printf_to_char(ch, "You are now %s a new note on the {W%s{x 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);
printf_to_char(ch, "{CFrom{x: %s\n\r\n\r", ch->name);
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{x\" will be chosen.\n\r",
ch->pcdata->board->names);
break;
case DEF_INCLUDE:
sprintf(buf,
"The recipient list MUST include \"{W%s{x\". 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{x\".",
ch->pcdata->board->names);
break;
}
send_to_char(buf, ch);
send_to_char("\n\r{CTo{x: ", 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 */
{
printf_to_char(ch, "{CTo{x: %s\n\r"
"{CExpires{x: %s\n\r"
"{CSubject{x: %s\n\r",
ch->pcdata->in_progress->to_list,
ctime(&ch->pcdata->in_progress->expire),
ch->pcdata->in_progress->subject);
send_to_char("{GYour note so far:{x\n\r", ch);
send_to_char(ch->pcdata->in_progress->text, ch);
send_to_char
("\n\rPress ENTER to begin writing the text of your note.\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)
{
int temp_board, new_board, old_board;
bool loop = true;
NOTE_DATA *p;
int count = 0, number;
time_t *last_note =
&ch->pcdata->last_note[board_number(ch->pcdata->board)];
temp_board = new_board = old_board = 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;
}
printf_to_char(ch, "{wNo new notes in {W%s{w.\n\r",
ch->pcdata->board->short_name);
if (next_board(ch))
{
new_board = board_number(ch->pcdata->board);
if ((new_board != old_board + 1) &&
((new_board != 0) || (old_board != get_max_board(ch))))
send_to_char("Skipping boards with no unread notes...\n\r",
ch);
while (loop)
{
if (temp_board != new_board)
{
if ((unread_notes(ch, &boards[temp_board]) !=
BOARD_NOACCESS) && (temp_board != old_board) &&
((new_board != old_board + 1) &&
((new_board != 0) ||
(old_board != get_max_board(ch)))))
printf_to_char(ch, "{b Skipping board: {B%s{x\n\r",
boards[temp_board].short_name);
if (temp_board == get_max_board(ch))
temp_board = 0;
else
temp_board++;
}
else
loop = false;
}
sprintf(buf, "Changed to board {W%s{x.\n\r",
ch->pcdata->board->short_name);
}
else
sprintf(buf, "There are no 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, p->sender) &&
get_trust(ch) < ch->pcdata->board->remove_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 */
}
static void do_nexpire(CHAR_DATA * ch, char *argument)
{
NOTE_DATA *p;
int days;
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
char archive_name[200];
argument = one_argument(argument, arg1);
argument = one_argument(argument, arg2);
if (IS_NPC(ch))
return;
if (!is_number(arg1))
{
send_to_char("Set new expiration on which note?\n\r", ch);
return;
}
p = find_note(ch, ch->pcdata->board, atoi(arg1));
if (!p)
{
send_to_char("No such note.\n\r", ch);
return;
}
if (is_number(arg2))
{
days = atoi(arg2);
if (days < -1)
{
send_to_char("Use positive numbers only!\n\r", ch);
return;
}
}
else
{
if (!strcmp(arg2, "never"))
days = -1;
else if (!strcmp(arg2, "now"))
days = 0;
else
{
send_to_char("Syntax: note expire <note#> <#days|never|now>\n\r",
ch);
return;
}
}
if (days == -1)
p->expire = 0;
else
{
FILE *fp_archive;
p->expire = current_time + (days * 60 * 60 * 24);
sprintf(archive_name, "%s/%s.old", NOTE_DIR,
boards[board_number(ch->pcdata->board)].short_name);
fp_archive = file_open(archive_name, "a");
append_note(fp_archive, p);
unlink_note(ch->pcdata->board, p);
free_note(p);
file_close(fp_archive);
}
save_board(ch->pcdata->board);
sprintf(buf,
"{wExpiration set to {C%s%s{w on {W%s's{w note, '{W%s{w'\n\r({W%s{w board, note {W%s{w).{x\n\r",
arg2, days < 1 ? "" : " days", p->sender, p->subject,
boards[board_number(ch->pcdata->board)].short_name, arg1);
send_to_char(buf, ch);
}
/* 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, 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++;
}
sprintf(buf, "{cNotes on {C%s {cboard:{x\n\r",
ch->pcdata->board->short_name);
send_to_char(buf, ch);
send_to_char("\n\r{WNum Author Subject{x\n\r"
"{b==== ============ ==========================================={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 (!show || ((count - show) < has_shown))
{
sprintf(buf, "{W%3d{c){R%c{x{C%-13s{x{c%s{x\n\r",
num,
last_note < p->date_stamp ? '*' : ' ',
p->sender, p->subject);
send_to_char(buf, ch);
}
}
}
}
/* 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, "expire")) && IS_IMMORTAL(ch))
do_nexpire(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
send_to_char("{wHuh? Type {Chelp note{w for usage.\n\r", ch);
}
void do_unread(CHAR_DATA * ch, char *argument)
{
int i, count, unread, total, maxtotal = 0;
if (IS_NPC(ch))
return;
count = 1;
send_to_char("{WNum Name Unread Total Description{x\n\r"
"{b============================================================={x\n\r",
ch);
if (get_total_unread(ch) == 0)
{
send_to_char("{WYou have no unread notes.\n\r{x", ch);
}
else
{
for (i = 0; i < MAX_BOARD; i++)
{
unread = unread_notes(ch, &boards[i]); /* how many unread notes? */
total = total_notes(ch, &boards[i]);
if (unread)
maxtotal += total;
if (unread && (unread != BOARD_NOACCESS) &&
(total != BOARD_NOACCESS))
{
printf_to_char(ch,
" {W%2d{x{c) {C%12s{b [%s%4d{b] [%s%4d{b] {c%s{x\n\r",
count, boards[i].short_name,
"{C", unread,
total ? "{C" : "{c", total,
boards[i].long_name);
count++;
} /* if has access */
} /* for each board */
}
send_to_char
("{b============================================================={x\n\r",
ch);
printf_to_char(ch, " {WTotals: {B[%s%4d{B] [%s%4d{B]{x\n\r",
(get_total_unread(ch) == 0) ? "{w" : "{W",
get_total_unread(ch), (!maxtotal) ? "{w" : "{W", maxtotal);
printf_to_char(ch, "\n\rYour current board is {W%s{x.\n\r",
ch->pcdata->board->short_name);
/* Inform of rights */
if (ch->pcdata->board->read_level > get_trust(ch))
send_to_char("You cannot read nor write notes on this board.\n\r",
ch);
else if (ch->pcdata->board->write_level > get_trust(ch))
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;
}
/* 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;
int total;
count = 1;
send_to_char("{WNum Name Unread Total Description{x\n\r"
"{b============================================================={x\n\r",
ch);
for (i = 0; i < MAX_BOARD; i++)
{
unread = unread_notes(ch, &boards[i]); /* how many unread notes? */
total = total_notes(ch, &boards[i]);
if ((unread != BOARD_NOACCESS) && (total != BOARD_NOACCESS))
{
printf_to_char(ch,
" {W%2d{x{c) {C%12s{b [%s%4d{b] [%s%4d{b] {c%s{x\n\r",
count, boards[i].short_name,
unread ? "{C" : "{c", unread,
total ? "{C" : "{c", total,
boards[i].long_name);
count++;
} /* if has access */
} /* for each board */
send_to_char
("{b============================================================={x\n\r",
ch);
printf_to_char(ch, " {WTotals: {B[%s%4d{B] [%s%4d{B]{x\n\r",
(get_total_unread(ch) == 0) ? "{w" : "{W",
get_total_unread(ch),
(get_total_notes(ch) == 0) ? "{w" : "{W",
get_total_notes(ch));
printf_to_char(ch, "\n\rYou current board is {W%s{x.\n\r",
ch->pcdata->board->short_name);
/* Inform of rights */
if (ch->pcdata->board->read_level > get_trust(ch))
send_to_char("You cannot read nor write notes on this board.\n\r",
ch);
else if (ch->pcdata->board->write_level > get_trust(ch))
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 {W%s{x. %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.\n\r", ch);
return;
}
/* Non-number given, find board with that name */
for (i = 0; i < MAX_BOARD; i++)
if (!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{x. %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)
{
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 = 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;
bool loop = true;
if (get_total_unread(ch) == 0)
return false;
while (loop)
{
if (i == get_max_board(ch) + 1)
i = 0;
if ((unread_notes(ch, &boards[i]) < 1)
|| (unread_notes(ch, &boards[i]) == BOARD_NOACCESS))
i++;
else
loop = false;
}
ch->pcdata->board = &boards[i];
return true;
}
int get_total_unread(CHAR_DATA * ch)
{
int i;
int tmp = 0;
int unread = 0;
for (i = 0; i < MAX_BOARD; i++)
{
if ((tmp = unread_notes(ch, &boards[i])) != BOARD_NOACCESS)
unread += tmp;
}
return unread;
}
int get_total_notes(CHAR_DATA * ch)
{
int i;
int tmp = 0;
int total = 0;
for (i = 0; i < MAX_BOARD; i++)
{
if ((tmp = total_notes(ch, &boards[i])) != BOARD_NOACCESS)
total += tmp;
}
return total;
}
int get_max_board(CHAR_DATA * ch)
{
int i;
int tmp = 0;
int max = 0;
for (i = 0; i < MAX_BOARD; i++)
{
if ((tmp = total_notes(ch, &boards[i])) != BOARD_NOACCESS)
max = i;
}
return max;
}
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;
}
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);
printf_to_desc(d, "Assumed default recipient: {W%s{x\n\r",
ch->pcdata->board->names);
}
else
ch->pcdata->in_progress->to_list = str_dup(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 = str_dup(buf);
printf_to_desc(d,
"\n\rYou did not specify %s as recipient, so it was automatically added.\n\r"
"{CNew To{x : %s\n\r",
ch->pcdata->board->names,
ch->pcdata->in_progress->to_list);
}
else
ch->pcdata->in_progress->to_list = str_dup(buf);
break;
case DEF_EXCLUDE: /* forced exclude */
if (!buf[0])
{
send_to_desc("You must specify a recipient.\n\r"
"{CTo{x: ", d);
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"
"{CTo{x: ", ch->pcdata->board->names);
send_to_desc(buf, d);
return; /* return from nanny, not changing to the next state! */
}
else
ch->pcdata->in_progress->to_list = str_dup(buf);
break;
}
send_to_desc("{C\n\rSubject{x: ", d);
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;
}
strcpy(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);
printf_to_desc(d, "{CSubject{x: ");
}
else if (strlen(buf) > 60)
{
write_to_buffer(d,
"No, no. This is just the Subject. You're note 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 */
{
printf_to_desc(d,
"\n\rHow many days do you want this note to expire in?\n\r"
"Press Enter for default value for this board, {W%d{x days.\n\r"
"{CExpire{x: ", ch->pcdata->board->purge_days);
d->connected = CON_NOTE_EXPIRE;
}
else
{
ch->pcdata->in_progress->expire =
current_time + ch->pcdata->board->purge_days * 24L * 3600L;
printf_to_desc(d, "This note will expire %s\r",
ctime(&ch->pcdata->in_progress->expire));
send_to_desc
("\n\rPress ENTER to begin writing the text of your note.\n\r",
d);
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 */
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_desc("{CExpire{x: ", d);
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_desc("{CExpire{x: ", d);
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_desc
("\n\rPress ENTER to begin writing the text of your note.\n\r", d);
d->connected = CON_NOTE_TEXT;
}
void handle_con_note_text(DESCRIPTOR_DATA * d, char *argument)
{
CHAR_DATA *ch = d->character;
if (!ch->pcdata->in_progress)
{
d->connected = CON_PLAYING;
bug("nanny: In CON_NOTE_TEXT, but no note in progress", 0);
return;
}
string_append(ch, &ch->pcdata->in_progress->text);
d->connected = CON_NOTE_FINISH;
}
void handle_con_note_finish(DESCRIPTOR_DATA * d, char *argument)
{
CHAR_DATA *ch = d->character;
CHAR_DATA *wch;
char buf[MSL];
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 */
write_to_buffer(d, "Continuing note...\n\r", 0);
d->connected = CON_NOTE_TEXT;
break;
case 'v': /* view note so far */
if (ch->pcdata->in_progress->text)
{
send_to_desc("{cText of your note so far:{x\n\r", d);
write_to_buffer(d, ch->pcdata->in_progress->text, 0);
}
else
write_to_buffer(d, "You haven't written a thing!\n\r\n\r", 0);
printf_to_desc(d, "%s", szFinishPrompt);
write_to_buffer(d, "\n\r", 0);
break;
case 'p': /* post note */
finish_note(ch->pcdata->board, ch->pcdata->in_progress);
write_to_buffer(d, "Note posted.\n\r", 0);
d->connected = CON_PLAYING;
info( ch, 0, "{G[INFO]:{x {R%s has finished writing their note!{x", ch->name );
if (IS_SET(ch->comm,COMM_AFK))
REMOVE_BIT(ch->comm,COMM_AFK);
for (d = descriptor_list; d; d = d->next)
{
wch = d->original ? d->original : d->character;
if (d->connected != CON_PLAYING)
continue;
if (is_note_to(wch, ch->pcdata->in_progress) &&
str_cmp(wch->name, ch->pcdata->in_progress->sender) &&
get_trust(wch) >= ch->pcdata->board->read_level)
{
sprintf(buf,
"{wYou receive a new note on the {W%s{w board.{x\n\r",
ch->pcdata->board->short_name);
send_to_char(buf, wch);
sound( "ding.wav", wch );
}
}
/* remove AFK status */
ch->pcdata->in_progress = NULL;
act("{G$n finishes $s note.{x", 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;
d->connected = CON_PLAYING;
/* remove afk status */
if (IS_SET(ch->comm,COMM_AFK))
REMOVE_BIT(ch->comm,COMM_AFK);
break;
default: /* invalid response */
write_to_buffer(d, "Huh? Valid answers are:\n\r\n\r", 0);
printf_to_desc(d, "%s", szFinishPrompt);
write_to_buffer(d, "\n\r", 0);
}
}