/ circle3.1 / src / modify.c
modify.c
  1  /* ************************************************************************
  2  *   File: modify.c                                      Part of CircleMUD *
  3  *  Usage: Run-time modification of game variables                         *
  4  *                                                                         *
  5  *  All rights reserved.  See license.doc for complete information.        *
  6  *                                                                         *
  7  *  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
  8  *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
  9  ************************************************************************ */
 10  
 11  #include "conf.h"
 12  #include "sysdep.h"
 13  
 14  
 15  #include "structs.h"
 16  #include "utils.h"
 17  #include "interpreter.h"
 18  #include "handler.h"
 19  #include "db.h"
 20  #include "comm.h"
 21  #include "spells.h"
 22  #include "mail.h"
 23  #include "boards.h"
 24  
 25  void show_string(struct descriptor_data *d, char *input);
 26  
 27  extern struct spell_info_type spell_info[];
 28  extern const char *MENU;
 29  extern const char *unused_spellname;	/* spell_parser.c */
 30  
 31  /* local functions */
 32  void smash_tilde(char *str);
 33  ACMD(do_skillset);
 34  char *next_page(char *str);
 35  int count_pages(char *str);
 36  void paginate_string(char *str, struct descriptor_data *d);
 37  
 38  const char *string_fields[] =
 39  {
 40    "name",
 41    "short",
 42    "long",
 43    "description",
 44    "title",
 45    "delete-description",
 46    "\n"
 47  };
 48  
 49  
 50  /* maximum length for text field x+1 */
 51  int length[] =
 52  {
 53    15,
 54    60,
 55    256,
 56    240,
 57    60
 58  };
 59  
 60  
 61  /* ************************************************************************
 62  *  modification of malloc'ed strings                                      *
 63  ************************************************************************ */
 64  
 65  /*
 66   * Put '#if 1' here to erase ~, or roll your own method.  A common idea
 67   * is smash/show tilde to convert the tilde to another innocuous character
 68   * to save and then back to display it. Whatever you do, at least keep the
 69   * function around because other MUD packages use it, like mudFTP.
 70   *   -gg 9/9/98
 71   */
 72  void smash_tilde(char *str)
 73  {
 74  #if 0
 75    /*
 76     * Erase any ~'s inserted by people in the editor.  This prevents anyone
 77     * using online creation from causing parse errors in the world files.
 78     * Derived from an idea by Sammy <samedi@dhc.net> (who happens to like
 79     * his tildes thank you very much.), -gg 2/20/98
 80     */
 81      while ((str = strchr(str, '~')) != NULL)
 82        *str = ' ';
 83  #endif
 84  }
 85  
 86  /*
 87   * Basic API function to start writing somewhere.
 88   *
 89   * 'data' isn't used in stock CircleMUD but you can use it to pass whatever
 90   * else you may want through it.  The improved editor patch when updated
 91   * could use it to pass the old text buffer, for instance.
 92   */
 93  void string_write(struct descriptor_data *d, char **writeto, size_t len, long mailto, void *data)
 94  {
 95    if (d->character && !IS_NPC(d->character))
 96      SET_BIT(PLR_FLAGS(d->character), PLR_WRITING);
 97  
 98    if (data)
 99      mudlog(BRF, LVL_IMMORT, TRUE, "SYSERR: string_write: I don't understand special data.");
100  
101    d->str = writeto;
102    d->max_str = len;
103    d->mail_to = mailto;
104  }
105  
106  /* Add user input to the 'current' string (as defined by d->str) */
107  void string_add(struct descriptor_data *d, char *str)
108  {
109    int terminator;
110  
111    /* determine if this is the terminal string, and truncate if so */
112    /* changed to only accept '@' at the beginning of line - J. Elson 1/17/94 */
113  
114    delete_doubledollar(str);
115  
116    if ((terminator = (*str == '@')))
117      *str = '\0';
118  
119    smash_tilde(str);
120  
121    if (!(*d->str)) {
122      if (strlen(str) + 3 > d->max_str) { /* \r\n\0 */
123        send_to_char(d->character, "String too long - Truncated.\r\n");
124        strcpy(&str[d->max_str - 3], "\r\n");	/* strcpy: OK (size checked) */
125        CREATE(*d->str, char, d->max_str);
126        strcpy(*d->str, str);	/* strcpy: OK (size checked) */
127        terminator = 1;
128      } else {
129        CREATE(*d->str, char, strlen(str) + 3);
130        strcpy(*d->str, str);	/* strcpy: OK (size checked) */
131      }
132    } else {
133      if (strlen(str) + strlen(*d->str) + 3 > d->max_str) { /* \r\n\0 */
134        send_to_char(d->character, "String too long.  Last line skipped.\r\n");
135        terminator = 1;
136      } else {
137        RECREATE(*d->str, char, strlen(*d->str) + strlen(str) + 3); /* \r\n\0 */
138        strcat(*d->str, str);	/* strcat: OK (size precalculated) */
139      }
140    }
141  
142    if (terminator) {
143      if (STATE(d) == CON_PLAYING && (PLR_FLAGGED(d->character, PLR_MAILING))) {
144        store_mail(d->mail_to, GET_IDNUM(d->character), *d->str);
145        d->mail_to = 0;
146        free(*d->str);
147        free(d->str);
148        write_to_output(d, "Message sent!\r\n");
149        if (!IS_NPC(d->character))
150  	REMOVE_BIT(PLR_FLAGS(d->character), PLR_MAILING | PLR_WRITING);
151      }
152      d->str = NULL;
153  
154      if (d->mail_to >= BOARD_MAGIC) {
155        Board_save_board(d->mail_to - BOARD_MAGIC);
156        d->mail_to = 0;
157      }
158      if (STATE(d) == CON_EXDESC) {
159        write_to_output(d, "%s", MENU);
160        STATE(d) = CON_MENU;
161      }
162      if (STATE(d) == CON_PLAYING && d->character && !IS_NPC(d->character))
163        REMOVE_BIT(PLR_FLAGS(d->character), PLR_WRITING);
164    } else
165      strcat(*d->str, "\r\n");	/* strcat: OK (size checked) */
166  }
167  
168  
169  
170  /* **********************************************************************
171  *  Modification of character skills                                     *
172  ********************************************************************** */
173  
174  ACMD(do_skillset)
175  {
176    struct char_data *vict;
177    char name[MAX_INPUT_LENGTH];
178    char buf[MAX_INPUT_LENGTH], help[MAX_STRING_LENGTH];
179    int skill, value, i, qend;
180  
181    argument = one_argument(argument, name);
182  
183    if (!*name) {			/* no arguments. print an informative text */
184      send_to_char(ch, "Syntax: skillset <name> '<skill>' <value>\r\n"
185  		"Skill being one of the following:\r\n");
186      for (qend = 0, i = 0; i <= TOP_SPELL_DEFINE; i++) {
187        if (spell_info[i].name == unused_spellname)	/* This is valid. */
188  	continue;
189        send_to_char(ch, "%18s", spell_info[i].name);
190        if (qend++ % 4 == 3)
191  	send_to_char(ch, "\r\n");
192      }
193      if (qend % 4 != 0)
194        send_to_char(ch, "\r\n");
195      return;
196    }
197  
198    if (!(vict = get_char_vis(ch, name, NULL, FIND_CHAR_WORLD))) {
199      send_to_char(ch, "%s", NOPERSON);
200      return;
201    }
202    skip_spaces(&argument);
203  
204    /* If there is no chars in argument */
205    if (!*argument) {
206      send_to_char(ch, "Skill name expected.\r\n");
207      return;
208    }
209    if (*argument != '\'') {
210      send_to_char(ch, "Skill must be enclosed in: ''\r\n");
211      return;
212    }
213    /* Locate the last quote and lowercase the magic words (if any) */
214  
215    for (qend = 1; argument[qend] && argument[qend] != '\''; qend++)
216      argument[qend] = LOWER(argument[qend]);
217  
218    if (argument[qend] != '\'') {
219      send_to_char(ch, "Skill must be enclosed in: ''\r\n");
220      return;
221    }
222    strcpy(help, (argument + 1));	/* strcpy: OK (MAX_INPUT_LENGTH <= MAX_STRING_LENGTH) */
223    help[qend - 1] = '\0';
224    if ((skill = find_skill_num(help)) <= 0) {
225      send_to_char(ch, "Unrecognized skill.\r\n");
226      return;
227    }
228    argument += qend + 1;		/* skip to next parameter */
229    argument = one_argument(argument, buf);
230  
231    if (!*buf) {
232      send_to_char(ch, "Learned value expected.\r\n");
233      return;
234    }
235    value = atoi(buf);
236    if (value < 0) {
237      send_to_char(ch, "Minimum value for learned is 0.\r\n");
238      return;
239    }
240    if (value > 100) {
241      send_to_char(ch, "Max value for learned is 100.\r\n");
242      return;
243    }
244    if (IS_NPC(vict)) {
245      send_to_char(ch, "You can't set NPC skills.\r\n");
246      return;
247    }
248  
249    /*
250     * find_skill_num() guarantees a valid spell_info[] index, or -1, and we
251     * checked for the -1 above so we are safe here.
252     */
253    SET_SKILL(vict, skill, value);
254    mudlog(BRF, LVL_IMMORT, TRUE, "%s changed %s's %s to %d.", GET_NAME(ch), GET_NAME(vict), spell_info[skill].name, value);
255    send_to_char(ch, "You change %s's %s to %d.\r\n", GET_NAME(vict), spell_info[skill].name, value);
256  }
257  
258  
259  
260  /*********************************************************************
261  * New Pagination Code
262  * Michael Buselli submitted the following code for an enhanced pager
263  * for CircleMUD.  All functions below are his.  --JE 8 Mar 96
264  *
265  *********************************************************************/
266  
267  /* Traverse down the string until the begining of the next page has been
268   * reached.  Return NULL if this is the last page of the string.
269   */
270  char *next_page(char *str)
271  {
272    int col = 1, line = 1, spec_code = FALSE;
273  
274    for (;; str++) {
275      /* If end of string, return NULL. */
276      if (*str == '\0')
277        return (NULL);
278  
279      /* If we're at the start of the next page, return this fact. */
280      else if (line > PAGE_LENGTH)
281        return (str);
282  
283      /* Check for the begining of an ANSI color code block. */
284      else if (*str == '\x1B' && !spec_code)
285        spec_code = TRUE;
286  
287      /* Check for the end of an ANSI color code block. */
288      else if (*str == 'm' && spec_code)
289        spec_code = FALSE;
290  
291      /* Check for everything else. */
292      else if (!spec_code) {
293        /* Carriage return puts us in column one. */
294        if (*str == '\r')
295  	col = 1;
296        /* Newline puts us on the next line. */
297        else if (*str == '\n')
298  	line++;
299  
300        /* We need to check here and see if we are over the page width,
301         * and if so, compensate by going to the begining of the next line.
302         */
303        else if (col++ > PAGE_WIDTH) {
304  	col = 1;
305  	line++;
306        }
307      }
308    }
309  }
310  
311  
312  /* Function that returns the number of pages in the string. */
313  int count_pages(char *str)
314  {
315    int pages;
316  
317    for (pages = 1; (str = next_page(str)); pages++);
318    return (pages);
319  }
320  
321  
322  /* This function assigns all the pointers for showstr_vector for the
323   * page_string function, after showstr_vector has been allocated and
324   * showstr_count set.
325   */
326  void paginate_string(char *str, struct descriptor_data *d)
327  {
328    int i;
329  
330    if (d->showstr_count)
331      *(d->showstr_vector) = str;
332  
333    for (i = 1; i < d->showstr_count && str; i++)
334      str = d->showstr_vector[i] = next_page(str);
335  
336    d->showstr_page = 0;
337  }
338  
339  
340  /* The call that gets the paging ball rolling... */
341  void page_string(struct descriptor_data *d, char *str, int keep_internal)
342  {
343    char actbuf[MAX_INPUT_LENGTH] = "";
344  
345    if (!d)
346      return;
347  
348    if (!str || !*str)
349      return;
350  
351    d->showstr_count = count_pages(str);
352    CREATE(d->showstr_vector, char *, d->showstr_count);
353  
354    if (keep_internal) {
355      d->showstr_head = strdup(str);
356      paginate_string(d->showstr_head, d);
357    } else
358      paginate_string(str, d);
359  
360    show_string(d, actbuf);
361  }
362  
363  
364  /* The call that displays the next page. */
365  void show_string(struct descriptor_data *d, char *input)
366  {
367    char buffer[MAX_STRING_LENGTH], buf[MAX_INPUT_LENGTH];
368    int diff;
369  
370    any_one_arg(input, buf);
371  
372    /* Q is for quit. :) */
373    if (LOWER(*buf) == 'q') {
374      free(d->showstr_vector);
375      d->showstr_vector = NULL;
376      d->showstr_count = 0;
377      if (d->showstr_head) {
378        free(d->showstr_head);
379        d->showstr_head = NULL;
380      }
381      return;
382    }
383    /* R is for refresh, so back up one page internally so we can display
384     * it again.
385     */
386    else if (LOWER(*buf) == 'r')
387      d->showstr_page = MAX(0, d->showstr_page - 1);
388  
389    /* B is for back, so back up two pages internally so we can display the
390     * correct page here.
391     */
392    else if (LOWER(*buf) == 'b')
393      d->showstr_page = MAX(0, d->showstr_page - 2);
394  
395    /* Feature to 'goto' a page.  Just type the number of the page and you
396     * are there!
397     */
398    else if (isdigit(*buf))
399      d->showstr_page = MAX(0, MIN(atoi(buf) - 1, d->showstr_count - 1));
400  
401    else if (*buf) {
402      send_to_char(d->character, "Valid commands while paging are RETURN, Q, R, B, or a numeric value.\r\n");
403      return;
404    }
405    /* If we're displaying the last page, just send it to the character, and
406     * then free up the space we used.
407     */
408    if (d->showstr_page + 1 >= d->showstr_count) {
409      send_to_char(d->character, "%s", d->showstr_vector[d->showstr_page]);
410      free(d->showstr_vector);
411      d->showstr_vector = NULL;
412      d->showstr_count = 0;
413      if (d->showstr_head) {
414        free(d->showstr_head);
415        d->showstr_head = NULL;
416      }
417    }
418    /* Or if we have more to show.... */
419    else {
420      diff = d->showstr_vector[d->showstr_page + 1] - d->showstr_vector[d->showstr_page];
421      if (diff > MAX_STRING_LENGTH - 3) /* 3=\r\n\0 */
422        diff = MAX_STRING_LENGTH - 3;
423      strncpy(buffer, d->showstr_vector[d->showstr_page], diff);	/* strncpy: OK (size truncated above) */
424      /*
425       * Fix for prompt overwriting last line in compact mode submitted by
426       * Peter Ajamian <peter@pajamian.dhs.org> on 04/21/2001
427       */
428      if (buffer[diff - 2] == '\r' && buffer[diff - 1]=='\n')
429        buffer[diff] = '\0';
430      else if (buffer[diff - 2] == '\n' && buffer[diff - 1] == '\r')
431        /* This is backwards.  Fix it. */
432        strcpy(buffer + diff - 2, "\r\n");	/* strcpy: OK (size checked) */
433      else if (buffer[diff - 1] == '\r' || buffer[diff - 1] == '\n')
434        /* Just one of \r\n.  Overwrite it. */
435        strcpy(buffer + diff - 1, "\r\n");	/* strcpy: OK (size checked) */
436      else
437        /* Tack \r\n onto the end to fix bug with prompt overwriting last line. */
438        strcpy(buffer + diff, "\r\n");	/* strcpy: OK (size checked) */
439      send_to_char(d->character, "%s", buffer);
440      d->showstr_page++;
441    }
442  }