mirror of
https://github.com/tbamud/tbamud.git
synced 2026-04-01 09:57:19 +02:00
915 lines
26 KiB
C
915 lines
26 KiB
C
/**************************************************************************
|
|
* 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 <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 (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;
|
|
}
|