/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: modify.c                                      Part of CircleMUD *
*  Usage: Run-time modification of game variables                         *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "comm.h"
#include "mail.h"
#include "objedit.h"
#include "roomedit.h"
#include "screen.h"

#define REBOOT_AT    10  /* 0-23, time of optional reboot if -e lib/reboot */


#define TP_MOB    0
#define TP_OBJ	  1
#define TP_ERROR  2

void print_mstr(struct descriptor_data *d);
void	show_string(struct descriptor_data *d, char *input);
void print_ostr(struct descriptor_data *d);
void print_exd(struct descriptor_data *d);
void print_rstr(struct descriptor_data *d);
void print_rexd(struct descriptor_data *d);
void print_tele(struct descriptor_data *d);
void print_exit(struct descriptor_data *d);
void print_messages(struct descriptor_data *d);
void print_zonedata(struct descriptor_data *d);

char	*string_fields[] = 
{
   "name",
   "short",
   "long",
   "description",
   "title",
   "delete-description",
   "\n"
};


/* maximum length for text field x+1 */
int	length[] = 
{
   15,
   60,
   256,
   240,
   60
};



char	*skill_fields[] = 
{
   "learned",
   "affected",
   "duration",
   "recognize",
   "\n"
};



int	max_value[] = 
{
   255,
   255,
   10000,
   1
};


/* ************************************************************************
*  modification of malloc'ed strings                                      *
************************************************************************ */

/* Add user input to the 'current' string (as defined by d->str) */
void	string_add(struct descriptor_data *d, char *str)
{
   char	*scan;
   int	terminator = 0;
   extern char	*MENU;
   extern char	*ANSI_MENU;
   /* determine if this is the terminal string, and truncate if so */
   for (scan = str; *scan; scan++)
      if ((terminator = (*scan == '@' && *(scan -1) == '@'))) {
	 *(scan -1) = '\0';
	 break;
      }

   if (!(*d->str)) {
      if (strlen(str) > d->max_str) {
	 send_to_char("String too long - Truncated.\r\n",
	     d->character);
	 *(str + d->max_str) = '\0';
	 terminator = 1;
      }
      CREATE(*d->str, char, strlen(str) + 3);
      strcpy(*d->str, str);
   } else {
      if (strlen(str) + strlen(*d->str) > d->max_str) {
	 send_to_char("String too long. Last line skipped.\r\n",
	     d->character);
	 terminator = 1;
      } else	 {
	 if (!(*d->str = (char *) realloc(*d->str, strlen(*d->str) + 
	     strlen(str) + 3))) {
	    perror("string_add");
	    exit(1);
	 }
	 strcat(*d->str, str);
      }
   }

   if (terminator) {
      if (!d->connected && (PLR_FLAGGED(d->character, PLR_MAILING))) {
	 store_mail(d->name, d->character->player.name, *d->str);
	 free(*d->str);
	 free(d->str);
	 free(d->name);
	 d->name = 0;
	 SEND_TO_Q("Message sent!\r\n", d);
	 if (!IS_NPC(d->character)
	     && (PLR_FLAGGED(d->character,PLR_MAILING))){
	     act("$n finishes writing a message.",TRUE,d->character,0,0,TO_ROOM);	     
	    REMOVE_BIT(PLR_FLAGS(d->character), PLR_MAILING);
	 }
      }
      d->str = 0;
      if (d->connected == CON_EXDSCR) {
	  if (d->color)
	      SEND_TO_Q(ANSI_MENU,d);
	  else
	      SEND_TO_Q(MENU, d);
	  d->connected = CON_SLCT;
      }
      if(d->mob_edit)
	  print_mstr(d);
      else if(d->obj_edit){
	  if (d->oedit_mode == OSTR_EDIT)
	      print_ostr(d);
	  else
	      print_exd(d);
      }
      else if (d->shop_edit) {
	print_messages(d);
      }
      else if (d->zone_edit) {
	print_zonedata(d);
      }      
      else if(d->room_edit){
	  if (d->redit_mode == RTEXT_EDIT)
	      print_rstr(d);
	  else if (d->redit_mode == REDD_EDIT)
	      print_rexd(d);
	  else if (d->redit_mode == RTELE_EDIT)
	      print_tele(d);	  
	  else if (d->redit_mode == REXIT_EDIT)
	      print_exit(d);
	  else if (d->redit_mode == WITH_MODE) {
	    SEND_TO_Q("With: ",d);
	    d->str = &(d->with);
	    free(*d->str);
	    *d->str = 0;
	    d->max_str = 29;
	    d->redit_mode = REPCON_MODE;
	    return;
	  }
	  else if (d->redit_mode == REPCON_MODE) {
	    sprintf(buf, "Confirm replace %s%s%s with %s%s%s (y/n): ",
		    CCGRN(d->character,C_NRM),d->replace
		    ,CCNRM(d->character,C_NRM),
		    CCGRN(d->character,C_NRM),d->with
		    ,CCNRM(d->character,C_NRM));
	    SEND_TO_Q(buf,d);
	  }
      }
	 if (!d->connected && d->character && !IS_NPC(d->character)
	     && (PLR_FLAGGED(d->character, PLR_WRITING))){
	     act("$n finishes writing a message on the board.",TRUE,d->character,0,0,TO_ROOM);
	  
	     REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING);
      }
   } else
      strcat(*d->str, "\r\n");
}


/* interpret an argument for do_string */
void	quad_arg(char *arg, int *type, char *name, int *field, char *string)
{
   char	buff[MAX_STRING_LENGTH];

   /* determine type */
   arg = one_argument(arg, buff);
   if (is_abbrev(buff, "char"))
      *type = TP_MOB;
   else if (is_abbrev(buff, "obj"))
      *type = TP_OBJ;
   else {
      *type = TP_ERROR;
      return;
   }

   /* find name */
   arg = one_argument(arg, name);

   /* field name and number */
   arg = one_argument(arg, buf);
   if (!(*field = old_search_block(buf, 0, strlen(buf), string_fields, 0)))
      return;

   /* string */
   for (; isspace(*arg); arg++)
      ;
   for (; (*string = *arg); arg++, string++)
      ;

   return;
}

/* modification of malloc'ed strings in chars/objects */
ACMD(do_string)
{
   char	name[MAX_STRING_LENGTH], string[MAX_STRING_LENGTH];
   int	field, type;
   struct char_data *mob;
   struct obj_data *obj;
   struct extra_descr_data *ed=0, *tmp=0;

   if (IS_NPC(ch))
      return;

   quad_arg(argument, &type, name, &field, string);

   if (type == TP_ERROR) {
      send_to_char("Usage: string ('obj'|'char') <name> <field> [<string>]", ch);
      return;
   }

   if (!field) {
      send_to_char("No field by that name. Try 'help string'.\r\n", ch);
      return;
   }

   if (type == TP_MOB) {
      /* locate the beast */
      if (!(mob = get_char_vis(ch, name))) {
	 send_to_char("I don't know anyone by that name...\r\n", ch);
	 return;
      }

      /*      if (IS_NPC(mob)) {
	 send_to_char("Sorry, string is disabled for mobs.\r\n", ch);
	 return; 
      }*/

      switch (field) {
      case 1:
	 if (!IS_NPC(mob)) {
	    send_to_char("You can't change that field for players.", ch);
	    return;
	 }
	 ch->desc->str = &(mob->player.name);
	 if (!IS_NPC(mob))
	    send_to_char("WARNING: You have changed the name of a player.\r\n", ch);
	 break;
      case 2:
	 if (!IS_NPC(mob)) {
	    send_to_char("That field is for monsters only.\r\n", ch);
	    return;
	 }
	 ch->desc->str = &mob->player.short_descr;
	 break;
      case 3:
	 if (!IS_NPC(mob)) {
	    send_to_char("That field is for monsters only.\r\n", ch);
	    return;
	 }
	 ch->desc->str = &mob->player.long_descr;
	 break;
      case 4:
	 ch->desc->str = &mob->player.description;
	 break;
      case 5:
	 if (IS_NPC(mob)) {
	    send_to_char("Monsters have no titles.\r\n", ch);
	    return;
	 }
	 ch->desc->str = &mob->player.title;
	 break;
      default:
	 send_to_char("That field is undefined for monsters.\r\n", ch);
	 return;
	 break;
      }
   } else {/* type == TP_OBJ 	 {
      send_to_char("Sorry, string has been disabled for objects.\r\n", ch);
      return; */

      /* locate the object */
      if (!(obj = get_obj_vis(ch, name))) {
	 send_to_char("Can't find such a thing here..\r\n", ch);
	 return;
      }

      switch (field) {
      case 1:
	 ch->desc->str = &obj->name;
	 break;
      case 2:
	 ch->desc->str = &obj->short_description;
	 break;
      case 3:
	 ch->desc->str = &obj->description;
	 break;
      case 4:
	 if (!*string) {
	    send_to_char("You have to supply a keyword.\r\n", ch);
	    return;
	 }
	 /* try to locate extra description */
	 for (ed = obj->ex_description; ; ed = ed->next)
	    if (!ed) /* the field was not found. create a new one. */ {
	       CREATE(ed , struct extra_descr_data, 1);
	       ed->next = obj->ex_description;
	       obj->ex_description = ed;
	       CREATE(ed->keyword, char, strlen(string) + 1);
	       strcpy(ed->keyword, string);
	       ed->description = 0;
	       ch->desc->str = &ed->description;
	       send_to_char("New field.\r\n", ch);
	       break;
	    }
	    else if (!str_cmp(ed->keyword, string)) /* the field exists */ {
	       free(ed->description);
	       ed->description = 0;
	       ch->desc->str = &ed->description;
	       send_to_char("Modifying description.\r\n", ch);
	       break;
	    }
	 ch->desc->max_str = MAX_STRING_LENGTH;
	 return; /* the stndrd (see below) procedure does not apply here */
	 break;
      case 6:
	 if (!*string) {
	    send_to_char("You must supply a field name.\r\n", ch);
	    return;
	 }
	 /* try to locate field */
	 for (ed = obj->ex_description; ; ed = ed->next)
	    if (!ed) {
	       send_to_char("No field with that keyword.\r\n", ch);
	       return;
	    }
	    else if (!str_cmp(ed->keyword, string)) {
	       free(ed->keyword);
	       if (ed->description)
		  free(ed->description);

	       /* delete the entry in the desr list */
	       if (ed == obj->ex_description)
		  obj->ex_description = ed->next;
	       else {
		  for (tmp = obj->ex_description; tmp->next != ed; 
		      tmp = tmp->next)
		     ;
		  tmp->next = ed->next;
	       }
	       free(ed);

	       send_to_char("Field deleted.\r\n", ch);
	       return;
	    }
	 break;
      default:
	 send_to_char(
	     "That field is undefined for objects.\r\n", ch);
	 return;
	 break;
      }
   }

   if (*ch->desc->str) {
      free(*ch->desc->str);
   }

   if (*string)   /* there was a string in the argument array */ {
      if (strlen(string) > length[field - 1]) {
	 send_to_char("String too long - truncated.\r\n", ch);
	 *(string + length[field - 1]) = '\0';
      }
      CREATE(*ch->desc->str, char, strlen(string) + 1);
      strcpy(*ch->desc->str, string);
      ch->desc->str = 0;
      send_to_char("Ok.\r\n", ch);
   } else /* there was no string. enter string mode */	 {
      send_to_char("Enter string. terminate with '@@'.\r\n", ch);
      *ch->desc->str = 0;
      ch->desc->max_str = length[field - 1];
   }
}

/* db stuff *********************************************** */


/* One_Word is like one_argument, execpt that words in quotes "" are */
/* regarded as ONE word                                              */

char	*one_word(char *argument, char *first_arg )
{
   int	found, begin, look_at;

   found = begin = 0;

   do {
      for ( ; isspace(*(argument + begin)); begin++)
	 ;

      if (*(argument + begin) == '\"') {  /* is it a quote */

	 begin++;

	 for (look_at = 0; (*(argument + begin + look_at) >= ' ') && 
	     (*(argument + begin + look_at) != '\"') ; look_at++)
	    *(first_arg + look_at) = LOWER(*(argument + begin + look_at));

	 if (*(argument + begin + look_at) == '\"')
	    begin++;

      } else {

	 for (look_at = 0; *(argument + begin + look_at) > ' ' ; look_at++)
	    *(first_arg + look_at) = LOWER(*(argument + begin + look_at));

      }

      *(first_arg + look_at) = '\0';
      begin += look_at;
   } while (fill_word(first_arg));

   return(argument + begin);
}


struct help_index_element *build_help_index(FILE *fl, int *num)
{
   int	nr = -1, issorted, i;
   struct help_index_element *list = 0, mem;
   char	buf[81], tmp[81], *scan;
   long	pos;

   for (; ; ) {
      pos = ftell(fl);
      fgets(buf, 81, fl);
      *(buf + strlen(buf) - 1) = '\0';
      scan = buf;
      for (; ; ) {
	 /* extract the keywords */
	 scan = one_word(scan, tmp);

	 if (!*tmp)
	    break;

	 if (!list) {
	    CREATE(list, struct help_index_element, 1);
	    nr = 0;
	 } else
	    RECREATE(list, struct help_index_element, ++nr + 1);

	 list[nr].pos = pos;
	 CREATE(list[nr].keyword, char, strlen(tmp) + 1);
	 strcpy(list[nr].keyword, tmp);
      }
      /* skip the text */
      do
	 fgets(buf, 81, fl);
      while (*buf != '#');
      if (*(buf + 1) == '~')
	 break;
   }
   /* we might as well sort the stuff */
   do {
      issorted = 1;
      for (i = 0; i < nr; i++)
	 if (str_cmp(list[i].keyword, list[i + 1].keyword) > 0) {
	    mem = list[i];
	    list[i] = list[i + 1];
	    list[i + 1] = mem;
	    issorted = 0;
	 }
   } while (!issorted);

   *num = nr;
   return(list);
}



void	page_string(struct descriptor_data *d, char *str, int keep_internal)
{
   if (!d)
      return;

   if (keep_internal) {
      CREATE(d->showstr_head, char, strlen(str) + 1);
      strcpy(d->showstr_head, str);
      d->showstr_point = d->showstr_head;
   } else
      d->showstr_point = str;

   show_string(d, "");
}



void	show_string(struct descriptor_data *d, char *input)
{
   char	buffer[MAX_STRING_LENGTH], buf[MAX_INPUT_LENGTH];
   register char	*scan, *chk;
   int	lines = 0, toggle = 1;

   one_argument(input, buf);

   if (*buf) {
      if (d->showstr_head) {
	 free(d->showstr_head);
	 d->showstr_head = 0;
      }
      d->showstr_point = 0;
      return;
   }

   /* show a chunk */
   for (scan = buffer; ; scan++, d->showstr_point++)
      if ((((*scan = *d->showstr_point) == '\n') || (*scan == '\r')) && 
          ((toggle = -toggle) < 0))
	 lines++;
      else if (!*scan || (lines >= 21)) {
	 *scan = '\0';
	 SEND_TO_Q(buffer, d);

	 /* see if this is the end (or near the end) of the string */
	 for (chk = d->showstr_point; isspace(*chk); chk++)
	    ;
	 if (!*chk) {
	    if (d->showstr_head) {
	       free(d->showstr_head);
	       d->showstr_head = 0;
	    }
	    d->showstr_point = 0;
	 }
	 SEND_TO_Q("\r\n",d); /* add an extra crlf for giggles */
	 return;
      }
}



void	check_reboot(void)
{
   long	tc;
   struct tm *t_info;
   char	dummy;
   FILE * boot;

   extern int	circle_shutdown, circle_reboot;

   tc = time(0);
   t_info = localtime(&tc);

   if ((t_info->tm_hour + 1) == REBOOT_AT && t_info->tm_min > 30)
      if ((boot = fopen("./reboot", "r"))) {
	 if (t_info->tm_min > 50) {
	    logg("Reboot exists.");
	    fread(&dummy, sizeof(dummy), 1, boot);
	    if (!feof(boot))   /* the file is nonepty */ {
	       logg("Reboot is nonempty.");
	       if (system("./reboot")) {
		  logg("Reboot script terminated abnormally");
		  send_to_all("The reboot was cancelled.\r\n");
		  system("mv ./reboot reboot.FAILED");
		  fclose(boot);
		  return;
	       } else
		  system("mv ./reboot reboot.SUCCEEDED");
	    }

	    send_to_all("Automatic reboot.  Come back in a little while.\r\n");
	    circle_shutdown = circle_reboot = 1;
	 } else if (t_info->tm_min > 40)
	    send_to_all("ATTENTION: CircleMUD will reboot in 10 minutes.\r\n");
	 else if (t_info->tm_min > 30)
	    send_to_all(
	        "Warning: The game will close and reboot in 20 minutes.\r\n");

	 fclose(boot);
      }
}