/*************************************************************************** * file: save.c, Database module. Part of DIKUMUD * * Usage: Saving and loading of characters * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * * * * Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse * * Rewritten by MERC Industries, based on crash.c by prometheus * * (Taquin Ho) and abaddon (Jeff Stile). * * You can use our stuff in any way you like whatsoever so long as this * * copyright notice remains intact. If you like it please drop a line * * to mec@garnet.berkeley.edu. * * * * This is free software and you are benefitting. We hope that you * * share your changes too. What goes around, comes around. * ***************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <time.h> #include "structs.h" #include "mob.h" #include "obj.h" #include "utils.h" #include "db.h" #include "handler.h" #include "spells.h" extern struct index_data *obj_index; extern struct room_data *world; void obj_store_to_char(struct char_data *ch, struct obj_file_elem *object); bool put_obj_in_store(struct obj_data *obj, struct char_data *ch, FILE *fpsave); void restore_weight(struct obj_data *obj); void store_to_char(struct char_file_u *st, struct char_data *ch); void char_to_store(struct char_data *ch, struct char_file_u *st); /* * Save a character and inventory. * Would be cool to save NPC's too for quest purposes. */ void save_char_obj( struct char_data *ch ) { struct char_file_u uchar; FILE * fpsave = NULL; char strsave[MAX_INPUT_LENGTH]; int iWear; if ( IS_NPC(ch) || GET_LEVEL(ch) < 2 ) goto LSuccess; sprintf( strsave, "%s/%s", SAVE_DIR, ch->player.name ); if ( ( fpsave = fopen( strsave, "wb" ) ) == NULL ) goto LError; char_to_store( ch, &uchar ); if ( ch->in_room < 2 ) uchar.load_room = world[ch->specials.was_in_room].number; else uchar.load_room = world[ch->in_room].number; if ( fwrite( &uchar, sizeof(uchar), 1, fpsave ) == 0 ) goto LError; if ( !obj_to_store( ch->carrying, ch, fpsave ) ) goto LError; restore_weight( ch->carrying ); for ( iWear = 0; iWear < MAX_WEAR; iWear++ ) { if ( ch->equipment[iWear] ) { if ( !obj_to_store( ch->equipment[iWear], ch, fpsave ) ) goto LError; restore_weight( ch->equipment[iWear] ); } } goto LSuccess; LError: sprintf( log_buf, "Save_char_obj: %s", strsave ); log( log_buf ); LSuccess: if ( fpsave != NULL ) fclose( fpsave ); return; } /* * Load a char and inventory into a new ch structure. */ bool load_char_obj( struct descriptor_data *d, char *name ) { FILE * fpsave = NULL; char strsave[MAX_INPUT_LENGTH]; struct char_file_u uchar; struct char_data *ch; CREATE( ch, struct char_data, 1 ); d->character = ch; clear_char( ch ); ch->desc = d; sprintf( strsave, "%s/%s", SAVE_DIR, name ); if ( ( fpsave = fopen( strsave, "rb" ) ) == NULL ) return FALSE; if ( fread( &uchar, sizeof(uchar), 1, fpsave ) == 0 ) goto LError; reset_char( ch ); GET_NAME(ch) = str_dup(name); store_to_char( &uchar, ch ); if ( world[ch->in_room].zone != world[real_room(3001)].zone ) gain_exp(ch, 0 - MIN(GET_EXP(ch)/2, 10*GET_LEVEL(ch)*GET_LEVEL(ch) )); while ( !feof( fpsave ) ) { struct obj_file_elem object; fread( &object, sizeof(object), 1, fpsave ); if ( ferror( fpsave ) ) goto LError; if ( feof( fpsave ) ) break; obj_store_to_char( ch, &object ); } goto LSuccess; LError: sprintf( log_buf, "Load_char_obj: %s", strsave ); log( log_buf ); if ( fpsave != NULL ) fclose( fpsave ); return FALSE; LSuccess: if ( fpsave != NULL ) fclose( fpsave ); return TRUE; } void obj_store_to_char(struct char_data *ch, struct obj_file_elem *object) { struct obj_data *obj; int j; int nr; void obj_to_char(struct obj_data *object, struct char_data *ch); if ( ( nr = real_object(object->item_number) ) > -1 ) { obj = read_object( nr, 0 ); obj->obj_flags.value[0] = object->value[0]; obj->obj_flags.value[1] = object->value[1]; obj->obj_flags.value[2] = object->value[2]; obj->obj_flags.value[3] = object->value[3]; obj->obj_flags.extra_flags = object->extra_flags; obj->obj_flags.weight = object->weight; obj->obj_flags.timer = object->timer; obj->obj_flags.eq_level = object->eq_level; obj->obj_flags.bitvector = object->bitvector; for(j=0; j<MAX_OBJ_AFFECT; j++) obj->affected[j] = object->affected[j]; obj_to_char(obj, ch); } } bool obj_to_store( struct obj_data *obj, struct char_data *ch, FILE *fpsave ) { struct obj_data *tmp; if ( obj == NULL ) return TRUE; /* Write depth first (so weights come out right) */ if ( !obj_to_store( obj->contains, ch, fpsave ) ) return FALSE; if ( !obj_to_store( obj->next_content, ch, fpsave) ) return FALSE; if ( !put_obj_in_store( obj, ch, fpsave ) ) return FALSE; /* Adjust container weights of up-linked items */ for ( tmp = obj->in_obj; tmp; tmp = tmp->in_obj ) GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); return TRUE; } /* * Write one object to the file. */ bool put_obj_in_store(struct obj_data *obj, struct char_data *ch, FILE *fpsave) { int iAffect; struct obj_file_elem object; if ( GET_ITEM_TYPE(obj) == ITEM_KEY ) return TRUE; object.version = 0; object.item_number = obj_index[obj->item_number].virtual; object.value[0] = obj->obj_flags.value[0]; object.value[1] = obj->obj_flags.value[1]; object.value[2] = obj->obj_flags.value[2]; object.value[3] = obj->obj_flags.value[3]; object.extra_flags = obj->obj_flags.extra_flags; object.weight = obj->obj_flags.weight; object.timer = obj->obj_flags.timer; object.eq_level = obj->obj_flags.eq_level; object.bitvector = obj->obj_flags.bitvector; for ( iAffect = 0; iAffect < MAX_OBJ_AFFECT; iAffect++ ) object.affected[iAffect] = obj->affected[iAffect]; if ( fwrite( &object, sizeof(object), 1, fpsave ) == 0 ) return FALSE; return TRUE; } /* * Restore container weights after a save. */ void restore_weight(struct obj_data *obj) { struct obj_data *tmp; if ( obj == NULL ) return; restore_weight( obj->contains ); restore_weight( obj->next_content ); for ( tmp = obj->in_obj; tmp; tmp = tmp->in_obj ) GET_OBJ_WEIGHT( tmp ) += GET_OBJ_WEIGHT( obj ); } void store_to_char(struct char_file_u *st, struct char_data *ch) { int i; strncpy( ch->pwd, st->pwd, 11 ); GET_SEX(ch) = st->sex; GET_CLASS(ch) = st->class; GET_LEVEL(ch) = st->level; ch->player.short_descr = 0; ch->player.long_descr = 0; if (*st->title) { CREATE(ch->player.title, char, strlen(st->title) + 1); strcpy(ch->player.title, st->title); } else GET_TITLE(ch) = 0; if (*st->description) { CREATE(ch->player.description, char, strlen(st->description) + 1); strcpy(ch->player.description, st->description); } else ch->player.description = 0; ch->player.hometown = st->hometown; ch->player.time.birth = st->birth; ch->player.time.played = st->played; ch->player.time.logon = time(0); for (i = 0; i < MAX_TONGUE; i++) ch->player.talks[i] = st->talks[i]; ch->player.weight = st->weight; ch->player.height = st->height; ch->abilities = st->abilities; ch->tmpabilities = st->abilities; ch->points = st->points; if (ch->points.max_mana < 100) { ch->points.max_mana = 100; } /* if */ for (i = 0; i < MAX_SKILLS; i++) ch->skills[i] = st->skills[i]; ch->specials.practices = st->practices; ch->specials.alignment = st->alignment; ch->specials.act = st->act; ch->specials.carry_weight = 0; ch->specials.carry_items = 0; ch->points.armor = 100; ch->points.hitroll = 0; ch->points.damroll = 0; /* Not used as far as I can see (Michael) */ for(i = 0; i <= 4; i++) ch->specials.apply_saving_throw[i] = st->apply_saving_throw[i]; for(i = 0; i <= 2; i++) GET_COND(ch, i) = st->conditions[i]; /* Add all spell effects */ for(i=0; i < MAX_AFFECT; i++) { if (st->affected[i].type) affect_to_char(ch, &st->affected[i]); } ch->in_room = real_room(st->load_room); affect_total(ch); } /* copy vital data from a players char-structure to the file structure */ void char_to_store(struct char_data *ch, struct char_file_u *st) { int i; struct affected_type *af; struct obj_data *char_eq[MAX_WEAR]; strncpy( st->pwd, ch->pwd, 11 ); /* Unaffect everything a character can be affected by */ for(i=0; i<MAX_WEAR; i++) { if (ch->equipment[i]) char_eq[i] = unequip_char(ch, i); else char_eq[i] = 0; } for(af = ch->affected, i = 0; i<MAX_AFFECT; i++) { if (af && (!(af->type == SPELL_INVISIBLE && af->bitvector == AFF_INVISIBLE && ch->specials.wizInvis))) { st->affected[i] = *af; st->affected[i].next = 0; /* subtract effect of the spell or the effect will be doubled */ affect_modify( ch, st->affected[i].location, st->affected[i].modifier, st->affected[i].bitvector, FALSE); af = af->next; } else { st->affected[i].type = 0; /* Zero signifies not used */ st->affected[i].duration = 0; st->affected[i].modifier = 0; st->affected[i].location = 0; st->affected[i].bitvector = 0; st->affected[i].next = 0; } } if ((i >= MAX_AFFECT) && af && af->next) log( "Char_to_store: too many affects." ); ch->tmpabilities = ch->abilities; st->version = 0; st->birth = ch->player.time.birth; st->played = ch->player.time.played; st->played += (long) (time(0) - ch->player.time.logon); st->last_logon = time(0); ch->player.time.played = st->played; ch->player.time.logon = time(0); st->hometown = ch->player.hometown; st->weight = GET_WEIGHT(ch); st->height = GET_HEIGHT(ch); st->sex = GET_SEX(ch); st->class = GET_CLASS(ch); st->level = GET_LEVEL(ch); st->abilities = ch->abilities; st->points = ch->points; st->alignment = ch->specials.alignment; st->practices = ch->specials.practices; st->act = ch->specials.act; st->points.armor = 100; st->points.hitroll = 0; st->points.damroll = 0; if (GET_TITLE(ch)) strcpy(st->title, GET_TITLE(ch)); else *st->title = '\0'; if (ch->player.description) strcpy(st->description, ch->player.description); else *st->description = '\0'; for (i = 0; i < MAX_TONGUE; i++) st->talks[i] = ch->player.talks[i]; for (i = 0; i < MAX_SKILLS; i++) st->skills[i] = ch->skills[i]; strcpy(st->name, GET_NAME(ch) ); for(i = 0; i <= 4; i++) st->apply_saving_throw[i] = ch->specials.apply_saving_throw[i]; for(i = 0; i <= 2; i++) st->conditions[i] = GET_COND(ch, i); for(af = ch->affected, i = 0; i<MAX_AFFECT; i++) { if (af && (!(af->type == SPELL_INVISIBLE && af->bitvector == AFF_INVISIBLE && ch->specials.wizInvis))) { /* Add effect of the spell or it will be lost */ /* When saving without quitting */ affect_modify( ch, st->affected[i].location, st->affected[i].modifier, st->affected[i].bitvector, TRUE); af = af->next; } } for(i=0; i<MAX_WEAR; i++) { if (char_eq[i]) equip_char(ch, char_eq[i], i); } affect_total(ch); }