//
// loadsave.cc - Map IO for starting the game, saving and loading
//
#include <stdio.h>
#include <string.h>
#include "ithelib.h"
#include "media.h"
#include "console.h"
#include "core.hpp"
#include "gamedata.h"
#include "loadsave.hpp"
#include "oscli.h"
#include "cookies.h"
#include "object.hpp"
#include "nuspeech.hpp"
#include "loadfile.h"
#include "linklist.hpp"
#include "script.hpp"
#include "init.h"
#include "nuspeech.hpp"
#include "textfile.h"
// Defines
//#define DEBUG_SAVE
//#define LOG_RW // If you want to debug object load/save
// Variables
static char stemp[128];
static char sigtemp[40]; // Signature buffer
static IFILE *ofp;
static unsigned int fastfind_id_num=0;
static OBJECT **fastfind_id_list=NULL;
static USEDATA empty;
// Functions
void load_map();
void load_z1(char *filename);
void save_z1(char *filename);
void wipe_z1();
void load_z2();
static void WriteContainers(OBJECT *cont, FILE *fp);
static void WriteContainerSchedules(OBJECT *cont);
static char *LoadSaveList_getter(int index, int *list_size);
static int ProcessBadObject(OBJECT **objptr, char *name);
static void DeleteProtoObject(OBJECT *o);
static void MoveToPocketEnd(OBJECT *object, OBJECT *container);
int SumObjects(OBJECT *cont, char *name, int total);
OBJECT *GetFirstObject(OBJECT *cont, char *name);
extern void Init_Areas(OBJECT *objsel);
static void fastfind_id_start();
static void fastfind_id_stop();
static OBJECT *fastfind_id(unsigned int id);
static void assign_objects(int mapnum);
static int CMP_sort(const void *a, const void *b);
static int CMP_search(const void *a, const void *b);
static int UsedataChanged(OBJECT *o);
extern void CallVMnum(int pos);
extern char *BestName(OBJECT *o);
extern "C" OBJECT *find_id(unsigned int id);
extern int isDecor(int x,int y);
extern "C"
{
extern void read_OBJECT(OBJECT *o,IFILE *f);
extern void write_OBJECT(OBJECT *o,IFILE *f);
extern void read_STATS(STATS *s,IFILE *f);
extern void read_OLDSTATS(STATS *s,IFILE *f);
extern void write_STATS(STATS *s,IFILE *f);
extern void read_FUNCS(FUNCS *F,IFILE *f);
extern void write_FUNCS(FUNCS *F,IFILE *f);
extern void read_WORLD(WORLD *w,IFILE *f);
extern void write_WORLD(WORLD *w,IFILE *f);
extern void TWriteObject(FILE *fp,OBJECT *o);
}
extern void GetGlobalPtrs(void ***ptr, unsigned int *num);
extern void GetGlobalInts(void ***ptr, unsigned int *num);
// Code
/*
* LoadMap- Load the whole map from disk, for first-time startup
*/
void LoadMap(int mapnum)
{
char *fname;
OBJLIST *ptr;
ilog_printf(" base\n");
load_map(mapnum);
fname=makemapname(mapnum,0,".mz1");
if(!fname)
ithe_panic("LoadMap: Cannot open map file",NULL);
strcpy(stemp,fname);
ilog_printf(" objects: ");
load_z1(stemp);
ilog_printf(" roof: ");
load_z2(mapnum);
ilog_printf("Done\n");
ilog_printf(" lights: ");
load_z3(mapnum);
ilog_printf("Done\n");
ilog_printf("\n");
ilog_printf("Run Object Inits\n");
// Ensure the system pocket is ready
syspocket = curmap->object;
if(!in_editor)
for(ptr=MasterList;ptr;ptr=ptr->next)
if(!ptr->ptr->flags.didinit)
{
if(ptr->ptr->funcs->icache != -1)
{
current_object = ptr->ptr;
person = ptr->ptr;
new_x = person->x;
new_y = person->y;
CallVMnum(ptr->ptr->funcs->icache);
}
ptr->ptr->flags.didinit=1;
}
ilog_printf("\n");
}
//
// Functions for the tilemap IO
//
/*
* load_map - Load the tiles from disk for startup
*/
void load_map(int mapnum)
{
int yt,x,y,b,do_delete=0;
long mem;
char key;
char filename[1024];
char number[16];
char *fname;
IFILE *fp;
curmap->w = map_W;
curmap->h = map_H;
ilog_printf("Opening map: %04d\n",mapnum);
fname=makemapname(mapnum,0,".map");
if(!fname)
ithe_panic("load_map: Cannot open map",NULL);
strcpy(stemp,fname);
if(!loadfile(fname,filename))
{
ilog_printf("Could not find file '%s'\n",stemp);
ilog_printf("Hit Y to create a new file, or any other key to exit.\n");
key = WaitForAscii();
if(key!='y' && key!='Y')
exit(1);
try_again:
mem = map_W * map_H * (sizeof(short)+sizeof(char)+sizeof(char)+sizeof(char)+sizeof(OBJECT *))/1024;
ilog_printf("\n\n\n");
ilog_printf("The new map will be %dx%d, and require %ldKB of memory.\n",map_W,map_H,mem);
ilog_printf("Is this okay? (Y/N)\n");
key = WaitForAscii();
if(key!='y' && key!='Y')
{
ilog_printf("No.\n");
ilog_printf("\n");
do {
number[0]=0;
ilog_printf("Enter the new width of the map:\n");
if(!irecon_getinput(number,15))
exit(1);
map_W = atoi(number);
} while(map_W<1);
do {
number[0]=0;
ilog_printf("Enter the new height of the map:\n");
if(!irecon_getinput(number,15))
exit(1);
map_H = atoi(number);
} while(map_H<1);
curmap->w = map_W;
curmap->h = map_H;
goto try_again;
}
curmap->sig=MAPSIG;
if(!curmap->objmap)
{
curmap->object = (struct OBJECT *)M_get(1,sizeof(OBJECT));
curmap->object->next=NULL;
}
if(!curmap->physmap)
curmap->physmap = (unsigned short *)M_get(curmap->w * curmap->h,sizeof(short));
if(!curmap->roof)
curmap->roof = (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
if(!curmap->objmap)
curmap->objmap = (OBJECT **)M_get(curmap->w * curmap->h, sizeof(OBJECT *));
if(!curmap->light)
curmap->light = (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
if(!curmap->lightst)
curmap->lightst = (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
// Build quick-access table for object layer
for(yt=0;yt<curmap->h;yt++)
ytab[yt]=curmap->w * yt;
}
else
{
fp = iopen(filename);
if(!fp)
ithe_panic("load_map: cannot open file",stemp);
iread((unsigned char *)sigtemp,40,fp);
if(memcmp(sigtemp,COOKIE_MAP,39)) // Skip last byte (either CR or LF)
ithe_panic("load_map: file does not contain the right cookie",stemp);
read_WORLD(curmap,fp);
if(curmap->sig != MAPSIG)
{
sprintf(filename,"Current map version is 0x%x, but this is 0x%x\n",MAPSIG,curmap->sig);
ithe_panic(filename,stemp);
}
// JM: 08/01/2006 : Free the maps first, to force reallocation
// since they may be a different size to the original map and reusing
// it won't work.
erase_curmap();
if(!curmap->physmap)
curmap->physmap = (unsigned short *)M_get(curmap->w * curmap->h,sizeof(short));
iread((unsigned char *)curmap->physmap,curmap->w*curmap->h*sizeof(short),fp);
iclose(fp);
if(!curmap->object)
curmap->object = (struct OBJECT *)M_get(1,sizeof(OBJECT));
curmap->object->name="Linkedlist entrypoint object";
curmap->object->next=NULL;
curmap->object->flags.on=1; // The Syspocket MUST BE ALIVE!
// Set up matrix of linked lists
if(!curmap->objmap)
curmap->objmap = (OBJECT **)M_get(curmap->w * curmap->h, sizeof(OBJECT *));
// Build quick-access table for the matrix
for(yt=0;yt<curmap->h;yt++)
ytab[yt]=curmap->w * yt;
if(!curmap->roof)
curmap->roof = (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
if(!curmap->light)
curmap->light= (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
if(!curmap->lightst)
curmap->lightst= (unsigned char *)M_get(curmap->w * curmap->h,sizeof(char));
}
// Store current map size for script language
map_W = curmap->w;
map_H = curmap->h;
ilog_quiet("Check map integrity\n");
// Check map state and correct if necessary
for(y=0;y<curmap->h;y++)
for(x=0;x<curmap->w;x++)
{
/*
ilog_quiet("curmap = %x\n",curmap);
ilog_quiet("curmap->physmap = %x\n",curmap->physmap);
ilog_quiet("pos = %d\n",MAP_POS(x,y));
ilog_quiet("curmap->physmap[%d] = %x\n",MAP_POS(x,y),curmap->physmap[MAP_POS(x,y)]);
*/
b=curmap->physmap[MAP_POS(x,y)];
if(b>=TItot && b != 0xffff) // 0xffff is random and allowed
{
if(in_editor)
{
if(!do_delete)
{
ilog_printf("Invalid tiles detected in the map.\n");
ilog_printf("Do you want to:\n");
ilog_printf("1. Set all unknown tiles to 0\n");
ilog_printf("2. Quit so you can add the tile to resource.txt\n");
do
{
key=WaitForAscii();
if(key == '1')
do_delete=1;
if(key == '2')
exit(1);
} while(key != '1');
}
}
else
{
curmap->physmap[MAP_POS(x,y)] = 0xffff; // surprise!!
do_delete = 2;
}
if(do_delete == 1)
curmap->physmap[MAP_POS(x,y)]=0;
}
}
if(do_delete == 2)
Bug("Invalid map tiles detected\n");
}
/*
* save_map - Save the tiles to disk
*/
void save_map(int mapnum)
{
char *fname;
ofp=NULL;
fname=makemapname(mapnum,0,".map");
strcpy(stemp,fname);
ofp = iopen_write(stemp);
// Since error checking will have been done previously it is safe to explode
// if something has gone wrong.
iwrite((unsigned char *)COOKIE_MAP,40,ofp);
curmap->sig=MAPSIG; // Just to make sure
write_WORLD(curmap,ofp);
iwrite((unsigned char *)curmap->physmap,curmap->w*curmap->h*sizeof(short),ofp);
iclose(ofp);
}
/*
* Free the memory used by a map
*/
void erase_curmap()
{
// JM: 31/10/2006 - I was a total moron and only tried to free them if
// they were ALREADY null. The devastation THAT caused was horrific.. it's
// amazing the game worked at all.
if(curmap->object)
{
FreePockets(curmap->object);
// M_free(curmap->object);
curmap->object = NULL;
}
if(curmap->physmap)
{
M_free(curmap->physmap);
curmap->physmap = NULL;
}
if(curmap->roof)
{
M_free(curmap->roof);
curmap->roof = NULL;
}
if(curmap->objmap)
{
M_free(curmap->objmap);
curmap->objmap = NULL;
}
if(curmap->light)
{
M_free(curmap->light);
curmap->light = NULL;
}
if(curmap->lightst)
{
M_free(curmap->lightst);
curmap->lightst = NULL;
}
}
//
// Z1 functions for the object layer IO
//
/*
* save_z1 - Write the object layer, or a savegame
*/
void save_z1(char *filename)
{
OBJECT *temp;
int vx,vy;
FILE *fp;
// Open the file.
fp=fopen(filename,"w");
if(!fp)
return;
// Write the header
fprintf(fp,"%s\n\n",COOKIE_MZ1);
// Set the object IDs
assign_objects(mapnumber);
// Scan entire map line by line, row by row.
// This may seem inefficient, but it is the only way to record the decoratives
// and ensure the objects are in the right places.
for(vy=0;vy<curmap->h;vy++)
for(vx=0;vx<curmap->w;vx++)
{
temp = GetRawObjectBase(vx,vy);
for(;temp;temp=temp->next)
if(temp->user->edecor) // If it's decorative, write special
fprintf(fp,"\tdecorative %s at %d %d\n\n",temp->name,vx,vy);
else
{
if(temp->pocket.objptr)
WriteContainers(temp->pocket.objptr,fp);
TWriteObject(fp,temp);
}
}
fclose(fp);
}
/*
* load_z1 - Load the object layer, or a savegame
*/
void load_z1(char *fname)
{
OBJECT *temp,*parent,*next;
OBJLIST *ptr;
struct TF_S z1;
int ctr,x,y,ke,ctr2;
unsigned int saveid,num;
char *str,bad,ok;
char first[1024],filename[1024];
char *l;
if(!loadfile(fname,filename))
{
irecon_cls();
ilog_printf("Could not find file '%s'\n",fname);
ilog_printf("Hit Y to create a new file, or any other key to exit.\n");
ke=WaitForAscii();
if(ke!='y' && ke!='Y')
exit(1);
return;
}
TF_init(&z1);
TF_load(&z1,filename);
if(strncmp(z1.line[0],COOKIE_MZ1,40))
{
ilog_quiet("[%s]\n",z1.line[0]);
ilog_quiet("[%s]\n",z1.line[1]);
ithe_panic("load_mz1: file does not contain the right cookie",z1.line[0]);
}
temp=NULL;
Plot(z1.lines);
for(ctr=1;ctr<z1.lines;ctr++)
{
Plot(0);
l=z1.line[ctr];
strcpy(first,strfirst(l));
strstrip(first);
// decorative <type> at <xnum> <ynum>
if(!istricmp(first,"decorative"))
{
x = atoi(strfirst(strrest(strrest(strrest(l)))));
y = atoi(strfirst(strrest(strrest(strrest(strrest(l))))));
str = strfirst(strrest(l));
// If the coordinates are 65535, the object has been deleted
// by the map utilities
if(x == 65535 || y == 65535)
{
temp=NULL;
continue;
}
temp = OB_Decor(str); // Is it a decorative object?
if(temp)
{
temp->save_id=SAVEID_INVALID;
ForceDropObject(x,y,temp);
}
else
// The editor treats Decoratives as regular objects for simplicity
{
temp = OB_Alloc();
temp->save_id=0;
temp->flags.on = 1; // Activate, or things will go wrong later
OB_Init(temp,str);
ShrinkDecor(temp); // Save memory if we can
MoveToMap(x,y,temp);
}
// Bug("load_mz1: decorative '%s' isn't decorative in file %s at line %d\n",str,filename,ctr);
temp=NULL;
continue;
}
// object <idnum>
if(!istricmp(first,"object"))
{
temp = OB_Alloc();
saveid = atoi(strfirst(strrest(l)));
if(!saveid)
Bug("load_mz1: object saveid = 0 in file %s at line %d\n",filename,ctr);
temp->save_id = saveid;
temp->flags.on = 1; // Activate, or things will go wrong later
continue;
}
// If we don't have an object, skip until the next create command
if(!temp)
continue;
// type <name>
if(!istricmp(first,"type"))
{
str = strfirst(strrest(l));
do {
bad=0;
if(!OB_Init(temp,str))
{
if(!in_editor)
ithe_panic("Load_Z1: Could not find this object in 'section: character'!",str);
else
{
bad=ProcessBadObject(&temp,str);
}
}
else
{
// Activate it
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
}
} while(bad); // Todo: error handling not just panic
continue;
}
// inside <idnum>
if(!istricmp(first,"inside"))
{
num = atoi(strfirst(strrest(l)));
if(!num)
Bug("load_mz1: object inside Null in file %s at line %d\n",filename,ctr);
// Set the parent as an ID, not a pointer
temp->parent.saveid = num;
continue;
}
// at <x> <y>
if(!istricmp(first,"at"))
{
temp->x = atoi(strfirst(strrest(l)));
temp->y = atoi(strfirst(strrest(strrest(l))));
// If the coordinates are 65535, the object has been deleted
// by the map utilities
if(temp->x == 65535 || temp->y == 65535)
{
DeleteProtoObject(temp);
temp=NULL;
continue;
}
// Move the object to its destination
MoveToMap(temp->x,temp->y,temp);
// Init_Areas(temp);
continue;
}
// form <formstring>
if(!istricmp(first,"form"))
{
str = strfirst(strrest(l));
OB_SetSeq(temp,str);
continue;
}
// sequence <sptr_num> <slen_num> <sdir_num>
if(!istricmp(first,"sequence"))
{
x = atoi(strfirst(strrest(l)));
y = atoi(strfirst(strrest(strrest(l))));
num = atoi(strfirst(strrest(strrest(strrest(l)))));
temp->sptr = x;
// temp->slen = y;
temp->sdir = num;
continue;
}
// curdir <dir_num>
if(!istricmp(first,"curdir"))
{
num = atoi(strfirst(strrest(l)));
if(num < 0 || num > 3)
Bug("load_mz1: bad direction in file %s at line %d\n",filename,ctr);
OB_SetDir(temp,num,FORCE_SHAPE|FORCE_FRAME);
// Init_Areas(temp);
CalcSize(temp);
// ilog_quiet("object %s : w:%d h:%d\n",temp->name,temp->w,temp->h);
continue;
}
// flags <num>
if(!istricmp(first,"flags"))
{
sscanf(strfirst(strrest(l)),"%x",&num);
if(!num)
Bug("load_mz1: flags = 0 in file %s at line %d\n",filename,ctr);
// Cram the flags in place
// Do we want to load the flags in? (Are we doing a total restore?)
if(fullrestore)
{
*(int *)(&temp->flags) = num;
// Make it active
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
}
continue;
}
// Do we care about Width and Height? (Only if restoring a savegame)
if(fullrestore)
{
// width <num>
if(!istricmp(first,"width"))
{
temp->w = atoi(strfirst(strrest(l)));
continue;
}
// height <num>
if(!istricmp(first,"height"))
{
temp->h = atoi(strfirst(strrest(l)));
continue;
}
// mwidth <num>
if(!istricmp(first,"mwidth"))
{
temp->mw = atoi(strfirst(strrest(l)));
continue;
}
// mheight <num>
if(!istricmp(first,"mheight"))
{
temp->mh = atoi(strfirst(strrest(l)));
continue;
}
}
// z <num>
if(!istricmp(first,"z"))
{
temp->z = atoi(strfirst(strrest(l)));
continue;
}
// name <personalname>
if(!istricmp(first,"name"))
{
strncpy(temp->personalname,strrest(l),31);
temp->personalname[31]=0;
continue;
}
// target <idnum>
if(!istricmp(first,"target"))
{
num = atoi(strfirst(strrest(l)));
if(!num)
Bug("load_mz1: object target = Null in file %s at line %d\n",filename,ctr);
// Set the field as an ID, not a pointer
temp->target.saveid = num;
continue;
}
// enemy <idnum>
if(!istricmp(first,"enemy"))
{
num = atoi(strfirst(strrest(l)));
if(!num)
Bug("load_mz1: object enemy = Null in file %s at line %d\n",filename,ctr);
// Set the field as an ID, not a pointer
temp->enemy.saveid = num;
continue;
}
// tag <num>
if(!istricmp(first,"tag"))
{
temp->tag = atoi(strfirst(strrest(l)));
continue;
}
// light <num>
if(!istricmp(first,"light"))
{
temp->light = atoi(strfirst(strrest(l)));
continue;
}
// activity <funcname>
if(!istricmp(first,"activity"))
{
temp->activity = getnum4PE(strfirst(strrest(l)));
// Did the function exist?
if(temp->activity < 0)
continue; // No
// Make it active
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
continue;
}
// labels->location <string>
if(!istricmp(first,"labels->location"))
{
SetLocation(temp,strfirst(strrest(l)));
continue;
}
//
// Stats
//
// stats->hp <num>
if(!istricmp(first,"stats->hp"))
{
temp->stats->hp = atoi(strfirst(strrest(l)));
temp->user->oldhp = temp->stats->hp; // Keep it in sync
continue;
}
// stats->dex <num>
if(!istricmp(first,"stats->dex"))
{
temp->stats->dex = atoi(strfirst(strrest(l)));
continue;
}
// stats->str <num>
if(!istricmp(first,"stats->str"))
{
temp->stats->str = atoi(strfirst(strrest(l)));
continue;
}
// stats->intel <num>
if(!istricmp(first,"stats->intel"))
{
temp->stats->intel = atoi(strfirst(strrest(l)));
continue;
}
// stats->weight <num>
if(!istricmp(first,"stats->weight"))
{
temp->stats->weight = atoi(strfirst(strrest(l)));
continue;
}
// maxstats->weight <num>
if(!istricmp(first,"maxstats->weight"))
{
temp->maxstats->weight = atoi(strfirst(strrest(l)));
continue;
}
// stats->quantity <num>
if(!istricmp(first,"stats->quantity"))
{
temp->stats->quantity = atoi(strfirst(strrest(l)));
continue;
}
// stats->armour <num>
if(!istricmp(first,"stats->armour"))
{
temp->stats->armour = atoi(strfirst(strrest(l)));
continue;
}
// stats->damage <num>
if(!istricmp(first,"stats->damage"))
{
temp->stats->damage = atoi(strfirst(strrest(l)));
continue;
}
// stats->tick <num>
if(!istricmp(first,"stats->tick"))
{
temp->stats->tick = atoi(strfirst(strrest(l)));
continue;
}
// stats->owner <num>
if(!istricmp(first,"stats->owner"))
{
temp->stats->owner.saveid = atoi(strfirst(strrest(l)));
continue;
}
// stats->karma <num>
if(!istricmp(first,"stats->karma"))
{
temp->stats->karma = atoi(strfirst(strrest(l)));
continue;
}
// stats->bulk <num>
if(!istricmp(first,"stats->bulk"))
{
temp->stats->bulk = atoi(strfirst(strrest(l)));
continue;
}
// stats->range <num>
if(!istricmp(first,"stats->range"))
{
temp->stats->range = atoi(strfirst(strrest(l)));
continue;
}
// stats->speed <num>
if(!istricmp(first,"stats->speed"))
{
temp->maxstats->speed = atoi(strfirst(strrest(l)));
continue;
}
// stats->level <num>
if(!istricmp(first,"stats->level"))
{
temp->stats->level = atoi(strfirst(strrest(l)));
continue;
}
// stats->radius <num>
if(!istricmp(first,"stats->radius"))
{
temp->stats->radius = atoi(strfirst(strrest(l)));
// Make it active
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
continue;
}
// stats->alignment <num>
if(!istricmp(first,"stats->alignment"))
{
temp->stats->dex = atoi(strfirst(strrest(l)));
continue;
}
// stats->npcflags <num>
if(!istricmp(first,"stats->npcflags"))
{
// num = atoi(strfirst(strrest(l)));
sscanf(strfirst(strrest(l)),"%x",&num);
// Cram the flags in place, if we are doing a savegame restore
#ifdef DEBUG_SAVE
ilog_quiet("%s: npcf = %x, fullrest=%d\n",temp->name,num,fullrestore);
#endif
if(fullrestore)
{
*(int *)(&temp->stats->npcflags) = num;
// Make it active
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
}
continue;
}
//
// Funcs
//
// funcs->use <string>
if(!istricmp(first,"funcs->use"))
{
strcpy(temp->funcs->use,strfirst(strrest(l)));
continue;
}
// funcs->talk <string>
if(!istricmp(first,"funcs->talk"))
{
strcpy(temp->funcs->talk,strfirst(strrest(l)));
temp->funcs->tcache=1;
continue;
}
// funcs->kill <string>
if(!istricmp(first,"funcs->kill"))
{
strcpy(temp->funcs->kill,strfirst(strrest(l)));
continue;
}
// funcs->look <string>
if(!istricmp(first,"funcs->look"))
{
strcpy(temp->funcs->look,strfirst(strrest(l)));
continue;
}
// funcs->stand <string>
if(!istricmp(first,"funcs->stand"))
{
strcpy(temp->funcs->stand,strfirst(strrest(l)));
// Make it active
if(!ML_InList(&ActiveList,temp))
AL_Add(&ActiveList,temp);
continue;
}
// funcs->hurt <string>
if(!istricmp(first,"funcs->hurt"))
{
strcpy(temp->funcs->hurt,strfirst(strrest(l)));
continue;
}
// funcs->init <string>
if(!istricmp(first,"funcs->init"))
{
strcpy(temp->funcs->init,strfirst(strrest(l)));
continue;
}
// funcs->resurrect <string>
if(!istricmp(first,"funcs->resurrect"))
{
strcpy(temp->funcs->resurrect,strfirst(strrest(l)));
continue;
}
// funcs->wield <string>
if(!istricmp(first,"funcs->wield"))
{
strcpy(temp->funcs->wield,strfirst(strrest(l)));
continue;
}
// funcs->horror <string>
if(!istricmp(first,"funcs->horror"))
{
strcpy(temp->funcs->horror,strfirst(strrest(l)));
continue;
}
// funcs->attack <string>
if(!istricmp(first,"funcs->attack"))
{
strcpy(temp->funcs->attack,strfirst(strrest(l)));
continue;
}
// funcs->attack <string>
if(!istricmp(first,"funcs->user1"))
{
strcpy(temp->funcs->user1,strrest(l));
continue;
}
// funcs->attack <string>
if(!istricmp(first,"funcs->user2"))
{
strcpy(temp->funcs->user2,strrest(l));
continue;
}
// funcs->stand <string>
if(!istricmp(first,"funcs->quantity"))
{
strcpy(temp->funcs->quantity,strfirst(strrest(l)));
continue;
}
//
// Schedule
//
if(!istricmp(first,"schedule"))
{
x = atoi(strfirst(strrest(l)));
y = atoi(strfirst(strrest(strrest(l))));
num = atoi(strfirst(strrest(strrest(strrest(strrest(l))))));
str = strfirst(strrest(strrest(strrest(l))));
// Initialise schedule if none present
if(!temp->schedule)
{
temp->schedule = (SCHEDULE *)M_get(24,sizeof(SCHEDULE));
for(ctr2=0;ctr2<24;ctr2++)
temp->schedule[ctr2].hour = -1;
}
// Find a spare slot
ke=-1;
for(ctr2=0;ctr2<24;ctr2++)
if(temp->schedule[ctr2].hour == -1)
{
ke=ctr2;
break;
}
// Fill the slot
if(ke>-1)
{
temp->schedule[ke].hour = x;
temp->schedule[ke].minute = y;
temp->schedule[ke].target.saveid = num; // Fix up later
strcpy(temp->schedule[ke].vrm,str);
temp->schedule[ke].call = getnum4PE(str);
}
continue;
}
}
// Don't need text file open anymore
TF_term(&z1);
ilog_printf("\n");
// Sort out nested objects
ilog_printf(" Nested Objects\n");
fastfind_id_start();
temp = curmap->object->next;
while(temp)
{
next = temp->next;
parent = fastfind_id(temp->parent.saveid);
if(parent)
{
// ilog_quiet("putting %s (%d) into Parent %s (%d)\n",temp->name,temp.save_id,parent->name,parent.save_id);
MoveToPocketEnd(temp,parent);
// Objects inside eggs should become dormant to save CPU
if(temp->stats->npcflags.biological)
ML_Del(&ActiveList,temp);
}
else
{
ilog_quiet("Oh shit! %s is orphaned. ID = %d\n",temp->name,temp->save_id);
// Bug("%s [%d] has an identity crisis\n",temp->name,temp->save_id);
// DestroyObject(temp);
}
temp = next;
};
// Convert remaining integers to pointers and set up functions
for(ptr=MasterList;ptr;ptr=ptr->next)
if(ptr->ptr)
{
if(ptr->ptr->target.saveid)
ptr->ptr->target.objptr = fastfind_id(ptr->ptr->target.saveid);
if(ptr->ptr->enemy.saveid)
ptr->ptr->enemy.objptr = fastfind_id(ptr->ptr->enemy.saveid);
if(ptr->ptr->stats->owner.saveid)
ptr->ptr->stats->owner.objptr = fastfind_id(ptr->ptr->stats->owner.saveid);
// Convert any targets
if(ptr->ptr->schedule)
{
for(ctr2=0;ctr2<24;ctr2++)
if(ptr->ptr->schedule[ctr2].hour != -1)
ptr->ptr->schedule[ctr2].target.objptr = fastfind_id(ptr->ptr->schedule[ctr2].target.saveid);
}
InitFuncsFor(ptr->ptr,0);
OB_Funcs(ptr->ptr);
}
fastfind_id_stop();
// If we're in the game, deck the empty schedule pointers so we can quickly
// see if the object has any events or not
//OBJLIST *ptr;
//char ok=0;
if(!in_editor)
for(ptr=MasterList;ptr;ptr=ptr->next)
{
if(ptr->ptr->schedule)
{
ok=0;
for(ctr=0;ctr<24;ctr++)
if(ptr->ptr->schedule[ctr].hour != -1)
ok=1;
if(!ok)
{
M_free(ptr->ptr->schedule);
ptr->ptr->schedule = NULL;
}
}
}
}
/*
* wipe_z1 - deallocate the z1 layer entirely
* called prior to load_z1 in game, not in editor
*/
void wipe_z1()
{
OBJECT *temp,*anchor;
int vx,vy;
ilog_printf(" wiping object matrix\n");
//ilog_quiet("player at %d,%d\n",player->x,player->y);
for(vy=0;vy<curmap->h;vy++)
for(vx=0;vx<curmap->w;vx++)
{
anchor = GetRawObjectBase(vx,vy); // RAWobjectbase ignores large objs
if(anchor)
{
if(anchor->flags.decor)
{
curmap->objmap[ytab[vy]+vx]=NULL; // Kill the decorative!
if(isDecor(vx,vy))
Bug("Something WENT WRONG deleting at %d,%d\n",vx,vy);
}
else
for(temp = anchor;temp;temp=temp->next)
{
if(temp->flags.decor)
{
if(temp->next)
ithe_panic("Oh shit",NULL);
DelDecor(vx,vy, temp);
if(isDecor(vx,vy))
Bug("Something WENT WRONG deleting at %d,%d\n",vx,vy);
}
else
{
temp->flags.on=0;
// ilog_quiet("del %s %x at %d,%d\n",temp->name,temp,vx,vy);
}
}
}
}
ilog_printf(" Emptying Syspocket\n");
FreePockets(curmap->object);
ilog_printf(" Delete Everything1\n");
MassCheckDepend();
// Skip internal dependency checks (because we just did a bulk scan)
NoDeps=1;
ilog_printf(" Delete Everything2\n");
pending_delete=1; // Force garbage collection
DeletePending();
pending_delete=1; // Force garbage collection
DeletePending();
// Careful with that axe, Eugene
NoDeps=0;
if(!change_map)
{
player=NULL;
}
//ilog_quiet("Making Sure\n\n");
memset(curmap->objmap,0,curmap->w*curmap->h*sizeof(OBJECT *));
ilog_printf(" Finished\n");
if(!change_map)
{
ilog_quiet("\nwiping party members\n\n");
for(vx=0;vx<MAX_MEMBERS;vx++)
party[vx]=NULL;
}
}
//
// Z2 rooftops layer map IO
//
/*
* load_z2 - Load the rooftops from disk
*/
void load_z2(int mapnum)
{
IFILE *fp;
char filename[1024];
char *fname;
int key;
ilog_printf(" Rooftops\n");
// Objects layer 2 - roof
fname=makemapname(mapnum,0,".mz2");
strcpy(stemp,fname);
if(!loadfile(stemp,filename))
{
irecon_cls();
ilog_printf("Could not find file '%s'\n",stemp);
irecon_printf("Hit Y to create a new file, or any other key to exit.\n");
key=WaitForAscii();
if(key!='Y'&&key!='y')
exit(1);
}
else
{
fp=iopen(filename);
iread((unsigned char *)sigtemp,40,fp);
if(strcmp(sigtemp,COOKIE_MZ2))
{
ilog_printf("File '%s' is not a z2 map.. it does not contain the right cookie\n",stemp);
exit(1);
}
iread((unsigned char *)curmap->roof,curmap->w*curmap->h*sizeof(unsigned char),fp);
iclose(fp);
}
}
/*
* save_z2 - Save the rooftops to disk
*/
void save_z2(int mapnum)
{
char *fname;
// Objects layer 2 - statlc lights
fname=makemapname(mapnum,0,".mz2");
strcpy(stemp,fname);
ofp = iopen_write(stemp);
if(!ofp)
{
if(!in_editor)
{
ilog_printf("Oh no!\n");
ilog_printf("Could not create file '%s'\n",stemp);
}
return;
}
iwrite((unsigned char *)COOKIE_MZ2,40,ofp);
iwrite((unsigned char *)curmap->roof,curmap->w*curmap->h,ofp);
iclose(ofp);
return;
}
//
// Z3 static light layer map IO
//
/*
* load_z3 - Load the static lighting from disk
*/
void load_z3(int mapnum)
{
IFILE *fp;
char filename[1024];
char *fname;
int key;
// Objects layer 3 - static lights
fname=makemapname(mapnum,0,".mz3");
strcpy(stemp,fname);
if(!loadfile(stemp,filename))
{
irecon_cls();
ilog_printf("Could not find file '%s'\n",stemp);
irecon_printf("Hit Y to create a new file, or any other key to exit.\n");
key=WaitForAscii();
if(key!='Y'&&key!='y')
exit(1);
}
else
{
fp=iopen(filename);
iread((unsigned char *)sigtemp,40,fp);
if(strcmp(sigtemp,COOKIE_MZ3))
{
ilog_printf("File '%s' is not a z2 map.. it does not contain the right cookie\n",stemp);
exit(1);
}
iread((unsigned char *)curmap->light,curmap->w*curmap->h*sizeof(unsigned char),fp);
iclose(fp);
}
}
/*
* save_z3 - Save the static lighting to disk
*/
void save_z3(int mapnum)
{
char *fname;
// Objects layer 3 - static lights
fname=makemapname(mapnum,0,".mz3");
strcpy(stemp,fname);
ofp = iopen_write(stemp);
if(!ofp)
{
if(!in_editor)
{
ilog_printf("Oh no!\n");
ilog_printf("Could not create file '%s'\n",stemp);
}
return;
}
iwrite((unsigned char *)COOKIE_MZ3,40,ofp);
iwrite((unsigned char *)curmap->light,curmap->w*curmap->h,ofp);
iclose(ofp);
return;
}
// Miscellaneous State
/*
* load_ms - Read miscellaneous savegame data
*/
void load_ms(char *filename)
{
IFILE *ifp;
char buf[256];
unsigned int ctr,num,ctr2,ctr3,ut,ctr4,newpos,saveid;
OBJECT *o,*o2;
USEDATA usedata;
int act;
void **intlist=NULL;
void **objlist=NULL;
// Open the file.
ifp=iopen(filename);
// Read the header
iread((unsigned char *)buf,40,ifp); // Cookie
if(strcmp(COOKIE_MS,buf))
{
iclose(ifp);
return;
}
if(MZ1_SavingGame)
goto reload_game; // Naughty Doug
if(!GetWadEntry(ifp,"PARTYBLK"))
ithe_panic("Savegame corrupted","PARTYBLK not found");
saveid = igetl_i(ifp);
player=find_id(saveid);
#ifdef DEBUG_SAVE
ilog_quiet("player id = %d\n",saveid);
#endif
if(!player)
ithe_panic("load_ms: Player AWOL in savegame",NULL);
for(ctr=0;ctr<MAX_MEMBERS;ctr++)
{
saveid = igetl_i(ifp);
if(saveid)
{
party[ctr] = find_id(saveid); // ID numbers change to objptrs
party[ctr]->flags.party = 1;
}
else
party[ctr] = NULL;
}
if(!GetWadEntry(ifp,"FLAGSBLK"))
ithe_panic("Savegame corrupted","FLAGSBLK not found");
// Load user flags
Wipe_tFlags();
ut=igetl_i(ifp);
for(ctr=0;ctr<ut;ctr++)
{
num=igetb(ifp);
iread((unsigned char *)buf,num,ifp);
// ilog_quiet("flag: '%s'\n",buf);
Set_tFlag(buf,1);
}
// Load time/date
if(!GetWadEntry(ifp,"TIMEDATE"))
ithe_panic("Savegame corrupted","TIMEDATE not found");
game_minute = igetl_i(ifp);
game_hour = igetl_i(ifp);
game_day = igetl_i(ifp);
game_month = igetl_i(ifp);
game_year = igetl_i(ifp);
days_passed = igetl_i(ifp);
day_of_week = igetl_i(ifp);
ilog_quiet("game time = %02d:%02d\n",game_hour,game_minute);
// If we're doing a map switch, reload from here:
reload_game:
// Load current goal data
if(!GetWadEntry(ifp,"GOALDATA"))
ithe_panic("Savegame corrupted","GOALDATA not found");
num = igetl_i(ifp);
#ifdef DEBUG_SAVE
ilog_quiet("%d goals\n",num);
#endif
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
{
Bug("Goal: Cannot find object number %d\n",saveid);
// ithe_panic("Oh well whatever","Never mind");
}
iread((unsigned char *)buf,32,ifp);
saveid = igetl_i(ifp);
o2=NULL;
if(saveid != SAVEID_INVALID)
{
o2 = find_id(saveid);
if(!o2)
{
Bug("Goal: Cannot find target object number %d\n",saveid);
// ithe_panic("Oh well whatever","Never mind");
}
}
if(o)
{
SubAction_Wipe(o); // Erase any sub-tasks
ActivityName(o,buf,o2);
}
}
// Load usedata
if(!GetWadEntry(ifp,"USEDATA "))
ithe_panic("Savegame corrupted","USEDATA not found");
num = igetl_i(ifp);
ctr2 = igetl_i(ifp);
#ifdef DEBUG_SAVE
ilog_printf("Reading %d usedata entries:\n",num);
#endif
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
{
ilog_quiet("Load_miscellaneous_state (usedata): can't load object %d\n",saveid);
// ithe_panic("Load_miscellaneous_state (usedata): Can't find object","Savegame corrupted");
}
if(ctr2 == sizeof(USEDATA))
iread((unsigned char *)&usedata,sizeof(USEDATA),ifp);
else
{
// Not good, but try to do it anyway:
newpos = itell(ifp)+ctr2;
iread((unsigned char *)&usedata,sizeof(USEDATA),ifp);
iseek(ifp,newpos,SEEK_SET);
}
if(o)
{
memcpy(o->user,&usedata,sizeof(USEDATA));
// Now we must clear all the pointers to prevent death
o->user->arcpocket.objptr=NULL;
o->user->pathgoal.objptr=NULL;
o->user->npctalk=NULL;
o->user->lFlags=NULL;
// Delete activity lists too
memset(o->user->actlist,0,sizeof(o->user->actlist));
memset(o->user->acttarget,0,sizeof(o->user->acttarget)); // Delete list
}
}
// Load secondary goal data (sub-tasks or subactions)
if(!GetWadEntry(ifp,"SUBTASKS"))
ithe_panic("Savegame corrupted","SUBTASKS not found");
num = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
Bug("SubTasks: Cannot find object number %d\n",saveid);
for(ctr2=0;ctr2<ACT_STACK;ctr2++)
{
act = igetl_i(ifp);
saveid = igetl_i(ifp);
if(act != -1)
{
if(saveid == SAVEID_INVALID)
{
SubAction_Push(o,act,NULL);
// ilog_quiet("restore queue: %s does %s\n",o->name,PElist[act].name);
}
else
{
o2 = find_id(saveid);
if(!o2)
Bug("Subtask: Cannot find target object number %d\n",saveid);
// ilog_quiet("restore queue: %s does %s to %s\n",o->name,PElist[act].name,o2->name);
SubAction_Push(o,act,o2);
}
}
}
}
// load record of what we've said to NPCs
if(!GetWadEntry(ifp,"NPCTALKS"))
ithe_panic("Savegame corrupted","NPCTALKS not found");
num = igetl_i(ifp);
#ifdef DEBUG_SAVE
ilog_quiet("Reading %d npctalks\n",num);
#endif
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
ithe_panic("Load_miscellaneous_state (NPCtalk): Can't find object","Savegame corrupted");
ut = igetl_i(ifp); // number of entries for this NPC
for(ctr2=0;ctr2<ut;ctr2++)
{
ctr3=igetl_i(ifp);
ctr4=igetb(ifp)&0xff; // Limit to 255 for safety
iread((unsigned char *)buf,ctr4,ifp);
o2 = find_id(ctr3);
#ifdef DEBUG_SAVE
ilog_quiet("npc %d spoke to %d\n",saveid,ctr3);
#endif
if(!o2)
{
ilog_quiet("looking for %d, player's id is %d\n",ctr3,player->save_id);
ithe_panic("Load_miscellaneous_state (NPCtalk): Can't find 'player'","Savegame corrupted");
}
#ifdef DEBUG_SAVE
ilog_quiet("BeenRead: %x,%s,%x\n",o,buf,o2);
#endif
NPC_BeenRead(o,buf,o2);
}
}
// load record of local NPC flags
if(!GetWadEntry(ifp,"NPCLFLAG"))
ithe_panic("Savegame corrupted","NPCLFLAG not found");
num = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
{
ilog_quiet("can't find object %d (0x%x)\n",saveid,saveid);
ithe_panic("Load_miscellaneous_state (lFlags): Can't find object","Savegame corrupted");
}
ut = igetl_i(ifp); // number of entries for this NPC
for(ctr2=0;ctr2<ut;ctr2++)
{
ctr3=igetl_i(ifp);
ctr4=(unsigned char)igetb(ifp);
iread((unsigned char *)buf,ctr4,ifp);
o2 = find_id(ctr3);
if(!o2)
{
ilog_quiet("can't find object %d (0x%x)\n",saveid,saveid);
ithe_panic("Load_miscellaneous_state (lFlags): Can't find 'player'","Savegame corrupted");
}
NPC_set_lFlag(o,buf,o2,1);
}
}
// Load wielded objects
if(!GetWadEntry(ifp,"NPCWIELD"))
ithe_panic("Savegame corrupted","NPCWIELD not found");
num = igetl_i(ifp);
ctr2 = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
saveid = igetl_i(ifp);
o = find_id(saveid);
if(!o)
ithe_panic("Load_miscellaneous_state (wieldtab): Can't find object","Savegame corrupted");
memset(o->wield,0,sizeof(WIELD)); // Default to blank
for(ctr3=0;ctr3<ctr2;ctr3++)
{
saveid = igetl_i(ifp);
if(saveid != SAVEID_INVALID)
{
o2 = find_id(saveid);
if(!o2)
ithe_panic("Load_miscellaneous_state (wieldtab2): Can't find object","Savegame corrupted");
SetWielded(o,ctr3,o2);
}
}
}
// Load Global Integers
if(GetWadEntry(ifp,"GLOBALVI"))
{
GetGlobalInts(&intlist,&num);
num = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
ctr2 = igetl_i(ifp);
if(intlist[ctr])
*(int *)intlist[ctr] = ctr2;
}
}
// Load Global Objects
if(GetWadEntry(ifp,"GLOBALVO"))
{
GetGlobalPtrs(&objlist,&num);
num = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
ctr2 = igetl_i(ifp);
o = find_id(ctr2);
if(objlist[ctr])
{
// ilog_quiet("Item %d in list was %x",ctr,*(OBJECT **)objlist[ctr]);
*(OBJECT **)objlist[ctr] = o;
/*
ilog_quiet(" now %x\n",o);
if(o)
ilog_quiet(" called %s\n",o->name);
*/
}
}
}
// Load Global Objects
if(GetWadEntry(ifp,"PATHGOAL"))
{
num = igetl_i(ifp);
for(ctr=0;ctr<num;ctr++)
{
// Get object to set
ctr2 = igetl_i(ifp);
o = find_id(ctr2);
// Get object in path goal (if any)
ctr2 = igetl_i(ifp);
o2 = find_id(ctr2);
// Set it up
if(o && o->user)
o->user->pathgoal.objptr = o2;
}
}
iclose(ifp);
}
/*
* save_ms - Write miscellaneous savegame data
*/
void save_ms(char *filename)
{
long i;
unsigned int ctr,ctr2,ctr3,ok;
WadEntry entry[32];
int Entry=0;
OBJLIST *o;
NPC_RECORDING *n;
IFILE *ofp;
// For saving globals:
unsigned int num;
void **intlist=NULL;
OBJECT **temp,*objtmp;
void **objlist=NULL;
ofp=iopen_write(filename);
if(!ofp)
{
if(!in_editor)
{
ilog_printf("Oh no!\n");
ilog_printf("Could not create file '%s'\n",filename);
}
return;
}
// Write the header
iwrite((unsigned char *)COOKIE_MS,40,ofp); // Cookie
StartWad(ofp);
// Block header
entry[Entry].name="PARTYBLK";
entry[Entry++].start = itell(ofp);
iputl_i(player->save_id,ofp); // The Player
#ifdef DEBUG_SAVE
ilog_quiet("player id = %d\n",player->save_id);
#endif
for(ctr=0;ctr<MAX_MEMBERS;ctr++)
{
i=0; // Get the member's ID without blowing up
if(party[ctr]) // if the member does not exist.
i=party[ctr]->save_id;
iputl_i(i,ofp); // Write the ID number, or 0 for N/A
}
// Block header
entry[Entry].name="FLAGSBLK";
entry[Entry++].start = itell(ofp);
// Flags: find all flags which are TRUE
i=0;
for(ctr=0;ctr<MAXFLAGS;ctr++)
if(tFlag[ctr])
{
if(!tFlagIdentifier[ctr])
{
Bug("Flag is TRUE but has no identifier!\n");
continue;
}
i++;
}
iputl_i(i,ofp);
// Now write the names to disk
for(ctr=0;ctr<MAXFLAGS;ctr++)
if(tFlag[ctr])
{
i = strlen(tFlagIdentifier[ctr])+1;
iputb((unsigned char)i,ofp);
iwrite((unsigned char *)tFlagIdentifier[ctr],i,ofp);
}
// Block header
entry[Entry].name="TIMEDATE";
entry[Entry++].start = itell(ofp);
iputl_i(game_minute,ofp);
iputl_i(game_hour,ofp);
iputl_i(game_day,ofp);
iputl_i(game_month,ofp);
iputl_i(game_year,ofp);
iputl_i(days_passed,ofp);
iputl_i(day_of_week,ofp);
// Block header
entry[Entry].name="GOALDATA";
entry[Entry++].start = itell(ofp);
i=0;
for(o=ActiveList;o;o=o->next)
if(o->ptr->activity >= 0)
if(o->ptr->save_id > 0)
i++;
iputl_i(i,ofp);
for(o=ActiveList;o;o=o->next)
if(o->ptr->activity >= 0)
if(o->ptr->save_id > 0)
{
// Write the main goal
#ifdef DEBUG_SAVE
ilog_quiet("Storing goal for ID %d 0x%x [%s at %d,%d]\n",o->ptr->save_id,o->ptr,o->ptr->name,o->ptr->x,o->ptr->y);
if(o->ptr->parent.objptr)
ilog_quiet("%d is inside %s at %d,%d",o->ptr->save_id,o->ptr->parent.objptr->name,o->ptr->parent.objptr->x,o->ptr->parent.objptr->y);
#endif
iputl_i(o->ptr->save_id,ofp);
iwrite((unsigned char *)PElist[o->ptr->activity].name,32,ofp);
if(!o->ptr->target.objptr)
iputl_i(SAVEID_INVALID,ofp);
else
iputl_i(o->ptr->target.objptr->save_id,ofp);
}
// Save usedata
// Block header
entry[Entry].name="USEDATA ";
entry[Entry++].start = itell(ofp);
// count blank ones
memset(&empty,0,sizeof(empty));
i=0;
for(o=MasterList;o;o=o->next)
if(o->ptr->save_id > 0)
if(UsedataChanged(o->ptr))
i++;
iputl_i(i,ofp);
iputl_i(sizeof(USEDATA),ofp);
for(o=MasterList;o;o=o->next)
if(o->ptr->save_id > 0)
if(UsedataChanged(o->ptr))
{
#ifdef DEBUG_SAVE
ilog_quiet("Save %d : %s\n",o->ptr->save_id,o->ptr->name);
#endif
iputl_i(o->ptr->save_id,ofp);
iwrite((unsigned char *)o->ptr->user,sizeof(USEDATA),ofp);
}
// Save the sub-tasks
entry[Entry].name="SUBTASKS";
entry[Entry++].start = itell(ofp);
// First count how many objects have subtasks
i=0;
for(o=ActiveList;o;o=o->next)
if(o->ptr->activity >= 0)
if(o->ptr->save_id > 0)
{
// Does it have any tasks?
ok=0;
for(ctr=0;ctr<ACT_STACK;ctr++)
if(o->ptr->user->actlist[ctr]>0)
ok=1;
// Yes
if(ok)
i++;
}
iputl_i(i,ofp);
for(o=ActiveList;o;o=o->next)
if(o->ptr->activity >= 0)
if(o->ptr->save_id > 0)
{
ok=0;
for(ctr=0;ctr<ACT_STACK;ctr++)
if(o->ptr->user->actlist[ctr]>0)
ok=1;
if(ok)
{
// Write the object ID
iputl_i(o->ptr->save_id,ofp);
// Write the tasks
for(ctr=0;ctr<ACT_STACK;ctr++)
{
if(o->ptr->user->actlist[ctr]<=0)
{
iputl_i(-1,ofp);
iputl_i(SAVEID_INVALID,ofp);
}
else
{
#ifdef DEBUG_SAVE
if(o->ptr->target.objptr)
ilog_quiet("subtask %d: %s '%s' does %s to %s\n",ctr,o->ptr->name,o->ptr->personalname,PElist[o->ptr->activity].name,o->ptr->target.objptr->name);
else
ilog_quiet("subtask %d: %s '%s' does %s\n",ctr,o->ptr->name,o->ptr->personalname,PElist[o->ptr->activity].name);
#endif
iputl_i(o->ptr->user->actlist[ctr],ofp);
if(o->ptr->user->acttarget[ctr])
iputl_i(o->ptr->user->acttarget[ctr]->save_id,ofp);
else
iputl_i(SAVEID_INVALID,ofp); // No target
}
}
}
}
// Write NPC conversation log
entry[Entry].name="NPCTALKS";
entry[Entry++].start = itell(ofp);
// Count number of NPC's we've spoken to. This can become fragmented with
// blanks so the detection routine is a little strange
i=0;
for(o=MasterList;o;o=o->next)
if(o->ptr->user->npctalk)
{
ok=0; // Did we speak with words, or silence?
for(n=o->ptr->user->npctalk;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
ok=1;
if(ok)
i++;
}
iputl_i(i,ofp); // Store the count
#ifdef DEBUG_SAVE
ilog_quiet("Writing %d npctalks\n",i);
#endif
// Write all the pages we've seen for each NPC
for(o=MasterList;o;o=o->next)
if(o->ptr->user->npctalk)
{
i=0;
for(n=o->ptr->user->npctalk;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
i++;
#ifdef DEBUG_SAVE
ilog_quiet("writing %d entries for NPC %d\n",i,o->ptr->save_id);
#endif
if(i>0)
{
iputl_i(o->ptr->save_id,ofp);
iputl_i(i,ofp);
for(n=o->ptr->user->npctalk;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
{
#ifdef DEBUG_SAVE
ilog_quiet("Save_NPCtalk: %d\n",n->player->save_id);
#endif
iputl_i(n->player->save_id,ofp);
i=strlen(n->page)+1;
iputb((unsigned char)i,ofp);
iwrite((unsigned char *)n->page,i,ofp);
}
}
}
// Write NPC conversation log
entry[Entry].name="NPCLFLAG";
entry[Entry++].start = itell(ofp);
// Count number of NPC's with lFlags
// Again this may be fragmented.
i=0;
for(o=MasterList;o;o=o->next)
if(o->ptr->user->lFlags)
{
ok=0; // Did we speak with words, or silence?
for(n=o->ptr->user->lFlags;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
ok=1;
if(ok)
i++;
}
iputl_i(i,ofp); // Store the count
// Write all the lFlags for each NPC
for(o=MasterList;o;o=o->next)
if(o->ptr->user->lFlags)
{
i=0;
for(n=o->ptr->user->lFlags;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
i++;
if(i>0)
{
iputl_i(o->ptr->save_id,ofp);
iputl_i(i,ofp);
for(n=o->ptr->user->lFlags;n;n=n->next)
if(n->player != NULL && n->page[0] != '\0')
{
iputl_i(n->player->save_id,ofp);
i=strlen(n->page)+1;
iputb((unsigned char)i,ofp);
iwrite((unsigned char *)n->page,i,ofp);
}
}
}
// Write NPC wield table
entry[Entry].name="NPCWIELD";
entry[Entry++].start = itell(ofp);
// Count number of characters wielding objects
i=0;
for(o=MasterList;o;o=o->next)
{
ctr2=0;
for(ctr=0;ctr<WIELDMAX;ctr++)
if(GetWielded(o->ptr,ctr))
ctr2=1;
if(ctr2)
i++;
}
iputl_i(i,ofp); // Store the count
iputl_i(WIELDMAX,ofp); // 9 objects in the wieldtab in this version, maybe more later
// Store the wielded objects
for(o=MasterList;o;o=o->next)
{
ctr2=0;
for(ctr=0;ctr<WIELDMAX;ctr++)
if(GetWielded(o->ptr,ctr))
ctr2=1;
if(ctr2)
{
iputl_i(o->ptr->save_id,ofp);
for(ctr3=0;ctr3<WIELDMAX;ctr3++)
{
objtmp = GetWielded(o->ptr,ctr3);
if(objtmp)
iputl_i(objtmp->save_id,ofp); // Store ID number
else
iputl_i(-1,ofp); // No object here
}
}
}
// Save the global variables in the script engine
// Write Global Integers
entry[Entry].name="GLOBALVI";
entry[Entry++].start = itell(ofp);
GetGlobalInts(&intlist,&num);
iputl_i(num,ofp);
for(ctr=0;ctr<num;ctr++)
{
if(intlist[ctr])
iputl_i(*(int *)intlist[ctr],ofp);
else
iputl_i(-1,ofp);
}
// Write Global Objects
entry[Entry].name="GLOBALVO";
entry[Entry++].start = itell(ofp);
GetGlobalPtrs(&objlist,&num);
iputl_i(num,ofp);
for(ctr=0;ctr<num;ctr++)
{
temp = (OBJECT **)objlist[ctr];
if(temp && *temp)
{
iputl_i((*temp)->save_id,ofp);
// ilog_quiet("Stored item %d, which was %s\n",ctr,(*temp)->name);
}
else
iputl_i(-1,ofp);
}
// Write NPC path goals
entry[Entry].name="PATHGOAL";
entry[Entry++].start = itell(ofp);
// Count number of characters with pathgoals
i=0;
for(o=MasterList;o;o=o->next)
if(o->ptr->user && o->ptr->user->pathgoal.objptr)
i++;
iputl_i(i,ofp); // Store the count
// Store the pathgoals
for(o=MasterList;o;o=o->next)
if(o->ptr->user && o->ptr->user->pathgoal.objptr)
iputl_i(o->ptr->user->pathgoal.objptr->save_id,ofp); // Store ID number
// Finish the WAD
entry[Entry].name=NULL;
FinishWad(ofp,entry); // This closes the file stream
}
/*
* load_lightstate - load the map's current light states
*/
void load_lightstate(int mapnum, int sgnum)
{
IFILE *fp;
char filename[1024];
char *fname;
fname=makemapname(mapnum,sgnum,".lsd");
strcpy(stemp,fname);
if(!loadfile(stemp,filename))
{
// Can't open file, just blank the lights
memset((unsigned char *)curmap->lightst,0,curmap->w*curmap->h*sizeof(unsigned char));
return;
}
fp=iopen(filename);
iread((unsigned char *)sigtemp,40,fp);
if(strcmp(sigtemp,COOKIE_LSD))
{
ilog_printf("File '%s' is not light state data.. it does not contain the right cookie\n",stemp);
exit(1);
}
iread((unsigned char *)curmap->lightst,curmap->w*curmap->h*sizeof(unsigned char),fp);
iclose(fp);
ilog_printf(" Done\n");
}
/*
* save_lightstate - Save the static lighting to disk
*/
void save_lightstate(int mapnum, int sgnum)
{
char *fname;
// Objects layer 3 - static lights
fname=makemapname(mapnum,sgnum,".lsd");
strcpy(stemp,fname);
ofp = iopen_write(stemp);
if(!ofp)
{
if(!in_editor)
{
ilog_printf("Oh no!\n");
ilog_printf("Could not create file '%s'\n",stemp);
}
return;
}
iwrite((unsigned char *)COOKIE_LSD,40,ofp);
iwrite((unsigned char *)curmap->lightst,curmap->w*curmap->h,ofp);
iclose(ofp);
return;
}
/*
* Read savegame header
*/
extern char *read_sgheader(int sgnum, int *world)
{
IFILE *fp;
char filename[1024];
char *fname;
char *title;
fname=makemapname(0,sgnum,".sav");
strcpy(stemp,fname);
if(!loadfile(stemp,filename))
return NULL;
fp=iopen(filename);
iread((unsigned char *)sigtemp,40,fp);
if(strcmp(sigtemp,COOKIE_SAV))
{
iclose(fp);
return NULL;
}
title=strLocalBuf(); // Allocate a 'local' buffer
// Get world number
*world=igetl_i(fp);
// Get savegame title
iread((unsigned char *)title,40,fp);
// Finish
iclose(fp);
return title;
}
/*
* Write savegame header
*/
void write_sgheader(int sgnum, int world, char *title)
{
IFILE *fp;
char *fname;
fname=makemapname(0,sgnum,".sav");
strcpy(stemp,fname);
fp=iopen_write(fname);
// Write magic cookie
iwrite((unsigned char *)COOKIE_SAV,40,fp);
// Write current map number
iputl_i(world,fp);
// Write savegame title
iwrite((unsigned char *)title,40,fp);
// Finish
iclose(fp);
return;
}
//======================================================================
//=========== Auxiliary functions for map IO systems ==================
//======================================================================
/*
* MoveToPocketEnd() - Move an object into the end of someone's pocket
* Only for objects in the main objlist
*/
void MoveToPocketEnd(OBJECT *object, OBJECT *container)
{
OBJECT *temp;
// Check for silliness. This error has never happened to me, but if it
// does I thought that you should know.
if(!object || !container)
{
Bug("MoveToPocketEnd(%x,%x) attempted\n",object,container);
return;
}
// Look for the object in the list
for(temp=curmap->object;temp->next;temp=temp->next)
if(temp->next==object)
{
LL_Remove(&curmap->object->next,object);
LL_Add(&container->pocket.objptr,object); // Add to end
object->parent.objptr = container;
object->flags.fixed=1;
return;
}
ithe_panic("MoveToPocketEnd: This object was not in the list:",object->name);
}
/*
* WriteContainers - Write contents containers to disk.
* Recursive, called by save_z1
*/
void WriteContainers(OBJECT *cont, FILE *fp)
{
OBJECT *temp;
for(temp = cont;temp;temp=temp->next)
{
TWriteObject(fp,temp);
if(temp->pocket.objptr)
WriteContainers(temp->pocket.objptr,fp);
}
return;
}
/*
* WriteContainerSchedules - Write Schedules of container contents
*/
void WriteContainerSchedules(OBJECT *cont)
{
OBJECT *old,*temp;
char ok;
int ctr;
for(temp = cont;temp;temp=temp->next)
{
ok=0;
if(temp->schedule)
for(ctr=0;ctr<24;ctr++)
if(temp->schedule[ctr].hour != -1)
ok=1;
if(!ok)
iputl_i(SAVEID_INVALID,ofp);
else
{
iputl_i(temp->save_id,ofp);
for(ctr=0;ctr<24;ctr++)
{
// Convert the pointer to an ID number for the saving
old = temp->schedule[ctr].target.objptr;
if(old)
temp->schedule[ctr].target.saveid=temp->schedule[ctr].target.objptr->save_id;
// Save it
iwrite((unsigned char *)&temp->schedule[ctr],sizeof(SCHEDULE),ofp);
// Restore the original
temp->schedule[ctr].target.objptr=old;
}
}
if(temp->pocket.objptr)
WriteContainerSchedules(temp->pocket.objptr);
}
return;
}
/*
* When changing maps, objects that we wish to keep must be moved out
* of the current world and into limbo.
*/
void save_objects(int mapnum)
{
int ctr;
OBJECT *temp;
assign_objects(mapnum); // Ensure even the limbo objects have SaveIDs
// Get the party, handling the case where someone is in a boat etc
for(ctr=0;ctr<MAX_MEMBERS;ctr++)
if(party[ctr])
{
if(party[ctr]->parent.objptr)
{
#ifdef DEBUG_SAVE
ilog_quiet("%s is in pocket\n",party[ctr]->name);
#endif
temp = party[ctr]->parent.objptr;
if(temp)
{
#ifdef DEBUG_SAVE
ilog_quiet("pocket is called %s\n",temp->name);
#endif
party[ctr]->user->archive = UARC_INPOCKET;
party[ctr]->user->arcpocket.objptr = temp;
TransferToPocket(party[ctr],&limbo);
// And transfer the container too, but only once
if(temp->user->archive != UARC_ONCEONLY)
{
temp->user->archive = UARC_ONCEONLY;
TransferToPocket(temp,&limbo);
}
}
}
else
{
party[ctr]->user->archive = UARC_NORMAL;
// ilog_quiet("dump member %s\n",party[ctr]->name);
TransferToPocket(party[ctr],&limbo);
}
}
// Get the stuff in Nowhereland
while(curmap->object->next)
{
(curmap->object->next)->user->archive = UARC_SYSLIST;
#ifdef DEBUG_SAVE
ilog_quiet("%s is in syslist\n",curmap->object->next->name);
#endif
TransferToPocket(curmap->object->next,&limbo);
}
// Get the stuff in the Syspocket
while(curmap->object->pocket.objptr)
{
(curmap->object->pocket.objptr)->user->archive = UARC_SYSPOCKET;
#ifdef DEBUG_SAVE
ilog_quiet("%s is in syspocket\n",curmap->object->pocket.objptr->name);
#endif
TransferToPocket(curmap->object->pocket.objptr,&limbo);
}
}
/*
* Move objects out of limbo and into the Real World
*/
void restore_objects()
{
OBJECT *temp,*t2;
// Find new location, if using a tag
if(change_map_tag)
{
// Find the destination object
temp=FindTag(change_map_tag,"target");
if(!temp)
temp=FindTag(change_map_tag,NULL);
if(temp)
{
// We will land here
change_map_x = temp->x;
change_map_y = temp->y;
}
}
change_map_tag=0; // reset tag
// Decode everything
while(limbo.pocket.objptr)
{
t2=limbo.pocket.objptr;
#ifdef DEBUG_SAVE
ilog_quiet("unpack %s: archive flags = %x (%d)\n",limbo.pocket.objptr->name,limbo.pocket.objptr->user->archive,limbo.pocket.objptr->user->archive);
#endif
switch(limbo.pocket.objptr->user->archive)
{
// Move to the linked list buffer
case UARC_SYSPOCKET:
temp=limbo.pocket.objptr->next;
TransferToPocket(limbo.pocket.objptr,curmap->object);
limbo.pocket.objptr=temp;
break;
// Move to the linked list buffer
case UARC_SYSLIST:
temp=limbo.pocket.objptr->next;
limbo.pocket.objptr->parent.objptr = NULL;
LL_Add(&curmap->object,limbo.pocket.objptr);
limbo.pocket.objptr=temp;
break;
// Move to another object's pocket
case UARC_INPOCKET:
temp=limbo.pocket.objptr->next;
TransferToPocket(limbo.pocket.objptr,limbo.pocket.objptr->user->arcpocket.objptr);
// LL_Add(&limbo.pocket->user->arcpocket,limbo.pocket);
limbo.pocket.objptr=temp;
break;
// Move the object to its appropriate map position
case UARC_NORMAL:
case UARC_ONCEONLY:
default:
limbo.pocket.objptr->x=change_map_x;
limbo.pocket.objptr->y=change_map_y;
if(!ForceFromPocket(limbo.pocket.objptr,&limbo,limbo.pocket.objptr->x,limbo.pocket.objptr->y))
{
Bug("Problem restoring '%s' from Limbo!\n",limbo.pocket.objptr->name);
// Force us to the next one
limbo.pocket.objptr = limbo.pocket.objptr->next;
}
break;
}
// Reset Archive state for object
t2->user->archive=0;
}
}
// Set object IDs
static void assign_objects(int mapnum)
{
int objtot,objctr;
unsigned int newid;
OBJLIST *ptr;
// First, set each object so it has an ID of its own.
objctr=1; // 1-based, so 0 means 'nothing'
objtot=0;
for(ptr=MasterList;ptr;ptr=ptr->next)
if(in_editor || ptr->ptr->save_id < 1) // Only if it doesn't have one already
{
if(!in_editor)
{
newid=0;
do
{
newid=MakeSaveID(mapnum,objctr++);
} while(find_id(newid));
}
else
newid=MakeSaveID(mapnum,objctr++);
ptr->ptr->save_id = newid;
#ifdef DEBUG_SAVE
ilog_quiet("Assigned object '%s' the ID %d, from map %d\n",ptr->ptr->name,ptr->ptr->save_id,mapnumber);
#endif
objtot++;
}
}
// Fast Find object ID
static void fastfind_id_start()
{
OBJLIST *t;
int ctr;
if(fastfind_id_list)
return;
ctr=0;
for(t=MasterList;t;t=t->next)
if(t->ptr)
ctr++;
if(!ctr)
ithe_panic("fastfind_id_start: No objects in the world!",NULL);
fastfind_id_num = ctr;
fastfind_id_list = (OBJECT **)M_get(ctr,sizeof(OBJECT *));
ctr=0;
for(t=MasterList;t;t=t->next)
if(t->ptr)
fastfind_id_list[ctr++]=t->ptr;
qsort(fastfind_id_list,fastfind_id_num,sizeof(OBJECT *),CMP_sort);
}
static void fastfind_id_stop()
{
if(!fastfind_id_list)
return;
M_free(fastfind_id_list);
fastfind_id_list = NULL;
}
static OBJECT *fastfind_id(unsigned int id)
{
OBJECT **xx;
if(!id)
return NULL;
if(!fastfind_id_list)
ithe_panic("FastFind_ID called before FastFind_ID_start",NULL);
xx=(OBJECT **)bsearch((const void *)id,fastfind_id_list,fastfind_id_num,sizeof(OBJECT *),CMP_search);
if(xx)
return *xx;
return NULL;
}
static int CMP_sort(const void *a, const void *b)
{
return (*(OBJECT **)a)->save_id - (*(OBJECT **)b)->save_id;
}
static int CMP_search(const void *a, const void *b)
{
return (int)((long)a - (*(OBJECT **)b)->save_id);
}
/*
* Get savegame slot number from an Allegro dialog
*/
// Declare the stuff we'll need
static DIALOG SaveList_dialog[] =
{
/* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) */
{ d_billwin_proc, 0, 8, 368, 166, 0, 0, 0, 0, 0, 0, (void *)"" },
{ d_ctext_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (void *)"" },
{ d_billbutton_proc, 288, 113, 64, 16, 0, 0, 0, D_EXIT, 0, 0, (void *)"OK" },
{ d_billbutton_proc, 288, 135, 64, 16, 0, 0, 27, D_EXIT, 0, 0, (void *)"Cancel" },
{ d_billlist_proc, 16, 28, 256, 123, 0, 0, 0, D_EXIT, 0, 0, (void *)LoadSaveList_getter},
{ NULL }
};
static char **SL_UserList;
static int SL_ListLen=0;
// Helper function
static char *LoadSaveList_getter(int index, int *list_size)
{
if(index < 0)
{
if(list_size)
*list_size = SL_ListLen;
return NULL;
}
return SL_UserList[index];
}
// Do it
int GetLoadSaveSlot(int saving)
{
int ctr,blank;
char *savename;
char savegame[MAX_SAVES][40];
int savenum[MAX_SAVES];
char **list;
int listtotal,ret,ret2;
memset(savegame,0,sizeof(savegame));
memset(savenum,0,sizeof(savenum));
// Scan existing savegames
listtotal=0;
// Record which ones exist
for(ctr=1;ctr<MAX_SAVES;ctr++)
{
savename=read_sgheader(ctr,&blank);
if(savename)
{
strcpy(savegame[listtotal],savename);
savenum[listtotal]=ctr;
listtotal++;
}
}
// Now add an extra item to the list
if(saving)
{
strcpy(savegame[listtotal],"Empty Slot");
if(listtotal>0)
savenum[listtotal]=savenum[listtotal-1]+1; // New slot number
else
savenum[listtotal]=1;; // New slot number
if(savenum[listtotal]>=MAX_SAVES)
listtotal--; // No more slots
}
else
{
strcpy(savegame[listtotal],"Restart Game");
savenum[listtotal]=-666; // Special
}
listtotal++;
list=(char **)M_get(listtotal,sizeof(char *)); // Make list to give to dialog
// POP-U-LATE!
for(ctr=0;ctr<listtotal;ctr++)
list[ctr]=&savegame[ctr][0];
// Set list default to end
SaveList_dialog[4].d1=listtotal-1;// Control 4 is the list box
// Set up some globals
SL_ListLen=listtotal;
SL_UserList=list;
// Do the dialog
centre_dialog(SaveList_dialog);
ret = moveable_do_dialog(SaveList_dialog, 4);
ret2 = SaveList_dialog[4].d1;
M_free(list);
if(ret==3) // Cancel
return -1; // No savegame for you
if(ret2>=listtotal)
return -1; // Problem
return savenum[ret2]; // Return slot number
}
int GetTextBox(char *text, char *prompt)
{
int ret;
DIALOG TextEdit_dialog[] =
{
/* (dialog proc) (x) (y) (w) (h) (fg) (bg) (key) (flags) (d1) (d2) (dp) */
{ d_billwin_proc, 0, 8, 320, 64, 0, 0, 0, 0, 0, 0, (void *)""},
{ d_ctext_proc, 128, 16, 0, 0, 0, 0, 0, 0, 0, 0, (void *)""},
{ d_billedit_proc, 32, 32, 256, 16, 0, 0, 0, D_EXIT, 40, 0, (void *)""},
{ d_billbutton_proc, 0, 0, 0, 0, 0, 0, 27, D_EXIT, 0, 0, (void *)""},
{ NULL }
};
TextEdit_dialog[1].dp=(void *)prompt;
TextEdit_dialog[1].bg=get_bill_color(bill_face);
TextEdit_dialog[2].dp=(void *)text;
// Do the dialog
centre_dialog(TextEdit_dialog);
ret = moveable_do_dialog(TextEdit_dialog, 2); // Set 2 in focus
if(ret == 3)
return 0;
return 1;
}
//
// Has the usedata block changed to be worth saving?
//
int UsedataChanged(OBJECT *o)
{
int hp;
// Wish away the oldHP value because it gets regenerated during reload
// and otherwise every object in the world gets saved (~10k objects)
hp = o->user->oldhp;
o->user->oldhp=0;
// Now see if it's all blank
if(memcmp(o->user,&empty,sizeof(USEDATA)))
{
// No, store it
o->user->oldhp=hp;
return 1;
}
// Yes, don't bother with this one
o->user->oldhp=hp;
return 0;
}
//
// In the editor, process objects that we don't understand when loading
//
int ProcessBadObject(OBJECT **objptr, char *name)
{
static int DeleteAll=0;
static int ReplaceAll=0;
int key;
// If we're deleteing all unknown objects, do it automatically
if(DeleteAll)
{
DeleteProtoObject(*objptr);
*objptr=NULL;
return 0; // Dealt with
}
if(ReplaceAll)
{
strcpy(name,CHlist[0].name);
return 1; // Retry it, this will cause it to initialise as this
}
// Ask the user what to do
irecon_printf("Could not find definition of object '%s' in the scriptfile.\n",name);
irecon_printf("Do you want to..\n");
irecon_printf("1. Change this object to '%s'\n",CHlist[0].name);
irecon_printf("2. Change ALL unknown objects to '%s'\n",CHlist[0].name);
irecon_printf("3. Delete this object\n");
irecon_printf("4. Delete ALL unknown objects\n");
//irecon_printf("5. Enter a name to change this object to\n");
irecon_printf("5. Quit (so you can add the object to resource.txt)\n");
do
{
key=WaitForAscii();
switch(key)
{
case '1':
strcpy(name,CHlist[0].name); // Turn to unknown object
return 1; // Re-evaluate the new string
case '2':
ReplaceAll=1; // Do this in future
strcpy(name,CHlist[0].name); // Turn to unknown object
return 1; // Re-evaluate
case '3':
// Kill it
DeleteProtoObject(*objptr);
*objptr=NULL;
return 0; // Dealt with
case '4':
DeleteAll=1; // Do this in future
DeleteProtoObject(*objptr);
*objptr=NULL;
return 0; // Dealt with
case '5':
ithe_panic("Could not find this character in resource.txt:",name);
return 0; // Likely
default:
break;
}
} while(1);
/*
if(key == '5')
{
do {
irecon_printf("Enter the object name:\n");
if(!GetStringInput(objname,64))
exit(1);
// scanf("%s",stemp);
temp_int = getnum4char(objname); // Find character
if(temp_int == -1)
irecon_printf("Not found in main.txt\n");
} while(temp_int == -1);
}
*/
return 0;
}
//
// Delete a not-yet-fully-formed object
//
void DeleteProtoObject(OBJECT *o)
{
LL_Remove(&curmap->object,o);
ML_Del(&MasterList,o);
M_free(o);
}
//
// SaveID coder/decoder
//
unsigned int inline MakeSaveID(unsigned int map, unsigned int id)
{
return ((map<<SAVEID_IDBITS) & SAVEID_MAPMASK) | (id & SAVEID_IDMASK);
}
unsigned int inline GetSaveID_ID(unsigned int saveid)
{
return (saveid & SAVEID_IDMASK);
}
unsigned int inline GetSaveID_Map(unsigned int saveid)
{
return (saveid & SAVEID_MAPMASK)>>SAVEID_IDBITS;
}