tbamud/src/objsave.c
2025-09-30 07:39:45 -07:00

988 lines
30 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**************************************************************************
* 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 were 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: "#<vnum>" (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 <locate> less than 1 below this: (equipped items
* are assumed to have <locate>==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 <locate> 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 <locate>: If there's already a list of contents with the
* same <locate> put obj to it if not, start a new list. Since <locate> 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;
}