ssss/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe. *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "Smaugx.h"
#include "SysData.h"
#include "objects.h"
#include "rooms.h"
#include "boards.h"
#include "SmaugWizDoc.h"
#include "SmaugFiles.h"
#include "descriptor.h"
#include "character.h"
/* Defines for voting on notes. -- Narn */
#define VOTE_NONE 0
#define VOTE_OPEN 1
#define VOTE_CLOSED 2
BOOL is_note_to (CCharacter *ch, CNoteData *pnote);
void note_attach (CCharacter *ch);
void do_note (CCharacter *ch, char *arg_passed, BOOL IS_MAIL);
CNoteData *read_note (const char *notefile, FILE *fp);
CBoardData::~CBoardData ()
{
delete note_file;
delete read_group;
delete post_group;
delete extra_readers;
delete extra_removers;
while (! m_NList.IsEmpty ()) {
delete (CNoteData*) m_NList.RemoveTail ();
}
m_NList.RemoveAll ();
}
void CBoardList::RemoveAll ()
{
while (!IsEmpty ()) {
delete (CBoardData*) RemoveTail ();
}
CPtrList::RemoveAll ();
}
BOOL can_remove (CCharacter *ch, CBoardData *board)
{
/* If your trust is high enough, you can remove it. */
if (ch->GetTrustLevel () >= board->min_remove_level)
return TRUE;
if (board->extra_removers[0] != '\0')
{
if (is_name (ch->GetName (), board->extra_removers))
return TRUE;
}
return FALSE;
}
BOOL can_read (CCharacter *ch, CBoardData *board)
{
/* If your trust is high enough, you can read it. */
if (ch->GetTrustLevel () >= board->min_read_level)
return TRUE;
/* Your trust wasn't high enough, so check if a read_group or extra
readers have been set up. */
if (board->read_group[0] != '\0')
{
if (ch->GetPcData ()->GetClan ()
&& !str_cmp (ch->GetPcData ()->GetClan ()->GetName (), board->read_group))
return TRUE;
if (ch->GetPcData ()->council && !str_cmp (ch->GetPcData ()->council->name, board->read_group))
return TRUE;
}
if (board->extra_readers[0] != '\0')
{
if (is_name (ch->GetName (), board->extra_readers))
return TRUE;
}
return FALSE;
}
BOOL can_post (CCharacter *ch, CBoardData *board)
{
/* If your trust is high enough, you can post. */
if (ch->GetTrustLevel () >= board->min_post_level)
return TRUE;
/* Your trust wasn't high enough, so check if a post_group has been set up. */
if (board->post_group[0] != '\0')
{
if (ch->GetPcData ()->GetClan ()
&& !str_cmp (ch->GetPcData ()->GetClan ()->GetName (), board->post_group))
return TRUE;
if (ch->GetPcData ()->council && !str_cmp (ch->GetPcData ()->council->name, board->post_group))
return TRUE;
}
return FALSE;
}
// board commands.
void CBoardList::WriteBoardsFile ()
{
FILE *fp;
fp = fopen (FileTable.GetName (SM_BOARD_FILE), "w");
if (! fp) {
bug ("FATAL: cannot open board.txt for writing!\n\r");
return;
}
POSITION pos = GetHeadPosition ();
while (pos) {
CBoardData &Bd = *(CBoardData*) GetNext (pos);
fprintf (fp, "Filename %s~\n", Bd.note_file);
fprintf (fp, "Vnum %d\n", Bd.board_obj);
fprintf (fp, "Min_read_level %d\n", Bd.min_read_level);
fprintf (fp, "Min_post_level %d\n", Bd.min_post_level);
fprintf (fp, "Min_remove_level %d\n", Bd.min_remove_level);
fprintf (fp, "Max_posts %d\n", Bd.max_posts);
fprintf (fp, "Type %d\n", Bd.type);
fprintf (fp, "Read_group %s~\n", Bd.read_group);
fprintf (fp, "Post_group %s~\n", Bd.post_group);
fprintf (fp, "Extra_readers %s~\n", Bd.extra_readers);
fprintf (fp, "Extra_removers %s~\n", Bd.extra_removers);
fprintf (fp, "End\n");
}
fclose (fp);
}
CBoardData *CBoardList::GetBoard (CObjData *obj)
{
CBoardData *board;
POSITION pos = GetHeadPosition ();
while (pos) {
board = (CBoardData*) GetNext (pos);
if (board->board_obj == obj->pIndexData->vnum)
return board;
}
return NULL;
}
CBoardData *find_board (CCharacter *ch)
{
CObjData *obj;
CBoardData *board;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (obj = ch->GetInRoom ()->GetNextContent (pos)) {
if ((board = BoardList.GetBoard (obj)) != NULL)
return board;
}
return NULL;
}
BOOL is_note_to (CCharacter *ch, CNoteData *pnote)
{
if (!str_cmp (ch->GetName (), pnote->sender))
return TRUE;
if (is_name ("all", pnote->to_list))
return TRUE;
if (ch->IsHero () && is_name ("immortal", pnote->to_list))
return TRUE;
if (is_name (ch->GetName (), pnote->to_list))
return TRUE;
return FALSE;
}
void note_attach (CCharacter *ch)
{
CNoteData *pnote;
if (ch->GetNotes ())
return;
pnote = new CNoteData;
pnote->SetNext (NULL);
pnote->SetPrev (NULL);
pnote->sender = QUICKLINK (ch->GetName ());
pnote->date = STRALLOC ("");
pnote->to_list = STRALLOC ("");
pnote->subject = STRALLOC ("");
pnote->text = STRALLOC ("");
ch->SetNotes (pnote);
}
void CBoardData::WriteAllNotes ()
{
FILE *fp;
// Rewrite entire note list.
fclose (fpReserve);
CString nfn = FileTable.MakeName (SD_BOARD_DIR, note_file);
if ((fp = fopen (nfn, "w")) == NULL) {
perror (nfn);
} else {
POSITION pos = m_NList.GetHeadPosition ();
while (pos) {
CNoteData &Nd = *(CNoteData*) m_NList.GetNext (pos);
fprintf (fp, "Sender %s~\nDate %s~\nTo %s~\nSubject "
"%s~\nVoting %d\nYesvotes %s~\nNovotes %s~\nAbstentions "
"%s~\nText\n%s~\n\n",
Nd.sender, Nd.date, Nd.to_list, Nd.subject, Nd.voting,
Nd.yesvotes, Nd.novotes, Nd.abstentions,
strip_cr (Nd.text));
}
fclose (fp);
}
fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "r");
}
void CBoardData::RemoveNote (CNoteData *pnote)
{
ASSERT (this);
POSITION pos = m_NList.Find (pnote);
if (! pos) {
bug ("CBoardData::RemoveNote: pnote not in list");
return;
}
m_NList.RemoveAt (pos); // Remove note from linked list.
// for now STRFREE's are not done in destructors...
STRFREE (pnote->text);
STRFREE (pnote->subject);
STRFREE (pnote->to_list);
STRFREE (pnote->date);
STRFREE (pnote->sender);
delete pnote;
--num_posts;
WriteAllNotes (); // update the note file
}
CObjData *find_quill (CCharacter *ch)
{
CObjData *quill;
POSITION pos = ch->GetHeadCarryPos ();
while (quill = ch->GetNextCarrying (pos))
if (quill->item_type == ITEM_PEN && can_see_obj (ch, *quill))
return quill;
return NULL;
}
void do_noteroom (CCharacter *ch, char *argument)
{
CBoardData *board;
char arg[MAX_STRING_LENGTH];
char arg_passed[MAX_STRING_LENGTH];
strcpy (arg_passed, argument);
switch (ch->GetSubstate ())
{
case SUB_WRITING_NOTE:
do_note (ch, arg_passed, FALSE);
break;
default:
argument = one_argument (argument, arg);
smash_tilde (argument);
if (!str_cmp (arg, "write") || !str_cmp (arg, "to")
|| !str_cmp (arg, "subject") || !str_cmp (arg, "show"))
{
do_note (ch, arg_passed, FALSE);
return;
}
board = find_board (ch);
if (!board)
{
ch->SendText ("There is no bulletin board here to look at.\n\r");
return;
}
if (board->type != BOARD_NOTE)
{
ch->SendText ("You can only use note commands on a note board.\n\r");
return;
}
else
{
do_note (ch, arg_passed, FALSE);
return;
}
}
}
void do_mailroom (CCharacter *ch, char *argument)
{
CBoardData *board;
char arg[MAX_STRING_LENGTH];
char arg_passed[MAX_STRING_LENGTH];
strcpy (arg_passed, argument);
switch (ch->GetSubstate ())
{
case SUB_WRITING_NOTE:
do_note (ch, arg_passed, TRUE);
break;
default:
argument = one_argument (argument, arg);
smash_tilde (argument);
if (!str_cmp (arg, "write") || !str_cmp (arg, "to")
|| !str_cmp (arg, "subject") || !str_cmp (arg, "show"))
{
do_note (ch, arg_passed, TRUE);
return;
}
board = find_board (ch);
if (!board)
{
ch->SendText ("There is no mail facility here.\n\r");
return;
}
if (board->type != BOARD_MAIL)
{
ch->SendText ("You can only use mail commands in a post office.\n\r");
return;
}
else
{
do_note (ch, arg_passed, TRUE);
return;
}
}
}
void do_note (CCharacter *ch, char *arg_passed, BOOL IS_MAIL)
{
char buf [MAX_STRING_LENGTH];
char arg [MAX_INPUT_LENGTH];
CNoteData *pnote;
CBoardData *board;
int vnum;
int anum;
int first_list;
CObjData *quill, *paper, *tmpobj = NULL;
char notebuf[MAX_STRING_LENGTH];
char short_desc_buf[MAX_STRING_LENGTH];
char long_desc_buf[MAX_STRING_LENGTH];
char keyword_buf[MAX_STRING_LENGTH];
BOOL mfound = FALSE;
CExtraDescrData *ed = NULL;
if (ch->IsNpc ())
return;
if (!ch->GetDesc ()) {
bug ("do_note: no descriptor", 0);
return;
}
switch (ch->GetSubstate ()) {
default:
break;
case SUB_WRITING_NOTE:
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER) {
bug ("do_note: player not holding paper");
ch->StopEditing ();
return;
}
ed = (CExtraDescrData*) ch->dest_buf;
STRFREE (ed->description);
ed->description = ch->GetEditBuffer ();
ch->StopEditing ();
return;
break;
}
set_char_color (AT_NOTE, ch);
arg_passed = one_argument (arg_passed, arg);
smash_tilde (arg_passed);
if (!str_cmp (arg, "list")) {
board = find_board (ch);
if (!board) {
ch->SendText ("There is no board here to look at.\n\r");
return;
}
if (! can_read (ch, board)) {
ch->SendText ("You cannot make any sense of the cryptic "
"scrawl on this board...\n\r");
return;
}
first_list = atoi (arg_passed);
if (first_list) {
if (IS_MAIL) {
ch->SendText ("You cannot use a list number (at this time) "
"with mail.\n\r");
return;
}
if (first_list < 1) {
ch->SendText ("You can't read a note before 1!\n\r");
return;
}
}
if (!IS_MAIL) {
int nNote = 0;
set_pager_color (AT_NOTE, ch);
POSITION pos = board->GetFirstNotePosition ();
while (pos) {
CNoteData &Nd = *board->GetNextNote (pos);
++nNote;
if ((first_list && nNote >= first_list) || !first_list)
pager_printf (ch, "%2d%c %-12s%c %-12s %s\n\r", nNote,
is_note_to (ch, &Nd) ? ')' : '}',
Nd.sender, (Nd.voting != VOTE_NONE)
? (Nd.voting == VOTE_OPEN ? 'V' : 'C') : ':',
Nd.to_list, Nd.subject);
}
act (AT_ACTION, "$n glances over the notes.", ch, NULL, NULL,
TO_ROOM);
return;
} else {
if (IS_MAIL) { // SB Mail check for Brit
POSITION pos = board->GetFirstNotePosition ();
while (pos) {
pnote = board->GetNextNote (pos);
if (is_note_to (ch, pnote)) {
mfound = TRUE;
break;
}
}
if (!mfound && ch->GetTrustLevel() < SysData.ReadAllMailLev) {
ch->SendTextf ("You have no mail.\n\r");
return;
}
}
POSITION pos = board->GetFirstNotePosition ();
int nNote = 0;
while (pos) {
pnote = board->GetNextNote (pos);
if (is_note_to (ch, pnote)
|| ch->GetTrustLevel () > SysData.ReadAllMailLev)
ch->SendTextf ("%2d%c %s: %s\n\r", ++nNote,
is_note_to (ch, pnote) ? '-' : '}',
pnote->sender, pnote->subject);
}
return;
}
}
if (!str_cmp (arg, "read")) {
BOOL fAll;
board = find_board (ch);
if (!board) {
ch->SendText ("There is no board here to look at.\n\r");
return;
}
if (!can_read (ch, board)) {
ch->SendText ("You cannot make any sense of the cryptic scrawl "
"on this board...\n\r");
return;
}
if (!str_cmp (arg_passed, "all")) {
fAll = TRUE;
anum = 0;
}
else if (is_number (arg_passed)) {
fAll = FALSE;
anum = atoi (arg_passed);
} else {
ch->SendText ("Note read which number?\n\r");
return;
}
set_pager_color (AT_NOTE, ch);
if (!IS_MAIL) {
POSITION pos = board->GetFirstNotePosition ();
int nNote = 0;
while (pos) {
pnote = board->GetNextNote (pos);
++nNote;
if (nNote == anum || fAll) {
pager_printf (ch, "[%3d] %s: %s\n\r%s\n\rTo: %s\n\r%s",
nNote, pnote->sender, pnote->subject, pnote->date,
pnote->to_list, pnote->text);
if (pnote->yesvotes[0] != '\0'
|| pnote->novotes[0] != '\0'
|| pnote->abstentions[0] != '\0') {
send_to_pager ("---------------------------------"
"---------------------------\n\r", ch);
pager_printf (ch, "Votes:\n\rYes: %s\n\rNo: "
" %s\n\rAbstain: %s\n\r",
pnote->yesvotes, pnote->novotes,
pnote->abstentions);
}
act (AT_ACTION, "$n reads a note.", ch, NULL, NULL,
TO_ROOM);
return;
}
}
ch->SendText ("No such note.\n\r");
return;
} else {
vnum = 0;
POSITION pos = board->GetFirstNotePosition ();
int nNote = 0;
while (pos) {
pnote = board->GetNextNote (pos);
if (is_note_to (ch, pnote)
|| ch->GetTrustLevel () > SysData.ReadAllMailLev) {
++nNote;
if (nNote == anum || fAll) {
if (ch->GetGold () < 10
&& ch->GetTrustLevel () < SysData.ReadMailFreeLev) {
ch->SendText ("It costs 10 gold coins to read a "
"message.\n\r");
return;
}
if (ch->GetTrustLevel () < SysData.ReadMailFreeLev)
ch->AddGold (-10);
pager_printf (ch,
"[%3d] %s: %s\n\r%s\n\rTo: %s\n\r%s", nNote,
pnote->sender, pnote->subject, pnote->date,
pnote->to_list, pnote->text);
return;
}
}
}
ch->SendText ("No such message.\n\r");
return;
}
}
// Voting added by Narn, June '96
if (!str_cmp (arg, "vote")) {
char arg2 [MAX_INPUT_LENGTH];
arg_passed = one_argument (arg_passed, arg2);
board = find_board (ch);
if (!board) {
ch->SendText ("There is no bulletin board here.\n\r");
return;
}
if (!can_read (ch, board)) {
ch->SendText ("You cannot vote on this board.\n\r");
return;
}
if (is_number (arg2))
anum = atoi (arg2);
else {
ch->SendText ("Note vote which number?\n\r");
return;
}
pnote = board->GetNoteByNumber (anum-1);
if (!pnote) {
ch->SendText ("No such note.\n\r");
return;
}
// Options: open close yes no abstain
// If you're the author of the note and can read the board you can
// open and close voting.
// If you can read it and voting is open you can vote.
if (!str_cmp (arg_passed, "open")) {
if (str_cmp (ch->GetName (), pnote->sender)) {
ch->SendText ("You are not the author of this note.\n\r");
return;
}
pnote->voting = VOTE_OPEN;
act (AT_ACTION, "$n opens voting on a note.", ch, NULL,
NULL, TO_ROOM);
ch->SendText ("Voting opened.\n\r");
board->WriteAllNotes ();
return;
}
if (!str_cmp (arg_passed, "close")) {
if (str_cmp (ch->GetName (), pnote->sender)) {
ch->SendText ("You are not the author of this note.\n\r");
return;
}
pnote->voting = VOTE_CLOSED;
act (AT_ACTION, "$n closes voting on a note.", ch, NULL,
NULL, TO_ROOM);
ch->SendText ("Voting closed.\n\r");
board->WriteAllNotes ();
return;
}
// Make sure the note is open for voting before going on.
if (pnote->voting != VOTE_OPEN) {
ch->SendText ("Voting is not open on this note.\n\r");
return;
}
// Can only vote once on a note.
sprintf (buf, "%s %s %s",
pnote->yesvotes, pnote->novotes, pnote->abstentions);
if (is_name (ch->GetName (), buf)) {
ch->SendText ("You have already voted on this note.\n\r");
return;
}
if (!str_cmp (arg_passed, "yes")) {
sprintf (buf, "%s %s", pnote->yesvotes, ch->GetName ());
delete pnote->yesvotes;
pnote->yesvotes = str_dup (buf);
act (AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_ROOM);
ch->SendText ("Ok.\n\r");
board->WriteAllNotes ();
return;
}
if (!str_cmp (arg_passed, "no")) {
sprintf (buf, "%s %s", pnote->novotes, ch->GetName ());
delete pnote->novotes;
pnote->novotes = str_dup (buf);
act (AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_ROOM);
ch->SendText ("Ok.\n\r");
board->WriteAllNotes ();
return;
}
if (!str_cmp (arg_passed, "abstain")) {
sprintf (buf, "%s %s", pnote->abstentions, ch->GetName ());
delete pnote->abstentions;
pnote->abstentions = str_dup (buf);
act (AT_ACTION, "$n votes on a note.", ch, NULL, NULL, TO_ROOM);
ch->SendText ("Ok.\n\r");
board->WriteAllNotes ();
return;
}
do_note (ch, "", FALSE);
}
if (!str_cmp (arg, "write")) {
if (ch->GetSubstate () == SUB_RESTRICTED)
{
ch->SendText ("You cannot write a note from within another command.\n\r");
return;
}
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev)
{
quill = find_quill (ch);
if (!quill)
{
ch->SendText ("You need a quill to write a note.\n\r");
return;
}
if (quill->value[0] < 1)
{
ch->SendText ("Your quill is dry.\n\r");
return;
}
}
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER)
{
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev)
{
ch->SendText ("You need to be holding a fresh piece of parchment to write a note.\n\r");
return;
}
paper = create_object (OIdxTable.GetObj (OBJ_VNUM_NOTE), 0);
if ((tmpobj = get_eq_char (ch, WEAR_HOLD)) != NULL)
unequip_char (ch, tmpobj);
paper = obj_to_char (paper, ch);
equip_char (ch, paper, WEAR_HOLD);
act (AT_MAGIC, "A piece of parchment magically appears in $n's hands!",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "A piece of parchment appears in your hands.",
ch, NULL, NULL, TO_CHAR);
}
if (paper->value[0] < 2)
{
paper->value[0] = 1;
ed = SetOExtra (paper, "_text_");
ch->SetSubstate (SUB_WRITING_NOTE);
ch->dest_buf = ed;
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev)
--quill->value[0];
start_editing (ch, ed->description);
return;
}
else
{
ch->SendText ("You cannot modify this note.\n\r");
return;
}
}
if (!str_cmp (arg, "subject")) {
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev)
{
quill = find_quill (ch);
if (!quill)
{
ch->SendText ("You need a quill to write a note.\n\r");
return;
}
if (quill->value[0] < 1)
{
ch->SendText ("Your quill is dry.\n\r");
return;
}
}
if (!arg_passed || arg_passed[0] == '\0')
{
ch->SendText ("What do you wish the subject to be?\n\r");
return;
}
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER)
{
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev)
{
ch->SendText ("You need to be holding a fresh piece of parchment to write a note.\n\r");
return;
}
paper = create_object (OIdxTable.GetObj (OBJ_VNUM_NOTE), 0);
if ((tmpobj = get_eq_char (ch, WEAR_HOLD)) != NULL)
unequip_char (ch, tmpobj);
paper = obj_to_char (paper, ch);
equip_char (ch, paper, WEAR_HOLD);
act (AT_MAGIC, "A piece of parchment magically appears in $n's hands!",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "A piece of parchment appears in your hands.",
ch, NULL, NULL, TO_CHAR);
}
if (paper->value[1] > 1)
{
ch->SendText ("You cannot modify this note.\n\r");
return;
}
else
{
paper->value[1] = 1;
ed = SetOExtra (paper, "_subject_");
STRFREE (ed->description);
ed->description = STRALLOC (arg_passed);
ch->SendText ("Ok.\n\r");
return;
}
}
if (!str_cmp (arg, "to")) {
struct stat fst;
char fname [1024];
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev) {
quill = find_quill (ch);
if (!quill) {
ch->SendText ("You need a quill to write a note.\n\r");
return;
}
if (quill->value[0] < 1) {
ch->SendText ("Your quill is dry.\n\r");
return;
}
}
if (!arg_passed || arg_passed[0] == '\0') {
ch->SendText ("Please specify an addressee.\n\r");
return;
}
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER) {
if (ch->GetTrustLevel () < SysData.WriteMailFreeLev) {
ch->SendText ("You need to be holding a fresh piece of "
"parchment to write a note.\n\r");
return;
}
paper = create_object (OIdxTable.GetObj (OBJ_VNUM_NOTE), 0);
if ((tmpobj = get_eq_char (ch, WEAR_HOLD)) != NULL)
unequip_char (ch, tmpobj);
paper = obj_to_char (paper, ch);
equip_char (ch, paper, WEAR_HOLD);
act (AT_MAGIC, "A piece of parchment magically appears in $n's hands!",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "A piece of parchment appears in your hands.",
ch, NULL, NULL, TO_CHAR);
}
if (paper->value[2] > 1) {
ch->SendText ("You cannot modify this note.\n\r");
return;
}
arg_passed [0] = UPPER (arg_passed [0]);
strcpy (fname, FileTable.PlayerName (capitalize (arg_passed)));
if (!IS_MAIL || stat (fname, &fst) != -1
|| !str_cmp (arg_passed, "all")) {
paper->value [2] = 1;
ed = SetOExtra (paper, "_to_");
STRFREE (ed->description);
ed->description = STRALLOC (arg_passed);
ch->SendText ("Ok.\n\r");
return;
} else {
ch->SendText ("No player exists by that name.\n\r");
return;
}
}
if (!str_cmp (arg, "show")) {
char *subject, *to_list, *text;
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER) {
ch->SendText ("You are not holding a note.\n\r");
return;
}
if ((subject = GetExtraDescr ("_subject_", paper->ExDesList)) == NULL)
subject = "(no subject)";
if ((to_list = GetExtraDescr ("_to_", paper->ExDesList)) == NULL)
to_list = "(nobody)";
sprintf (buf, "%s: %s\n\rTo: %s\n\r", ch->GetName (), subject,
to_list);
ch->SendText (buf);
if ((text = GetExtraDescr ("_text_", paper->ExDesList)) == NULL)
text = "The note is blank.\n\r";
ch->SendText (text);
return;
}
if (!str_cmp (arg, "post")) {
if ((paper = get_eq_char (ch, WEAR_HOLD)) == NULL
|| paper->item_type != ITEM_PAPER) {
ch->SendText ("You are not holding a note.\n\r");
return;
}
if (paper->value[0] == 0) {
ch->SendText ("There is nothing written on this note.\n\r");
return;
}
if (paper->value[1] == 0) {
ch->SendText ("This note has no subject.\n\r");
return;
}
if (paper->value[2] == 0) {
ch->SendText ("This note is addressed to no one!\n\r");
return;
}
board = find_board (ch);
if (!board) {
ch->SendText ("There is no bulletin board here to post "
"your note on.\n\r");
return;
}
if (!can_post (ch, board)) {
ch->SendText ("A magical force prevents you from posting "
"your note here...\n\r");
return;
}
if (board->num_posts >= board->max_posts) {
ch->SendText ("There is no room on this board to post your note.\n\r");
return;
}
act (AT_ACTION, "$n posts a note.", ch, NULL, NULL, TO_ROOM);
pnote = new CNoteData;
pnote->date = STRALLOC (CurrentTime.GetString ());
char *text = GetExtraDescr ("_text_", paper->ExDesList);
pnote->text = text ? STRALLOC (text) : STRALLOC ("");
text = GetExtraDescr ("_to_", paper->ExDesList);
pnote->to_list = text ? STRALLOC (text) : STRALLOC ("all");
text = GetExtraDescr ("_subject_", paper->ExDesList);
pnote->subject = text ? STRALLOC (text) : STRALLOC ("");
pnote->sender = QUICKLINK (ch->GetName ());
pnote->voting = 0;
pnote->yesvotes = str_dup ("");
pnote->novotes = str_dup ("");
pnote->abstentions = str_dup ("");
board->AddNote (pnote);
board->WriteAllNotes ();
ch->SendText ("You post your note on the board.\n\r");
extract_obj (paper);
return;
}
if (!str_cmp (arg, "remove")
|| !str_cmp (arg, "take") || !str_cmp (arg, "copy")) {
char take;
board = find_board (ch);
if (!board) {
ch->SendText ("There is no board here to take a note from!\n\r");
return;
}
if (!str_cmp (arg, "take"))
take = 1;
else if (!str_cmp (arg, "copy")) {
if (ch->IsMortal ()) {
ch->SendText ("Huh? Type 'help note' for usage.\n\r");
return;
}
take = 2;
}
else take = 0;
if (!is_number (arg_passed)) {
ch->SendText ("Note remove which number?\n\r");
return;
}
if (!can_read (ch, board)) {
ch->SendText ("You can't make any sense of what's posted here, "
"let alone remove anything!\n\r");
return;
}
anum = atoi (arg_passed);
POSITION pos = board->GetFirstNotePosition ();
int nNote = 0;
while (pos) {
pnote = board->GetNextNote (pos);
if (IS_MAIL && ((is_note_to (ch, pnote))
|| ch->GetTrustLevel () >= SysData.TakeOthersMailLev))
++nNote;
else if (!IS_MAIL) then ++nNote;
if ((is_note_to (ch, pnote)
|| can_remove (ch, board)) && (nNote == anum)) {
if ((is_name ("all", pnote->to_list))
&& (ch->GetTrustLevel () < SysData.TakeOthersMailLev)
&& (take == 1)) {
ch->SendText ("Notes addressed to 'all' can not be taken.\n\r");
return;
}
if (take != 0) {
if (ch->GetGold () < 50
&& ch->GetTrustLevel () < SysData.ReadMailFreeLev) {
if (take == 1)
ch->SendText ("It costs 50 coins to take your mail.\n\r");
else
ch->SendText ("It costs 50 coins to copy your mail.\n\r");
return;
}
if (ch->GetTrustLevel () < SysData.ReadMailFreeLev)
ch->AddGold (-50);
paper = create_object (OIdxTable.GetObj (OBJ_VNUM_NOTE), 0);
ed = SetOExtra (paper, "_sender_");
STRFREE (ed->description);
ed->description = QUICKLINK (pnote->sender);
ed = SetOExtra (paper, "_text_");
STRFREE (ed->description);
ed->description = QUICKLINK (pnote->text);
ed = SetOExtra (paper, "_to_");
STRFREE (ed->description);
ed->description = QUICKLINK (pnote->to_list);
ed = SetOExtra (paper, "_subject_");
STRFREE (ed->description);
ed->description = QUICKLINK (pnote->subject);
ed = SetOExtra (paper, "_date_");
STRFREE (ed->description);
ed->description = QUICKLINK (pnote->date);
ed = SetOExtra (paper, "note");
STRFREE (ed->description);
sprintf (notebuf, "From: ");
strcat (notebuf, pnote->sender);
strcat (notebuf, "\r\nTo: ");
strcat (notebuf, pnote->to_list);
strcat (notebuf, "\r\nSubject: ");
strcat (notebuf, pnote->subject);
strcat (notebuf, "\r\n\r\n");
strcat (notebuf, strip_cr (pnote->text));
strcat (notebuf, "\n\r");
ed->description = STRALLOC (notebuf);
paper->value[0] = 2;
paper->value[1] = 2;
paper->value[2] = 2;
sprintf (short_desc_buf, "a note from %s to %s",
pnote->sender, pnote->to_list);
paper->SetShortDescr (short_desc_buf);
sprintf (long_desc_buf,
"A note from %s to %s lies on the ground.",
pnote->sender, pnote->to_list);
paper->SetDescription (long_desc_buf);
sprintf (keyword_buf, "note parchment paper %s",
pnote->to_list);
paper->SetName (keyword_buf);
}
if (take != 2)
board->RemoveNote (pnote);
ch->SendText ("Ok.\n\r");
if (take == 1) {
act (AT_ACTION, "$n takes a note.", ch, NULL, NULL, TO_ROOM);
obj_to_char (paper, ch);
}
else if (take == 2) {
act (AT_ACTION, "$n copies a note.", ch, NULL, NULL, TO_ROOM);
obj_to_char (paper, ch);
}
else
act (AT_ACTION, "$n removes a note.", ch, NULL, NULL, TO_ROOM);
return;
}
}
ch->SendText ("No such note.\n\r");
return;
}
ch->SendText ("Huh? Type 'help note' for usage.\n\r");
return;
}
void CBoardData::Read (char* pLine, FILE* fp)
{
#ifdef KEY
#undef KEY
#endif
#define KEY(literal,field,value) \
if (!str_cmp (word, literal)) { \
field = value; \
fMatch = TRUE; \
break; \
}
if (str_cmp (ParseWord (pLine), "filename")) {
bug ("CBoardData::Read: No notefile name in boards.txt");
return;
}
note_file = ParseStringNohash (pLine, fp);
char *word;
char buf [MAX_STRING_LENGTH];
BOOL fMatch;
for (;;) {
fMatch = FALSE;
if (! feof (fp)) {
pLine = fread_line (fp);
word = ParseWord (pLine);
}
else word = "End";
switch (UPPER (word [0])) {
case '*':
fMatch = TRUE;
break;
case 'E':
KEY ("Extra_readers", extra_readers, ParseStringNohash (pLine, fp));
KEY ("Extra_removers", extra_removers, ParseStringNohash (pLine, fp));
if (!str_cmp (word, "End")) {
num_posts = 0;
SetNext (NULL);
SetPrev (NULL);
if (!read_group)
read_group = str_dup ("");
if (!post_group)
post_group = str_dup ("");
if (!extra_readers)
extra_readers = str_dup ("");
if (!extra_removers)
extra_removers = str_dup ("");
return;
}
case 'M':
KEY ("Min_read_level", min_read_level, ParseNumber (pLine));
KEY ("Min_post_level", min_post_level, ParseNumber (pLine));
KEY ("Min_remove_level", min_remove_level,ParseNumber (pLine));
KEY ("Max_posts", max_posts, ParseNumber (pLine));
case 'P':
KEY ("Post_group", post_group, ParseStringNohash (pLine, fp));
case 'R':
KEY ("Read_group", read_group, ParseStringNohash (pLine, fp));
case 'T':
KEY ("Type", type, ParseNumber (pLine));
case 'V':
KEY ("Vnum", board_obj, ParseNumber (pLine));
}
if (! fMatch) {
sprintf (buf, "CBoardData::Read: no match: %s", word);
bug (buf);
}
}
}
CBoardData *CBoardList::ReadBoard (FILE *fp)
{
if (! feof (fp)) {
char *pLine = fread_line (fp);
if (*pLine) {
CBoardData *pBoard = new CBoardData;
pBoard->Read (pLine, fp);
return pBoard;
}
}
return NULL;
}
// Load boards file.
void CBoardList::Load ()
{
FILE *board_fp;
FILE *note_fp;
CBoardData *board;
CNoteData *pnote;
gpDoc->LogString ("Loading boards...", LOG_BOOT);
if (! (board_fp = fopen (FileTable.GetName (SM_BOARD_FILE), "r")))
return;
while ((board = ReadBoard (board_fp))) {
AddTail (board);
CString nfn = FileTable.MakeName (SD_BOARD_DIR, board->note_file);
gpDoc->LogString (nfn, LOG_BOOT);
if ((note_fp = fopen (nfn, "r")) != NULL) {
while ((pnote = read_note (nfn, note_fp))) {
board->m_NList.AddTail (pnote);
board->num_posts++;
}
}
}
fclose (board_fp);
}
void do_makeboard (CCharacter *ch, char *argument)
{
CBoardData *board;
if (!argument || argument[0] == '\0') {
ch->SendText ("Usage: makeboard <filename>\n\r");
return;
}
smash_tilde (argument);
board = new CBoardData;
board->note_file = str_dup (strlower (argument));
board->read_group = str_dup ("");
board->post_group = str_dup ("");
board->extra_readers = str_dup ("");
board->extra_removers = str_dup ("");
BoardList.AddBoard (board);
}
CNoteData *read_note (const char *notefile, FILE *fp)
{
char *word, *pLine;
CNoteData *pNote = new CNoteData;
for (;;) {
if (feof (fp)) {
fclose (fp);
delete pNote;
return NULL;
}
pLine = fread_line (fp);
if (*pLine) {
word = ParseWord (pLine);
if (! str_cmp (word, "sender"))
pNote->sender = ParseString (pLine, fp);
else if (! str_cmp (word, "date"))
pNote->date = ParseString (pLine, fp);
else if (! str_cmp (word, "to"))
pNote->to_list = ParseString (pLine, fp);
else if (! str_cmp (word, "subject"))
pNote->subject = ParseString (pLine, fp);
else if (! str_cmp (word, "voting"))
pNote->voting = ParseNumber (pLine);
else if (! str_cmp (word, "yesvotes"))
pNote->yesvotes = ParseStringNohash (pLine, fp);
else if (! str_cmp (word, "novotes"))
pNote->novotes = ParseStringNohash (pLine, fp);
else if (! str_cmp (word, "abstentions"))
pNote->abstentions = ParseStringNohash (pLine, fp);
else if (! str_cmp (word, "text")) {
pLine = fread_line (fp);
pNote->text = ParseString (pLine, fp);
break;
}
else {
bug ("read_note: Bad keyword (%s)", word);
ThrowSmaugException (SE_NOTES);
}
}
}
if (! pNote->yesvotes) pNote->yesvotes = str_dup ("");
if (! pNote->novotes) pNote->novotes = str_dup ("");
if (! pNote->abstentions) pNote->abstentions = str_dup ("");
pNote->SetNext (NULL);
pNote->SetPrev (NULL);
return pNote;
}
void do_bset (CCharacter *ch, char *argument)
{
CBoardData *board;
BOOL found;
char arg1 [MAX_INPUT_LENGTH];
char arg2 [MAX_INPUT_LENGTH];
char buf [MAX_STRING_LENGTH];
int value;
argument = one_argument (argument, arg1);
argument = one_argument (argument, arg2);
if (arg1[0] == '\0' || arg2[0] == '\0') {
ch->SendText ("Usage: bset <board filename> <field> value\n\r");
ch->SendText ("\n\rField being one of:\n\r");
ch->SendText (" vnum read post remove maxpost filename type\n\r");
ch->SendText (" read_group post_group extra_readers extra_removers\n\r");
return;
}
value = atoi (argument);
found = FALSE;
POSITION pos = BoardList.GetHeadPosition ();
while (pos) {
board = BoardList.GetNext (pos);
if (!str_cmp (arg1, board->note_file)) {
found = TRUE;
break;
}
}
if (!found) {
ch->SendText ("Board not found.\n\r");
return;
}
if (!str_cmp (arg2, "vnum")) {
if (!OIdxTable.GetObj (value)) {
ch->SendText ("No such object.\n\r");
return;
}
board->board_obj = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "read")) {
if (value < 0 || value > MAX_LEVEL) {
ch->SendText ("Value out of range.\n\r");
return;
}
board->min_read_level = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "read_group")) {
if (!argument || argument[0] == '\0') {
ch->SendText ("No group specified.\n\r");
return;
}
delete board->read_group;
if (!str_cmp (argument, "none"))
board->read_group = str_dup ("");
else
board->read_group = str_dup (argument);
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "post_group")) {
if (!argument || argument[0] == '\0') {
ch->SendText ("No group specified.\n\r");
return;
}
delete board->post_group;
if (!str_cmp (argument, "none"))
board->post_group = str_dup ("");
else
board->post_group = str_dup (argument);
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "extra_removers")) {
if (!argument || argument[0] == '\0') {
ch->SendText ("No names specified.\n\r");
return;
}
if (!str_cmp (argument, "none"))
buf[0] = '\0';
else
sprintf (buf, "%s %s", board->extra_removers, argument);
delete board->extra_removers;
board->extra_removers = str_dup (buf);
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "extra_readers")) {
if (!argument || argument[0] == '\0') {
ch->SendText ("No names specified.\n\r");
return;
}
if (!str_cmp (argument, "none"))
buf[0] = '\0';
else
sprintf (buf, "%s %s", board->extra_readers, argument);
delete board->extra_readers;
board->extra_readers = str_dup (buf);
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "filename")) {
if (!argument || argument[0] == '\0') {
ch->SendText ("No filename specified.\n\r");
return;
}
delete board->note_file;
board->note_file = str_dup (argument);
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "post")) {
if (value < 0 || value > MAX_LEVEL) {
ch->SendText ("Value out of range.\n\r");
return;
}
board->min_post_level = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "remove")) {
if (value < 0 || value > MAX_LEVEL) {
ch->SendText ("Value out of range.\n\r");
return;
}
board->min_remove_level = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "maxpost")) {
if (value < 1 || value > 1000) {
ch->SendText ("Value out of range.\n\r");
return;
}
board->max_posts = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
if (!str_cmp (arg2, "type")) {
if (value < 0 || value > 1) {
ch->SendText ("Value out of range.\n\r");
return;
}
board->type = value;
BoardList.WriteBoardsFile ();
ch->SendText ("Done.\n\r");
return;
}
do_bset (ch, "");
}
void do_bstat (CCharacter *ch, char *argument)
{
CBoardData *board;
BOOL found;
char arg [MAX_INPUT_LENGTH];
argument = one_argument (argument, arg);
if (arg[0] == '\0') {
ch->SendText ("Usage: bstat <board filename>\n\r");
return;
}
found = FALSE;
POSITION pos = BoardList.GetHeadPosition ();
while (pos) {
board = BoardList.GetNext (pos);
if (!str_cmp (arg, board->note_file)) {
found = TRUE;
break;
}
}
if (!found) {
ch->SendText ("Board not found.\n\r");
return;
}
ch->SendTextf ("%-12s Vnum: %5d Read: %2d Post: %2d Rmv: %2d Max: %2d "
"Posts: %d Type: %d\n\r", board->note_file, board->board_obj,
board->min_read_level, board->min_post_level,
board->min_remove_level, board->max_posts,
board->num_posts, board->type);
ch->SendTextf (
"Read_group: %-15s Post_group: %-15s \n\rExtra_readers: %-10s\n\r",
board->read_group, board->post_group, board->extra_readers);
}
void do_boards (CCharacter *ch, char *argument)
{
if (BoardList.IsEmpty ()) {
ch->SendText ("There are no boards.\n\r");
return;
}
set_char_color (AT_NOTE, ch);
POSITION pos = BoardList.GetHeadPosition ();
while (pos) {
CBoardData &Bd = *BoardList.GetNext (pos);
ch->SendTextf ("%-16s Vnum: %5d Read: %2d Post: %2d Rmv: %2d "
"Max: %2d Posts: %d Type: %d\n\r", Bd.note_file,
Bd.board_obj, Bd.min_read_level, Bd.min_post_level,
Bd.min_remove_level, Bd.max_posts, Bd.num_posts, Bd.type);
}
}
void mail_count (CCharacter *ch)
{
int Count = 0;
POSITION pos = BoardList.GetHeadPosition ();
while (pos) {
CBoardData &Bd = *BoardList.GetNext (pos);
if (Bd.type == BOARD_MAIL && can_read (ch, &Bd)) {
POSITION pos = Bd.GetFirstNotePosition ();
while (pos) {
if (is_note_to (ch, Bd.GetNextNote (pos)))
++Count;
}
}
}
if (Count)
ch->SendTextf ("You have %d mail messages waiting.\n\r", Count);
}
CNoteData::~CNoteData ()
{
delete yesvotes;
delete novotes;
delete abstentions;
}