/************************************************************************** * File: objsave.c Part of tbaMUD * * Usage: loading/saving player objects for crash-save and login restore * * * * 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 */ #include "toml.h" #include "toml_utils.h" /* these factors should be unique integers */ #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 void Crash_write_header(struct char_data *ch, FILE *fp, int savecode); static int toml_get_int_default(toml_table_t *tab, const char *key, int def) { toml_datum_t d = toml_int_in(tab, key); if (!d.ok) return def; return (int)d.u.i; } static char *toml_get_string_dup(toml_table_t *tab, const char *key) { toml_datum_t d = toml_string_in(tab, key); if (!d.ok) return NULL; return d.u.s; } static void toml_read_int_array(toml_array_t *arr, int *out, int out_count, int def) { int i; for (i = 0; i < out_count; i++) out[i] = def; if (!arr) return; for (i = 0; i < out_count; i++) { toml_datum_t d = toml_int_at(arr, i); if (d.ok) out[i] = (int)d.u.i; } } /* 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]; int out_locate = (locate > 0) ? locate : 0; int nest = (locate < 0) ? -locate : 0; fprintf(fp, "\n[[object]]\n"); fprintf(fp, "vnum = %d\n", GET_OBJ_VNUM(obj)); fprintf(fp, "locate = %d\n", out_locate); fprintf(fp, "nest = %d\n", nest); fprintf(fp, "values = ["); for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { if (i > 0) fputs(", ", fp); fprintf(fp, "%d", GET_OBJ_VAL(obj, i)); } fprintf(fp, "]\n"); fprintf(fp, "extra_flags = [%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]); fprintf(fp, "wear_flags = [%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]); toml_write_kv_string_opt(fp, "name", obj->name); toml_write_kv_string_opt(fp, "short", obj->short_description); toml_write_kv_string_opt(fp, "description", obj->description); if (obj->main_description && *obj->main_description) { strlcpy(buf1, obj->main_description, sizeof(buf1)); strip_cr(buf1); toml_write_kv_string(fp, "main_description", buf1); } fprintf(fp, "type = %d\n", GET_OBJ_TYPE(obj)); fprintf(fp, "weight = %d\n", GET_OBJ_WEIGHT(obj)); fprintf(fp, "cost = %d\n", GET_OBJ_COST(obj)); 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)) { if (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]; FILE *fl; char errbuf[256]; toml_table_t *tab = NULL; toml_table_t *header = NULL; int savecode; 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; } tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); fclose(fl); if (!tab) return FALSE; header = toml_table_in(tab, "header"); if (!header) { toml_free(tab); return FALSE; } savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); toml_free(tab); if (savecode == SAVE_CRASH) Crash_delete_file(GET_NAME(ch)); return TRUE; } int Crash_clean_file(char *name) { char filename[MAX_INPUT_LENGTH], filetype[20]; FILE *fl; char errbuf[256]; toml_table_t *tab = NULL; toml_table_t *header = NULL; int savecode, timed; 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; } tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); fclose(fl); if (!tab) return FALSE; header = toml_table_in(tab, "header"); if (!header) { toml_free(tab); return FALSE; } savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); timed = toml_get_int_default(header, "timed", 0); toml_free(tab); if ((savecode == SAVE_CRASH) || (savecode == SAVE_LOGOUT) || (savecode == SAVE_FORCED) || (savecode == SAVE_TIMEDOUT) ) { if (timed < time(0) - (CONFIG_CRASH_TIMEOUT * SECS_PER_REAL_DAY)) { Crash_delete_file(name); switch (savecode) { case SAVE_CRASH: strcpy(filetype, "crash"); break; case SAVE_LOGOUT: strcpy(filetype, "legacy save"); break; case SAVE_FORCED: strcpy(filetype, "idle save"); break; case SAVE_TIMEDOUT: strcpy(filetype, "idle save"); break; default: strcpy(filetype, "UNKNOWN!"); break; } log(" Deleting %s's %s file.", name, filetype); 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); } /* Return values: * 0 - successful load, keep char in load room. * 1 - load failure or load of crash items -- put char in temple. */ 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); } } static void Crash_write_header(struct char_data *ch, FILE *fp, int savecode) { int timed = (int)time(0); int netcost = 0; int coins = ch ? GET_COINS(ch) : 0; int account = ch ? GET_BANK_COINS(ch) : 0; int nitems = 0; fprintf(fp, "[header]\n"); fprintf(fp, "save_code = %d\n", savecode); fprintf(fp, "timed = %d\n", timed); fprintf(fp, "net_cost = %d\n", netcost); fprintf(fp, "coins = %d\n", coins); fprintf(fp, "account = %d\n", account); fprintf(fp, "item_count = %d\n", nitems); } 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; Crash_write_header(ch, fp, SAVE_CRASH); 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); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } /* Shortened because we don't use storage fees in this game */ void Crash_idlesave(struct char_data *ch) { if (!ch || IS_NPC(ch)) return; char buf[MAX_INPUT_LENGTH]; int j; FILE *fp; if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) return; if (!(fp = fopen(buf, "w"))) return; Crash_write_header(ch, fp, SAVE_FORCED); 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); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } /* Shortened because we don't use storage fees in this game */ void Crash_rentsave(struct char_data *ch, int cost) { if (!ch || IS_NPC(ch)) return; char buf[MAX_INPUT_LENGTH]; int j; FILE *fp; if (!get_filename(buf, sizeof(buf), CRASH_FILE, GET_NAME(ch))) return; if (!(fp = fopen(buf, "w"))) return; Crash_write_header(ch, fp, SAVE_LOGOUT); 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); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } 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 TOML into memory. */ static obj_save_data *objsave_parse_objects_from_toml(toml_table_t *tab, const char *filename) { obj_save_data *head = NULL, *tail = NULL; toml_array_t *arr = NULL; int count, i; arr = toml_array_in(tab, "object"); if (!arr) return NULL; count = toml_array_nelem(arr); for (i = 0; i < count; i++) { toml_table_t *obj_tab = toml_table_at(arr, i); struct obj_data *temp = NULL; int locate, nest; int values[NUM_OBJ_VAL_POSITIONS]; int flags[4]; char *str = NULL; if (!obj_tab) continue; { int vnum = toml_get_int_default(obj_tab, "vnum", -1); if (vnum == -1) { temp = create_obj(); temp->item_number = NOTHING; 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."); } else { int rnum = real_object((obj_vnum)vnum); if (rnum >= 0) { temp = read_object(rnum, REAL); } else { temp = create_obj(); 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."); } } } locate = toml_get_int_default(obj_tab, "locate", 0); if (locate < 0 || locate > NUM_WEARS) locate = 0; nest = toml_get_int_default(obj_tab, "nest", 0); if (nest < 0) nest = 0; if (nest > MAX_BAG_ROWS) { if (filename) mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: nest level %d too deep in %s; clamping to %d.", nest, filename, MAX_BAG_ROWS); else mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: nest level %d too deep; clamping to %d.", nest, MAX_BAG_ROWS); nest = MAX_BAG_ROWS; } toml_read_int_array(toml_array_in(obj_tab, "values"), values, NUM_OBJ_VAL_POSITIONS, 0); for (int j = 0; j < NUM_OBJ_VAL_POSITIONS; j++) GET_OBJ_VAL(temp, j) = values[j]; toml_read_int_array(toml_array_in(obj_tab, "extra_flags"), flags, 4, 0); for (int j = 0; j < 4; j++) GET_OBJ_EXTRA(temp)[j] = flags[j]; toml_read_int_array(toml_array_in(obj_tab, "wear_flags"), flags, 4, 0); for (int j = 0; j < 4; j++) GET_OBJ_WEAR(temp)[j] = flags[j]; str = toml_get_string_dup(obj_tab, "name"); if (str) { if (temp->name) free(temp->name); temp->name = str; } str = toml_get_string_dup(obj_tab, "short"); if (str) { if (temp->short_description) free(temp->short_description); temp->short_description = str; } str = toml_get_string_dup(obj_tab, "description"); if (str) { if (temp->description) free(temp->description); temp->description = str; } str = toml_get_string_dup(obj_tab, "main_description"); if (str) { if (temp->main_description) free(temp->main_description); temp->main_description = str; } GET_OBJ_TYPE(temp) = toml_get_int_default(obj_tab, "type", GET_OBJ_TYPE(temp)); GET_OBJ_WEIGHT(temp) = toml_get_int_default(obj_tab, "weight", GET_OBJ_WEIGHT(temp)); GET_OBJ_COST(temp) = toml_get_int_default(obj_tab, "cost", GET_OBJ_COST(temp)); if (GET_OBJ_TYPE(temp) == ITEM_MONEY) update_money_obj(temp); { int effective_loc = (nest > 0) ? -nest : locate; 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; } } return head; } obj_save_data *objsave_parse_objects(FILE *fl) { char errbuf[256]; toml_table_t *tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); obj_save_data *list = NULL; if (!tab) return NULL; list = objsave_parse_objects_from_toml(tab, NULL); toml_free(tab); return list; } static int Crash_load_objs(struct char_data *ch) { FILE *fl; char filename[PATH_MAX]; char buf[MAX_STRING_LENGTH]; int i, orig_save_code, num_objs=0; struct obj_data *cont_row[MAX_BAG_ROWS]; int savecode = SAVE_UNDEF; int timed=0,netcost=0,coins=0,account=0,nitems=0; char errbuf[256]; toml_table_t *tab = NULL; toml_table_t *header = NULL; 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)); if (GET_COINS(ch) > 0) { int old_coins = GET_COINS(ch); GET_COINS(ch) = 0; add_coins_to_char(ch, old_coins); } return 1; } tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); fclose(fl); if (!tab) { mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "Failed to parse player's object file %s: %s.", filename, errbuf); return 1; } header = toml_table_in(tab, "header"); if (!header) { mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "Player object file %s missing header.", filename); toml_free(tab); return 1; } savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); timed = toml_get_int_default(header, "timed", 0); netcost = toml_get_int_default(header, "net_cost", 0); coins = toml_get_int_default(header, "coins", 0); account = toml_get_int_default(header, "account", 0); nitems = toml_get_int_default(header, "item_count", 0); if (savecode == SAVE_LOGOUT || savecode == SAVE_TIMEDOUT) { mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s entering game with legacy save code; no fees applied.", GET_NAME(ch)); } switch (orig_save_code = savecode) { case SAVE_LOGOUT: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s restoring saved items and entering game.", GET_NAME(ch)); break; case SAVE_CRASH: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving crash-saved items and entering game.", GET_NAME(ch)); break; case SAVE_CRYO: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s restoring cryo-saved items and entering game.", GET_NAME(ch)); break; case SAVE_FORCED: case SAVE_TIMEDOUT: mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "%s retrieving idle-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 save code.", GET_NAME(ch)); break; } loaded = objsave_parse_objects_from_toml(tab, filename); toml_free(tab); 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); } { int old_coins = GET_COINS(ch); int coin_total = count_char_coins(ch); if (coin_total > 0) { GET_COINS(ch) = coin_total; } else if (old_coins > 0) { GET_COINS(ch) = 0; add_coins_to_char(ch, old_coins); } else { GET_COINS(ch) = 0; } } /* Little hoarding check. -gg 3/1/98 */ mudlog(NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE, "%s (level %d) has %d object%s.", GET_NAME(ch), GET_LEVEL(ch), num_objs, num_objs != 1 ? "s" : ""); if ((orig_save_code == SAVE_LOGOUT) || (orig_save_code == SAVE_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 (obj_is_storage(temp)) { /* 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 (obj_is_storage(temp)) { /* 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 saving */ 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; }