/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA objsave.c Object saving to/loading from disk. Force saves and normal (cryo) saves. Houses, players, etc. No longer uses binary files btw, now strictly text. ******** Heavily modified and expanded ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ /* RoA, totally overhauled object save code, saves in text file now easier on the conversions we'll be going thru and have gone thru... easier to add stuff to objects now, without having to convert obect files.. no rent code crap, no 30 day limit anymore, no nothing, but saves yer damn obects to disk, thats it, thats all :) oh yeah, loads every object off a proto save for a FEW things that are written to disk, like object hits left on the object and what not all the autowear stuff is also saved to disk, loads off the PROTO now so everytime a character logs in, their objects will be up to date with the current versions of the protos, no more having to sift thru every object file and pull out of whack objects of course, this has it's disadvantages as well, but the benes won here -jtrhone */ #include "conf.h" #include "sysdep.h" #include "structures.h" #include "comm.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "acmd.h" #include "utils.h" #include "magic.h" #include "objsave.h" #include "lists.h" #include "nature.h" #include "darkenelf.h" // defines #define NAME(x) ((temp = get_name_by_id(x)) == NULL ? "<UNDEF>" : temp) // protos extern char *get_name_by_id(long id); // global vars extern struct str_app_type str_app[]; extern char *inst_names[]; BOOL delete_object_file(chdata *ch) { char filename[50]; if (IS_NPC(ch)) return FALSE; if (!get_pdata_filename(GET_NAME(ch), "roa", filename)) return FALSE; if (unlink(filename) < 0) { if (errno != ENOENT) { /* if it fails, NOT because of no file */ sprintf(buf1, "SYSERR: deleting object file %s (2)", filename); perror(buf1); } } return(TRUE); } /* hack way to delete player reimb file */ BOOL delete_reimb_file(chdata *ch) { char filename[50]; if (IS_NPC(ch)) return FALSE; if (!get_pdata_filename(GET_NAME(ch), "rmb", filename)) return FALSE; if (unlink(filename) < 0) { if (errno != ENOENT) { /* if it fails, NOT because of no file */ sprintf(buf1, "SYSERR: deleting reimb file %s (2)", filename); perror(buf1); } } return(TRUE); } void listrent(chdata *ch, char *name) { FILE * fl; char fname[MAX_INPUT_LENGTH], buf[20000]; obdata *obj = NULL; int tmp; if (!get_pdata_filename(name, "roa", fname)) { send_to_char("Object file unlocatable.\n\r",ch); return; } if (!(fl = fopen(fname, "rt"))) { sprintf(buf, "%s has no object file.\n\r", name); S2C(); return; } sprintf(buf, "%%5%s%%0\n\r", fname); while (!feof(fl)) { if(fscanf(fl, "%d", &tmp) != 1) break; obj = read_object(tmp, VIRTUAL); if (obj) { sprintf(buf2, " [%%b%d%%0] %s\n\r",tmp,obj->shdesc); strcat(buf, buf2); extract_obj(obj); } fgets(buf1, 80, fl); /* skip the rest man */ } fclose(fl); page_string(ch->desc, buf, 1); } void listreimb(chdata *ch, char *name) { FILE * fl; char fname[MAX_INPUT_LENGTH], buf[20000]; obdata *obj = NULL; int tmp; if (!get_pdata_filename(name, "rmb", fname)) { send_to_char("Reimbursement file unlocatable.\n\r",ch); return; } if (!(fl = fopen(fname, "rt"))) { sprintf(buf, "%s has no reimbursement file.\n\r", name); S2C(); return; } sprintf(buf, "%%5%s%%0\n\r", fname); while (!feof(fl)) { if(fscanf(fl, "%d", &tmp) != 1) break; obj = read_object(tmp, VIRTUAL); if (obj) { sprintf(buf2, " [%%b%d%%0] %s\n\r",tmp,obj->shdesc); strcat(buf, buf2); extract_obj(obj); } fgets(buf1, 80, fl); /* skip the rest man */ } fclose(fl); page_string(ch->desc, buf, 1); } BOOL reimburse(chdata *ch) { FILE *fl; char fname[MAX_INPUT_LENGTH]; obdata *obj = NULL; int tmp; if (IS_NPC(ch)) return FALSE; if (!get_pdata_filename(GET_NAME(ch), "rmb", fname)) return FALSE; if (!(fl = fopen(fname, "rt"))) return FALSE; while (!feof(fl)) { if(fscanf(fl, "%d", &tmp) != 1) break; obj = read_object(tmp, VIRTUAL); if (obj) obj_to_char(obj, ch); fgets(buf1, 80, fl); /* skip the rest man */ } fclose(fl); if (unlink(fname) < 0) { if (errno != ENOENT) { /* if it fails, NOT because of no file */ sprintf(buf1, "SYSERR: deleting reimb file %s (3)", fname); perror(buf1); } } return TRUE; } void write_reimb_file(chdata *ch) { FILE * fl; char fname[MAX_INPUT_LENGTH]; int j; if (IS_NPC(ch)) return; if (!get_pdata_filename(GET_NAME(ch), "rmb", fname)) return; if (!(fl = fopen(fname, "wt"))) return; if (!save_object(ch->carrying, fl)) { fclose(fl); return; } for (j = 0; j < MAX_WEAR; j++) if (EQ(ch, j) && !save_object(EQ(ch, j), fl)) { fclose(fl); return; } fclose(fl); } void extract_objects(obdata *obj) { if (obj) { extract_objects(obj->contains); extract_objects(obj->next_content); extract_obj(obj); } } BOOL is_unrentable(obdata *obj) { if (!obj) return FALSE; if (IS_SET(OBJ_EXTRAS(obj), ITEM_NORENT) || obj->item_number <= -1 || ITEM_TYPE(obj) == ITEM_KEY) return TRUE; return FALSE; } void extract_norents(obdata *obj) { if (obj) { extract_norents(obj->contains); extract_norents(obj->next_content); if (is_unrentable(obj)) extract_obj(obj); } } void extract_cheapest(obdata *obj) { obdata *tobj = NULL, *min = NULL; min = obj; for (tobj = obj; tobj; tobj = tobj->next_content) if ((tobj->cost < min->cost) && ITEM_TYPE(tobj) != ITEM_CONTAINER) min = tobj; extract_obj(min); } /* Find all the unrentables inside an object */ BOOL find_unrentables(chdata *ch, obdata *obj) { if (obj) { if (is_unrentable(obj)) return TRUE; if(find_unrentables(ch, obj->contains)) return TRUE; if(find_unrentables(ch, obj->next_content)) return TRUE; } return FALSE; } /* Test whether char has unrentables */ BOOL has_norents(chdata *ch) { int i; for(i = 0;i < MAX_WEAR;i++) if(find_unrentables(ch, EQ(ch, i))) return TRUE; if(find_unrentables(ch, ch->carrying)) return TRUE; return FALSE; } // make a common routine to load obj from file 5/30/98 -jtrhone void fill_obj_from_file(obdata *obj, FILE *fl) { int tmp1, tmp2, tmp3, tmp4; char *temp; fscanf(fl, "%d %d %d %d %d %d %d %d %d %d %d %d\n", &tmp1, &tmp2, &tmp3, &tmp4, &obj->objsave_parent, &obj->tmp1, &obj->objsave_self, &obj->tmp2, &obj->objsave_where, &obj->obj_hits, &obj->owner_of, &obj->obj_id); // restore everything of these 4 values except weapons -roa if (!IS_WEAPON(obj)) { obj->value[0] = tmp1; obj->value[1] = tmp2; obj->value[2] = tmp3; obj->value[3] = tmp4; } /* restore instrument name based on player who owns it */ if (OBJ_FLAGGED(obj, ITEM_INSTRUM) && OBJ_ID(obj)) { if (GET_OBJ_VNUM(obj) == 115) { sprintf(buf, "instrument inst %s", inst_names[OBJ_ID(obj)]); obj->name = STR_DUP(buf); strcpy(buf2, NAME(obj->owner_of)); CAP(buf2); sprintf(buf, "%s's %s is lying here.", buf2, inst_names[OBJ_ID(obj)]); obj->description = STR_DUP(buf); sprintf(buf, "%s's %s", buf2, inst_names[OBJ_ID(obj)]); obj->shdesc = STR_DUP(buf); // hit up the actdesc too :) obj->actdesc = STR_DUP(buf); } else if (GET_OBJ_VNUM(obj) == 116) { sprintf(buf, "instrument inst magic magical %s", inst_names[OBJ_ID(obj)]); obj->name = STR_DUP(buf); strcpy(buf2, NAME(obj->owner_of)); CAP(buf2); sprintf(buf, "%s's %%Bmagical%%0 %s is lying here.", buf2, inst_names[OBJ_ID(obj)]); obj->description = STR_DUP(buf); sprintf(buf, "%s's %%Bmagical%%0 %s", buf2, inst_names[OBJ_ID(obj)]); obj->shdesc = STR_DUP(buf); // hit up the actdesc too :) obj->actdesc = STR_DUP(buf); } } } // uses common fill obj from file 5/30/98 -jtrhone BOOL load_room_objects(int hvnum) { FILE *fl; char fname[MAX_STRING_LENGTH]; obdata *obj = NULL; int tmp, hrnum; if (!(hrnum = real_room(hvnum))) { sprintf(buf, "Room #%d does not exist.", hvnum); mudlog(buf, NRM, LEV_IMM, TRUE); return FALSE; } sprintf(fname, "rmobjs/%d.roa", hvnum); if (!(fl = fopen(fname, "rt"))) { if (errno != ENOENT) { sprintf(buf, "Room #%d object loading error.", hvnum); mudlog(buf, NRM, LEV_IMM, TRUE); } return FALSE; } /* read objects here, restore saved stats, give em to room */ while (!feof(fl)) { fscanf(fl, "%d", &tmp); if (!(obj = read_object(tmp, VIRTUAL))) { fgets(buf, 80, fl); /* skip this line */ continue; } else /* lets read this puppy */ { fill_obj_from_file(obj, fl); obj_to_room(obj, hrnum); } } fclose(fl); return TRUE; } // uses common fill obj from file 5/30/98 -jtrhone BOOL load_house_objects(int hvnum) { FILE *fl; char fname[MAX_STRING_LENGTH]; obdata *obj = NULL; int tmp, hrnum; if (!(hrnum = real_room(hvnum))) { sprintf(buf, "House #%d does not exist.", hvnum); mudlog(buf, NRM, LEV_IMM, TRUE); return FALSE; } sprintf(fname, "house/%d.house", hvnum); if (!(fl = fopen(fname, "rt"))) { if (errno != ENOENT) { sprintf(buf, "House #%d object loading error.", hvnum); mudlog(buf, NRM, LEV_IMM, TRUE); } return FALSE; } /* read objects here, restore saved stats, give em to room */ while (!feof(fl)) { fscanf(fl, "%d", &tmp); if (!(obj = read_object(tmp, VIRTUAL))) { fgets(buf, 80, fl); /* skip this line */ continue; } else /* lets read this puppy */ { fill_obj_from_file(obj, fl); obj_to_room(obj, hrnum); } } fclose(fl); return TRUE; } // updated name of instrument to use owner_of for name ref 5/29/98 -jtrhone // uses common fill obj from file 5/30/98 -jtrhone BOOL load_char_objects(chdata *ch) { FILE * fl; char fname[MAX_STRING_LENGTH]; obdata *obj = NULL; int tmp; if (!get_pdata_filename(GET_NAME(ch), "roa", fname)) return FALSE; if (!(fl = fopen(fname, "rt"))) { if (errno != ENOENT) /* failed other than not found */ send_to_char("\n\r********************* NOTICE *********************\n\r" "There was a problem loading your objects from disk.\n\r" "Notify an Immortal for assistance.\n\r", ch); sprintf(buf, "%s entering game with no equipment.", GET_NAME(ch)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE); return FALSE; } if (PLR_FLAGGED(ch, PLR_CRYO)) sprintf(buf1, "cryo-saved "); else if (PLR_FLAGGED(ch, PLR_FORCED)) sprintf(buf1, "force-saved "); else if (PLR_FLAGGED(ch, PLR_CRASH)) sprintf(buf1, "crash-saved "); else strcpy(buf1, ""); sprintf(buf, "%s retrieving %sitems and entering game.", GET_NAME(ch), buf1); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE); REMOVE_BIT(PLR_FLAGS(ch), PLR_CRYO | PLR_CRASH | PLR_FORCED); /* read objects here, restore saved stats, give em to character */ while (!feof(fl)) { fscanf(fl, "%d", &tmp); if (!(obj = read_object(tmp, VIRTUAL))) { fgets(buf, 80, fl); /* skip this line */ continue; } else { fill_obj_from_file(obj, fl); obj_to_char(obj, ch); } } fclose(fl); return TRUE; } /* write an object list to disk along with everything in it */ /* uses recursion to save everything inside of everything else */ BOOL save_object(obdata *obj, FILE *fp) { if (obj) { save_object(obj->contains, fp); save_object(obj->next_content, fp); /* now lets write out this object to disk */ if (obj->item_number >= 0) /* only write if a real object */ fprintf(fp, "%d %d %d %d %d %d %d %d %d %d %d %d %d\n", GET_OBJ_VNUM(obj), obj->value[0], obj->value[1], obj->value[2], obj->value[3], obj->objsave_parent, obj->tmp1, obj->objsave_self, obj->tmp2, obj->objsave_where, obj->obj_hits, obj->owner_of, obj->obj_id); } return TRUE; } /* normal save here, but dont wax em */ void save_char_objects(chdata *ch) { char buf[MAX_INPUT_LENGTH]; FILE * fp; int j; if (IS_NPC(ch)) return; if (check_num(ch) <= 0) { delete_object_file(ch); return; } reduce_num(ch); assign_inventory(ch); if (!get_pdata_filename(GET_NAME(ch), "roa", buf)) return; if (!(fp = fopen(buf, "w"))) return; if (!save_object(ch->carrying, fp)) { fclose(fp); return; } for (j = 0; j < MAX_WEAR; j++) if (EQ(ch, j) && !save_object(EQ(ch, j), fp)) { fclose(fp); return; } fclose(fp); REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH); } /* save everybody's objects to disk, and save players */ void mortal_save(void) { dsdata *d; Descriptors(d) if (D_CHECK(d) && IS_PC(d->character) && PLR_FLAGGED(d->character, PLR_CRASH)) { save_char_objects(d->character); save_char(d->character, NOWHERE); } } /* now, save em to disk, then wax em and set cryo flag */ void cryo_save(chdata *ch) { char buf[MAX_INPUT_LENGTH]; int j; FILE * fp; obdata *obj = NULL; if (IS_NPC(ch)) return; if (check_num(ch) <= 0) { delete_object_file(ch); return; } // extract all norentables from inventory and equipment -roa if (has_norents(ch)) { extract_norents(ch->carrying); for (j = 0; j < MAX_WEAR; j++) if ((obj = EQ(ch, j))) { if (is_unrentable(obj)) obj = unequip_char(ch, j, FALSE); extract_norents(obj); } } if (check_num(ch) > MAX_RENT_S) { sprintf(buf,"Please reduce your inventory to %%6%d%%0 items.\n\r",MAX_RENT_S); send_to_char(buf, ch); sprintf(buf, "%%6Failure%%0 to do so will result in EQ %%6LOSS%%0."); send_to_char(buf, ch); return; } // assign inventory for autowear numbers assign_inventory(ch); // now remove all to inventory for (j = 0; j < MAX_WEAR; j++) if (EQ(ch, j)) obj_to_char(unequip_char(ch, j, FALSE), ch); if (!get_pdata_filename(GET_NAME(ch), "roa", buf)) return; if (!(fp = fopen(buf, "w"))) return; if (!save_object(ch->carrying, fp)) { fclose(fp); return; } fclose(fp); send_to_char("Objects saved.\n\r", ch); extract_objects(ch->carrying); REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH | PLR_FORCED); SET_BIT(PLR_FLAGS(ch), PLR_CRYO); } /* now, save em to disk, then wax em and set forced flag */ void force_save(chdata *ch) { char buf[MAX_INPUT_LENGTH]; int j; FILE * fp; obdata *obj = NULL; if (IS_NPC(ch)) return; if (check_num(ch) <= 0) { delete_object_file(ch); return; } // extract all norentables from inventory and equipment -roa if (has_norents(ch)) { extract_norents(ch->carrying); for (j = 0; j < MAX_WEAR; j++) if ((obj = EQ(ch, j))) { if (is_unrentable(obj)) obj = unequip_char(ch, j, FALSE); extract_norents(obj); } } if (!get_pdata_filename(GET_NAME(ch), "roa", buf)) return; if (!(fp = fopen(buf, "w"))) return; reduce_num(ch); /* force to drop everything over 50 items */ // assign inventory for autowear numbers assign_inventory(ch); for (j = 0; j < MAX_WEAR; j++) if (EQ(ch, j)) obj_to_char(unequip_char(ch, j, FALSE), ch); if (!save_object(ch->carrying, fp)) { fclose(fp); return; } fclose(fp); send_to_char("Objects force-saved.\n\r", ch); extract_objects(ch->carrying); REMOVE_BIT(PLR_FLAGS(ch), PLR_CRASH | PLR_CRYO); SET_BIT(PLR_FLAGS(ch), PLR_FORCED); sprintf(buf, "%s force-saved and extracted.", GET_NAME(ch)); mudlog(buf, BRF, LEV_IMM, FALSE); } /* Procedures for automatic eq positions saving begin here - SL */ /* modified jtrhone -roa */ /* assign_num - assigns a (unique) number to the object and all of its containees, returns the smallest number still unassigned this_obj_id is the first avaliable number for assigning */ int assign_num(chdata *ch, obdata *obj, int this_obj_id) { int next_obj_id; obdata *ob = NULL; if (!ch || !obj) return this_obj_id; /* (re) assign the object itself */ obj->objsave_self = this_obj_id; obj->objsave_parent = -1; obj->objsave_where = -1; next_obj_id = this_obj_id + 1; /* Assign containees */ for(ob = obj->contains;ob;ob = ob->next_content) next_obj_id = assign_num(ch, ob, next_obj_id); return next_obj_id; } /* Now, assign the numbers to all the objects in the char's inventory */ void assign_position_ids(chdata *ch) { int current_id, position; obdata *ob = NULL; /* this is the FIRST object in restore list */ current_id = 0; /* Assign values to eq slots and their containees */ /* only IF there is an obj in particular eq slot */ for(position = 0; position < MAX_WEAR; position++) if (EQ(ch, position)) { current_id = assign_num(ch, EQ(ch, position), current_id); EQ(ch, position)->objsave_where = position; } /* Assign values to everything else ch is carrying */ for(ob = ch->carrying; ob; ob = ob->next_content) current_id = assign_num(ch, ob, current_id); } /* Now, assign parent values to object and all of its containees */ void assign_parents(obdata *obj) { obdata *ob = NULL; if (!obj) return; /* let object know what object_id it is contained in (find parent) */ if(obj->in_obj) obj->objsave_parent = (obj->in_obj)->objsave_self; for(ob = obj->contains; ob; ob = ob->next_content) assign_parents(ob); } // now, assign parents to every obj in char eq and inventory void assign_inventory(chdata *ch) { int i; obdata *ob = NULL; /* give each object on char a unique position id number */ assign_position_ids(ch); /* Now that the numbers are assigned, assign parents in EQ */ for(i = 0; i < MAX_WEAR; i++) assign_parents(EQ(ch, i)); /* and assign parents to the objects in inventory */ for(ob = ch->carrying; ob;ob = ob->next_content) assign_parents(ob); } // OK, function to restore one object recursively... // restore an object and everything inside it... // added bounds check, tightened for loop 5/28/98 -jtrhone void restore_obj_and_contents(chdata *ch, obdata *ob, obdata **ob_array) { int id_count; obdata *tmp_obj = NULL; for (id_count = 0; (tmp_obj = *(ob_array + id_count)) && (id_count < OBJ_ARRAY_SIZE); id_count++) if (tmp_obj->objsave_parent > -1 && tmp_obj->objsave_parent == ob->objsave_self) { restore_obj_and_contents(ch, tmp_obj, ob_array); perform_silent_put(ch, tmp_obj, ob); } } // counts obj + contents + objs in list after it int inv_count(obdata *obj) { int cnt = 0; if (obj) { cnt++; cnt+=inv_count(obj->contains); cnt+=inv_count(obj->next_content); } return cnt; } // adds up obj count of inventory and eq list on char int check_num(chdata *ch) { int cc = 0; int j; if (IS_NPC(ch)) return 0; cc += inv_count(ch->carrying); for (j=0;j<MAX_WEAR; j++) cc += inv_count(EQ(ch, j)); return cc; } // if more than MAX_RENT_S objs on char (eq & inv), force them to drop some stuff // updated for new do_unequip 4/14/98 -jtrhone void reduce_num(chdata *ch) { obdata *obj = NULL; int i; if (IS_NPC(ch)) return; if ((i = check_num(ch)) <= MAX_RENT_S) /* player is ok */ return; sprintf(buf, "Player inventory being reduced: %s (%d items)", GET_NAME(ch), i); mudlog(buf, BRF, LEV_GOD, FALSE); /* place all equipment in player inventory */ sprintf(buf, "all"); do_unequip(ch, buf, 0, 0); send_to_char("%B%1Your inventory is being reduced.%0\n\r",ch); for (i = 0; (check_num(ch) > MAX_RENT_S) && (i < MAX_RENT_S); i++) { assert(ch->carrying); if (ch->in_room < 4) /* not in the game, idle or what not */ extract_cheapest(ch->carrying); else { obj = ch->carrying; act("%B%1WARNING%0: You have been forced to drop $p.", FALSE, ch, obj, 0, TO_CHAR); obj_from_char(obj); float_sink_object(obj, ch->in_room); /* replaces obj_to_room 5/28/98 -jtrhone*/ } } } ACMD(do_icount) { int ic = 0; if (IS_NPC(ch)) return; ic = check_num(ch); sprintf(buf,"Total of %%6%d%%0 items (weight: %%6%d%%0, max: %%6%d%%0).\n\r", ic, IS_CARRYING_W(ch), CAN_CARRY_W(ch)); S2C(); if (ic > MAX_RENT_S) { sprintf(buf,"%%1WARNING!%%0. You have exceeded the MAX rent limit by %%6%d%%0 items.\n\r", (ic - MAX_RENT_S)); S2C(); } } /* the function to restore the inventory */ /* returns 0 if something's screwy, in which case nothing is done */ // tightened up function in anticipation of eq slot revamp 5/28/98 -jtrhone int autowear(chdata *ch) { obdata *objects[OBJ_ARRAY_SIZE]; obdata *ob = NULL; int i; if (IS_NPC(ch)) return 0; /* if obj didnt get assigned a unique # for some reason */ for(ob = ch->carrying;ob;ob = ob->next_content) if (ob->objsave_self < 0) return FALSE; // wipe array memory memset(objects, 0, sizeof(obdata*) * OBJ_ARRAY_SIZE); // make sure they are within limits... reduce_num(ch); for(i = 0, ob = ch->carrying; ob; ob = ob->next_content) { objects[i] = ob; i++; } /* start at first object slot */ /* if they dont have a parent restore everything inside them */ /* if they have parent, they will be restored by restore_obj_and_contents */ for (i = 0; (ob = objects[i]) && i < OBJ_ARRAY_SIZE; i++) if (ob->objsave_parent < 0) restore_obj_and_contents(ch, ob, objects); // Equip the char with any objects in the list // that have an objsave_where position for (i = 0; (ob = objects[i]) && i < OBJ_ARRAY_SIZE; i++) if (ob->objsave_where > -1 && ob->objsave_where < MAX_WEAR) { obj_from_char(ob); if (!equip_char(ch, ob, (ob->objsave_where), FALSE)) { sprintf(buf, "SYSINFO: %s autowear failure (%s).", GET_NAME(ch), ob->shdesc); mudlog(buf, BRF, LEV_IMM, FALSE); S2C(); return FALSE; } } return TRUE; }