コード例 #1
0
ファイル: BAM.CPP プロジェクト: mhjlam1/bam
// Return random between 0 and range-1
int
ARandomDebug(int range, int line, char *file)
{
	#ifndef NDEBUG
	BamDebug.Out("#%d@%d ARand(%d)l%d %s\n",
		bGlobal.randGenCalls, ATicks(), range,	line, file);

	bGlobal.randGenCalls++;
	return bGlobal.randGen.GetNumber(range);
	#else
	range = range;
	line = line;
	file = file;
	APanic("ARandomDebug() called w/ #NDEBUG!\n");
	return(0);
	#endif
}
コード例 #2
0
ファイル: BAM.CPP プロジェクト: mhjlam1/bam
int
main(int argc, char** argv)
{
   char     mess[100], *string1;
   uint16  	saverResult;
	int		loop1, loop2, framesRun, framesPerSec = 0;
	bool		fWaitToSend;
   BAM_WorldEnderPopup     *pWEPop;
	FILE	*pFile;

//	Nothing		*pNothing1 = new Nothing;
//	NothingMore	*pNothingMore1 = new NothingMore;
//	Dummy			*pDummy1 = new Dummy(0x1234);

//	pNothing1->SetValue(0x1234);
//	pNothingMore1->SetValue(0x5678);
//	pDummy1->SetValue(0x90ab);

// Unit	*pUnit = new Unit(TRUE);
//	Unit	unit1(TRUE);

/*	Jay - here's the code for the test case you said you wanted to try next:

	pFile = fopen("unit1.dat", "wb");
	fwrite(&unit1, 1, sizeof(unit1), pFile);
	fclose(pFile);
*/
	
/*
	pFile = fopen("unit1.dat", "rb");
	fread(&unit1, 1, sizeof(unit1), pFile);
	fclose(pFile);
*/

	time_t	startTime, timeDif, lastTime;
	time(&startTime);
	lastTime = startTime;

	// disable critical error handler
	_harderr(critical_error_handler);

	AInitializePlatform();

	#ifndef NDEBUG
	printf("\n\n&ReportFreeMem==0x%08X\n", (int)ReportFreeMem);
	#endif

#ifdef OS_DOS
	printf("\nBLOOD & MAGIC Copyright (C) 1996 by Tachyon Studios Inc..\n");
	printf("Developed by Tachyon Studios for Interplay Productions.\n");
   printf("BLOOD & MAGIC, FORGOTTEN REALMS and the TSR LOGO are Trademarks\n");
	printf("owned by TSR, Inc and are used under license.\n");
	printf("\nCompiled %s %s\n", __DATE__, __TIME__);
	printf("%dk free after platform initialization\n", AAvailMem() / 1024);

	if(AAvailMem() < MIN_MEM_REQ)
	{
		printf("\nWARNING: Free XMS memory is low (<%dk)!\nBAM may not run reliably\n",
			MIN_MEM_REQ / 1000);
		sleep(4);
	}

#endif

	// short-circuit any command line args
	//	argc = 1;

	if(argc > 1 && (!memcmp(argv[1], "?", 2) || !memcmp(argv[1], "-HELP", 6)))
	{
//		printf("Format: BAM [mapNum] [-NOINTRO] [-NOFOG] [-SIDEx] [-NOWIN] [-NET] [-SHOWOFF]\n");
		return(0);
	}

	// if the seed is the same, the sequence will repeat. cool!
//	ASeedRandom(41);			// for repeatability of bugs
//	ASeedRandom2(41);			// for repeatability of bugs
	ASeedRandom(startTime);	// for genuine randomness
	ASeedRandom2(startTime);	// for genuine randomness

	// If you ever don't want to see unfreed memory, like for demos,
	// then uncomment this.
//	extern bool	fPrintUnfreedPtrs;
//	fPrintUnfreedPtrs = FALSE;

	TRACK_MEM("Mono");			pMono = new Mono;
	// if we really must allocate this on the fly instead of
	// having it global, then we must tell the memmgr not to save it.
	ASetSaveStatus(AGetGrip(pMono), FALSE);
	// also, we don't want this to be purged on a restore.
	ASetPurgeStatus(AGetGrip(pMono), FALSE);

	pMono->SetWindow(0, 7, 79, 12);
	pMono->Clear();
	pMono->Out("Monochrome output initialized\n");

	#ifndef NDBEUG
	pMono->Out("&ReportFreeMem==0x%08X\n", (int)ReportFreeMem);
	sleep(4);
	#endif

	pMono->Out("EventMgr initializing\n");
	TRACK_MEM("EventMgr");		new EventMgr;
	// we are going to update ticks when we want to.
	AAutoUpdateTicks(FALSE);

	pMono->Out("ContextMgr initializing\n");
	TRACK_MEM("ContextMgr");	new ContextMgr;
	pMono->Out("ResMgr initializing\n");
	TRACK_MEM("ResMgr");			new ResourceMgr(TRUE);

	// NOTE - IT IS HIGHLY RECOMMENDED THAT YOU INIT THE SOUND MANAGER
	// BEFORE THE GRAPH MANAGER.  If you don't, streamed sounds will
	// stop and start during restore because of the busy loop in graph
	// manager that happens when the palette fades up.
	pMono->Out("SoundMgr initializing\n");
	TRACK_MEM("SndMgr");	new SoundMgr;
	pSoundMgr->Init();

	// BUGBUG! If we bind w/ DOS4GW/Pro we will crash here!
	//setup smacker sound stuff
	SmackSoundUseSOS3((u16)pSoundMgr->hDigiDriverHandle,0);

	pMono->Out("GraphMgr initializing\n");
	TRACK_MEM("GraphMgr");		pGraphMgr = new GraphicsMgr(MODEX_320X400);

	pMono->Out("%dk free after platform initialization\n", AAvailMem() / 1024);

//	Debugger debug1, debug2;

//	debug1.OpenWindow(0, 0, 79, 10);
//	debug2.OpenWindow(40, 11, 79, 21);
//	for(loop1 = 0; loop1 < 25; loop1++)
//	{
//		pMono->Out("\n%d", loop1);
//	}
//	sleep(2);

	TRACK_MEM("FontMgr");		pFontMgr = new FontMgr;
	// default font
	pFontMgr->SetRes(9050);
	// default font  color
	pFontMgr->ForeColor(TEXT_DEFAULT);
	pFontMgr->colors[FNT_BACK_COLOR] = CI_SKIP;

	#ifdef OS_MAC
//		#include "menubar.hpp"
//		DS9MenuBar*	pMenuBar;
//		TRACK_MEM("menuBar");	pMenuBar = new DS9MenuBar;

		TRACK_MEM("Mouse");	new Mouse;
	#else

		TRACK_MEM("Mouse");	new MouseInt;
	#endif

	if(pMouse->hideCount == 999)
	{
		// init error
		ShutDownSoundMgr();
		APrintUnfreedPtrs(FALSE);
		exit(1);
	}
	pMouse->Init(0,0,SCREEN_WIDTH-1,SCREEN_HEIGHT-1);
	pMouse->SetRes(RES_ANIM, POINTER_RES, 1);
	pMouse->Hide(); //let bamroom show it at room change time.

	// this is our global data saver
	AtSave(GlobalSave);

	// create the application instance
	TRACK_MEM("BAM App");
	pBam = new BAM_Application;
	bGlobal.gBam = pBam->gSelf;
	pBam->msgMask = E_MOUSE_DOWN | E_MOUSE_UP | E_KEY_DOWN;

	// command-line options parsed here
	memcpy(pBam->scenarioName, "9110", 5);	// default scenario
	pBam->fNoIntro = FALSE;
	pBam->fDefaultScenario = FALSE;
	pBam->fUseFog = TRUE;
	pBam->fUseWinLose = TRUE;
	pBam->playerSide = SIDE1;
	pBam->fMapEdit = FALSE;
	pBam->playerTypes[SIDE0] = PLAYER_NONE;
	pBam->playerTypes[SIDE1] = PLAYER_LOCAL;
	pBam->playerTypes[SIDE2] = PLAYER_NONE;
	pBam->playerTypes[SIDE3] = PLAYER_NONE;
	pBam->playerTypes[SIDE4] = PLAYER_NONE;

	{
		FILE	*pPentiumFile;

		pPentiumFile = fopen("PENTIUM.VAN", "rb");
		if(pPentiumFile)
		{
			fclose(pPentiumFile);
			fPentium = TRUE;
		}
	}

	for(loop1 = 1; loop1 < argc; loop1++)
	{
		strcpy(mess, argv[loop1]);
		string1 = mess;
		do
		{
			*string1 = (char) toupper(*string1);
			string1++;
		}	while(*string1);

		if(mess[0] == '-')
		{
//			#ifdef ENABLE_EDITOR
//			if(!memcmp(mess, "-EDIT", 5))				// turn on edit mode
//				pBam->fMapEdit = TRUE;
//			#endif
			
			if(!memcmp(mess, "-NOINTRO", 9))		// turn off opening cinematic
				pBam->fNoIntro = TRUE;
			else if(!memcmp(mess, "-NOWIN", 6))	// turn on win/lose conditions
				pBam->fUseWinLose = FALSE;
			else if(!memcmp(mess, "-NOFOG", 6))		// turn off fog
				pBam->fUseFog = FALSE;
			else if(!memcmp(mess, "-NET", 4))		// network play
			{
				pBam->fNetworkTest = TRUE;
				bGlobal.storyLine = NETGAME;
				ASeedRandom(42);	// need sync'ed RNGs
			}
			else if(!memcmp(mess, "-PENTIUM", 8))
			{
				fPentium = TRUE;
			}
			else if(!memcmp(mess, "-MUSIC", 6))
			{
				bGlobal.altMusicNum = atoi(mess + 6);
			}
			else if(!memcmp(mess, "-AIUNITS", 8))
			{
				bGlobal.aiUnitMultiplier = atoi(mess + 8);
			}
			else if(!memcmp(mess, "-AI", 3))
			{
				bGlobal.aiOveride = atoi(mess + 3);
			}
			else if(!memcmp(mess, "-FRENCH", 7))
			{
				SetLanguage(LANG_FRENCH);
			}
			else if(!memcmp(mess, "-GERMAN", 7))
			{
				SetLanguage(LANG_GERMAN);
			}
			else if(!memcmp(mess, "-ENGLISH", 8))
			{
				SetLanguage(LANG_ENGLISH);
			}
			else if(!memcmp(mess, "-SHOWOFF", 8))	// storefront demo mode
			{
				bGlobal.storyLine = SHOW_OFF;
			}
			else if(!memcmp(mess, "-HACKRES", 8))	// allow single resource replacement
			{
				pResMgr->fSearchStuffsFirst = FALSE;
			}
			else if(!memcmp(mess, "-SIDE", 5))	// set player side (1 or 2)
			{
				switch(mess[5])
				{
					case '0':
						pBam->playerSide = SIDE0;
						pBam->playerTypes[SIDE1] = PLAYER_NONE;
						pBam->playerTypes[SIDE2] = PLAYER_NONE;
						break;

					case '1':
						pBam->playerSide = SIDE1;
						pBam->playerTypes[SIDE1] = PLAYER_LOCAL;
						pBam->playerTypes[SIDE2] = PLAYER_NONE;
						break;

					case '2':
						pBam->playerSide = SIDE2;
						pBam->playerTypes[SIDE1] = PLAYER_NONE;
						pBam->playerTypes[SIDE2] = PLAYER_LOCAL;
						break;

					default:
						pMono->Out("Invalid option to command line arg -SIDEx\n");
						break;
				}
			}
		}
		else if(atoi(argv[loop1]))
		{
			memcpy(pBam->scenarioName, argv[loop1], 8);
			pBam->fDefaultScenario = TRUE;
		}
	}

	#ifdef ENABLE_EDITOR
	if(pBam->fMapEdit)				// if editor mode, then turn off fog
		pBam->fUseFog = FALSE;
		pBam->fShowTileNums = TRUE;
	#endif

	// initialize game start, 
	pBam->Activate(TRUE);

	pContextMgr->msgMask = E_MOUSE_DOWN | E_MOUSE_UP | E_KEY_DOWN;

	#ifdef COUNT_FRAMES
		FrameCounter	fps;
	#endif // COUNT_FRAMES

	TRACK_MEM("TCommMgr");		new TCommMgr;

	if(pBam->fNetworkTest)	// if set up network from command line
	{
//		TNetwork			*pComm;
//		TModem			*pComm;
		TComm::ERROR	errVal;
		int	int1;

//		pMono->Out("New TCommMgr\n");
//		TRACK_MEM("TCommMgr");		pCommMgr = new TCommMgr;	//it isn't an object -no gSelf

		TRACK_MEM("TNetwork");		pComm = new TNetwork;
//		pComm = new TModem;
//		((TModem*) pComm)->SetPort(3);
//		((TModem*) pComm)->SetBaud(19200);

		#ifdef OS_MAC
		int	commInitArg = 0;	// ignored by DOS/TIGRE
			// player 1 is caller, player two is listener
			// at least for now, using command line args to specify direction
			if (pBam->playerTypes[SIDE1] == PLAYER_LOCAL)
			{
				commInitArg = TCommMacintosh::kATalkCaller;
			}
			else
			{
				commInitArg = TCommMacintosh::kATalkListener;
			}
			pCommMgr->Init(pComm, commInitArg);
		#else
			pCommMgr->SetUserAbortFn ((pIntFnInt) CycleConnection);
			pCommMgr->Init(pComm);
		#endif

		pMono->Out("Attempting connection...\n");
		int1 = (int)pCommMgr->Connect();
		errVal = (TComm::ERROR)int1;

		if(errVal != TComm::ALL_OK)
		{
			pMono->Out("Main() - error, commMgr->Connect() failed\n");
			sleep(1);
			pBam->fNetworkTest = FALSE;
		}
		else
		{
			pMono->Out("Main() - connection established\n");
			pBam->playerTypes[(pBam->playerSide == SIDE1)? SIDE2: SIDE1] = pCommMgr->GetUserID();
			pMono->Out("User ID==%d\n", pCommMgr->GetUserID());
//			sleep(2);
		}
	}

	pMono->Out("TIGRE engine initialized\n");
	ReportFreeMem();

	// side1 and side2 MUST have a player setting of some kind
	if(pBam->playerTypes[SIDE1] == PLAYER_NONE)
		pBam->playerTypes[SIDE1] = PLAYER_COMPUTER;
	if(pBam->playerTypes[SIDE2] == PLAYER_NONE)
		pBam->playerTypes[SIDE2] = PLAYER_COMPUTER;

	// initial room
	if(pBam->fDefaultScenario)
	{
		SetDefaults(pBam->scenarioName);
		bGlobal.roomMgr.NewRoom(BR_WORLD);			// skip everything, go to game
	}
	else if(bGlobal.storyLine == SHOW_OFF)
	{
		bGlobal.roomMgr.NewRoom(BR_WORLD);			// skip everything, go to game
	}
	else if(pBam->fNoIntro)
		bGlobal.roomMgr.NewRoom(BR_MENU);			// skip intro, go to story selector
	else bGlobal.roomMgr.NewRoom(BR_CINE);		// skip nothing

	bGlobal.roomMgr.CheckRoomChange();

	#ifdef OS_MAC
		#include "menubar.hpp"
		DS9MenuBar *pMenuBar;
		TRACK_MEM("menuBar");	MenuBar.Init();
	#endif
	
	ticks_t	currTicks = ATicks();
	int		snapShot = 0;

	// anti-piracy stuff 
	pFile = fopen("HMICARDS.386", "rb");
	if(!pFile)
		bGlobal.antiPiracyCrashFlag &= 0xFFFFF0FF;
	else
	{
		for(loop1 = 0; loop1 < 5; loop1++)
		{
			char string1[128];
			unsigned long lineCheck1, lineCheck2;

			fread(string1, 32, 1, pFile);								// camoflage string
			fread(string1, 128, 1, pFile);							// encrypted string
			fread(&lineCheck1, sizeof(lineCheck1), 1, pFile);		// line checksum

			// verify checksum
			for(lineCheck2 = 0, loop2 = 0; loop2 < 128; loop2++)
				lineCheck2 = lineCheck2 + string1[loop2];
			lineCheck2 ^= 0xF61E22C9;
			if(lineCheck1 != lineCheck2)
			{
				// failure - set the delayed crash flag
				fclose(pFile);
				bGlobal.antiPiracyCrashFlag &= 0xFFFFF0FF;
			}
		}
		fclose(pFile);
	}

	//=======================================================================
	//MAIN GAME LOOP
	//=======================================================================

	ReportFreeMem();

	enum timerTags {TIMER_CYC_PER_FRAME = 0, TIMER_UPDATE_TICKS, TIMER_CONTEXT_CYCLE,
		TIMER_ANIMATE, TIMER_SYNC_SEND, TIMER_SYNC_RECV, TIMER_MAX};
	DebugTimer	timers[TIMER_MAX];

	while (!pContextMgr->fQuitting)
	{
		#ifndef NDEBUG
		timers[TIMER_CYC_PER_FRAME].Start();
		#endif

		#ifdef DEBUG_BIG_NET
			pMono->Out("main loop()..");
		#endif

		// our own exceptional ptrs
		pBam = ADerefAs(BAM_Application, bGlobal.gBam);
		if (bGlobal.gWorld)
		{
			pWorld = ADerefAs(World, bGlobal.gWorld);
		}
		else
		{
			pWorld = NULL;
		}

		#ifdef DEBUG_BIG_NET
			pMono->Out("roomMgr.cycle()..");
		#endif
		bGlobal.roomMgr.Cycle();

		//fps.Count();

		#ifdef DEBUG_BIG_NET
			pMono->Out("memMgr.cycle()..");
		#endif
		pMemMgr->Cycle();
		
		#ifndef OLD_MOUSE_INTERRUPT_HANDLER
      //
      // MDB - Modified the mouse interrupt handler.
      //
      MouseHandler( FALSE );
		#endif

		#ifndef NDEBUG
		//mem_check();
		#endif

		#ifdef DEBUG_BIG_NET
			pMono->Out("roomMgr.CheckRoomChange()..");
		#endif
		bGlobal.roomMgr.CheckRoomChange();

		#ifndef OLD_MOUSE_INTERRUPT_HANDLER
      //
      // MDB - Modified the mouse interrupt handler.
      //
      MouseHandler( FALSE );
		#endif

		//if snap is on lets handle tick updates ourself	
		if(bGlobal.gSnap && pSnap->snapOn)
		{
			//if snap just toggled on
			if(!snapShot)
			{
				snapShot++;
				currTicks = ATicks();
			}
			else
			{
				currTicks += 2;
			}
			ASetTicks(currTicks);
		}
		else
		{
			snapShot = 0;	//reset

			#ifdef DEBUG_BIG_NET
				pMono->Out("EventMgr->Publish()..");
			#endif

			// add local actions to actionPool2
			pEventMgr->PublishNext();

			#ifndef OLD_MOUSE_INTERRUPT_HANDLER
	      //
   	   // MDB - Modified the mouse interrupt handler.
      	//
	      MouseHandler( FALSE );
			#endif

			if(pWorld)
			{
				// wait for next tick to occur, because otherwise there's no point.
				// note: UpdateTicks() cannot be trusted to change, in case of Pause()

				currTicks = ATicks();
//				#ifndef NDEBUG
//				BamDebug.Out("main() - update tick counter (curr=%d)\n",
//					currTicks);
//				#endif
				#ifndef NDEBUG	
				timers[TIMER_UPDATE_TICKS].Start();
				#endif
				UpdateTicks();

				// did the tick counter fail to increment?
				if(ATicks() == currTicks)
				{
					// loop on clock() delta, then resume.  A tick will have changed by
					// then or else we're in Pause() mode, so who cares.

					clock_t	currClock = clock();
					while(currClock == clock())
					{
						#ifndef OLD_MOUSE_INTERRUPT_HANDLER
				      MouseHandler( FALSE );
						#endif
					};

					// try again - if it fails again, assume Pause() and don't worry
					UpdateTicks();
				}

				// if we skipped over a tick somehow..
				if(ATicks() > currTicks + 1)
				{					
//					#ifndef NDEBUG
//					BamDebug.Out("main() WARNING: ATicks() advanced by %d! - retarding\n",
//						ATicks() - currTicks);
//					#endif

					// dont allow the tick counter to increment by more than 1
					// tick per frame, or we may get out of sync with the
					// remote machine
					ASetTicks(currTicks + 1);
				}
				#ifndef NDEBUG
				timers[TIMER_UPDATE_TICKS].Stop();
				#endif

//				#ifndef NDEBUG
//				BamDebug.Out("ATicks() == %d\n", ATicks());
//				#endif

				// sync1 - send actionPool2 to remote
				#ifndef NDEBUG
				timers[TIMER_SYNC_SEND].Start();
				#endif

				fWaitToSend = FALSE;
				if(pCommMgr && pCommMgr->totalPacketsWaiting)
					fWaitToSend = TRUE;

				if(!pWorld->SyncSend())
				{
					#ifndef OLD_MOUSE_INTERRUPT_HANDLER
			      MouseHandler( FALSE );
					#endif
					//pWorld->AITakeOver();
					if(!pBam->fWorldEnderPopupExists)
					{
						TRACK_MEM("BAM_WorldEnderPopup");       pWEPop = new BAM_WorldEnderPopup;
						pWEPop->Setup();
						pBam->fWorldEnderPopupExists = TRUE;
					}
				}
				#ifndef NDEBUG
				timers[TIMER_SYNC_SEND].Stop();
				#endif

				// process actionPool1
				pWorld->ProcessActions();
				#ifndef OLD_MOUSE_INTERRUPT_HANDLER
		      MouseHandler( FALSE );
				#endif
			}
			else
			{
				#ifndef NDEBUG
				timers[TIMER_UPDATE_TICKS].Start();
				#endif

				UpdateTicks();
				#ifndef NDEBUG
				timers[TIMER_UPDATE_TICKS].Stop();
				#endif
			}

			if(bGlobal.storyLine == NETGAME && !bGlobal.netDisconnect)
			{
				pCommMgr->EnQueueData();
				#ifndef OLD_MOUSE_INTERRUPT_HANDLER
		      MouseHandler( FALSE );
				#endif
			}
		}

		#ifdef DEBUG_BIG_NET
			pMono->Out("contextMgr.cycle()..");
		#endif
		#ifndef NDEBUG
		timers[TIMER_CONTEXT_CYCLE].Start();
		#endif
		pContextMgr->Cycle();
		#ifndef NDEBUG
		timers[TIMER_CONTEXT_CYCLE].Stop();
		#endif
		#ifndef OLD_MOUSE_INTERRUPT_HANDLER
      MouseHandler( FALSE );
		#endif

		if(bGlobal.storyLine == NETGAME && !bGlobal.netDisconnect)
		{
			pCommMgr->EnQueueData();
			#ifndef OLD_MOUSE_INTERRUPT_HANDLER
	      MouseHandler( FALSE );
			#endif
		}

		#ifdef DEBUG_BIG_NET
		pMono->Out("GraphMgr->Animate()..");
		#endif
		#ifndef NDEBUG
		timers[TIMER_ANIMATE].Start();
		#endif
		pGraphMgr->Animate();
		#ifndef NDEBUG
		timers[TIMER_ANIMATE].Stop();
		#endif

		#ifndef OLD_MOUSE_INTERRUPT_HANDLER
      MouseHandler( FALSE );
		#endif

		if(bGlobal.storyLine == NETGAME && !bGlobal.netDisconnect)
		{
			pCommMgr->EnQueueData();
			#ifndef OLD_MOUSE_INTERRUPT_HANDLER
   	   MouseHandler( FALSE );
			#endif
		}

		#ifdef DEBUG_BIG_NET
			pMono->Out("soundMgr.cycle()..");
		#endif
		pSoundMgr->Cycle();

		// current frame now done

		// gather cmds from remote for next frame, then prepare to run it
		if(pWorld)
		{
			pWorld->currFrame++;

			// sync2 - add remote actions to local actions in actionPool2
			#ifndef NDEBUG
			timers[TIMER_SYNC_RECV].Start();
			#endif
			if(!pWorld->SyncReceive())
			{
				#ifndef OLD_MOUSE_INTERRUPT_HANDLER
   		   MouseHandler( FALSE );
				#endif
				//pWorld->AITakeOver();
				if(!pBam->fWorldEnderPopupExists)
				{
					TRACK_MEM("BAM_WorldEnderPopup");       pWEPop = new BAM_WorldEnderPopup;
					pWEPop->Setup();
					pBam->fWorldEnderPopupExists = TRUE;
				}
			}

			// sync3 - swap action pools
			pWorld->SwapActionPools();
			#ifndef NDEBUG
			timers[TIMER_SYNC_RECV].Stop();
			#endif
		}

		if(bGlobal.gSnap && pSnap->snapOn)
		{
			pSnap->SnapScreen();
		}

		if(netRestoreNum || netSaveNum)
		{
			//this means we're in a netgame save or restore
			//we don't run the eventmgr to keep both sides in sync
			//and we set the restore number here, after the context cycle,
			//to insure the otherside has had time to be notified of this
			if(netRestoreNum)
				restoreNum = netRestoreNum;	//our one net save game

			if(netSaveNum)
			{
				saveNum = netSaveNum;
				netSerialNum = ARandom(99999);
				sprintf(saveMessage,"%d",netSerialNum);
			}
		}

      if (saveNum)
      {
         pMemMgr->Dump((uint16) (100 + saveNum), "Save Dump", TRUE);
         saverResult = saveMgr.Save((uint16) saveNum, bGlobal.versionNum,
				bGlobal.versionSubNum, bGlobal.buildID, saveMessage);
         if (saverResult)
         {
            sprintf(mess, "save failed.  error #%d", saverResult);
            APanic(mess);
         }
			netSaveNum = 0;	//reset this after save
         saveNum = 0;    
      }
      if (restoreNum)
      {
         saverResult = saveMgr.Restore((uint16) restoreNum, bGlobal.versionNum,
				bGlobal.versionSubNum, bGlobal.buildID);
         if (saverResult)
         {
//         	sprintf(mess, "restore failed.  error #%d", saverResult);
				BamDebug.Out("Restore failed!  Error %d\n", saverResult);
//         	APanic(mess);
         }
         pMemMgr->Dump((uint16) (200 + restoreNum), "Restore Dump", TRUE);
			netRestoreNum = 0;	//reset this after restore
         restoreNum = 0; 
			bGlobal.roomMgr.Cycle();

			// frantic temporary debugging measure
			pBam = ADerefAs(BAM_Application, bGlobal.gBam);
			if (bGlobal.gWorld)
			{
				pWorld = ADerefAs(World, bGlobal.gWorld);
				pWorld->Save(AFTER_RESTORE);
			}
			else
			{
				pWorld = NULL;
			}
			bGlobal.roomMgr.Cycle();
			pMemMgr->Cycle();
			pSoundMgr->Cycle();

			if(pWorld)
			{
				pBam->fPauseWorld = TRUE;	//force ResumeTicks() to be called in PauseWorld
				pWorld->Pause(FALSE, TRUE);
				pBam->PauseWorld();
			}
			else bGlobal.roomMgr.curRoom->Pause(FALSE, TRUE);
			#ifndef OLD_MOUSE_INTERRUPT_HANDLER
   	   MouseHandler( FALSE );
			#endif
      }

		if(pWorld)
			if(pBam->fPauseWorld != pWorld->fIsPaused)
				pBam->PauseWorld();

		#ifdef DEBUG_BIG_NET
			pMono->Out("main loop() done\n");
		#endif

		#ifndef NDEBUG
		timers[TIMER_CYC_PER_FRAME].Stop();
		#endif

		#ifndef NDEBUG
		pMono->SaveWindow();
		pMono->Goto(24, 1);
		pMono->Out("CpF%5d UTick%5d CmC%5d GmA%5d Tx%4d:%dp@%4db]%c Rx[%4d]",
			timers[TIMER_CYC_PER_FRAME].duration, timers[TIMER_UPDATE_TICKS].duration,
			timers[TIMER_CONTEXT_CYCLE].duration, timers[TIMER_ANIMATE].duration,
			timers[TIMER_SYNC_SEND].duration, pCommMgr->pComm->totalPacketsSent,
			pCommMgr->pComm->totalBytesSent,
			fWaitToSend? 'W': 'w', timers[TIMER_SYNC_RECV].duration);
		pMono->Goto(25, 1);
		time(&timeDif);
		if(timeDif != lastTime)
		{
			lastTime = timeDif;
			framesPerSec = AMin(framesRun, 99);
			framesRun = 0;
		}
		else framesRun++;
		timeDif -= startTime;

		pMono->Out("%05dkt %05dkl %02d:%02d:%02d %2dFPS", pMemMgr->AvailMem() / 1024,
			pMemMgr->LargestAlloc() / 1024, timeDif / 3600, (timeDif / 60) % 60,
			timeDif % 60, framesPerSec);

		if(pWorld)
		{
			pMono->Goto(25, 32);
			pMono->Out("r%d g%d 0x%08x", pWorld->tileResNum,
				ALoad(RES_TILELIB, pWorld->tileResNum),
				AGetResData(ALoad(RES_TILELIB, pWorld->tileResNum)));
		}

		pCommMgr->pComm->totalBytesSent = 0;
		pCommMgr->pComm->totalPacketsSent = 0;
		pMono->RestoreWindow();
		#endif

		#ifndef OLD_MOUSE_INTERRUPT_HANDLER
      MouseHandler( FALSE );
		#endif
	}

	//===================================
	LoadExitQuote();

	//===================================

	// get rid of our current room
	bGlobal.roomMgr.DeleteCurRoom();

	// shut down communications
	if(pCommMgr)
	{
		pCommMgr->Disconnect();
		pCommMgr->DiscardData();
		delete pCommMgr; //its destructor will delete pComm
		pCommMgr = NULL;
	}

	if(bGlobal.gSnap)
		ADelete(bGlobal.gSnap);
	ADelete(bGlobal.gBam);

	// all actors should be removed from graphmgr, do one more
	// animate to clear lists.
	pGraphMgr->Animate();

	ADelete(pContextMgr->gSelf);

//	TSound	exitSound;
//	exitSound.Play(667);
//	while(exitSound.IsPlaying())
//		pSoundMgr->Cycle();

	// storm the office!  kill all managers!
	ADelete(pGraphMgr->gSelf);
	ADelete(pFontMgr->gSelf);
	ADelete(pSoundMgr->gSelf);
	ADelete(pResMgr->gSelf);
	ADelete(pEventMgr->gSelf);
	ADelete(pMouse->gSelf);

	delete pMono;

//	DESTROY_MGR_CAREFUL(GraphicsMgr, pGraphMgr);
//	DESTROY_MGR_CAREFUL(Mono, pMono);
//	DESTROY_MGR_CAREFUL(FontMgr, pFontMgr);
//	DESTROY_MGR_CAREFUL(SoundMgr, pSoundMgr);
//	DESTROY_MGR(pResMgr);
//	DESTROY_MGR(pContextMgr);
//	DESTROY_MGR_CAREFUL(EventMgr, pEventMgr);

	#ifdef INTERACTIVE_DEMO
	DPMI dpmi;
	char *pScreen = (char *)dpmi.RealToProtected(0xB8000000);

	// kludge - remove "Installer" from data
	for(loop1 = 0; loop1 < 9; loop1++)
		MSG_DEMO_ENGL[86 + loop1 * 2] = ' ';

	memcpy(pScreen, (char *)MSG_DEMO_ENGL, 4000);
	while(!kbhit());
	getch();
	#endif
	system("mode co80");	// yeah, mega-cheesy I know.  It's a bug patch. -Van

	printf("\n%s\n",exitMessage);
	printf("%s\n",exitAuthor);

	return 0;						
}