/* Crimson2 Mud Server
* All source written/copyright Ryan Haksi 1995 *
* This source code is proprietary. Use in whole or in part without
* explicity permission by the author is strictly prohibited
*
* Current email address(es): cryogen@infoserve.net
* Phone number: (604) 591-5295
*
* C4 Script Language written/copyright Cam Lesiuk 1995
* Email: clesiuk@engr.uvic.ca
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "crimson2.h"
#include "macro.h"
#include "log.h"
#include "mem.h"
#include "str.h"
#include "ini.h"
#include "file.h"
#include "extra.h"
#include "property.h"
#include "code.h"
#include "thing.h"
#include "index.h"
#include "edit.h"
#include "history.h"
#include "socket.h"
#include "exit.h"
#include "world.h"
#include "base.h"
#include "object.h"
#include "char.h"
#include "mobile.h"
#include "area.h"
#include "reset.h"
#include "cmd_inv.h"
BYTE *rFlagList[] = {
"RESET-WHEN-EMPTY",
"RESET-IMMEDIATELY",
"AREA-NO-ENTER",
"AREA-NO-TAKE",
"AREA-NO-EXPERIENCE",
"AREA-NO-MONEY",
"AREA-NO-TELEPORT-IN",
"AREA-NO-TELEPORT-OUT",
"AREA-MOBFREEZE",
"AREA-IS-CLOSED",
""
};
BYTE resetErrorLog; /* log if a reset is illegal (bad ref etc.) */
BYTE resetReadLog; /* log each world structure as its read */
LWORD resetNum = 0; /* number of world structures in use - just curious really */
void ResetInit(void) {
BYTE buf[256];
resetErrorLog = INILWordRead("crimson2.ini", "resetErrorLog", 0);
resetReadLog = INILWordRead("crimson2.ini", "resetReadLog", 0);
sprintf(buf, "Reading Reset logging defaults\n");
Log(LOG_BOOT, buf);
sprintf(buf, "Reset structure size is %d bytes\n", sizeof(RESETLIST));
Log(LOG_BOOT, buf);
}
void ResetRead(WORD area) {
FILE *resetFile;
BYTE resetFileBuf[256];
BYTE buf[256];
BYTE tmp[256];
RESETLIST *resetList = NULL;
LWORD resetI = -1;
LWORD i;
STR *sKey;
STR *sDesc;
sprintf(resetFileBuf, "area/%s.rst", areaList[area].aFileName->sText);
resetFile = fopen(resetFileBuf, "rb");
if (!resetFile) { /* hardly a fatal error */
sprintf(buf, "Unable to read %s\n", resetFileBuf);
Log(LOG_BOOT, buf);
return;
}
if (areaList[area].aOffset) {
sprintf(buf, "Relocating Area/Resets [%s]\n", areaList[area].aFileName->sText);
Log(LOG_BOOT, buf);
}
/* Read Properties */
while (!feof(resetFile)) {
fgets(buf, sizeof(buf), resetFile);
/* in original diku this was the zone #, but now just indicates last property */
if (buf[0] == '#')
break;
else if (buf[0] == 'P') { /* property */
sKey = FileStrRead(resetFile);
sDesc= FileStrRead(resetFile);
areaList[area].aResetThing.tProperty =
PropertyCreate(areaList[area].aResetThing.tProperty, sKey, sDesc);
}
}
FILESTRREAD(resetFile, areaList[area].aDesc);
fscanf(resetFile, " %*d %hd ", &areaList[area].aResetDelay);
/* althought there is no performance reason for staggereing the resets
* since the code only will execute 1 reset per second, its annoying if
* you're watching the area channel to have them all go off 1/second
*/
areaList[area].aResetLast = -1*Number(0,areaList[area].aResetDelay*60-1);
areaList[area].aResetFlag = FileFlagRead(resetFile, rFlagList);
fscanf(resetFile, "\n"); /* make sure we read the return */
/* okay we opened it up so read it.... */
while (!feof(resetFile)) {
/* read in a line */
fgets(tmp, 255, resetFile);
if (resetReadLog)
Log(LOG_BOOT, tmp);
if (tmp[0] == '$' || tmp[0] == 'S' || feof(resetFile))
break; /* Dikumud file format EOF character */
tmp[255] = '\0';
if (resetReadLog) {
sprintf(buf, "%s.rst:", areaList[area].aFileName->sText);
Log(LOG_BOOT, buf);
LogPrintf(LOG_BOOT, tmp);
}
/* kill off trailing \n's and \r's */
for(i=strlen(tmp)-1; i>=0&&(tmp[i]=='\n'||tmp[i]=='\r'); i++)
tmp[i]='\0';
/* alloc some space for this */
resetI++;
REALLOC("ResetRead(reset.c): resetList reallocation\n", resetList, RESETLIST, resetI+1, areaList[area].aResetByte);
memset( (void*)&resetList[resetI], 0, sizeof(RESETLIST)); /* init to zeros */
/* Make reset commands case insensitive */
tmp[0] = toupper(tmp[0]);
switch (tmp[0]) {
case '*': /* comment */
resetList[resetI].rCmd = '*';
resetList[resetI].rArg1.rStr = STRCREATE(tmp);
break;
case 'O':
resetList[resetI].rArg4.rNum = 1; /* default room max */
sscanf(tmp, "%c %hd %ld %ld %ld %ld",
&resetList[resetI].rCmd,
&resetList[resetI].rIf,
&resetList[resetI].rArg1.rNum,
&resetList[resetI].rArg2.rNum,
&resetList[resetI].rArg3.rNum,
&resetList[resetI].rArg4.rNum);
break;
case 'M':
resetList[resetI].rArg4.rNum = 99; /* default room max */
case 'G':
case 'P':
case 'E':
case 'D':
case 'R':
sscanf(tmp, "%c %hd %ld %ld %ld %ld",
&resetList[resetI].rCmd,
&resetList[resetI].rIf,
&resetList[resetI].rArg1.rNum,
&resetList[resetI].rArg2.rNum,
&resetList[resetI].rArg3.rNum,
&resetList[resetI].rArg4.rNum);
break;
default:
sprintf(buf,
"ResetRead(reset.c) %s, line#%ld theres a problem with:\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
Log(LOG_BOOT, tmp);
resetList[resetI].rCmd = '#';
break;
} /* switch */
/* Relocate Virtuals as necessary */
if (areaList[area].aOffset) {
switch(resetList[resetI].rCmd) {
case 'M':
case 'O':
case 'P':
resetList[resetI].rArg3.rNum = AreaMove(area, resetList[resetI].rArg3.rNum);
/* Fall thru */
case 'G':
case 'E':
case 'D':
case 'R':
resetList[resetI].rArg1.rNum = AreaMove(area, resetList[resetI].rArg1.rNum);
break;
}
}
} /* while */
/* all done close up shop */
fclose(resetFile);
areaList[area].aResetList = resetList;
areaList[area].aResetNum = resetI;
resetNum += resetI;
}
void ResetReIndex(void) {
BYTE buf[256];
RESETLIST *resetList;
LWORD area;
LWORD resetI;
Log(LOG_BOOT, "Re-Indexing Reset Entries\n");
for (area = 0; area<areaListMax; area++) {
resetList = areaList[area].aResetList;
for (resetI = 0; resetI<areaList[area].aResetNum; resetI++) {
switch (resetList[resetI].rCmd) {
case '*': /* comment */
break;
case 'M':
resetList[resetI].rArg1.rMob = MobileOf(resetList[resetI].rArg1.rNum);
if (!resetList[resetI].rArg1.rMob) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s,line#%ld Illegal Mob#\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
break;
}
resetList[resetI].rArg3.rWld = WorldOf(resetList[resetI].rArg3.rNum);
if (!resetList[resetI].rArg3.rWld) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal World #\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
}
break;
case 'O':
resetList[resetI].rArg1.rObj = ObjectOf(resetList[resetI].rArg1.rNum);
if (!resetList[resetI].rArg1.rObj) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal Object #\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
break;
}
resetList[resetI].rArg3.rWld = WorldOf(resetList[resetI].rArg3.rNum);
if (!resetList[resetI].rArg3.rWld) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal World #\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
}
break;
case 'G':
case 'E':
resetList[resetI].rArg1.rObj = ObjectOf(resetList[resetI].rArg1.rNum);
if (!resetList[resetI].rArg1.rObj) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal Object#\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
}
break;
case 'P':
resetList[resetI].rArg1.rObj = ObjectOf(resetList[resetI].rArg1.rNum);
if (!resetList[resetI].rArg1.rObj) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal object #\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
}
break;
case 'D':
case 'R':
resetList[resetI].rArg1.rWld = WorldOf(resetList[resetI].rArg1.rNum);
if (!resetList[resetI].rArg1.rWld) {
if (resetErrorLog) {
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal World #\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
}
resetList[resetI].rCmd = '#';
}
break;
default:
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal command (memory corruption?!?)\n",
areaList[area].aFileName->sText,
resetI + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_BOOT, buf);
resetList[resetI].rCmd = '#';
break;
} /* switch */
}
}
}
void ResetWrite(WORD area) {
FILE *resetFile;
BYTE resetFileBuf[256];
BYTE buf[256];
BYTE line[256];
BYTE comment[256];
OBJTEMPLATE *obj;
RESETLIST *resetList = NULL;
LWORD resetI = -1;
PROPERTY *property;
/* take a backup case we crash halfways through this */
sprintf(buf,
"mv area/rst/%s.rst area/rst/%s.rst.bak",
areaList[area].aFileName->sText,
areaList[area].aFileName->sText);
system(buf);
sprintf(resetFileBuf, "area/%s.rst", areaList[area].aFileName->sText);
resetFile = fopen(resetFileBuf, "wb");
if (!resetFile) { /* hardly a fatal error */
sprintf(buf, "Unable to write %s\n", resetFileBuf);
Log(LOG_ERROR, buf);
PERROR("ResetWrite");
return;
}
/* write out property list here */
/* property's */
for (property=areaList[area].aResetThing.tProperty; property; property=property->pNext) {
fprintf(resetFile, "P\n");
FileStrWrite(resetFile, property->pKey);
if ( CodeIsCompiled(property) ) {
if (!areaWriteBinary) {
/* Decompile the code b4 saving */
CodeDecompProperty(property, NULL);
/* Warn if it didnt decompile */
if (CodeIsCompiled(property)) {
sprintf(buf, "ResetWrite: Property %s failed to decompile for area#%hd!\n",
property->pKey->sText,
area);
Log(LOG_AREA, buf);
} else {
CodeClearFlag(&areaList[area].aResetThing, property);
FileStrWrite(resetFile, property->pDesc);
/* recompile the code */
if (!CodeCompileProperty(property, NULL))
CodeSetFlag(&areaList[area].aResetThing, property);
}
} else {
FileBinaryWrite(resetFile, property->pDesc);
}
} else {
FileStrWrite(resetFile, property->pDesc);
}
}
fprintf(resetFile, "#%d\n", 0); /* in original diku this was the zone #, but now indicates end of prop list */
FileStrWrite(resetFile, areaList[area].aDesc);
fprintf(resetFile, "%ld %hd ", areaList[area].aResetNum, areaList[area].aResetDelay);
FileFlagWrite(resetFile, areaList[area].aResetFlag, rFlagList, '\n');
/* okay we opened it up so write it.... */
resetList = areaList[area].aResetList;
for(resetI=0; resetI<areaList[area].aResetNum; resetI++) {
switch (resetList[resetI].rCmd) {
case '#': /* disabled command */
break;
case '*': /* comment */
fprintf(resetFile, "%s\n",
resetList[resetI].rArg1.rStr->sText);
continue;
case 'O':
sprintf(line, "%c %hd %ld %ld %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
resetList[resetI].rArg1.rObj->oVirtual,
resetList[resetI].rArg2.rNum,
Wld(resetList[resetI].rArg3.rWld)->wVirtual,
resetList[resetI].rArg4.rNum);
sprintf(comment, "%s", resetList[resetI].rArg1.rObj->oSDesc->sText);
break;
case 'M':
sprintf(line, "%c %hd %ld %ld %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
resetList[resetI].rArg1.rMob->mVirtual,
resetList[resetI].rArg2.rNum,
Wld(resetList[resetI].rArg3.rWld)->wVirtual,
resetList[resetI].rArg4.rNum);
sprintf(comment, "%s", resetList[resetI].rArg1.rMob->mSDesc->sText);
break;
case 'G':
case 'E':
sprintf(line, "%c %hd %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
resetList[resetI].rArg1.rObj->oVirtual,
resetList[resetI].rArg2.rNum);
sprintf(comment, "%s", resetList[resetI].rArg1.rObj->oSDesc->sText);
break;
case 'P':
sprintf(line, "%c %hd %ld %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
resetList[resetI].rArg1.rObj->oVirtual,
resetList[resetI].rArg2.rNum,
resetList[resetI].rArg3.rNum);
sprintf(comment, "%s", resetList[resetI].rArg1.rObj->oSDesc->sText);
break;
case 'D':
sprintf(line, "%c %hd %ld %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
Wld(resetList[resetI].rArg1.rWld)->wVirtual,
resetList[resetI].rArg2.rNum,
resetList[resetI].rArg3.rNum);
sprintf(comment, "%s-%s", resetList[resetI].rArg1.rWld->tSDesc->sText, dirList[resetList[resetI].rArg2.rNum]);
break;
case 'R':
sprintf(line, "%c %hd %ld %ld",
resetList[resetI].rCmd,
resetList[resetI].rIf,
Wld(resetList[resetI].rArg1.rWld)->wVirtual,
resetList[resetI].rArg2.rNum);
obj = ObjectOf(resetList[resetI].rArg2.rNum);
if (obj)
sprintf(comment, "%s", obj->oSDesc->sText);
else
sprintf(comment, "%s", "<Unknown object>");
break;
default:
Log(LOG_ERROR, "ResetWrite(reset.c):Illegal reset command type\n");
break;
} /* switch */
if (resetList[resetI].rIf)
fprintf(resetFile, "%-30s %s\n", line, comment);
else
fprintf(resetFile, "%-30s %s\n", line, comment);
} /* for */
/* all done close up shop */
fprintf(resetFile, "*\nS\n*\n$\n");
fclose(resetFile);
}
void ResetArea(LWORD area) {
if (!CodeParseReset(&areaList[area].aResetThing, &areaList[area].aResetThing))
ResetAreaPrimitive(area);
}
void ResetAreaPrimitive(LWORD area) {
THING *mobile;
THING *object;
LWORD i;
LWORD last;
RESETLIST *resetList;
BYTE buf[256];
mobile=object=NULL;
last = TRUE;
for(i=0; i<areaList[area].aResetNum; i++) {
resetList = areaList[area].aResetList;
switch (resetList[i].rCmd) {
case 'M':
if ((resetList[i].rArg1.rMob->mOnline < resetList[i].rArg2.rNum)
&&(!resetList[i].rIf || last)
&&((resetList[i].rArg4.rNum<=0) || (MobilePresent(resetList[i].rArg1.rMob, resetList[i].rArg3.rWld) < resetList[i].rArg4.rNum))
) {
mobile = MobileCreate(resetList[i].rArg1.rMob, resetList[i].rArg3.rWld);
last = TRUE;
} else
last = FALSE;
break;
case 'O':
if (!ObjectMaxReached(resetList[i].rArg1.rObj, resetList[i].rArg2.rNum)
&&(!resetList[i].rIf || last)
&&((resetList[i].rArg4.rNum<=0) || (ObjectPresent(resetList[i].rArg1.rObj, resetList[i].rArg3.rWld, OP_ALL) < resetList[i].rArg4.rNum))
) {
object = ObjectCreate(resetList[i].rArg1.rObj, resetList[i].rArg3.rWld);
last = TRUE;
} else
last = FALSE;
break;
case 'G':
if ((mobile)
&&(!resetList[i].rIf || last)
&&!ObjectMaxReached(resetList[i].rArg1.rObj, resetList[i].rArg2.rNum)
) {
ObjectCreate(resetList[i].rArg1.rObj, mobile);
last = TRUE;
} else
last = FALSE;
break;
case 'P':
if ((object)
&&(!resetList[i].rIf || last)
&&!ObjectMaxReached(resetList[i].rArg1.rObj, resetList[i].rArg2.rNum)
) {
ObjectCreate(resetList[i].rArg1.rObj, object);
last = TRUE;
} else
last = FALSE;
break;
case 'E':
if ((mobile)
&&(!resetList[i].rIf || last)
&&!ObjectMaxReached(resetList[i].rArg1.rObj, resetList[i].rArg2.rNum)
) {
object = ObjectCreate(resetList[i].rArg1.rObj, mobile);
InvEquip(mobile, object, NULL);
last = TRUE;
} else
last = FALSE;
break;
case 'D':
if ((resetList[i].rArg1.rWld)
&&(!resetList[i].rIf || last)
) {
EXIT *exit;
exit = ExitDir(Wld(resetList[i].rArg1.rWld)->wExit, resetList[i].rArg2.rNum);
if (exit) {
exit->eFlag = 0;
BITSET(exit->eFlag, resetList[i].rArg3.rNum);
exit = ExitReverse(resetList[i].rArg1.rWld, exit);
if (exit) {
exit->eFlag = 0;
BITSET(exit->eFlag, resetList[i].rArg3.rNum);
}
}
}
break;
case 'R':
if ((resetList[i].rArg1.rWld)
&&(!resetList[i].rIf || last)
) {
THING *thing;
thing = ThingFind(NULL, resetList[i].rArg2.rNum, resetList[i].rArg1.rWld, TF_OBJ, NULL);
while (thing) {
THINGFREE(thing);
thing = ThingFind(NULL, resetList[i].rArg2.rNum, resetList[i].rArg1.rWld, TF_OBJ, NULL);
}
}
break;
case '#': /* disabled command */
case '*': /* comment */
break;
default:
/* do nothing */
sprintf(buf,
"ResetReIndex(reset.c) %s, line#%ld Illegal command (memory corruption?!?)\n",
areaList[area].aFileName->sText,
i + StrNumLine(areaList[area].aDesc) + 2);
Log(LOG_ERROR, buf);
resetList[i].rCmd = '#';
break;
}
}
CodeParseAfterReset(&areaList[area].aResetThing, &areaList[area].aResetThing);
for (i=0; i<areaList[area].aWldIndex.iNum; i++) {
CodeParseAfterReset(&areaList[area].aResetThing, areaList[area].aWldIndex.iThing[i]);
}
}
void ResetAll(void) {
LWORD i;
Log(LOG_BOOT, "Resetting all Areas\n");
for(i=0; i<areaListMax; i++)
ResetArea(i);
}
/* Call frequently - will stagger resets so they dont all happen at once */
void ResetIdle(void) {
LWORD area;
ULWORD minuteSinceReset;
BYTE buf[256];
BYTE word[256];
for (area=0; area<areaListMax; area++) {
if (BITANY(areaList[area].aResetFlag, RF_RESET_WHEN_EMPTY|RF_RESET_IMMEDIATELY)
&& (areaList[area].aResetDelay>0)
&& (BIT(areaList[area].aResetFlag, RF_RESET_IMMEDIATELY) || (!AreaHasPlayers(area)))) {
minuteSinceReset = (time(0)-areaList[area].aResetLast-startTime)/60;
if (minuteSinceReset > areaList[area].aResetDelay) {
ResetArea(area);
areaList[area].aResetLast=time(0) - startTime;
sprintf(buf, "Resetting [%s]\n", StrFirstWord(areaList[area].aFileName->sText,word));
Log(LOG_AREA, buf);
return;
}
}
}
}