/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Character saving and loading module *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "SysData.h"
#include "skill.h"
#include "mobiles.h"
#include "objects.h"
#include "rooms.h"
#include "deity.h"
#include "class.h"
#include "SmaugWizDoc.h"
#include "SmaugFiles.h"
#include "descriptor.h"
#include "character.h"
// Array to keep track of equipment temporarily. -Thoric
CObjData *save_equipment[MAX_WEAR][8];
CCharacter *quitting_char, *loading_char, *saving_char;
int file_ver;
// Array of containers read for proper re-nesting of objects.
static CObjData * rgObjNest [MAX_NEST];
// Un-equip character before saving to ensure proper -Thoric
// stats are saved in case of changes to or removal of EQ
void de_equip_char (CCharacter *ch)
{
CObjData *obj;
int x, y;
for (x = 0; x < MAX_WEAR; x++)
for (y = 0; y < MAX_LAYERS; y++)
save_equipment [x][y] = NULL;
POSITION pos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (pos)) {
if (obj->wear_loc > -1 && obj->wear_loc < MAX_WEAR) {
if (ch->GetTrustLevel () >= obj->GetLevel ()) {
for (x = 0; x < MAX_LAYERS; ++x)
if (! save_equipment [obj->wear_loc][x]) {
save_equipment [obj->wear_loc][x] = obj;
break;
}
if (x == MAX_LAYERS) {
bug ("%s had on more than %d layers of clothing in one"
" location (%d): %s", ch->GetName (),
MAX_LAYERS, obj->wear_loc, obj->GetName ());
}
} else {
bug ("%s had on %s: ch->GetLevel () = %d "
"obj->GetLevel () = %d", ch->GetName (),
obj->GetName (), ch->GetLevel (), obj->GetLevel ());
}
unequip_char (ch, obj);
}
}
}
/*
* Re-equip character -Thoric
*/
void re_equip_char (CCharacter *ch)
{
int x,y;
for (x = 0; x < MAX_WEAR; x++)
for (y = 0; y < MAX_LAYERS; y++)
if (save_equipment[x][y] != NULL)
{
if (quitting_char != ch)
equip_char (ch, save_equipment[x][y], x);
save_equipment[x][y] = NULL;
}
else
break;
}
// Save a character and inventory.
// Would be cool to save NPC's too for quest purposes,
// some of the infrastructure is provided.
void save_char_obj (CCharacter *ch)
{
CString strsave;
CString strback;
FILE *fp;
if (!ch) {
bug ("Save_char_obj: null ch!", 0);
return;
}
if (ch->IsNpc () || ch->GetLevel () < 2)
return;
saving_char = ch;
// save pc's clan's data while we're at it to keep the data in sync
if (!ch->IsNpc () && ch->GetPcData ()->GetClan ())
ch->GetPcData ()->GetClan ()->Save ();
// save deity's data to keep it in sync -ren
if (!ch->IsNpc () && ch->GetPcData ()->deity)
save_deity (ch->GetPcData ()->deity);
if (ch->GetDesc () && ch->GetDesc ()->m_pOriginal)
ch = ch->GetDesc ()->m_pOriginal;
de_equip_char (ch);
ch->SetSaveTime (CurrentTime);
strsave = FileTable.PlayerName (capitalize (ch->GetName ()));
// Save immortal stats, level & vnums for wizlist -Thoric
// and do_vnums command
//
// Also save the player flags so we the wizlist builder can see
// who is a guest and who is retired.
if (ch->GetLevel () >= LEVEL_IMMORTAL) {
strback = FileTable.MakeName (SD_GOD_DIR, capitalize (ch->GetName ()));
if ((fp = fopen (strback, "w")) == NULL) {
bug ("Save_god_level: fopen, file = %s", NCCP strsave);
} else {
fprintf (fp, "Level %d\n", ch->GetLevel ());
fprintf (fp, "Pcflags %d\n", ch->GetPcFlags ());
if (ch->GetPcData ()->r_range_lo && ch->GetPcData ()->r_range_hi)
fprintf (fp, "RoomRange %d %d\n",
ch->GetPcData ()->r_range_lo,
ch->GetPcData ()->r_range_hi);
if (ch->GetPcData ()->o_range_lo && ch->GetPcData ()->o_range_hi)
fprintf (fp, "ObjRange %d %d\n",
ch->GetPcData ()->o_range_lo,
ch->GetPcData ()->o_range_hi);
if (ch->GetPcData ()->m_range_lo && ch->GetPcData ()->m_range_hi)
fprintf (fp, "MobRange %d %d\n",
ch->GetPcData ()->m_range_lo,
ch->GetPcData ()->m_range_hi);
fclose (fp);
}
}
// Find out if player directory exists, and create it if not.
// Make sure Dir is there, or change strsave to alternate save loc.
BOOL bChanged = FileTable.CreatePlayerDirectory (strsave,ch->GetName ());
if (bChanged)
ch->SendText ("Not Saved in Player Directory. Ask imm's to fix\n");
if ((fp = fopen (strsave, "w")) == NULL) {
bug ("Save_char_obj: fopen, file = %s", NCCP strsave);
} else {
ch->Write (fp);
if (! ch->GetCarryList ().IsEmpty ())
fwrite_obj (ch->GetCarryList (), fp, 0, OS_CARRY, ch->GetLevel ());
if (ch->HasPet ())
ch->GetPet ()->Write (fp);
if (ch->comments)
fwrite_comments (ch, fp); // comments
fprintf (fp, "#END\n");
fclose (fp);
}
// Auto-backup pfile (can cause lag with high disk access situtations
if (SysData.IsBackup ()) {
// Find out if backup player directory exists, and create it if not.
// Make sure Dir is there, or change strsave to alternate save loc.
// (TRUE means create a backup directory)
FileTable.CreatePlayerDirectory (strback, ch->GetName (), TRUE);
if (! CopyFile (strsave, strback, FALSE)) {
bug ("save_char_obj::Error %d: Unable to copy %s to %s.",
GetLastError (), NCCP strsave, NCCP strback);
}
}
re_equip_char (ch);
write_corpses (ch, NULL);
quitting_char = NULL;
saving_char = NULL;
}
// Write an object and its contents.
void fwrite_obj (CObjectList& List, FILE *fp, int iNest, short os_type,
short ChLevel /* = 0 */)
{
if (iNest >= MAX_NEST) {
bug ("fwrite_obj: iNest hit MAX_NEST %d", iNest);
return;
}
POSITION pos = List.GetHeadPosition ();
while (pos) {
CObjData &Obj = *List.GetNext (pos);
Obj.Write (fp, iNest, os_type, ChLevel);
if (! Obj.GetContentList ().IsEmpty ())
fwrite_obj (Obj.GetContentList (), fp, iNest + 1, OS_CARRY, ChLevel);
}
}
// Load a char and inventory into a new ch structure.
BOOL load_char_obj (CDescriptor *d, char *name, BOOL preload)
{
char buf [MAX_INPUT_LENGTH];
char strsave [MAX_INPUT_LENGTH];
extern char strArea [MAX_INPUT_LENGTH];
FILE *fp;
char *pLine;
struct stat fst;
int i, x;
extern FILE *fpArea;
extern int gAcount;
CCharacter *ch = new CCharacter;
ch->InitChar ();
for (x = 0; x < MAX_WEAR; x++)
for (i = 0; i < MAX_LAYERS; i++)
save_equipment [x][i] = NULL;
loading_char = ch;
ch->SetPcData (new pc_data);
pc_data &Pc = *ch->GetPcData ();
d->m_pCharacter = ch;
ch->SetDesc (d);
ch->SetName (name);
ch->SetBlank ();
ch->SetCombine ();
ch->SetHasPrompt ();
ch->perm_str = 13;
ch->perm_int = 13;
ch->perm_wis = 13;
ch->perm_dex = 13;
ch->perm_con = 13;
ch->perm_cha = 13;
ch->perm_lck = 13;
Pc.condition [COND_THIRST] = 48;
Pc.condition [COND_FULL] = 48;
Pc.condition [COND_BLOODTHIRST] = 10;
ch->SetMentalState (-10);
ch->m_Style = STYLE_FIGHTING;
Pc.pagerlen = 24;
ch->LockMenus (TRUE);
ch->SetMenuPagelen (24); // BUILD INTERFACE
strcpy (strsave, FileTable.PlayerName (capitalize (name)));
if (stat (strsave, &fst) != -1) {
if (fst.st_size == 0) {
strcpy (strsave, FileTable.BackupPlayerName (capitalize (name)));
ch->SendText ("Restoring your backup player file...");
} else {
sprintf (buf, "%s player data for: %s (%dK)",
preload ? "Preloading" : "Loading", ch->GetName (),
(int) fst.st_size/1024);
gpDoc->LogString (buf, LOG_COMM, LEVEL_GREATER);
}
}
// else no player file
BOOL bFound = FALSE;
if (fp = fopen (strsave, "r")) {
bFound = TRUE;
for (int iNest=0; iNest < MAX_NEST; ++iNest)
rgObjNest [iNest] = NULL;
// Cheat so that bug will show line #'s -- Altrag
fpArea = fp;
strcpy (strArea, strsave);
gAcount = 0; // clear line count
for (;;) {
char letter;
char *word;
pLine = fread_line (fp);
letter = *pLine++;
if (letter == '*')
continue;
if (letter != '#') {
bug ("Load_char_obj: %s, # character not found.", name);
delete ch;
d->m_pCharacter = NULL;
return TRUE;
}
word = ParseWord (pLine);
if (! str_cmp (word, "PLAYER")) {
if (! ch->Read (fp, preload)) {
delete ch;
d->m_pCharacter = NULL;
return TRUE;
}
if (preload)
break;
}
else if (! str_cmp (word, "OBJECT")) // Objects
fread_obj (ch, fp, OS_CARRY);
// 1.4add
// else if (! strcmp (word, "MorphData")) // Morphs
// fread_morph_data (ch, fp);
else if (! str_cmp (word, "COMMENT"))
fread_comment (ch, fp); // Comments
else if (! str_cmp (word, "MOBILE")) {
CCharacter *pet = fread_mobile (fp);
ch->SetPet (pet);
pet->SetMaster (ch);
pet->SetCharmed ();
// Temporarily set the pet in the polymorph room, until
// the player gets fully loaded.
pet->SetInRoom (RoomTable.GetRoom (SysData.m_RoomPoly));
// pet->SendToRoom (ch->GetInRoom ());
}
else if (! str_cmp (word, "END")) // Done
break;
else {
bug ("Load_char_obj: %s, bad section.", name);
break;
}
}
fclose (fp);
fpArea = NULL;
strcpy (strArea, "$");
}
if (! Pc.ice_listen)
Pc.ice_listen = str_dup ("");
if (! bFound) {
ch->SetShortDescr ("");
ch->SetLongDescr ("");
ch->SetDescription ("");
Pc.SetClanName (STRALLOC (""));
Pc.SetCouncilName (STRALLOC (""));
Pc.SetDeityName (STRALLOC (""));
Pc.SetPassWord (str_dup (""));
Pc.SetBamfin (str_dup (""));
Pc.SetBamfout (str_dup (""));
Pc.SetRank (str_dup (""));
Pc.SetBestowments (str_dup (""));
Pc.SetTitle (STRALLOC (""));
Pc.SetHomepage (str_dup (""));
Pc.bio = STRALLOC ("");
Pc.authed_by = STRALLOC ("");
Pc.SetPrompt (STRALLOC (""));
Pc.SetFightPrompt (STRALLOC (""));
} else {
if (! Pc.GetClanName ()) {
Pc.SetClanName (STRALLOC (""));
Pc.SetClan (NULL);
}
if (! Pc.GetCouncilName ()) {
Pc.SetCouncilName (STRALLOC (""));
Pc.council = NULL;
}
if (! Pc.GetDeityName ()) {
Pc.SetDeityName (STRALLOC (""));
Pc.deity = NULL;
}
if (! Pc.bio)
Pc.bio = STRALLOC ("");
if (! Pc.authed_by)
Pc.authed_by = STRALLOC ("");
if (!ch->IsNpc () && ch->IsImmortal ()) {
if (Pc.wizinvis < 2)
Pc.wizinvis = ch->GetLevel ();
if (! preload)
assign_area (ch);
}
for (i = 0; i < MAX_WEAR; i++)
for (x = 0; x < MAX_LAYERS; x++)
if (save_equipment [i][x]) {
equip_char (ch, save_equipment [i][x], i);
save_equipment [i][x] = NULL;
}
}
loading_char = NULL;
return bFound;
}
CCharacter *fread_mobile (FILE *fp)
{
char *word = "", *pLine;
if (! feof (fp)) {
pLine = fread_line (fp);
word = ParseWord (pLine);
}
if (strcmp (word, "Vnum")) {
ScanToEnd (fp, "EndMobile");
bug ("fread_mobile: Vnum not found");
return NULL;
}
int vnum = ParseNumber (pLine);
CCharacter *mob = create_mobile (MobTable.GetMob (vnum));
if (! mob) {
ScanToEnd (fp, "EndMobile");
bug ("fread_mobile: No index data for vnum %d", vnum);
return NULL;
}
mob->ReadMob (fp);
return mob;
}
#if defined (KEY)
#undef KEY
#endif
#define KEY(literal,field,value) \
if (!str_cmp (word, literal)) { \
field = value; \
fMatch = TRUE; \
break; \
}
#define KEY2(literal, field, value) \
if (!str_cmp (word, literal)) { \
field (value); \
fMatch = TRUE; \
break; \
}
void fread_obj (CCharacter *ch, FILE *fp, short os_type)
{
CObjData *obj;
char *word;
int iNest;
BOOL fMatch;
CRoomIndexData *room = NULL;
char *pLine;
obj = new CObjData;
obj->count = 1;
obj->wear_loc = -1;
obj->weight = 1;
BOOL fNest = TRUE; // Requiring a Nest 0 is a waste
BOOL fVnum = TRUE;
iNest = 0;
for (;;) {
fMatch = FALSE;
if (! feof (fp)) {
pLine = fread_line (fp);
word = ParseWord (pLine);
}
else word = "End";
switch (UPPER (word [0])) {
case '*':
fMatch = TRUE;
break;
case 'A':
KEY2("ActionDesc", obj->SetActionDescr, ParseCString (pLine, fp));
if (!str_cmp (word, "Affect") || !str_cmp (word, "AffectData")) {
CAffectData *paf;
int pafmod;
paf = new CAffectData;
if (! str_cmp (word, "Affect")) {
paf->type = ParseNumber (pLine);
} else {
int sn = SkillTable.Lookup (ParseWord (pLine));
if (sn < 0)
bug ("Fread_obj: unknown skill.");
else
paf->type = sn;
}
paf->duration = ParseNumber (pLine);
pafmod = ParseNumber (pLine);
paf->location = ParseNumber (pLine);
paf->SetVector (ParseWord (pLine));
if (paf->location == APPLY_WEAPONSPELL
|| paf->location == APPLY_WEARSPELL
|| paf->location == APPLY_REMOVESPELL
|| paf->location == APPLY_STRIPSN
|| paf->location == APPLY_RECURRINGSPELL)
paf->modifier = slot_lookup (pafmod);
else
paf->modifier = pafmod;
obj->AddAffect (paf);
fMatch = TRUE;
break;
}
if (! str_cmp (word, "AntiClass")) {
CClassData &Cl = *ClassTable.Find (ParseCString (pLine, fp));
if (&Cl)
obj->SetAntiClass (Cl.GetClass ());
fMatch = TRUE;
break;
}
break;
case 'C':
KEY ("Cost", obj->cost, ParseNumber (pLine));
KEY ("Count", obj->count, ParseNumber (pLine));
break;
case 'D':
KEY2("Description", obj->SetDescription, ParseCString (pLine, fp));
break;
case 'E':
if (! str_cmp (word, "ExtraFlags")) {
obj->m_ExtraFlags.Parse (pLine);
fMatch = TRUE;
break;
}
if (! str_cmp (word, "ExtraDescr")) {
CExtraDescrData *ed = new CExtraDescrData;
ed->keyword = ParseString (pLine, fp);
ed->SetDescription (ParseString (pLine, fp));
obj->ExDesList.AddTail (ed);
fMatch = TRUE;
break;
}
if (! str_cmp (word, "End")) {
if (! fNest || ! fVnum) {
bug ("Fread_obj: %s, incomplete object.", obj->GetName ());
delete obj;
return;
} else {
short wear_loc = obj->wear_loc;
CObjIndexData &Idx = *obj->GetIndex ();
if (! obj->GetName ())
obj->SetName (Idx.GetName ());
if (! obj->GetDescription ())
obj->SetDescription (Idx.GetDescription ());
if (!obj->GetShortDescr ())
obj->SetShortDescr (Idx.GetShortDescr ());
if (! obj->GetActionDescr ())
obj->SetActionDescr (Idx.GetActionDescr ());
if (! obj->serial) {
cur_obj_serial =
UMAX ((cur_obj_serial + 1) & (BV30-1), 1);
obj->serial =
obj->GetIndex ()->serial = cur_obj_serial;
}
if (fNest)
rgObjNest [iNest] = obj;
Idx.m_ObjList.AddTail (obj);
Idx.count += obj->count;
obj->wear_loc = -1; // Why?
// Corpse saving. -- Altrag
if (os_type == OS_CORPSE) {
if (! room) {
bug ("Fread_obj: %s, Corpse without room",
obj->GetName ());
room = RoomTable.GetRoom (SysData.m_RoomLimbo);
}
// Give the corpse a timer if there isn't one
if (obj->timer < 1)
obj->timer = 40;
if (room->vnum == SysData.m_RoomHallOfFallen
&& ! obj->GetContentList ().IsEmpty ())
obj->timer = -1;
obj = obj_to_room (obj, room);
}
else if (iNest == 0 || rgObjNest [iNest] == NULL) {
int slot;
int x;
BOOL reslot = FALSE;
if (wear_loc > -1 && wear_loc < MAX_WEAR) {
for (x = 0; x < MAX_LAYERS; x++)
if (! save_equipment [wear_loc][x]) {
save_equipment [wear_loc][x] = obj;
slot = x;
reslot = TRUE;
break;
}
if (x == MAX_LAYERS)
bug ("Fread_obj: too many layers %d",
wear_loc);
}
obj = obj_to_char (obj, ch);
if (reslot && slot != -1)
save_equipment [wear_loc][slot] = obj;
} else {
if (rgObjNest [iNest-1]) {
separate_obj (rgObjNest [iNest-1]);
obj = obj_to_obj (obj, rgObjNest [iNest-1]);
}
else
bug ("Fread_obj: nest layer missing %d", iNest-1);
}
if (fNest)
rgObjNest [iNest] = obj;
return;
}
}
break;
case 'I':
KEY ("ItemType", obj->item_type, ParseNumber (pLine));
break;
case 'L':
KEY2("Level", obj->SetLevel, ParseNumber (pLine));
break;
case 'N':
KEY2("Name", obj->SetName, ParseCString (pLine, fp));
if (! str_cmp (word, "Nest")) {
iNest = ParseNumber (pLine);
if (iNest < 0 || iNest >= MAX_NEST) {
bug ("Fread_obj: bad nest %d.", iNest);
iNest = 0;
fNest = FALSE;
}
fMatch = TRUE;
}
break;
case 'R':
KEY ("Room", room, RoomTable.GetRoom (ParseNumber (pLine)));
case 'S':
KEY2("ShortDescr", obj->SetShortDescr, ParseCString (pLine, fp));
if (! str_cmp (word, "Spell")) {
int iValue = ParseNumber (pLine);
int sn = SkillTable.Lookup (ParseWord (pLine));
if (iValue < 0 || iValue > 5)
bug ("Fread_obj: bad iValue %d.", iValue);
else if (sn < 0)
bug ("Fread_obj: unknown skill.");
else
obj->value [iValue] = sn;
fMatch = TRUE;
break;
}
break;
case 'T':
KEY ("Timer", obj->timer, ParseNumber (pLine));
break;
case 'V':
if (! str_cmp (word, "Values")) {
int x1, x2, x3, x4, x5, x6;
x1 = x2 = x3 = x4 = x5 = x6 = 0;
sscanf (pLine, "%d %d %d %d %d %d",
&x1, &x2, &x3, &x4, &x5, &x6);
obj->value [0] = x1;
obj->value [1] = x2;
obj->value [2] = x3;
obj->value [3] = x4;
obj->value [4] = x5;
obj->value [5] = x6;
fMatch = TRUE;
break;
}
if (! str_cmp (word, "Vnum")) {
int vnum = ParseNumber (pLine);
if ((obj->pIndexData = OIdxTable.GetObj (vnum)) == NULL)
fVnum = FALSE;
else {
fVnum = TRUE;
obj->cost = obj->GetIndex ()->cost;
obj->weight = obj->GetIndex ()->weight;
obj->item_type = obj->GetIndex ()->item_type;
obj->wear_flags = obj->GetIndex ()->wear_flags;
obj->m_ExtraFlags = obj->GetIndex ()->m_ExtraFlags;
}
fMatch = TRUE;
break;
}
break;
case 'W':
KEY ("WearFlags", obj->wear_flags, ParseNumber (pLine));
KEY ("WearLoc", obj->wear_loc, ParseNumber (pLine));
KEY ("Weight", obj->weight, ParseNumber (pLine));
break;
}
if (! fMatch) {
bug ("Fread_obj: no match: %s", word);
delete obj;
return;
}
}
}
// Based on last time modified, show when a player was last on -Thoric
void do_last (CCharacter *ch, char *argument)
{
char buf [MAX_STRING_LENGTH];
char arg [MAX_INPUT_LENGTH];
char name[MAX_INPUT_LENGTH];
struct stat fst;
one_argument (argument, arg);
if (arg[0] == '\0')
{
ch->SendText ("Usage: last <playername>\n\r");
return;
}
strcpy (name, capitalize (arg));
strcpy (buf, FileTable.PlayerName (name));
if (stat (buf, &fst) != -1)
sprintf (buf, "%s was last on: %s\r", name,
NCCP gpDoc->GetTimeString (fst.st_mtime));
else
sprintf (buf, "%s was not found.\n\r", name);
ch->SendText (buf);
}
void write_corpses (CCharacter *ch, const char *name)
{
FILE *fp = NULL;
// Name and ch support so that we dont have to have a char to save their
// corpses.. (ie: decayed corpses while offline)
if (ch && ch->IsNpc ()) {
bug ("Write_corpses: writing NPC corpse.");
return;
}
if (ch)
name = ch->GetName ();
CString Cname = FileTable.MakeCorpseName (capitalize (name));
// Go by vnum, less chance of screwups. -- Altrag
CObjIndexData &Idx = *OIdxTable.Find (OBJ_VNUM_CORPSE_PC);
if (&Idx) {
POSITION pos = Idx.m_ObjList.GetHeadPosition ();
while (pos) {
CObjData &Corpse = *Idx.m_ObjList.GetNext (pos);
if (Corpse.GetInRoom ()
&& !str_cmp (Corpse.GetShortDescr ()+14, name)) {
if (! fp) {
if (! (fp = fopen (Cname, "w"))) {
bug ("Write_corpses: Cannot open file: %s", Cname);
return;
}
}
Corpse.Write (fp, 0, OS_CORPSE);
fwrite_obj (Corpse.GetContentList (), fp, 1, OS_CARRY);
}
}
}
if (fp) {
fprintf (fp, "#END\n\n");
fclose (fp);
} else {
remove (FileTable.MakeCorpseName (capitalize (name)));
}
}
void load_corpses ()
{
CString Fname;
FILE *fp;
extern int falling;
WIN32_FIND_DATA Fd;
HANDLE Dh = FindFirstFile (FileTable.MakeCorpseName ("*"), &Fd);
if (Dh == INVALID_HANDLE_VALUE) then return;
gpDoc->LogString ("Loading corpses...", LOG_BOOT);
falling = 1; // Arbitrary, must be >0 though.
do {
Fname = FileTable.MakeName (SD_CORPSE_DIR, Fd.cFileName);
gpDoc->m_pLog->Printf (LOG_ALWAYS, "Corpse -> %s\n", NCCP Fname);
if (! (fp = fopen (Fname, "r"))) {
perror (Fname);
continue;
}
for (;;) {
char letter;
char *word, *pLine;
pLine = fread_line (fp);
letter = *pLine++;
if (letter == '*')
continue;
if (letter != '#') {
bug ("Load_corpses: # not found.");
break;
}
word = ParseWord (pLine);
if (! str_cmp (word, "CORPSE"))
fread_obj (NULL, fp, OS_CORPSE);
else if (! str_cmp (word, "OBJECT"))
fread_obj (NULL, fp, OS_CARRY);
else if (! str_cmp (word, "END"))
break;
else {
bug ("Load_corpses: bad section.");
break;
}
}
fclose (fp);
} while (FindNextFile (Dh, &Fd));
FindClose (Dh);
falling = 0;
}