nexus/save.c

1411 lines
36 KiB
C

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include "defs.h"
#include "flag.h"
#include "io.h"
#include "lf.h"
#include "map.h"
#include "move.h"
#include "nexus.h"
#include "objects.h"
#include "save.h"
#include "text.h"
#include "vault.h"
extern long curtime;
extern lifeform_t *player;
extern map_t *firstmap;
extern race_t *firstrace;
extern knowledge_t *knowledge;
extern region_t *firstregion,*lastregion;
extern regionoutline_t *firstregionoutline,*lastregionoutline;
extern map_t *heaven;
extern long curtime, gamedays, gamesecs;
extern enum GAMEMODE gamemode;
// returns TRUE if we successfully loaded a save file
int loadall(void) {
//int db = B_FALSE;
int foundsavegame = B_FALSE;
region_t *heavenregion;
gamemode = GM_LOADING;
if (loadsavegame()) {
// update fixed region pointers
heavenregion = findregionbytype(BH_HEAVEN);
heaven = findregionmap(heavenregion->id, 1);
foundsavegame = B_TRUE;
}
gamemode = GM_LOADED;
return foundsavegame;
}
int loadflagpile(FILE *f, flagpile_t *fp) {
flag_t tempflag;
flag_t *fl;
char buf[BUFLEN];
int rv;
int skid;
int db = B_FALSE;
rv = fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%ld,%d\n",
&tempflag.id, &tempflag.nvals, &tempflag.val[0], &tempflag.val[1], &tempflag.val[2],&tempflag.lifetime, &tempflag.known,&tempflag.obfrom,&skid);
while (tempflag.id != -1) {
dblog("got flag id=%d\n",tempflag.id);
// get flag text
fgets(buf, BUFLEN, f);
buf[strlen(buf)-1] = '\0'; // strip newline
fl = addflag_real(fp, tempflag.id,
tempflag.val[0],
tempflag.val[1],
tempflag.val[2], strcmp(buf, "^^^") ? buf : NULL, tempflag.lifetime, tempflag.known, tempflag.obfrom);
fl->skillfrom = findskill((enum SKILL)skid);
if (db) {
dblog("--> added flag id=%d. v0=%d, v1=%d, v2=%d, text=%s, lifetime=%d, known=%d, obfrom=%ld\n",fl->id,
fl->val[0], fl->val[1], fl->val[2], fl->text ? fl->text : "(null)", fl->lifetime, fl->known,fl->obfrom);
}
// load next one
rv = fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%ld\n",
&tempflag.id, &tempflag.nvals, &tempflag.val[0], &tempflag.val[1], &tempflag.val[2],&tempflag.lifetime, &tempflag.known,&tempflag.obfrom);
//dblog("fscanf returned %d\n",rv);
}
return B_FALSE;
}
int loadknowledge(FILE *f) {
int db = B_FALSE;
char buf[BUFLEN];
char line[BUFLEN];
int otid,known;
char hiddenname[BUFLEN];
char *p,*dummy;
if (db) dblog("--> Loading knowledge...\n");
fscanf(f, "startknowledge\n");
fgets(buf, BUFLEN, f);
buf[strlen(buf)-1] = '\0'; // strip newline
while (strcmp(buf, "endknowledge")) {
//sscanf(buf, "%d^%s^%d",&otid, hiddenname, &known);
strcpy(line, buf);
p = strtok_r(line, "^", &dummy);
otid = atoi(p);
p = strtok_r(NULL, "^", &dummy);
strcpy(hiddenname, p);
p = strtok_r(NULL, "^", &dummy);
known = atoi(p);
addknowledge(otid, hiddenname, known);
// get next line
fgets(buf, BUFLEN, f);
buf[strlen(buf)-1] = '\0'; // strip newline
}
return B_FALSE;
}
int loadvars(FILE *f) {
int id;
int db = B_FALSE;
race_t *r;
if (db) dblog("--> Loading knowledge...\n");
fscanf(f, "startvars\n");
fscanf(f, "curtime:%ld\n",&curtime);
fscanf(f, "endvars\n");
fscanf(f, "startknownraces\n");
fscanf(f, "%d\n", &id);
while (id != -1) {
r = findrace(id);
r->known = B_TRUE;
fscanf(f, "%d\n", &id);
}
fscanf(f, "endknownraces\n");
return B_FALSE;
}
// load and allocate one lifeform from given file
lifeform_t *loadlf(FILE *f, cell_t *where) {
lifeform_t *l;
int lfid, lfraceid;
long obid;
int rv;
int i;
char buf[BUFLEN];
int obcount;
int mapid;
map_t *m;
int x,y,level,newlevel;
int db = B_TRUE;
int matid;
if (db) dblog("--> Loading lifeform...\n");
fscanf(f, "startlf\n");
fscanf(f, "lfid: %d\n",&lfid);
fscanf(f, "race: %d\n",&lfraceid);
fscanf(f, "map: %d\n",&mapid);
fscanf(f, "pos: %d,%d\n",&x, &y);
fscanf(f, "level: %d\n",&level);
fscanf(f, "newlevel: %d\n",&newlevel);
// find the map
m = findmap(mapid);
if (!m) {
dblog("ERROR loading lf id %d: can't find map id %d\n",lfid,mapid);
exit(1);
}
if (where == NULL) {
where = getcellat(m, x, y);
}
if (!m) {
dblog("ERROR loading lf id %d: can't find cell at %d,%d on map id %d\n",lfid,x,y,mapid);
exit(1);
}
l = addlf(where, lfraceid,level);
l->id = lfid;
l->x = x;
l->y = y;
l->newlevel = newlevel;
// load rest of this lf
fscanf(f, "str: %d/%d\n",&l->att[A_STR],&l->baseatt[A_STR]);
fscanf(f, "dex: %d/%d\n",&l->att[A_AGI],&l->baseatt[A_AGI]);
fscanf(f, "int: %d/%d\n",&l->att[A_IQ],&l->baseatt[A_IQ]);
fscanf(f, "xp: %ld\n",&l->xp);
fscanf(f, "skillxp: %ld\n",&l->skillxp);
fscanf(f, "totskillpoints: %ld\n",&l->totskillpoints);
fscanf(f, "skp: %d\n",&l->skillpoints);
fscanf(f, "contr: %d\n",&l->controller);
fscanf(f, "hp: %d/%d\n",&l->hp, &l->maxhp);
fscanf(f, "mp: %d/%d\n",&l->mp, &l->maxmp);
fscanf(f, "stamina: %f\n",&l->stamina);
fscanf(f, "alive: %d\n",&l->alive);
fscanf(f, "lastdamtype: %d\n",(int *)&l->lastdamtype);
fgets(buf, BUFLEN, f); // lastdam
buf[strlen(buf)-1] = '\0'; // strip newline
l->lastdam = strdup(buf + 9); // after 'lastdam: '
fscanf(f, "material: %d\n",&matid);
l->material = findmaterial(matid);
fscanf(f, "timespent: %d\n",&l->timespent);
fscanf(f, "sorted: %d\n",&l->sorted);
fscanf(f, "polyrevert: %d\n",&l->polyrevert);
fscanf(f, "forgettimer: %f\n",&l->forgettimer);
//fscanf(f, "eyeadj: %d\n",&l->eyeadjustment);
fscanf(f, "facing: %d\n",&l->facing);
fscanf(f, "turncounter: %d\n",&l->turncounter);
fscanf(f, "rotated: %d\n",&l->rotated);
if (db) dblog("--> Got hp=%d/%d. timespend=%d. sorted=%d. Now loading flags.",l->hp,l->maxhp,l->timespent,l->sorted);
// clear existing flags from addlf
while (l->flags->first) {
killflag(l->flags->first);
}
// load lf flags
loadflagpile(f, l->flags);
if (db) dblog("--> now loading objects");
// load object list
obcount = 0;
obid = 9999; // for testing
rv = fscanf(f, "ob:%ld\n",&obid);
while (obid != -1) {
if (db) dblog("--> Load ob id %d into list...",obid);
l->pack->oblist[obcount] = obid;
obcount++;
fscanf(f, "ob:%ld\n",&obid);
}
// terminate with -1s!
for (i = obcount ; i < MAXPILEOBS; i++) {
l->pack->oblist[i] = -1;
}
if (db) dblog("--> Finished oblist. Found %d objects.",obcount);
// now load object defs for this player!
fscanf(f, "obdefs\n");
for (i = 0; i < obcount; i++) {
long thisid;
if (db) dblog("--> Creating ob #%d for lf.",i);
//if (db) dblog("-----> ob %d/%d...\n",i+1,obcount);
if (loadob(f, l->pack, &thisid)) {
dblog("Error - can't create object %d/%d!\n",i+1,obcount);
exit(1);
}
if (db) dblog("----> done (id=%ld)",thisid);
}
// now repeat the above for polypack...
obcount = 0;
obid = 9998; // for testing
rv = fscanf(f, "polyob:%ld\n",&obid);
while (obid != -1) {
if (db) dblog("--> Load ob id %d into polypack list...",obid);
l->polypack->oblist[obcount] = obid;
obcount++;
fscanf(f, "polyob:%ld\n",&obid);
}
// terminate with -1s!
for (i = obcount ; i < MAXPILEOBS; i++) {
l->polypack->oblist[i] = -1;
}
if (db) dblog("--> Finished polypack oblist. Found %d objects.",obcount);
// now load object defs for this lf's pack!
fscanf(f, "polyobdefs\n");
for (i = 0; i < obcount; i++) {
long thisid;
if (db) dblog("--> Creating polypack ob #%d for lf.",i);
//if (db) dblog("-----> ob %d/%d...\n",i+1,obcount);
if (loadob(f, l->polypack, &thisid)) {
dblog("Error - can't create object %d/%d!\n",i+1,obcount);
exit(1);
}
if (db) dblog("----> done (id=%ld)",thisid);
}
// is this the player?
if (l->controller == C_PLAYER) {
player = l;
}
sortlf(l->cell->map, l);
return l;
}
map_t *loadmap(FILE *f) {
char buf[BUFLEN];
int obcount;
int i;
int x,y;
int db = B_TRUE;
enum HABITAT habitatid;
int illum;
lifeform_t *l;
object_t *o;
map_t *m;
cell_t *dummycell;
int regionid;
// create map
m = addmap();
dummycell = malloc(sizeof(cell_t));
dummycell->obpile = addobpile(NULL, dummycell, NULL);
dummycell->map = m;
dummycell->type = (celltype_t *)DUMMYCELLTYPE; // for debugging
if (!m) {
printf("Error creating map while loading.");
return NULL;
}
// load map info
if (db) dblog("--> Loading map info...\n");
fscanf(f, "id:%d\n",&m->id); // map id
fscanf(f, "region:%d\n",&regionid); // region id
m->region = findregion(regionid);
fscanf(f, "depth:%d\n",&m->depth); // map depth
fgets(buf, BUFLEN, f); // map name
buf[strlen(buf)-1] = '\0'; // strip newline
m->name = strdup(buf + 5); // after 'name:'
fscanf(f, "habitat:%d\n",(int *)&habitatid); // habitat
m->habitat = findhabitat(habitatid);
fscanf(f, "seed:%u\n",&m->seed); // seed
fscanf(f, "lastplayervisit:%ld\n",&m->lastplayervisit);
fscanf(f, "dims:%d,%d\n",&m->w, &m->h); // map dimensons
fscanf(f, "illumination:%d\n",&illum);
m->illumination = illum;
fscanf(f, "nextmaps:\n");
for (i = 0; i < MAXDIR_ORTH; i++) {
fscanf(f, "%d\n",&m->nextmap[i]); // map dimensons
}
if (db) dblog("--> Finished map info. name='%s', dims=%d x %d\n",m->name, m->w, m->h);
fscanf(f, "beingcreated:%d\n",&m->beingcreated);
fscanf(f, "id:%d\n",&m->id); // map id
// load lifeforms
if (db) dblog("--> Loading lifeforms...\n");
fscanf(f, "lifeforms:\n");
fscanf(f, "%s\n",buf);
while (!strcmp(buf ,"startlf")) {
loadlf(f, dummycell);
// check for more lifeforms...
fscanf(f, "%s\n",buf);
}
// load room defs
fscanf(f, "nrooms:%d\n",&m->nrooms);
for (i = 0; i < m->nrooms; i++) {
char line[BUFLEN];
fgets(line, BUFLEN, f);
//if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0';
sscanf(line, "%d,%d,%d,%d,%d,%[ a-zA-Z_0-9+'*/-]\n",&m->room[i].id,
&m->room[i].x1, &m->room[i].y1,
&m->room[i].x2, &m->room[i].y2, buf);
m->room[i].vault = findvault(buf);
}
// load cells
if (db) dblog("--> Loading map cells...\n");
fscanf(f, "cells:\n");
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
celltype_t *ct;
int celltypeid;
long obid;
int temphab;
int roomid;
//if (db) dblog("cell %d,%d...",x,y);
// allocate this cell
c = addcell(m, x, y);
// cell info
fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
&roomid, &celltypeid, &c->known, &c->knowntime, &c->knownglyph.ch, &c->knownglyph.colour, &c->visited, &c->locked, &temphab, &c->isroomwall, &c->temperature);
c->habitat = findhabitat(temphab);
c->room = findroom(m, roomid);
ct = findcelltype(celltypeid);
if (ct) {
c->type = ct;
} else {
dblog("ERROR loading map cells - can't find celltype id %ld\n",celltypeid);
exit(1);
}
// cell objects
obcount = 0;
fscanf(f, "ob:%ld\n",&obid);
while (obid != -1) {
c->obpile->oblist[obcount] = obid;
obcount++;
fscanf(f, "ob:%ld\n",&obid);
}
// terminate with -1s!
for (i = obcount ; i < MAXPILEOBS; i++) {
c->obpile->oblist[i] = -1;
}
}
}
// load object definitions
if (db) dblog("--> Loading object definitions for map obs...\n");
fscanf(f, "MAPOBS:%d\n",&obcount); // how many obs?
// create all objects in a dummy cell
for (i = 0; i < obcount; i++) {
long thisid;
if (db) dblog("-----> loading into dummycell: mapob %d/%d...\n",i+1,obcount);
if (loadob(f, dummycell->obpile, &thisid)) {
dblog("Error - can't create object %d/%d!\n",i+1,obcount);
exit(1);
}
if (db) dblog("----------> got obid %ld\n",thisid);
// check!
if (!hasobid(dummycell->obpile, thisid)) {
dblog("Error: after loading obid %ld, can't find it in dummycell!\n",thisid);
exit(1);
}
}
// hand out the objects to lifeforms...
/*
for (l = m->lf ; l ; l = l->next) {
int curob = 0;
long obid;
obid = l->pack->oblist[curob];
while (obid != -1) {
int found = B_FALSE;
// find this ob id in the dummycell
for (o = dummycell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->id == obid) {
relinkob(o, l->pack);
found = B_TRUE;
break;
}
}
if (!found) {
dblog("Error loading obs - lf %d should have obid %ld but can't find it in dummycell.\n",l->id, obid);
exit(1);
}
// next one
curob++;
obid = l->pack->oblist[curob];
}
// clear the oblist
for (i = 0; i < MAXPILEOBS; i++) {
l->pack->oblist[i] = -1;
}
}
*/
// hand out objects to cells
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
long obid;
int curob = 0;
c = m->cell[y * m->w + x];
obid = c->obpile->oblist[curob];
dblog("Handing out objects to cell %d,%d\n",x,y);
while (obid != -1) {
dblog(" Looking for obid %ld in dummycell...",obid);
// find this ob id in the dummycell
o = hasobid(dummycell->obpile, obid);
if (o) {
dblog(" Got it.");
relinkob(o, c->obpile);
} else {
dblog(" Error loading obs - cell %d,%d should have obid %ld but can't find it.\n",x,y, obid);
exit(1);
}
// next one
curob++;
obid = c->obpile->oblist[curob];
}
// clear the oblist
for (i = 0; i < MAXPILEOBS; i++) {
c->obpile->oblist[i] = -1;
}
}
}
// load flags
loadflagpile(f, m->flags);
// we are now finished loading from the file.
// move lifeforms to their proper locations
for (l = m->lf ; l ; l = l->next) {
cell_t *c;
c = getcellat(m, l->x, l->y);
if (!c) {
dblog("Error loading map - Can't find cell at %d,%d to place lifeform id %d.\n",
l->x, l->y, l->id);
exit(1);
}
movelf(l, c, B_FALSE);
//dblog("Moving lf %d to %d,%d\n",l->id, l->x, l->y);
}
free(dummycell);
return m;
}
int loadob(FILE *f, obpile_t *op, long *id) {
objecttype_t *ot;
object_t *o;
material_t *mat;
long obid;
int otid,matid;
char buf[BUFLEN];
int db = B_TRUE;
fscanf(f, "id:%ld\n",&obid);
fscanf(f, "type:%d\n",&otid);
if (db) dblog("... loading object id %ld",obid);
if (id) {
*id = obid;
}
ot = findot(otid);
if (!ot) {
dblog("ERROR loading objects - can't find obtype id %d (obj id %ld)\n",otid,obid);
return B_TRUE;
}
// create the object
o = addobject(op, NULL, B_NOSTACK, B_FALSE, ot->id); // no stacking!
// overwrite ob parameters
o->id = obid;
fscanf(f, "material:%d\n",&matid);
mat = findmaterial(matid);
if (!mat) {
dblog("ERROR loading objects - can't find material %d\n",matid);
return B_TRUE;
}
o->material = mat;
fscanf(f, "weight:%f\n",&o->weight);
fgets(buf, BUFLEN, f); // inscription
buf[strlen(buf)-1] = '\0'; // strip newline
o->inscription = strdup(buf + 6); // after 'inscr:'
if (!strcmp(o->inscription, "^^^")) { // ie. if equal to ^^^...
free(o->inscription);
o->inscription = NULL;
}
fscanf(f, "letter:%c\n",&o->letter);
fscanf(f, "bless:%d\n",&o->blessed);
fscanf(f, "blessknown:%d\n",&o->blessknown);
fscanf(f, "amt:%d\n",&o->amt);
fscanf(f, "birthtime:%ld\n",&o->birthtime);
// now remove ALL obejct flags (which were inherited
// from the obtype during addobject() ). these will be
// loaded in instead.
while (o->flags->first) {
killflag(o->flags->first);
}
fscanf(f, "flags:\n");
dblog("About to start loading object flags...");
loadflagpile(f, o->flags);
fgets(buf, BUFLEN, f);// either 'obcontents:xx' or 'endob'
if (strstarts(buf, "obcontents")) {
// load this object's contents...
char *p;
char buf2[BUFLEN];
int ncontents;
int i;
//dblog("got obcontents");
// strip newline
buf[strlen(buf)-1] = '\0';
p = readuntil(buf2, buf, ':'); // ignore bit before :
p = readuntil(buf2, p, ')'); // ) will really be eol
ncontents = atoi(buf2);
for (i = 0 ; i < ncontents; i++) {
loadob(f, o->contents, NULL);
}
fgets(buf, BUFLEN, f);// 'endobcontents'
//dblog("want endobcontents, got: '%s'", buf);
fgets(buf, BUFLEN, f);// 'endob'
//dblog("want endob, got: '%s'", buf);
}
if (strstarts(buf, "endob")) {
// we got 'endob'
} else {
dblog("ERROR loading objects - expecting 'endob' but got '%s'\n",buf);
exit(1);
}
//fscanf(f, "endob\n");
return B_FALSE;
}
int loadregions(FILE *f) {
int rtid,nthings,i,n;
int numoutlines,numregions;
int db = B_TRUE;
int depthmod;
fscanf(f, "start_regions\n");
fscanf(f, "numoutlines:%d\n",&numoutlines);
if (db) dblog("Found %d region outlines.\n",numoutlines);
for (n = 0; n < numoutlines; n++) {
regionthing_t *thing;
if (db) dblog(" loading regionoutline #%d / %d",n+1, numoutlines);
fscanf(f, "startro\n");
fscanf(f, "rtypeid:%d\n",&rtid); // region type id
addregionoutline(rtid);
fscanf(f, "nthings:%d\n",&nthings);
for (i = 0; i < nthings; i++) {
int depth,x,y,val,id;
enum REGIONTHING whatkind;
char buf[BUFLEN];
fscanf(f, "startthing\n");
fscanf(f, " thingid:%d\n",&id);
fscanf(f, " thingdepth:%d,%d,%d\n",&depth, &x, &y);
fscanf(f, " thingkind:%d\n",(int *)&whatkind);
fscanf(f, " thingval:%d\n",&val);
fscanf(f, " thingwhat:%[ a-zA-Z_0-9+'*/-]\n",buf);
fscanf(f, "endthing\n");
// replace ^ with ' ' in thingwhat
/*
for (p = buf ; *p; p++) {
if (*p == '^') *p = ' ';
}
*/
if (db) dblog(" got regionthing: [%s]", buf);
thing = addregionthing(lastregionoutline, depth, x, y, whatkind, val, streq(buf, "NULL") ? NULL : buf);
thing->id = id;
}
fscanf(f, "endro\n");
if (db) dblog("Loaded regionoutline #%d / %d",n+1, numoutlines);
}
// now load in the actual region->outline mappings
fscanf(f, "numregions:%d\n",&numregions);
if (db) dblog("Found %d regions.\n",numregions);
for (n = 0; n < numregions; n++) {
region_t *r;
enum BRANCH rtid;
int rid;
int outlineid,parentid,nthings,createdby;
fscanf(f, "startregion\n");
fscanf(f, " rid:%d\n",(int *)&rid);
fscanf(f, " rtypeid:%d\n",(int *)&rtid);
fscanf(f, " outline:%d\n",&outlineid);
fscanf(f, " parentregion:%d\n",&parentid);
fscanf(f, " nthings:%d\n",&nthings);
fscanf(f, " depthmod:%d\n",&depthmod);
fscanf(f, " createdby:%d\n",&createdby);
fscanf(f, "endregion\n");
r = addregion(rtid, (parentid == -1) ? NULL : findregion(parentid), outlineid, depthmod, createdby);
r->id = rid;
r->nthings = nthings;
if (db) dblog("Loaded region #%d / %d",n+1, numregions);
}
fscanf(f, "end_regions\n");
return B_FALSE;
}
// returns TRUE if there was a savegame file.
int loadsavegame(void) {
char filename[BUFLEN];
FILE *f;
int mapcount,i;
// check for a save file...
snprintf(filename, BUFLEN, "%s/game.sav",SAVEDIR);
f = fopen(filename, "rt");
if (!f) {
// this isn't an error - just means no savegames
dblog("No savegame found.");
return B_FALSE;
}
// load region outlines
if (loadregions(f)) {
printf("No region data found in savegame file!");
exit(1);
}
// load maps
fscanf(f, "start_maps:%d\n",&mapcount);
for (i = 0; i < mapcount; i++) {
if (!loadmap(f)) {
printf("Error loading map %d of %d!", i+1, mapcount);
exit(1);
}
}
fscanf(f, "end_maps\n");
// load world data
if (loadworlddata(f)) {
printf("Error load world data from save file.");
exit(1);
}
// load player
if (!loadlf(f, NULL)) {
printf("Error loading player from save file.");
exit(1);
}
// load knowledge
if (loadknowledge(f)) {
printf("Error loading knowledge from save file.");
exit(1);
}
// load game variables
if (loadvars(f)) {
printf("Error loading game variables from save file.");
exit(1);
}
// successful load - close and kill the savegame file
fclose(f);
unlink(filename);
return B_TRUE;
}
int savevars(FILE *f) {
int db = B_FALSE;
race_t *r;
if (db) dblog("--> Saving knowledge...\n");
fprintf(f, "startvars\n");
fprintf(f, "curtime:%ld\n",curtime);
fprintf(f, "endvars\n");
fprintf(f, "startknownraces\n");
for (r = firstrace ; r ; r = r->next) {
if (r->known) {
fprintf(f, "%d\n",r->id);
}
}
fprintf(f, "-1\n");
fprintf(f, "endknownraces\n");
return B_FALSE;
}
int loadworlddata(FILE *f) {
fscanf(f, "timedata:%ld,%ld,%ld\n",&curtime,&gamedays,&gamesecs);
return B_FALSE;
}
int removevaultfile(char *filename) {
if (unlink(filename)) {
msg("ERROR: Could not unlink bones file after creation ('%s')", filename);
dblog("ERROR: Could not unlink bones file after creation ('%s')", filename);
return B_TRUE;
}
return B_FALSE;
}
// dynamically create a temporary vault file based on the given room.
int savebones(map_t *m, room_t *r) {
cell_t *c;
int x,y;
FILE *f;
char filename[BUFLEN];
DIR *dp;
struct dirent *ep;
int maxid = 0, bonesid;
celltype_t *ct;
char ch;
char nextch = 'a';
celltype_t *legendct[MAXCANDIDATES];
char legendch[MAXCANDIDATES];
int nlegends = 0,n;
// get list of existing bones files
dp = opendir(BONESDIR);
if (dp != NULL) {
while ((ep = readdir(dp)) != NULL) {
char thisname[BUFLEN];
char *p;
strcpy(thisname, ep->d_name);
p = strstr(thisname, "bones");
if (p) {
char numbuf[BUFLEN];
char *np;
int thisid;
p += 5;
for (np = numbuf; isdigit(*p); p++, np++) {
*np = *p;
}
*np = '\0';
thisid = atoi(numbuf);
if (thisid > maxid) maxid = thisid;
}
}
closedir(dp);
}
bonesid = maxid + 1;
snprintf(filename, BUFLEN, "%s/bones%d.vlt",BONESDIR, bonesid);
f = fopen(filename, "wt");
if (!f) {
msg("Could not write bones file '%s'!", filename);
return B_TRUE;
}
fprintf(f, "@id:bones%d\n", bonesid);
fprintf(f, "@map\n");
for (y = r->y1; y <= r->y2; y++) {
for (x = r->x1; x <= r->x2; x++) {
c = getcellat(m, x, y);
// do we already ahve a legend entry for this celltype?
ct = NULL;
ch = '\0';
for (n = 0; n < nlegends; n++ ){
if (legendct[n] == c->type) {
ch = legendch[n];
break;
}
}
if (ch == '\0') {
ch = nextch;
legendct[nlegends] = c->type;
legendch[nlegends] = ch;
nlegends++;
if (nextch == 'z') nextch = 'A';
else if (nextch == 'Z') nextch = '0';
else nextch++;
}
// wall
fprintf(f, "%c", ch);
}
fprintf(f, "\n");
}
fprintf(f, "@end\n");
fprintf(f, "@legend\n");
for (n = 0; n < nlegends; n++){
fprintf(f, "%c:cell:%s\n",legendch[n], legendct[n]->name);
}
fprintf(f, "@end\n");
fprintf(f, "@flags\n");
// contents...
for (y = r->y1; y <= r->y2; y++) {
for (x = r->x1; x <= r->x2; x++) {
c = getcellat(m, x, y);
if (c) {
object_t *o;
int relx,rely;
relx = x - r->x1;
rely = y - r->y1;
if (c->lf) {
if (isplayer(c->lf)) {
// the lf's objects will appear in the bones file
for (o = c->lf->pack->first; o ; o = o->next) {
saveobtobones(o, f, relx, rely);
}
} else {
// the lf will appear in the bones file
char lfname[BUFLEN];
real_getlfname(c->lf, lfname, NULL, B_SHOWALL, B_REALRACE);
fprintf(f, "at(%d,%d) lf:%s\n", relx,rely, lfname);
}
}
for (o = c->obpile->first ; o ; o = o->next) {
saveobtobones(o, f, relx, rely);
}
}
}
}
// other flags.
fprintf(f, "tag:bones\n");
fprintf(f, "rarity:frequent\n");
fprintf(f, "goesin:%s\n", m->habitat->name);
fprintf(f, "dlevmin:%d\n", m->depth);
fprintf(f, "dlevmax:%d\n", m->depth);
fprintf(f, "@end\n");
fclose(f);
return B_FALSE;
}
int saveflagpile(FILE *f, flagpile_t *fp) {
flag_t *fl;
fprintf(f, "flags:\n");
for (fl = fp->first ; fl ; fl = fl->next) {
fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%ld,%d\n",
fl->id, fl->nvals, fl->val[0], fl->val[1], fl->val[2],fl->lifetime,fl->known,fl->obfrom, fl->skillfrom->id);
if (!fl->text || !strcmp(fl->text, "")) {
fprintf(f, "%s\n","^^^");
} else {
fprintf(f, "%s\n",fl->text);
}
}
fprintf(f, "-1,-1,-1,-1,-1,-1,-1,-1\n");
return B_FALSE;
}
int saveknowledge(FILE *f) {
int db = B_FALSE;
knowledge_t *k;
if (db) dblog("--> Saving knowledge...\n");
fprintf(f, "startknowledge\n");
for (k = knowledge; k ; k = k->next) {
fprintf(f, "%d^%s^%d\n",k->id, k->hiddenname, k->known);
}
fprintf(f, "endknowledge\n");
return B_FALSE;
}
int savelf(FILE *f, lifeform_t *l) {
object_t *o;
int obcount = 0;
// save this lf
fprintf(f, "startlf\n");
fprintf(f, "lfid: %d\n",l->id);
fprintf(f, "race: %d\n",l->race->id);
fprintf(f, "map: %d\n",l->cell->map->id);
fprintf(f, "pos: %d,%d\n",l->cell->x, l->cell->y);
fprintf(f, "level: %d\n",l->level);
fprintf(f, "newlevel: %d\n",l->newlevel);
// liefform will be created after loading the above.
fprintf(f, "str: %d/%d\n",l->att[A_STR],l->baseatt[A_STR]);
fprintf(f, "dex: %d/%d\n",l->att[A_AGI],l->baseatt[A_AGI]);
fprintf(f, "int: %d/%d\n",l->att[A_IQ],l->baseatt[A_IQ]);
fprintf(f, "xp: %ld\n",l->xp);
fprintf(f, "skillxp: %ld\n",l->skillxp);
fprintf(f, "totskillpoints: %ld\n",l->totskillpoints);
fprintf(f, "skp: %d\n",l->skillpoints);
fprintf(f, "contr: %d\n",l->controller);
fprintf(f, "hp: %d/%d\n",l->hp, l->maxhp);
fprintf(f, "mp: %d/%d\n",l->mp, l->maxmp);
fprintf(f, "stamina: %f\n", l->stamina);
fprintf(f, "alive: %d\n",l->alive);
fprintf(f, "lastdamtype: %d\n",l->lastdamtype);
fprintf(f, "lastdam: %s\n",l->lastdam);
fprintf(f, "material: %d\n",l->material->id);
fprintf(f, "timespent: %d\n",l->timespent);
fprintf(f, "sorted: %d\n",l->sorted);
fprintf(f, "polyrevert: %d\n",l->polyrevert);
fprintf(f, "forgettimer: %f\n",l->forgettimer);
//fprintf(f, "eyeadj: %d\n",l->eyeadjustment);
fprintf(f, "facing: %d\n",l->facing);
fprintf(f, "turncounter: %d\n",l->turncounter);
fprintf(f, "rotated: %d\n",l->rotated);
// lf flags
saveflagpile(f, l->flags);
// lifeform objects
obcount = 0;
for (o = l->pack->first ; o ; o = o->next) {
fprintf(f, "ob:%ld\n",o->id);
obcount++;
}
fprintf(f, "ob:-1\n");
fprintf(f, "obdefs\n");
// now save our object definitions
for (o = l->pack->first ; o ; o = o->next) {
saveob(f, o);
}
// lifeform polypack objects
obcount = 0;
for (o = l->polypack->first ; o ; o = o->next) {
fprintf(f, "polyob:%ld\n",o->id);
obcount++;
}
fprintf(f, "polyob:-1\n");
fprintf(f, "polyobdefs\n");
// now save our polypack object definitions
for (o = l->polypack->first ; o ; o = o->next) {
saveob(f, o);
}
return B_FALSE;
}
int savegame(void) {
map_t *m;
FILE *f;
char buf[BUFLEN];
int rv;
int mapcount = 0;
snprintf(buf, BUFLEN, "%s/game.sav",SAVEDIR);
f = fopen(buf, "wt");
if (!f) {
msg("Could not open save file!");
return B_TRUE;
}
rv = saveregions(f);
if (rv) {
msg("Could not save region data.");
return B_TRUE;
}
for (m = firstmap; m ; m = m->next) {
mapcount++;
}
fprintf(f, "start_maps:%d\n",mapcount);
for (m = firstmap; m ; m = m->next) {
// save world
rv = savemap(f, m);
if (rv) {
msg("Could not save map '%s'",m->name);
return B_TRUE;
}
}
fprintf(f, "end_maps\n");
// load world data
saveworlddata(f);
// save player + their objects
savelf(f, player);
saveknowledge(f);
savevars(f);
fclose(f);
return B_FALSE;
}
int savemap(FILE *f, map_t *m) {
int i;
object_t *o;
lifeform_t *l;
int x,y;
int obcount = 0;
// save map info
fprintf(f, "id:%d\n",m->id); // map id
fprintf(f, "region:%d\n",m->region->id); // map region id
fprintf(f, "depth:%d\n",m->depth); // map depth
fprintf(f, "name:%s\n",m->name); // map name
fprintf(f, "habitat:%d\n",m->habitat->id); // habitat
fprintf(f, "seed:%u\n",m->seed); // seed
fprintf(f, "lastplayervisit:%ld\n",m->lastplayervisit);
fprintf(f, "dims:%d,%d\n",m->w, m->h); // map dimensons
fprintf(f, "illumination:%d\n",m->illumination); // seed
fprintf(f, "nextmaps:\n");
for (i = 0; i < MAXDIR_ORTH; i++) {
fprintf(f, "%d\n",m->nextmap[i] ); // map dimensons
}
fprintf(f, "beingcreated:%d\n",m->beingcreated);
// save all non-player lifeforms (includes their objects)
fprintf(f, "lifeforms:\n");
for (l = m->lf ; l ; l = l->next) {
if (l->controller != C_PLAYER) { // don't save the player!
savelf(f, l);
}
}
fprintf(f, "endlifeforms\n");
// save room defs
fprintf(f, "nrooms:%d\n",m->nrooms);
for (i = 0; i < m->nrooms; i++) {
fprintf(f, "%d,%d,%d,%d,%d,%s\n",m->room[i].id,
m->room[i].x1, m->room[i].y1,
m->room[i].x2, m->room[i].y2,
m->room[i].vault ? m->room[i].vault->id : "^^^");
}
// cells
fprintf(f, "cells:\n");
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
// cell info
fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
c->room ? c->room->id : -1, c->type->id, c->known, c->knowntime, c->knownglyph.ch, c->knownglyph.colour, c->visited,c->locked, c->habitat->id, c->isroomwall, c->temperature );
// cell objects
for (o = c->obpile->first ; o ; o = o->next) {
fprintf(f, "ob:%ld\n",o->id);
obcount++;
}
fprintf(f, "ob:-1\n");
}
}
// save object definitions from map cells
fprintf(f, "MAPOBS:%d\n",obcount);
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
// cell objects
for (o = c->obpile->first ; o ; o = o->next) {
saveob(f, o);
}
}
}
saveflagpile(f, m->flags);
return B_FALSE;
}
int saveob(FILE *f, object_t *o) {
object_t *oo;
int ncontents;
fprintf(f, "id:%ld\n",o->id);
fprintf(f, "type:%d\n",o->type->id);
fprintf(f, "material:%d\n",o->material->id);
fprintf(f, "weight:%f\n",o->weight);
fprintf(f, "inscr:%s\n",o->inscription ? o->inscription : "^^^");
fprintf(f, "letter:%c\n",o->letter);
fprintf(f, "bless:%d\n",o->blessed);
fprintf(f, "blessknown:%d\n",o->blessknown);
fprintf(f, "amt:%d\n",o->amt);
fprintf(f, "birthtime:%ld\n",o->birthtime);
saveflagpile(f, o->flags);
// object contents...
ncontents = countobs(o->contents, B_FALSE);
if (ncontents) {
fprintf(f, "obcontents:%d\n",ncontents);
for (oo = o->contents->first ; oo ; oo = oo->next) {
saveob(f, oo);
}
fprintf(f, "endobcontents\n");
}
fprintf(f, "endob\n");
return B_FALSE;
}
int saveobtobones(object_t *o, FILE *f, int x, int y) {
char obname[BUFLEN];
if (!obgoesinbones(o)) return B_TRUE;
// chance that someone picked up the object
if (onein(4)) return B_TRUE;
// modify the object?
if (hasflag(o->flags, F_DECAY)) {
// food will decay by the time someone else finds it.
return B_TRUE;
}
if (isweapon(o) || isarmour(o)) {
// weapons/armour might degrade in quality
if (hasflag(o->flags, F_MASTERWORK)) {
// masterwork -> normal
killflagsofid(o->flags, F_MASTERWORK);
} else {
if (onein(3)) {
applyobmod(o, findobmod(OM_SHODDY));
}
if (onein(3) && ismetal(o->material->id)) {
addflag(o->flags, F_RUSTED, R_RUSTY, NA, NA, NULL);
}
}
}
if (o->material->id == MT_BLOOD) {
sprintf(obname, "a blood stain");
} else if (o->type->id == OT_GRIMOIRE) {
sprintf(obname, "grimoire");
} else {
getobnametrue(o, obname, o->amt);
}
fprintf(f, "at(%d,%d) ob:%s\n", x,y, obname);
return B_FALSE;
}
int saveregions(FILE *f) {
int i;
regionoutline_t *ro;
region_t *r;
int numoutlines = 0,numregions = 0;
fprintf(f, "start_regions\n");
for (ro = firstregionoutline ; ro ; ro = ro->next) {
numoutlines++;
}
fprintf(f, "numoutlines:%d\n",numoutlines);
for (ro = firstregionoutline ; ro ; ro = ro->next) {
// save this outline
fprintf(f, "startro\n");
fprintf(f, "rtypeid:%d\n",ro->rtype->id); // region type id
fprintf(f, "nthings:%d\n",ro->nthings);
for (i = 0; i < ro->nthings; i++) {
fprintf(f, "startthing\n");
fprintf(f, " thingid:%d\n",(int)ro->thing[i].id);
fprintf(f, " thingdepth:%d,%d,%d\n",ro->thing[i].depth, ro->thing[i].x, ro->thing[i].y);
fprintf(f, " thingkind:%d\n",(int)ro->thing[i].whatkind);
fprintf(f, " thingval:%d\n",(int)ro->thing[i].value);
if (strlen(ro->thing[i].what)) {
char localwhat[BUFLEN];
strcpy(localwhat,ro->thing[i].what);
/*
for (p = localwhat ; *p; p++) {
if (*p == ' ') *p = '^';
}
*/
fprintf(f, " thingwhat:%s\n",localwhat);
} else {
fprintf(f, " thingwhat:NULL\n");
}
fprintf(f, "endthing\n");
}
fprintf(f, "endro\n");
}
// now save out the actual region->outline mappings
numregions = 0;
for (r = firstregion ; r; r = r->next) {
numregions++;
}
fprintf(f, "numregions:%d\n",numregions);
for (r = firstregion ; r; r = r->next) {
fprintf(f, "startregion\n");
fprintf(f, " rid:%d\n",r->id);
fprintf(f, " rtypeid:%d\n",r->rtype->id);
fprintf(f, " outline:%d\n",r->outline ? r->outline->id : -1);
fprintf(f, " parentregion:%d\n",r->parentregion ? r->parentregion->id : -1);
fprintf(f, " nthings:%d\n",r->nthings);
fprintf(f, " depthmod:%d\n",r->depthmod);
fprintf(f, " createdby:%d\n",r->createdbymapid);
fprintf(f, "endregion\n");
}
fprintf(f, "end_regions\n");
return B_FALSE;
}
int saveworlddata(FILE *f) {
fprintf(f, "timedata:%ld,%ld,%ld\n",curtime,gamedays,gamesecs);
return B_FALSE;
}
int showhiscores(lifeform_t *lf, int min, int max) {
sqlite3 *db;
int rc;
char filename[BUFLEN],*cmd;
char *errmsg = NULL;
char hilitescoretext[BUFLEN];
long hilitescore = -1;
if (lf && !lfhasflag(lf, F_NOSCORE)) {
hilitescore = calcscore(lf);
if (hilitescore == 0) hilitescore = -1;
} else {
hilitescore = -1;
}
// open database
snprintf(filename, BUFLEN, "%s/hiscores.db", DATADIR);
rc = sqlite3_open(filename, &db);
if (rc) {
msg("error opening hiscore file '%s'.\n",filename);
dblog("error opening hiscore file '%s'.\n",filename);
return B_TRUE;
}
// show top ten
//snprintf(cmd, BUFLEN, "select * from hiscores order by score desc limit 10;");
asprintf(&cmd, "select (select count(*) from hiscores b where b.score > a.score) + 1 as rank, score,name,job,killedby from hiscores a where (rank >= %d) and (rank <= %d) order by score desc limit 10;", min, max);
snprintf(hilitescoretext, BUFLEN, "%ld",hilitescore);
rc = sqlite3_exec(db, cmd, showhiscoreline, hilitescoretext, &errmsg);
if (rc != SQLITE_OK) {
msg("error readin hiscores: '%s'", errmsg);
dblog("query was: '%s'", cmd);
dblog("error readin hiscores: '%s'\n sql command: [%s]", errmsg, cmd);
sqlite3_free(errmsg);
}
free(cmd);
sqlite3_close(db);
return B_FALSE;
}
// returns player's rank in 'rank'
int writehiscore(lifeform_t *lf, int *rank) {
sqlite3 *db;
int rc;
char filename[BUFLEN],*cmd;
char pname[BUFLEN];
char racename[BUFLEN];
char jobname[BUFLEN];
char killedby[BUFLEN];
char *escaped;
char *errmsg = NULL;
sqlite3_stmt *smt;
job_t *j;
long minscore = 0;
long score;
score = calcscore(lf);
// open database
snprintf(filename, BUFLEN, "%s/hiscores.db", DATADIR);
rc = sqlite3_open(filename, &db);
if (rc) {
msg("error opening hiscore file '%s'.\n",filename);
dblog("error opening hiscore file '%s'.\n",filename);
return B_TRUE;
}
getplayername(pname);
j = getjob(player);
strcpy(racename, player->race->name);
capitalise(racename);
snprintf(jobname, BUFLEN, "Lv%d %s %s", player->level, racename, getjobname(player));
makekillertext(killedby, lf->killverb, lf->lastdam, lf->cell->map, B_FALSE, B_TRUE);
// add escapes
escaped = strdup(killedby);
strrep(&escaped, "'", "''", NULL);
if (!lfhasflag(lf, F_NOSCORE) && (score > 0)) {
asprintf(&cmd, "insert into 'hiscores' (score,name,job,killedby) VALUES (%ld, '%s', '%s', '%s')", score, pname, jobname, escaped);
rc = sqlite3_exec(db, cmd, NULL, NULL, &errmsg);
free(cmd);
if (rc != SQLITE_OK) {
msg("error writing hiscores: '%s'", errmsg);
dblog("error writing hiscores: '%s'", errmsg);
dblog("query was: '%s'", cmd);
sqlite3_free(errmsg);
}
}
free(escaped);
if (rank) {
if (lfhasflag(lf, F_NOSCORE)) {
*rank = -1;
} else if (score == 0) {
*rank = -2;
} else {
// find out the player's rank
asprintf(&cmd, "select (select count(*) from hiscores b where b.score > a.score) + 1 as rank from hiscores a where score = %ld;",score);
rc = sqlite3_prepare_v2(db, cmd, -1, &smt, 0);
free(cmd);
rc = sqlite3_step(smt);
while (rc == SQLITE_ROW) {
const char *p;
p = (const char *)sqlite3_column_text(smt, 0);
*rank = atoi(p);
rc = sqlite3_step(smt);
}
}
}
// now delete all hiscore entries after 100
// find score number 100...
asprintf(&cmd, "select score from hiscores order by score desc limit 100;");
rc = sqlite3_prepare_v2(db, cmd, -1, &smt, 0);
free(cmd);
rc = sqlite3_step(smt);
while (rc == SQLITE_ROW) {
const char *p;
p = (const char *)sqlite3_column_text(smt, 0);
minscore = atol(p);
rc = sqlite3_step(smt);
}
if (minscore > 0) {
// we now have a minimum score - delete everything lower than it.
asprintf(&cmd, "delete from hiscores where score < %ld;", minscore);
rc = sqlite3_exec(db, cmd, NULL, NULL, &errmsg);
free(cmd);
}
sqlite3_close(db);
return 0;
}