/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of Penumbra Overture.
 */

#include "hpl1/penumbra-overture/PlayerHelper.h"

#include "hpl1/penumbra-overture/DeathMenu.h"
#include "hpl1/penumbra-overture/GameArea.h"
#include "hpl1/penumbra-overture/GameEnemy.h"
#include "hpl1/penumbra-overture/GameEntity.h"
#include "hpl1/penumbra-overture/GameMusicHandler.h"
#include "hpl1/penumbra-overture/GameObject.h"
#include "hpl1/penumbra-overture/Init.h"
#include "hpl1/penumbra-overture/Inventory.h"
#include "hpl1/penumbra-overture/MapHandler.h"
#include "hpl1/penumbra-overture/Notebook.h"
#include "hpl1/penumbra-overture/NumericalPanel.h"
#include "hpl1/penumbra-overture/Player.h"
#include "hpl1/penumbra-overture/PlayerHands.h"

//////////////////////////////////////////////////////////////////////////
// HIT GROUND CALLBACK
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerBodyCallback::cPlayerBodyCallback(cPlayer *apPlayer) {
	mpPlayer = apPlayer;
	cInit *pInit = apPlayer->GetInit();

	mfFallSpeed_Min = pInit->mpGameConfig->GetFloat("Player", "FallSpeed_Min", 0);
	mfFallDamage_Min = pInit->mpGameConfig->GetFloat("Player", "FallDamage_Min", 0);
	msFallSound_Min = pInit->mpGameConfig->GetString("Player", "FallSound_Min", "");

	mfFallSpeed_Med = pInit->mpGameConfig->GetFloat("Player", "FallSpeed_Med", 0);
	mfFallDamage_Med = pInit->mpGameConfig->GetFloat("Player", "FallDamage_Med", 0);
	msFallSound_Med = pInit->mpGameConfig->GetString("Player", "FallSound_Med", "");

	mfFallSpeed_Max = pInit->mpGameConfig->GetFloat("Player", "FallSpeed_Max", 0);
	mfFallDamage_Max = pInit->mpGameConfig->GetFloat("Player", "FallDamage_Max", 0);
	msFallSound_Max = pInit->mpGameConfig->GetString("Player", "FallSound_Max", "");
}

//-----------------------------------------------------------------------

void cPlayerBodyCallback::OnHitGround(iCharacterBody *apCharBody, const cVector3f &avVel) {
	// if(avVel.y < -5.5f)
	//	mpPlayer->FootStep(1.0f,"run",true);

	if (mpPlayer->GetMoveState() == ePlayerMoveState_Jump) {
		if (avVel.y < 0) {
			if (avVel.y < -3.5f) {
				mpPlayer->FootStep(1.0f, "run", true);
			}
			mpPlayer->SetLandedFromJump(true);
		}
	} else if (avVel.y < -8.5f) {
		mpPlayer->FootStep(1.0f, "run", true);
	}

	tString sSound = "";
	float fVol = 1.0f;
	cSoundHandler *pSoundHandler = mpPlayer->GetInit()->mpGame->GetSound()->GetSoundHandler();
	// Log("Speed: %f\n",avVel.y);
	/////////////////////
	// Fall damage
	// Hard
	if (avVel.y < mfFallSpeed_Max) {
		sSound = msFallSound_Max;
		fVol = 1.0f;
		mpPlayer->Damage(mfFallDamage_Max, ePlayerDamageType_BloodSplash);
		pSoundHandler->PlayGui("player_fall_damage", false, 1);
		// Log("Hard\n");
	}
	// Medium
	else if (avVel.y < mfFallSpeed_Med) {
		sSound = msFallSound_Med;
		fVol = 1.0f;
		mpPlayer->Damage(mfFallDamage_Med, ePlayerDamageType_BloodSplash);
		pSoundHandler->PlayGui("player_fall_damage", false, 0.75f);
		// Log("Medium\n");
	}
	// Light
	else if (avVel.y < mfFallSpeed_Min) {
		sSound = msFallSound_Min;
		fVol = 1.0f;
		mpPlayer->Damage(mfFallDamage_Min, ePlayerDamageType_BloodSplash);
		pSoundHandler->PlayGui("player_fall_damage", false, 0.5f);
		// Log("Light\n");
	}

	if (sSound != "") {
		cWorld3D *pWorld = mpPlayer->GetInit()->mpGame->GetScene()->GetWorld3D();
		cSoundEntity *pSound = pWorld->CreateSoundEntity("HitGround", sSound, true);
		if (pSound) {
			iCharacterBody *pCharBody = mpPlayer->GetCharacterBody();
			pSound->SetVolume(fVol * pSound->GetVolume());
			pSound->SetPosition(pCharBody->GetPosition() - cVector3f(0, pCharBody->GetSize().y / 2 - 0.2f, 0));
		}
	}
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// GROUND RAY CALLBACK
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------

cPlayerGroundRayCallback::cPlayerGroundRayCallback() {
	mpMaterial = NULL;
	mbOnGround = false;
}

//-----------------------------------------------------------------------

void cPlayerGroundRayCallback::Clear() {
	mfMinDist = 1000;
	mpMaterial = NULL;
}

//-----------------------------------------------------------------------

void cPlayerGroundRayCallback::OnWorldExit() {
	mpMaterial = NULL;
}

void cPlayerGroundRayCallback::Reset() {
	mpMaterial = NULL;
}

//-----------------------------------------------------------------------

bool cPlayerGroundRayCallback::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
	mbOnGround = true;

	if (apParams->mfDist < mfMinDist && pBody->GetMaterial()->GetSurfaceData() &&
		pBody->GetMaterial()->GetSurfaceData()->GetStepType() != "") {
		mpMaterial = pBody->GetMaterial();
		mfMinDist = apParams->mfDist;
	}

	return true;
}

//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PICK RAY CALLBACK
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------

cPlayerPickRayCallback::cPlayerPickRayCallback() {
	mfMaxDistance = 10.0f;
	mpPickedBody = NULL;

	mLastEntityType = eGameEntityType_LastEnum;
}

//-----------------------------------------------------------------------

void cPlayerPickRayCallback::Clear() {
	mfPickedDist = mfMaxDistance + 10.0f;
	mfPickedAreaDist = mfMaxDistance + 10.0f;

	mpPickedBody = NULL;
	mpPickedAreaBody = NULL;

	mfMinStaticDist = mfMaxDistance + 10.0f;
}

//-----------------------------------------------------------------------

void cPlayerPickRayCallback::OnWorldExit() {
	mpPickedBody = NULL;
}

//-----------------------------------------------------------------------

bool cPlayerPickRayCallback::OnIntersect(iPhysicsBody *apBody, cPhysicsRayParams *apParams) {
	float &fDist = apParams->mfDist;

	// Must be positive
	if (fDist <= 0)
		return true;

	// Must be below max distance
	if (fDist > mfMaxDistance)
		return true;

	// No characters allowed
	if (apBody->IsCharacter())
		return true;

	// Check if is below current picked dist
	iGameEntity *pEntity = NULL;
	if (apBody->GetUserData())
		pEntity = (iGameEntity *)apBody->GetUserData();

	//////////////////////////////
	// Entity
	if (pEntity) {
		// Skip stick area
		if (pEntity->GetType() == eGameEntityType_StickArea)
			return true;

		// Area
		if (pEntity->GetType() == eGameEntityType_Area ||
			pEntity->GetType() == eGameEntityType_StickArea ||
			pEntity->GetType() == eGameEntityType_DamageArea ||
			pEntity->GetType() == eGameEntityType_ForceArea) {
			if (mfPickedAreaDist > fDist &&
				(pEntity->GetDescription() != _W("") ||
				 pEntity->GetCallbackScript(eGameEntityScriptType_PlayerInteract) != NULL ||
				 pEntity->GetCallbackScript(eGameEntityScriptType_PlayerExamine) != NULL ||
				 pEntity->GetHasInteraction())) {
				mfPickedAreaDist = fDist;
				mvPickedAreaPos = apParams->mvPoint;
				mpPickedAreaBody = apBody;
			}
		}
		// Other entity
		else {
			if (mfPickedDist > fDist) {
				mfPickedDist = fDist;
				mvPickedPos = apParams->mvPoint;
				mpPickedBody = apBody;
			}
		}
	}
	///////////////////////////////
	// Non Entity
	else {
		if (mfPickedDist > fDist) {
			mfPickedDist = fDist;
			mpPickedBody = NULL;
		}
	}

	return true;
}

void cPlayerPickRayCallback::CalculateResults() {
	// Check if an area is closer than the closest normal body
	if (mpPickedAreaBody && mfPickedAreaDist < mfPickedDist) {
		bool bUseArea = false;

		if (mpPickedBody) {
			iGameEntity *pEntity = (iGameEntity *)mpPickedBody->GetUserData();
			// cGameArea *pArea = (cGameArea *)mpPickedAreaBody->GetUserData();

			if (pEntity) {
				// Too for from object
				if (pEntity->GetMaxExamineDist() < mfPickedDist)
					bUseArea = true;

				// No description for object and it is not possible to interact with it
				else if (pEntity->GetDescription() == _W("")) {
					if (mpPickedBody->GetMass() == 0)
						bUseArea = true;

					else if (pEntity->GetType() == eGameEntityType_Object) {
						cGameObject *pObject = static_cast<cGameObject *>(pEntity);

						if (pObject->GetInteractMode() == eObjectInteractMode_Static)
							bUseArea = true;
					}
				}
			} else {
				bUseArea = true;
			}
		} else {
			bUseArea = true;
		}

		if (bUseArea) {
			mpPickedBody = mpPickedAreaBody;
			mfPickedDist = mfPickedAreaDist;
			mvPickedPos = mvPickedAreaPos;
		}
	}
}

//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// HEAD MOVE
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerHeadMove::cPlayerHeadMove(cPlayer *apPlayer) {
	mfAdd = 0;
	mfPos = 0;
	mbActive = false;
	mpPlayer = apPlayer;

	mbFootStepped = false;
	mbWasActive = false;

	mfClimbCount = 0;
	mfClimbPos = 0;
}

//-----------------------------------------------------------------------

void cPlayerHeadMove::Update(float afTimeStep) {
	///////////////////////////////
	// Climb count
	iCharacterBody *pBody = mpPlayer->GetCharacterBody();
	/*float fUpVel = (pBody->GetPosition().y - pBody->GetLastPosition().y) / afTimeStep;*/

	if (pBody->IsClimbing() && mfClimbCount == 0) {
		mfClimbCount = 0.5f;
		mfClimbPos = mfPos;
	} else if (mfClimbCount > 0) {
		mfClimbCount -= afTimeStep;
		if (mfClimbCount < 0) {
			mfClimbCount = 0;
			mfPos = mfClimbPos;
			mfAdd = mfHeadMoveSpeed;
		}
	}

	///////////////////////////////
	// Head movement
	if (mbActive) {
		mbWasActive = true;

		mfPos += mfAdd * afTimeStep * mpPlayer->GetHeadMoveSpeedMul();
		if (mfAdd < 0) {
			if (mfPos <= mfMinHeadMove * mpPlayer->GetHeadMoveSpeedMul()) {
				mfPos = mfMinHeadMove * mpPlayer->GetHeadMoveSpeedMul();
				mfAdd = mfHeadMoveSpeed;

				// Create a footstep.
				if (mfFootStepMul > 0)
					mpPlayer->FootStep(mfFootStepMul);
				mbFootStepped = true;
			}
		} else {
			if (mfPos >= mfMaxHeadMove * mpPlayer->GetHeadMoveSpeedMul()) {
				mfPos = mfMaxHeadMove * mpPlayer->GetHeadMoveSpeedMul();
				mfAdd = -mfHeadMoveSpeed;
			}
		}
	} else {
		if (mbWasActive == true && (mbFootStepped == false || (mfPos < 0 && mfAdd < 0))) {
			if (mfFootStepMul > 0)
				mpPlayer->FootStep(mfFootStepMul * 0.6f);
		}
		mbFootStepped = false;
		mbWasActive = false;

		if (mfPos == 0)
			return;

		if (mfPos < 0) {
			mfPos += mfHeadMoveBackSpeed * afTimeStep;
			if (mfPos > 0) {
				mfPos = 0;
			}
		} else {
			mfPos -= mfHeadMoveBackSpeed * afTimeStep;
			if (mfPos < 0) {
				mfPos = 0;
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerHeadMove::Start() {
	if (mbActive == false) {
		mbActive = true;
	}

	if (fabs(fabs(mfAdd) - mfHeadMoveSpeed) > 0.001f) {
		mfAdd = mfHeadMoveSpeed;
	}
}

void cPlayerHeadMove::Stop() {
	mbActive = false;
}
//-----------------------------------------------------------------------

float cPlayerHeadMove::GetPos() {
	if (mfClimbCount > 0)
		return mfClimbPos;

	return mfPos;
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// HEALTH
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerHealth::cPlayerHealth(cInit *apInit) {
	mpInit = apInit;
	mfTimeCount = 0;

	mpDrawer = mpInit->mpGame->GetGraphics()->GetDrawer();

	mpDamageGfx = mpDrawer->CreateGfxObject("player_hurt.bmp", "diffalpha2d");

	mfGfxAlpha = 0;
	mfGfxAlphaAdd = 0;

	mfGfxGlobalAlpha = 0;

	mfTerrorCheckCount = 0;

	mpSoundEntry = NULL;
}

//-----------------------------------------------------------------------

void cPlayerHealth::Reset() {
	mfTerrorCheckCount = 0;
	if (mpSoundEntry)
		mpSoundEntry->mpSound->Stop();
	mpSoundEntry = NULL;

	mlTerrorLevel = 0;
}

//-----------------------------------------------------------------------

void cPlayerHealth::Update(float afTimeStep) {
	cPlayer *pPlayer = mpInit->mpPlayer;

	if (pPlayer->GetHealth() <= 0) {
		if (mpSoundEntry)
			mpSoundEntry->mpSound->Stop();
		mpSoundEntry = NULL;

		mfGfxAlpha -= afTimeStep * 2.0f;
		return;
	}

	////////////////////////////
	// ALpha
	mfGfxAlpha += mfGfxAlphaAdd * afTimeStep;
	if (mfGfxAlphaAdd < 0 && mfGfxAlpha < 0)
		mfGfxAlpha = 0;
	if (mfGfxAlphaAdd > 0 && mfGfxAlpha >= 1.0f) {
		mfGfxAlpha = 1.0f;
		mfGfxAlphaAdd = -1.5;
	}

	////////////////////////////
	// Heart timer
	if (mfTimeCount <= 0) {
		if (pPlayer->GetHealth() > 50) {
			pPlayer->SetHealthSpeedMul(1.0f);
			mfGfxGlobalAlpha = 0;
		} else {
			float fVolume = 0.5;
			if (pPlayer->GetHealth() > 25) {
				mfTimeCount = 1.7f;
				pPlayer->SetHealthSpeedMul(0.8f);
				mfGfxGlobalAlpha = 0.5f;
			} else if (pPlayer->GetHealth() > 10) {
				mfTimeCount = 1.0;
				fVolume = 0.75f;
				pPlayer->SetHealthSpeedMul(0.6f);
				mfGfxGlobalAlpha = 0.75f;
			} else {
				mfTimeCount = 0.7f;
				fVolume = 1.0f;
				pPlayer->SetHealthSpeedMul(0.4f);
				mfGfxGlobalAlpha = 1.0f;
			}

			mpInit->mpGame->GetSound()->GetSoundHandler()->PlayGui("player_heartbeat", false, fVolume);
			mfGfxAlphaAdd = 3.5f;
		}
	} else {
		mfTimeCount -= afTimeStep;
	}

	////////////////////////////
	// Increase health over time:
	switch (mpInit->mDifficulty) {
	case eGameDifficulty_Easy:
		pPlayer->AddHealth(afTimeStep * 1.4f);
		break;
	case eGameDifficulty_Normal:
		pPlayer->AddHealth(afTimeStep * 0.4f);
		break;
	case eGameDifficulty_Hard:
		pPlayer->AddHealth(afTimeStep * 0.14f);
		break;
	case eGameDifficulty_LastEnum:
		break;
	}
}

void cPlayerHealth::Draw() {
	if (mfGfxAlpha <= 0)
		return;

	mpDrawer->DrawGfxObject(mpDamageGfx, cVector3f(0, 0, 50), cVector2f(800, 600),
							cColor(1, mfGfxGlobalAlpha * mfGfxAlpha));
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// EAR RING
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerEarRing::cPlayerEarRing(cInit *apInit, cPlayer *apPlayer) {
	mpInit = apInit;
	mpPlayer = apPlayer;

	mpSoundEntry = NULL;
	mfTimeCount = 0;

	mbDeaf = false;
}

//-----------------------------------------------------------------------

void cPlayerEarRing::Reset() {
	cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

	if (mpSoundEntry)
		mpSoundEntry->mpSound->Stop();
	pSoundHandler->SetVolume(1.0f, 0.1f, eSoundDest_World);

	mpSoundEntry = NULL;
	mfTimeCount = 0;

	mbDeaf = false;
}

//-----------------------------------------------------------------------

void cPlayerEarRing::Update(float afTimeStep) {
	if (mfTimeCount > 0) {
		cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

		mfTimeCount -= afTimeStep;
		mfDeafTime -= afTimeStep;

		if (mbDeaf && mfDeafTime <= 0) {
			mbDeaf = false;
			pSoundHandler->SetVolume(0.2f, 1.5f, eSoundDest_World);
		}

		if (mfTimeCount <= 0) {
			mfTimeCount = 0;
			pSoundHandler->SetVolume(1.0f, 3.5f, eSoundDest_World);

			if (mpSoundEntry) {
				mpSoundEntry->mfNormalVolumeFadeDest = 0;
				mpSoundEntry->mfNormalVolumeFadeSpeed = -0.25f;
				mpSoundEntry = NULL;
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerEarRing::Start(float afTime) {
	if (mpInit->mpPlayer->GetHealth() <= 0)
		return;

	if (mfTimeCount > 0) {
	} else {
		cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

		iSoundChannel *pChannel = pSoundHandler->PlayGui("player_ear_ring", true, 0.5f);
		pChannel->SetPriority(200);
		mpSoundEntry = pSoundHandler->GetEntryFromSound(pChannel);

		mpSoundEntry->mfNormalVolumeMul = 0;
		mpSoundEntry->mfNormalVolumeFadeDest = 1;
		mpSoundEntry->mfNormalVolumeFadeSpeed = 0.55f;

		pSoundHandler->SetVolume(0.0f, 1.2f, eSoundDest_World);
	}

	mfTimeCount = afTime;

	mbDeaf = true;
	mfDeafTime = afTime * 0.3f;
}

//-----------------------------------------------------------------------

void cPlayerEarRing::Stop(bool abStopDirectly) {
	cSoundHandler *pSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

	if (abStopDirectly) {
		if (mpSoundEntry)
			mpSoundEntry->mpSound->Stop();
		mpSoundEntry = NULL;

		pSoundHandler->SetVolume(1, 5, eSoundDest_World);
	} else {
		mfTimeCount = 0;
		pSoundHandler->SetVolume(1.0f, 3.5f, eSoundDest_World);

		if (mpSoundEntry) {
			mpSoundEntry->mfNormalVolumeFadeDest = 0;
			mpSoundEntry->mfNormalVolumeFadeSpeed = -0.25f;
			mpSoundEntry = NULL;
		}
	}
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// LEAN
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerLean::cPlayerLean(cInit *apInit, cPlayer *apPlayer) {
	mpPlayer = apPlayer;
	mpInit = apInit;

	mfMaxMovement = 0.5f;
	mfMaxRotation = cMath::ToRad(15);

	mpHeadShape = NULL;
}

void cPlayerLean::Reset() {
	mfDir = 0;
	mfMaxTime = 0.8f;
	mfMovement = 0;
	mfRotation = 0;

	mfMoveSpeed = 0;

	mbPressed = false;
}

//-----------------------------------------------------------------------

void cPlayerLean::OnWorldLoad() {
	iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();

	float fRadius = mpInit->mpPlayer->GetSize().x / 2 * 0.63f;
	float fHeight = mpInit->mpPlayer->GetCameraHeightAdd() * 2;
	if (fHeight < 0)
		fHeight = fHeight * -1;
	cMatrixf mtxOffset = cMath::MatrixRotateZ(kPi2f);
	mpHeadShape = pPhysicsWorld->CreateCylinderShape(fRadius, fHeight, &mtxOffset);
}

//-----------------------------------------------------------------------

void cPlayerLean::Update(float afTimeStep) {
	////////////////////////////////
	// If pressed move in direction
	if (mbPressed) {
		mbPressed = false;

		float fGoalPos = mfMaxMovement * mfDir;
		float fGoalRot = mfMaxRotation * -mfDir;

		//////////////
		// Position
		float fPrevMovement = mfMovement;
		float fMoveSpeed = (fGoalPos - mfMovement);
		if (fabsf(fMoveSpeed) < 0.1f)
			fMoveSpeed = 0.1f * mfDir;
		mfMovement += fMoveSpeed * afTimeStep * 3;

		if (fGoalPos < 0 && mfMovement < fGoalPos)
			mfMovement = fGoalPos;
		if (fGoalPos > 0 && mfMovement > fGoalPos)
			mfMovement = fGoalPos;

		//////////////
		// Rotation
		float fPrevRotation = mfRotation;
		float fRotSpeed = fGoalRot - mfRotation;
		if (fabsf(fRotSpeed) < 0.13f)
			fRotSpeed = 0.13f * -mfDir;

		mfRotation += fRotSpeed * afTimeStep * 2;

		if (fGoalRot < 0 && mfRotation < fGoalRot)
			mfRotation = fGoalRot;
		if (fGoalRot > 0 && mfRotation > fGoalRot)
			mfRotation = fGoalRot;

		////////////////////
		// Check collision
		cCamera3D *pCam = mpInit->mpPlayer->GetCamera();
		iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();

		float fReverseMov = fPrevMovement - mfMovement;
		float fReverseRot = fPrevRotation - mfRotation;

		iCharacterBody *pCharBody = mpPlayer->GetCharacterBody();
		float fHeightAdd = mpPlayer->GetSize().y + mpPlayer->GetHeightAdd() + mpPlayer->GetCameraHeightAdd();
		cVector3f vStartPos = pCharBody->GetFeetPosition() + cVector3f(0, fHeightAdd, 0);

		cVector3f vPos = vStartPos + pCam->GetRight() * mfMovement;

		int lCount = 0;
		while (pPhysicsWorld->CheckShapeWorldCollision(NULL, mpHeadShape,
													   cMath::MatrixTranslate(vPos), NULL, false, true, NULL, false)) {
			mfMovement += fReverseMov;
			mfRotation += fReverseRot;

			if (fReverseMov < 0 && mfMovement < 0) {
				mfMovement = 0;
				mfRotation = 0;
				break;
			}
			if (fReverseMov > 0 && mfMovement > 0) {
				mfMovement = 0;
				mfRotation = 0;
				break;
			}

			vPos = vStartPos + pCam->GetRight() * mfMovement;
			lCount++;
			if (lCount > 10) {
				mfMovement = 0;
				mfRotation = 0;
				break;
			}
		}
	}
	////////////////////////////
	// Not pressed move back
	else {
		float fMoveSpeed = (0 - mfMovement);
		mfMovement += fMoveSpeed * afTimeStep * 6;

		float fRotSpeed = 0 - mfRotation;
		mfRotation += fRotSpeed * afTimeStep * 3;
	}

	mpPlayer->GetCamera()->AddRoll(mfRotation);
}

//-----------------------------------------------------------------------

bool cPlayerLean::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
	if (pBody == mpPlayer->GetCharacterBody()->GetBody())
		return true;

	mbIntersect = true;
	return false;
}

//-----------------------------------------------------------------------

void cPlayerLean::Lean(float afMul, float afTimeStep) {
	mbPressed = true;
	mfDir = afMul;
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// DAMAGE
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerDamage::cPlayerDamage(cInit *apInit) {
	mpInit = apInit;
	mpDrawer = mpInit->mpGame->GetGraphics()->GetDrawer();
	mpPostEffects = mpInit->mpGame->GetGraphics()->GetRendererPostEffects();

	mbActive = false;

	mType = ePlayerDamageType_BloodSplash;

	mvHitGfx[ePlayerDamageType_BloodSplash] = mpDrawer->CreateGfxObject("player_hit_blood_splash.bmp", "diffalpha2d");
	mvHitGfx[ePlayerDamageType_Ice] = mpDrawer->CreateGfxObject("player_hit_ice.bmp", "diffalpha2d");

	mvHeadSwingAcc = cVector2f(17.0f, 17.0f);
}

cPlayerDamage::~cPlayerDamage() {
	for (int i = 0; i < ePlayerDamageType_LastEnum; ++i)
		mpDrawer->DestroyGfxObject(mvHitGfx[i]);
}

//-----------------------------------------------------------------------

void cPlayerDamage::Start(float afSize, ePlayerDamageType aType) {
	if (mbActive)
		return;

	mfSize = afSize;

	mType = aType;

	mfAlpha = 0;
	mfAlphaAdd = 4.3f;

	mbActive = true;

	if (!mpInit->mpPlayer->GetFearFilter()->IsActive()) {
		mpPostEffects->SetImageTrailActive(true);
		mpPostEffects->SetImageTrailAmount(0);
	}

	mvHeadSwingSpeed = cVector2f(cMath::RandRectf(-1, 1), cMath::RandRectf(0, 0.5f));
	if (mvHeadSwingSpeed.x == 0 && mvHeadSwingSpeed.y == 0)
		mvHeadSwingSpeed.x = 1;
	else
		mvHeadSwingSpeed.Normalise();

	mvHeadSwingSpeed = mvHeadSwingSpeed * afSize * 1.5f;

	mpInit->mpPlayer->GetCharacterBody()->SetMoveSpeed(eCharDir_Forward, 0);
	mpInit->mpPlayer->GetCharacterBody()->SetMoveSpeed(eCharDir_Right, 0);
}

//-----------------------------------------------------------------------

void cPlayerDamage::Update(float afTimeStep) {
	if (mbActive == false)
		return;

	// Alpha update
	mfAlpha += mfAlphaAdd * afTimeStep;
	if (mfAlphaAdd > 0) {
		if (mfAlpha >= 1) {
			mfAlpha = 1;
			mfAlphaAdd = -2.2f;
		}
	} else if (mfAlphaAdd < 0) {
		if (mfAlpha <= 0) {
			mfAlpha = 0;
		}
	}

	// Update swing
	mpInit->mpPlayer->GetCamera()->AddYaw(mvHeadSwingSpeed.x * afTimeStep);
	mpInit->mpPlayer->GetCamera()->AddPitch(mvHeadSwingSpeed.y * afTimeStep);

	if (mvHeadSwingSpeed.x > 0) {
		mvHeadSwingSpeed.x -= (mvHeadSwingAcc.x / mfSize) * afTimeStep * 2.2f;
		if (mvHeadSwingSpeed.x < 0)
			mvHeadSwingSpeed.x = 0;
	} else {
		mvHeadSwingSpeed.x += (mvHeadSwingAcc.x / mfSize) * afTimeStep * 2.2f;
		if (mvHeadSwingSpeed.x > 0)
			mvHeadSwingSpeed.x = 0;
	}
	if (mvHeadSwingSpeed.y > 0) {
		mvHeadSwingSpeed.y -= (mvHeadSwingAcc.x / mfSize) * afTimeStep * 2.2f;
		if (mvHeadSwingSpeed.y < 0)
			mvHeadSwingSpeed.y = 0;
	} else {
		mvHeadSwingSpeed.y += (mvHeadSwingAcc.x / mfSize) * afTimeStep * 2.2f;
		if (mvHeadSwingSpeed.y > 0)
			mvHeadSwingSpeed.y = 0;
	}

	// Blur update
	if (mpInit->mpPlayer->IsDead() == false) {
		if (!mpInit->mpPlayer->GetFearFilter()->IsActive()) {
			mpPostEffects->SetImageTrailAmount(mfAlpha * 0.92f);
		}
	}

	// Has it ended?
	if (mvHeadSwingSpeed.x == 0 && mvHeadSwingSpeed.y == 0 && mfAlpha == 0) {
		mbActive = false;
		if (mpInit->mpPlayer->IsDead() == false) {
			if (!mpInit->mpPlayer->GetFearFilter()->IsActive()) {
				mpPostEffects->SetImageTrailActive(false);
				mpPostEffects->SetImageTrailAmount(0);
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerDamage::Draw() {
	if (mbActive == false)
		return;

	mpDrawer->DrawGfxObject(mvHitGfx[mType], cVector3f(0, 0, 3), cVector2f(800, 600), cColor(1, mfAlpha * 0.6f));
}

//-----------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////////
// DEATH
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerDeath::cPlayerDeath(cInit *apInit) {
	mpInit = apInit;
	mpDrawer = mpInit->mpGame->GetGraphics()->GetDrawer();
	mpPostEffects = mpInit->mpGame->GetGraphics()->GetRendererPostEffects();

	mpFadeGfx = mpDrawer->CreateGfxObject("player_death_fade.bmp", "smoke2d");
	mpBlackGfx = mpDrawer->CreateGfxObject("player_death_black.bmp", "smoke2d");
}

cPlayerDeath::~cPlayerDeath() {
	mpDrawer->DestroyGfxObject(mpFadeGfx);
}

//-----------------------------------------------------------------------

void cPlayerDeath::Reset() {
	mbActive = false;

	mfHeightAdd = 0;
	mfRoll = 0;

	mpPostEffects->SetImageTrailActive(false);
}

//-----------------------------------------------------------------------

void cPlayerDeath::Start() {
	if (mbActive)
		return;

	if (mpInit->mpInventory->IsActive())
		mpInit->mpInventory->SetActive(false);
	if (mpInit->mpNotebook->IsActive())
		mpInit->mpNotebook->SetActive(false);
	if (mpInit->mpNumericalPanel->IsActive())
		mpInit->mpNumericalPanel->SetActive(false);
	mpInit->mpPlayer->GetFlashLight()->SetActive(false);
	mpInit->mpPlayer->GetGlowStick()->SetActive(false);
	mpInit->mpPlayer->GetFlare()->SetActive(false);
	mpInit->mpPlayerHands->SetCurrentModel(0, "");
	mpInit->mpPlayerHands->SetCurrentModel(1, "");

	mpInit->mpPlayer->ChangeState(ePlayerState_Normal);

	mfHeightAdd = 0;
	mfRoll = 0;

	mfMinHeightAdd = -1.3f;
	if (mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
		mfMinHeightAdd = -0.7f;

	mbActive = true;
	mbStartFade = false;

	mfFadeAlpha = 0;
	mfBlackAlpha = 0;

	mpPostEffects->SetImageTrailActive(true);
	mpPostEffects->SetImageTrailAmount(0.7f);

	mpInit->mpPlayer->GetEarRing()->Stop(false);

	for (int i = 0; i <= 10; ++i) {
		mpInit->mpMusicHandler->Stop(0.2f, i);
	}
}

//-----------------------------------------------------------------------

void cPlayerDeath::Update(float afTimeStep) {
	if (mbActive == false)
		return;

	mfHeightAdd -= 0.95f * afTimeStep;
	if (mfHeightAdd < mfMinHeightAdd) {
		mfHeightAdd = mfMinHeightAdd;
		mbStartFade = true;
		mpInit->mpDeathMenu->SetActive(true);
	}

	mfRoll += cMath::ToRad(40.0f) * afTimeStep;
	if (mfRoll > cMath::ToRad(65.0f))
		mfRoll = cMath::ToRad(65.0f);

	mpInit->mpPlayer->GetCamera()->AddRoll(mfRoll);

	if (mbStartFade) {
		mfFadeAlpha += 0.7f * afTimeStep;
		if (mfFadeAlpha > 1)
			mfFadeAlpha = 1;

		if (mfFadeAlpha > 0.6f) {
			mfBlackAlpha += 0.45f * afTimeStep;
			if (mfBlackAlpha > 1)
				mfBlackAlpha = 1;
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerDeath::Draw() {
	if (mbActive == false)
		return;

	cVector3f vPos(-mfFadeAlpha * 400 - mfBlackAlpha * 200, -mfFadeAlpha * 400 - mfBlackAlpha * 200, 4);
	cVector2f vSize(800 + mfFadeAlpha * 800 + mfBlackAlpha * 400, 600 + mfFadeAlpha * 800 + mfBlackAlpha * 400);
	mpDrawer->DrawGfxObject(mpFadeGfx, vPos, vSize, cColor(mfFadeAlpha, 0));

	mpDrawer->DrawGfxObject(mpBlackGfx, cVector3f(0, 0, 5), cVector2f(800, 600), cColor(mfBlackAlpha, 0));
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// FLASHLIGHT
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerFlashLight::cPlayerFlashLight(cInit *apInit) {
	mpInit = apInit;

	mpInit->mpPlayerHands->AddModelFromFile("hud_object_flashlight.hud");

	Reset();
}

//-----------------------------------------------------------------------

cPlayerFlashLight::~cPlayerFlashLight() {
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::Reset() {
	mbActive = false;
	mfAlpha = 0;
	mfFlickerTime = 0;

	mfRayCastTime = 0;
	mpClosestBody = NULL;
	mfClosestDist = 10000.0f;

	mbDisabled = false;
}

//-----------------------------------------------------------------------

bool cPlayerFlashLight::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
	if (pBody->GetCollide() == false)
		return true;
	if (pBody->IsCharacter() && pBody == mpInit->mpPlayer->GetCharacterBody()->GetBody())
		return true;

	if (apParams->mfDist < mfClosestDist) {
		mfClosestDist = apParams->mfDist;
		mpClosestBody = pBody;
	}

	return true;
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::OnWorldLoad() {
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::Update(float afTimeStep) {
	if (mbActive) {
		//////////////////////////
		// Hits enemy
		if (mfRayCastTime >= 1) // 1.0f/20.0f)
		{
			mfRayCastTime = 0;

			cCamera3D *pCam = mpInit->mpPlayer->GetCamera();
			iPhysicsWorld *pPhysicsWorld = mpInit->mpGame->GetScene()->GetWorld3D()->GetPhysicsWorld();

			iHudModel *pHudModel = mpInit->mpPlayerHands->GetModel("Flashlight");

			cVector3f vForward = pCam->GetForward();
			if (pHudModel && pHudModel->GetEntity()) {
				vForward = cMath::MatrixMul(
					pHudModel->GetEntity()->GetWorldMatrix().GetRotation(),
					cVector3f(0, -1, 0));
			}

			if (pHudModel == mpInit->mpPlayerHands->GetCurrentModel(0)) {
				tGameEnemyIterator it = mpInit->mpMapHandler->GetGameEnemyIterator();
				while (it.HasNext()) {
					iGameEnemy *pEnemy = it.Next();

					if (pEnemy->GetHealth() <= 0 || pEnemy->IsActive() == false)
						continue;

					cVector3f vToEnemy = pEnemy->GetMover()->GetCharBody()->GetPosition() -
										 mpInit->mpPlayer->GetCharacterBody()->GetPosition();
					float fSqrDist = vToEnemy.SqrLength();
					vToEnemy.Normalise();

					if (fSqrDist < 100.0f) {
						float fAngle = cMath::Vector3Angle(vToEnemy, vForward);

						if (fAngle < cMath::ToRad(25)) {
							cVector3f vStart = pCam->GetPosition();
							cVector3f vEnd = pEnemy->GetMover()->GetCharBody()->GetPosition();

							mvStart = vStart;
							mvEnd = vEnd;

							mpClosestBody = NULL;
							mfClosestDist = 10000.0f;
							pPhysicsWorld->CastRay(this, vStart, vEnd, true, false, false, false);

							if (mpClosestBody == NULL || mpClosestBody->IsCharacter() == true) {
								if (pEnemy->GetUsesTriggers()) {
									pEnemy->OnFlashlight(pCam->GetPosition());
								}
							}
						}
					}
				}
			}
		} else {
			mfRayCastTime += afTimeStep;
		}
	}

	iHudModel *pHudModel = mpInit->mpPlayerHands->GetCurrentModel(0);

	//////////////////////////
	// Player power
	if (mbActive &&
		mpInit->mpInventory->IsActive() == false &&
		mpInit->mpNotebook->IsActive() == false &&
		mpInit->mpNumericalPanel->IsActive() == false) {
		mpInit->mpPlayer->AddPower(-0.1f * afTimeStep);
		if (mpInit->mpPlayer->GetPower() == 0) {
			SetActive(false);
		}
	}

	//////////////////////////
	// Flicker at low energy
	if (mbActive && mpInit->mpInventory->IsActive() == false &&
		pHudModel->msName == "Flashlight") {
		if (mpInit->mpPlayer->GetPower() < 3.0f) {
			if (mfFlickerTime <= 0) {
				if (pHudModel->mvLights[0]->IsVisible()) {
					pHudModel->mvLights[0]->SetVisible(false);
					pHudModel->mvBillboards[0]->SetVisible(false);

					mfFlickerTime = cMath::RandRectf(0.04f, 0.15f);

					cSoundHandler *pSoundHanlder = mpInit->mpGame->GetSound()->GetSoundHandler();

					pSoundHanlder->PlayGui("item_flashlight_flicker", false, 1);
				} else {
					pHudModel->mvLights[0]->SetVisible(true);
					pHudModel->mvBillboards[0]->SetVisible(true);
					mfFlickerTime = cMath::RandRectf(0.05f, 1.5f);
				}
			} else {
				mfFlickerTime -= afTimeStep;
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::SetActive(bool abX) {
	cSoundHandler *pSoundHanlder = mpInit->mpGame->GetSound()->GetSoundHandler();

	if (abX && (mpInit->mpPlayer->GetPower() == 0 || mbDisabled)) {
		pSoundHanlder->PlayGui("item_flashlight_nopower", false, 1);
		return;
	}

	if (mbActive == abX)
		return;
	mbActive = abX;

	/////////////////////////////
	// Active
	if (mbActive) {
		mpInit->mpPlayerHands->SetCurrentModel(0, "Flashlight");

		// pSoundHanlder->PlayGui("item_flashlight_on",false,1);
	}
	/////////////////////////////
	// Not active
	else if (mpInit->mpPlayerHands->GetCurrentModel(0) &&
			 mpInit->mpPlayerHands->GetCurrentModel(0)->msName == "Flashlight" &&
			 mpInit->mpPlayerHands->GetCurrentModel(0)->GetState() != eHudModelState_Unequip) {
		mpInit->mpPlayerHands->SetCurrentModel(0, "");

		if (mpInit->mpPlayer->GetPower() == 0)
			pSoundHanlder->PlayGui("item_flashlight_nopower", false, 1);
		// else
		//	pSoundHanlder->PlayGui("item_flashlight_off",false,1);
	}
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::SetDisabled(bool abX) {
	if (abX == true)
		SetActive(false);

	mbDisabled = abX;
}

//-----------------------------------------------------------------------

void cPlayerFlashLight::OnPostSceneDraw() {
	/*iLowLevelGraphics *mpLowGfx = mpInit->mpGame->GetGraphics()->GetLowLevel();

	mpLowGfx->DrawLine(mvStart,mvEnd,cColor(1,0,1));*/
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// GLOW STICK
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerGlowStick::cPlayerGlowStick(cInit *apInit) {
	mpInit = apInit;

	mpInit->mpPlayerHands->AddModelFromFile("hud_object_glowstick.hud");

	Reset();
}

//-----------------------------------------------------------------------

cPlayerGlowStick::~cPlayerGlowStick() {
}

//-----------------------------------------------------------------------

void cPlayerGlowStick::Reset() {
	mbActive = false;
}

//-----------------------------------------------------------------------

void cPlayerGlowStick::OnWorldLoad() {
}

//-----------------------------------------------------------------------

void cPlayerGlowStick::Destroy() {
}

//-----------------------------------------------------------------------

void cPlayerGlowStick::Update(float afTimeStep) {
}

//-----------------------------------------------------------------------

void cPlayerGlowStick::SetActive(bool abX) {
	if (mbActive == abX)
		return;
	mbActive = abX;

	/*cSoundHandler *pSoundHanlder = */ mpInit->mpGame->GetSound()->GetSoundHandler();

	if (mbActive) {
		// Log("Setting the glowstick to TRUE\n");
		mpInit->mpPlayerHands->SetCurrentModel(0, "Glowstick");
		// pSoundHanlder->PlayGui("item_glowstick_on",false,1);
	} else if (mpInit->mpPlayerHands->GetCurrentModel(0) &&
			   mpInit->mpPlayerHands->GetCurrentModel(0)->msName == "Glowstick" &&
			   mpInit->mpPlayerHands->GetCurrentModel(0)->GetState() != eHudModelState_Unequip) {
		// Log("Setting the glowstick to FALSE\n");
		mpInit->mpPlayerHands->SetCurrentModel(0, "");
		// pSoundHanlder->PlayGui("item_glowstick_off",false,1);
	}
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// FLARE
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerFlare::cPlayerFlare(cInit *apInit) {
	mpInit = apInit;

	mpInit->mpPlayerHands->AddModelFromFile("hud_object_flare.hud");

	mfTime = mpInit->mpGameConfig->GetFloat("Items", "FlareGlowTime", 1);

	mfLightPulse = 1.0f;
	mfLightPulseAdd = 0.88f;

	mpLight = NULL;

	Reset();
}

//-----------------------------------------------------------------------

cPlayerFlare::~cPlayerFlare() {
}

//-----------------------------------------------------------------------

void cPlayerFlare::Reset() {
	mbActive = false;
	mpLight = NULL;
}

//-----------------------------------------------------------------------

void cPlayerFlare::OnWorldLoad() {
	mpLight = NULL;
}

//-----------------------------------------------------------------------

void cPlayerFlare::Update(float afTimeStep) {
	if (mbActive) {
		//////////////////////////////////////
		// Model entities are loaded and light gotten
		if (mpLight) {
			//////////////////////
			// Fade at end
			if (mfTime <= 30) {
				mfLightRadius = (mfTime / 30.0f) * mfMaxLightRadius;
			}

			float fRadius = mfLightRadius;

			//////////////////////
			// Pulse
			mfLightPulse += mfLightPulseAdd * afTimeStep;
			if (mfLightPulseAdd > 0 && mfLightPulse >= 1.0f) {
				mfLightPulse = 1.0f;
				mfLightPulseAdd = -mfLightPulseAdd;
			}
			if (mfLightPulseAdd < 0 && mfLightPulse <= 0.0f) {
				mfLightPulse = 0.0f;
				mfLightPulseAdd = -mfLightPulseAdd;
			}

			fRadius = (mfLightPulse * 0.4f + 0.8f) * mfLightRadius;

			if (mpLight)
				mpLight->SetFarAttenuation(fRadius);

			//////////////////////
			// Time
			if (mpInit->mpInventory->IsActive() == false &&
				mpInit->mpNotebook->IsActive() == false &&
				mpInit->mpNumericalPanel->IsActive() == false) {
				mfTime -= afTimeStep;
				if (mfTime <= 0) {
					SetActive(false);
				}
			}
		}
		//////////////////////////////////////
		// Model entities not loaded yet
		else {
			if (mpModel == mpInit->mpPlayerHands->GetCurrentModel(0)) {
				if (mpModel->mvLights.empty() == false) {
					mpLight = mpModel->mvLights[0];
					mfMaxLightRadius = mpLight->GetFarAttenuation();
					mfLightRadius = mfMaxLightRadius;
				} else {
					Warning("No lights in flare!\n");
				}
			}
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerFlare::SetActive(bool abX) {
	if (mbActive == abX)
		return;
	mbActive = abX;

	/*cSoundHandler *pSoundHanlder = */ mpInit->mpGame->GetSound()->GetSoundHandler();

	if (mbActive) {
		mpInit->mpPlayerHands->SetCurrentModel(0, "Flare");

		if (mpInit->mpPlayer->GetFlashLight()->IsActive())
			mpInit->mpPlayer->GetFlashLight()->SetActive(false);
		if (mpInit->mpPlayer->GetGlowStick()->IsActive())
			mpInit->mpPlayer->GetGlowStick()->SetActive(false);

		mfTime = mpInit->mpGameConfig->GetFloat("Items", "FlareGlowTime", 1);

		mpModel = mpInit->mpPlayerHands->GetModel("Flare");
		mpLight = NULL;
	} else {
		///////////////////////////////
		// Check if hud model should be put down.
		if (mpInit->mpPlayerHands->GetCurrentModel(0) &&
			mpInit->mpPlayerHands->GetCurrentModel(0)->msName == "Flare" &&
			mpInit->mpPlayerHands->GetCurrentModel(0)->GetState() != eHudModelState_Unequip) {
			mpInit->mpPlayerHands->SetCurrentModel(0, "");
		}

		///////////////////////////////
		// Create entity
		cCamera3D *pCam = mpInit->mpPlayer->GetCamera();

		cVector3f vRot = cVector3f(pCam->GetPitch(), pCam->GetYaw(), pCam->GetRoll());
		cMatrixf mtxStart = cMath::MatrixRotate(vRot, eEulerRotationOrder_XYZ);
		mtxStart.SetTranslation(pCam->GetPosition());

		iEntity3D *pEntity = mpInit->mpGame->GetScene()->GetWorld3D()->CreateEntity("Throw", mtxStart,
																					"items_flare_thrown.ent", true);
		if (pEntity) {
			iGameEntity *pEntity2 = mpInit->mpMapHandler->GetLatestEntity();

			cVector3f vRot2 = cMath::MatrixMul(mtxStart.GetRotation(), cVector3f(1, 0.3f, 0));

			for (int i = 0; i < pEntity2->GetBodyNum(); ++i) {
				iPhysicsBody *pBody = pEntity2->GetBody(i);
				pBody->AddImpulse(pCam->GetForward() * 3.0f);
				pBody->AddTorque(vRot2);
			}

			// setup light
			if (mfTime <= 30) {
				float fRadius = (mfTime / 30.0f);
				if (fRadius < 0)
					fRadius = 0;

				for (int i = 0; i < pEntity2->GetLightNum(); ++i) {
					iLight3D *pLight = pEntity2->GetLight(i);
					pLight->SetFarAttenuation(pLight->GetFarAttenuation() * fRadius);
				}
			}
		}
	}
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// NOISE FILTER
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerNoiseFilter::cPlayerNoiseFilter(cInit *apInit) {
	mpInit = apInit;
	mpDrawer = mpInit->mpGame->GetGraphics()->GetDrawer();

	mlAmount = 6;

	for (int i = 0; i < mlAmount; ++i) {
		tString sFileName = "effect_noise0" + cString::ToString(i);
		cGfxObject *pObject = mpDrawer->CreateGfxObject(sFileName, "smoke2d");
		if (pObject == NULL) {
			error("Error loading noise filter");
		}

		mvGfxNoise.push_back(pObject);
	}

	Hpl1::resizeAndFill(mvCurrentGfx, 20 * 15, cMath::RandRectl(0, mlAmount - 1));

	mfAlpha = 0.3f;
	mfTimeCount = 0;

	mbActive = mpInit->mpConfig->GetBool("Graphics", "NoiseFilter", true);
}

//-----------------------------------------------------------------------

cPlayerNoiseFilter::~cPlayerNoiseFilter() {
	mpInit->mpConfig->SetBool("Graphics", "NoiseFilter", mbActive);

	for (size_t i = 0; i < mvGfxNoise.size(); ++i) {
		mpDrawer->DestroyGfxObject(mvGfxNoise[i]);
	}
}

//-----------------------------------------------------------------------

void cPlayerNoiseFilter::Draw() {
	if (mbActive == false)
		return;

	for (int x = 0; x < 20; ++x)
		for (int y = 0; y < 15; ++y) {
			cVector3f vPos((float)x * 40, (float)y * 40, 0);

			mpDrawer->DrawGfxObject(mvGfxNoise[mvCurrentGfx[x * y]], vPos, 40, cColor(mfAlpha, 0));
			// mpDrawer->DrawGfxObject(mvGfxNoise[0],vPos,40,cColor(mfAlpha,mfAlpha));
		}
}

//-----------------------------------------------------------------------

void cPlayerNoiseFilter::Update(float afTimeStep) {
	if (mbActive == false)
		return;
	if (mfTimeCount <= 0) {
		for (int x = 0; x < 20; ++x)
			for (int y = 0; y < 15; ++y) {
				// Change the image
				int lCurrent = mvCurrentGfx[x * y];
				int lRand = cMath::RandRectl(0, mlAmount - 1);
				if (lRand == lCurrent) {
					++lRand;
					if (lRand >= mlAmount)
						lRand = 0;
				}
				mvCurrentGfx[x * y] = lRand;
			}

		mfTimeCount = 1 / 15.0f;
	} else {
		mfTimeCount -= afTimeStep;
	}
}

//-----------------------------------------------------------------------

void cPlayerNoiseFilter::SetActive(bool abX) {
	mbActive = abX;
}

//-----------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////
// FEAR FILTER
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerFearFilter::cPlayerFearFilter(cInit *apInit) {
	mpInit = apInit;

	Reset();
}

//-----------------------------------------------------------------------

cPlayerFearFilter::~cPlayerFearFilter() {
}

//-----------------------------------------------------------------------

void cPlayerFearFilter::Draw() {
}

//-----------------------------------------------------------------------

void cPlayerFearFilter::Update(float afTimeStep) {
	if (mpInit->mpPlayer->IsDead())
		return;

	if (mbActive) {
		mfAlpha += afTimeStep * 0.5f;
		if (mfAlpha > mfMaxAlpha)
			mfAlpha = mfMaxAlpha;
		mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailAmount(mfAlpha);
	} else if (mfAlpha > 0) {
		mfAlpha -= afTimeStep * 0.15f;
		if (mfAlpha < 0) {
			mfAlpha = 0;
			mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(false);
		} else {
			mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailAmount(mfAlpha);
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerFearFilter::Reset() {
	mbActive = false;
	mfAlpha = 0;
	mfMaxAlpha = 0.58f;
}

//-----------------------------------------------------------------------

void cPlayerFearFilter::SetActive(bool abX) {
	if (mbActive == abX)
		return;

	mbActive = abX;
	mfAlpha = 0;

	mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(true);
	mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailAmount(0);
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// LOOK AT
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerLookAt::cPlayerLookAt(cPlayer *apPlayer) {
	mpPlayer = apPlayer;

	Reset();
}

//-----------------------------------------------------------------------

cPlayerLookAt::~cPlayerLookAt() {
}

//-----------------------------------------------------------------------

void cPlayerLookAt::Update(float afTimeStep) {
	if (mbActive == false)
		return;

	cCamera3D *pCam = mpPlayer->GetCamera();
	cVector3f vGoalAngle = cMath::GetAngleFromPoints3D(pCam->GetPosition(), mvTargetPos);

	// Get distance to goal
	cVector3f vDist;
	vDist.x = cMath::GetAngleDistanceRad(pCam->GetPitch(), vGoalAngle.x);
	vDist.y = cMath::GetAngleDistanceRad(pCam->GetYaw(), vGoalAngle.y);

	// Get the Speed
	cVector3f vSpeed;
	vSpeed.x = cMath::Min(vDist.x * mfSpeedMul, mfMaxSpeed);
	vSpeed.y = cMath::Min(vDist.y * mfSpeedMul, mfMaxSpeed);

	// Add Pitch
	pCam->AddPitch(vSpeed.x * afTimeStep);

	// Add yaw
	pCam->AddYaw(vSpeed.y * afTimeStep);
	mpPlayer->GetCharacterBody()->SetYaw(pCam->GetYaw());
}

void cPlayerLookAt::Reset() {
	mbActive = false;
	mfMaxSpeed = 9999.0f;
	mfSpeedMul = 1.0f;
	mvTargetPos = cVector3f(0, 1, 0);
}

void cPlayerLookAt::SetTarget(const cVector3f &avTargetPos, float afSpeedMul, float afMaxSpeed) {
	mvTargetPos = avTargetPos;
	mfSpeedMul = afSpeedMul;
	mfMaxSpeed = afMaxSpeed;
}

void cPlayerLookAt::SetActive(bool abX) {
	mbActive = abX;
}

//-----------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////
// HIDDEN
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

cPlayerHidden::cPlayerHidden(cInit *apInit) {
	mpInit = apInit;
	mpRenderer = mpInit->mpGame->GetGraphics()->GetRenderer3D();
	mpDrawer = mpInit->mpGame->GetGraphics()->GetDrawer();

	mpSoundHandler = mpInit->mpGame->GetSound()->GetSoundHandler();

	mfUpdateCount = 1.0f / 3.0f;

	cVector2f vScreenSize = mpInit->mpGame->GetGraphics()->GetLowLevel()->GetScreenSize();

	mfStartEffectOffset = 40;
	mfHiddenEffectOffset = 0;
	mfEffectOffsetAdd = mfStartEffectOffset - mfHiddenEffectOffset;

	mfStartAspect = vScreenSize.x / vScreenSize.y;
	mfHiddenAspect = mfStartAspect * 0.82f;
	mfAspectAdd = mfStartAspect - mfHiddenAspect;

	mfStartFov = cMath::ToRad(70);
	mfHiddenFov = cMath::ToRad(78);
	mfFovAdd = mfHiddenFov - mfStartFov;

	mfEnemyTooCloseMax = 4.5f;

	mfCloseEffectFovMax = cMath::ToRad(2);
	mfCloseEffectFovMin = cMath::ToRad(-11);

	Reset();

	// Get images
	mpInShadowGfx = mpDrawer->CreateGfxObject("player_in_shadow.jpg", "diffadditive2d");

	// Get font
	mpFont = mpInit->mpGame->GetResources()->GetFontManager()->CreateFontData("verdana.fnt");
}

//-----------------------------------------------------------------------

cPlayerHidden::~cPlayerHidden() {
}

//-----------------------------------------------------------------------

void cPlayerHidden::OnWorldLoad() {
	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	mpLight = pWorld->CreateLightPoint("HiddenLight");

	mpLight->SetFarAttenuation(13);
	mpLight->SetDiffuseColor(cColor(0, 0, 0, 0));
	mpLight->SetVisible(false);
	mpLight->SetCastShadows(false);
}

void cPlayerHidden::OnWorldExit() {
}

//-----------------------------------------------------------------------

void cPlayerHidden::Draw() {
	// Draw in shadow effect
	if (mfInShadowAlpha > 0) {
		float fAlpha = (mfInShadowPulse * 0.5f + 0.5f) * mfInShadowAlpha * 0.85f;
		mpDrawer->DrawGfxObject(mpInShadowGfx, cVector3f(-mfEffectOffset, -mfEffectOffset, 0),
								cVector2f(800 + mfEffectOffset * 2, 600 + mfEffectOffset * 2),
								cColor(fAlpha, fAlpha));
	}
}

//-----------------------------------------------------------------------

void cPlayerHidden::UnHide() {
	if (mbHidden) {
		mpLight->FadeTo(cColor(0, 0, 0, 0), mpLight->GetFarAttenuation(), mfHiddenOffTime);

		if (mfHiddenOnCount > 2)
			mpSoundHandler->PlayGui("player_hidden_off", false, 1);
	}
	mbHidden = false;
	mfHiddenCount = 0;
	mfHiddenOnCount = 0;

	if (mbEnemyTooClose) {
		mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(false);
	}

	mfEnemyTooCloseCount = 0;
	mbEnemyTooClose = false;
}

//-----------------------------------------------------------------------

bool cPlayerHidden::InShadows() {
	if (mfLight <= mfMaxLight)
		return true;

	return false;
}

//-----------------------------------------------------------------------

static float GetMaxRGB(const cColor &aCol) {
	float fAmount = aCol.r;
	if (fAmount < aCol.g)
		fAmount = aCol.g;
	if (fAmount < aCol.b)
		fAmount = aCol.b;

	return fAmount;
}

void cPlayerHidden::Update(float afTimeStep) {
	iCharacterBody *pCharBody = mpInit->mpPlayer->GetCharacterBody();

	bool bIsCrouching = false;
	if (mpInit->mpPlayer->GetMoveState() == ePlayerMoveState_Crouch)
		bIsCrouching = true;

	///////////////////////////////////
	// Update Hidden effect
	if (mbHidden) {
		// Aspect
		if (mfAspect > mfHiddenAspect) {
			mfAspect -= afTimeStep * mfAspectAdd * (1 / mfHiddenOnTime);
			if (mfAspect < mfHiddenAspect)
				mfAspect = mfHiddenAspect;

			mpInit->mpPlayer->GetCamera()->SetAspect(mfAspect);
		}

		// FOV
		if (mfFov < mfHiddenFov) {
			mfFov += afTimeStep * mfFovAdd * (1 / mfHiddenOnTime);
			if (mfFov > mfHiddenFov)
				mfFov = mfHiddenFov;

			mpInit->mpPlayer->GetCamera()->SetFOV(mfFov);
		}

		// Effect offset
		if (mfEffectOffset > mfHiddenEffectOffset) {
			mfEffectOffset -= afTimeStep * mfEffectOffsetAdd * (1 / mfHiddenOnTime);
			if (mfEffectOffset < mfHiddenEffectOffset)
				mfEffectOffset = mfHiddenEffectOffset;
		}
	} else {
		// Aspect
		if (mfAspect < mfStartAspect) {
			mfAspect += afTimeStep * mfAspectAdd * (1 / mfHiddenOffTime);
			if (mfAspect > mfStartAspect)
				mfAspect = mfStartAspect;

			mpInit->mpPlayer->GetCamera()->SetAspect(mfAspect);
		}

		// FOV
		if (mfFov > mfStartFov) {
			mfFov -= afTimeStep * mfFovAdd * (1 / mfHiddenOffTime);
			if (mfFov < mfStartFov)
				mfFov = mfStartFov;

			mpInit->mpPlayer->GetCamera()->SetFOV(mfFov);
		}

		// Effect offset
		if (mfEffectOffset < mfStartEffectOffset) {
			mfEffectOffset += afTimeStep * mfEffectOffsetAdd * (1 / mfHiddenOffTime);
			if (mfEffectOffset > mfStartEffectOffset)
				mfEffectOffset = mfStartEffectOffset;
		}
	}

	///////////////////////////////////
	// Update In Shadow effect

	// Alpha
	if (mfLight <= mfMaxLight && bIsCrouching) {
		mfInShadowAlpha += afTimeStep;
		if (mfInShadowAlpha > 1)
			mfInShadowAlpha = 1;
	} else {
		mfInShadowAlpha -= afTimeStep;
		if (mfInShadowAlpha < 0)
			mfInShadowAlpha = 0;
	}

	// Pulse
	mfInShadowPulse += afTimeStep * mfInShadowPulseAdd;
	if (mfInShadowPulseAdd < 0 && mfInShadowPulse < 0) {
		mfInShadowPulse = 0;
		mfInShadowPulseAdd = -mfInShadowPulseAdd;
	} else if (mfInShadowPulseAdd > 0 && mfInShadowPulse > 1) {
		mfInShadowPulse = 1;
		mfInShadowPulseAdd = -mfInShadowPulseAdd;
	}

	if (mbInShadows)
		mpLight->SetPosition(mpInit->mpPlayer->GetCamera()->GetPosition());

	//////////////////////////////////////////
	// Check if the player is hidden
	if (mfLight <= mfMaxLight && bIsCrouching && mpInit->mpPlayer->GetHealth() > 0) {
		cVector3f vDiff = pCharBody->GetLastPosition() - pCharBody->GetPosition();
		if (fabsf(vDiff.x) < 0.01f && fabsf(vDiff.y) < 0.01f && fabsf(vDiff.z) < 0.01f) {
			if (!mbHidden) {
				if (mfHiddenCount >= mfHiddenTime) {
					mbInShadows = false;
					mbHidden = true;
					mpLight->SetVisible(true);
					mpLight->SetPosition(mpInit->mpPlayer->GetCamera()->GetPosition());
					mpLight->FadeTo(cColor(0.1f, 0.1f, 0.6f, 0), 12, mfHiddenOnTime);

					mpSoundHandler->PlayGui("player_hidden_on", false, 1);
				} else {
					mfHiddenCount += afTimeStep;
				}
			} else {
				mfHiddenOnCount += afTimeStep;
			}
		} else {
			UnHide();
		}
		if (!mbHidden) {
			if (!mbInShadows) {
				mpLight->FadeTo(cColor(0.09f, 0.09f, 0.28f, 0), 6, 1);
				mpLight->SetVisible(true);
			}
			mbInShadows = true;
		}
	} else {
		if (mbInShadows) {
			mbInShadows = false;
			mpLight->FadeTo(cColor(0, 0, 0, 0), 8, 1);
		}

		UnHide();
	}

	//////////////////////////////////////////7
	// Update the current light level
	if (mfUpdateCount <= 0) {
		mfUpdateCount = 1.0f / 2.0f;

		mfLight = GetMaxRGB(mpInit->mpGame->GetGraphics()->GetRenderer3D()->GetAmbientColor());
		cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
		if (pWorld == NULL)
			return;
		iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();
		cBoundingVolume *pPlayerBV = pCharBody->GetBody()->GetBV();

		// Just to be sure...
		if (mpInit->mpPlayer->GetFlashLight()->IsActive())
			mfLight += 0.5;

		// Iterate lights
		cLight3DListIterator lightIt = pWorld->GetLightIterator();
		while (lightIt.HasNext()) {
			iLight3D *pLight = lightIt.Next();
			if (pLight == mpLight)
				continue;
			if (pLight->IsActive() == false || pLight->IsVisible() == false)
				continue;

			if (cMath::CheckCollisionBV(*pPlayerBV, *pLight->GetBoundingVolume())) {
				// Check line of sight
				if (HasLineOfSight(pLight, pPhysicsWorld) == false)
					continue;

				// Get highest value of rg b
				float fAmount = GetMaxRGB(pLight->GetDiffuseColor());

				// Get distance to the light
				float fDist = cMath::Vector3Dist(pLight->GetWorldPosition(),
												 pPlayerBV->GetWorldCenter());

				// Calculate attenuation
				float fT = 1 - fDist / pLight->GetFarAttenuation();
				if (fT < 0)
					fT = 0;
				fAmount *= fT;

				mfLight += fAmount;
			}
		}

	} else {
		mfUpdateCount -= afTimeStep;
	}

	////////////////////////////////////////////////////
	// Check if enemy is too close and act accordingly.
	UpdateEnemyTooClose(afTimeStep);
}

//-----------------------------------------------------------------------

bool cPlayerHidden::OnIntersect(iPhysicsBody *pBody, cPhysicsRayParams *apParams) {
	// bool bDebug = pBody->GetName() == "polySurface76";

	if (pBody->IsCharacter() || pBody->GetCollide() == false || pBody->GetBlocksLight() == false) {
		// if(bDebug)Log("Out on first!\n");
		return true;
	}

	iGameEntity *pEntity = (iGameEntity *)pBody->GetUserData();
	if (pEntity && pEntity->GetMeshEntity()) {
		cMeshEntity *pMeshEntity = pEntity->GetMeshEntity();
		if (pMeshEntity->IsShadowCaster() == false)
			return true;

		bool bFoundSolid = false;
		for (int i = 0; i < pMeshEntity->GetSubMeshEntityNum(); ++i) {
			iMaterial *pMaterial = pMeshEntity->GetSubMeshEntity(i)->GetMaterial();
			if (pMaterial &&
				(pMaterial->IsTransperant() == false && pMaterial->HasAlpha() == false)) {
				bFoundSolid = true;
				break;
			}
		}
		if (bFoundSolid == false) {
			// if(bDebug)Log("Out on non solid!\n");
			return true;
		}
	}

	mbIntersected = true;
	// if(bDebug)Log("Intersection!!\n");
	return false;
}

//-----------------------------------------------------------------------

bool cPlayerHidden::HasLineOfSight(iLight3D *pLight, iPhysicsWorld *pPhysicsWorld) {
	if (pLight->GetCastShadows() == false)
		return true;

	if (mpRenderer->GetShowShadows() == eRendererShowShadows_None)
		return true;

	iCharacterBody *pCharBody = mpInit->mpPlayer->GetCharacterBody();
	cVector3f vPlayerPos = pCharBody->GetPosition();

	// Check frustum of spotlight
	if (pLight->GetLightType() == eLight3DType_Spot) {
		cLight3DSpot *pSpotLight = static_cast<cLight3DSpot *>(pLight);

		if (pSpotLight->GetFrustum()->CollideBoundingVolume(pLight->GetBoundingVolume()) ==
			eFrustumCollision_Outside) {
			return false;
		}
	}

	int lCount = 0;

	cVector3f vPosAdd[5];
	int lPosAddNum = 5;

	// Centre
	vPosAdd[0] = cVector3f(0, 0, 0);
	// Head
	vPosAdd[1] = cVector3f(0, pCharBody->GetSize().y / 2 - 0.1f, 0);
	// Feet
	vPosAdd[2] = cVector3f(0, -(pCharBody->GetSize().y / 2 + 0.1f), 0);

	cVector3f vRight = cMath::Vector3Cross(cMath::Vector3Normalize(pLight->GetWorldPosition() - vPlayerPos),
										   cVector3f(0, 1, 0));
	// Right
	vPosAdd[3] = vRight * (pCharBody->GetSize().x * 0.4f);
	// Left
	vPosAdd[4] = vRight * (pCharBody->GetSize().x * -0.4f);

	for (int i = 0; i < lPosAddNum; ++i) {
		mbIntersected = false;
		pPhysicsWorld->CastRay(this, vPlayerPos + vPosAdd[i], pLight->GetWorldPosition(),
							   false, false, false, false);

		if (mbIntersected)
			++lCount;
		else {
			break;
		}
	}

	return lCount < lPosAddNum;
}

//-----------------------------------------------------------------------

void cPlayerHidden::UpdateEnemyTooClose(float afTimeStep) {
	cCamera3D *pCam = mpInit->mpPlayer->GetCamera();
	cWorld3D *pWorld = mpInit->mpGame->GetScene()->GetWorld3D();
	if (pWorld == NULL)
		return;

	/////////////////////////////
	// Update too close effect
	if (mbEnemyTooClose && mfEnemyTooCloseCount > 1.2f) {
		if (mfCloseEffectFovAdd > 0) {
			mfCloseEffectFov += mfCloseEffectFovAdd * afTimeStep;
			if (mfCloseEffectFov >= mfCloseEffectFovMax) {
				mfCloseEffectFovAdd = -mfCloseEffectFovAdd;
				mfCloseEffectFov = mfCloseEffectFovMax;

				if (mfEnemyTooCloseCount > mfEnemyTooCloseMax * 0.4f)
					mpSoundHandler->PlayGui("player_scare_mid", false, 1);
				else
					mpSoundHandler->PlayGui("player_scare_low", false, 1);
			}
		} else {
			mfCloseEffectFov += mfCloseEffectFovAdd * afTimeStep;
			if (mfCloseEffectFov <= mfCloseEffectFovMin) {
				mfCloseEffectFovAdd = -mfCloseEffectFovAdd;
				mfCloseEffectFov = mfCloseEffectFovMin;

				// if(mfEnemyTooCloseCount > mfEnemyTooCloseMax*0.6f)
				//	mpSoundHandler->PlayGui("player_scare_mid",false,1);
				// else
				//	mpSoundHandler->PlayGui("player_scare_low",false,1);
			}
		}

		float fAdd = 0;
		if (mfCloseEffectFov >= 0) {
			float fT = mfCloseEffectFov / mfCloseEffectFovMax;
			fAdd = sin(fT * kPi2f) * mfCloseEffectFovMax;
		} else {
			float fT = mfCloseEffectFov / mfCloseEffectFovMin;
			fAdd = sin(fT * kPi2f) * mfCloseEffectFovMin;
		}

		pCam->SetFOV(mfFov + fAdd);

		mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(true);
		mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailAmount(0.8f);
	} else if (mfCloseEffectFov != 0) {
		mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(false);

		if (mfCloseEffectFov < 0) {
			mfCloseEffectFov += afTimeStep * cMath::ToRad(7);
			if (mfCloseEffectFov > 0)
				mfCloseEffectFov = 0;
		} else {
			mfCloseEffectFov -= afTimeStep * cMath::ToRad(7);
			if (mfCloseEffectFov < 0)
				mfCloseEffectFov = 0;
		}

		float fAdd = 0;
		if (mfCloseEffectFov >= 0) {
			float fT = mfCloseEffectFov / mfCloseEffectFovMax;
			fAdd = sin(fT * kPi2f) * mfCloseEffectFovMax;
		} else {
			float fT = mfCloseEffectFov / mfCloseEffectFovMin;
			fAdd = sin(fT * kPi2f) * mfCloseEffectFovMin;
		}

		pCam->SetFOV(mfFov + fAdd);
	}

	/////////////////////////////
	// Update the count
	if (InShadows()) // mbHidden)
	{
		if (mfCheckEnemyCloseCount <= 0) {
			// FIXME: Change value of FMaxDist when hidden?
			// float fMaxDist = mbHidden ? 11.0f : 11.0f;
			float fMaxDist = 11.0f;
			float fMaxDistSqr = fMaxDist * fMaxDist;

			// Reset variables
			mbEnemyTooClose = false;
			mfCheckEnemyCloseCount = 0.3f;

			/// Get needed data
			iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();

			// Iterate enemies
			tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator();
			while (enemyIt.HasNext()) {
				iGameEnemy *pEnemy = enemyIt.Next();

				if (pEnemy->GetHealth() <= 0 || pEnemy->IsActive() == false)
					continue;

				iCharacterBody *pCharBody = pEnemy->GetMover()->GetCharBody();

				// Check if the enemy is close enough
				float fDistSqr = cMath::Vector3DistSqr(pCam->GetPosition(), pCharBody->GetPosition());
				if (fDistSqr > fMaxDistSqr)
					continue;

				// Check if enemy is in FOV
				cVector3f vDir = pCharBody->GetPosition() - pCam->GetPosition();
				vDir.Normalise();

				float fAngle = cMath::Vector3Angle(pCam->GetForward(), vDir);
				if (fAngle > pCam->GetFOV() * 0.5f)
					continue;

				// Check if ther is a line of sight
				mbIntersected = false;
				pPhysicsWorld->CastRay(this, pCam->GetPosition(), pCharBody->GetPosition(),
									   false, false, false, false);
				if (mbIntersected == false) {
					mbEnemyTooClose = true;
					break;
				}
			}
		} else {
			mfCheckEnemyCloseCount -= afTimeStep;
		}

		////////////////////////////////
		// Update the count if enemy is near
		if (mbEnemyTooClose) {
			mfEnemyTooCloseCount += afTimeStep;

			// Do some effect

			// Check if limit is reached
			if (mfEnemyTooCloseCount > mfEnemyTooCloseMax) {
				UnHide();
				mpInit->mpPlayer->ChangeMoveState(ePlayerMoveState_Walk);
				mpSoundHandler->PlayGui("player_scare_high", false, 1);

				cSoundEntity *pSound = pWorld->CreateSoundEntity("Scare", "player_scare_high", true);
				if (pSound) {
					pSound->SetPosition(pCam->GetPosition());
				}

				// Show player to nearby enemies.
				tGameEnemyIterator enemyIt = mpInit->mpMapHandler->GetGameEnemyIterator();
				while (enemyIt.HasNext()) {
					iGameEnemy *pEnemy = enemyIt.Next();

					if (pEnemy->GetHealth() <= 0 || pEnemy->IsActive() == false)
						continue;
					iCharacterBody *pCharBody = pEnemy->GetMover()->GetCharBody();

					// Check if the enemy is close enough
					float fDist = cMath::Vector3Dist(pCam->GetPosition(), pCharBody->GetPosition());

					if (fDist <= 8) {
						pEnemy->ShowPlayer(mpInit->mpPlayer->GetCharacterBody()->GetFeetPosition());
					}
				}
			}
		} else if (mfEnemyTooCloseCount > 0) {
			mfEnemyTooCloseCount -= afTimeStep;
			mpInit->mpGame->GetGraphics()->GetRendererPostEffects()->SetImageTrailActive(false);
		}
	}
}

//-----------------------------------------------------------------------

void cPlayerHidden::Reset() {
	mpLight = NULL;

	mfHiddenOnTime = 1;
	mfHiddenOffTime = 0.5f;

	mfHiddenOnCount = 0;

	mfEffectOffset = mfStartEffectOffset;
	mfAspect = mfStartAspect;
	mfFov = mfStartFov;

	mbInShadows = false;

	mfLight = 1.0f;
	mfMaxLight = 0.25f;

	mfHiddenCount = 0;
	mfHiddenTime = 1.4f;
	mbHidden = false;

	mfInShadowAlpha = 0;
	mfInShadowPulse = 0;
	mfInShadowPulseAdd = 1;

	mfCheckEnemyCloseCount = 0;
	mfEnemyTooCloseCount = 0;
	mbEnemyTooClose = false;

	mfCloseEffectFovAdd = cMath::ToRad(15);
	mfCloseEffectFov = 0;
}

//-----------------------------------------------------------------------
