I have tried and tried to get this to work, and I can't figure it out. I've managed to be able to make it so when a player starts writing a note, it opens the append mode, and the user can actually edit everything just like they should be able to. However, when they use the @ to end the append mode, it doesn't switch to "POST", and it doesn't output the text to the note itself. The only thing it actually outputs is the commands typed while in the editor (such as .s, .lr, etc.) So… Can someone please help me? I just don't know where to put the damn thing, or how to get it to work right. And I don't have a lot of time (I currently need to get ready for work). I have taken all that append stuff out, and am using Erwin's board system. I believe it's version 2. Here's the board.c file. Can't attach things on posts (which would be nice…), so sorry for the long-ass post.
/*************************************************************************** * 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. * ***************************************************************************/
Basically, the notes are split up into several boards. The boards do not exist physically, they can be read anywhere and in any position.
Each of the note boards has its own file. Each of the boards can have its own "rights": who can read/write.
Each character has an extra field added, namele the timestamp of the last note read by him/her on a certain board.
The note entering system is changed too, making it more interactive. When entering a note, a character is put AFK and into a special CON_ state. Everything typed goes into the note.
For the immortals it is possible to purge notes based on age. An Archive options is available which moves the notes older than X days into a special board. The file of this board should then be moved into some other directory during e.g. the startup script and perhaps renamed depending on date.
Note that write_level MUST be >= read_level or else there will be strange output in certain functions.
Board DEFAULT_BOARD must be at least readable by *everyone*.
*/
#define L_SUP (MAX_LEVEL - 1) /* if not already defined */ #define L_IMP (MAX_LEVEL - 7) #define L_QIMM (MAX_LEVEL - 12)
/* The prompt that the character is given after finishing a note with ~ or END */ const char * szFinishPrompt = "({WC{x)ontinue, ({WV{x)iew, ({WP{x)ost or ({WF{x)orget it?";
long last_note_stamp = 0; /* To generate unique timestamps on notes */
/* 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;
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 (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 */ }
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_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;
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;
if (IS_NPC(ch)) /* NPC cannot post notes */ return; /* level 2 and validation check -Koqlb */ if ((ch->level < 2) || !IS_VALIDATED(ch)) { send_to_char("You must be at least level {C2{x and {Wvalidated{x\n\r",ch); send_to_char("Before you can write notes.{x\n\r",ch); 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';
act ("{W$n starts writing a {Bnote{x.{x", ch, NULL, NULL, TO_ROOM);
/* Begin writing the note ! */ printf_to_char (ch, "{xYou are now %s a new note on the {W%s{x board.\n\r" "{xIf 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);
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);
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, "{rTo{W: %s\n\r" "{GExpires{x: {W%s{x\n\r" "{BSubject{x:{W %s{x\n\r", ch->pcdata->in_progress->to_list, ctime(&ch->pcdata->in_progress->expire), ch->pcdata->in_progress->subject); send_to_char ("{CYour note so far:{x\n\r", ch); send_to_char (ch->pcdata->in_progress->text,ch);
send_to_char ("\n\rEnter text. Type {W~{x or {WEND{x on an empty line to end note.\n\r" "{W======================================================={x\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 (is_number (argument)) { number = atoi(argument);
for (p = ch->pcdata->board->note_first; p; p = p->next) if (++count == number) break;
if (!p || !is_note_to(ch, p)) send_to_char ("No such note.\n\r",ch); else { show_note_to_char (ch,p,count); *last_note = UMAX (*last_note, p->date_stamp); } } else /* just next one */ { char buf[200];
count = 1; for (p = ch->pcdata->board->note_first; p ; p = p->next, count++) if ((p->date_stamp > *last_note) && is_note_to(ch,p)) { show_note_to_char (ch,p,count); /* Advance if new note is newer than the currently newest for that char */ *last_note = UMAX (*last_note, p->date_stamp); return; }
send_to_char ("No new notes in this board.\n\r",ch);
if (next_board (ch)) sprintf (buf, "Changed to next board, %s.\n\r", ch->pcdata->board->short_name); else sprintf (buf, "There are no more boards.\n\r");
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, num = 0, has_shown = 0; time_t last_note; NOTE_DATA *p; char buf[MAX_STRING_LENGTH];
if (is_number(argument)) /* first, count the number of notes */ { show = atoi(argument);
for (p = ch->pcdata->board->note_first; p; p = p->next) if (is_note_to(ch,p)) count++; }
send_to_char ("{WNotes on this board:{x\n\r" "{W(Num) {YAuthor {WSubject{x\n\r",ch);
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, "{D({W%3d{D){B%c{x{Y%-13s{x%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 mesages 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);
/* 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 ("{bS{Bu{cb{Cv{Wer{Cs{ci{Bv{be {RV{ri{Rs{Wi{Ro{rn{Rs{Y Message Board{x\n\r",ch); send_to_char ("{WNum {YName {RUnread {WDescription{x\n\r",ch); for (i = 0; i < MAX_BOARD; i++) { unread = unread_notes (ch,&boards[i]); /* how many unread notes? */ if (unread != BOARD_NOACCESS) { printf_to_char (ch, "[{W%2d{x] {Y%9s{x[{R%s%3d{x] {W%s{x\n\r", count, boards[i].short_name, unread ? "{R" : "{w", unread, boards[i].long_name); count++; } /* if has access */
} /* for each board */
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) || !IS_VALIDATED(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 {G%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_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 {G%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); }
/* convert to ascii. ctime returns a string which last character is \n, so remove that */ strtime = ctime (¤t_time); strtime[strlen(strtime)-1] = '\0';
note->date = str_dup (strtime);
finish_note (board, note);
}
/* tries to change to the next accessible board */ static bool next_board (CHAR_DATA *ch) { int i = board_number (ch->pcdata->board) + 1;
while ((i < MAX_BOARD) && (unread_notes(ch,&boards[i]) == BOARD_NOACCESS)) i++;
if (i == MAX_BOARD) return FALSE; else { ch->pcdata->board = &boards[i]; return TRUE; } }
printf_to_desc (d, "\n\rYou did not specify %s as recipient, so it was automatically added.\n\r" "{YNew 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" "{YTo{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" "{YTo{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;
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, "{YSubject{x: "); } else if (strlen_color(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" "{YExpire{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\rEnter text. Type {W~{x or {WEND{x on an empty line to end note.\n\r" "=======================================================\n\r",d); d->connected = CON_NOTE_TEXT; } } }
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 ("{YExpire{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 ("{YExpire{x: ",d); return; } }
/* note that ctime returns XXX\n so we only need to add an \r */
send_to_desc ("\n\rEnter text. Type {W~{x or {WEND{x on an empty line to end note.\n\r" "{W=======================================================\n\r{x",d);
/* Check for too long lines. Do not allow lines longer than 100 chars */
/* Hey, why #define MAX_LINE_LENGTH, then put a hardcoded value in here? ;-) * – JR 09/24/00 */
if (strlen_color (buf) > MAX_LINE_LENGTH) { printf_to_desc (d, "Too long line rejected. Do NOT go over %d characters!\n\r", MAX_LINE_LENGTH ); return; }
/* Not end of note. Copy current text into temp buffer, add new line, and copy back */
/* How would the system react to strcpy( , NULL) ? */ if (ch->pcdata->in_progress->text) { strcpy (letter, ch->pcdata->in_progress->text); free_string (ch->pcdata->in_progress->text); ch->pcdata->in_progress->text = NULL; /* be sure we don't free it twice */ } else strcpy (letter, "");
/* Check for overflow */
if ((strlen_color(letter) + strlen_color (buf)) > MAX_NOTE_TEXT) { /* Note too long, take appropriate steps */ write_to_buffer (d, "Note too long!\n\r", 0); 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); strcat (letter, "\r\n"); /* new line. \r first to make note files better readable */
case 'v': /* view note so far */ if (ch->pcdata->in_progress->text) { send_to_desc ("{GText 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;
I've managed to be able to make it so when a player starts writing a note, it opens the append mode,
and the user can actually edit everything just like they should be able to. However,
when they use the @ to end the append mode, it doesn't switch to "POST", and it doesn't output
the text to the note itself. The only thing it actually outputs is the commands typed while in the
editor (such as .s, .lr, etc.)
So… Can someone please help me? I just don't know where to put the damn thing, or how to get
it to work right. And I don't have a lot of time (I currently need to get ready for work).
I have taken all that append stuff out, and am using Erwin's board system. I believe it's version 2. Here's the board.c
file. Can't attach things on posts (which would be nice…), so sorry for the long-ass post.
Thanks!
Koqlb of Subversive Visions.
http://subversive.themudhost.net
subversive.themudhost.net port 7500