/* CrossFire, A Multiplayer game for X-windows Copyright (C) 2002 Mark Wedel & Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at */ #include #include #include #include #include extern void sub_weight (object *, signed long); extern void add_weight (object *, signed long); extern long pticks; /* If flag is non zero, it means that we want to try and save everyone, but * keep the game running. Thus, we don't want to free any information. */ void emergency_save (int flag) { trying_emergency_save = 1; LOG (llevError, "Emergency save: "); for_all_players (pl) { if (!pl->ob) { LOG (llevError, "No name, ignoring this.\n"); continue; } LOG (llevError, "%s ", &pl->ob->name); new_draw_info (NDI_UNIQUE, 0, pl->ob, "Emergency save..."); /* If we are not exiting the game (ie, this is sort of a backup save), then * don't change the location back to the village. Note that there are other * options to have backup saves be done at the starting village */ if (!flag) { strcpy (pl->maplevel, first_map_path); pl->ob->map = 0; pl->ob->x = -1; pl->ob->y = -1; } pl->save (1); } LOG (llevError, "\n"); } /* Delete character with name. if new is set, also delete the new * style directory, otherwise, just delete the old style playfile * (needed for transition) */ void delete_character (const char *name, int newchar) { char buf[MAX_BUF]; sprintf (buf, "%s/%s/%s.pl", settings.localdir, settings.playerdir, name); if (unlink (buf) == -1) LOG (llevDebug, "Cannot delete character file %s: %s\n", buf, strerror (errno)); if (newchar) { sprintf (buf, "%s/%s/%s", settings.localdir, settings.playerdir, name); /* this effectively does an rm -rf on the directory */ remove_directory (buf); } } int create_savedir_if_needed (char *savedir) { struct stat *buf; if ((buf = (struct stat *) malloc (sizeof (struct stat))) == NULL) { LOG (llevError, "Unable to save playerfile... out of memory.\n"); return 0; } else { stat (savedir, buf); if (!S_ISDIR (buf->st_mode)) if (mkdir (savedir, SAVE_DIR_MODE)) { LOG (llevError, "Unable to create player savedir %s: %s\n", savedir, strerror (errno)); return 0; } free (buf); } return 1; } /* * If final is set, it a clean/final save, not a backup, ie dont remove objects from inventory */ void player::save (bool final) { object *tmp, *container = 0; /* Sanity check - some stuff changes this when player is exiting */ if (ob->type != PLAYER || !enable_save || !ns) return; INVOKE_PLAYER (SAVE, ob->contr); object_freezer freezer; int wiz = ob->flag [FLAG_WIZ]; /* Eneq(@csd.uu.se): If we have an open container hide it. */ container = ob->container; ob->container = 0; fprintf (freezer, "password %s\n", password); if (own_title[0] != '\0') fprintf (freezer, "title %s\n", own_title); fprintf (freezer, "explore %d\n", explore); fprintf (freezer, "gen_hp %d\n", gen_hp); fprintf (freezer, "gen_sp %d\n", gen_sp); fprintf (freezer, "gen_grace %d\n", gen_grace); fprintf (freezer, "listening %d\n", listening); fprintf (freezer, "shoottype %d\n", shoottype); fprintf (freezer, "bowtype %d\n", bowtype); fprintf (freezer, "petmode %d\n", petmode); fprintf (freezer, "peaceful %d\n", peaceful); fprintf (freezer, "digestion %d\n", digestion); fprintf (freezer, "pickup %d\n", mode); fprintf (freezer, "outputs_sync %d\n", outputs_sync); fprintf (freezer, "outputs_count %d\n", outputs_count); /* Match the enumerations but in string form */ fprintf (freezer, "usekeys %s\n", usekeys == key_inventory ? "key_inventory" : (usekeys == keyrings ? "keyrings" : "containers")); /* Match the enumerations but in string form */ fprintf (freezer, "unapply %s\n", unapply == unapply_nochoice ? "unapply_nochoice" : (unapply == unapply_never ? "unapply_never" : "unapply_always")); if (ob->map) fprintf (freezer, "map %s\n", ob->map->path); else fprintf (freezer, "map %s\n", settings.emergency_mapname); fprintf (freezer, "savebed_map %s\n", savebed_map); fprintf (freezer, "bed_x %d\nbed_y %d\n", bed_x, bed_y); fprintf (freezer, "weapon_sp %f\n", weapon_sp); fprintf (freezer, "Str %d\n", orig_stats.Str); fprintf (freezer, "Dex %d\n", orig_stats.Dex); fprintf (freezer, "Con %d\n", orig_stats.Con); fprintf (freezer, "Int %d\n", orig_stats.Int); fprintf (freezer, "Pow %d\n", orig_stats.Pow); fprintf (freezer, "Wis %d\n", orig_stats.Wis); fprintf (freezer, "Cha %d\n", orig_stats.Cha); fprintf (freezer, "lev_array %d\n", ob->level > 10 ? 10 : ob->level); for (int i = 1; i <= last_level && i <= 10; i++) { fprintf (freezer, "%d\n", levhp[i]); fprintf (freezer, "%d\n", levsp[i]); fprintf (freezer, "%d\n", levgrace[i]); } freezer.put (ob->contr); fprintf (freezer, "endplst\n"); SET_FLAG (ob, FLAG_NO_FIX_PLAYER); CLEAR_FLAG (ob, FLAG_WIZ); save_object (freezer, ob, 1); /* don't check and don't remove */ char filename[MAX_BUF]; sprintf (filename, "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, &ob->name, &ob->name); make_path_to_file (filename); freezer.save (filename); CLEAR_FLAG (ob, FLAG_NO_FIX_PLAYER); /* Eneq(@csd.uu.se): Reveal the container if we have one. */ ob->container = container; ob->flag [FLAG_WIZ] = wiz; enable_save = !final; } player * player::load (const char *path) { object_thawer thawer (path); /* If no file, must be a new player, so lets get confirmation of * the password. Return control to the higher level dispatch, * since the rest of this just deals with loading of the file. */ if (!thawer) return 0; player *pl = new player; char buf[MAX_BUF], bufall[MAX_BUF]; pl->set_object (object::create ()); pl->last_save_time = time (0); assign (pl->savebed_map, first_map_path); /* Loop through the file, loading the rest of the values */ while (fgets (bufall, MAX_BUF, thawer)) { int value; sscanf (bufall, "%s %d\n", buf, &value); if (!strcmp (buf, "endplst")) break; else if (!strcmp (buf, "oid")) thawer.get (pl, value); else if (!strcmp (buf, "password")) sscanf (bufall, "password %[^\n]", pl->password); else if (!strcmp (buf, "title")) sscanf (bufall, "title %[^\n]", pl->own_title); else if (!strcmp (buf, "explore")) pl->explore = value; else if (!strcmp (buf, "gen_hp")) pl->gen_hp = value; else if (!strcmp (buf, "shoottype")) pl->shoottype = (rangetype) value; else if (!strcmp (buf, "bowtype")) pl->bowtype = (bowtype_t) value; else if (!strcmp (buf, "petmode")) pl->petmode = (petmode_t) value; else if (!strcmp (buf, "gen_sp")) pl->gen_sp = value; else if (!strcmp (buf, "gen_grace")) pl->gen_grace = value; else if (!strcmp (buf, "listening")) pl->listening = value; else if (!strcmp (buf, "peaceful")) pl->peaceful = value; else if (!strcmp (buf, "digestion")) pl->digestion = value; else if (!strcmp (buf, "pickup")) pl->mode = value; else if (!strcmp (buf, "outputs_sync")) pl->outputs_sync = value; else if (!strcmp (buf, "outputs_count")) pl->outputs_count = value; else if (!strcmp (buf, "map")) sscanf (bufall, "map %s", pl->maplevel); else if (!strcmp (buf, "savebed_map")) sscanf (bufall, "savebed_map %s", pl->savebed_map); else if (!strcmp (buf, "bed_x")) pl->bed_x = value; else if (!strcmp (buf, "bed_y")) pl->bed_y = value; else if (!strcmp (buf, "weapon_sp")) sscanf (buf, "weapon_sp %f", &pl->weapon_sp); else if (!strcmp (buf, "Str")) pl->orig_stats.Str = value; else if (!strcmp (buf, "Dex")) pl->orig_stats.Dex = value; else if (!strcmp (buf, "Con")) pl->orig_stats.Con = value; else if (!strcmp (buf, "Int")) pl->orig_stats.Int = value; else if (!strcmp (buf, "Pow")) pl->orig_stats.Pow = value; else if (!strcmp (buf, "Wis")) pl->orig_stats.Wis = value; else if (!strcmp (buf, "Cha")) pl->orig_stats.Cha = value; else if (!strcmp (buf, "usekeys")) { if (!strcmp (bufall + 8, "key_inventory\n")) pl->usekeys = key_inventory; else if (!strcmp (bufall + 8, "keyrings\n")) pl->usekeys = keyrings; else if (!strcmp (bufall + 8, "containers\n")) pl->usekeys = containers; else LOG (llevDebug, "load_player: got unknown usekeys type: %s\n", bufall + 8); } else if (!strcmp (buf, "unapply")) { if (!strcmp (bufall + 8, "unapply_nochoice\n")) pl->unapply = unapply_nochoice; else if (!strcmp (bufall + 8, "unapply_never\n")) pl->unapply = unapply_never; else if (!strcmp (bufall + 8, "unapply_always\n")) pl->unapply = unapply_always; else LOG (llevDebug, "load_player: got unknown unapply type: %s\n", bufall + 8); } else if (!strcmp (buf, "lev_array")) { for (int i = 1; i <= value; i++) { char line[128]; fgets (line, 128, thawer); pl->levhp[i] = atoi (line); fgets (line, 128, thawer); pl->levsp[i] = atoi (line); fgets (line, 128, thawer); pl->levgrace[i] = atoi (line); } /* spell_array code removed - don't know when that was last used. * Even the load code below will someday be replaced by spells being * objects. */ } else LOG (llevDebug, "unparseable line in player file %s: %s\n", path, bufall); } /* this loads the standard objects values. */ load_object (thawer, pl->ob, 0); /* If the map where the person was last saved does not exist, * restart them on their home-savebed. This is good for when * maps change between versions * First, we check for partial path, then check to see if the full * path (for unique player maps) */ if (!has_been_loaded (pl->maplevel) && check_path (pl->maplevel, 1) == -1 && check_path (pl->maplevel, 0) == -1) { strcpy (pl->maplevel, pl->savebed_map); pl->ob->x = pl->bed_x, pl->ob->y = pl->bed_y; } pl->last_save_tick = pticks; INVOKE_PLAYER (LOAD, pl, ARG_STRING (path)); return pl; }