/************************************************************************** * File: objsave.c Part of tbaMUD * * Usage: loading/saving player objects for rent and crash-save * * * * All rights reserved. See license for complete information. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * **************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "spells.h" #include "act.h" #include "class.h" #include "config.h" #include "modify.h" #include "genolc.h" /* for strip_cr and sprintascii */ /* these factors should be unique integers */ #define RENT_FACTOR 1 #define CRYO_FACTOR 4 #define LOC_INVENTORY 0 #define MAX_BAG_ROWS 5 /* local functions */ static int Crash_save(struct obj_data *obj, FILE *fp, int location); static void auto_equip(struct char_data *ch, struct obj_data *obj, int location); static void Crash_restore_weight(struct obj_data *obj); static int Crash_load_objs(struct char_data *ch); static int handle_obj(struct obj_data *obj, struct char_data *ch, int locate, struct obj_data **cont_rows); static int objsave_write_rentcode(FILE *fl, int rentcode, int cost_per_day, struct char_data *ch); /* Writes one object record to FILE. Old name: Obj_to_store(). * Updated to save all NUM_OBJ_VAL_POSITIONS values instead of only 4. */ int objsave_save_obj_record(struct obj_data *obj, FILE *fp, int locate) { int i; char buf1[MAX_STRING_LENGTH + 1]; struct obj_data *temp = NULL; /* Build a prototype baseline to diff against so we only emit changed fields */ if (GET_OBJ_VNUM(obj) != NOTHING) temp = read_object(GET_OBJ_VNUM(obj), VIRTUAL); else { temp = create_obj(); temp->item_number = NOWHERE; } if (obj->main_description) { strcpy(buf1, obj->main_description); strip_cr(buf1); } else *buf1 = 0; /* Header and placement */ fprintf(fp, "#%d\n", GET_OBJ_VNUM(obj)); /* Top-level worn slots are positive (1..NUM_WEARS); inventory is 0. * Children use negative numbers from Crash_save recursion (…,-1,-2,…) — we map that to Nest. */ if (locate > 0) fprintf(fp, "Loc : %d\n", locate); if (locate < 0) { int nest = -locate; /* e.g. -1 => Nest:1, -2 => Nest:2, etc. */ fprintf(fp, "Nest: %d\n", nest); } else { fprintf(fp, "Nest: %d\n", 0); /* top-level object (inventory or worn) */ } /* Save all object values (diffed against proto) */ { bool diff = FALSE; for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { if (GET_OBJ_VAL(obj, i) != GET_OBJ_VAL(temp, i)) { diff = TRUE; break; } } if (diff) { fprintf(fp, "Vals:"); for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) fprintf(fp, " %d", GET_OBJ_VAL(obj, i)); fprintf(fp, "\n"); } } /* Extra flags (array words) */ if (GET_OBJ_EXTRA(obj) != GET_OBJ_EXTRA(temp)) fprintf(fp, "Flag: %d %d %d %d\n", GET_OBJ_EXTRA(obj)[0], GET_OBJ_EXTRA(obj)[1], GET_OBJ_EXTRA(obj)[2], GET_OBJ_EXTRA(obj)[3]); /* Names/descriptions */ if (obj->name && (!temp->name || strcmp(obj->name, temp->name))) fprintf(fp, "Name: %s\n", obj->name); if (obj->short_description && (!temp->short_description || strcmp(obj->short_description, temp->short_description))) fprintf(fp, "Shrt: %s\n", obj->short_description); if (obj->description && (!temp->description || strcmp(obj->description, temp->description))) fprintf(fp, "Desc: %s\n", obj->description); if (obj->main_description && (!temp->main_description || strcmp(obj->main_description, temp->main_description))) fprintf(fp, "ADes:\n%s~\n", buf1); /* Core fields */ if (GET_OBJ_TYPE(obj) != GET_OBJ_TYPE(temp)) fprintf(fp, "Type: %d\n", GET_OBJ_TYPE(obj)); if (GET_OBJ_WEIGHT(obj) != GET_OBJ_WEIGHT(temp)) fprintf(fp, "Wght: %d\n", GET_OBJ_WEIGHT(obj)); if (GET_OBJ_COST(obj) != GET_OBJ_COST(temp)) fprintf(fp, "Cost: %d\n", GET_OBJ_COST(obj)); if (GET_OBJ_RENT(obj) != GET_OBJ_RENT(temp)) fprintf(fp, "Rent: %d\n", GET_OBJ_RENT(obj)); /* Permanent affects (array words) */ if (GET_OBJ_AFFECT(obj)[0] != GET_OBJ_AFFECT(temp)[0] || GET_OBJ_AFFECT(obj)[1] != GET_OBJ_AFFECT(temp)[1] || GET_OBJ_AFFECT(obj)[2] != GET_OBJ_AFFECT(temp)[2] || GET_OBJ_AFFECT(obj)[3] != GET_OBJ_AFFECT(temp)[3]) fprintf(fp, "Perm: %d %d %d %d\n", GET_OBJ_AFFECT(obj)[0], GET_OBJ_AFFECT(obj)[1], GET_OBJ_AFFECT(obj)[2], GET_OBJ_AFFECT(obj)[3]); /* Wear flags (array words) */ if (GET_OBJ_WEAR(obj)[0] != GET_OBJ_WEAR(temp)[0] || GET_OBJ_WEAR(obj)[1] != GET_OBJ_WEAR(temp)[1] || GET_OBJ_WEAR(obj)[2] != GET_OBJ_WEAR(temp)[2] || GET_OBJ_WEAR(obj)[3] != GET_OBJ_WEAR(temp)[3]) fprintf(fp, "Wear: %d %d %d %d\n", GET_OBJ_WEAR(obj)[0], GET_OBJ_WEAR(obj)[1], GET_OBJ_WEAR(obj)[2], GET_OBJ_WEAR(obj)[3]); /* (If you also persist applies, extra descs, scripts, etc., keep that code here unchanged) */ return 1; } #undef TEST_OBJS #undef TEST_OBJN /* AutoEQ by Burkhard Knopf. */ static void auto_equip(struct char_data *ch, struct obj_data *obj, int location) { int j; /* Lots of checks... */ if (location > 0) { /* Was wearing it. */ switch (j = (location - 1)) { case WEAR_LIGHT: break; case WEAR_FINGER_R: case WEAR_FINGER_L: if (!CAN_WEAR(obj, ITEM_WEAR_FINGER)) /* not fitting :( */ location = LOC_INVENTORY; break; case WEAR_NECK_1: case WEAR_NECK_2: if (!CAN_WEAR(obj, ITEM_WEAR_NECK)) location = LOC_INVENTORY; break; case WEAR_BACK: if (!CAN_WEAR(obj, ITEM_WEAR_BACK)) location = LOC_INVENTORY; break; case WEAR_BODY: if (!CAN_WEAR(obj, ITEM_WEAR_BODY)) location = LOC_INVENTORY; break; case WEAR_HEAD: if (!CAN_WEAR(obj, ITEM_WEAR_HEAD)) location = LOC_INVENTORY; break; case WEAR_LEGS: if (!CAN_WEAR(obj, ITEM_WEAR_LEGS)) location = LOC_INVENTORY; break; case WEAR_FEET: if (!CAN_WEAR(obj, ITEM_WEAR_FEET)) location = LOC_INVENTORY; break; case WEAR_HANDS: if (!CAN_WEAR(obj, ITEM_WEAR_HANDS)) location = LOC_INVENTORY; break; case WEAR_ARMS: if (!CAN_WEAR(obj, ITEM_WEAR_ARMS)) location = LOC_INVENTORY; break; case WEAR_SHIELD: if (!CAN_WEAR(obj, ITEM_WEAR_SHIELD)) location = LOC_INVENTORY; break; case WEAR_ABOUT: if (!CAN_WEAR(obj, ITEM_WEAR_ABOUT)) location = LOC_INVENTORY; break; case WEAR_WAIST: if (!CAN_WEAR(obj, ITEM_WEAR_WAIST)) location = LOC_INVENTORY; break; case WEAR_WRIST_R: case WEAR_WRIST_L: if (!CAN_WEAR(obj, ITEM_WEAR_WRIST)) location = LOC_INVENTORY; break; case WEAR_WIELD: if (!CAN_WEAR(obj, ITEM_WEAR_WIELD)) location = LOC_INVENTORY; break; case WEAR_HOLD: if (CAN_WEAR(obj, ITEM_WEAR_HOLD)) break; if (IS_FIGHTER(ch) && CAN_WEAR(obj, ITEM_WEAR_WIELD) && GET_OBJ_TYPE(obj) == ITEM_WEAPON) break; location = LOC_INVENTORY; break; default: location = LOC_INVENTORY; } if (location > 0) { /* Wearable. */ if (!GET_EQ(ch,j)) { /* Check the characters's alignment to prevent them from being zapped * through the auto-equipping. */ if (invalid_align(ch, obj) || invalid_class(ch, obj)) location = LOC_INVENTORY; else equip_char(ch, obj, j); } else { /* Oops, saved a player with double equipment? */ mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "SYSERR: autoeq: '%s' already equipped in position %d.", GET_NAME(ch), location); location = LOC_INVENTORY; } } } if (location <= 0) /* Inventory */ obj_to_char(obj, ch); } int Crash_delete_file(char *name) { char filename[MAX_INPUT_LENGTH]; FILE *fl; if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) return FALSE; if (!(fl = fopen(filename, "r"))) { if (errno != ENOENT) /* if it fails but NOT because of no file */ log("SYSERR: deleting crash file %s (1): %s", filename, strerror(errno)); return FALSE; } fclose(fl); /* if it fails, NOT because of no file */ if (remove(filename) < 0 && errno != ENOENT) log("SYSERR: deleting crash file %s (2): %s", filename, strerror(errno)); return TRUE; } int Crash_delete_crashfile(struct char_data *ch) { char filename[MAX_INPUT_LENGTH]; int numread; FILE *fl; int rentcode; char line[READ_SIZE]; if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) return FALSE; if (!(fl = fopen(filename, "r"))) { if (errno != ENOENT) /* if it fails, NOT because of no file */ log("SYSERR: checking for crash file %s (3): %s", filename, strerror(errno)); return FALSE; } numread = get_line(fl,line); fclose(fl); if (numread == FALSE) return FALSE; sscanf(line,"%d ",&rentcode); if (rentcode == RENT_CRASH) Crash_delete_file(GET_NAME(ch)); return TRUE; } int Crash_clean_file(char *name) { char filename[MAX_INPUT_LENGTH], filetype[20]; int numread; FILE *fl; int rentcode, timed, netcost, gold, account, nitems; char line[READ_SIZE]; if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) return FALSE; /* Open so that permission problems will be flagged now, at boot time. */ if (!(fl = fopen(filename, "r"))) { if (errno != ENOENT) /* if it fails, NOT because of no file */ log("SYSERR: OPENING OBJECT FILE %s (4): %s", filename, strerror(errno)); return FALSE; } numread = get_line(fl,line); fclose(fl); if (numread == FALSE) return FALSE; sscanf(line, "%d %d %d %d %d %d",&rentcode,&timed,&netcost, &gold,&account,&nitems); if ((rentcode == RENT_CRASH) || (rentcode == RENT_FORCED) || (rentcode == RENT_TIMEDOUT) ) { if (timed < time(0) - (CONFIG_CRASH_TIMEOUT * SECS_PER_REAL_DAY)) { Crash_delete_file(name); switch (rentcode) { case RENT_CRASH: strcpy(filetype, "crash"); break; case RENT_FORCED: strcpy(filetype, "forced rent"); break; case RENT_TIMEDOUT: strcpy(filetype, "idlesave"); break; default: strcpy(filetype, "UNKNOWN!"); break; } log(" Deleting %s's %s file.", name, filetype); return TRUE; } /* Must retrieve rented items w/in 30 days */ } else if (rentcode == RENT_RENTED) if (timed < time(0) - (CONFIG_RENT_TIMEOUT * SECS_PER_REAL_DAY)) { Crash_delete_file(name); log(" Deleting %s's rent file.", name); return TRUE; } return FALSE; } void update_obj_file(void) { int i; for (i = 0; i <= top_of_p_table; i++) if (*player_table[i].name) Crash_clean_file(player_table[i].name); } void Crash_listrent(struct char_data *ch, char *name) { FILE *fl; char filename[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH], line[READ_SIZE]; obj_save_data *loaded, *current; int rentcode = RENT_UNDEF, timed, netcost, gold, account, nitems, numread, len; if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) return; if (!(fl = fopen(filename, "r"))) { send_to_char(ch, "%s has no rent file.\r\n", name); return; } len = snprintf(buf, sizeof(buf),"%s\r\n", filename); numread = get_line(fl, line); /* Oops, can't get the data, punt. */ if (numread == FALSE) { send_to_char(ch, "Error reading rent information.\r\n"); fclose(fl); return; } sscanf(line,"%d %d %d %d %d %d", &rentcode,&timed,&netcost,&gold,&account,&nitems); switch (rentcode) { case RENT_RENTED: len += snprintf(buf+len, sizeof(buf)-len, "Rent\r\n"); break; case RENT_CRASH: len += snprintf(buf+len, sizeof(buf)-len,"Crash\r\n"); break; case RENT_CRYO: len += snprintf(buf+len, sizeof(buf)-len, "Cryo\r\n"); break; case RENT_TIMEDOUT: case RENT_FORCED: len += snprintf(buf+len, sizeof(buf)-len, "TimedOut\r\n"); break; default: len += snprintf(buf+len, sizeof(buf)-len, "Undef\r\n"); break; } loaded = objsave_parse_objects(fl); for (current = loaded; current != NULL; current=current->next) len += snprintf(buf+len, sizeof(buf)-len, "[%5d] (%5dau) %-20s\r\n", GET_OBJ_VNUM(current->obj), GET_OBJ_RENT(current->obj), current->obj->short_description); /* Now it's safe to free the obj_save_data list and the objects on it. */ while (loaded != NULL) { current = loaded; loaded = loaded->next; extract_obj(current->obj); free(current); } page_string(ch->desc,buf,0); fclose(fl); } /* Return values: * 0 - successful load, keep char in rent room. * 1 - load failure or load of crash items -- put char in temple. * 2 - rented equipment lost (no $) */ int Crash_load(struct char_data *ch) { return (Crash_load_objs(ch)); } static int Crash_save(struct obj_data *obj, FILE *fp, int location) { struct obj_data *tmp; int result; if (obj) { Crash_save(obj->next_content, fp, location); Crash_save(obj->contains, fp, MIN(0, location) - 1); result = objsave_save_obj_record(obj, fp, location); for (tmp = obj->in_obj; tmp; tmp = tmp->in_obj) GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); if (!result) return FALSE; } return (TRUE); } static void Crash_restore_weight(struct obj_data *obj) { if (obj) { Crash_restore_weight(obj->contains); Crash_restore_weight(obj->next_content); if (obj->in_obj) GET_OBJ_WEIGHT(obj->in_obj) += GET_OBJ_WEIGHT(obj); } } void Crash_crashsave(struct char_data *ch) { char buf[MAX_INPUT_LENGTH]; int j; FILE *fp; if (IS_NPC(ch)) return; if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) return; if (!(fp = fopen(buf, "w"))) return; if (!objsave_write_rentcode(fp, RENT_CRASH, 0, ch)) return; for (j = 0; j < NUM_WEARS; j++) if (GET_EQ(ch, j)) { if (!Crash_save(GET_EQ(ch, j), fp, j + 1)) { fclose(fp); return; } Crash_restore_weight(GET_EQ(ch, j)); } if (!Crash_save(ch->carrying, fp, 0)) { fclose(fp); return; } Crash_restore_weight(ch->carrying); fprintf(fp, "$~\n"); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } /* Shortened because we don't handle rent in this game */ void Crash_idlesave(struct char_data *ch) { if (!ch || IS_NPC(ch)) return; Crash_crashsave(ch); } /* Shortened because we don't handle rent in this game */ void Crash_rentsave(struct char_data *ch, int cost) { if (!ch || IS_NPC(ch)) return; Crash_crashsave(ch); } static int objsave_write_rentcode(FILE *fl, int rentcode, int cost_per_day, struct char_data *ch) { if (fprintf(fl, "%d %ld %d %d %d %d\r\n", rentcode, (long) time(0), cost_per_day, GET_GOLD(ch), GET_BANK_GOLD(ch), 0) < 1) { perror("Syserr: Writing rent code"); return FALSE; } return TRUE; } void Crash_save_all(void) { struct descriptor_data *d; /* Respect config: if autosave is off, do nothing. */ if (!CONFIG_AUTO_SAVE) return; for (d = descriptor_list; d; d = d->next) { if (STATE(d) != CON_PLAYING) continue; if (!d->character || IS_NPC(d->character)) continue; /* Skip characters not fully placed yet (prevents clobbering Room to 65535). */ if (IN_ROOM(d->character) == NOWHERE) continue; /* Optional hardening: if spawn vnum is not yet established, skip this tick. */ if (GET_LOADROOM(d->character) == NOWHERE) continue; /* IMPORTANT: Do NOT modify GET_LOADROOM here. Autosave should not change the player's spawn point. */ /* Persist character and object file. */ save_char(d->character); Crash_crashsave(d->character); if (PLR_FLAGGED(d->character, PLR_CRASH)) REMOVE_BIT_AR(PLR_FLAGS(d->character), PLR_CRASH); } } /* Load all objects from file into memory. Updated to load NUM_OBJ_VAL_POSITIONS values. */ obj_save_data *objsave_parse_objects(FILE *fl) { char line[MAX_STRING_LENGTH]; obj_save_data *head = NULL, *tail = NULL; /* State for the object we’re currently assembling */ struct obj_data *temp = NULL; int pending_locate = 0; /* 0 = inventory, 1..NUM_WEARS = worn slot */ int pending_nest = 0; /* 0 = top-level; >0 = inside container at level-1 */ /* --- helpers (GCC nested functions OK in tbaMUD build) ---------------- */ /* append current object to the result list with proper locate */ void commit_current(void) { if (!temp) return; /* sanitize top-level locate range only; children will be negative later */ int loc = pending_locate; if (pending_nest <= 0) { if (loc < 0 || loc > NUM_WEARS) { mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: bad locate %d for vnum %d; defaulting to inventory.", loc, GET_OBJ_VNUM(temp)); loc = 0; } } /* convert Nest>0 into negative locate for handle_obj()/cont_row */ int effective_loc = (pending_nest > 0) ? -pending_nest : loc; obj_save_data *node = NULL; CREATE(node, obj_save_data, 1); node->obj = temp; node->locate = effective_loc; node->next = NULL; if (!head) head = node, tail = node; else tail->next = node, tail = node; temp = NULL; pending_locate = 0; pending_nest = 0; } /* split a line into normalized tag (no colon) and payload pointer */ void split_tag_line(const char *src, char tag_out[6], const char **payload_out) { const char *s = src; while (*s && isspace((unsigned char)*s)) s++; /* skip leading ws */ const char *te = s; while (*te && !isspace((unsigned char)*te) && *te != ':') te++; size_t tlen = (size_t)(te - s); if (tlen > 5) tlen = 5; memcpy(tag_out, s, tlen); tag_out[tlen] = '\0'; const char *p = te; while (*p && isspace((unsigned char)*p)) p++; if (*p == ':') { p++; while (*p && isspace((unsigned char)*p)) p++; } *payload_out = p; } /* ---------------------------------------------------------------------- */ while (get_line(fl, line)) { if (!*line) continue; /* New object header: "#" (commit any previous one first) */ if (*line == '#') { if (temp) commit_current(); long vnum = -1L; vnum = strtol(line + 1, NULL, 10); if (vnum <= 0) { mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: bad vnum header: '%s'", line); temp = NULL; pending_locate = 0; pending_nest = 0; continue; } /* Instantiate from prototype if available, else create a blank */ int rnum = real_object((obj_vnum)vnum); if (rnum >= 0) { temp = read_object(rnum, REAL); } else { temp = create_obj(); /* Do NOT assign GET_OBJ_VNUM(temp); item_number derives vnum. */ if (!temp->name) temp->name = strdup("object"); if (!temp->short_description) temp->short_description = strdup("an object"); if (!temp->description) temp->description = strdup("An object lies here."); } pending_locate = 0; pending_nest = 0; continue; } /* Normal data line: TAG [ : ] payload */ char tag[6]; const char *payload = NULL; split_tag_line(line, tag, &payload); if (!*tag) continue; if (!temp) { mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: data before header ignored: '%s'", line); continue; } if (!strcmp(tag, "Loc")) { pending_locate = (int)strtol(payload, NULL, 10); } else if (!strcmp(tag, "Nest")) { pending_nest = (int)strtol(payload, NULL, 10); if (pending_nest < 0) pending_nest = 0; if (pending_nest > MAX_BAG_ROWS) { mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: nest level %d too deep; clamping to %d.", pending_nest, MAX_BAG_ROWS); pending_nest = MAX_BAG_ROWS; } } else if (!strcmp(tag, "Vals")) { const char *p = payload; for (int i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { if (!*p) { GET_OBJ_VAL(temp, i) = 0; continue; } GET_OBJ_VAL(temp, i) = (int)strtol(p, (char **)&p, 10); } } else if (!strcmp(tag, "Wght")) { GET_OBJ_WEIGHT(temp) = (int)strtol(payload, NULL, 10); } else if (!strcmp(tag, "Cost")) { GET_OBJ_COST(temp) = (int)strtol(payload, NULL, 10); } else if (!strcmp(tag, "Rent")) { GET_OBJ_RENT(temp) = (int)strtol(payload, NULL, 10); } else if (!strcmp(tag, "Type")) { GET_OBJ_TYPE(temp) = (int)strtol(payload, NULL, 10); } else if (!strcmp(tag, "Wear")) { unsigned long words[4] = {0,0,0,0}; const char *p = payload; for (int i = 0; i < 4 && *p; i++) words[i] = strtoul(p, (char **)&p, 10); #if defined(TW_ARRAY_MAX) && defined(GET_OBJ_WEAR_AR) for (int i = 0; i < 4; i++) { if (i < TW_ARRAY_MAX) GET_OBJ_WEAR_AR(temp, i) = (bitvector_t)words[i]; else if (words[i]) mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: Wear word %d (%lu) truncated (TW_ARRAY_MAX=%d).", i, words[i], TW_ARRAY_MAX); } #elif defined(GET_OBJ_WEAR_AR) for (int i = 0; i < 4; i++) GET_OBJ_WEAR_AR(temp, i) = (bitvector_t)words[i]; #endif } else if (!strcmp(tag, "Flag")) { unsigned long words[4] = {0,0,0,0}; const char *p = payload; for (int i = 0; i < 4 && *p; i++) words[i] = strtoul(p, (char **)&p, 10); #if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) for (int i = 0; i < 4; i++) { if (i < EF_ARRAY_MAX) GET_OBJ_EXTRA_AR(temp, i) = (bitvector_t)words[i]; else if (words[i]) mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: Extra word %d (%lu) truncated (EF_ARRAY_MAX=%d).", i, words[i], EF_ARRAY_MAX); } #elif defined(GET_OBJ_EXTRA_AR) for (int i = 0; i < 4; i++) GET_OBJ_EXTRA_AR(temp, i) = (bitvector_t)words[i]; #endif } else if (!strcmp(tag, "Name")) { if (temp->name) free(temp->name); temp->name = *payload ? strdup(payload) : strdup("object"); } else if (!strcmp(tag, "Shrt")) { if (temp->short_description) free(temp->short_description); temp->short_description = *payload ? strdup(payload) : strdup("an object"); } else if (!strcmp(tag, "Desc")) { if (temp->description) free(temp->description); temp->description = *payload ? strdup(payload) : strdup("An object lies here."); } else if (!strcmp(tag, "ADes")) { if (temp->main_description) free(temp->main_description); temp->main_description = *payload ? strdup(payload) : NULL; } else if (!strcmp(tag, "End")) { commit_current(); } else { mudlog(NRM, LVL_IMMORT, TRUE, "RENT-LOAD: unknown tag '%s'", tag); } } if (temp) commit_current(); return head; } static int Crash_load_objs(struct char_data *ch) { FILE *fl; char filename[PATH_MAX]; char line[READ_SIZE]; char buf[MAX_STRING_LENGTH]; char str[64]; int i, num_of_days, orig_rent_code, num_objs=0; unsigned long cost; struct obj_data *cont_row[MAX_BAG_ROWS]; int rentcode = RENT_UNDEF; int timed=0,netcost=0,gold,account,nitems; obj_save_data *loaded, *current; if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) return 1; for (i = 0; i < MAX_BAG_ROWS; i++) cont_row[i] = NULL; if (!(fl = fopen(filename, "r"))) { if (errno != ENOENT) { /* if it fails, NOT because of no file */ snprintf(buf, MAX_STRING_LENGTH, "SYSERR: READING OBJECT FILE %s (5)", filename); perror(buf); send_to_char(ch, "\r\n********************* NOTICE *********************\r\n" "There was a problem loading your objects from disk.\r\n" "Contact a God for assistance.\r\n"); } mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s entering game with no equipment.", GET_NAME(ch)); return 1; } if (!get_line(fl, line)) mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "Failed to read player's rent code: %s.", GET_NAME(ch)); else sscanf(line,"%d %d %d %d %d %d",&rentcode, &timed, &netcost,&gold,&account,&nitems); if (rentcode == RENT_RENTED || rentcode == RENT_TIMEDOUT) { sprintf(str, "%d", SECS_PER_REAL_DAY); num_of_days = (int)((float) (time(0) - timed) / atoi(str)); cost = (unsigned int) (netcost * num_of_days); if (cost > (unsigned int)GET_GOLD(ch) + (unsigned int)GET_BANK_GOLD(ch)) { fclose(fl); mudlog(BRF, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s entering game, rented equipment lost (no $).", GET_NAME(ch)); Crash_crashsave(ch); return 2; } else { GET_BANK_GOLD(ch) -= MAX(cost - GET_GOLD(ch), 0); GET_GOLD(ch) = MAX(GET_GOLD(ch) - cost, 0); save_char(ch); } } switch (orig_rent_code = rentcode) { case RENT_RENTED: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s un-renting and entering game.", GET_NAME(ch)); break; case RENT_CRASH: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving crash-saved items and entering game.", GET_NAME(ch)); break; case RENT_CRYO: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s un-cryo'ing and entering game.", GET_NAME(ch)); break; case RENT_FORCED: case RENT_TIMEDOUT: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving force-saved items and entering game.", GET_NAME(ch)); break; default: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "WARNING: %s entering game with undefined rent code.", GET_NAME(ch)); break; } loaded = objsave_parse_objects(fl); for (current = loaded; current != NULL; current=current->next) num_objs += handle_obj(current->obj, ch, current->locate, cont_row); /* now it's safe to free the obj_save_data list - all members of it * have been put in the correct lists by handle_obj() */ while (loaded != NULL) { current = loaded; loaded = loaded->next; free(current); } /* Little hoarding check. -gg 3/1/98 */ mudlog(NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE, "%s (level %d) has %d object%s (max %d).", GET_NAME(ch), GET_LEVEL(ch), num_objs, num_objs != 1 ? "s" : "", CONFIG_MAX_OBJ_SAVE); fclose(fl); if ((orig_rent_code == RENT_RENTED) || (orig_rent_code == RENT_CRYO)) return 0; else return 1; } static int handle_obj(struct obj_data *temp, struct char_data *ch, int locate, struct obj_data **cont_row) { int j; struct obj_data *obj1; if (!temp) /* this should never happen, but.... */ return FALSE; auto_equip(ch, temp, locate); /* What to do with a new loaded item: * If there's a list with less than 1 below this: (equipped items * are assumed to have ==0 here) then its container has disappeared * from the file *gasp* -> put all the list back to ch's inventory if * there's a list of contents with 1 below this: check if it's a * container - if so: get it from ch, fill it, and give it back to ch (this * way the container has its correct weight before modifying ch) - if not: * the container is missing -> put all the list to ch's inventory. For items * with negative : If there's already a list of contents with the * same put obj to it if not, start a new list. Since for * contents is < 0 the list indices are switched to non-negative. */ if (locate > 0) { /* item equipped */ for (j = MAX_BAG_ROWS-1;j > 0;j--) if (cont_row[j]) { /* no container -> back to ch's inventory */ for (;cont_row[j];cont_row[j] = obj1) { obj1 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } if (cont_row[0]) { /* content list existing */ if (GET_OBJ_TYPE(temp) == ITEM_CONTAINER) { /* rem item ; fill ; equip again */ temp = unequip_char(ch, locate-1); temp->contains = NULL; /* should be empty - but who knows */ for (;cont_row[0];cont_row[0] = obj1) { obj1 = cont_row[0]->next_content; obj_to_obj(cont_row[0], temp); } equip_char(ch, temp, locate-1); } else { /* object isn't container -> empty content list */ for (;cont_row[0];cont_row[0] = obj1) { obj1 = cont_row[0]->next_content; obj_to_char(cont_row[0], ch); } cont_row[0] = NULL; } } } else { /* locate <= 0 */ for (j = MAX_BAG_ROWS-1;j > -locate;j--) if (cont_row[j]) { /* no container -> back to ch's inventory */ for (;cont_row[j];cont_row[j] = obj1) { obj1 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } if (j == -locate && cont_row[j]) { /* content list existing */ if (GET_OBJ_TYPE(temp) == ITEM_CONTAINER) { /* take item ; fill ; give to char again */ obj_from_char(temp); temp->contains = NULL; for (;cont_row[j];cont_row[j] = obj1) { obj1 = cont_row[j]->next_content; obj_to_obj(cont_row[j], temp); } obj_to_char(temp, ch); /* add to inv first ... */ } else { /* object isn't container -> empty content list */ for (;cont_row[j];cont_row[j] = obj1) { obj1 = cont_row[j]->next_content; obj_to_char(cont_row[j], ch); } cont_row[j] = NULL; } } if (locate < 0 && locate >= -MAX_BAG_ROWS) { /* let obj be part of content list but put it at the list's end thus having the items in the same order as before renting */ obj_from_char(temp); if ((obj1 = cont_row[-locate-1])) { while (obj1->next_content) obj1 = obj1->next_content; obj1->next_content = temp; } else cont_row[-locate-1] = temp; } } /* locate less than zero */ return TRUE; }