///////////////////////////////////////////////////////////
//
//  IMPLEMENTATION
//
//  CONTEXT: Genesis3D lightweight framework
//  MODULE:	 Genesis3D wrapper (service)
//  PREREQ:	 mainviewcontroller.h 
//  FILE:	 genewrap.cpp
//	AUTHOR:  Klaus Zerbe <klaus@zerbe.com>
//  ORIGIN:  http://klaus.zerbe.com
//  VERSION: 1.0
//  DATE:    3.2.1999
//  PURPOSE: encapsulate Genesis engine details
//
///////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//MALLWALKER:  A demonstration of simulated actor behavior programming with
// the Eclipse Genesis 3D Engine and the Genewalker application framework.
//
//	by JJ Robinson II
//  mkia1@geocities.com
//	www.geocities.com/mkia1/
////////////////////////////////////////////////////////////////////////////////

#include <stdafx.h>
#include <assert.h>
#include <mmsystem.h>
#include "resource.h"
#include "mci.h"

////////////////////////////////////////////////////////////////////////////////
//
// abstract entity meta class
//
// This manages a list of subclass instances which are used as templates 
// (meta classes) for all kinds of entities (lights, actors, Artefacts, doors etc.)
// 
////////////////////////////////////////////////////////////////////////////////

// entity template list head and tail
CEntityTemplate* CEntityTemplate::m_pFirst=NULL;
CEntityTemplate* CEntityTemplate::m_pLast=NULL;

// register new entity meta class
CEntityTemplate::CEntityTemplate(CEngineWrapper *pEngine)
{
	// store often used engine wrapper items
	m_pWrapper= pEngine; 
	m_pEngine= pEngine->GetEngine();
	m_pWorld= pEngine->GetWorld();
	m_pContext= pEngine->GetContext();
	// store new metaclass instance in list
	m_pNext= NULL;
	if (!m_pFirst) m_pFirst= this;
	if (m_pLast) m_pLast->m_pNext= this;
	m_pLast= this;
}

// remove metaclass instance from list
CEntityTemplate::~CEntityTemplate()
{
	CEntityTemplate *p;
	if (!m_pFirst) return;
	for (p=m_pFirst; p && (p->m_pNext != this); p=p->m_pNext);
	if (m_pFirst == this) m_pFirst= this->m_pNext;
	if (m_pLast == this) m_pLast= p;
	if (!p) return;
	p->m_pNext= this->m_pNext;
}

// clear all metaclasses from list
void CEntityTemplate::ShutDown()
{
	CEntityTemplate *p,*q;
	for (p=m_pFirst; p; p=q) {
		q= p->m_pNext;
		delete p;
	}
}

// draw all entities, given camera transformation (at player position) and time
void CEntityTemplate::RenderAll(geXForm3d *pViewPoint, DWORD  dwTime)
{
	CEntityTemplate *p;
	for (p=m_pFirst; p; p= p->m_pNext) {
		p->Render(pViewPoint, dwTime);
	}
}

// handle collisions with all entities
void CEntityTemplate::HandleCollision(geWorld_Model *pModel)
{
	CEntityTemplate *p;
	for (p=m_pFirst; p; p= p->m_pNext) {
		p->DoCollision(pModel);
	}
}

// create alpha channelled Genesis bitmap out of two Windows BMP files
// accessible in directory pBaseFS
geBitmap* CEntityTemplate::CreateAlphaBitmap(const geVFile * pBaseFS, const char * pBmpName, 
	const char *pAlphaName)
{
	geBitmap *pBmp= NULL, *pAlphaBmp= NULL;
	try {
		// create  main bitmap
		pBmp = geBitmap_CreateFromFileName(pBaseFS, pBmpName);
		if (!pBmp) throw 0;
		// create alpha bitmap
		pAlphaBmp = geBitmap_CreateFromFileName(pBaseFS, pAlphaName);
		if (!pAlphaBmp) throw 0;
		// combine them
		if (!geBitmap_SetAlpha(pBmp,pAlphaBmp)) throw 0;
		return pBmp;
	}
	catch (int)	{
		// something went wrong (not found?)
		if (pBmp) geBitmap_Destroy(&pBmp);
		if (pAlphaBmp) geBitmap_Destroy(&pAlphaBmp);
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Genesis3D Engine - Wrapper Class
//
// encapsulates all Genesis3D groundwork
//
////////////////////////////////////////////////////////////////////////////////

CEngineWrapper::CEngineWrapper(CMainViewController *pView)
	: m_pView(pView)
{
	// init all pointers or get hanged!
	m_pEngine= NULL;
	m_pCamera= NULL;
	m_pWorld= NULL;
	m_pLevel= NULL;
	m_pSoundSys= NULL;
	// prepare camera
	geXForm3d_SetIdentity(&m_xfmCamera);
	*m_szArtworkFolder='\0';
	m_bRendering= false;
}


// start engine with artwork in pVirtualFile on level pLevel
// pVirtualFile can be a DOS-directory or a VFS generated Hollywood3D virtual file
void CEngineWrapper::Startup(char *pVirtualFile, char *pLevel)
{
	static char szExePath[MAX_PATH], *p;
	geDriver *pDriver;
	geDriver_Mode *pDrvMode;
	RECT rc;
	m_bRendering= false; // block rendering
	m_mousemode= off; // no current control function
	// find application directory: Genesis- and driver-DLLs should be there
	GetModuleFileName(m_pView->GetInstance(), szExePath, MAX_PATH);
	p= strrchr(szExePath, '\\');
	if (p) szExePath[p-szExePath]= '\0';
	// create engine instance, pass view handle and driver directory
	m_pEngine= geEngine_Create(m_pView->GetHandle(), m_pView->GetCaption(), szExePath);
	if (!m_pEngine) throw "can't start engine";
	// find best driver and mode for rendering to window
	if (!FindBestWindowedDriver(&pDriver, &pDrvMode)) 
		throw "no usable graphics driver found";
	// turn of framerate display by default
	SetFramerateCount(false);
	// load and start drivers
	if (!geEngine_SetDriverAndMode(m_pEngine, pDriver, pDrvMode))
		throw "can't start graphics driver";
	// init window to use for camera
	GetClientRect(m_pView->GetHandle(), &rc);
	m_rcCamera.Left= m_rcCamera.Top= 0;
	m_rcCamera.Right= rc.right;
	m_rcCamera.Bottom= rc.bottom;
	// create camera
	m_pCamera= geCamera_Create(2.0f, &m_rcCamera);
	if (!m_pCamera) throw "can't create camera";
	geXForm3d_SetIdentity(&m_xfmCamera);
	// create artwork directory
	strlwr(pVirtualFile);
	// is a Hollywood3D virtual file system (vfs-file) used?
	p= strstr(pVirtualFile, ".vfs"); 
	if (p) {
		// open directory as virtual file system file (vfs file)
		m_pVirtual= geVFile_OpenNewSystem(NULL, GE_VFILE_TYPE_DOS, pVirtualFile, NULL, GE_VFILE_OPEN_READONLY);
		if (!m_pVirtual) throw "can't find game data file";
		m_pLevel= geVFile_OpenNewSystem(m_pVirtual, GE_VFILE_TYPE_VIRTUAL, NULL, NULL, 
			GE_VFILE_OPEN_READONLY | GE_VFILE_OPEN_DIRECTORY);
	} else {
		// open directory as regular DOS directory
		m_pVirtual= NULL;
		m_pLevel= geVFile_OpenNewSystem(NULL, GE_VFILE_TYPE_DOS, pVirtualFile, NULL, 
			GE_VFILE_OPEN_READONLY | GE_VFILE_OPEN_DIRECTORY);
	}
	if (!m_pLevel) throw "can't find game directory";
	// load level
	geVFile *pWorldFile= geVFile_Open(m_pLevel, pLevel, GE_VFILE_OPEN_READONLY);
	if (!pWorldFile) throw "can't find requested level";
	m_pWorld= geWorld_Create(pWorldFile);
	geVFile_Close(pWorldFile);
	if (!m_pWorld) throw "can't load level";
	// connect world to engine
	if (geEngine_AddWorld(m_pEngine, m_pWorld)== GE_FALSE)
		throw "engine cannot use this level";
	// start render timer
	m_dwLastRenderTime= timeGetTime();
	// get player entity
	bool bHasPlayer= false;
	// existiert PlayerStart Entity?
	geEntity_EntitySet *pSet = geWorld_GetEntitySet(m_pWorld, "PlayerStart");
	if (pSet) {
		// set player data
		geEntity *pPlayer= geEntity_EntitySetGetNextEntity(pSet, NULL);
		// "playerstart" entity found?
		if (pPlayer) {
			bHasPlayer= true;
			// get player data
			PlayerStart *pStart= (PlayerStart*)geEntity_GetUserData(pPlayer);
			// assign given values
			m_pos= pStart->Origin;
			m_mins= pStart->mins;
			m_max= pStart->maxs;
			m_headup= pStart->orientation.X;
			m_heading= pStart->orientation.Y;
			m_speed= pStart->speed;
			m_rotSpeed= pStart->rotspeed;
		}
	}
	if (!bHasPlayer) { // no playerstart entity?
		// use default values
		m_heading= m_headup= 0.0f;
		m_speed= 0.3f;
		m_rotSpeed= 0.1f;
		geVec3d_Set(&m_pos, 0.0f, 0.0f, 0.0f);
		geVec3d_Set(&m_mins, -40.0f, -100.0f, -40.0f);
		geVec3d_Set(&m_max, 40.0f, 60.0f, 40.0f);
	}
	// reset camera to player position
	CameraReset();
	// init sound system
	m_pSoundSys= geSound_CreateSoundSystem(m_pView->GetHandle());
}

// shutdown engine and all dependend stuff
void CEngineWrapper::Shutdown()
{
	// stop rendering
	m_pView->SetReady(false);
	// destroy all entities
	CEntityTemplate::ShutDown();
	// stop sound system
	if (m_pSoundSys) geSound_DestroySoundSystem(m_pSoundSys);
	// drop world
	if (m_pWorld) {
		geWorld_Free(m_pWorld);
		m_pWorld= NULL;
	}
	// destroy camera
	if (m_pCamera) geCamera_Destroy(&m_pCamera);
	// free engine
	if (m_pEngine) {
		geEngine_Free(m_pEngine);
		m_pEngine= NULL;
	}
	// close file system
	if (m_pLevel) {
		geVFile_Close(m_pLevel);
		m_pLevel= NULL;
	}
	if (m_pVirtual) {
		geVFile_Close(m_pVirtual);
		m_pVirtual= NULL;
	}
}

// frame rate count on/off
void CEngineWrapper::SetFramerateCount(bool bFramerateCnt)
{
	if (m_pEngine)
		geEngine_EnableFrameRateCounter(m_pEngine, bFramerateCnt);
}

// draw a frame
void CEngineWrapper::RenderFrame()
{
	// avoid "recurse" calls
	if (m_bRendering) return;
	// get current time
	DWORD dwNewTime= timeGetTime();
	// avoid  rendering to often, slowing down the system
	if (dwNewTime - m_dwLastRenderTime < 40) {
		Sleep(40);
		return;
	}
	// now render
	m_bRendering= true;
	// adjust camera
	TransformCamera(dwNewTime);
	// render all entities
	CEntityTemplate::RenderAll(&m_xfmCamera, dwNewTime);
	// draw next frame
	if (geEngine_BeginFrame(m_pEngine, m_pCamera, GE_TRUE)) {
		if (geEngine_RenderWorld(m_pEngine, m_pWorld, m_pCamera, float(dwNewTime-m_dwLastRenderTime))) {
			if (!geEngine_EndFrame(m_pEngine)) throw "error in: geEngine_EndFrame";
		} else throw "error in: geEngine_RenderWorld";
	} else throw "error in: geEngine_BeginFrame";
	// remember render time
	m_dwLastRenderTime= dwNewTime;
	// now finished
	m_bRendering= false;
}


// look for the best (accelerated) driver for rendering into a window;
// worst case will be software renderer
bool CEngineWrapper::FindBestWindowedDriver(geDriver **pBestDriver, geDriver_Mode **pBestMode)
{
	geDriver_System *pDriverSystem;
	geDriver *pDrv; 
	geDriver_Mode *pMode; 
	assert(m_pEngine);
	*pBestDriver= NULL;
	*pBestMode= NULL;	
	// init driver system
	pDriverSystem= geEngine_GetDriverSystem(m_pEngine);
	if (!pDriverSystem) throw "driver system not accessible";
	// enumerate drivers
	for (pDrv= geDriver_SystemGetNextDriver(pDriverSystem, NULL); pDrv;
	  pDrv= geDriver_SystemGetNextDriver(pDriverSystem, pDrv)) {
		const char *pDriverName;
		// get driver name
		geDriver_GetName(pDrv, &pDriverName);
		// enumerate modes for driver
		for (pMode= geDriver_GetNextMode(pDrv, NULL); pMode; 
		  pMode= geDriver_GetNextMode(pDrv, pMode)) {
		  	const char *pModeName;
		  	// get mode name
		  	geDriver_ModeGetName(pMode, &pModeName);
		  	// output in VC++ debug window
		  	OutputDebugString(pModeName);
		  	OutputDebugString("\r\n");
		  	// try to find window mode, prefer accelerated driver
		  	if (!strnicmp(pModeName, "Window", 6)) {
		  		bool bSoft= strnicmp(pDriverName, "Soft", 4)==0;
		  		if (!bSoft || (bSoft && !*pBestDriver)) {
		  			// take over driver and mode
		  			*pBestDriver= pDrv;
		  			*pBestMode= pMode;
				} 
		  	}
		}
	}
	if (!(pBestDriver && pBestMode)) return false; // nothing found
	return true;
}

// reset camera to player position
void CEngineWrapper::CameraReset()
{
	geXForm3d_SetTranslation(&m_xfmCamera, m_pos.X, m_pos.Y, m_pos.Z);
}

// check for world collision when moving delta
bool CEngineWrapper::CheckPosition(geVec3d* delta)
{
	geVec3d pos;
	GE_Contents contents;
	pos= m_xfmCamera.Translation;
	// compute position after move
	geVec3d_Add(&pos, delta, &pos);
	// check  for collision with world or world models (eg. doors)
	if (!geWorld_GetContents(m_pWorld, &pos, &m_mins, &m_max, GE_COLLIDE_MODELS, 0, 
		NULL, NULL, &contents)) return true;
	// final position inside something?
	if (contents.Contents & GE_CONTENTS_SOLID) return false;
	return true;
}

// check for movement by delta possible, try to step up,
// fall down if "hanging in air"
//
bool CEngineWrapper::CanMove(geVec3d *delta)
{
	const float step= 40.0f; // max. step height
	geVec3d from, to;
	GE_Collision col, col2;
	geBoolean b;
	from= m_xfmCamera.Translation;
	to= from;
	// compute final position
	geVec3d_Add(&from, delta, &to);
	// banged into any polygon model (actor)?
	b= geWorld_Collision(m_pWorld, &m_mins, &m_max, &from, &to, GE_CONTENTS_SOLID_CLIP, 
		GE_COLLIDE_ACTORS, 0xffffffff, NULL, NULL, &col);
	// if so, don't move
	if (b) return false;
	// banged into level architecture (walls, doors, stairs)?
	b= geWorld_Collision(m_pWorld, &m_mins, &m_max, &from, &to, GE_CONTENTS_SOLID_CLIP, 
		GE_COLLIDE_MODELS, 0xffffffff, NULL, NULL, &col);
	if (b) {
		// for world models handled by entities, handle collision
		if (col.Model) {
			CEntityTemplate::HandleCollision(col.Model);
		}
		// assuming a step/ramp try climbing up
		to.Y+= step; delta->Y+= step;
	} else {
		// no collision, but maybe no floor under the feet
		geVec3d vec= *delta;
		// try to fall down
		to.Y-= step; vec.Y-= step;
		// floor reached?
		if (!geWorld_Collision(m_pWorld, &m_mins, &m_max, &from, &to, GE_CONTENTS_SOLID_CLIP, 
			GE_COLLIDE_MODELS, 0, NULL, NULL, &col2)) *delta= vec;
	}
	// check final position
	return CheckPosition(delta);
}

// position camera for frame at dwTime
void CEngineWrapper::TransformCamera(DWORD dwTime)
{
	// get time since last rendered
	int t= dwTime- m_dwLastRenderTime;
	float angle=0, dx, dz;
	geVec3d delta, trans= m_xfmCamera.Translation;
	delta.X= delta.Y= delta.Z= 0;
	// compute rotation
	angle= DegreesToRadians(m_heading);
	// compute position offsets
	dx= m_speed *t * (float)sin(angle);
	dz= m_speed *t * (float)cos(angle);
	// do navigation function
	switch (m_mousemode) {
		case lookdown: 
			m_headup -= m_rotSpeed *t;
			break;
		case lookup: 
			m_headup += m_rotSpeed *t;
			break;
		case lookStraight: 
			m_heading = 0;
			break;
		case left: 
			m_heading += m_rotSpeed * t;
			break;
		case right: 
			m_heading -= m_rotSpeed * t;
			break;
		case fore:	
			delta.X= -dx; delta.Z = -dz;
			break;
		case back: 
			delta.X= dx; delta.Z= dz;
			break;
		case strLeft: // strafe left
			delta.X= -dz; delta.Z= dx;
			break;
		case strRight: // strafe right
			delta.X= dz; delta.Z= -dx;
			break;
	}
	// set camera orientation 
	geXForm3d_SetXRotation(&m_xfmCamera, DegreesToRadians(m_headup));
	geXForm3d_RotateY(&m_xfmCamera, angle);
	// set camera position
	m_xfmCamera.Translation= trans;
	// try to move
	if (CanMove(&delta)) geXForm3d_Translate(&m_xfmCamera, delta.X, delta.Y, delta.Z);
	// set camera transform
	geCamera_SetWorldSpaceXForm(m_pCamera, &m_xfmCamera);
}

// get current navigation function and cursor icon to use
MouseMode CEngineWrapper::GetMouseMode(int *idc)
{
	POINT pt;
	RECT rc;
	int dx,dy;
	// mapping of "screen zone" to navigation function
	static MouseMode modes[3][3]= {
		{strLeft, lookup, strRight}, 
		{left, lookStraight, right },
		{back, lookdown, fore },
	};
	// mapping of "screen zone" to cursor icon
	static int idcs[3][3]= {
		{IDC_LEFT, IDC_TURNUP, IDC_RIGHT},
		{IDC_TURNLEFT, IDC_CENTER, IDC_TURNRIGHT},
		{IDC_DOWN, IDC_TURNDOWN, IDC_UP},
	};
	// get cursor position
	HWND hwnd= m_pView->GetHandle();
	GetCursorPos(&pt);
	ScreenToClient(hwnd, &pt);
	GetClientRect(hwnd, &rc);
	// cursor in client area?
	if (!PtInRect(&rc, pt)) {
		*idc=-1; return off;
	}
	// compute "screen zone"
    dx= pt.x / (rc.right/3);
    dy= pt.y / (rc.bottom/3);
    // get cursor icon
    *idc= idcs[dy][dx];
    // return navigation function
    return modes[dy][dx];
}

// place cursor
void CEngineWrapper::SetCursor()
{
	int idCursor;
	HINSTANCE hInst= m_pView->GetInstance();
	// get cursor icon
	GetMouseMode(&idCursor);
	// place cursor with right icon
	if (idCursor!=-1) ::SetCursor(LoadCursor(hInst, MAKEINTRESOURCE(idCursor)));
}

////////////////////////////////////////////////////////////////////////////////
//
// general meta class for most passive world objects (polygon models)
// supports rotation around a given axis and a predefined animation and sound
//
////////////////////////////////////////////////////////////////////////////////

// head and tail for actor definition list
CActorDefEntry* CActorDefEntry::m_pFirst= NULL;
CActorDefEntry* CActorDefEntry::m_pLast= NULL;

// create actor definition
CActorDefEntry::CActorDefEntry(char *pName, geActor_Def *pActorDef)
{
	strcpy(m_szName, pName);
	m_pActorDef= pActorDef;
	m_pNext= NULL;
}

// add actor definition to list
void CActorDefEntry::Add(char *pName, geActor_Def *pActorDef)
{
	CActorDefEntry *p= new CActorDefEntry(pName, pActorDef);
	if (!m_pFirst) m_pFirst= p;
	if (m_pLast) m_pLast->m_pNext= p;
	m_pLast= p;
}

// try to find actor definition pName in List
// return it or  NULL
geActor_Def* CActorDefEntry::Find(char *pName)
{
	CActorDefEntry *p;
	for (p=m_pFirst; p; p=p->m_pNext) {
		if (strcmp(pName, p->m_szName)==0) 
			return p->m_pActorDef; // found
	}
	// not found
	return NULL;
}

// clear list
void CActorDefEntry::Clear()
{
	CActorDefEntry *p,*q;
	for (p=m_pFirst; p; p=q) {
		q= p->m_pNext;
		delete p;
	}
}

////////////////////////////////////////////////////////////////////////////////

// create Artefact (polygon model) support
CArtefact::CArtefact(CEngineWrapper *pEngine) : CEntityTemplate(pEngine)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
	geVFile *pActorFile;
	geActor_Def *pActorDef;
	Artefact *pArtefact;

 	if	(!m_pWorld) throw "no world"; 
	try {
		// are there any "Artefact" entities?
		pSet = geWorld_GetEntitySet(m_pWorld, "Artefact");
		if	(!pSet) return;
		// enumerate them
		for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	  	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) {
			// get Artefact data
			pArtefact= (Artefact*)geEntity_GetUserData(pEntity);
			// actor defintion already present?
			pActorDef= CActorDefEntry::Find(pArtefact->actorName);
			if (!pActorDef) {
				// no, so load the actor file now
				pActorFile= geVFile_Open(m_pContext, pArtefact->actorName, GE_VFILE_ATTRIB_READONLY);
				if (!pActorFile) throw "1]can't find actor file";
				pActorDef= geActor_DefCreateFromFile(pActorFile);
				if (!pActorDef) throw "2]can't load actor file";
				// enter new actor definition
				CActorDefEntry::Add(pArtefact->actorName, pActorDef);
				geVFile_Close(pActorFile);
			}
			// create new actor instance (Artefact) from that definition
			pArtefact->pActor= geActor_Create(pActorDef);
			pArtefact->pActorDef= pActorDef;
			pArtefact->lastTime=0.0f;
			if (!pArtefact->pActor) throw "can't create actor instance";
/*	geSound_Def *	pDefSoundDef;  // sound template
	geSound_Def *	pProxSoundDef;  // sound template
	geSound_Def *	pCollSoundDef;  // sound template

    char *			DefSound;		// default sound (WAV- file name)
    char *			ProxSound;		// proximity sound (WAV- file name)
    char *			CollSound;		// collision sound (WAV- file name)

	geSound *		pDefSound;  
	geSound *		pProxSound;  
	geSound *		pCollSound;  
*/
			// sound system available?
			geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
			if (pSndSys)
			{	//if more sounds are needed by an Actor instance, this will have to be cleaned up:JJ Robinson II
				if(pArtefact->DefSound) 
				{
					// get default sound file
					geVFile *pfile= geVFile_Open(m_pContext, pArtefact->DefSound,
						GE_VFILE_ATTRIB_READONLY);
					if (pfile) 
					{
					// load sound file
						pArtefact->pDefSoundDef= geSound_LoadSoundDef(pSndSys, pfile);
					// init sound
						if (pArtefact->pDefSoundDef) 
							pArtefact->pDefSound= geSound_PlaySoundDef(pSndSys, pArtefact->pDefSoundDef, 
												0.0, 0.0, 0.0, GE_TRUE);
						geVFile_Close(pfile);
					} 
					else	
					{
					// no sound
						pArtefact->pDefSoundDef= NULL;
						pArtefact->pDefSound= NULL;
					}
				}
				if(pArtefact->ProxSound) 
				{
					// get default sound file
					geVFile *pfile= geVFile_Open(m_pContext, pArtefact->ProxSound,
						GE_VFILE_ATTRIB_READONLY);
					if (pfile) 
					{
					// load sound file
						pArtefact->pProxSoundDef= geSound_LoadSoundDef(pSndSys, pfile);
					// init sound
//						if (pArtefact->pProxSoundDef) 
//							pArtefact->pProxSound= geSound_PlaySoundDef(pSndSys, pArtefact->pProxSoundDef, 
//												0.0, 0.0, 0.0, GE_FALSE);
						geVFile_Close(pfile);
					} 
					else	
					{
					// no sound
						pArtefact->pProxSoundDef= NULL;
						pArtefact->pProxSound= NULL;
					}
				}
				if(pArtefact->CollSound) 
				{
					// get default sound file
					geVFile *pfile= geVFile_Open(m_pContext, pArtefact->CollSound,
						GE_VFILE_ATTRIB_READONLY);
					if (pfile) 
					{
					// load sound file
						pArtefact->pCollSoundDef= geSound_LoadSoundDef(pSndSys, pfile);
						geVFile_Close(pfile);
					} 
					else	
					{
					// no sound
						pArtefact->pCollSoundDef= NULL;
						pArtefact->pCollSound= NULL;
					}
				}

			}
			// add Artefact to world, allowing collisions with player
			if (geWorld_AddActor(m_pWorld, pArtefact->pActor, GE_ACTOR_RENDER_NORMAL|GE_ACTOR_COLLIDE, 
			  0xffffffff)==GE_FALSE)
				throw "can't place actor";
		}	
	}
	catch (char *p) {
		// if something failed, remove all litter
		if (pArtefact->pActor) {
			geActor_Destroy(&pArtefact->pActor);
			pArtefact->pActor= NULL;
		}
		if (pActorDef) {
			geActor_DefDestroy(&pActorDef);
			pActorDef= NULL;
		}
		throw p;
	}
}

// destroy all Artefacts
CArtefact::~CArtefact()
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
	Artefact *pArtefact;
 	if	(!m_pWorld) throw "no world";
 	// any Artefacts there?
	pSet = geWorld_GetEntitySet(m_pWorld, "Artefact");
	if	(!pSet) return;
	// so enumerate them
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
  	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) 
	{
		pArtefact= (Artefact*)geEntity_GetUserData(pEntity);
		// remove Artefact
		if (pArtefact->pActor) geActor_Destroy(&pArtefact->pActor);
		pArtefact->pActor= NULL;
		// sound system up?
		geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
			if (pSndSys)
			{
				if(pArtefact->DefSound) 
				{
					geSound_StopSound(pSndSys, pArtefact->pDefSound);
				}			
				geSound_FreeSoundDef(pSndSys, pArtefact->pDefSoundDef);

				if(pArtefact->ProxSound) 
				{
					geSound_StopSound(pSndSys, pArtefact->pProxSound);
				}			
				geSound_FreeSoundDef(pSndSys, pArtefact->pProxSoundDef);

				if(pArtefact->CollSound) 
				{

					geSound_StopSound(pSndSys, pArtefact->pProxSound);
				}			
				geSound_FreeSoundDef(pSndSys, pArtefact->pProxSoundDef);

			}			// stop sound and free resources
	}		
	// clear all actor definitions
	CActorDefEntry::Clear();
}
//=====================================================================================
//Some automated actor behaviors:  JJ Robinson II
//=====================================================================================

//=====================================================================================
//  CArtefact::MoveToWaypoint:  JJ Robinson II
//	Compute the direction vector to the current waypoint target,
//  or, if this actor is within one frame's movement of it,
//  select a new waypoint.
//=====================================================================================
geVec3d CArtefact::MoveToWaypoint(Artefact *pArtefact)
{
	geEntity_EntitySet	*pSet;
	geEntity			*pEntity;
	geVec3d				WPDirection;
	geFloat				DistanceToWP;
	char				*WPName = "WayPoint1";
	WayPoint			*WP;
	if	(!m_pWorld) throw "no world";
	pSet = geWorld_GetEntitySet(m_pWorld, "WayPoint");
	if	(!pSet) throw "no waypoints";
 	//get distance between actor and current waypoint
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
  				pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) 			
	{					
		geEntity_GetName(pEntity, WPName, 15);
		WP = (WayPoint*)geEntity_GetUserData(pEntity); 		
		if (strcmp(WPName, pArtefact->CurrentWayPointName)==0)
		{			
			DistanceToWP = geVec3d_DistanceBetween( &(pArtefact->Origin),  &(WP->origin));
				//if zero, get next waypoint
			if(DistanceToWP < pArtefact->Speed)
			{
				pEntity = geEntity_EntitySetGetNextEntity(pSet, pEntity);
				//if no more waypoints, get first waypoint
				if(!pEntity)			
					pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL);//Get first WayPoint
				if(!pEntity)throw "can't find a waypoint";
				geEntity_GetName(pEntity, pArtefact->CurrentWayPointName, 15);//Make the new WP Name Current
				WP = (WayPoint*)geEntity_GetUserData(pEntity); 
			}
			break;
		}
	}
	//get vector from actor to waypoint		
	geVec3d_Subtract(&(WP->origin),&(pArtefact->Origin) , &WPDirection);
	geVec3d_Normalize(&WPDirection);
	//Scale to Speed
	geVec3d_Scale(&WPDirection, pArtefact->Speed, &WPDirection);

	return WPDirection;
}

//=====================================================================================
//	FaceToDirectionVector
//  Adapted from Bot_SetupXForm in Bot.c: JJ Robinson II
//=====================================================================================
geXForm3d CArtefact::FaceToDirectionVector(geVec3d *DirectionVector)
{
	geVec3d			InVect;
	geVec3d			LVect, UpVect = {0.0f,1.0f,0.0f} ;
	geXForm3d		XForm;
	InVect = *DirectionVector;
	//InVect.Y = 0.0f; // Make it a 2D vector 
    if (geVec3d_Length(&InVect) == 0.0f)
        {
		// This is hardly ever going to happen - but need to test for it
		geXForm3d_SetIdentity(&XForm);
        }
    else
        {
		geVec3d_Normalize(&InVect);
		geVec3d_Inverse(&InVect);

		geVec3d_CrossProduct(&InVect, &UpVect, &LVect);
		geVec3d_Normalize(&LVect);
		geVec3d_CrossProduct(&InVect, &LVect, &UpVect);
		//This has to come before other contributions to XForm:  JJ Robinson II
		geXForm3d_SetFromLeftUpIn(&XForm, &LVect, &UpVect, &InVect);
		}
	return XForm;
}

//=====================================================================================
//	FaceToPlayer
//  Face Actor toward Player: JJ Robinson II
//=====================================================================================

geXForm3d CArtefact::FaceToPlayer(Artefact *pArtefact, geXForm3d *pViewPoint)
{
	geVec3d			InVect;
	geVec3d			LVect, UpVect = {0.0f,1.0f,0.0f} ;
	geXForm3d XForm;
	geVec3d   ActorToPlayerVector;

	//get vector from actor to waypoint		
	geVec3d_Subtract(&(pArtefact->Origin), &(pViewPoint->Translation),&ActorToPlayerVector);
	geVec3d_Normalize(&ActorToPlayerVector);
	InVect = ActorToPlayerVector;
	InVect.Y = 0.0f; // Make it a 2D vector 
    if (geVec3d_Length(&InVect) == 0.0f)
    {
		geXForm3d_SetIdentity(&XForm);
    }
    else
        {
		geVec3d_Normalize(&InVect);
		geVec3d_CrossProduct(&InVect, &UpVect, &LVect);
		geVec3d_Normalize(&LVect);
		geVec3d_CrossProduct(&InVect, &LVect, &UpVect);
		geXForm3d_SetFromLeftUpIn(&XForm, &LVect, &UpVect, &InVect);
		}
	return XForm;
}

//=====================================================================================
//	DetectProximityToPlayer
//  Detect if Player is within action range of Actor: JJ Robinson II
//=====================================================================================
bool CArtefact::DetectProximityToPlayer(Artefact *pArtefact, geXForm3d *pViewPoint)
{
	geVec3d PlayerPosition, ActorPosition;
	ActorPosition = pArtefact->Origin;
	PlayerPosition = pViewPoint->Translation;

	ActorPosition.Y = 0.0f;
	PlayerPosition.Y = 0.0f;
	if(geVec3d_DistanceBetween( &(ActorPosition), &(PlayerPosition)) < pArtefact->ProximityActionRadius)
	return TRUE;
	return FALSE;
}

bool CArtefact::CanMove(Artefact *pArtefact, geXForm3d *pViewPoint, geVec3d *delta)
{
	geVec3d from, to;
	geVec3d mins, maxs;//, mins2, maxs2;
	GE_Collision Collision;
	GE_Contents Contents;
	//float  fx, fy, fz;
	mins.X =-50.0; mins.Y = -100.0; mins.Z = -50.0;	//Arbitrary bounding box
	maxs.X = 50.0; maxs.Y = 60.0; maxs.Z = 50.0;	//
	geBoolean b = false;
	from= pArtefact->Origin;
	//	geVec3d_Inverse(&to);

	to= from;
	// compute final position
	geVec3d_Add(&from, delta, &to);
	if	(!m_pWorld) throw "no world";
	//Check for actor collision with world geometry and each other:JJ Robinson II
	b= geWorld_Collision(m_pWorld, &mins, &maxs, &from, &to, GE_CONTENTS_CANNOT_OCCUPY,  
		GE_COLLIDE_MODELS|GE_COLLIDE_ACTORS, 0xffffffff, NULL, NULL, &Collision);	
	if(!b)					//Check for actor collision with Player
	{						//in the absence of a local Player actor:  JJ Robinson II
		if (geWorld_GetContents(m_pWorld, &(pViewPoint->Translation), 
			&mins, &maxs, GE_COLLIDE_ACTORS, 0xffffffff,  NULL, NULL, &Contents))
		{
			if (Contents.Actor == pArtefact->pActor)
			{//Collision with Player can currently occur with any actor facing:JJ Robinson II
						b = true;
			}
		}
	}
	// if so, don't move
	return (!b);
}

// render pArtefact
void CArtefact::Render(geXForm3d *pViewPoint, DWORD  dwTime)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
	geActor *pActor;
	bool bHasMotion;
	geVec3d	WPDirection;
	geSound * pSound;
/*
	geSound *		pDefSound;  // current default sound instance
	geSound *		pProxSound;  // current proximity sound instance
	geSound *		pCollSound;  // current collision sound instance
*/
 	if	(!m_pWorld) throw "no world";
 	// are Artefacts here?
	pSet = geWorld_GetEntitySet(m_pWorld, "Artefact");
	if	(!pSet) return;
	// so enumerate then
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) 
	{
		geVec3d pos, scale;
		geXForm3d xform;
		float motionTime, startTime, time/*, SndStTime, SndCrntTime*/;
		int rx,ry,rz;
		bool bHasCollided, bHasProximity;
		
		geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
		
		// get Artefact individual data
		Artefact *pArtefact= (Artefact*)geEntity_GetUserData(pEntity);
		scale= pArtefact->scale;
		pActor= pArtefact->pActor;
		// scale it
		geActor_SetScale(pActor, scale.X, scale.Y, scale.Z);
		pos= pArtefact->Origin;
		// get animation time
		time= pArtefact->lastTime= ((float)(dwTime - pArtefact->startTime))/1000.0f;

		bHasCollided = FALSE;
		bHasProximity = FALSE;

		// start with identity matrix
		geXForm3d_SetIdentity(&xform);
		//Translate toward current waypoint: JJ Robinson II
		if(pArtefact->hasWayPointTracking)	
		{
		//Compute direction vector to current waypoint, or find a new one: JJ Robinson II
			WPDirection = MoveToWaypoint(pArtefact);
			if(CanMove(pArtefact, pViewPoint, &WPDirection))  //Did the actor hit something?
			{
				pos.X += WPDirection.X;
				pos.Y += WPDirection.Y;
				pos.Z += WPDirection.Z;

				if(pArtefact->hasActOnProximity)
				{
					if(DetectProximityToPlayer(pArtefact, pViewPoint))
					{
						bHasProximity = TRUE;
					// play proximity sound, if any, at specified interval	
						pArtefact->SoundCurrentTime = (geFloat)(dwTime - pArtefact->SoundStartTime);
						if(pArtefact->SoundCurrentTime >= pArtefact->ProxSoundInterval)
						{	
							pArtefact->SoundStartTime = dwTime;	
							
							if (pArtefact->pProxSoundDef) 
								pArtefact->pProxSound= geSound_PlaySoundDef(pSndSys, 
									pArtefact->pProxSoundDef, 0.0, 0.0, 0.0, GE_FALSE);
						}
					}
				}
			}
			else		//Yes, there's been a collision
			{
				if(pArtefact->hasActOnCollision)
				{
					bHasCollided = TRUE;
				// play collision sound, if any, at specified interval	
					pArtefact->SoundCurrentTime = (geFloat)(dwTime - pArtefact->SoundStartTime);
					if(pArtefact->SoundCurrentTime >= pArtefact->CollSoundInterval)
					{	
						pArtefact->SoundStartTime = dwTime;	
						//geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
						if (pArtefact->pCollSoundDef) 
							pArtefact->pCollSound= geSound_PlaySoundDef(pSndSys, 
								pArtefact->pCollSoundDef, 0.0, 0.0, 0.0, GE_FALSE);

					}
				}
			}
			xform = FaceToDirectionVector(&WPDirection);
		}
		else		//Entity Orientation currently doesn't work with MoveToWaypoint: JJ Robinson II
		{
		  // compute orientation (as given)
		  rx= int(pArtefact->rotation.X * time); rx %= 360;
		  ry= int(pArtefact->rotation.Y * time); ry %= 360;
		  rz= int(pArtefact->rotation.Z * time); rz %= 360;
		  // rotate around any axes used
		  geXForm3d_RotateX(&xform, m_pWrapper->DegreesToRadians(rx + pArtefact->orientation.X));
		  geXForm3d_RotateY(&xform, m_pWrapper->DegreesToRadians(ry + pArtefact->orientation.Y));
		  geXForm3d_RotateZ(&xform, m_pWrapper->DegreesToRadians(rz + pArtefact->orientation.Z));		
		}
		//
		// position it
		//
		geXForm3d_Translate(&xform, pos.X, pos.Y, pos.Z);
		pArtefact->Origin = pos;
		bHasMotion= false;

//Check for predefined conditions such as proximity, collision, etc.
//Check for provisional actor motions on proximity, collision, etc.
//else  =>play the default animation as below: JJ Robinson II

		// collision animation requested?
		if (bHasCollided) 
		{
			pSound = pArtefact->pCollSound;
			if(pArtefact->CollMotion && *pArtefact->CollMotion)
			{
			// get requested motion
			  geMotion *pMotion= geActor_GetMotionByName(pArtefact->pActorDef, pArtefact->CollMotion);
			  if (pMotion)
			  {
				bHasMotion= true; // animation found
				// get start/end times of animation
				geMotion_GetTimeExtents(pMotion, &startTime, &motionTime);
				// after reaching endtime reset to start
				if (pArtefact->lastTime >= motionTime) 
				{
					pArtefact->lastTime= 0.0f;
					pArtefact->startTime= float(dwTime);
				}
				geActor_ClearPose(pActor, &xform);
				xform = FaceToPlayer(pArtefact, pViewPoint);
				geXForm3d_Translate(&xform, pos.X, pos.Y, pos.Z);
				// set animation frame transform
				geActor_SetPose(pArtefact->pActor, pMotion, pArtefact->lastTime, &xform);
			  }
			}
		}
		else //If no collision, then use default or proximity animation:JJ Robinson II
		{
			if(bHasProximity)
				pSound = pArtefact->pProxSound;
			else
				pSound = pArtefact->pDefSound;
				// default animation requested?
				if (pArtefact->DefMotion && *pArtefact->DefMotion) 
				{
				// get requested motion
					geMotion *pMotion= geActor_GetMotionByName(pArtefact->pActorDef, pArtefact->DefMotion);
					if (pMotion)
					{
						bHasMotion= true; // animation found
					// get start/end times of animation
						geMotion_GetTimeExtents(pMotion, &startTime, &motionTime);
					// after reaching endtime reset to start
						if (pArtefact->lastTime >= motionTime) 
						{
							pArtefact->lastTime= 0.0f;
							pArtefact->startTime= float(dwTime);
						}
					// set animation frame transform
						geActor_SetPose(pArtefact->pActor, pMotion, pArtefact->lastTime, &xform);
					} // end if
				} //end if
		}//end else
		// if no motion requested just pose actor
		if (!bHasMotion) geActor_ClearPose(pActor, &xform);
		// sound system available?
		//geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
		// any sound wanted?
		if (pSndSys && pSound) 
		{
			float	vol, pan, freq;
			// get 3D sound parameters for player position
			geSound3D_GetConfig(m_pWorld, pViewPoint, &pArtefact->Origin, 350.0f, 2.0f, 
				&vol, &pan, &freq);
			// modify sound matching for current player position
			geSound_ModifySound(pSndSys, pSound, vol, pan, freq);
		}

		if((geSound_SoundIsPlaying(pSndSys, pArtefact->pCollSound))||(geSound_SoundIsPlaying(pSndSys, pArtefact->pProxSound)))
		{
			geMotion *pMotion= geActor_GetMotionByName(pArtefact->pActorDef, pArtefact->ProxMotion);
			if (pMotion)
			{		
				geActor_BlendPose(pArtefact->pActor, pMotion, pArtefact->lastTime, 
					&xform, 0.3);

			}
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
//
// movable world parts (eg. doors)
//
////////////////////////////////////////////////////////////////////////////////

//
// so do just automatic doors here
//

CDoor::CDoor(CEngineWrapper *pEngine) : CEntityTemplate(pEngine)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
 	if	(!m_pWorld) throw "no world";
 	// has world doors?
	pSet = geWorld_GetEntitySet(m_pWorld, "Door");
	if	(!pSet) return;
	// sound system active
	geSound_System *pSndSys= pEngine->GetSoundSystem();
	if (pSndSys){
		// get door open sound
		geVFile *pfile= geVFile_Open(m_pContext, "DoorOpen.wav", GE_VFILE_OPEN_READONLY);
		if (pfile) { 
			// load sound file
			m_pSound= geSound_LoadSoundDef(pSndSys, pfile);
			geVFile_Close(pfile);
		} else m_pSound= NULL;
	}
	// enumerate doors
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) {
		Door *pDoor= (Door*)geEntity_GetUserData(pEntity);
		// reset animations for all doors
		pDoor->lastTime= pDoor->startTime= 0.0;
		pDoor->bInAnimation= false;
	}
}

CDoor::~CDoor()
{
	// free sound ressources
	geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
	if (pSndSys){
		if (m_pSound) geSound_FreeSoundDef(pSndSys, m_pSound);
	}
}

// render doors
void CDoor::Render(geXForm3d *pViewPoint, DWORD  dwTime)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
	geMotion *pMotion;
	gePath *pPath;
 	if	(!m_pWorld) throw "no world";
 	// has world doors?
	pSet = geWorld_GetEntitySet(m_pWorld, "Door");
	if	(!pSet) return;
	// enumerate doors
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) {
		float tStart, tEnd, time;
		// get individual door data
		Door *pDoor= (Door*)geEntity_GetUserData(pEntity);
		// get animation time in seconds
		time= pDoor->lastTime= ((float)(dwTime - pDoor->startTime))/1000.0f;
		// is there a world model to animate?
		if	(pDoor->Model) {
			geXForm3d xfmDest;
			// get motion data for world model
			pMotion= geWorld_ModelGetMotion(pDoor->Model);
			assert(pMotion);
			// get animation path
			pPath= geMotion_GetPath(pMotion, 0);
			assert(pPath);
			// got a door trigger (eg. due to collide)
			if (pDoor->bTrigger) {
				// set animation times
				pDoor->lastTime= 0.0f;
				pDoor->startTime= float(dwTime);
				pDoor->bTrigger= false;
				// start animation
				pDoor->bInAnimation= true;
				if (m_pSound) { // play door open sound
					float vol, pan, pitch;
					geSound_System *pSndSys= m_pWrapper->GetSoundSystem();
					// get 3D sound parameters respective to player position
					geSound3D_GetConfig(m_pWorld, pViewPoint, &pDoor->Origin, 600.0f, 2.0f, 
						&vol, &pan, &pitch);
					// start sound
					geSound_PlaySoundDef(pSndSys, m_pSound, vol, pan, pitch, GE_FALSE);
				}
			} else if (!pDoor->bInAnimation) continue; // door stays closed
			// get animation interval
			geMotion_GetTimeExtents(pMotion, &tStart, &tEnd);
			// mark animation finished, when done
			if (pDoor->lastTime >= tEnd) {
				pDoor->bInAnimation= false;
			}
			// transform world model for current frame
			gePath_Sample(pPath, time, &xfmDest);
			geWorld_SetModelXForm(m_pWorld, pDoor->Model, &xfmDest);
		} else throw "no model defined for door";
	}
}

// handle collision with door
// (used to trigger open door for automatic doors)
void CDoor::DoCollision(geWorld_Model *pModel)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
 	if	(!m_pWorld) throw "no world";
 	// has world doors?
	pSet = geWorld_GetEntitySet(m_pWorld, "Door");
	if	(!pSet) return;
	// enumerate doors
	for (pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL); pEntity;
	  pEntity= geEntity_EntitySetGetNextEntity(pSet, pEntity)) {
		// get individual door data
		Door *pDoor= (Door*)geEntity_GetUserData(pEntity);
		// if collided with this door, trigger it for open
		if (pDoor->Model == pModel && !pDoor->bInAnimation) {
			pDoor->bTrigger= true;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// musical world
//
////////////////////////////////////////////////////////////////////////////////

CMusic::CMusic(CEngineWrapper *pEngine) : CEntityTemplate(pEngine)
{
	geEntity_EntitySet *pSet;
	geEntity *pEntity;
 	if	(!m_pWorld) throw "no world";
 	// has world music?
	pSet = geWorld_GetEntitySet(m_pWorld, "Music");
	if	(!pSet) return;
	// only one music in each world!
	pEntity= geEntity_EntitySetGetNextEntity(pSet, NULL);
	if (!pEntity) return;
	Music *pMusic= (Music*)geEntity_GetUserData(pEntity);
	// if midi-file defined, play it
	if (pMusic->MidiFile) {
		// get player singleton
		CMCI_Player *pPlayer= CMCI_Player::GetMidiPlayer();
		if (pPlayer) {
			// just open and play it continously
			pPlayer->Open(pMusic->MidiFile);
			pPlayer->Play(true);
		}
	}
}

// stop music
CMusic::~CMusic()
{
	// get singleton player instance
	CMCI_Player *pPlayer= CMCI_Player::GetMidiPlayer();
	// stop player and close file/device
	pPlayer->Stop();
	pPlayer->Close();
}

/*FILE *out;
out = fopen("genewalker.err", "a+");
fprintf(out, "2]WPDirection.X = %f\n", WPDirection.X );
fprintf(out, "WPDirection.Y = %f\n", WPDirection.Y );
fprintf(out, "WPDirection.Z = %f\n", WPDirection.Z );
fclose(out);*/
