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 }