objsave.c
1 /* ************************************************************************ 2 * File: objsave.c Part of CircleMUD * 3 * Usage: loading/saving player objects for rent and crash-save * 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 "comm.h" 17 #include "handler.h" 18 #include "db.h" 19 #include "interpreter.h" 20 #include "utils.h" 21 #include "spells.h" 22 23 /* these factors should be unique integers */ 24 #define RENT_FACTOR 1 25 #define CRYO_FACTOR 4 26 27 #define LOC_INVENTORY 0 28 #define MAX_BAG_ROWS 5 29 30 /* external variables */ 31 extern struct player_index_element *player_table; 32 extern int top_of_p_table; 33 extern int rent_file_timeout, crash_file_timeout; 34 extern int free_rent; 35 extern int min_rent_cost; 36 extern int max_obj_save; /* change in config.c */ 37 38 /* Extern functions */ 39 ACMD(do_action); 40 ACMD(do_tell); 41 SPECIAL(receptionist); 42 SPECIAL(cryogenicist); 43 int invalid_class(struct char_data *ch, struct obj_data *obj); 44 45 /* local functions */ 46 void Crash_extract_norent_eq(struct char_data *ch); 47 void auto_equip(struct char_data *ch, struct obj_data *obj, int location); 48 int Crash_offer_rent(struct char_data *ch, struct char_data *recep, int display, int factor); 49 int Crash_report_unrentables(struct char_data *ch, struct char_data *recep, struct obj_data *obj); 50 void Crash_report_rent(struct char_data *ch, struct char_data *recep, struct obj_data *obj, long *cost, long *nitems, int display, int factor); 51 struct obj_data *Obj_from_store(struct obj_file_elem object, int *location); 52 int Obj_to_store(struct obj_data *obj, FILE *fl, int location); 53 void update_obj_file(void); 54 int Crash_write_rentcode(struct char_data *ch, FILE *fl, struct rent_info *rent); 55 int gen_receptionist(struct char_data *ch, struct char_data *recep, int cmd, char *arg, int mode); 56 int Crash_save(struct obj_data *obj, FILE *fp, int location); 57 void Crash_rent_deadline(struct char_data *ch, struct char_data *recep, long cost); 58 void Crash_restore_weight(struct obj_data *obj); 59 void Crash_extract_objs(struct obj_data *obj); 60 int Crash_is_unrentable(struct obj_data *obj); 61 void Crash_extract_norents(struct obj_data *obj); 62 void Crash_extract_expensive(struct obj_data *obj); 63 void Crash_calculate_rent(struct obj_data *obj, int *cost); 64 void Crash_rentsave(struct char_data *ch, int cost); 65 void Crash_cryosave(struct char_data *ch, int cost); 66 67 68 struct obj_data *Obj_from_store(struct obj_file_elem object, int *location) 69 { 70 struct obj_data *obj; 71 obj_rnum itemnum; 72 int j; 73 74 *location = 0; 75 if ((itemnum = real_object(object.item_number)) == NOTHING) 76 return (NULL); 77 78 obj = read_object(itemnum, REAL); 79 #if USE_AUTOEQ 80 *location = object.location; 81 #endif 82 GET_OBJ_VAL(obj, 0) = object.value[0]; 83 GET_OBJ_VAL(obj, 1) = object.value[1]; 84 GET_OBJ_VAL(obj, 2) = object.value[2]; 85 GET_OBJ_VAL(obj, 3) = object.value[3]; 86 GET_OBJ_EXTRA(obj) = object.extra_flags; 87 GET_OBJ_WEIGHT(obj) = object.weight; 88 GET_OBJ_TIMER(obj) = object.timer; 89 GET_OBJ_AFFECT(obj) = object.bitvector; 90 91 for (j = 0; j < MAX_OBJ_AFFECT; j++) 92 obj->affected[j] = object.affected[j]; 93 94 return (obj); 95 } 96 97 98 99 int Obj_to_store(struct obj_data *obj, FILE *fl, int location) 100 { 101 int j; 102 struct obj_file_elem object; 103 104 object.item_number = GET_OBJ_VNUM(obj); 105 #if USE_AUTOEQ 106 object.location = location; 107 #endif 108 object.value[0] = GET_OBJ_VAL(obj, 0); 109 object.value[1] = GET_OBJ_VAL(obj, 1); 110 object.value[2] = GET_OBJ_VAL(obj, 2); 111 object.value[3] = GET_OBJ_VAL(obj, 3); 112 object.extra_flags = GET_OBJ_EXTRA(obj); 113 object.weight = GET_OBJ_WEIGHT(obj); 114 object.timer = GET_OBJ_TIMER(obj); 115 object.bitvector = GET_OBJ_AFFECT(obj); 116 for (j = 0; j < MAX_OBJ_AFFECT; j++) 117 object.affected[j] = obj->affected[j]; 118 119 if (fwrite(&object, sizeof(struct obj_file_elem), 1, fl) < 1) { 120 perror("SYSERR: error writing object in Obj_to_store"); 121 return (0); 122 } 123 return (1); 124 } 125 126 /* 127 * AutoEQ by Burkhard Knopf <burkhard.knopf@informatik.tu-clausthal.de> 128 */ 129 void auto_equip(struct char_data *ch, struct obj_data *obj, int location) 130 { 131 int j; 132 133 /* Lots of checks... */ 134 if (location > 0) { /* Was wearing it. */ 135 switch (j = (location - 1)) { 136 case WEAR_LIGHT: 137 break; 138 case WEAR_FINGER_R: 139 case WEAR_FINGER_L: 140 if (!CAN_WEAR(obj, ITEM_WEAR_FINGER)) /* not fitting :( */ 141 location = LOC_INVENTORY; 142 break; 143 case WEAR_NECK_1: 144 case WEAR_NECK_2: 145 if (!CAN_WEAR(obj, ITEM_WEAR_NECK)) 146 location = LOC_INVENTORY; 147 break; 148 case WEAR_BODY: 149 if (!CAN_WEAR(obj, ITEM_WEAR_BODY)) 150 location = LOC_INVENTORY; 151 break; 152 case WEAR_HEAD: 153 if (!CAN_WEAR(obj, ITEM_WEAR_HEAD)) 154 location = LOC_INVENTORY; 155 break; 156 case WEAR_LEGS: 157 if (!CAN_WEAR(obj, ITEM_WEAR_LEGS)) 158 location = LOC_INVENTORY; 159 break; 160 case WEAR_FEET: 161 if (!CAN_WEAR(obj, ITEM_WEAR_FEET)) 162 location = LOC_INVENTORY; 163 break; 164 case WEAR_HANDS: 165 if (!CAN_WEAR(obj, ITEM_WEAR_HANDS)) 166 location = LOC_INVENTORY; 167 break; 168 case WEAR_ARMS: 169 if (!CAN_WEAR(obj, ITEM_WEAR_ARMS)) 170 location = LOC_INVENTORY; 171 break; 172 case WEAR_SHIELD: 173 if (!CAN_WEAR(obj, ITEM_WEAR_SHIELD)) 174 location = LOC_INVENTORY; 175 break; 176 case WEAR_ABOUT: 177 if (!CAN_WEAR(obj, ITEM_WEAR_ABOUT)) 178 location = LOC_INVENTORY; 179 break; 180 case WEAR_WAIST: 181 if (!CAN_WEAR(obj, ITEM_WEAR_WAIST)) 182 location = LOC_INVENTORY; 183 break; 184 case WEAR_WRIST_R: 185 case WEAR_WRIST_L: 186 if (!CAN_WEAR(obj, ITEM_WEAR_WRIST)) 187 location = LOC_INVENTORY; 188 break; 189 case WEAR_WIELD: 190 if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) 191 location = LOC_INVENTORY; 192 break; 193 case WEAR_HOLD: 194 if (CAN_WEAR(obj, ITEM_WEAR_HOLD)) 195 break; 196 if (IS_WARRIOR(ch) && CAN_WEAR(obj, ITEM_WEAR_WIELD) && GET_OBJ_TYPE(obj) == ITEM_WEAPON) 197 break; 198 location = LOC_INVENTORY; 199 break; 200 default: 201 location = LOC_INVENTORY; 202 } 203 204 if (location > 0) { /* Wearable. */ 205 if (!GET_EQ(ch,j)) { 206 /* 207 * Check the characters's alignment to prevent them from being 208 * zapped through the auto-equipping. 209 */ 210 if (invalid_align(ch, obj) || invalid_class(ch, obj)) 211 location = LOC_INVENTORY; 212 else 213 equip_char(ch, obj, j); 214 } else { /* Oops, saved a player with double equipment? */ 215 mudlog(BRF, LVL_IMMORT, TRUE, "SYSERR: autoeq: '%s' already equipped in position %d.", GET_NAME(ch), location); 216 location = LOC_INVENTORY; 217 } 218 } 219 } 220 if (location <= 0) /* Inventory */ 221 obj_to_char(obj, ch); 222 } 223 224 225 int Crash_delete_file(char *name) 226 { 227 char filename[50]; 228 FILE *fl; 229 230 if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) 231 return (0); 232 if (!(fl = fopen(filename, "rb"))) { 233 if (errno != ENOENT) /* if it fails but NOT because of no file */ 234 log("SYSERR: deleting crash file %s (1): %s", filename, strerror(errno)); 235 return (0); 236 } 237 fclose(fl); 238 239 /* if it fails, NOT because of no file */ 240 if (remove(filename) < 0 && errno != ENOENT) 241 log("SYSERR: deleting crash file %s (2): %s", filename, strerror(errno)); 242 243 return (1); 244 } 245 246 247 int Crash_delete_crashfile(struct char_data *ch) 248 { 249 char filename[MAX_INPUT_LENGTH]; 250 struct rent_info rent; 251 int numread; 252 FILE *fl; 253 254 if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) 255 return (0); 256 if (!(fl = fopen(filename, "rb"))) { 257 if (errno != ENOENT) /* if it fails, NOT because of no file */ 258 log("SYSERR: checking for crash file %s (3): %s", filename, strerror(errno)); 259 return (0); 260 } 261 numread = fread(&rent, sizeof(struct rent_info), 1, fl); 262 fclose(fl); 263 264 if (numread == 0) 265 return (0); 266 267 if (rent.rentcode == RENT_CRASH) 268 Crash_delete_file(GET_NAME(ch)); 269 270 return (1); 271 } 272 273 274 int Crash_clean_file(char *name) 275 { 276 char filename[MAX_STRING_LENGTH]; 277 struct rent_info rent; 278 int numread; 279 FILE *fl; 280 281 if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) 282 return (0); 283 /* 284 * open for write so that permission problems will be flagged now, at boot 285 * time. 286 */ 287 if (!(fl = fopen(filename, "r+b"))) { 288 if (errno != ENOENT) /* if it fails, NOT because of no file */ 289 log("SYSERR: OPENING OBJECT FILE %s (4): %s", filename, strerror(errno)); 290 return (0); 291 } 292 numread = fread(&rent, sizeof(struct rent_info), 1, fl); 293 fclose(fl); 294 295 if (numread == 0) 296 return (0); 297 298 if ((rent.rentcode == RENT_CRASH) || 299 (rent.rentcode == RENT_FORCED) || (rent.rentcode == RENT_TIMEDOUT)) { 300 if (rent.time < time(0) - (crash_file_timeout * SECS_PER_REAL_DAY)) { 301 const char *filetype; 302 303 Crash_delete_file(name); 304 switch (rent.rentcode) { 305 case RENT_CRASH: 306 filetype = "crash"; 307 break; 308 case RENT_FORCED: 309 filetype = "forced rent"; 310 break; 311 case RENT_TIMEDOUT: 312 filetype = "idlesave"; 313 break; 314 default: 315 filetype = "UNKNOWN!"; 316 break; 317 } 318 log(" Deleting %s's %s file.", name, filetype); 319 return (1); 320 } 321 /* Must retrieve rented items w/in 30 days */ 322 } else if (rent.rentcode == RENT_RENTED) 323 if (rent.time < time(0) - (rent_file_timeout * SECS_PER_REAL_DAY)) { 324 Crash_delete_file(name); 325 log(" Deleting %s's rent file.", name); 326 return (1); 327 } 328 return (0); 329 } 330 331 332 void update_obj_file(void) 333 { 334 int i; 335 336 for (i = 0; i <= top_of_p_table; i++) 337 if (*player_table[i].name) 338 Crash_clean_file(player_table[i].name); 339 } 340 341 342 void Crash_listrent(struct char_data *ch, char *name) 343 { 344 FILE *fl; 345 char filename[MAX_INPUT_LENGTH]; 346 struct obj_file_elem object; 347 struct obj_data *obj; 348 struct rent_info rent; 349 int numread; 350 351 if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) 352 return; 353 if (!(fl = fopen(filename, "rb"))) { 354 send_to_char(ch, "%s has no rent file.\r\n", name); 355 return; 356 } 357 numread = fread(&rent, sizeof(struct rent_info), 1, fl); 358 359 /* Oops, can't get the data, punt. */ 360 if (numread == 0) { 361 send_to_char(ch, "Error reading rent information.\r\n"); 362 fclose(fl); 363 return; 364 } 365 366 send_to_char(ch, "%s\r\n", filename); 367 switch (rent.rentcode) { 368 case RENT_RENTED: 369 send_to_char(ch, "Rent\r\n"); 370 break; 371 case RENT_CRASH: 372 send_to_char(ch, "Crash\r\n"); 373 break; 374 case RENT_CRYO: 375 send_to_char(ch, "Cryo\r\n"); 376 break; 377 case RENT_TIMEDOUT: 378 case RENT_FORCED: 379 send_to_char(ch, "TimedOut\r\n"); 380 break; 381 default: 382 send_to_char(ch, "Undef\r\n"); 383 break; 384 } 385 while (!feof(fl)) { 386 fread(&object, sizeof(struct obj_file_elem), 1, fl); 387 if (ferror(fl)) { 388 fclose(fl); 389 return; 390 } 391 if (!feof(fl)) 392 if (real_object(object.item_number) != NOTHING) { 393 obj = read_object(object.item_number, VIRTUAL); 394 #if USE_AUTOEQ 395 send_to_char(ch, " [%5d] (%5dau) <%2d> %-20s\r\n", 396 object.item_number, GET_OBJ_RENT(obj), 397 object.location, obj->short_description); 398 #else 399 send_to_char(ch, " [%5d] (%5dau) %-20s\r\n", 400 object.item_number, GET_OBJ_RENT(obj), 401 obj->short_description); 402 #endif 403 extract_obj(obj); 404 } 405 } 406 fclose(fl); 407 } 408 409 410 int Crash_write_rentcode(struct char_data *ch, FILE *fl, struct rent_info *rent) 411 { 412 if (fwrite(rent, sizeof(struct rent_info), 1, fl) < 1) { 413 perror("SYSERR: writing rent code"); 414 return (0); 415 } 416 return (1); 417 } 418 419 420 /* 421 * Return values: 422 * 0 - successful load, keep char in rent room. 423 * 1 - load failure or load of crash items -- put char in temple. 424 * 2 - rented equipment lost (no $) 425 */ 426 int Crash_load(struct char_data *ch) 427 { 428 FILE *fl; 429 char filename[MAX_STRING_LENGTH]; 430 struct obj_file_elem object; 431 struct rent_info rent; 432 int cost, orig_rent_code, num_objs = 0, j; 433 float num_of_days; 434 /* AutoEQ addition. */ 435 struct obj_data *obj, *obj2, *cont_row[MAX_BAG_ROWS]; 436 int location; 437 438 /* Empty all of the container lists (you never know ...) */ 439 for (j = 0; j < MAX_BAG_ROWS; j++) 440 cont_row[j] = NULL; 441 442 if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) 443 return (1); 444 if (!(fl = fopen(filename, "r+b"))) { 445 if (errno != ENOENT) { /* if it fails, NOT because of no file */ 446 log("SYSERR: READING OBJECT FILE %s (5): %s", filename, strerror(errno)); 447 send_to_char(ch, 448 "\r\n********************* NOTICE *********************\r\n" 449 "There was a problem loading your objects from disk.\r\n" 450 "Contact a God for assistance.\r\n"); 451 } 452 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s entering game with no equipment.", GET_NAME(ch)); 453 return (1); 454 } 455 if (!feof(fl)) 456 fread(&rent, sizeof(struct rent_info), 1, fl); 457 else { 458 log("SYSERR: Crash_load: %s's rent file was empty!", GET_NAME(ch)); 459 return (1); 460 } 461 462 if (rent.rentcode == RENT_RENTED || rent.rentcode == RENT_TIMEDOUT) { 463 num_of_days = (float) (time(0) - rent.time) / SECS_PER_REAL_DAY; 464 cost = (int) (rent.net_cost_per_diem * num_of_days); 465 if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) { 466 fclose(fl); 467 mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s entering game, rented equipment lost (no $).", GET_NAME(ch)); 468 Crash_crashsave(ch); 469 return (2); 470 } else { 471 GET_BANK_GOLD(ch) -= MAX(cost - GET_GOLD(ch), 0); 472 GET_GOLD(ch) = MAX(GET_GOLD(ch) - cost, 0); 473 save_char(ch); 474 } 475 } 476 switch (orig_rent_code = rent.rentcode) { 477 case RENT_RENTED: 478 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s un-renting and entering game.", GET_NAME(ch)); 479 break; 480 case RENT_CRASH: 481 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving crash-saved items and entering game.", GET_NAME(ch)); 482 break; 483 case RENT_CRYO: 484 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s un-cryo'ing and entering game.", GET_NAME(ch)); 485 break; 486 case RENT_FORCED: 487 case RENT_TIMEDOUT: 488 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving force-saved items and entering game.", GET_NAME(ch)); 489 break; 490 default: 491 mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, 492 "SYSERR: %s entering game with undefined rent code %d.", GET_NAME(ch), rent.rentcode); 493 break; 494 } 495 496 while (!feof(fl)) { 497 fread(&object, sizeof(struct obj_file_elem), 1, fl); 498 if (ferror(fl)) { 499 perror("SYSERR: Reading crash file: Crash_load"); 500 fclose(fl); 501 return (1); 502 } 503 if (feof(fl)) 504 break; 505 ++num_objs; 506 if ((obj = Obj_from_store(object, &location)) == NULL) 507 continue; 508 509 auto_equip(ch, obj, location); 510 /* 511 * What to do with a new loaded item: 512 * 513 * If there's a list with location less than 1 below this, then its 514 * container has disappeared from the file so we put the list back into 515 * the character's inventory. (Equipped items are 0 here.) 516 * 517 * If there's a list of contents with location of 1 below this, then we 518 * check if it is a container: 519 * - Yes: Get it from the character, fill it, and give it back so we 520 * have the correct weight. 521 * - No: The container is missing so we put everything back into the 522 * character's inventory. 523 * 524 * For items with negative location, we check if there is already a list 525 * of contents with the same location. If so, we put it there and if not, 526 * we start a new list. 527 * 528 * Since location for contents is < 0, the list indices are switched to 529 * non-negative. 530 * 531 * This looks ugly, but it works. 532 */ 533 if (location > 0) { /* Equipped */ 534 for (j = MAX_BAG_ROWS - 1; j > 0; j--) { 535 if (cont_row[j]) { /* No container, back to inventory. */ 536 for (; cont_row[j]; cont_row[j] = obj2) { 537 obj2 = cont_row[j]->next_content; 538 obj_to_char(cont_row[j], ch); 539 } 540 cont_row[j] = NULL; 541 } 542 } 543 if (cont_row[0]) { /* Content list existing. */ 544 if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER) { 545 /* Remove object, fill it, equip again. */ 546 obj = unequip_char(ch, location - 1); 547 obj->contains = NULL; /* Should be NULL anyway, but just in case. */ 548 for (; cont_row[0]; cont_row[0] = obj2) { 549 obj2 = cont_row[0]->next_content; 550 obj_to_obj(cont_row[0], obj); 551 } 552 equip_char(ch, obj, location - 1); 553 } else { /* Object isn't container, empty the list. */ 554 for (; cont_row[0]; cont_row[0] = obj2) { 555 obj2 = cont_row[0]->next_content; 556 obj_to_char(cont_row[0], ch); 557 } 558 cont_row[0] = NULL; 559 } 560 } 561 } else { /* location <= 0 */ 562 for (j = MAX_BAG_ROWS - 1; j > -location; j--) { 563 if (cont_row[j]) { /* No container, back to inventory. */ 564 for (; cont_row[j]; cont_row[j] = obj2) { 565 obj2 = cont_row[j]->next_content; 566 obj_to_char(cont_row[j], ch); 567 } 568 cont_row[j] = NULL; 569 } 570 } 571 if (j == -location && cont_row[j]) { /* Content list exists. */ 572 if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER) { 573 /* Take the item, fill it, and give it back. */ 574 obj_from_char(obj); 575 obj->contains = NULL; 576 for (; cont_row[j]; cont_row[j] = obj2) { 577 obj2 = cont_row[j]->next_content; 578 obj_to_obj(cont_row[j], obj); 579 } 580 obj_to_char(obj, ch); /* Add to inventory first. */ 581 } else { /* Object isn't container, empty content list. */ 582 for (; cont_row[j]; cont_row[j] = obj2) { 583 obj2 = cont_row[j]->next_content; 584 obj_to_char(cont_row[j], ch); 585 } 586 cont_row[j] = NULL; 587 } 588 } 589 if (location < 0 && location >= -MAX_BAG_ROWS) { 590 /* 591 * Let the object be part of the content list but put it at the 592 * list's end. Thus having the items in the same order as before 593 * the character rented. 594 */ 595 obj_from_char(obj); 596 if ((obj2 = cont_row[-location - 1]) != NULL) { 597 while (obj2->next_content) 598 obj2 = obj2->next_content; 599 obj2->next_content = obj; 600 } else 601 cont_row[-location - 1] = obj; 602 } 603 } 604 } 605 606 /* Little hoarding check. -gg 3/1/98 */ 607 mudlog(NRM, MAX(GET_INVIS_LEV(ch), LVL_GOD), TRUE, "%s (level %d) has %d object%s (max %d).", 608 GET_NAME(ch), GET_LEVEL(ch), num_objs, num_objs != 1 ? "s" : "", max_obj_save); 609 610 /* turn this into a crash file by re-writing the control block */ 611 rent.rentcode = RENT_CRASH; 612 rent.time = time(0); 613 rewind(fl); 614 Crash_write_rentcode(ch, fl, &rent); 615 616 fclose(fl); 617 618 if ((orig_rent_code == RENT_RENTED) || (orig_rent_code == RENT_CRYO)) 619 return (0); 620 else 621 return (1); 622 } 623 624 625 626 int Crash_save(struct obj_data *obj, FILE *fp, int location) 627 { 628 struct obj_data *tmp; 629 int result; 630 631 if (obj) { 632 Crash_save(obj->next_content, fp, location); 633 Crash_save(obj->contains, fp, MIN(0, location) - 1); 634 result = Obj_to_store(obj, fp, location); 635 636 for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj) 637 GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); 638 639 if (!result) 640 return (0); 641 } 642 return (TRUE); 643 } 644 645 646 void Crash_restore_weight(struct obj_data *obj) 647 { 648 if (obj) { 649 Crash_restore_weight(obj->contains); 650 Crash_restore_weight(obj->next_content); 651 if (obj->in_obj) 652 GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj); 653 } 654 } 655 656 /* 657 * Get !RENT items from equipment to inventory and 658 * extract !RENT out of worn containers. 659 */ 660 void Crash_extract_norent_eq(struct char_data *ch) 661 { 662 int j; 663 664 for (j = 0; j < NUM_WEARS; j++) { 665 if (GET_EQ(ch, j) == NULL) 666 continue; 667 668 if (Crash_is_unrentable(GET_EQ(ch, j))) 669 obj_to_char(unequip_char(ch, j), ch); 670 else 671 Crash_extract_norents(GET_EQ(ch, j)); 672 } 673 } 674 675 void Crash_extract_objs(struct obj_data *obj) 676 { 677 if (obj) { 678 Crash_extract_objs(obj->contains); 679 Crash_extract_objs(obj->next_content); 680 extract_obj(obj); 681 } 682 } 683 684 685 int Crash_is_unrentable(struct obj_data *obj) 686 { 687 if (!obj) 688 return (0); 689 690 if (OBJ_FLAGGED(obj, ITEM_NORENT) || GET_OBJ_RENT(obj) < 0 || 691 GET_OBJ_RNUM(obj) == NOTHING || GET_OBJ_TYPE(obj) == ITEM_KEY) 692 return (1); 693 694 return (0); 695 } 696 697 698 void Crash_extract_norents(struct obj_data *obj) 699 { 700 if (obj) { 701 Crash_extract_norents(obj->contains); 702 Crash_extract_norents(obj->next_content); 703 if (Crash_is_unrentable(obj)) 704 extract_obj(obj); 705 } 706 } 707 708 709 void Crash_extract_expensive(struct obj_data *obj) 710 { 711 struct obj_data *tobj, *max; 712 713 max = obj; 714 for (tobj = obj; tobj; tobj = tobj->next_content) 715 if (GET_OBJ_RENT(tobj) > GET_OBJ_RENT(max)) 716 max = tobj; 717 extract_obj(max); 718 } 719 720 721 722 void Crash_calculate_rent(struct obj_data *obj, int *cost) 723 { 724 if (obj) { 725 *cost += MAX(0, GET_OBJ_RENT(obj)); 726 Crash_calculate_rent(obj->contains, cost); 727 Crash_calculate_rent(obj->next_content, cost); 728 } 729 } 730 731 732 void Crash_crashsave(struct char_data *ch) 733 { 734 char buf[MAX_INPUT_LENGTH]; 735 struct rent_info rent; 736 int j; 737 FILE *fp; 738 739 if (IS_NPC(ch)) 740 return; 741 742 if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) 743 return; 744 if (!(fp = fopen(buf, "wb"))) 745 return; 746 747 rent.rentcode = RENT_CRASH; 748 rent.time = time(0); 749 if (!Crash_write_rentcode(ch, fp, &rent)) { 750 fclose(fp); 751 return; 752 } 753 754 for (j = 0; j < NUM_WEARS; j++) 755 if (GET_EQ(ch, j)) { 756 if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) { 757 fclose(fp); 758 return; 759 } 760 Crash_restore_weight(GET_EQ(ch, j)); 761 } 762 763 if (!Crash_save(ch->carrying, fp, 0)) { 764 fclose(fp); 765 return; 766 } 767 Crash_restore_weight(ch->carrying); 768 769 fclose(fp); 770 REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH); 771 } 772 773 774 void Crash_idlesave(struct char_data *ch) 775 { 776 char buf[MAX_INPUT_LENGTH]; 777 struct rent_info rent; 778 int j; 779 int cost, cost_eq; 780 FILE *fp; 781 782 if (IS_NPC(ch)) 783 return; 784 785 if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) 786 return; 787 if (!(fp = fopen(buf, "wb"))) 788 return; 789 790 Crash_extract_norent_eq(ch); 791 Crash_extract_norents(ch->carrying); 792 793 cost = 0; 794 Crash_calculate_rent(ch->carrying, &cost); 795 796 cost_eq = 0; 797 for (j = 0; j < NUM_WEARS; j++) 798 Crash_calculate_rent(GET_EQ(ch, j), &cost_eq); 799 800 cost += cost_eq; 801 cost *= 2; /* forcerent cost is 2x normal rent */ 802 803 if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) { 804 for (j = 0; j < NUM_WEARS; j++) /* Unequip players with low gold. */ 805 if (GET_EQ(ch, j)) 806 obj_to_char(unequip_char(ch, j), ch); 807 808 while ((cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) && ch->carrying) { 809 Crash_extract_expensive(ch->carrying); 810 cost = 0; 811 Crash_calculate_rent(ch->carrying, &cost); 812 cost *= 2; 813 } 814 } 815 816 if (ch->carrying == NULL) { 817 for (j = 0; j < NUM_WEARS && GET_EQ(ch, j) == NULL; j++) /* Nothing */ ; 818 if (j == NUM_WEARS) { /* No equipment or inventory. */ 819 fclose(fp); 820 Crash_delete_file(GET_NAME(ch)); 821 return; 822 } 823 } 824 rent.net_cost_per_diem = cost; 825 826 rent.rentcode = RENT_TIMEDOUT; 827 rent.time = time(0); 828 rent.gold = GET_GOLD(ch); 829 rent.account = GET_BANK_GOLD(ch); 830 if (!Crash_write_rentcode(ch, fp, &rent)) { 831 fclose(fp); 832 return; 833 } 834 for (j = 0; j < NUM_WEARS; j++) { 835 if (GET_EQ(ch, j)) { 836 if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) { 837 fclose(fp); 838 return; 839 } 840 Crash_restore_weight(GET_EQ(ch, j)); 841 Crash_extract_objs(GET_EQ(ch, j)); 842 } 843 } 844 if (!Crash_save(ch->carrying, fp, 0)) { 845 fclose(fp); 846 return; 847 } 848 fclose(fp); 849 850 Crash_extract_objs(ch->carrying); 851 } 852 853 854 void Crash_rentsave(struct char_data *ch, int cost) 855 { 856 char buf[MAX_INPUT_LENGTH]; 857 struct rent_info rent; 858 int j; 859 FILE *fp; 860 861 if (IS_NPC(ch)) 862 return; 863 864 if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) 865 return; 866 if (!(fp = fopen(buf, "wb"))) 867 return; 868 869 Crash_extract_norent_eq(ch); 870 Crash_extract_norents(ch->carrying); 871 872 rent.net_cost_per_diem = cost; 873 rent.rentcode = RENT_RENTED; 874 rent.time = time(0); 875 rent.gold = GET_GOLD(ch); 876 rent.account = GET_BANK_GOLD(ch); 877 if (!Crash_write_rentcode(ch, fp, &rent)) { 878 fclose(fp); 879 return; 880 } 881 for (j = 0; j < NUM_WEARS; j++) 882 if (GET_EQ(ch, j)) { 883 if (!Crash_save(GET_EQ(ch,j), fp, j + 1)) { 884 fclose(fp); 885 return; 886 } 887 Crash_restore_weight(GET_EQ(ch, j)); 888 Crash_extract_objs(GET_EQ(ch, j)); 889 } 890 if (!Crash_save(ch->carrying, fp, 0)) { 891 fclose(fp); 892 return; 893 } 894 fclose(fp); 895 896 Crash_extract_objs(ch->carrying); 897 } 898 899 900 void Crash_cryosave(struct char_data *ch, int cost) 901 { 902 char buf[MAX_INPUT_LENGTH]; 903 struct rent_info rent; 904 int j; 905 FILE *fp; 906 907 if (IS_NPC(ch)) 908 return; 909 910 if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) 911 return; 912 if (!(fp = fopen(buf, "wb"))) 913 return; 914 915 Crash_extract_norent_eq(ch); 916 Crash_extract_norents(ch->carrying); 917 918 GET_GOLD(ch) = MAX(0, GET_GOLD(ch) - cost); 919 920 rent.rentcode = RENT_CRYO; 921 rent.time = time(0); 922 rent.gold = GET_GOLD(ch); 923 rent.account = GET_BANK_GOLD(ch); 924 rent.net_cost_per_diem = 0; 925 if (!Crash_write_rentcode(ch, fp, &rent)) { 926 fclose(fp); 927 return; 928 } 929 for (j = 0; j < NUM_WEARS; j++) 930 if (GET_EQ(ch, j)) { 931 if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) { 932 fclose(fp); 933 return; 934 } 935 Crash_restore_weight(GET_EQ(ch, j)); 936 Crash_extract_objs(GET_EQ(ch, j)); 937 } 938 if (!Crash_save(ch->carrying, fp, 0)) { 939 fclose(fp); 940 return; 941 } 942 fclose(fp); 943 944 Crash_extract_objs(ch->carrying); 945 SET_BIT(PLR_FLAGS(ch), PLR_CRYO); 946 } 947 948 949 /* ************************************************************************ 950 * Routines used for the receptionist * 951 ************************************************************************* */ 952 953 void Crash_rent_deadline(struct char_data *ch, struct char_data *recep, 954 long cost) 955 { 956 char buf[256]; 957 long rent_deadline; 958 959 if (!cost) 960 return; 961 962 rent_deadline = ((GET_GOLD(ch) + GET_BANK_GOLD(ch)) / cost); 963 snprintf(buf, sizeof(buf), "$n tells you, 'You can rent for %ld day%s with the gold you have\r\n" 964 "on hand and in the bank.'\r\n", rent_deadline, rent_deadline != 1 ? "s" : ""); 965 act(buf, FALSE, recep, 0, ch, TO_VICT); 966 } 967 968 int Crash_report_unrentables(struct char_data *ch, struct char_data *recep, 969 struct obj_data *obj) 970 { 971 int has_norents = 0; 972 973 if (obj) { 974 if (Crash_is_unrentable(obj)) { 975 char buf[128]; 976 977 has_norents = 1; 978 snprintf(buf, sizeof(buf), "$n tells you, 'You cannot store %s.'", OBJS(obj, ch)); 979 act(buf, FALSE, recep, 0, ch, TO_VICT); 980 } 981 has_norents += Crash_report_unrentables(ch, recep, obj->contains); 982 has_norents += Crash_report_unrentables(ch, recep, obj->next_content); 983 } 984 return (has_norents); 985 } 986 987 988 989 void Crash_report_rent(struct char_data *ch, struct char_data *recep, 990 struct obj_data *obj, long *cost, long *nitems, int display, int factor) 991 { 992 if (obj) { 993 if (!Crash_is_unrentable(obj)) { 994 (*nitems)++; 995 *cost += MAX(0, (GET_OBJ_RENT(obj) * factor)); 996 if (display) { 997 char buf[256]; 998 999 snprintf(buf, sizeof(buf), "$n tells you, '%5d coins for %s..'", GET_OBJ_RENT(obj) * factor, OBJS(obj, ch)); 1000 act(buf, FALSE, recep, 0, ch, TO_VICT); 1001 } 1002 } 1003 Crash_report_rent(ch, recep, obj->contains, cost, nitems, display, factor); 1004 Crash_report_rent(ch, recep, obj->next_content, cost, nitems, display, factor); 1005 } 1006 } 1007 1008 1009 1010 int Crash_offer_rent(struct char_data *ch, struct char_data *recep, 1011 int display, int factor) 1012 { 1013 int i; 1014 long totalcost = 0, numitems = 0, norent; 1015 1016 norent = Crash_report_unrentables(ch, recep, ch->carrying); 1017 for (i = 0; i < NUM_WEARS; i++) 1018 norent += Crash_report_unrentables(ch, recep, GET_EQ(ch, i)); 1019 1020 if (norent) 1021 return (0); 1022 1023 totalcost = min_rent_cost * factor; 1024 1025 Crash_report_rent(ch, recep, ch->carrying, &totalcost, &numitems, display, factor); 1026 1027 for (i = 0; i < NUM_WEARS; i++) 1028 Crash_report_rent(ch, recep, GET_EQ(ch, i), &totalcost, &numitems, display, factor); 1029 1030 if (!numitems) { 1031 act("$n tells you, 'But you are not carrying anything! Just quit!'", 1032 FALSE, recep, 0, ch, TO_VICT); 1033 return (0); 1034 } 1035 if (numitems > max_obj_save) { 1036 char buf[256]; 1037 1038 snprintf(buf, sizeof(buf), "$n tells you, 'Sorry, but I cannot store more than %d items.'", max_obj_save); 1039 act(buf, FALSE, recep, 0, ch, TO_VICT); 1040 return (0); 1041 } 1042 if (display) { 1043 char buf[256]; 1044 1045 snprintf(buf, sizeof(buf), "$n tells you, 'Plus, my %d coin fee..'", min_rent_cost * factor); 1046 act(buf, FALSE, recep, 0, ch, TO_VICT); 1047 1048 snprintf(buf, sizeof(buf), "$n tells you, 'For a total of %ld coins%s.'", totalcost, factor == RENT_FACTOR ? " per day" : ""); 1049 act(buf, FALSE, recep, 0, ch, TO_VICT); 1050 1051 if (totalcost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) { 1052 act("$n tells you, '...which I see you can't afford.'", FALSE, recep, 0, ch, TO_VICT); 1053 return (0); 1054 } else if (factor == RENT_FACTOR) 1055 Crash_rent_deadline(ch, recep, totalcost); 1056 } 1057 return (totalcost); 1058 } 1059 1060 1061 1062 int gen_receptionist(struct char_data *ch, struct char_data *recep, 1063 int cmd, char *arg, int mode) 1064 { 1065 int cost; 1066 const char *action_table[] = { "smile", "dance", "sigh", "blush", "burp", 1067 "cough", "fart", "twiddle", "yawn" }; 1068 1069 if (!cmd && !rand_number(0, 5)) { 1070 do_action(recep, NULL, find_command(action_table[rand_number(0, 8)]), 0); 1071 return (FALSE); 1072 } 1073 1074 if (!ch->desc || IS_NPC(ch)) 1075 return (FALSE); 1076 1077 if (!CMD_IS("offer") && !CMD_IS("rent")) 1078 return (FALSE); 1079 1080 if (!AWAKE(recep)) { 1081 send_to_char(ch, "%s is unable to talk to you...\r\n", HSSH(recep)); 1082 return (TRUE); 1083 } 1084 1085 if (!CAN_SEE(recep, ch)) { 1086 act("$n says, 'I don't deal with people I can't see!'", FALSE, recep, 0, 0, TO_ROOM); 1087 return (TRUE); 1088 } 1089 1090 if (free_rent) { 1091 act("$n tells you, 'Rent is free here. Just quit, and your objects will be saved!'", 1092 FALSE, recep, 0, ch, TO_VICT); 1093 return (1); 1094 } 1095 1096 if (CMD_IS("rent")) { 1097 char buf[128]; 1098 1099 if (!(cost = Crash_offer_rent(ch, recep, FALSE, mode))) 1100 return (TRUE); 1101 if (mode == RENT_FACTOR) 1102 snprintf(buf, sizeof(buf), "$n tells you, 'Rent will cost you %d gold coins per day.'", cost); 1103 else if (mode == CRYO_FACTOR) 1104 snprintf(buf, sizeof(buf), "$n tells you, 'It will cost you %d gold coins to be frozen.'", cost); 1105 act(buf, FALSE, recep, 0, ch, TO_VICT); 1106 1107 if (cost > GET_GOLD(ch) + GET_BANK_GOLD(ch)) { 1108 act("$n tells you, '...which I see you can't afford.'", 1109 FALSE, recep, 0, ch, TO_VICT); 1110 return (TRUE); 1111 } 1112 if (cost && (mode == RENT_FACTOR)) 1113 Crash_rent_deadline(ch, recep, cost); 1114 1115 if (mode == RENT_FACTOR) { 1116 act("$n stores your belongings and helps you into your private chamber.", FALSE, recep, 0, ch, TO_VICT); 1117 Crash_rentsave(ch, cost); 1118 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s has rented (%d/day, %d tot.)", 1119 GET_NAME(ch), cost, GET_GOLD(ch) + GET_BANK_GOLD(ch)); 1120 } else { /* cryo */ 1121 act("$n stores your belongings and helps you into your private chamber.\r\n" 1122 "A white mist appears in the room, chilling you to the bone...\r\n" 1123 "You begin to lose consciousness...", 1124 FALSE, recep, 0, ch, TO_VICT); 1125 Crash_cryosave(ch, cost); 1126 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s has cryo-rented.", GET_NAME(ch)); 1127 SET_BIT(PLR_FLAGS(ch), PLR_CRYO); 1128 } 1129 1130 act("$n helps $N into $S private chamber.", FALSE, recep, 0, ch, TO_NOTVICT); 1131 1132 GET_LOADROOM(ch) = GET_ROOM_VNUM(IN_ROOM(ch)); 1133 extract_char(ch); /* It saves. */ 1134 } else { 1135 Crash_offer_rent(ch, recep, TRUE, mode); 1136 act("$N gives $n an offer.", FALSE, ch, 0, recep, TO_ROOM); 1137 } 1138 return (TRUE); 1139 } 1140 1141 1142 SPECIAL(receptionist) 1143 { 1144 return (gen_receptionist(ch, (struct char_data *)me, cmd, argument, RENT_FACTOR)); 1145 } 1146 1147 1148 SPECIAL(cryogenicist) 1149 { 1150 return (gen_receptionist(ch, (struct char_data *)me, cmd, argument, CRYO_FACTOR)); 1151 } 1152 1153 1154 void Crash_save_all(void) 1155 { 1156 struct descriptor_data *d; 1157 for (d = descriptor_list; d; d = d->next) { 1158 if ((STATE(d) == CON_PLAYING) && !IS_NPC(d->character)) { 1159 if (PLR_FLAGGED(d->character, PLR_CRASH)) { 1160 Crash_crashsave(d->character); 1161 save_char(d->character); 1162 REMOVE_BIT(PLR_FLAGS(d->character), PLR_CRASH); 1163 } 1164 } 1165 } 1166 }