/*************************************************************************** * file: board.c , Implementation of boards. Part of DIKUMUD * * Usage : Board Commands. * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * * * * Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse * * Performance optimization and bug fixes by MERC Industries. * * You can use our stuff in any way you like whatsoever so long as this * * copyright notice remains intact. If you like it please drop a line * * to mec@garnet.berkeley.edu. * * * * This is free software and you are benefitting. We hope that you * * share your changes too. What goes around, comes around. * ***************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include "structs.h" #include "mob.h" #include "utils.h" extern struct room_data *world; #define MAX_MSGS 40 /* Max number of messages. */ #define SAVE_FILE "boards/board" /* Name of file for saving messages */ #define MAX_MESSAGE_LENGTH 2048 /* that should be enough */ struct board { struct board *next; int room; char filename[MAX_INPUT_LENGTH]; int has_loaded; int has_saved; char *msgs[MAX_MSGS]; /* message */ char *head[MAX_MSGS]; /* heading line */ int msg_num; int flag; /* 1=being written to; 0=safe */ } *brd=0; struct board * gpointer=0; void board_write_msg(struct board *,CHAR_DATA *, char *); int board_display_msg(struct board *brd,CHAR_DATA *ch, char *arg); int board_remove_msg(struct board *brd,CHAR_DATA *ch, char *arg); void board_save_board(struct board *brd); void board_load_board(struct board *brd); void board_reset_board(struct board *brd); void error_log(); void board_fix_long_desc(int num, char *headers[MAX_MSGS]); int board_show_board(struct board *brd,CHAR_DATA *ch, char *arg); /* I have used cmd number 180-182 as the cmd numbers here. */ /* The commands would be, in order : NOTE <header> */ /* READ <message number>, REMOVE <message number> */ /* LOOK AT BOARD should give the long desc of the board */ /* and that should equal a list of message numbers and */ /* headers. This is done by calling a function that sets */ /* the long desc in the board object. This function is */ /* called when someone does a REMOVE or NOTE command. */ /* I have named the function board_fix_long_desc(). In the */ /* board_write_msg() function there is a part that should */ /* be replaced with a call to some dreadful routine used */ /* by the STRING command to receive player input. It is */ /* reputed to lurk somewhere within the limits of an evil */ /* file named act.comm.c... or is that modify.c?..*/ /* saving the board after the addition of a new messg */ /* poses a slight problem, since the text isn't actually */ /* entered in board_write. What I'll do is to let board() */ /* save the first time a 'look' is issued in the room.. */ /* ugh! that's ugly - gotta think of something better. */ /* -quinn */ /* And here is the board...correct me if I'm wrong. */ struct board *create_board(int room,int fix) { struct board *board; int a; log("creating board"); board=(struct board *)malloc(sizeof (struct board)); if(board==0) log("cannot allocate board structure."); else { board->next=brd; board->room=room; board->has_loaded=0; board->has_saved=1; if(fix) { sprintf(board->filename,"%s.global",SAVE_FILE); gpointer=board; } else sprintf(board->filename,"%s.%d",SAVE_FILE,room); log(board->filename); for(a=0;a<MAX_MSGS;a++) { board->head[a]=0; board->msgs[a]=0; } board->msg_num=0; board->flag=0; brd=board; } return(board); } struct board *get_board(CHAR_DATA *ch, int fix) { struct board *board; if(brd==0 || (fix && !gpointer)) { if(fix) return(gpointer=create_board(world[ch->in_room].number,fix)); else return(create_board(world[ch->in_room].number,fix)); } if(fix) return(gpointer); for(board=brd;board!=0;board=board->next) if(board->room == world[ch->in_room].number) return(board); return(create_board(world[ch->in_room].number,fix)); } int being_written(struct board *board) { return(board->flag); } int board(CHAR_DATA *ch, int cmd, char *arg, struct obj_data *object) { struct board *board; static int isbusy=0; if (isbusy) return(FALSE); if (!ch->desc) return(FALSE); /* By MS or all NPC's will be trapped at the board */ if(!(board=get_board(ch,FALSE))) return(FALSE); /* note: I'll let display and remove return 0 if the arg was non-board- */ /* related. Thus, it'll be possible to read other things than the board */ /* while you're in the room. Conceiveably, you could do this for write, */ /* too, but I'm not in the mood for such hacking. */ if (!(board->has_loaded)) board_load_board(board); if (!(board->has_saved)) board_save_board(board); switch (cmd) { case 15: /* look */ return(board_show_board(board, ch, arg)); case 149: /* write */ isbusy=1; board_write_msg(board, ch, arg); isbusy=0; return 1; case 63: /* read */ return(board_display_msg(board, ch, arg)); case 66: /* remove */ return(board_remove_msg(board, ch, arg)); default: return 0; } } int board_general(CHAR_DATA *ch, int cmd, char *arg, struct obj_data *object) { struct board *board; static int isbusy=0; if (isbusy) return(FALSE); if (!ch->desc) return(FALSE); /* By MS or all NPC's will be trapped at the board */ if(!(board=get_board(ch,TRUE))) return(FALSE); /* note: I'll let display and remove return 0 if the arg was non-board- */ /* related. Thus, it'll be possible to read other things than the board */ /* while you're in the room. Conceiveably, you could do this for write, */ /* too, but I'm not in the mood for such hacking. */ if (!(board->has_loaded)) board_load_board(board); if (!(board->has_saved)) board_save_board(board); switch (cmd) { case 15: /* look */ if (being_written(board)) { send_to_char("Someone else is writing... Wait your turn.\r\n", ch); return(FALSE); } board_load_board(board); return(board_show_board(board, ch, arg)); case 149: /* write */ isbusy=1; board_write_msg(board, ch, arg); isbusy=0; return 1; case 63: /* read */ if (being_written(board)) { send_to_char("Someone else is writing... Wait your turn.\r\n", ch); return(TRUE); } return(board_display_msg(board, ch, arg)); case 66: /* remove */ if (being_written(board)) { send_to_char("Someone else is writing... Wait your turn.\r\n", ch); return(FALSE); } return(board_remove_msg(board, ch, arg)); default: return 0; } } void board_write_msg(struct board *brd, CHAR_DATA *ch, char *arg) { if (being_written(brd)) { send_to_char("Someone else is writing... Wait your turn.\r\n", ch); return; } if (brd->msg_num > MAX_MSGS - 1) { send_to_char("The board is full already.\r\n", ch); return; } /* skip blanks */ for(; isspace(*arg); arg++); if (!*arg) { send_to_char("We must have a headline!\r\n", ch); return; } brd->head[brd->msg_num] = (char *)malloc(strlen(arg) + strlen(GET_NAME(ch)) + 4); /* +4 is for a space and '()' around the character name. */ if (!brd->head[brd->msg_num]) { error_log("Malloc for board header failed.\r\n"); send_to_char("The board is malfunctioning - sorry.\r\n", ch); return; } strcpy(brd->head[brd->msg_num], arg); /* Is this clumsy? - four strcat() in a row I mean..*/ strcat(brd->head[brd->msg_num], " ("); strcat(brd->head[brd->msg_num], GET_NAME(ch)); strcat(brd->head[brd->msg_num], ")"); brd->msgs[brd->msg_num] = NULL; send_to_char("Write your message. Terminate with a @.\r\n\r\n", ch); act("$n starts to write a message.", TRUE, ch, 0, 0, TO_ROOM); ch->desc->str = &(brd->msgs[brd->msg_num]); ch->desc->max_str = MAX_MESSAGE_LENGTH; ch->desc->flag = &(brd->flag); brd->msg_num++; brd->has_saved=0; brd->flag = 1; } int board_remove_msg(struct board *brd,CHAR_DATA *ch, char *arg) { int ind, msg; char buf[256],number[MAX_INPUT_LENGTH]; if (being_written(brd)) { send_to_char("Someone is writing on it now... Please wait.\r\n", ch); return(TRUE); } one_argument(arg, number); if (!*number || !isdigit(*number)) return(0); if (!(msg = atoi(number))) return(0); if (!brd->msg_num) { send_to_char("The board is empty!\r\n", ch); return(1); } if (msg < 1 || msg > brd->msg_num) { send_to_char("That message exists only in your imagination.\r\n",ch); return(1); } if (!strncmp(brd->head[msg-1], "to:", 3)) { send_to_char("Private message.\r\n", ch); if(IS_NPC(ch) || (GET_LEVEL(ch)<33 && !isname(GET_NAME(ch), brd->head[msg-1]+3))) { send_to_char("And it is for someone else!\r\n", ch); return(1); } } else if (GET_LEVEL(ch)<31 && !isname(GET_NAME(ch),brd->head[msg-1]) ) { send_to_char("Only 10th level characters and higher may\r\n", ch); send_to_char("remove messages which they did not write.\r\n", ch); return(TRUE); } ind = msg; free(brd->head[--ind]); sprintf(buf, "$n just removed message %d.", ind + 1); if (brd->msgs[ind]) free(brd->msgs[ind]); for (; ind < brd->msg_num -1; ind++) { brd->head[ind] = brd->head[ind + 1]; brd->msgs[ind] = brd->msgs[ind + 1]; } brd->msg_num--; send_to_char("Message removed.\r\n", ch); act(buf, FALSE, ch, 0, 0, TO_ROOM); brd->has_saved=0; board_save_board(brd); return(1); } void board_save_board(struct board *brd) { FILE *the_file; int ind, len; if (being_written(brd)) return; log("saving board"); if (!brd->msg_num) { error_log("No messages to save.\r\n"); brd->has_saved=1; return; } the_file = fopen(brd->filename, "wb"); if (!the_file) { error_log("Unable to open/create savefile..\r\n"); return; } fwrite(&(brd->msg_num), sizeof(int), 1, the_file); for (ind = 0; ind < brd->msg_num; ind++) { len = strlen(brd->head[ind]) + 1; fwrite(&len, sizeof(int), 1, the_file); fwrite(brd->head[ind], sizeof(char), len, the_file); len = strlen(brd->msgs[ind]) + 1; fwrite(&len, sizeof(int), 1, the_file); fwrite(brd->msgs[ind], sizeof(char), len, the_file); } fclose(the_file); board_fix_long_desc(brd->msg_num, brd->head); brd->has_saved=1; return; } void board_load_board(struct board *brd) { FILE *the_file; int ind, len = 0; log("loading board"); board_reset_board(brd); the_file = fopen(brd->filename, "rb"); if (!the_file) { error_log("Can't open message file. Board will be empty.\r\n",0); brd->has_loaded=1; return; } fread(&(brd->msg_num), sizeof(int), 1, the_file); if (brd->msg_num < 1 || brd->msg_num > MAX_MSGS || feof(the_file)) { error_log("Board-message file corrupt or nonexistent.\r\n"); fclose(the_file); brd->msg_num=0; brd->has_loaded=1; return; } for (ind = 0; ind < brd->msg_num; ind++) { fread(&len, sizeof(int), 1, the_file); brd->head[ind] = (char *)malloc(len + 1); if (!brd->head[ind]) { error_log("Malloc for board header failed.\r\n"); board_reset_board(brd); fclose(the_file); brd->has_loaded=1; return; } fread(brd->head[ind], sizeof(char), len, the_file); fread(&len, sizeof(int), 1, the_file); brd->msgs[ind] = (char *)malloc(len + 1); if (!brd->msgs[ind]) { error_log("Malloc for board msg failed..\r\n"); board_reset_board(brd); fclose(the_file); brd->has_loaded=1; return; } fread(brd->msgs[ind], sizeof(char), len, the_file); } fclose(the_file); board_fix_long_desc(brd->msg_num, brd->head); brd->has_loaded=1; return; } void board_reset_board(struct board *brd) { int ind; for (ind = 0; ind < MAX_MSGS; ind++) { if(brd->head[ind]!=0) free(brd->head[ind]); if(brd->msgs[ind]!=0) free(brd->msgs[ind]); } brd->msg_num = 0; board_fix_long_desc(0, 0); return; } void error_log(char *str) { /* The original error-handling was MUCH */ fputs("Board : ", stderr); /* more competent than the current but */ fputs(str, stderr); /* I got the advice to cut it out..;) */ return; } int board_display_msg(struct board *brd,CHAR_DATA *ch, char *arg) { char number[MAX_INPUT_LENGTH], buffer[MAX_STRING_LENGTH]; int msg; if (being_written(brd)) { send_to_char("Someone is writing on it... Please wait.\r\n",ch); return(1); } one_argument(arg, number); if (!*number || !isdigit(*number)) return(0); if (!(msg = atoi(number))) return(0); if (!brd->msg_num) { send_to_char("The board is empty!\r\n", ch); return(1); } if (msg < 1 || msg > brd->msg_num) { send_to_char("That message exists only in your imagination.\r\n",ch); return(1); } if (!strncmp(brd->head[msg-1], "to:", 3)) { send_to_char("Private message.\r\n", ch); if(IS_NPC(ch) || (GET_LEVEL(ch)<32 && !isname(GET_NAME(ch), brd->head[msg-1]+3))) { send_to_char("And it is for someone else!\r\n", ch); return(1); } } sprintf(buffer, "Message %d : %s\r\n\r\n%s", msg,brd->head[msg - 1], brd->msgs[msg - 1]); page_string(ch->desc, buffer, 1); return(1); } void board_fix_long_desc(int num, char *headers[MAX_MSGS]) { /**** Assign the right value to this pointer..how? ****/ /**** It should point to the bulletin board object ****/ /**** Then make ob.description point to a malloced ****/ /**** space containing itoa(msg_num) and all the ****/ /**** headers. In the format : This is a bulletin board. Usage : READ/REMOVE <message #>, NOTE <header> There are 12 messages on the board. 1 : Re : Whatever and something else too. 2 : I don't agree with Rainbird. 3 : Me neither. 4 : Groo got hungry again - bug or sabotage? Well...something like that..;) ****/ /**** It is always to contain the first line and ****/ /**** the second line will vary in how many notes ****/ /**** the board has. Then the headers and message ****/ /**** numbers will be listed. ****/ return; } int board_show_board(struct board *brd,CHAR_DATA *ch, char *arg) { int i; char buf[MAX_STRING_LENGTH], tmp[MAX_INPUT_LENGTH]; one_argument(arg, tmp); if (!*tmp || !isname(tmp, "board bulletin")) return(0); act("$n studies the board.", TRUE, ch, 0, 0, TO_ROOM); strcpy(buf, "This is a bulletin board. Usage: READ/REMOVE <messg #>, WRITE <header>\r\nUse WRITE TO: <name> to write a private message.\r\n"); if (!brd->msg_num) strcat(buf, "The board is empty.\r\n"); else { sprintf(buf + strlen(buf), "There are %d messages on the board.\r\n", brd->msg_num); for (i = 0; i < brd->msg_num; i++) sprintf(buf + strlen(buf), "%-2d : %s\r\n", i + 1, brd->head[i]); } if(being_written(brd)) strcat(buf, "It is currently being written on.\r\n"); page_string(ch->desc, buf, 1); return(1); }