//////////
//
//	File:		VRScript.c
//
//	Contains:	Functions for script file processing.
//
//	Written by:	Tim Monroe
//
//	Copyright:	© 1997-1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <65>	 	04/05/00		rtm		removed support for SIOUX (which apparently isn't Carbon-compliant)
//	   <64>	 	03/17/00		rtm		made changes to get things running under CarbonLib
//	   <63>	 	08/11/99		rtm		fixed sprite ID determination in VRScript_CheckForWiredActionCommands
//	   <62>	 	08/05/99		rtm		added VRScript_EnlistWiredActionCommand; changed return value determination
//										in VRScript_CheckForClickSpriteCommands
//	   <61>	 	07/01/99		rtm		added VRScript_InstallAllQTVRCallbackProcs, for convenience; added call to
//										VRScript_InstallAllQTVRCallbackProcs to VRScript_SetCurrentMovie; added
//										QTVRWrapAndConstrain calls to kSetPanTiltZoom, which seems to fix things
//	   <60>	 	06/24/99		rtm		major overhaul of geometry handling, using newly-revised code from
//										VRMovies project; removied VRScript_RemoveBackBufferImagingProc
//	   <59>	 	06/23/99		rtm		added support for SIOUX (conditionalized using USE_SIOUX_FOR_DEBUG flag)
//	   <58>	 	05/27/99		rtm		moved VRScript_MakeFSSpecForPathName into new file, FileUtilities.c; now
//										relative pathnames work on Windows too
//	   <57>	 	05/26/99		rtm		added VRScript_MakeFSSpecForPathName
//	   <56>	 	05/20/99		rtm		added SetVerboseState command (_OpenDebugWindow and _PrintToDebugWindow)
//	   <55>	 	05/17/99		rtm		tweaked VRScript_InstallBackBufferImagingProc and revisited modifications
//										in <54>: we again support real-time rotation of back buffer movies if the
//										back buffer is not oriented horizontally
//	   <54>	 	04/05/99		rtm		added kQTVRBackBufferHorizontal to flags in _InstallBackBufferImagingProc;
//										from now on, we assume that our back buffer is horizontal and we no longer
//										support real-time rotation of back buffer movies
//	   <53>	 	03/04/99		rtm		added VRScript_MoviePrePrerollCompleteProc, based on code from Tom Dowdy
//	   <52>	 	01/04/99		rtm		changed AtClickHS command into AtClickHSID and AtClickHSType commands
//	   <51>	 	12/17/98		rtm		removed VRScript_SetHotSpotState (moved code into _ProcessScriptCommandLine);
//										added VRScript_SetCurrentDirectory and SetCurrentDirectory command
//	   <50>	 	12/16/98		rtm		tweaked VRScript_DeleteListOfType
//	   <49>	 	12/07/98		rtm		fixed VRScript_SetCurrentMovie to work with URLs to wired movies
//	   <48>	 	12/02/98		rtm		added VRScript_SetCurrentMovie; added URLUtilities.c to project
//	   <47>	 	11/28/98		rtm		reworked parser in VRScript_ProcessScriptCommandLine to use a hash table,
//										using routines in VRHash.c; the basic idea was inspired by Jon Summers' code
//										in his mTropolis VRScript MOD, but my implementation is different from his 
//	   <46>	 	11/24/98		rtm		added SetTrackState, SetTrackLayer, SetMovieTime, SetMovieRate, 
//										and SetMovieTimeScale commands
//	   <45>	 	11/19/98		rtm		added VRScript_OpenCommandLineScriptFile, to support scripts dropped
//										onto the Windows version
//	   <44>	 	11/18/98		rtm		removed some variable initializations to work around CW compiler bugs
//	   <43>	 	09/04/98		rtm		added SetTrackVolume
//	   <42>	 	06/19/98		rtm		added support for clicking on sprites in sprite tracks, using code from
//										QTSprites sample; added theTolerance parameter to VRScript_FloatsAreEqual
//	   <41>	 	04/09/98		rtm		added check for empty command lines in VRScript_ProcessScriptCommandLine
//	   <40>	 	04/06/98		rtm		added Destroy3DObject command, based loosely on code from Bill Meikle
//	   <39>	 	03/12/98		rtm		added AtClickCustomButton to handle user clicks on the custom controller button
//	   <38>	 	03/09/98		rtm		added VRScript_FloatsAreEqual
//	   <37>	 	02/25/98		rtm		revised VRScript_EnteringNodeProc to call MoviesTask instead of QTVRUpdate
//	   <36>	 	12/12/97		rtm		fixed VRScript_FindAndOpenQTVRMovieFile (was opening the movie file
//										instead of allowing DoCreateMovieWindow to do so)
//	   <35>	 	10/29/97		rtm		finished adding support for QuickTime video effects
//	   <34>	 	10/27/97		rtm		began adding support for QuickTime video effects
//	   <33>	 	10/06/97		rtm		fixed bug in replace cursor commands (wouldn't restore original
//										cursors if a click on the hot spot caused the node to be exited)
//	   <32>	 	09/10/97		rtm		made changes necessary to compile for Windows execution
//	   <31>	 	07/21/97		rtm		added VRScript_DumpUnexpiredCommands
//	   <30>	 	07/17/97		rtm		revised all list-walking code to avoid dangling pointers
//	   <29>	 	07/15/97		rtm		reworked VRScript_MouseOverHotSpotProc to use a single while loop
//	   <28>	 	07/14/97		rtm		fixed SetHotSpotTypeCursors to use OSTypes
//	   <27>	 	07/12/97		rtm		fixed a bug in VRScript_FindAndOpenQTVRMovieFile
//	   <26>	 	07/11/97		rtm		added support for encoded commands
//	   <25>	 	07/08/97		rtm		added SetHotSpotTypeCursors command
//	   <24>	 	07/07/97		rtm		added ReplaceResource and SetHotSpotIDCursors commands
//	   <23>	 	06/19/97		rtm		added PlaySceneSound and PlaySceneQTMidi, for persistent sounds
//	   <22>	 	06/13/97		rtm		added code to make sure QD3D present before executing 3D commands
//	   <21>	 	06/12/97		rtm		added VRScript_FindAndOpenQTVRMovieFile
//	   <20>	 	06/05/97		rtm		added VRScript_SetControllerButtonState;
//										changed semantics of SetVariable and If commands
//	   <19>	 	06/04/97		rtm		added Mode parameter to SetPanTiltZoom;
//	   <18>	 	05/30/97		rtm		added script-defined variables (SetVariable and If commands)
//	   <17>	 	05/22/97		rtm		added ability to cancel node exit to VRScript_LeavingNodeProc
//	   <16>	 	05/01/97		rtm		code clean-up: put list heads into an array
//	   <15>	 	04/28/97		rtm		added support for QuickTime MIDI files
//	   <14>	 	04/17/97		rtm		added VRScript_DelistEntry; added fMaxExecutions to some commands
//	   <13>	 	04/10/97		rtm		added 3D object and 3D sound "Set" calls
//	   <12>	 	04/07/97		rtm		added SetResizeState
//	   <11>	 	04/02/97		rtm		added support for 3DMF files
//	   <10>	 	04/01/97		rtm		added VRScript_CheckForAngleCommands
//	   <9>	 	03/31/97		rtm		added node-entry and node-exit command support;
//										added support for additional imaging and interaction properties
//	   <8>	 	03/21/97		rtm		added VRScript_SetHotSpotState
//	   <7>	 	03/17/97		rtm		localized embedded QuickTime movie sounds
//	   <6>	 	03/13/97		rtm		reworked hot spot sounds; fixed code for 680x0 compilation;
//										added support for angle commands
//	   <5>	 	03/12/97		rtm		added support for mouse-over hot spot commands, external resource files,
//										hot spot click commands, and QT movies;
//	   <4>	 	03/11/97		rtm		added support for timed commands (cool!)
//	   <3>	 	03/10/97		rtm		added support for controller bar, localized sounds, and overlay pictures
//	   <2>	 	03/07/97		rtm		got hot spot sounds working (except for any options)
//	   <1>	 	03/06/97		rtm		first file
//
//
//	This file contain functions that support an external text script file for driving QuickTime VR movies.
//  The script file contains one command per line; a command line is a command word followed by one or more
//	command parameters. The number and meaning of the parameters depends on the command word. See the file 
//	"Script Syntax" for a complete description of the scripting language.
//
//////////


//////////
//	   
// header files
//	   
//////////

#include "VRSound.h"
#include "VRPicture.h"
#include "VRMovies.h"
#include "VREffects.h"
#include "VRHash.h"

#if QD3D_AVAIL
#include "VR3DObjects.h"
#endif	

#include "VRScript.h"


//////////
//	   
// global variables
//	   
//////////

Boolean					gReadingScriptFile;						// are we reading a script file?
Boolean					gIsVerbose = false;						// are we in verbose mode?

WindowPtr				gDebugWindow = NULL;					// the verbose mode debug window

char					gScriptFileName[kMaxFileNameLength];	// the name of the script file
MovieController			gPreviousMC = NULL;						// a controller that's been replaced by a call to ReplaceMainMovie				
Movie					gPreviousMovie = NULL;					// a movie that's been replaced by a call to ReplaceMainMovie		

extern Boolean			gHasSoundSprockets;						// is SoundSprockets available?
extern Boolean			gHasSoundManager30;						// is Sound Manager version 3.0 (or greater) available?
extern Boolean			gHasQuickDraw3D;						// is QuickDraw 3D available?
extern Boolean			gHasQuickDraw3D15;						// is QuickDraw 3D version 1.5 (or greater) available?
extern Boolean			gHasQTVideoEffects;						// are the QuickTime video effects available?

extern unsigned long	gAbsoluteElapsedTime;
extern unsigned long	gNodeStartingTime;
extern unsigned long	gNodeElapsedTime;

extern VRScriptPrefsHdl	gPreferences;							// a handle to the global preferences record

extern Rect				gMCResizeBounds;						// max size for any window


//////////
//
// VRScript_OpenScriptFile
// Open the external script file and process the commands (one command per line).
//
//////////

void VRScript_OpenScriptFile (WindowObject theWindowObject, char *theFileName)
{
	FILE		*myFile;
	char		myString[kMaxCmdLineLength];
	
	if (theFileName == NULL)
		return;
		
	myFile = fopen(theFileName, "r");
	if (myFile == NULL)
		return;

	gReadingScriptFile = true;

	while (fgets(myString, sizeof(myString), myFile) != NULL)
		VRScript_ProcessScriptCommandLine(theWindowObject, myString);

	gReadingScriptFile = false;

	fclose(myFile);
}


#if TARGET_OS_WIN32
//////////
//
// VRScript_OpenCommandLineScriptFile
// Parse the command line when the application first starts up and
// open as script files any files specified on the command line.
//
// Based on the routine DoOpenCommandLineMovies in WinFramework.c.
//
//////////

void VRScript_OpenCommandLineScriptFile (LPSTR theCmdLine)
{
#pragma unused(theCmdLine)
	LPSTR				myCmdLine;
	FSSpec				myFSSpec;
	WIN32_FIND_DATA		myFile;
	HANDLE				myFindFile;
	char 				myFilePath[MAX_PATH];
	
	// get the command line for the current process
	myCmdLine = GetCommandLine();

	// parse the command line
	if (*myCmdLine) {
		LPSTR			myTempLine;
		
		// the string preceding any white space is the name of the module (that is, the application)
		myTempLine = strchr(myCmdLine, ' ');
		if (myTempLine) {
			myCmdLine = myTempLine;  				// skip the name of the application
			while (*myCmdLine == ' ')
				myCmdLine++;            			// skip spaces to end of string or to first command

			while (*myCmdLine != '\0') {
				char 	myFileName[MAX_PATH];
				char 	myTempName[MAX_PATH];
				char 	myBuffName[MAX_PATH];
				int 	myIndex;
				
				// read thru the remaining string to find file names
				for (myIndex = 0; *myCmdLine != '\0'; myIndex++, myCmdLine++) {
					// if we encounter a space character, it might be a filename delimiter or a space in the filename;
					// we'll try to open the filename we have so far to see whether it's a valid filename; if not, the
					// space must be part of the filename we're constructing
					if (*myCmdLine == ' ') {
						HANDLE				myFindFile;
						WIN32_FIND_DATA		myFile;
					
						myTempName[myIndex] = '\0';
						strcpy(myBuffName, myTempName);
						
						myFindFile = FindFirstFile(myBuffName, &myFile);
						if (myFindFile != INVALID_HANDLE_VALUE) {
							// we found a file having the specified name; close our file search and
							// break out of our character-gobbling loop (since we've got a valid filename)
							FindClose(myFindFile);
							break;
						}
					}
				
					// if we made it here, *myCmdLine is part of the filename (possibly a space)
					myFileName[myIndex] = myTempName[myIndex] = *myCmdLine;
				}
				
				if (*myCmdLine != '\0')
					myCmdLine++;
				
				// add a terminating NULL character
				myFileName[myIndex] = '\0';

				// myFileName is in 8.3 form; call FindFirstFile again to convert it to a long name
				// and then make an FSSpec record using the long file name
				
				// get the directory path
				NativePathNameToFSSpec(myFileName, &myFSSpec, 0L);
				FSSpecToNativePathName(&myFSSpec, myFilePath, MAX_PATH, kDirectoryPathOnly);
				
				myFindFile = FindFirstFile(myFileName, &myFile);
				if (myFindFile != INVALID_HANDLE_VALUE) {
					strcat(myFilePath, kFilePathSepString);					// the path separator
					strcat(myFilePath, myFile.cFileName);
					NativePathNameToFSSpec(myFilePath, &myFSSpec, 0L);
					FindClose(myFindFile);
				} else {
					// if FindFirstFile fails, just use the 8.3 name
					NativePathNameToFSSpec(myFileName, &myFSSpec, 0L);
				}

				// open the script file; get the QTVR movie specified in it
				VRScript_FindAndOpenQTVRMovieFile(&myFSSpec);
			}

		} else
			myCmdLine += strlen(myCmdLine);   		// point to NULL
	}
}
#endif	// TARGET_OS_WIN32


//////////
//
// VRScript_FindAndOpenQTVRMovieFile
// Open the specified script file and look for the first OpenQTVRMovieFile command in it;
// open the specified movie file if one is found.
//
//////////

void VRScript_FindAndOpenQTVRMovieFile (FSSpec *theFSSpecPtr)
{
	FILE		*myFile = NULL;
	char		myString[kMaxCmdLineLength];
	char		myCommand[kMaxCmdWordLength];
	
	// set the default directory and volume to be those of the script file, not the application
	VRScript_SetCurrentDirectory(theFSSpecPtr);
	
	// convert the filename to a C string
	memcpy(gScriptFileName, &theFSSpecPtr->name[1], theFSSpecPtr->name[0]);
	gScriptFileName[theFSSpecPtr->name[0]] = '\0';
	
	// open the script file			
	myFile = fopen(gScriptFileName, "r");
	if (myFile == NULL)
		return;

	// search through the script file for a line beginning "OpenQTVRMovieFile"
	while (fgets(myString, sizeof(myString), myFile) != NULL) {
	
		// get the command word
		sscanf(myString, "%s ", myCommand);
		if (strlen(myCommand) == 0)
			break;
	
	 	// open the specified VR movie file
		if (strcmp(myCommand, "OpenQTVRMovieFile") == 0) {
			char		myPathName[kMaxFileNameLength];
			UInt32		myOptions;
			FSSpec		myFSSpec;

			sscanf(myString, "%*s %ld %s", &myOptions, myPathName);
			VRScript_UnpackString(myPathName);
			
			// create an FSSpec that picks out the QTVR movie file; if myPathName is a full pathname,
			// then use the movie file (if any) that it picks out; otherwise, look for a file having
			// that name in the same directory as the script file (which is now the current directory)
			FileUtils_MakeFSSpecForPathName(theFSSpecPtr->vRefNum, theFSSpecPtr->parID, myPathName, &myFSSpec);

			// now act as if "Open" were chosen from the File menu....
			QTFrame_OpenMovieInWindow(NULL, &myFSSpec);
			
			// break out of the while loop
			break;
		}
	}
	
	fclose(myFile);
}


//////////
//
// VRScript_ProcessScriptCommandLine
// Process a script command line.
//
// Remember that this application is intended to illustrate how to integrate media with
// QuickTime VR movies; it is not meant to provide a commercial-quality scripting language
// and parser. You've been warned!
//
//////////

void VRScript_ProcessScriptCommandLine (WindowObject theWindowObject, char *theCommandLine)
{
	UInt32		myResID;
	UInt32		myNodeID;
	UInt32		myOptions;
	UInt32		myUInt32;
	UInt32		myEntryID;
	UInt32		mySpriteID;
	UInt32		myHotSpotID;
	SInt32		myMaxTimes;
	float		myPanAngle;
	float		myTiltAngle;
	float		myFOVAngle;
	float		myMinAngle;
	float		myMaxAngle;
	float		myTempAngle;
	float		myFloat;
	char		myCommand[kMaxCmdWordLength];
	char		myCmdLine[kMaxCmdLineLength];
	char		myPathName[kMaxFileNameLength];
	UInt32		myCode;								// the command code for myCommand
	
	//////////
	//
	// do any necessary preprocessing on the command line
	//
	//////////
	
	// decode the string, if necessary;
	// any line that begins with '@' is assumed to be encoded using a simple rotate-11 scheme
	if (theCommandLine[0] == '@') {
		theCommandLine++;							// get rid of the '@'
		VRScript_DecodeString(theCommandLine);		// decode the rest of the command
	}
#if ONLY_ENCODED_SCRIPTS
	else {
		if (gReadingScriptFile)
			return;									// we allow only encoded scripts
	}
#endif

	// strip off any leading white space
	while (isspace(theCommandLine[0]))
		theCommandLine++;
		
	//////////
	//
	// filter out an otiose command line
	//
	//////////
	
	// ignore an empty command line
	if (strlen(theCommandLine) == 0)
		return;

	// any line that begins with '#' is a comment; ignore it
	if (theCommandLine[0] == '#')
		return;
		
	//////////
	//
	// if verbose mode is on, display the current command in our debug window
	//
	//////////

	if (gIsVerbose) {
		// first, make sure the debug window is open
		if (gDebugWindow == NULL)
			gDebugWindow = VRScript_OpenDebugWindow();

		// write the current command into the debug window
		VRScript_PrintToDebugWindow(theCommandLine);
	}
		
	//////////
	//
	// extract the command word from the command line
	//
	//////////

	sscanf(theCommandLine, "%s ", myCommand);
	if (strlen(myCommand) == 0)
		return;
		
	//////////
	//
	// process the command word
	//
	//////////
	
	myCode = VRHash_GetCommandCode(myCommand);
	switch (myCode) {
		case kOpenQTVRMovieFile:
			break;
			
		case kSetVerboseState:			// turn verbose mode on or off
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);

			if (myUInt32 == kVRState_Toggle)
				myUInt32 = !gIsVerbose;
				
			gIsVerbose = (Boolean)myUInt32;
			
			if (gDebugWindow != NULL)
				ShowHide(gDebugWindow, gIsVerbose);
			break;
		
		case kReplaceMainMovie: {		// open the specified QuickTime movie in place of the current one
			UInt32		myOverlayType;
			UInt32		myNameType;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %s", &myOverlayType, &myNameType, &myOptions, myPathName);
			VRScript_SetCurrentMovie(theWindowObject, myOverlayType, myNameType, myOptions, myPathName);
			break;
		}
		
		case kSetCurrentDirectory: {	// set the directory to search for content files
		
			// NOTE: as currently implemented, the "current directory" is set for the application, not for
			// each open QTVR movie; this means that one QTVR movie can change the current directory and thus
			// affect the search path of another open QTVR movie. This isn't optimal. It would be possible to
			// fix this and attach a current directory to each open QTVR movie; however, the main use of this
			// command is likely to be inside of the script file that's read when a QTVR movie is first opened.
			// So I haven't bothered to do the work necessary to prevent current directory collisions. I might
			// fix this at some point in the future.
			
			// NOTE: The pathname that is read from the command line must specify *a file* in the directory to
			// be made current. The specified file does not actually need to exist, but the directory must.

			FSSpec		myFSSpec;
			StringPtr	myString;
			
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myPathName);
			VRScript_UnpackString(myPathName);
			myString = QTUtils_ConvertCToPascalString(myPathName);
			FSMakeFSSpec(0, 0L, myString, &myFSSpec);
			VRScript_SetCurrentDirectory(&myFSSpec);
			free(myString);
			break;
		}
		
		case kSetBarState:		 		// show or hide the controller bar
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			VRScript_SetControllerBarState(theWindowObject, (Boolean)myUInt32, myOptions);
			break;
		
		case kSetButtonState: {	 		// show or hide a button in the controller bar;
			UInt32		myButton;		// or, enable or disable the display text in the bar
			
	 		// NOTE: the semantics of the custom controller button are reversed from the others: you need to
	 		// ask VRScript to *hide* the button (myState == 0) if you want it to appear; I might fix this at
	 		// some point in the future....
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myButton, &myUInt32, &myOptions);
			VRScript_SetControllerButtonState(theWindowObject, myButton, (Boolean)myUInt32, myOptions);
			break;
		}
		
		case kSetResizeState:	 		// enable or disable window resizing
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			VRScript_SetResizeState(theWindowObject, (Boolean)myUInt32, myOptions);
			break;
		
		case kSetWindowSize: {	 		// set the current size of a movie window
			UInt32				myHeight;
			UInt32				myWidth;
			Rect				myRect;
			MovieController 	myMC;
			
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myHeight, &myWidth, &myOptions);
			myMC = (**theWindowObject).fController;
			if (QTUtils_IsControllerBarVisible(myMC)) {
				MCGetControllerBoundsRect(myMC, &myRect);
			 } else {
				Movie	myMovie;
				
				myMovie = MCGetMovie(myMC);
				GetMovieBox(myMovie, &myRect);
			}

			myRect.right = (short)myWidth;
			myRect.bottom = (short)myHeight;
			MCSetControllerBoundsRect(myMC, &myRect);
			break;
		}
		
		case kSetMaxWindowSize: {	 	// set the maximum size of a movie window
			UInt32				myHeight;
			UInt32				myWidth;
			
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myHeight, &myWidth, &myOptions);
			gMCResizeBounds.right = (short)myWidth;
			gMCResizeBounds.bottom = (short)myHeight;
			VRScript_SetResizeState(theWindowObject, (**theWindowObject).fCanResizeWindow, myOptions);
			break;
		}
		
		case kReplaceCursor: {	 		// replace one cursor by another, or restore the original QTVR cursor
			SInt32				myPrevID;
			SInt32				myNewID;
			QTVRCursorRecord	myCursorRec;
			
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myPrevID, &myNewID, &myOptions);

			myCursorRec.theType = (UInt16)myOptions;		// the type of cursor to replace
			myCursorRec.rsrcID = (SInt16)myPrevID;			// the resource ID of cursor to replace
			if (myOptions == kQTVRUseDefaultCursor) {
				myCursorRec.handle = NULL;
			} else {
				myCursorRec.handle = (Handle)MacGetCursor((short)myNewID);
				if (myCursorRec.handle != NULL)
					DetachResource(myCursorRec.handle);
			}
			
			QTVRReplaceCursor((**theWindowObject).fInstance, &myCursorRec);
				
			// QTVRReplaceCursor makes a copy of the handle we pass it, so we can dispose of our handle;
			// make sure not to dispose the handle if QTVR loaded it, however
			if ((myCursorRec.handle != NULL) && (myOptions != kQTVRUseDefaultCursor))
				DisposeHandle(myCursorRec.handle);
			
			break;
		}
		
		case kSetHotSpotIDCursors: {	// replace the triad of cursors for a hot spot specified by its ID; currently this works only for *undefined* hot spots
			SInt32		myCurs1ID, myCurs2ID, myCurs3ID;	// resource IDs of the three replacement cursors

			if (theWindowObject == NULL)
				break;

			// read the command paramters	
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld", &myNodeID, &myHotSpotID, &myCurs1ID, &myCurs2ID, &myCurs3ID, &myOptions);

			// enlist three ReplaceCursor calls for entering the hot spot: install new cursor triad
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, myCurs3ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, myCurs2ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, myCurs1ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);

			// enlist three ReplaceCursor calls for leaving the hot spot: reinstall original cursor triad
			// NOTE: this prevents the new cursor from appearing on other hot spots of the same type, and
			// also works around a bug in QTVR 2.0.0 and 2.0.1: replacing a cursor by itself disposes of 
			// the cursor and eventually might lead to a crash.
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, 0L, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);

			// enlist three ReplaceCursor calls for leaving the node: reinstall original cursor triad
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);

			break;
		}
		
		case kSetHotSpotTypeCursors: {	// replace the triad of cursors for a hot spot specified by its type
			char		myHotSpotType[kMaxOSTypeLength];
			SInt32		myCurs1ID, myCurs2ID, myCurs3ID;	// resource IDs of the three replacement cursors
			OSType		myType;

			if (theWindowObject == NULL)
				break;

			// read the command paramters	
			sscanf(theCommandLine, "%*s %ld %s %ld %ld %ld %ld", &myNodeID, myHotSpotType, &myCurs1ID, &myCurs2ID, &myCurs3ID, &myOptions);
			
			// convert the string to an OSType
			myType = VRScript_StringToOSType(myHotSpotType);
			
			// enlist three ReplaceCursor calls for entering the hot spot: install new cursor triad
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, myCurs3ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, myCurs2ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, myCurs1ID, kQTVRStdCursorType);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotEnter, myCmdLine);

			// enlist three ReplaceCursor calls for leaving the hot spot: reinstall original cursor triad
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, kVRDoIt_Forever, kQTVRHotSpotLeave, myCmdLine);

			// enlist three ReplaceCursor calls for leaving the node: reinstall original cursor triad
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseUpOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseDownOnUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);
			sprintf(myCmdLine, "ReplaceCursor %ld %ld %ld", kCursID_MouseOverUndefHS, 0, kQTVRUseDefaultCursor);
			VRScript_EnlistNodeExitCommand(theWindowObject, myNodeID, kVRAnyNode, kVRDoIt_Forever, (UInt32)0, myCmdLine);

			break;
		}
		
		case kGoToNodeID:	 			// go to a node		
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myNodeID, &myOptions);
			QTVRGoToNodeID((**theWindowObject).fInstance, myNodeID);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kShowDefaultView:		 	// display the default view of the current node		
			if (theWindowObject == NULL)
				break;
				
			QTVRShowDefaultView((**theWindowObject).fInstance);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kOpenResourceFile:		 	// open the specified resource file
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myPathName);
			VRScript_UnpackString(myPathName);
			VRScript_OpenResourceFile(theWindowObject, myOptions, myPathName);
			break;
		
		case kSetCorrection:			// set the imaging correction mode
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetImagingProperty((**theWindowObject).fInstance, (QTVRImagingMode)myOptions, kQTVRImagingCorrection, myUInt32);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSetQuality:				// set the image quality
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetImagingProperty((**theWindowObject).fInstance, (QTVRImagingMode)myOptions, kQTVRImagingQuality, myUInt32);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSetSwingSpeed:	 		// set the speed of swing transitions
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetTransitionProperty((**theWindowObject).fInstance, kQTVRTransitionSwing, kQTVRTransitionSpeed, myUInt32);
			break;
		
		case kSetSwingDirection:	 	// set the direction of swing transitions
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetTransitionProperty((**theWindowObject).fInstance, kQTVRTransitionSwing, kQTVRTransitionDirection, myUInt32);
			break;
		
		case kSetSwingState:			// enable or disable swing transitions
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVREnableTransition((**theWindowObject).fInstance, kQTVRTransitionSwing, (Boolean)myUInt32);
			break;
		
		case kSetPanAngle:		 		// set the pan angle
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myPanAngle, &myOptions);
			myPanAngle = QTVRUtils_DegreesToRadians(myPanAngle);
			
			if (myOptions == kVRValue_Relative) {
				myTempAngle = QTVRGetPanAngle((**theWindowObject).fInstance);
				myPanAngle += myTempAngle;
			}
			
			QTVRSetPanAngle((**theWindowObject).fInstance, myPanAngle);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSetTiltAngle:				// set the tilt angle
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myTiltAngle, &myOptions);
			myTiltAngle = QTVRUtils_DegreesToRadians(myTiltAngle);
			
			if (myOptions == kVRValue_Relative) {
				myTempAngle = QTVRGetTiltAngle((**theWindowObject).fInstance);
				myTiltAngle += myTempAngle;
			}
			
			QTVRSetTiltAngle((**theWindowObject).fInstance, myTiltAngle);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSetPanTiltZoom:	 		// set the pan, tilt, and zoom angles
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %f %f %ld %ld", &myPanAngle, &myTiltAngle, &myFOVAngle, &myUInt32, &myOptions);
			
			// if the value passed in is kVRValue_Preserve and the values are absolute, then keep that angle constant
			if ((myPanAngle == kVRValue_Preserve) && (myOptions == kVRValue_Absolute))
				myPanAngle = QTVRGetPanAngle((**theWindowObject).fInstance);
			else
				myPanAngle = QTVRUtils_DegreesToRadians(myPanAngle);
				
			if ((myTiltAngle == kVRValue_Preserve) && (myOptions == kVRValue_Absolute))
				myTiltAngle = QTVRGetTiltAngle((**theWindowObject).fInstance);
			else
				myTiltAngle = QTVRUtils_DegreesToRadians(myTiltAngle);
				
			if ((myFOVAngle == kVRValue_Preserve) && (myOptions == kVRValue_Absolute))
				myFOVAngle = QTVRGetFieldOfView((**theWindowObject).fInstance);
			else
				myFOVAngle = QTVRUtils_DegreesToRadians(myFOVAngle);
				
			if (myOptions == kVRValue_Relative) {
				myTempAngle = QTVRGetPanAngle((**theWindowObject).fInstance);
				myPanAngle += myTempAngle;
				myTempAngle = QTVRGetTiltAngle((**theWindowObject).fInstance);
				myTiltAngle += myTempAngle;
				myTempAngle = QTVRGetFieldOfView((**theWindowObject).fInstance);
				myFOVAngle += myTempAngle;
			}
			
			// wrap and constrain, so that the values we shoot for are actually achievable
			QTVRWrapAndConstrain((**theWindowObject).fInstance, kQTVRPan, myPanAngle, &myPanAngle);
			QTVRWrapAndConstrain((**theWindowObject).fInstance, kQTVRTilt, myTiltAngle, &myTiltAngle);
			QTVRWrapAndConstrain((**theWindowObject).fInstance, kQTVRFieldOfView, myFOVAngle, &myFOVAngle);
			
			// enable swing transitions, if requested by myMode parameter
			if ((myUInt32 == kVRTransition_Swing) || (myUInt32 == kVRTransition_SwingWait))
				QTVREnableTransition((**theWindowObject).fInstance, kQTVRTransitionSwing, true);
			
			// set the desired FOV, pan, and tilt angles
			QTVRSetFieldOfView((**theWindowObject).fInstance, myFOVAngle);
			QTVRSetPanAngle((**theWindowObject).fInstance, myPanAngle);
			QTVRSetTiltAngle((**theWindowObject).fInstance, myTiltAngle);

			// update the screen
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);

			// if a blocking swing is requested, "spin our wheels" until we get (close to) to the destination angles
			if (myUInt32 == kVRTransition_SwingWait)
				while (!VRScript_FloatsAreEqual(myPanAngle, QTVRGetPanAngle((**theWindowObject).fInstance), kRadianTolerance) ||
					   !VRScript_FloatsAreEqual(myTiltAngle, QTVRGetTiltAngle((**theWindowObject).fInstance), kRadianTolerance) ||
					   !VRScript_FloatsAreEqual(myFOVAngle, QTVRGetFieldOfView((**theWindowObject).fInstance), kRadianTolerance)) {
					   
					QTVRSetFieldOfView((**theWindowObject).fInstance, myFOVAngle);
					QTVRSetPanAngle((**theWindowObject).fInstance, myPanAngle);
					QTVRSetTiltAngle((**theWindowObject).fInstance, myTiltAngle);
					
					QTApp_Idle((**theWindowObject).fWindow); 
					MCIdle((**theWindowObject).fController); 
				}

			// disable swing transitions, if previously enabled
			if ((myUInt32 == kVRTransition_Swing) || (myUInt32 == kVRTransition_SwingWait))
				QTVREnableTransition((**theWindowObject).fInstance, kQTVRTransitionSwing, false);
				
			break;
		
		case kSetFieldOfView:	 		// set the field of view
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myFOVAngle, &myOptions);
			myFOVAngle = QTVRUtils_DegreesToRadians(myFOVAngle);
			
			if (myOptions == kVRValue_Relative) {
				myTempAngle = QTVRGetFieldOfView((**theWindowObject).fInstance);
				myFOVAngle += myTempAngle;
			}
			
			QTVRSetFieldOfView((**theWindowObject).fInstance, myFOVAngle);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSetViewCenter: {	 		// set the view center of an object node
			QTVRFloatPoint		myPoint;
			
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %f %ld", &myPoint.x, &myPoint.y, &myOptions);
			QTVRSetViewCenter((**theWindowObject).fInstance, &myPoint);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kSetPanLimits:		 		// set the current pan angle constraints
			sscanf(theCommandLine, "%*s %f %f %ld", &myMinAngle, &myMaxAngle, &myOptions);
			VRScript_SetAngleConstraints(theWindowObject, kQTVRPan, myMinAngle, myMaxAngle, myOptions);
			break;
		
		case kSetTiltLimits:	 		// set the current tilt angle constraints
			sscanf(theCommandLine, "%*s %f %f %ld", &myMinAngle, &myMaxAngle, &myOptions);
			VRScript_SetAngleConstraints(theWindowObject, kQTVRTilt, myMinAngle, myMaxAngle, myOptions);
			break;
		
		case kSetZoomLimits:	 		// set the current zoom angle constraints
			sscanf(theCommandLine, "%*s %f %f %ld", &myMinAngle, &myMaxAngle, &myOptions);
			VRScript_SetAngleConstraints(theWindowObject, kQTVRFieldOfView, myMinAngle, myMaxAngle, myOptions);
			break;
		
		case kSetHotSpotState: {	 	// enable or disable a hot spot;
			UInt32		myState;		// there's no easy way to get the current state of a hot spot, so we don't support toggling

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myHotSpotID, &myState, &myOptions);
			QTVREnableHotSpot((**theWindowObject).fInstance, myOptions, myHotSpotID, (Boolean)myState);
			
			// we need to update, because the hot spot regions might currently be showing
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);

			break;
		}
		
		case kSetTranslateState:	 	// enable or disable object translation
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionTranslateOnMouseDown, (void *)myUInt32);
			break;
		
		case kSetClickRadius: {	 		// set the radius within which clicks occur
			UInt16		myRadius;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myRadius, &myOptions);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionMouseClickHysteresis, (void *)myRadius);
			break;
		}
		
		case kSetClickTimeout:		 	// set the timeout for clicks
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionMouseClickTimeout, (void *)myUInt32);
			break;
		
		case kSetPanTiltSpeed:		 	// set the pan and tilt speed
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionPanTiltSpeed, (void *)myUInt32);
			break;
		
		case kSetZoomSpeed:		 		// set the zoom speed
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionZoomSpeed, (void *)myUInt32);
			break;
		
		case kSetMouseScale:	 		// set the mouse-motion scale
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myFloat, &myOptions);
			myFloat = QTVRUtils_DegreesToRadians(myFloat);
			QTVRSetInteractionProperty((**theWindowObject).fInstance, kQTVRInteractionMouseMotionScale, &myFloat);
			break;
		
		case kSetFrameRate:		 		// set the frame rate of an object node
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myFloat, &myOptions);

			if (myOptions == kVRValue_Relative)
				myFloat += QTVRGetFrameRate((**theWindowObject).fInstance);
			
			QTVRSetFrameRate((**theWindowObject).fInstance, myFloat);
			break;
		
		case kSetViewRate:		 		// set the view rate of an object node
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myFloat, &myOptions);
			
			if (myOptions == kVRValue_Relative)
				myFloat += QTVRGetViewRate((**theWindowObject).fInstance);
			
			QTVRSetViewRate((**theWindowObject).fInstance, myFloat);
			break;
		
		case kSetViewTime: {	 		// set the current view time of an object node
			TimeValue	myTime;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %f %ld", &myTime, &myOptions);
			
			if (myOptions == kVRValue_Relative)
				myTime += QTVRGetViewCurrentTime((**theWindowObject).fInstance);
			
			QTVRSetViewCurrentTime((**theWindowObject).fInstance, myTime);
			break;
		}
		
		case kSetViewState: {	 		// set the current view state of an object node
			UInt32		myType;
			UInt32		myState;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myType, &myState, &myOptions);
			QTVRSetViewState((**theWindowObject).fInstance, (QTVRViewStateType)myType, (UInt16)myState);
			break;
		}
		
		case kSetAnimationState: {	 	// set the animation state of an object node
			UInt32		mySetting;
			UInt32		myState;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySetting, &myState, &myOptions);
			QTVRSetAnimationSetting((**theWindowObject).fInstance, (QTVRObjectAnimationSetting)mySetting, (Boolean)myState);
			break;
		}
		
		case kSetControlState: {	 	// set the control state of an object node
			UInt32		mySetting;
			UInt32		myState;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySetting, &myState, &myOptions);
			QTVRSetControlSetting((**theWindowObject).fInstance, (QTVRControlSetting)mySetting, (Boolean)myState);
			break;
		}
		
		case kSetFrameAnimState:	 	// enable or disable frame animation in an object node
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVREnableFrameAnimation((**theWindowObject).fInstance, (Boolean)myUInt32);
			break;
		
		case kSetViewAnimState:		 	// enable or disable view animation in an object node
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVREnableViewAnimation((**theWindowObject).fInstance, (Boolean)myUInt32);
			break;
		
		case kSetQTVRVisState:		 	// enable or disable QTVR movie visibility
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRSetVisible((**theWindowObject).fInstance, (Boolean)myUInt32);
			break;
		
		case kSetCachePrefs: {	 		// set the back buffer resolution, depth, and size
			SInt32		myResolution;
			SInt32		myDepth;
			SInt32		mySize;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld", &myResolution, &myDepth, &mySize, &myOptions);
			QTVRSetBackBufferPrefs((**theWindowObject).fInstance, kQTVRUseMovieGeometry, (UInt16)myResolution, (SInt16)myDepth, (SInt16)mySize);
			break;
		}
		
		case kSetMovieVolume:	 		// set the volume of a QTVR sound track
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			SetMovieVolume((**theWindowObject).fMovie, (short)myUInt32);
			break;
		
		case kSetSoundVolume: {	 		// set the volume of a sound
			UInt32				myVolume;
			VRScriptSoundPtr	mySoundPtr;
			VRScriptMoviePtr	myMoviePtr;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myVolume, &myOptions);
			
			// is it a sound resource?
			mySoundPtr = (VRScriptSoundPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_Sound, myEntryID);
			if (mySoundPtr != NULL) {
				VRSound_SetVolume(mySoundPtr->fChannel, (unsigned short)myVolume, (unsigned short)myVolume);
				break;
			}
			
			// is it a movie sound track?
			myMoviePtr = (VRScriptMoviePtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QTMovie, myEntryID);
			if (myMoviePtr != NULL)
				SetMovieVolume(myMoviePtr->fMovie, (short)myVolume);

			break;
		}
		
		case kSetSoundBalance: {	 	// set the balance of a sound
			UInt32				myLeftPct;
			UInt32				myRightPct;
			VRScriptSoundPtr	mySoundPtr;
			VRScriptMoviePtr	myMoviePtr;
			unsigned short		myLeftVol, myRightVol;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld", &myEntryID, &myLeftPct, &myRightPct, &myOptions);
			
			// is it a sound resource?
			mySoundPtr = (VRScriptSoundPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_Sound, myEntryID);
			if (mySoundPtr != NULL) {
				VRSound_GetVolume(mySoundPtr->fChannel, &myLeftVol, &myRightVol);
				VRSound_SetVolume(mySoundPtr->fChannel, myLeftVol * (myLeftPct / 100), myRightVol * (myRightPct / 100));
				break;
			}
			
			// is it a movie sound track?
			myMoviePtr = (VRScriptMoviePtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QTMovie, myEntryID);
			if (myMoviePtr != NULL) {
				short			myValue;
				
				myValue = (((float)myRightPct / 100) * kQTMaxSoundVolume) - (((float)myLeftPct / 100) * kQTMaxSoundVolume);
				MediaSetSoundBalance(myMoviePtr->fMediaHandler, myValue);
			}
			
			break;
		}
		
		case kPlaySceneSound: {			// play a movie-wide ambient sound asynchronously
	 		UInt32		myMode;
	 		UInt32		myFade;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld", &myResID, &myEntryID, &myMode, &myFade, &myOptions);
			VRSound_PlaySound(theWindowObject, kVRAnyNode, myResID, myEntryID, 1.0, 1.0, 1.0, 1.0, kSSpSourceMode_Ambient, myMode, myFade, myOptions);
			break;
		}
		
		case kPlaySceneQTMidi: {		// play a movie-wide ambient QuickTime sound-only file asynchronously
			UInt32		myMode;
	 		UInt32		myFade;
	 		UInt32		myIsLocal;
			float		myX, myY, myZ;
			float		myProjAngle;
		
			sscanf(theCommandLine, "%*s %ld %ld %f %f %f %f %ld %ld %ld %s", &myEntryID, &myIsLocal, &myX, &myY, &myZ, &myProjAngle, &myMode, &myFade, &myOptions, myPathName);
			myProjAngle = QTVRUtils_DegreesToRadians(myProjAngle);
			VRMoov_PlayMovie(theWindowObject, kVRAnyNode, myEntryID, QTVRUtils_Point3DToPanAngle(myX, myY, myZ), QTVRUtils_Point3DToTiltAngle(myX, myY, myZ), 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, myIsLocal, myIsLocal, myProjAngle, myMode, myOptions, myPathName);
			break;
		}
		
		case kPlayNodeQTMidi: {	 		// play a QuickTime MIDI file in a node
			UInt32		myMode;
			UInt32		myFade;
	 		UInt32		myIsLocal;
			float		myX, myY, myZ;
			float		myProjAngle;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %f %f %f %f %ld %ld %ld %s", &myNodeID, &myEntryID, &myMaxTimes, &myIsLocal, &myX, &myY, &myZ, &myProjAngle, &myMode, &myFade, &myOptions, myPathName);
			sprintf(myCmdLine, "PlayQTMidi %ld %ld %f %f %f %f %ld %ld %ld %s", myEntryID, myIsLocal, myX, myY, myZ, myProjAngle, myMode, myFade, myOptions, myPathName);
			VRScript_EnlistNodeEntryCommand(theWindowObject, myNodeID, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kPlayNodeSound: {	 		// play a sound in a node
			UInt32		myMode;
			UInt32		myFade;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld", &myResID, &myNodeID, &myEntryID, &myMaxTimes, &myMode, &myFade, &myOptions);
			sprintf(myCmdLine, "PlaySndResource %ld %ld %ld %ld %ld", myResID, myEntryID, myMode, myFade, myOptions);
			VRScript_EnlistNodeEntryCommand(theWindowObject, myNodeID, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kPlayNode3DSound: {	 	// play a 3D sound in a node
			float		myX, myY, myZ;
			float		myProjAngle;
			UInt32		mySourceMode;
			UInt32		myMode;
			UInt32		myFade;

			sscanf(theCommandLine, "%*s %ld %ld %ld %f %f %f %f %ld %ld %ld %ld %ld", &myResID, &myNodeID, &myEntryID, &myX, &myY, &myZ, &myProjAngle, &mySourceMode, &myMaxTimes, &myMode, &myFade, &myOptions);
			sprintf(myCmdLine, "Play3DSndResource %ld %ld %f %f %f %f %ld %ld %ld %ld", myResID, myEntryID, myX, myY, myZ, myProjAngle, mySourceMode, myMode, myFade, myOptions);
			VRScript_EnlistNodeEntryCommand(theWindowObject, myNodeID, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kHotSpotQTMidi: {	 		// play a QuickTime MIDI file when a hot spot is clicked
			UInt32		myMode;
			UInt32		myFade;
	 		UInt32		myIsLocal;
			float		myX, myY, myZ;
			float		myProjAngle;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %f %f %f %f %ld %ld %ld %s", &myNodeID, &myHotSpotID, &myEntryID, &myMaxTimes, &myIsLocal, &myX, &myY, &myZ, &myProjAngle, &myMode, &myFade, &myOptions, myPathName);
			sprintf(myCmdLine, "PlayQTMidi %ld %ld %f %f %f %f %ld %ld %ld %s", myEntryID, myIsLocal, myX, myY, myZ, myProjAngle, myMode, myFade, myOptions, myPathName);
			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kHotSpotSound: {	 		// play a sound when a hot spot is clicked
			UInt32		myMode;
			UInt32		myFade;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld %ld %ld", &myResID, &myNodeID, &myHotSpotID, &myEntryID, &myMaxTimes, &myMode, &myFade, &myOptions);
			sprintf(myCmdLine, "PlaySndResource %ld %ld %ld %ld %ld", myResID, myEntryID, myMode, myFade, myOptions);
			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kHotSpot3DSound: {	 		// play a 3D sound when a hot spot is clicked
			float		myX, myY, myZ;
			float		myProjAngle;
			UInt32		mySourceMode;
			UInt32		myMode;
			UInt32		myFade;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %f %f %f %f %ld %ld %ld %ld %ld", &myResID, &myNodeID, &myHotSpotID, &myEntryID, &myX, &myY, &myZ, &myProjAngle, &mySourceMode, &myMaxTimes, &myMode, &myFade, &myOptions);
			sprintf(myCmdLine, "Play3DSndResource %ld %ld %f %f %f %f %ld %ld %ld %ld", myResID, myEntryID, myX, myY, myZ, myProjAngle, mySourceMode, myMode, myFade, myOptions);
			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kHotSpotMovie: {	 		// play a movie when a hot spot is clicked
	 		float		myScale;
	 		float		myWidth;
	 		UInt32		myKeyRed;
	 		UInt32		myKeyGreen;
	 		UInt32		myKeyBlue;
			UInt32		myUseBuffer;
			UInt32		myUseCenter;
			UInt32		myUseKey;
			UInt32		myUseHide;
			UInt32		myUseDir;
			UInt32		myRotate;
	 		float		myVolAngle;
			UInt32		myMode;

			sscanf(theCommandLine, "%*s %ld %ld %ld %f %f %f %f %ld %ld %ld %ld %ld %ld %ld %ld %ld %f %ld %ld %ld %s", 
					&myEntryID, 
					&myNodeID, 
					&myHotSpotID, 
					&myPanAngle,
					&myTiltAngle,
					&myScale, 
					&myWidth, 
					&myKeyRed, 
					&myKeyGreen, 
					&myKeyBlue, 
					&myUseBuffer,
					&myUseCenter,
					&myUseKey,
					&myUseHide,
					&myUseDir,
					&myRotate,
					&myVolAngle,
					&myMaxTimes,
					&myMode,
					&myOptions,
					myPathName);
				
			sprintf(myCmdLine, "PlayMovie %ld %f %f %f %f %ld %ld %ld %ld %ld %ld %ld %ld %ld %f %ld %ld %s",
					myEntryID,
					myPanAngle,
					myTiltAngle,
					myScale, 
					myWidth, 
					myKeyRed, 
					myKeyGreen, 
					myKeyBlue, 
					(Boolean)myUseBuffer,
					(Boolean)myUseCenter,
					(Boolean)myUseKey,
					(Boolean)myUseHide,
					(Boolean)myUseDir,
					(Boolean)myRotate,
					myVolAngle,
					myMode,
					myOptions,
					myPathName);

			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, 0, myCmdLine);
			break;
		}
		
		case kTriggerHotSpot:	 		// trigger a particular hot spot
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld", &myUInt32, &myOptions);
			QTVRTriggerHotSpot((**theWindowObject).fInstance, myUInt32, 0, 0);
			break;
		
		case kPlayQTMidi: {				// play a QuickTime sound-only file asynchronously, or stop a file from playing
			UInt32		myMode;
	 		UInt32		myFade;
	 		UInt32		myIsLocal;
			float		myX, myY, myZ;
			float		myProjAngle;
		
			if (theWindowObject == NULL)
				break;
				
			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);

			sscanf(theCommandLine, "%*s %ld %ld %f %f %f %f %ld %ld %ld %s", &myEntryID, &myIsLocal, &myX, &myY, &myZ, &myProjAngle, &myMode, &myFade, &myOptions, myPathName);
			myProjAngle = QTVRUtils_DegreesToRadians(myProjAngle);
			VRMoov_PlayMovie(theWindowObject, myNodeID, myEntryID, QTVRUtils_Point3DToPanAngle(myX, myY, myZ), QTVRUtils_Point3DToTiltAngle(myX, myY, myZ), 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, myIsLocal, myIsLocal, myProjAngle, myMode, myOptions, myPathName);
			break;
		}
		
		case kPlaySndResource: {		// play a sound resource ambiently, or stop an ambient sound resource from playing
	 		UInt32		myMode;
	 		UInt32		myFade;

			if (theWindowObject == NULL)
				break;
			
			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld", &myResID, &myEntryID, &myMode, &myFade, &myOptions);
			VRSound_PlaySound(theWindowObject, myNodeID, myResID, myEntryID, 1.0, 1.0, 1.0, 1.0, kSSpSourceMode_Ambient, myMode, myFade, myOptions);
			break;
		}
		
		case kPlaySoundFile: {			// play a sound file ambiently, or stop an ambient sound file from playing
	 		UInt32		myMode;
	 		UInt32		myFade;
			short		myRefNum;
			short		myResID;

			if (theWindowObject == NULL)
				break;
			
			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %s", &myEntryID, &myMode, &myFade, &myOptions, myPathName);
			
			// a sound file (of type 'sfil') is a resource file that contains one 'snd ' resource,
			// so open the resource file and get the resource ID; then call VRSound_PlaySound.
			myRefNum = VRScript_OpenResourceFile(theWindowObject, 0, myPathName);
			myResID = VRSound_GetSndResourceID(myRefNum);
			VRSound_PlaySound(theWindowObject, myNodeID, myResID, myEntryID, 1.0, 1.0, 1.0, 1.0, kSSpSourceMode_Ambient, myMode, myFade, myOptions);
			break;
		}
		
		case kPlay3DSndResource: {		// play a sound file localized, or stop an ambient sound file from playing
			float		myX, myY, myZ;
			float		myProjAngle;
			UInt32		mySourceMode;
	 		UInt32		myMode;
			UInt32		myFade;

			if (theWindowObject == NULL)
				break;

			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
			
			sscanf(theCommandLine, "%*s %ld %ld %f %f %f %f %ld %ld %ld %ld", &myResID, &myEntryID, &myX, &myY, &myZ, &myProjAngle, &mySourceMode, &myMode, &myFade, &myOptions);
			myProjAngle = QTVRUtils_DegreesToRadians(myProjAngle);
			VRSound_PlaySound(theWindowObject, myNodeID, myResID, myEntryID, myX, myY, myZ, myProjAngle, mySourceMode, myMode, myFade, myOptions);
			break;
		}
		
		case kPlay3DSndResourceAngle: {	// play a localized sound, specified using angles
			TQ3Point3D	myPoint;
			float		myDistance;
			float		myProjAngle;
			UInt32		mySourceMode;
			UInt32		myMode;
			UInt32		myFade;
		
			if (theWindowObject == NULL)
				break;

			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
			
			sscanf(theCommandLine, "%*s %ld %ld %f %f %f %f %ld %ld %ld", &myResID, &myEntryID, &myPanAngle, &myTiltAngle, &myDistance, &myProjAngle, &mySourceMode, &myMode, &myFade, &myOptions);
			myPanAngle = QTVRUtils_DegreesToRadians(myPanAngle);
			myTiltAngle = QTVRUtils_DegreesToRadians(myTiltAngle);
			myProjAngle = QTVRUtils_DegreesToRadians(myProjAngle);

			myPoint.x = -sin(myPanAngle) * cos(myTiltAngle) * (myDistance);
			myPoint.y = sin(myTiltAngle) * (myDistance);
			myPoint.z = -cos(myPanAngle) * cos(myTiltAngle) * (myDistance);

			VRSound_PlaySound(theWindowObject, myNodeID, myResID, myEntryID, myPoint.x, myPoint.y, myPoint.z, myProjAngle, mySourceMode, myMode, myFade, myOptions);
			break;
		}
		
		case kShowPicture: {			// overlay a picture (in the front buffer)
			UInt32		myHeight;
			UInt32		myWidth;
			UInt32		myPegSides;
			UInt32		myOffset;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld %ld", &myResID, &myEntryID, &myHeight, &myWidth, &myPegSides, &myOffset, &myOptions);
			VRPicture_ShowPicture(theWindowObject, myResID, myEntryID, myHeight, myWidth, myPegSides, myOffset, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kShowNodePicture: {	 	// overlay a picture (in the front buffer) in a particular node
			UInt32		myHeight;
			UInt32		myWidth;
			UInt32		myPegSides;
			UInt32		myOffset;

			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld %ld %ld", &myResID, &myEntryID, &myNodeID, &myHeight, &myWidth, &myPegSides, &myOffset, &myOptions);
			sprintf(myCmdLine, "ShowPicture %ld %ld %ld %ld %ld %ld %ld", myResID, myEntryID, myHeight, myWidth, myPegSides, myOffset, myOptions);
			VRScript_EnlistNodeEntryCommand(theWindowObject, myNodeID, kVRDoIt_Forever, 0, myCmdLine);
			break;
		}
		
	 	case kAtTime: {	 				// execute a command at a specific time
			UInt32		myTicks;
			UInt32		myMode;
			UInt32		myRepeat;
			UInt32		myPeriod;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %ld %ld %ld %s", &myTicks, &myMode, &myNodeID, &myRepeat, &myPeriod, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistTimedCommand(theWindowObject, myTicks, myMode, myNodeID, myRepeat, myPeriod, myMaxTimes, myOptions, myCmdLine);
			break;
		}
		
	 	case kAtAppLaunch:		 		// execute a command when the application is launched
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_ProcessScriptCommandLine(theWindowObject, myCmdLine);
			break;
		
	 	case kAtAppQuit:	 			// execute a command when the application is quit
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistQuitCommand(theWindowObject, myOptions, myCmdLine);
			break;
		
	 	case kAtMouseOverHSID:		 	// execute a command when the mouse is over a hot spot, targeted by ID
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %s", &myNodeID, &myHotSpotID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtMouseOverHSType: {	 	// execute a command when the mouse is over a hot spot, targeted by type
			OSType		myType;
			char		myHotSpotType[kMaxOSTypeLength];
					
			sscanf(theCommandLine, "%*s %ld %s %ld %ld %s", &myNodeID, &myHotSpotType, &myMaxTimes, &myOptions, myCmdLine);
			myType = VRScript_StringToOSType(myHotSpotType);
			
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistMouseOverHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, myMaxTimes, myOptions, myCmdLine);
			break;
		}
		
	 	case kAtClickHSID:	 			// execute a command when the mouse is clicked on a hot spot, targeted by ID
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %s", &myNodeID, &myHotSpotID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, myHotSpotID, (OSType)0, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtClickHSType: {	 		// execute a command when the mouse is clicked on a hot spot, targeted by type
			OSType		myType;
			char		myHotSpotType[kMaxOSTypeLength];
					
			sscanf(theCommandLine, "%*s %ld %s %ld %ld %s", &myNodeID, &myHotSpotType, &myMaxTimes, &myOptions, myCmdLine);
			myType = VRScript_StringToOSType(myHotSpotType);

			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistClickHSCommand(theWindowObject, myNodeID, (UInt32)0, myType, myMaxTimes, myOptions, myCmdLine);
			break;
		}
		
	 	case kAtClickCustomButton:		// execute a command when the mouse is clicked on the custom button in the controller bar
			sscanf(theCommandLine, "%*s %ld %ld %ld %s", &myNodeID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistClickCustomButtonCommand(theWindowObject, myNodeID, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtClickSprite:			// execute a command when the mouse is clicked on a sprite			
			sscanf(theCommandLine, "%*s %ld %ld %ld %s", &myNodeID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistClickSpriteCommand(theWindowObject, myNodeID, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtTriggerWiredAction: {	// execute a command in response to a wired action being triggered			
			OSType		myType;
			char		myEventType[kMaxOSTypeLength];
			UInt32		myID;
					
			sscanf(theCommandLine, "%*s %ld %ld %s %ld %ld %s", &myNodeID, &myMaxTimes, &myEventType, &myID, &myOptions, myCmdLine);
			myType = VRScript_StringToOSType(myEventType);

			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistWiredActionCommand(theWindowObject, myNodeID, myMaxTimes, myType, myID, myOptions, myCmdLine);
			break;
		}

	 	case kAtNodeEntry:		 		// execute a command when the specified node is entered
			sscanf(theCommandLine, "%*s %ld %ld %ld %s", &myNodeID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistNodeEntryCommand(theWindowObject, myNodeID, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtNodeExit: {	 			// execute a command when the specified node is exited
			UInt32		myFromNodeID;
			UInt32		myToNodeID;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld %s", &myFromNodeID, &myToNodeID, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_UnpackString(myCmdLine);
			VRScript_EnlistNodeExitCommand(theWindowObject, myFromNodeID, myToNodeID, myMaxTimes, myOptions, myCmdLine);
			break;
		}
		
	 	case kAtPanAngle:	 			// execute a command at the specified pan angle
			sscanf(theCommandLine, "%*s %ld %f %f %ld %ld %s", &myNodeID, &myMinAngle, &myMaxAngle, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_EnlistAngleCommand(theWindowObject, kVREntry_PanAngleCmd, myNodeID, myMinAngle, myMaxAngle, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtTiltAngle:			 	// execute a command at the specified tilt angle
			sscanf(theCommandLine, "%*s %ld %f %f %ld %ld %s", &myNodeID, &myMinAngle, &myMaxAngle, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_EnlistAngleCommand(theWindowObject, kVREntry_TiltAngleCmd, myNodeID, myMinAngle, myMaxAngle, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kAtZoomAngle:		 		// execute a command at the specified zoom angle
			sscanf(theCommandLine, "%*s %ld %f %f %ld %ld %s", &myNodeID, &myMinAngle, &myMaxAngle, &myMaxTimes, &myOptions, myCmdLine);
			VRScript_EnlistAngleCommand(theWindowObject, kVREntry_FOVAngleCmd, myNodeID, myMinAngle, myMaxAngle, myMaxTimes, myOptions, myCmdLine);
			break;
		
	 	case kDoBoth: {	 				// execute both of the specified commands
			char		myCmdLine1[kMaxCmdLineLength];
			char		myCmdLine2[kMaxCmdLineLength];

			sscanf(theCommandLine, "%*s %ld %s %s", &myOptions, myCmdLine1, myCmdLine2);
			VRScript_UnpackString(myCmdLine1);
			VRScript_UnpackString(myCmdLine2);
			VRScript_ProcessScriptCommandLine(theWindowObject, myCmdLine1);
			VRScript_ProcessScriptCommandLine(theWindowObject, myCmdLine2);
			break;
		}
		
	 	case kDoNothing:	 			// don't do anything; this can be useful to trigger some side effects of an "At" command
			break;
		
	 	case kPlayMovie: {	 			// play a QuickTime movie
	 		float		myScale;
	 		float		myWidth;
	 		UInt32		myKeyRed;
	 		UInt32		myKeyGreen;
	 		UInt32		myKeyBlue;
			UInt32		myUseBuffer;
			UInt32		myUseCenter;
			UInt32		myUseKey;
			UInt32		myUseHide;
			UInt32		myUseDir;
			UInt32		myRotate;
			UInt32		myMode;
	 		float		myVolAngle;
			
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %ld %ld %ld %ld %ld %ld %ld %ld %ld %f %ld %ld %s", 
					&myEntryID,
					&myPanAngle,
					&myTiltAngle,
					&myScale, 
					&myWidth, 
					&myKeyRed, 
					&myKeyGreen, 
					&myKeyBlue, 
					&myUseBuffer,
					&myUseCenter,
					&myUseKey,
					&myUseHide,
					&myUseDir,
					&myRotate,
					&myVolAngle,
					&myMode,
					&myOptions,
					myPathName);
				
			myPanAngle = QTVRUtils_DegreesToRadians(myPanAngle);
			myTiltAngle = QTVRUtils_DegreesToRadians(myTiltAngle);
			myVolAngle = QTVRUtils_DegreesToRadians(myVolAngle);
			myWidth = QTVRUtils_DegreesToRadians(myWidth);

			if (theWindowObject == NULL)
				break;

			myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);

			VRScript_UnpackString(myPathName);
			VRMoov_PlayMovie(theWindowObject,
					myNodeID,
					myEntryID,
					myPanAngle,
					myTiltAngle,
					myScale, 
					myWidth, 
					myKeyRed, 
					myKeyGreen, 
					myKeyBlue, 
					(Boolean)myUseBuffer,
					(Boolean)myUseCenter,
					(Boolean)myUseKey,
					(Boolean)myUseHide,
					(Boolean)myUseDir,
					(Boolean)myRotate,
					myVolAngle,
					myMode,
					myOptions,
					myPathName);
			break;
		}
		
	 	case kPlayTransMovie:	 		// play a QuickTime movie as a transition between two nodes
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myPathName);
			VRMoov_PlayTransitionMovie(theWindowObject, myOptions, myPathName);
			break;
		
	 	case kPlayTransEffect: {	 	// play a QuickTime video effect as a transition between two nodes
			char		myEffectType[kMaxOSTypeLength];
			UInt32		myFromNodeID;
			UInt32		myToNodeID;
			long		myEffectNum;
			OSType		myType;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %s %ld %ld", &myFromNodeID, &myToNodeID, &myMaxTimes, myEffectType, &myEffectNum, &myOptions);

			// convert the string to an OSType
			myType = VRScript_StringToOSType(myEffectType);

			VRScript_EnlistTransitionEffect(theWindowObject, myFromNodeID, myToNodeID, myMaxTimes, myType, myEffectNum, myOptions);
			break;
		}
		
		case kMoveScreen: {		 		// shift the movie screen center
			QTVRFloatPoint	myCenter;
			float			myHoriz;
			float			myVert;

			sscanf(theCommandLine, "%*s %f %f %ld", &myHoriz, &myVert, &myOptions);
			myHoriz = QTVRUtils_DegreesToRadians(myHoriz);
			myVert = QTVRUtils_DegreesToRadians(myVert);
			VRMoov_GetEmbeddedMovieCenter(theWindowObject, &myCenter);
			myCenter.x += myHoriz;
			myCenter.y += myVert;
			VRMoov_SetEmbeddedMovieCenter(theWindowObject, &myCenter);
			break;
		}
		
		case kBeep:						// play the system beep
			QTFrame_Beep();
			break;
		
		case kProcessScript:			// open and process a script file (synchronously!)
			sscanf(theCommandLine, "%*s %ld %s", &myOptions, myPathName);
			VRScript_OpenScriptFile(theWindowObject, myPathName);
			break;
		
#if QD3D_AVAIL
		case kCreateBox: {	 			// create a box
			float		myX, myY, myZ;
			float		myXSize, myYSize, myZSize;

			if (!gHasQuickDraw3D)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myXSize, &myYSize, &myZSize, &myOptions);
			VR3DObjects_EnlistBox(theWindowObject, myEntryID, myX, myY, myZ, myXSize, myYSize, myZSize, myOptions);
			break;
		}

		case kCreateCone: {	 			// create a cone
			float		myX, myY, myZ;
			float		myMajRad, myMinRad, myHeight;

			if (!gHasQuickDraw3D15)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myMajRad, &myMinRad, &myHeight, &myOptions);
			VR3DObjects_EnlistCone(theWindowObject, myEntryID, myX, myY, myZ, myMajRad, myMinRad, myHeight, myOptions);
			break;
		}

		case kCreateCylinder: {	 		// create a cylinder
			float		myX, myY, myZ;
			float		myMajRad, myMinRad, myHeight;

			if (!gHasQuickDraw3D15)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myMajRad, &myMinRad, &myHeight, &myOptions);
			VR3DObjects_EnlistCylinder(theWindowObject, myEntryID, myX, myY, myZ, myMajRad, myMinRad, myHeight, myOptions);
			break;
		}

		case kCreateEllipsoid: {	 	// create an ellipsoid
			float		myX, myY, myZ;
			float		myMajRad, myMinRad, myHeight;

			if (!gHasQuickDraw3D15)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myMajRad, &myMinRad, &myHeight, &myOptions);
			VR3DObjects_EnlistEllipsoid(theWindowObject, myEntryID, myX, myY, myZ, myMajRad, myMinRad, myHeight, myOptions);
			break;
		}

		case kCreateTorus: {	 		// create a torus
			float		myX, myY, myZ;
			float		myMajRad, myMinRad, myHeight, myRatio;

			if (!gHasQuickDraw3D15)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myMajRad, &myMinRad, &myHeight, &myRatio, &myOptions);
			VR3DObjects_EnlistTorus(theWindowObject, myEntryID, myX, myY, myZ, myMajRad, myMinRad, myHeight, myRatio, myOptions);
			break;
		}

		case kCreateRectangle: {	 	// create a rectangle
			float		myX, myY, myZ;
			float		myX1, myY1, myZ1, myX2, myY2, myZ2, myX3, myY3, myZ3, myX4, myY4, myZ4;

			if (!gHasQuickDraw3D15)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myX1, &myY1, &myZ1, &myX2, &myY2, &myZ2, &myX3, &myY3, &myZ3, &myX4, &myY4, &myZ4, &myOptions);
			VR3DObjects_EnlistRectangle(theWindowObject, myEntryID, myX, myY, myZ, myX1, myY1, myZ1, myX2, myY2, myZ2, myX3, myY3, myZ3, myX4, myY4, myZ4, myOptions);
			break;
		}

		case kOpen3DMFFile: {	 		// load a 3D object contained in a 3DMF file
			float		myX, myY, myZ;

			if (!gHasQuickDraw3D)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %ld %s", &myEntryID, &myX, &myY, &myZ, &myOptions, myPathName);
			VR3DObjects_Enlist3DMFFile(theWindowObject, myEntryID, myX, myY, myZ, myOptions, myPathName);
			break;
		}

		case kSet3DObjLocation: {		// set the location of a 3D object
			float		myX, myY, myZ;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myOptions);
			VR3DObjects_SetLocation(theWindowObject, myEntryID, myX, myY, myZ, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kSet3DObjColor: {			// set the color of a 3D object
			float		myRed, myGreen, myBlue;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %ld", &myEntryID, &myRed, &myGreen, &myBlue, &myOptions);
			VR3DObjects_SetColor(theWindowObject, myEntryID, myRed, myGreen, myBlue, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kSet3DObjTransp: {			// set the transparency level of a 3D object
			float		myRed, myGreen, myBlue;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %ld", &myEntryID, &myRed, &myGreen, &myBlue, &myOptions);
			VR3DObjects_SetTransparency(theWindowObject, myEntryID, myRed, myGreen, myBlue, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kSet3DObjInterp:			// set the interpolation style of a 3D object
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);
			VR3DObjects_SetInterpolation(theWindowObject, myEntryID, myUInt32, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSet3DObjBackface:			// set the backfacing style of a 3D object
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);
			VR3DObjects_SetBackfacing(theWindowObject, myEntryID, myUInt32, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSet3DObjFill:				// set the fill style of a 3D object
			if (theWindowObject == NULL)
				break;

			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);
			VR3DObjects_SetFill(theWindowObject, myEntryID, myUInt32, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSet3DObjRotation: {		// set the rotation factors of a 3D object
			float		myX, myY, myZ;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myOptions);
			myX = QTVRUtils_DegreesToRadians(myX);
			myY = QTVRUtils_DegreesToRadians(myY);
			myZ = QTVRUtils_DegreesToRadians(myZ);
			VR3DObjects_SetRotation(theWindowObject, myEntryID, myX, myY, myZ, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		}
		
		case kSet3DObjRotState:			// set the rotation state of a 3D object
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);
			VR3DObjects_SetRotationState(theWindowObject, myEntryID, myUInt32, myOptions);
			break;
		
		case kSet3DObjVisState:			// set the visibility state of a 3D object
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);
			VR3DObjects_SetVisibleState(theWindowObject, myEntryID, myUInt32, myOptions);
			QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
			break;
		
		case kSet3DObjTexture:			// set the texture of a 3D object
			sscanf(theCommandLine, "%*s %ld %ld %ld %s", &myEntryID, &myUInt32, &myOptions, myPathName);
			VR3DObjects_SetTexture(theWindowObject, myEntryID, (Boolean)myUInt32, myOptions, myPathName);
			break;
		
		case kDestroy3DObject: {		// destroy a 3D object; note that the options are not yet defined
	    	VRScriptGenericPtr 	myPointer;

			sscanf(theCommandLine, "%*s %ld %ld", &myEntryID, &myOptions);
			myPointer = VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QD3DObject, myEntryID);
			if (myPointer != NULL)
				VRScript_DelistEntry(theWindowObject, myPointer);
			break;
		}
#endif // QD3D_AVAIL
		
		case kSet3DSndLocation: {		// set the location of a localized sound
			float		myX, myY, myZ;

			sscanf(theCommandLine, "%*s %ld %f %f %f %ld", &myEntryID, &myX, &myY, &myZ, &myOptions);
			VRSound_SetLocation(theWindowObject, myEntryID, myX, myY, myZ, myOptions);
			VRSound_Update3DSoundEnv(theWindowObject);
			break;
		}

		case kSetVariable: {			// set the value of a variable; variables are used only in If commands
			char					myVarName[kMaxVarNameLength];
			SInt32					myVarValue;
			VRScriptVariablePtr		myPointer;
			
			sscanf(theCommandLine, "%*s %s %ld %ld", myVarName, &myVarValue, &myOptions);
			myPointer = VRScript_GetVariableEntry(theWindowObject, myVarName);
			
			if (myPointer == NULL) {
				VRScript_EnlistVariable(theWindowObject, myVarName, myVarValue);
			} else {
				if (myOptions == kVRValue_Absolute)
					myPointer->fValue = myVarValue;
				if (myOptions == kVRValue_Relative)
					myPointer->fValue += myVarValue;
			}
			
			break;
		}

		case kIf: {						// evaluate the expression "var op value"; execute a command if true
			char					myVarName[kMaxVarNameLength];
			char					myOperation[kMaxVarOpLength];
			SInt32					myVarValue;
			VRScriptVariablePtr		myPointer;
			
			sscanf(theCommandLine, "%*s %s %s %ld %ld %s", myVarName, myOperation, &myVarValue, &myOptions, myCmdLine);
			myPointer = VRScript_GetVariableEntry(theWindowObject, myVarName);
			
			if (myPointer != NULL) {
				Boolean				myRunCommand = false;

				// find the appropriate operation and test for its satisfaction
				if ((strcmp(myOperation, "=") == 0) || (strcmp(myOperation, "==") == 0))
					myRunCommand = (myPointer->fValue == myVarValue);
				else if (strcmp(myOperation, "!=") == 0)
					myRunCommand = (myPointer->fValue != myVarValue);
				else if (strcmp(myOperation, "<") == 0)
					myRunCommand = (myPointer->fValue < myVarValue);
				else if (strcmp(myOperation, "<=") == 0)
					myRunCommand = (myPointer->fValue <= myVarValue);
				else if (strcmp(myOperation, ">") == 0)
					myRunCommand = (myPointer->fValue > myVarValue);
				else if (strcmp(myOperation, ">=") == 0)
					myRunCommand = (myPointer->fValue >= myVarValue);		
					
				if (myRunCommand) {
					VRScript_UnpackString(myCmdLine);
					VRScript_ProcessScriptCommandLine(theWindowObject, myCmdLine);
				}
			}
			
			break;
		}

		case kSetSpriteVisState:		// set the visibility of a sprite on or off
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySpriteID, &myUInt32, &myOptions);
			VRSprites_SetVisibleState(theWindowObject, (QTAtomID)mySpriteID, myUInt32, myOptions);
			break;

		case kSetSpriteLayer:			// set the layer of a sprite
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySpriteID, &myUInt32, &myOptions);
			VRSprites_SetLayer(theWindowObject, (QTAtomID)mySpriteID, myUInt32, myOptions);
			break;

		case kSetSpriteGraphicsMode:	// set the graphics mode of a sprite
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySpriteID, &myUInt32, &myOptions);
			VRSprites_SetGraphicsMode(theWindowObject, (QTAtomID)mySpriteID, myUInt32, myOptions);
			break;

		case kSetSpriteImageIndex:		// set the image index of a sprite
			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld", &mySpriteID, &myUInt32, &myOptions);
			VRSprites_SetImageIndex(theWindowObject, (QTAtomID)mySpriteID, (short)myUInt32, myOptions);
			break;

		case kSetSpriteMatrix: {		// set the matrix of a sprite
			float			myR0C0, myR0C1, myR0C2, myR1C0, myR1C1, myR1C2, myR2C0, myR2C1, myR2C2;
			MatrixRecord	myMatrix;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %f %f %f %f %f %f %f %f %f %ld", &mySpriteID, &myR0C0, &myR0C1, &myR0C2, &myR1C0, &myR1C1, &myR1C2, &myR2C0, &myR2C1, &myR2C2, &myOptions);
			myMatrix.matrix[0][0] = FloatToFixed(myR0C0);
			myMatrix.matrix[0][1] = FloatToFixed(myR0C1);
			myMatrix.matrix[0][2] = FloatToFract(myR0C2);
			myMatrix.matrix[1][0] = FloatToFixed(myR1C0);
			myMatrix.matrix[1][1] = FloatToFixed(myR1C1);
			myMatrix.matrix[1][2] = FloatToFract(myR1C2);
			myMatrix.matrix[2][0] = FloatToFixed(myR2C0);
			myMatrix.matrix[2][1] = FloatToFixed(myR2C1);
			myMatrix.matrix[2][2] = FloatToFract(myR2C2);
			VRSprites_SetMatrix(theWindowObject, (QTAtomID)mySpriteID, &myMatrix, myOptions);
			break;
		}
		
		case kSetSpriteLocation: {		// set the location of a sprite;
										// the h and v values are pixels relative to the sprite track's origin
			UInt32			myH, myV;
			Point			myPoint;

			if (theWindowObject == NULL)
				break;
				
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld", &mySpriteID, &myH, &myV, &myOptions);
			myPoint.h = (short)myH;
			myPoint.v = (short)myV;
			VRSprites_SetLocation(theWindowObject, (QTAtomID)mySpriteID, &myPoint, myOptions);
			break;
		}

		case kSetTrackVolume:	 		// set the volume of a sound track in an embedded QuickTime movie
		case kSetTrackState:	 		// enable or disable a track in an embedded QuickTime movie
		case kSetTrackLayer: {	 		// set the layer of a track in an embedded QuickTime movie
			UInt32				myValue;
			UInt32				myIndex;
			VRScriptMoviePtr	myMoviePtr = NULL;
			Track				myTrack = NULL;

			if (theWindowObject == NULL)
				break;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld %ld", &myEntryID, &myValue, &myIndex, &myOptions);

			myMoviePtr = (VRScriptMoviePtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QTMovie, myEntryID);
			if (myMoviePtr != NULL) {
				if (myMoviePtr->fMovie != NULL)
					myTrack = GetMovieIndTrack(myMoviePtr->fMovie, myIndex);
					
	    		if (myTrack == NULL)
	    		 	break;
				
				switch (myCode) {
					case kSetTrackVolume:
			    		if (myOptions == kVRValue_Absolute)
			    			SetTrackVolume(myTrack, (short)myValue);
			    		else
			    			SetTrackVolume(myTrack, (short)myValue + GetTrackVolume(myTrack));
						break;
					
					case kSetTrackState:
						if (myValue == kVRState_Toggle)
							myValue = (UInt32)!GetTrackEnabled(myTrack);
			
		    			SetTrackEnabled(myTrack, (Boolean)myValue);
						break;
					
					case kSetTrackLayer:
						SetTrackLayer(myTrack, (short)myValue);
						break;
				}	 
	    	}
	    	
			break;
		}
				
		case kSetMovieTime: 	 		// set the current time of the specified movie
		case kSetMovieRate: 	 		// set the rate of the specified movie
		case kSetMovieTimeScale: {	 	// set the time scale of the specified movie
			VRScriptMoviePtr	myMoviePtr = NULL;

			if (theWindowObject == NULL)
				break;
			
			sscanf(theCommandLine, "%*s %ld %ld %ld", &myEntryID, &myUInt32, &myOptions);

			myMoviePtr = (VRScriptMoviePtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QTMovie, myEntryID);
			if (myMoviePtr != NULL)
				if (myMoviePtr->fMovie != NULL)
					switch (myCode) {
						case kSetMovieTime:
			    			SetMovieTimeValue(myMoviePtr->fMovie, (TimeValue)myUInt32);
							break;
						case kSetMovieRate:
	    					SetMovieRate(myMoviePtr->fMovie, (Fixed)myUInt32);
							break;
						case kSetMovieTimeScale:
	    					SetMovieTimeScale(myMoviePtr->fMovie, (TimeScale)myUInt32);
							break;
					}
	    	
			break;
		}
	
		case kInvalidCommand:
		default:
			// if we got here, we encountered an unrecognized command;
			// in verbose mode, print a warning
			if (gIsVerbose)
				VRScript_PrintToDebugWindow(kUnknownCommandString);
			break;
			
	} // switch (myCode)
	
}


//////////
//
// VRScript_OpenDebugWindow
// Open the debug window.
//
//////////

WindowPtr VRScript_OpenDebugWindow (void)
{
	WindowPtr			myWindow = NULL;
	StringPtr			myTitle = QTUtils_ConvertCToPascalString(kDebugWindowTitle);
	Rect				myRect;
	GrafPtr				myPort = NULL;
		
	MacSetRect(&myRect, 50, 100, 700, 500);
	myWindow = NewCWindow(NULL, &myRect, myTitle, true, documentProc, (WindowPtr)-1, false, 0);

	if (myWindow != NULL) {
		GetPort(&myPort);
		MacSetPort((GrafPtr)GetWindowPort(myWindow));
		TextSize(kDebugWindowTextSize);
		MacSetPort(myPort);
	}

	free(myTitle);
	return(myWindow);
}


//////////
//
// VRScript_PrintToDebugWindow
// Display the specified string in the debug window.
//
//////////

void VRScript_PrintToDebugWindow (char *theString)
{
	static short	myCurYPos = 0;
	GrafPtr			myPort;
	Rect			myRect;
	RgnHandle		myRegion;
	StringPtr		myString = QTUtils_ConvertCToPascalString(theString);

	if (gDebugWindow == NULL)
		return;
		
	GetPort(&myPort);
	MacSetPort((GrafPtr)GetWindowPort(gDebugWindow));
	
#if TARGET_OS_MAC
	GetWindowPortBounds(gDebugWindow, &myRect);
#endif
#if TARGET_OS_WIN32
	myRect = gDebugWindow->portRect;
#endif
	
	MoveTo(0, myCurYPos);
	DrawString(myString);
	
	if (myCurYPos >= (myRect.bottom - kDebugWindowLineSize)) {
		myRegion = NewRgn();
		ScrollRect(&myRect, 0, -kDebugWindowLineSize, myRegion);
		DisposeRgn(myRegion);
	} else {
		myCurYPos += kDebugWindowLineSize;
	}
	
	MacSetPort(myPort);
	free(myString);
}


//////////
//
// VRScript_SetCurrentDirectory
// Set the current (or default) directory. All filenames specified in the script file that are not full
// pathnames are taken as relative to this directory.
//
// A bit of terminology: on MacOS, the directory that is searched when only a filename is given is called
// the "default directory"; on Windows, this is called the "current directory".
//
// NOTE: The theFSSpecPtr parameter must specify *a file* in the directory to be made current. That file
// does not need to exist, however.
//
//////////

void VRScript_SetCurrentDirectory (FSSpecPtr theFSSpecPtr)
{
#if TARGET_OS_MAC
	HSetVol(NULL, theFSSpecPtr->vRefNum, theFSSpecPtr->parID);
#elif TARGET_OS_WIN32
	char 		myFilePath[MAX_PATH];

	myFilePath[0] = '\0';
	FSSpecToNativePathName(theFSSpecPtr, myFilePath, MAX_PATH, kDirectoryPathOnly);
	SetCurrentDirectory(myFilePath);
#endif
}


//////////
//
// VRScript_SetCurrentMovie
// Set the QuickTime movie currently displayed in the window attached to theWindowObject.
//
//////////

void VRScript_SetCurrentMovie (WindowObject theWindowObject, UInt32 theOverlayType, UInt32 theNameType, UInt32 theOptions, char *thePathName)
{
#pragma unused(theOverlayType, theOptions)
	FSSpec				myFSSpec;
	Movie				myNewMovie = NULL;
	Movie				myOldMovie = NULL;
	MovieController		myNewMC = NULL;
	MovieController		myOldMC = NULL;
	short				myNewRefNum = 0;
	short				myOldRefNum = 0;
	short				myResID = 0;
	WindowReference		myWindow = NULL;
	QTVRInstance		myInstance = NULL;
#if TARGET_OS_WIN32
	char 				myFileName[MAX_PATH];
#endif
	OSErr				myErr = noErr;

	// make sure we have a valid window object and movie window
	if (theWindowObject == NULL)
		return;

	myWindow = (**theWindowObject).fWindow;
	if (myWindow == NULL)
		return;
	
	// get the previous movie and controller
	myOldMovie = (**theWindowObject).fMovie;
	myOldMC = (**theWindowObject).fController;
	myOldRefNum = (**theWindowObject).fFileRefNum;
	
	// find the target QuickTime movie file
	switch (theNameType) {
		case kVRRelativePath:
		case kVRAbsolutePath:
			myErr = FileUtils_MakeFSSpecForPathName(0, 0, thePathName, &myFSSpec);
			if (myErr != noErr)
				goto bail;
				
			// open the specified movie file;
			// ideally, we'd like read and write permission, but we'll settle for read-only permission
			myErr = OpenMovieFile(&myFSSpec, &myNewRefNum, fsRdWrPerm);
			if (myErr != noErr)
				myErr = OpenMovieFile(&myFSSpec, &myNewRefNum, fsRdPerm);

			// if we couldn't open the file with even just read-only permission, bail....
			if (myErr != noErr)
				goto bail;

			// now fetch the first movie from the file
			myResID = 0;
			myErr = NewMovieFromFile(&myNewMovie, myNewRefNum, &myResID, NULL, newMovieActive, NULL);
			if (myErr != noErr)
				goto bail;
			
			CloseMovieFile(myNewRefNum);
			break;
			
		case kVRAbsoluteURL: {
			char 	*myString;
			
			myResID = 0;
			myNewMovie = URLUtils_NewMovieFromURL(thePathName, newMovieActive, &myResID);
			
			myString = URLUtils_GetURLBasename(thePathName);
			FileUtils_MakeFSSpecForPathName(0, 0, myString, &myFSSpec);
			free(myString);
			
			break;
		}
			
		case kVRRelativeURL:
			// to be supplied
			break;
			
		default:
			goto bail;	// unknown pathname type
	}
	
	// make sure the movie uses the window GWorld in all situations
	SetMovieGWorld(myNewMovie, (CGrafPtr)QTFrame_GetPortFromWindowReference(myWindow), GetGWorldDevice((CGrafPtr)QTFrame_GetPortFromWindowReference(myWindow)));

	// create and configure the movie controller
	myNewMC = QTFrame_SetupController(myNewMovie, myWindow, false);
	if (myNewMC == NULL)
		goto bail;
		
	//////////
	//
	// in with the new....
	//
	//////////
	
	// set the window title
#if TARGET_OS_MAC
	SetWTitle(myWindow, myFSSpec.name);
#elif TARGET_OS_WIN32
	FSSpecToNativePathName(&myFSSpec, myFileName, MAX_PATH, kFileNameOnly);
	SetWindowText(myWindow, myFileName);
#endif

	// look for the QTVR instance 
	QTVRGetQTVRInstance(&myInstance, QTVRGetQTVRTrack(myNewMovie, 1), myNewMC);
	
	// store movie info in the window record
	(**theWindowObject).fMovie = myNewMovie;
	(**theWindowObject).fController = myNewMC;
	(**theWindowObject).fFileResID = myResID;
	(**theWindowObject).fFileRefNum = myNewRefNum;
	(**theWindowObject).fCanResizeWindow = true;
	(**theWindowObject).fIsDirty = false;
	(**theWindowObject).fInstance = myInstance;
	(**theWindowObject).fFileFSSpec = myFSSpec;

	// if it's a QuickTime VR movie, install all the QuickTime VR-related callback procedures
	if (myInstance != NULL)
		VRScript_InstallAllQTVRCallbackProcs(myInstance, theWindowObject);

	// redraw the window
	MCInvalidate(myNewMC, QTFrame_GetWindowFromWindowReference(myWindow), MCGetWindowRgn(myNewMC, QTFrame_GetWindowFromWindowReference(myWindow)));

	// reset the cursor
	InitCursor();
	
	//////////
	//
	// out with the old....
	//
	//////////
	
	// we'd like to be able to just call DisposeMovieController and DisposeMovie on the
	// previous controller and movie, but we can't; the reason is that this function might
	// have been called in response to a user event (such as a click on a hot spot) that
	// was passed to MCIsPlayerEvent in our main event loop. Experience tells me that it's
	// not good to dispose of the movie controller inside code called by MCIsPlayerEvent.
	// Our solution is to defer disposal until a safe time.
	
	gPreviousMC = myOldMC;
	gPreviousMovie = myOldMovie;
		
	if (myOldRefNum != 0)
		CloseMovieFile(myOldRefNum);

	VRMoov_StartMovie(myNewMovie);
		
	return;
	
bail:
	// if we arrived here, an error occurred and we could not load the new movie;
	// undo any work we did to install the new movie
	if (myNewMC != NULL)
		DisposeMovieController(myNewMC);
		
	if (myNewMovie != NULL)
		DisposeMovie(myNewMovie);
		
	if (myNewRefNum != 0)
		CloseMovieFile(myNewRefNum);
}


//////////
//
// VRScript_SetControllerBarState
// Set the state of the controller bar.
//
//////////

void VRScript_SetControllerBarState (WindowObject theWindowObject, Boolean theState, UInt32 theOptions)
{
#pragma unused(theOptions)

	if (theWindowObject == NULL)
		return;

	if (theState == kVRState_Toggle)
		theState = !MCGetVisible((**theWindowObject).fController);
	
	MCSetVisible((**theWindowObject).fController, theState);
}


//////////
//
// VRScript_SetControllerButtonState
// Set the state of the specified button (or text display) in the controller bar.
//
//////////

void VRScript_SetControllerButtonState (WindowObject theWindowObject, UInt32 theButton, Boolean theState, UInt32 theOptions)
{
#pragma unused(theOptions)
	
	long	myButton;
	
	if (theWindowObject == NULL)
		return;
		
	if ((**theWindowObject).fController == NULL)
		return;

	// convert VRScript's enum into the actual value expected by the movie controller
	switch (theButton) {
		case kVRButton_Step:			myButton = mcFlagSuppressStepButtons;			break;
		case kVRButton_Speaker:			myButton = mcFlagSuppressSpeakerButton;			break;
		case kVRButton_GoBack:			myButton = mcFlagQTVRSuppressBackBtn;			break;
		case kVRButton_ZoomInOut:		myButton = mcFlagQTVRSuppressZoomBtns;			break;
		case kVRButton_ShowHotSpots:	myButton = mcFlagQTVRSuppressHotSpotBtn;		break;
		case kVRButton_Translate:		myButton = mcFlagQTVRSuppressTranslateBtn;		break;
		case kVRButton_HelpText:		myButton = mcFlagQTVRSuppressHelpText;			break;
		case kVRButton_HotSpotNames:	myButton = mcFlagQTVRSuppressHotSpotNames;		break;
		case kVRButton_Custom:			myButton = mcFlagsUseCustomButton;				break;
		default:
			break;
	}

	// set the correct state of the button		
	switch (theState) {
		case kVRState_Hide:
			QTUtils_HideControllerButton((**theWindowObject).fController, myButton);
			break;
		case kVRState_Show:
			QTUtils_ShowControllerButton((**theWindowObject).fController, myButton);
			break;
		case kVRState_Toggle:
			QTUtils_ToggleControllerButton((**theWindowObject).fController, myButton);
			break;
		default:
			break;
	}
}


//////////
//
// VRScript_SetResizeState
// Set the state of window resizing.
//
//////////

void VRScript_SetResizeState (WindowObject theWindowObject, Boolean theState, UInt32 theOptions)
{
#pragma unused(theOptions)

	Rect			myRect;

	if (theWindowObject == NULL)
		return;

	if (theState == kVRState_Toggle)
		theState = !(**theWindowObject).fCanResizeWindow;

	(**theWindowObject).fCanResizeWindow = theState;
	
	// set size of growable window
	if ((**theWindowObject).fCanResizeWindow)
		MacSetRect(&myRect, gMCResizeBounds.left, gMCResizeBounds.top, gMCResizeBounds.right, gMCResizeBounds.bottom);
	else
		MacSetRect(&myRect, 0, 0, 0, 0);
	
	MCDoAction((**theWindowObject).fController, mcActionSetGrowBoxBounds, &myRect);
	QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
}


//////////
//
// VRScript_SetAngleConstraints
// Set the pan, tilt, or zoom constraints.
// Note: this function expects theMinAngle and theMaxAngle to be in *degrees*.
//
//////////

void VRScript_SetAngleConstraints (WindowObject theWindowObject, UInt16 theKind, float theMinAngle, float theMaxAngle, UInt32 theOptions)
{
	float	myMinAngle;
	float	myMaxAngle;

	if (theWindowObject == NULL)
		return;
	
	myMinAngle = QTVRUtils_DegreesToRadians(theMinAngle);
	myMaxAngle = QTVRUtils_DegreesToRadians(theMaxAngle);
	
	if (theOptions == kVRValue_Relative) {
		float	myCurrMin;
		float	myCurrMax;
		OSErr	myErr;

		myErr = QTVRGetConstraints((**theWindowObject).fInstance, theKind, &myCurrMin, &myCurrMax);
		if (myErr == noErr) {
			myMinAngle += myCurrMin;
			myMaxAngle += myCurrMax;
		}
	}
				
	QTVRSetConstraints((**theWindowObject).fInstance, theKind, myMinAngle, myMaxAngle);
	QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
}


//////////
//
// VRScript_OpenResourceFile
// Open the specified resource file and add it to the application's resource chain.
// Returns the file reference number, if successful, or -1 otherwise.
//
//////////

short VRScript_OpenResourceFile (WindowObject theWindowObject, UInt32 theOptions, char *thePathName)
{
#pragma unused(theWindowObject, theOptions)

	FSSpec		myFSSpec;
	short		myRefNum = -1;

	// we accept either full pathnames or partial pathnames specified relative to the default directory
	// on the default volume
	FileUtils_MakeFSSpecForPathName(0, 0L, thePathName, &myFSSpec);

	myRefNum = FSpOpenResFile(&myFSSpec, fsRdPerm);
	if (myRefNum == -1)
		;					// no error handling yet....
		
	return(myRefNum);
}


//////////
//
// VRScript_EnlistSound
// Install an ambient sound.
//
//////////

VRScriptSoundPtr VRScript_EnlistSound (WindowObject theWindowObject, UInt32 theNodeID, UInt32 theResID, UInt32 theEntryID, UInt32 theMode, UInt32 theFade, UInt32 theOptions, SndChannelPtr theChannel, SndListHandle theResource)
{
	VRScriptSoundPtr		myPointer;
	ApplicationDataHdl		myAppData;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return(NULL);
	
	myPointer = (VRScriptSoundPtr)NewPtrClear(sizeof(VRScriptSound));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_Sound;
		myPointer->fEntryID = theEntryID;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fMode = theMode;
		myPointer->fSoundContainer = kVRSound_SoundResource;
		myPointer->fResID = theResID;
		myPointer->fResourceData = theResource;
		myPointer->fChannel = theChannel;
		myPointer->fFadeWhenStopping = (Boolean)theFade;
		myPointer->fSoundIsLocalized = false;
		myPointer->fSource = 0;
		myPointer->fLocation.x = 0.0;
		myPointer->fLocation.y = 0.0;
		myPointer->fLocation.z = 0.0;
		myPointer->fProjAngle = 0.0;
		myPointer->fNextEntry = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
		(**myAppData).fListArray[kVREntry_Sound] = (VRScriptGenericPtr)myPointer;
	}
	
	return(myPointer);
}


//////////
//
// VRScript_EnlistLocalizedSound
// Install a localized sound.
//
//////////

VRScriptSoundPtr VRScript_EnlistLocalizedSound (WindowObject theWindowObject, UInt32 theNodeID, UInt32 theResID, UInt32 theEntryID, float theX, float theY, float theZ, float theProjAngle, UInt32 theMode, UInt32 theFade, UInt32 theOptions, SndChannelPtr theChannel, SndListHandle theResource, SSpSourceReference theSource)
{
	VRScriptSoundPtr		myPointer;
	ApplicationDataHdl		myAppData;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return(NULL);

	myPointer = (VRScriptSoundPtr)NewPtrClear(sizeof(VRScriptSound));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_Sound;
		myPointer->fEntryID = theEntryID;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fMode = theMode;
		myPointer->fSoundContainer = kVRSound_SoundResource;
		myPointer->fResID = theResID;
		myPointer->fResourceData = theResource;
		myPointer->fChannel = theChannel;
		myPointer->fSource = theSource;
		myPointer->fLocation.x = theX;
		myPointer->fLocation.y = theY;
		myPointer->fLocation.z = theZ;
		myPointer->fProjAngle = theProjAngle;
		myPointer->fFadeWhenStopping = (Boolean)theFade;
		myPointer->fSoundIsLocalized = true;
		myPointer->fNextEntry = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
		(**myAppData).fListArray[kVREntry_Sound] = (VRScriptGenericPtr)myPointer;
	}
	
	return(myPointer);
}


//////////
//
// VRScript_EnlistMovie
// Install a QuickTime movie by adding the given information to our linked list.
//
//////////

VRScriptMoviePtr VRScript_EnlistMovie (
				WindowObject theWindowObject,
				UInt32 theNodeID,
				UInt32 theEntryID,
				float thePanAngle,
				float theTiltAngle,
				float theScale, 
				float theWidth, 
				UInt32 theKeyRed, 
				UInt32 theKeyGreen, 
				UInt32 theKeyBlue, 
				Boolean theUseBuffer,
				Boolean theUseCenter,
				Boolean theUseKey,
				Boolean theUseHide,
				Boolean theUseDir,
				Boolean theRotate,
				float theVolAngle,
				UInt32 theMode,
				UInt32 theOptions,
				char *thePathName)
{
	VRScriptMoviePtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return(NULL);

	myPointer = (VRScriptMoviePtr)NewPtrClear(sizeof(VRScriptMovie));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_QTMovie;
		myPointer->fEntryID = theEntryID;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fMode = theMode;
		myPointer->fMovie = NULL;
		myPointer->fOffscreenGWorld = NULL;
		myPointer->fOffscreenPixMap = NULL;
		myPointer->fPrevBBufGWorld = NULL;
		myPointer->fMovieCenter.x = thePanAngle;
		myPointer->fMovieCenter.y = theTiltAngle;
		myPointer->fMovieScale = theScale;
		myPointer->fMovieWidth = theWidth;
		myPointer->fUseOffscreenGWorld = theUseBuffer;
		myPointer->fUseMovieCenter = theUseCenter;
		myPointer->fQTMovieHasSound = false;		// we don't really know until we open the file....
		myPointer->fQTMovieHasVideo = false;		// we don't really know until we open the file....
		myPointer->fCompositeMovie = theUseKey;
		myPointer->fUseHideRegion = theUseHide;
		myPointer->fChromaColor.red = (unsigned short)theKeyRed;
		myPointer->fChromaColor.green = (unsigned short)theKeyGreen;
		myPointer->fChromaColor.blue = (unsigned short)theKeyBlue;
		myPointer->fHideRegion = NULL;
		myPointer->fImageDesc = NULL;
		myPointer->fImageSequence = 0;
		myPointer->fSoundIsLocalized = theUseDir;
		myPointer->fDoRotateMovie = theRotate;
		myPointer->fVolAngle = theVolAngle;
		myPointer->fMediaHandler = NULL;
		
		SetIdentityMatrix(&myPointer->fMovieMatrix);
		SetIdentityMatrix(&myPointer->fOrigMovieMatrix);
		
		myPointer->fPathname = malloc(strlen(thePathName) + 1);
		strncpy(myPointer->fPathname, thePathName, strlen(thePathName) + 1);
		myPointer->fNextEntry = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
		(**myAppData).fListArray[kVREntry_QTMovie] = (VRScriptGenericPtr)myPointer;
	}
	
	return(myPointer);
}


//////////
//
// VRScript_Enlist3DObject
// Install a 3D object.
//
//////////

#if QD3D_AVAIL
void VRScript_Enlist3DObject (WindowObject theWindowObject, TQ3GroupObject theGroup, UInt32 theEntryID, float theX, float theY, float theZ, UInt32 theOptions)
{
	VRScript3DObjPtr		myPointer;
	ApplicationDataHdl		myAppData;
	TQ3Point3D				myPoint;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;
	
	myPointer = (VRScript3DObjPtr)NewPtrClear(sizeof(VRScript3DObj));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_QD3DObject;
		myPointer->fEntryID = theEntryID;
		myPointer->fOptions = theOptions;
		myPointer->fModel = theGroup;

		myPointer->fInterpolation = Q3InterpolationStyle_New((TQ3InterpolationStyle)kDefaultInterpolation);
		myPointer->fBackFacing = Q3BackfacingStyle_New((TQ3BackfacingStyle)kDefaultBackfacing);
		myPointer->fFillStyle = Q3FillStyle_New((TQ3FillStyle)kDefaultFill);
		
		Q3Matrix4x4_SetIdentity(&(myPointer->fRotation));	
		myPointer->fRotateFactors.x = kDefaultRotateRadians;		// default rotation factors
		myPointer->fRotateFactors.y = kDefaultRotateRadians;
		myPointer->fRotateFactors.y = kDefaultRotateRadians;
			
		Q3Point3D_Set(&myPoint, -1.0 * theX, theY, -1.0 * theZ);
		myPointer->fGroupCenter = myPoint;
		myPointer->fGroupScale = kDefaultScale;
		myPointer->fTexture = NULL;
		myPointer->fTextureIsMovie = false;
		myPointer->fModelIsVisible = true;
		myPointer->fModelIsAnimated = false;
		myPointer->fNextEntry = (VRScript3DObjPtr)(**myAppData).fListArray[kVREntry_QD3DObject];
		(**myAppData).fListArray[kVREntry_QD3DObject] = (VRScriptGenericPtr)myPointer;
	}
}
#endif


//////////
//
// VRScript_EnlistOverlayPicture
// Install an overlay picture by adding the given information to our linked list.
//
//////////

VRScriptPicturePtr VRScript_EnlistOverlayPicture (WindowObject theWindowObject, UInt32 theResID, UInt32 theEntryID, UInt32 theHeight, UInt32 theWidth, UInt32 thePegSides, UInt32 theOffset, PicHandle theResource, UInt32 theOptions)
{
	VRScriptPicturePtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return(NULL);

	myPointer = (VRScriptPicturePtr)NewPtrClear(sizeof(VRScriptPicture));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_OverlayPicture;
		myPointer->fEntryID = theEntryID;
		myPointer->fNodeID = 0;
		myPointer->fOptions = theOptions;
		myPointer->fResID = theResID;
		myPointer->fBoxHeight = theHeight;
		myPointer->fBoxWidth = theWidth;
		myPointer->fPegSides = thePegSides;
		myPointer->fOffset = theOffset;
		myPointer->fResourceData = theResource;
		myPointer->fNextEntry = (VRScriptPicturePtr)(**myAppData).fListArray[kVREntry_OverlayPicture];
		(**myAppData).fListArray[kVREntry_OverlayPicture] = (VRScriptGenericPtr)myPointer;
	}
	
	return(myPointer);
}


//////////
//
// VRScript_EnlistTimedCommand
// Install a timed-activated command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistTimedCommand (WindowObject theWindowObject, UInt32 theTicks, UInt32 theMode, UInt32 theNodeID, UInt32 theRepeat, UInt32 thePeriod, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptAtTimePtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptAtTimePtr)NewPtrClear(sizeof(VRScriptAtTime));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_TimedCommand;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fTime = theTicks;
		myPointer->fMode = theMode;
		myPointer->fTimeInstalled = gAbsoluteElapsedTime;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fRepeat = (Boolean)theRepeat;
		myPointer->fPeriod = thePeriod;
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptAtTimePtr)(**myAppData).fListArray[kVREntry_TimedCommand];
		(**myAppData).fListArray[kVREntry_TimedCommand] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistQuitCommand
// Install an application-quit command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistQuitCommand (WindowObject theWindowObject, UInt32 theOptions, char *theCmdLine)
{
	VRScriptAtQuitPtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptAtQuitPtr)NewPtrClear(sizeof(VRScriptAtQuit));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_QuitCommand;
		myPointer->fOptions = theOptions;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fNextEntry = (VRScriptAtQuitPtr)(**myAppData).fListArray[kVREntry_QuitCommand];
		(**myAppData).fListArray[kVREntry_QuitCommand] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistMouseOverHSCommand
// Install a mouse-over hot spot command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistMouseOverHSCommand (WindowObject theWindowObject, UInt32 theNodeID, UInt32 theHotSpotID, OSType theHotSpotType, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptAtMOHSPtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptAtMOHSPtr)NewPtrClear(sizeof(VRScriptAtMOHS));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_MouseOverHS;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;							// the hot spot action selector
		myPointer->fSelectByID = theHotSpotID > 0 ? true : false;
		myPointer->fHotSpotID = theHotSpotID;
		myPointer->fHotSpotType = theHotSpotType;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptAtMOHSPtr)(**myAppData).fListArray[kVREntry_MouseOverHS];
		(**myAppData).fListArray[kVREntry_MouseOverHS] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistClickHSCommand
// Install a hot spot click command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistClickHSCommand (WindowObject theWindowObject, UInt32 theNodeID, UInt32 theHotSpotID, OSType theHotSpotType, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptClickHSPtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptClickHSPtr)NewPtrClear(sizeof(VRScriptClickHS));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_ClickHS;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fSelectByID = theHotSpotID > 0 ? true : false;
		myPointer->fHotSpotID = theHotSpotID;
		myPointer->fHotSpotType = theHotSpotType;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptClickHSPtr)(**myAppData).fListArray[kVREntry_ClickHS];
		(**myAppData).fListArray[kVREntry_ClickHS] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistClickCustomButtonCommand
// Install a custom button click command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistClickCustomButtonCommand (WindowObject theWindowObject, UInt32 theNodeID, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptClickCustomPtr	myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptClickCustomPtr)NewPtrClear(sizeof(VRScriptClickCustom));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_ClickCustom;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptClickCustomPtr)(**myAppData).fListArray[kVREntry_ClickCustom];
		(**myAppData).fListArray[kVREntry_ClickCustom] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistClickSpriteCommand
// Install a sprite click command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistClickSpriteCommand (WindowObject theWindowObject, UInt32 theNodeID, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptClickSpritePtr	myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptClickSpritePtr)NewPtrClear(sizeof(VRScriptClickSprite));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_ClickSprite;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptClickSpritePtr)(**myAppData).fListArray[kVREntry_ClickSprite];
		(**myAppData).fListArray[kVREntry_ClickSprite] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistWiredActionCommand
// Install a wired-action-triggered command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistWiredActionCommand (WindowObject theWindowObject, UInt32 theNodeID, SInt32 theMaxTimes, OSType theType, UInt32 theID, UInt32 theOptions, char *theCmdLine)
{
	VRScriptWiredActionPtr	myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptWiredActionPtr)NewPtrClear(sizeof(VRScriptWiredAction));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_WiredAction;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fID = theID;
		myPointer->fEventType = theType;
		myPointer->fNextEntry = (VRScriptWiredActionPtr)(**myAppData).fListArray[kVREntry_WiredAction];
		(**myAppData).fListArray[kVREntry_WiredAction] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistNodeEntryCommand
// Install a node-entry command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistNodeEntryCommand (WindowObject theWindowObject, UInt32 theNodeID, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptNodeInPtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptNodeInPtr)NewPtrClear(sizeof(VRScriptNodeIn));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_NodeEntry;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptNodeInPtr)(**myAppData).fListArray[kVREntry_NodeEntry];
		(**myAppData).fListArray[kVREntry_NodeEntry] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistNodeExitCommand
// Install a node-exit command by adding the given information to our linked list.
//
//////////

void VRScript_EnlistNodeExitCommand (WindowObject theWindowObject, UInt32 theFromNodeID, UInt32 theToNodeID, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptNodeOutPtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myPointer = (VRScriptNodeOutPtr)NewPtrClear(sizeof(VRScriptNodeOut));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_NodeExit;
		myPointer->fOptions = theOptions;
		myPointer->fFromNodeID = theFromNodeID;
		myPointer->fToNodeID = theToNodeID;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fNextEntry = (VRScriptNodeOutPtr)(**myAppData).fListArray[kVREntry_NodeExit];
		(**myAppData).fListArray[kVREntry_NodeExit] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistAngleCommand
// Install an angle-triggered command by adding the given information to our linked list.
// Note: this function expects theMinAngle and theMaxAngle to be in *degrees*.
//
//////////

void VRScript_EnlistAngleCommand (WindowObject theWindowObject, UInt32 theKind, UInt32 theNodeID, float theMinAngle, float theMaxAngle, SInt32 theMaxTimes, UInt32 theOptions, char *theCmdLine)
{
	VRScriptAtAnglePtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	theMinAngle = QTVRUtils_DegreesToRadians(theMinAngle);
	theMaxAngle = QTVRUtils_DegreesToRadians(theMaxAngle);
	VRScript_UnpackString(theCmdLine);

	myPointer = (VRScriptAtAnglePtr)NewPtrClear(sizeof(VRScriptAtAngle));
	if (myPointer != NULL) {
		myPointer->fEntryType = theKind;
		myPointer->fNodeID = theNodeID;
		myPointer->fOptions = theOptions;
		myPointer->fMinAngle = theMinAngle;
		myPointer->fMaxAngle = theMaxAngle;
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fCommandLine = malloc(strlen(theCmdLine) + 1);
		strncpy(myPointer->fCommandLine, theCmdLine, strlen(theCmdLine) + 1);
		myPointer->fNextEntry = (VRScriptAtAnglePtr)(**myAppData).fListArray[theKind];
		(**myAppData).fListArray[theKind] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistVariable
// Install a variable by adding the given information to our linked list.
//
//////////

void VRScript_EnlistVariable (WindowObject theWindowObject, char *theVarName, UInt32 theVarValue)
{
	VRScriptVariablePtr		myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;
		
	myPointer = (VRScriptVariablePtr)NewPtrClear(sizeof(VRScriptVariable));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_Variable;
		myPointer->fValue = theVarValue;
		myPointer->fVarName = malloc(strlen(theVarName) + 1);
		strncpy(myPointer->fVarName, theVarName, strlen(theVarName) + 1);
		myPointer->fNextEntry = (VRScriptVariablePtr)(**myAppData).fListArray[kVREntry_Variable];
		(**myAppData).fListArray[kVREntry_Variable] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_EnlistTransition
// Install a transition effect by adding the given information to our linked list.
//
//////////

void VRScript_EnlistTransitionEffect (WindowObject theWindowObject, UInt32 theFromNodeID, UInt32 theToNodeID, SInt32 theMaxTimes, OSType theEffectType, long theEffectNum, UInt32 theOptions)
{
	VRScriptTransitionPtr	myPointer;
	ApplicationDataHdl		myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;
		
	myPointer = (VRScriptTransitionPtr)NewPtrClear(sizeof(VRScriptTransition));
	if (myPointer != NULL) {
		myPointer->fEntryType = kVREntry_TransitionEffect;
		myPointer->fOptions = theOptions;
		myPointer->fFromNodeID = theFromNodeID;
		myPointer->fToNodeID = theToNodeID;
		myPointer->fMaxExecutions = theMaxTimes;
		myPointer->fEffectType = theEffectType;
		myPointer->fEffectNum = theEffectNum;
		myPointer->fNumberOfSteps = (long)kDefaultNumSteps;
		myPointer->fNextEntry = (VRScriptTransitionPtr)(**myAppData).fListArray[kVREntry_TransitionEffect];
		(**myAppData).fListArray[kVREntry_TransitionEffect] = (VRScriptGenericPtr)myPointer;
	}
}


//////////
//
// VRScript_DelistEntry
// Remove an entry from a linked list.
//
//////////

void VRScript_DelistEntry (WindowObject theWindowObject, VRScriptGenericPtr theEntryPtr)
{
	ApplicationDataHdl		myAppData;
	VRScriptGenericPtr		*myListHead;	// the *address* of the list head
	
	if (theEntryPtr == NULL)
		return;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData != NULL) {
		
		// get the head of the specified list
		myListHead = &((**myAppData).fListArray[theEntryPtr->fEntryType]);
		
		// get rid of any memory associated with this entry
		switch (theEntryPtr->fEntryType) {
			case kVREntry_Sound:
				VRSound_DumpEntryMem((VRScriptSoundPtr)theEntryPtr);			break;
			case kVREntry_QTMovie: 
				VRMoov_DumpEntryMem((VRScriptMoviePtr)theEntryPtr);				break;
			case kVREntry_QD3DObject: 
#if QD3D_AVAIL
				VR3DObjects_DumpEntryMem((VRScript3DObjPtr)theEntryPtr);
#endif
				break;
			case kVREntry_OverlayPicture: 
				VRPicture_DumpEntryMem((VRScriptPicturePtr)theEntryPtr);		break;
			case kVREntry_TimedCommand: 
				free(((VRScriptAtTimePtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_QuitCommand: 
				free(((VRScriptAtQuitPtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_MouseOverHS: 
				free(((VRScriptAtMOHSPtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_ClickHS: 
				free(((VRScriptClickHSPtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_ClickCustom: 
				free(((VRScriptClickCustomPtr)theEntryPtr)->fCommandLine);		break;
			case kVREntry_ClickSprite: 
				free(((VRScriptClickSpritePtr)theEntryPtr)->fCommandLine);		break;
			case kVREntry_NodeEntry: 
				free(((VRScriptNodeInPtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_NodeExit: 
				free(((VRScriptNodeOutPtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_PanAngleCmd: 
				free(((VRScriptAtAnglePtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_TiltAngleCmd: 
				free(((VRScriptAtAnglePtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_FOVAngleCmd: 
				free(((VRScriptAtAnglePtr)theEntryPtr)->fCommandLine);			break;
			case kVREntry_Variable: 
				free(((VRScriptVariablePtr)theEntryPtr)->fVarName);				break;
			case kVREntry_TransitionEffect: 
				VREffects_DumpEntryMem((VRScriptTransitionPtr)theEntryPtr);		break;
			case kVREntry_Generic: 
			case kVREntry_Unknown: 
			default: 
				return;
				break;
		}
	
		// now drop the specified entry from the correct list

		if (*myListHead == theEntryPtr) {
			// the entry to remove is the head of the list; make the next entry the head	
			*myListHead = theEntryPtr->fNextEntry;
		} else {
			// the entry to remove is not the head of the list;
			// walk the list until we find the entry *before* the one to be removed
			VRScriptGenericPtr		myPointer;

			myPointer = *myListHead;
			while (myPointer != NULL) {
				if (myPointer->fNextEntry == theEntryPtr) {
					myPointer->fNextEntry = theEntryPtr->fNextEntry;
					break;
				}
				myPointer = (VRScriptGenericPtr)(myPointer->fNextEntry);
			}
		}
	}
	
	DisposePtr((Ptr)theEntryPtr);
}


//////////
//
// VRScript_DeleteListOfType
// Delete the list containing entries of the specified type.
//
//////////

void VRScript_DeleteListOfType (WindowObject theWindowObject, UInt32 theEntryType)
{
	ApplicationDataHdl		myAppData;
	VRScriptGenericPtr		myPointer;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;
		
	// get the head of specified list
	myPointer = (**myAppData).fListArray[theEntryType];
	while (myPointer != NULL) {
		VRScriptGenericPtr		myNext;
		
		myNext = myPointer->fNextEntry;
		
		// get rid of any memory associated with this entry
		switch (theEntryType) {
			case kVREntry_Sound: 
				VRSound_DumpEntryMem((VRScriptSoundPtr)myPointer);				break;
			case kVREntry_QTMovie: 
				VRMoov_DumpEntryMem((VRScriptMoviePtr)myPointer);				break;
			case kVREntry_QD3DObject: 
#if QD3D_AVAIL
				VR3DObjects_DumpEntryMem((VRScript3DObjPtr)myPointer);
#endif
																				break;
			case kVREntry_OverlayPicture: 
				VRPicture_DumpEntryMem((VRScriptPicturePtr)myPointer);			break;
			case kVREntry_TimedCommand: 
				free(((VRScriptAtTimePtr)myPointer)->fCommandLine);				break;
			case kVREntry_QuitCommand: 
				free(((VRScriptAtQuitPtr)myPointer)->fCommandLine);				break;
			case kVREntry_MouseOverHS: 
				free(((VRScriptAtMOHSPtr)myPointer)->fCommandLine);				break;
			case kVREntry_ClickHS: 
				free(((VRScriptClickHSPtr)myPointer)->fCommandLine);			break;
			case kVREntry_ClickCustom: 
				free(((VRScriptClickCustomPtr)myPointer)->fCommandLine);		break;
			case kVREntry_ClickSprite: 
				free(((VRScriptClickSpritePtr)myPointer)->fCommandLine);		break;
			case kVREntry_NodeEntry: 
				free(((VRScriptNodeInPtr)myPointer)->fCommandLine);				break;
			case kVREntry_NodeExit: 
				free(((VRScriptNodeOutPtr)myPointer)->fCommandLine);			break;
			case kVREntry_PanAngleCmd: 
				free(((VRScriptAtAnglePtr)myPointer)->fCommandLine);			break;
			case kVREntry_TiltAngleCmd: 
				free(((VRScriptAtAnglePtr)myPointer)->fCommandLine);			break;
			case kVREntry_FOVAngleCmd: 
				free(((VRScriptAtAnglePtr)myPointer)->fCommandLine);			break;
			case kVREntry_Variable: 
				free(((VRScriptVariablePtr)myPointer)->fVarName);				break;
			case kVREntry_TransitionEffect: 
				VREffects_DumpEntryMem((VRScriptTransitionPtr)myPointer);		break;
			case kVREntry_Generic: 
			case kVREntry_Unknown: 
			default: 
				return;
		}
		
		DisposePtr((Ptr)myPointer);
		myPointer = myNext;
	}
	
	(**myAppData).fListArray[theEntryType] = NULL;
}


//////////
//
// VRScript_DeleteAllLists
// Delete all lists used by this window.
//
//////////

void VRScript_DeleteAllLists (WindowObject theWindowObject)
{
	UInt32		myCount;
	
	for (myCount = kVRScript_FirstEntryType; myCount <= kVRScript_FinalEntryType; myCount++)
		VRScript_DeleteListOfType(theWindowObject, myCount);
}


//////////
//
// VRScript_GetObjectByEntryID
// Get the (first) list entry of the specified type having a specified entry ID.
//
//////////

VRScriptGenericPtr VRScript_GetObjectByEntryID (WindowObject theWindowObject, UInt32 theEntryType, UInt32 theEntryID)
{
	ApplicationDataHdl	myAppData;
	VRScriptGenericPtr	myPointer = NULL;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return(NULL);
	
	// walk our linked list to find the target object
	myPointer = (**myAppData).fListArray[theEntryType];
	while (myPointer != NULL) {
		if (myPointer->fEntryID == theEntryID)
			return(myPointer);
		myPointer = myPointer->fNextEntry;
	}
	
	return(NULL);
}


//////////
//
// VRScript_GetVariableEntry
// Get the (first) variable having the specified name.
//
//////////

VRScriptVariablePtr VRScript_GetVariableEntry (WindowObject theWindowObject, char *theVarName)
{
	ApplicationDataHdl	myAppData;
	VRScriptVariablePtr	myPointer = NULL;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);	
	if (myAppData == NULL)
		return(NULL);
	
	// walk our linked list to find the target object
	myPointer = (VRScriptVariablePtr)(**myAppData).fListArray[kVREntry_Variable];
	while (myPointer != NULL) {
		if (strcmp(myPointer->fVarName, theVarName) == 0)
			return(myPointer);
		myPointer = myPointer->fNextEntry;
	}
	
	return(NULL);
}


//////////
//
// VRScript_InstallAllQTVRCallbackProcs
// Install all QTVR-related callback procedures.
//
//////////

void VRScript_InstallAllQTVRCallbackProcs (QTVRInstance theInstance, WindowObject theWindowObject)
{
	// install an intercept procedure and a prescreen procedure
	VRScript_InstallInterceptRoutine(theInstance, theWindowObject);
	VRScript_InstallPrescreenRoutine(theInstance, theWindowObject);

	// install node-entering and node-leaving procedures
	QTVRSetEnteringNodeProc(theInstance, NewQTVREnteringNodeUPP(VRScript_EnteringNodeProc), (SInt32)theWindowObject, 0);
	QTVRSetLeavingNodeProc(theInstance, NewQTVRLeavingNodeUPP(VRScript_LeavingNodeProc), (SInt32)theWindowObject, 0);
	
	// install hot spot procedures
	VRScript_InstallHotSpotInterceptProc(theInstance, theWindowObject);
	VRScript_InstallMouseOverHotSpotProc(theInstance, theWindowObject);
}


//////////
//
// VRScript_InstallHotSpotInterceptProc
// Install an intercept procedure for handling hot spot triggerings.
//
//////////

void VRScript_InstallHotSpotInterceptProc (QTVRInstance theInstance, WindowObject theWindowObject)
{
	QTVRInterceptUPP		myInterceptProc;
	
	myInterceptProc = NewQTVRInterceptUPP(VRScript_HotSpotInterceptProc);
	QTVRInstallInterceptProc(theInstance, kQTVRTriggerHotSpotSelector, myInterceptProc, (long)theWindowObject, 0);
}


//////////
//
// VRScript_InstallMouseOverHotSpotProc
// Install an intercept procedure for handling mouse-over hot spots.
//
//////////

void VRScript_InstallMouseOverHotSpotProc (QTVRInstance theInstance, WindowObject theWindowObject)
{
	QTVRMouseOverHotSpotUPP		myInterceptProc;
	
	myInterceptProc = NewQTVRMouseOverHotSpotUPP(VRScript_MouseOverHotSpotProc);
	QTVRSetMouseOverHotSpotProc(theInstance, myInterceptProc, (long)theWindowObject, 0);
}


//////////
//
// VRScript_InstallPrescreenRoutine
// Install a prescreen buffer imaging complete procedure.
//
//////////

void VRScript_InstallPrescreenRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
{
	ApplicationDataHdl	myAppData;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData != NULL)
		QTVRSetPrescreenImagingCompleteProc(theInstance, (**myAppData).fPrescreenProc, (SInt32)theWindowObject, 0);
}


//////////
//
// VRScript_InstallBackBufferImagingProc
// Install a back buffer imaging procedure.
// (This routine might sometimes be called to move or resize the area of interest within the panorama.)
//
//////////

void VRScript_InstallBackBufferImagingProc (QTVRInstance theInstance, WindowObject theWindowObject)
{
	ApplicationDataHdl		myAppData = NULL;
	QTVRAreaOfInterest		myArea;
	float					myWidth, myHeight;
	VRScriptMoviePtr		myPointer = NULL;
	OSErr					myErr = paramErr;
	
	//////////
	//
	// initialize; clean up any existing back buffer procedure
	//
	//////////
	
	if ((theInstance == NULL) || (theWindowObject == NULL)) 
		return;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL) 
		return;

	HLock((Handle)myAppData);
	
	// remove any existing back buffer imaging procedure
	if ((**myAppData).fBackBufferProc != NULL)
		QTVRSetBackBufferImagingProc((**theWindowObject).fInstance, NULL, 0, NULL, 0);

	//////////
	//
	// set the area of interest
	//
	// the panAngle and tiltAngle fields define the top-left corner, in panorama space, of the area of interest;
	// so here we do not have to worry about whether the back buffer is oriented vertically or horizontally
	//
	//////////
	
	myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
	if (myPointer == NULL)
		goto bail;
	
	myWidth = myPointer->fMovieWidth * myPointer->fMovieScale;
	myHeight = myWidth * (((float)myPointer->fMovieBox.bottom) / ((float)myPointer->fMovieBox.right));

	if (myPointer->fUseMovieCenter) {
		// use the stored movie center
		myArea.panAngle = myPointer->fMovieCenter.x + (myWidth/2);
		myArea.tiltAngle = myPointer->fMovieCenter.y + (myHeight/2);
	} else {
		// center the movie on the current pan and tilt angles
		myArea.panAngle = QTVRGetPanAngle(theInstance) + (myWidth/2);
		myArea.tiltAngle = QTVRGetTiltAngle(theInstance) + (myHeight/2);
	}
	
	myArea.width = myWidth;
	myArea.height = myHeight;

	// *** insertion (B) ***
	
	//////////
	//
	// set the back buffer flags and install the back buffer procedure
	//
	//////////
	
	// make sure we get called on every idle event, so we can keep playing the embedded movie;
	// also make sure we get called on every back buffer update
	if (myPointer->fCompositeMovie)
		myArea.flags = kQTVRBackBufferEveryIdle | kQTVRBackBufferEveryUpdate | kQTVRBackBufferAlwaysRefresh;
	else
		myArea.flags = kQTVRBackBufferEveryIdle | kQTVRBackBufferEveryUpdate;
	
	// if the back buffer is oriented horizontally, set the appropriate flag
	if ((**myAppData).fBackBufferIsHoriz)
		myArea.flags |= kQTVRBackBufferHorizontal;

	// install our procedure
	myErr = QTVRSetBackBufferImagingProc(theInstance, (**myAppData).fBackBufferProc, 1, &myArea, (SInt32)theWindowObject);

bail:
	// make sure we successfully installed the procedure;
	// if an error occurred, clear the back-buffer imaging procedure
	if (myErr != noErr)
		QTVRSetBackBufferImagingProc((**theWindowObject).fInstance, NULL, 0, NULL, 0);
	
	HUnlock((Handle)myAppData);
}
	

//////////
//
// VRScript_InstallInterceptRoutine
// Install our QTVR intercept procedure.
//
//////////

void VRScript_InstallInterceptRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
{
	QTVRInterceptUPP	myInterceptProc;
	
	myInterceptProc = NewQTVRInterceptUPP(VRScript_InterceptRoutine);	
	
	// we'll just use the same intercept proc for each intercepted procedure
	QTVRInstallInterceptProc(theInstance, kQTVRSetPanAngleSelector, myInterceptProc, (SInt32)theWindowObject, 0);
	QTVRInstallInterceptProc(theInstance, kQTVRSetTiltAngleSelector, myInterceptProc, (SInt32)theWindowObject, 0);
	QTVRInstallInterceptProc(theInstance, kQTVRSetFieldOfViewSelector, myInterceptProc, (SInt32)theWindowObject, 0);
	QTVRInstallInterceptProc(theInstance, kQTVRSetViewCenterSelector, myInterceptProc, (SInt32)theWindowObject, 0);
}


//////////
//
// VRScript_HotSpotInterceptProc
// An intercept procedure for handling hot spot triggerings.
//
//////////

PASCAL_RTN void VRScript_HotSpotInterceptProc (QTVRInstance theInstance, QTVRInterceptPtr theMsg, SInt32 theRefCon, Boolean *theCancel)
{
	VRScriptClickHSPtr		myPointer;
	VRScriptClickHSPtr		myNext;
	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData;
	UInt32					myNodeID;
	UInt32					myHotSpotID;
	OSType					myType;
	Boolean					myCancelLink = false;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return;

	myNodeID = QTVRGetCurrentNodeID(theInstance);
	myHotSpotID = (UInt32)(theMsg->parameter[0]);
	
	// get the type of the hot spot, given its ID
	QTVRUtils_GetHotSpotType(theInstance, myNodeID, myHotSpotID, &myType);
	
	// walk the linked list and find any commands for this hot spot
	myPointer = (VRScriptClickHSPtr)(**myAppData).fListArray[kVREntry_ClickHS];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		
		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
			if (((myPointer->fSelectByID) && (myPointer->fHotSpotID == myHotSpotID))	// hot spot is specified by ID
			|| ((!myPointer->fSelectByID) && (myPointer->fHotSpotType == myType))) {	// hot spot is specified by type
				if (myPointer->fOptions != 0)
					myCancelLink = true;
				VRScript_ProcessScriptCommandLine(myWindowObject, myPointer->fCommandLine);
				VRScript_CheckForExpiredCommand(myWindowObject, (VRScriptGenericPtr)myPointer);
			}

		myPointer = myNext;
	}

	*theCancel = myCancelLink;
}


///////////
//
// VRScript_PrescreenRoutine
// A prescreen buffer imaging completion routine:
// 	* alter the position of the 3D sound listener based on the user's panning or tilting
// 	* set balance and volume of embedded QuickTime movie
//	* draw the rendered 3D scene into the prescreen buffer
//	* draw any enlisted overlay picture into the prescreen buffer
//
//////////

PASCAL_RTN OSErr VRScript_PrescreenRoutine (QTVRInstance theInstance, SInt32 theRefCon)
{
	float				myPan;
	float				myTilt;
	float				myFOV;
	UInt32				myNodeID;
	WindowObject		myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl	myAppData;

	// get the application-specific data associated with the window
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return(paramErr);
		
	// get the current environment
	myPan = QTVRGetPanAngle(theInstance);
	myTilt = QTVRGetTiltAngle(theInstance);
	myFOV = QTVRGetFieldOfView(theInstance);
	myNodeID = QTVRGetCurrentNodeID(theInstance);

	// process any localized sounds in the current node
	if ((**myAppData).fViewHasChanged || (**myAppData).fSoundHasChanged) 
		if ((**myAppData).fListArray[kVREntry_Sound] != NULL) {
			if (gHasSoundSprockets) {
#if SOUNDSPROCKET_AVAIL
				TQ3Vector3D			myOrientation;
			
				// figure out the orientation	
				myOrientation.x = -sin(myPan) * cos(myTilt);
				myOrientation.y = sin(myTilt);
				myOrientation.z = -cos(myPan) * cos(myTilt);
					
				// set the new orientation of the listener
				SSpListener_SetOrientation((**myAppData).fListener, &myOrientation);
				// update the virtual audio environment
				VRSound_Update3DSoundEnv(myWindowObject);
#endif
			} else {
				// we don't have SoundSprockets, so....
				// handle sound balance and volume attentuation ourselves
				VRSound_SetBalanceAndVolume(myWindowObject, myPan, myTilt);
			}
		}
		
#if QD3D_AVAIL
	// process any 3D objects in the current node
	if (gHasQuickDraw3D)
		if ((**myAppData).fListArray[kVREntry_QD3DObject] != NULL)
			VR3DObjects_PrescreenRoutine(theInstance, (SInt32)myWindowObject);
#endif

	// handle directionality and volume attenuation for QuickTime movie sound and texture-mapped QuickTime movie sound
	if ((**myAppData).fViewHasChanged || (**myAppData).fSoundHasChanged)
		if (((**myAppData).fListArray[kVREntry_QTMovie] != NULL) || ((**myAppData).fListArray[kVREntry_QD3DObject] != NULL))
			VRMoov_SetAllBalanceAndVolume(myWindowObject, myPan, myTilt);

	// draw any overlay pictures
	VRPicture_DrawNodePictures(myWindowObject);

	// reset our flags
	(**myAppData).fViewHasChanged = false;
	(**myAppData).fSoundHasChanged = false;
	
	return(noErr);
}


//////////
//
// VRScript_BackBufferImagingProc
// A back buffer imaging procedure:
//	* handle any QuickTime movies playing in the back buffer
//	* handle any pictures embedded in the back buffer [to be provided]
//
//////////

PASCAL_RTN OSErr VRScript_BackBufferImagingProc (QTVRInstance theInstance, Rect *theRect, UInt16 theAreaIndex, UInt32 theFlagsIn, UInt32 *theFlagsOut, SInt32 theRefCon)
{
#pragma unused(theAreaIndex)

	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData = NULL;
	OSErr					myErr = noErr;
	
	if ((theInstance == NULL) || (myWindowObject == NULL)) 
		return(paramErr);

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL) 
		return(paramErr);

	// if we've got an active movie to play, do so
	if (VRMoov_GetEmbeddedVideo(myWindowObject) != NULL)
		myErr = VRMoov_BackBufferImagingProc(theInstance, theRect, theAreaIndex, theFlagsIn, theFlagsOut, (SInt32)myWindowObject);
			
	return(myErr);
}


//////////
//
// VRScript_InterceptRoutine
// An intercept routine:
// 	* signal that the view parameters have changed
//
//////////

PASCAL_RTN void VRScript_InterceptRoutine (QTVRInstance theInstance, QTVRInterceptPtr theMsg, SInt32 theRefCon, Boolean *theCancel)
{
#pragma unused(theInstance)

	Boolean					myCancelInterceptedProc = false;	// true == do NOT call thru; false == call thru
	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData;
	
	*theCancel = myCancelInterceptedProc;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return;

	switch (theMsg->selector) {
		case kQTVRSetPanAngleSelector:
		case kQTVRSetTiltAngleSelector:
		case kQTVRSetFieldOfViewSelector:
		case kQTVRSetViewCenterSelector:
			(**myAppData).fViewHasChanged = true;
			break;
			
		default:
			break;
	}
	
}


//////////
//
// VRScript_EnteringNodeProc
// A node-entering procedure:
//	* initialize node-specific global variables (e.g., the node timer)
//	* execute the standard node-entry procedure
//	* execute any node-entry commands
//	* execute any custom transitions from the previous node to the current node
//
//////////

PASCAL_RTN OSErr VRScript_EnteringNodeProc (QTVRInstance theInstance, UInt32 theNodeID, SInt32 theRefCon)
{
	VRScriptNodeInPtr		myPointer = NULL;
	VRScriptNodeInPtr		myNext = NULL;
	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData;

	// initialize our node timer
	gNodeStartingTime = TickCount();

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return(paramErr);

	// call the standard node-entry procedure
	if ((**myWindowObject).fController != NULL)
		QTVRUtils_StandardEnteringNodeProc(theInstance, theNodeID, (**myWindowObject).fController);

	// node-entry commands might invoke QTVRUpdate, so enable update-streaming
	if (QTVRUtils_IsPanoNode(theInstance))
		QTVRBeginUpdateStream(theInstance, kQTVRCurrentMode);

	// walk the node-entry command list and execute any that apply to this node
	myPointer = (VRScriptNodeInPtr)(**myAppData).fListArray[kVREntry_NodeEntry];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		
		if ((myPointer->fNodeID == theNodeID) || (myPointer->fNodeID == kVRAnyNode)) {
			VRScript_ProcessScriptCommandLine(myWindowObject, myPointer->fCommandLine);
			VRScript_CheckForExpiredCommand(myWindowObject, (VRScriptGenericPtr)myPointer);
		}
	
		myPointer = myNext;
	}
	
	// disable update-streaming, if previously enabled
	if (QTVRUtils_IsPanoNode(theInstance))
		QTVREndUpdateStream(theInstance);

	// if a node transition effect is pending, force the movie image to be updated;
	// this draws the destination image into an offscreen GWorld
	if ((**myAppData).fActiveTransition != NULL) {
		MoviesTask((**myWindowObject).fMovie, 0L);
		
		// execute any custom transitions from the previous node to the current node
		if (gHasQTVideoEffects)
			VREffects_RunTransitionEffect(myWindowObject);
	}

	return(noErr);
}


//////////
//
// VRScript_LeavingNodeProc
// A node-leaving procedure:
//	* execute any node-exit commands
//	*Êstop any sounds attached to the current node
//	* dump any pictures attached to the current node
//	* dump any movies attached to the current node
//	* dump any unexpired AtTime commands that shouldn't be orphaned
//	* set up for any custom transitions from the current node to the target node

//////////

PASCAL_RTN OSErr VRScript_LeavingNodeProc (QTVRInstance theInstance, UInt32 fromNodeID, UInt32 toNodeID, Boolean *theCancel, SInt32 theRefCon)
{
	VRScriptNodeOutPtr		myPointer;
	VRScriptNodeOutPtr		myNext;
	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData;
	Boolean					myCancelExit = false;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return(paramErr);
		
	// walk the node-leaving command list and execute any that apply to this node
	myPointer = (VRScriptNodeOutPtr)(**myAppData).fListArray[kVREntry_NodeExit];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		
		if ((myPointer->fFromNodeID == fromNodeID) || (myPointer->fFromNodeID == kVRAnyNode))
			if ((myPointer->fToNodeID == toNodeID) || (myPointer->fToNodeID == kVRAnyNode)) {
				VRScript_ProcessScriptCommandLine(myWindowObject, myPointer->fCommandLine);
				if (myPointer->fOptions != 0)
					myCancelExit = true;
				VRScript_CheckForExpiredCommand(myWindowObject, (VRScriptGenericPtr)myPointer);
			}
	
		myPointer = myNext;
	}
	
	// set output parameter to indicate whether we're leaving the node or not
	*theCancel = myCancelExit;
	
	// if we're not leaving the node, just return
	if (myCancelExit)
		return(noErr);
	
	// set up for any custom transitions from the current node to the target node
	// (since we're going to get the first frame of our effect by rendering a frame
	// of the movie into an offscreen GWorld, we need to do this before we dump the
	// node pictures, movies, etc.)
	if (gHasQTVideoEffects)
		VREffects_SetupTransitionEffect(myWindowObject, fromNodeID, toNodeID);
	
	// if we really are leaving the node, dump node-specific sounds, pictures, and movies
	VRSound_DumpNodeSounds(myWindowObject);
	VRPicture_DumpNodePictures(myWindowObject);
	VRMoov_DumpNodeMovies(myWindowObject);
	
	// dump any unexpired time-based commands that are marked as to-be-killed
	VRScript_DumpUnexpiredCommands(myWindowObject, fromNodeID);
	
	// work around a bug in QTVR: 
	// moving from a panoramic node to an object node effectively hoses the prescreen routine,
	// so we need to reinstall that routine if we're moving from an object to a panoramic node
	if (QTVRGetNodeType(theInstance, fromNodeID) == kQTVRObjectType)
		if (QTVRGetNodeType(theInstance, toNodeID) == kQTVRPanoramaType)
			VRScript_InstallPrescreenRoutine(theInstance, myWindowObject);

	return(noErr);
}


//////////
//
// VRScript_MouseOverHotSpotProc
// Walk through our linked list of mouse-over hot spot commands and see whether any need to be executed.
//
// We support hot spots specified by ID (myPointer->fSelectByID == true) or by type (myPointer->fSelectByID == false).
//
//////////

PASCAL_RTN OSErr VRScript_MouseOverHotSpotProc (QTVRInstance theInstance, UInt32 theHotSpotID, UInt32 theFlags, SInt32 theRefCon)
{
	VRScriptAtMOHSPtr		myPointer;
	VRScriptAtMOHSPtr		myNext;
	WindowObject			myWindowObject = (WindowObject)theRefCon;
	ApplicationDataHdl		myAppData;
	UInt32					myNodeID;
	OSType					myType;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
	if (myAppData == NULL)
		return(paramErr);
	
	// get the current node ID
	myNodeID = QTVRGetCurrentNodeID(theInstance);
	
	// get the type of the hot spot, given its ID
	QTVRUtils_GetHotSpotType(theInstance, myNodeID, theHotSpotID, &myType);
	
	// walk the linked list and find any commands for this hot spot
	myPointer = (VRScriptAtMOHSPtr)(**myAppData).fListArray[kVREntry_MouseOverHS];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		
		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
			if (((myPointer->fSelectByID) && (myPointer->fHotSpotID == theHotSpotID))	// hot spot is specified by ID
			|| ((!myPointer->fSelectByID) && (myPointer->fHotSpotType == myType)))		// hot spot is specified by type
				if (myPointer->fOptions == theFlags) {
					VRScript_ProcessScriptCommandLine(myWindowObject, myPointer->fCommandLine);
					VRScript_CheckForExpiredCommand(myWindowObject, (VRScriptGenericPtr)myPointer);
				}
	
		myPointer = myNext;
	}

	return(noErr);		// returning noErr means QTVR should do its normal mouse-over-hot-spot stuff
}


//////////
//
// VRScript_MoviePrePrerollCompleteProc
// A completion procedure for pre-prerolling movies.
//
//////////

PASCAL_RTN void VRScript_MoviePrePrerollCompleteProc (Movie theMovie, OSErr thePrerollErr, void *theRefcon)
{
#pragma unused(thePrerollErr, theRefcon)
	StartMovie(theMovie);
}


//////////
//
// VRScript_CheckForTimedCommands
// Walk through our linked list of timed commands and see whether any need to be executed.
//
//////////

void VRScript_CheckForTimedCommands (WindowObject theWindowObject)
{
	ApplicationDataHdl		myAppData;
	UInt32					myNodeID;
	VRScriptAtTimePtr		myPointer;
	VRScriptAtTimePtr		myNext;
	Boolean					myRunCommand;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
	
	// walk the linked list and find any commands ready to be executed
	myPointer = (VRScriptAtTimePtr)(**myAppData).fListArray[kVREntry_TimedCommand];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		myRunCommand = false;

		switch (myPointer->fMode) {
			case kVRUseNodeTime:
				if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
					myRunCommand = (gNodeElapsedTime >= myPointer->fTime);
				break;
			case kVRUseAbsoluteTime:
				myRunCommand = (gAbsoluteElapsedTime >= myPointer->fTime);
				break;
			case kVRUseInstallTime:
				myRunCommand = ((gAbsoluteElapsedTime - myPointer->fTimeInstalled) >= myPointer->fTime);
				break;
		}

		if (myRunCommand) {
			VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
			if (myPointer->fRepeat) {
				myPointer->fTime += myPointer->fPeriod;
				VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
			} else {
				VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
			}
		}
		
		myPointer = myNext;
	}
}


//////////
//
// VRScript_CheckForClickCustomButtonCommands
// Walk through our linked list of custom-button-click commands and see whether any need to be executed.
//
//////////

void VRScript_CheckForClickCustomButtonCommands (WindowObject theWindowObject, EventRecord *theEvent)
{
	ApplicationDataHdl		myAppData;
	UInt32					myNodeID;
	VRScriptClickCustomPtr	myPointer = NULL;
	VRScriptClickCustomPtr	myNext = NULL;
	UInt32					myStartTicks = theEvent->when;
	UInt32					myElapsedTime = 0;
	UInt32					myIgnoredTime;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
	
	// walk the custom-button-click command linked list and execute any that apply to this node
	myPointer = (VRScriptClickCustomPtr)(**myAppData).fListArray[kVREntry_ClickCustom];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;
		
		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode)) {
			VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
			VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
		}
	
		myPointer = myNext;
	}
	
	// we want the button to be depressed for at least kMyButtonDelay ticks, so (perhaps) let's wait before returning
	myElapsedTime = TickCount() - myStartTicks;
	if (myElapsedTime < kMyButtonDelay)
		Delay(kMyButtonDelay - myElapsedTime, &myIgnoredTime);
}


//////////
//
// VRScript_CheckForClickSpriteCommands
// Determine whether a mouse click is on a sprite; if it is, then execute any commands
// enlisted for that sprite; return true if any command was executed, false otherwise.
//
// This routine is intended to be called from your movie controller action filter function,
// in response to mcActionMouseDown actions.
//
//////////

Boolean VRScript_CheckForClickSpriteCommands (WindowObject theWindowObject, EventRecord *theEvent)
{
	ApplicationDataHdl		myAppData;
	MediaHandler			myHandler = NULL;
	Boolean					isHandled = false;
	long					myFlags = 0L;
	QTAtomID				myAtomID = 0;
	Point					myPoint;
	UInt32					myNodeID;
	VRScriptClickSpritePtr	myPointer = NULL;
	VRScriptClickSpritePtr	myNext = NULL;
	ComponentResult			myErr = noErr;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		goto bail;

	if (theEvent == NULL)
		goto bail;
		
	myHandler = (**myAppData).fSpriteHandler;
	myFlags = spriteHitTestImage | spriteHitTestLocInDisplayCoordinates | spriteHitTestInvisibleSprites;
	myPoint = theEvent->where;
	myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
	
	myErr = SpriteMediaHitTestAllSprites(myHandler, myFlags, myPoint, &myAtomID);
	if ((myErr == noErr) && (myAtomID != 0)) {
		// the user has clicked on a sprite;
		// walk the sprite-click command linked list and execute any that apply to that sprite in this node
		myPointer = (VRScriptClickSpritePtr)(**myAppData).fListArray[kVREntry_ClickSprite];
		while (myPointer != NULL) {
			myNext = myPointer->fNextEntry;
			
			if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode)) {
				if ((myPointer->fOptions == (UInt32)myAtomID) || (myPointer->fOptions == kVRAnySprite)) {
					VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
					VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
					isHandled = true;
				}
			}
		
			myPointer = myNext;
		}
	}
	
bail:
	return(isHandled);
}


//////////
//
// VRScript_CheckForWiredActionCommands
// Execute any commands enlisted for a wired action.
// Return false if any file-based actions should be executed, true otherwise.
//
// This routine is intended to be called from your movie controller action filter function,
// in response to mcActionExecuteAllActionsForQTEvent actions.
//
//////////

Boolean VRScript_CheckForWiredActionCommands (WindowObject theWindowObject, ResolvedQTEventSpec *theEvent)
{
	ApplicationDataHdl		myAppData = NULL;
	Boolean					myCancelAction = false;
	QTAtomContainer			myContainer = NULL;
	QTAtom					myAtom;
	QTAtomType				myAtomType;
	QTAtomID				myAtomID = 0;
	UInt32					myNodeID;
	UInt32					myItemID = 0;
	VRScriptWiredActionPtr	myPointer = NULL;
	VRScriptWiredActionPtr	myNext = NULL;
	OSType					myType = 0;
	OSErr					myErr = noErr;
	
	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		goto bail;

	if (theEvent == NULL)
		goto bail;
		
	myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);
	
	// determine the type of the wired action passed to us
	myContainer = theEvent->actionAtom.container;
	myAtom = theEvent->actionAtom.atom;
	
	myErr = QTGetAtomTypeAndID(myContainer, myAtom, &myAtomType, &myAtomID);
	if (myErr != noErr)
		goto bail;
	
	if (myAtomType == kQTEventFrameLoaded)
		myType = (OSType)myAtomType;
	else if (myAtomType == kQTEventType)
		myType = (OSType)myAtomID;
	
	// get the sprite ID or the hot spot ID of the triggering item
	if (myAtomType == kQTEventFrameLoaded)
		myItemID = kVRAnyItemID;
	else if (myAtomType == kQTEventType)
		myItemID = theEvent->targetRefCon;		// this works for sprites, but not for VR hot spots (this should soon be fixed)
			
	if ((myErr == noErr) && (myType != 0)) {
		// the user has triggered a wired action;
		// walk the wired action command linked list and execute any that apply to the triggering item in this node
		myPointer = (VRScriptWiredActionPtr)(**myAppData).fListArray[kVREntry_WiredAction];
		while (myPointer != NULL) {
			myNext = myPointer->fNextEntry;
			
			if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode)) {
				if ((myPointer->fID == myItemID) || (myPointer->fID == kVRAnyItemID)) {
					if ((myPointer->fEventType == myType) || (myPointer->fEventType == kVRAnyEvent)) {
						VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
						if (myPointer->fOptions != kVRAction_Allow)
							myCancelAction = true;
						VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
					}
				}
			}
		
			myPointer = myNext;
		}
	}
	
bail:
	return(myCancelAction);
}


//////////
//
// VRScript_CheckForAngleCommands
// Walk through our linked lists of angle-triggered commands and see whether any need to be executed.
//
//////////

void VRScript_CheckForAngleCommands (WindowObject theWindowObject)
{
	ApplicationDataHdl		myAppData;
	VRScriptAtAnglePtr		myNext;
	VRScriptAtAnglePtr		myPointer;
	UInt32					myNodeID;
	float					myPan;
	float					myTilt;
	float					myFOV;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;
		
	// get the current environment
	myPan = QTVRGetPanAngle((**theWindowObject).fInstance);
	myTilt = QTVRGetTiltAngle((**theWindowObject).fInstance);
	myFOV = QTVRGetFieldOfView((**theWindowObject).fInstance);
	myNodeID = QTVRGetCurrentNodeID((**theWindowObject).fInstance);

	// walk the pan angle linked list
	myPointer = (VRScriptAtAnglePtr)(**myAppData).fListArray[kVREntry_PanAngleCmd];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;

		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
			if (myPointer->fMinAngle <= myPan)
				if (myPointer->fMaxAngle >= myPan) {
					VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
					VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
				}
		myPointer = myNext;
	}

	// walk the tilt angle linked list
	myPointer = (VRScriptAtAnglePtr)(**myAppData).fListArray[kVREntry_TiltAngleCmd];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;

		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
			if (myPointer->fMinAngle <= myTilt)
				if (myPointer->fMaxAngle >= myTilt) {
					VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
					VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
				}
		myPointer = myNext;
	}

	// walk the zoom angle linked list
	myPointer = (VRScriptAtAnglePtr)(**myAppData).fListArray[kVREntry_FOVAngleCmd];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;

		if ((myPointer->fNodeID == myNodeID) || (myPointer->fNodeID == kVRAnyNode))
			if (myPointer->fMinAngle <= myFOV)
				if (myPointer->fMaxAngle >= myFOV) {
					VRScript_ProcessScriptCommandLine(theWindowObject, myPointer->fCommandLine);
					VRScript_CheckForExpiredCommand(theWindowObject, (VRScriptGenericPtr)myPointer);
				}
		myPointer = myNext;
	}

}


//////////
//
// VRScript_CheckForExpiredCommand
// See whether the specified command entry has expired.
//
//////////

void VRScript_CheckForExpiredCommand (WindowObject theWindowObject, VRScriptGenericPtr thePointer)
{
	if (thePointer->fMaxExecutions != kVRDoIt_Forever)
		thePointer->fMaxExecutions--;
	if (thePointer->fMaxExecutions == 0)
		VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)thePointer);
}


//////////
//
// VRScript_DumpUnexpiredCommands
// Dump any unexpired AtTime commands, if the list entry so indicates.
// This is called only when leaving a node, to removed unexecuted ("orphaned") commands
// which would be executed the next time the user entered the node.
//
//////////

void VRScript_DumpUnexpiredCommands (WindowObject theWindowObject, UInt32 theNodeID)
{
	ApplicationDataHdl		myAppData;
	VRScriptAtTimePtr		myPointer;
	VRScriptAtTimePtr		myNext;

	myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
	if (myAppData == NULL)
		return;

	// walk the linked list of timed commands and find any commands to be removed from list
	myPointer = (VRScriptAtTimePtr)(**myAppData).fListArray[kVREntry_TimedCommand];
	while (myPointer != NULL) {
		myNext = myPointer->fNextEntry;

		if (myPointer->fNodeID == theNodeID)
			if (myPointer->fOptions == kVROrphan_Kill)
				VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
		
		myPointer = myNext;
	}
}


//////////
//
// VRScript_PackString
// Pack a string.
//
//////////

void VRScript_PackString (char *theString)
{
	short		myLength;
	short		myIndex;
	
	myLength = strlen(theString);
	for (myIndex = 0; myIndex < myLength; myIndex++)
		if (isspace(theString[myIndex]))
			theString[myIndex] = '#';
}


//////////
//
// VRScript_UnpackString
// Unpack a string (for instance, to make a packed command line suitable for parsing by VRScript_ProcessScriptCommandLine).
//
// We allow embedding commands in other commands: '*' -> '&' -> '%' -> '#' -> ' '
//
//////////

void VRScript_UnpackString (char *theString)
{
	short		myLength;
	short		myIndex;
	
	myLength = strlen(theString);
	for (myIndex = 0; myIndex < myLength; myIndex++) {
		if (theString[myIndex] == '#')
			theString[myIndex] = ' ';
		if (theString[myIndex] == '%')
			theString[myIndex] = '#';
		if (theString[myIndex] == '&')
			theString[myIndex] = '%';
		if (theString[myIndex] == '*')
			theString[myIndex] = '&';
	}
}


//////////
//
// VRScript_DecodeString
// Decode a string, assumed to have been encoded using a simple rotate-11 scheme.
//
//////////

void VRScript_DecodeString (char *theString)
{
	short		myLength;
	short		myIndex;
	
	myLength = strlen(theString);
	for (myIndex = 0; myIndex < myLength; myIndex++)
		if (theString[myIndex] != '\n')
			theString[myIndex] = theString[myIndex] - 11;
}


//////////
//
// VRScript_StringToOSType
// Convert a string to an OSType.
//
//////////

OSType VRScript_StringToOSType (char *theString)
{
	unsigned long		myType = 0L;
	
	if (strlen(theString) < kMaxOSTypeLength - 1)
		return((OSType)myType);
		
	myType += theString[3] << 0;
	myType += theString[2] << 8;
	myType += theString[1] << 16;
	myType += theString[0] << 24;
	
	return((OSType)myType);
}


//////////
//
// VRScript_OSTypeToString
// Convert an OSType to a string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

char *VRScript_OSTypeToString (OSType theType)
{
	char			*myString = malloc(kMaxOSTypeLength);
	
	if (myString == NULL)
		return(NULL);
		
	myString[0] = (theType & 0xff000000) >> 24;
	myString[1] = (theType & 0x00ff0000) >> 16;
	myString[2] = (theType & 0x0000ff00) >> 8;
	myString[3] = (theType & 0x000000ff) >> 0;
	myString[4] = '\0';
	
	return(myString);
}


//////////
//
// VRScript_FloatsAreEqual
// Are the specified floating-point numbers equal, within a specified tolerance?
//
//////////

Boolean VRScript_FloatsAreEqual (float theFloat1, float theFloat2, float theTolerance)
{
	Boolean			areEqual = false;
	float			myDifference;
	
	myDifference = theFloat1 - theFloat2;
	if (myDifference < 0.0)
		myDifference *= -1.0;
	
	if (myDifference <= theTolerance)
		areEqual = true;
	
	return(areEqual);
}


