/*
 * Copyright © 2011-2020 Frictional Games
 * 
 * This file is part of Amnesia: A Machine For Pigs.
 * 
 * Amnesia: A Machine For Pigs 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. 

 * Amnesia: A Machine For Pigs 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 Amnesia: A Machine For Pigs.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "LuxProp.h"

#include "LuxDebugHandler.h"
#include "LuxMap.h"
#include "LuxPlayer.h"
#include "LuxInteractConnections.h"
#include "LuxEffectRenderer.h"

//////////////////////////////////////////////////////////////////////////
// LOADER
//////////////////////////////////////////////////////////////////////////

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

iLuxPropLoader::iLuxPropLoader(const tString& asName) : cEntityLoader_Object(asName)
{
	mfDefaultMaxFocusDistance = gpBase->mpGameCfg->GetFloat("Player_Interaction", "Default_DefaultMaxFocusDist",0);

	mbForceFullGameSave = false;
}

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

void iLuxPropLoader::BeforeLoad(cXmlElement *apRootElem, const cMatrixf &a_mtxTransform,cWorld *apWorld, cResourceVarsObject *apInstanceVars)
{

}

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

void iLuxPropLoader::AfterLoad(cXmlElement *apRootElem, const cMatrixf &a_mtxTransform,cWorld *apWorld, cResourceVarsObject *apInstanceVars)
{
	cLuxMap *pMap = gpBase->mpCurrentMapLoading;
	if(pMap==NULL)	return;

	iLuxProp *pProp = CreateProp(mpEntity->GetName(), mlID,pMap);

	//////////////////////////////
	// Set data
	pProp->mpWorld = apWorld;
	pProp->msFileName = msFileName;
	pProp->m_mtxOnLoadTransform = a_mtxTransform;
	pProp->mvOnLoadScale = mvScale;

	pProp->mvBodies = mvBodies;
	pProp->mvJoints = mvJoints;

	pProp->mvBodyExtraData = mvBodyExtraData;

	pProp->mvLights = mvLights;
	pProp->mvParticleSystems = mvParticleSystems;
	pProp->mvBillboards = mvBillboards;
	pProp->mvBeams = mvBeams;
	pProp->mvSoundEntities = mvSoundEntities;

	pProp->mpMeshEntity = mpEntity;

	
	//Set body userdata
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];
		iLuxEntity *pEnt = pProp;
		pBody->SetUserData(pEnt);
	}

	//////////////////////////////
	// Load base properties
	pProp->mfHealth = GetVarFloat("Health", 100);
	pProp->mlToughness = GetVarInt("Toughness", 0);

	pProp->mfMaxFocusDistance = GetVarFloat("MaxFocusDistance", 0);
	pProp->msMainBodyName = GetVarString("MainPhysicsBody", "");
	
	pProp->mbDissolveOnDestruction = GetVarBool("DissolveOnDestruction", false);
	pProp->mfDissolveTime = GetVarFloat("DissolveTime", 1.0f);

	pProp->mbShowMesh = GetVarBool("ShowMesh", true);
	if(mpEntity) mpEntity->SetVisible(pProp->mbShowMesh);

	pProp->mbShowHints = GetVarBool("ShowHints", true);

	//////////////////////////////
	// Load effects properties
	pProp->msEffectsOnSound = GetVarString("EffectsOnSound", "");
	pProp->msEffectsOffSound = GetVarString("EffectsOffSound", "");

	pProp->mfEffectsOnTime = GetVarFloat("EffectsOnTime", 1);
	pProp->mfEffectsOffTime = GetVarFloat("EffectsOffTime", 1);

	pProp->mEffectsOffLightColor = GetVarColor("EffectsOffLightColor", cColor(0,0));
	pProp->mfEffectsOffLightRadius = GetVarFloat("EffectsOffLightRadius", 1);

    //////////////////////////////
	// Load move properties
	pProp->mbMoveCheckCollision = GetVarBool("StaticMoveCheckCollision", true);
	pProp->msMoveStartSound = GetVarString("StaticMoveStartSound", "");
	pProp->msMoveStopSound = GetVarString("StaticMoveStopSound", "");
	pProp->msMoveLoopSound = GetVarString("StaticMoveLoopSound", "");

	gpBase->PreloadSound(pProp->msMoveStartSound);
	gpBase->PreloadSound(pProp->msMoveStopSound);
	gpBase->PreloadSound(pProp->msMoveLoopSound);

	//////////////////////////////
	// Load misc properties
	if(pProp->mpMeshEntity && pProp->mpMeshEntity->GetAnimationStateNum()>0 && GetVarBool("RandomizeAnimationStart", true))
	{
		cAnimationState *pAnim = pProp->mpMeshEntity->GetAnimationState(0);
		float fLength = pAnim->GetLength();
		float fStartTime = cMath::RandRectf(0, fLength);
		
		pAnim->SetTimePosition(fStartTime);
	}
		
	
	//////////////////////////////
	// Load type specific properties
	LoadVariables(pProp, apRootElem);

	//////////////////////////////
	// Extra setup

	//If focus distance is still 0 or below, set it as default.
	if(pProp->mfMaxFocusDistance<=0) pProp->mfMaxFocusDistance = mfDefaultMaxFocusDistance;

	pProp->SetupAfterLoad(apWorld);

	//Add to the current map
	pMap->AddEntity(pProp);

	pProp->SetActive(mbActive);

	///////////////////////////////
	// Instance vars
	if(apInstanceVars)
	{
		if(pProp->mpMeshEntity)
		{
			pProp->mpMeshEntity->SetRenderFlagBit(eRenderableFlag_ShadowCaster, apInstanceVars->GetVarBool("CastShadows", true));
			pProp->mpMeshEntity->SetUpdateBonesWhenCulled(apInstanceVars->GetVarBool("UpdateAnimationWhenCulled", false));
		}

		tString sConnectedProps = apInstanceVars->GetVarString("ConnectedProps", "");
		cString::GetStringVec(sConnectedProps, pProp->mvConnectedProps);

		pProp->msCallbackFunc = apInstanceVars->GetVarString("CallbackFunc", "");
		pProp->msConnectionStateChangeCallback = apInstanceVars->GetVarString("ConnectionStateChangeCallback", "");
		pProp->mbFullGameSave = mbForceFullGameSave ? true : apInstanceVars->GetVarBool("FullGameSave", false);

		pProp->SetPlayerLookAtCallback(	apInstanceVars->GetVarString("PlayerLookAtCallback",""),
										apInstanceVars->GetVarBool("PlayerLookAtCallbackAutoRemove",false) );

		pProp->SetPlayerInteractCallback(	apInstanceVars->GetVarString("PlayerInteractCallback",""),
											apInstanceVars->GetVarBool("PlayerInteractCallbackAutoRemove",false) );

		pProp->SetStaticPhysics(apInstanceVars->GetVarBool("StaticPhysics", false));

		pProp->SetGlowColor(apInstanceVars->GetVarColor("GlowColor", cColor( 0.5,0.5,1.0,0 )));
		pProp->SetGlowOutlineColor(apInstanceVars->GetVarColor("GlowOutlineColor", cColor( 0,0,0.5,0 )));
		pProp->SetGlowEnabled(apInstanceVars->GetVarBool("GlowEnabled", false));

        LoadInstanceVariables(pProp, apInstanceVars);
	}
}

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

//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////

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

iLuxProp::iLuxProp(const tString &asName, int alID, cLuxMap *apMap, eLuxPropType aPropType) : iLuxEntity(asName,alID,apMap, eLuxEntityType_Prop)
{
	mPropType = aPropType;

	mpAttachmentParent = NULL;

	mbCheckOutsidePlayer = false;
	mbIsInteractedWith = false;
	
	mbEffectsActive = true;
	mfEffectsAlpha = 1;
	mbEffectAlphaFading = true;

	mbHasFlickering = false;

	mbInteractionDisabled = false;

	mbStaticPhysics = false;

	mpMainBody = NULL;

	mfMovingVolume =0;
	mfMoveStartCount =0;

	mbMoving = false;
	mbMovingAngular = false;
	mbMovingLinear = false;
	mpMoveLoopSound = NULL;

	mfMoveLinearAcc =0;
	mfMoveLinearSpeed=0;
	mfMoveLinearSlowdownDist=0;
	
	mfMoveAngularAcc =0;
	mfMoveAngularSpeed =0;
	mfMoveAngularSlowdownDist=0;
	
	mlCurrentNonLoopAnimIndex = -1;

	mfFadeInAlpha = 1.0f;
	mfFadeInSpeed =0;
 
	m_mtxLastBodyMoveMatrix = cMatrixf::Identity;

	mfFlashAlpha = 0;

    mcGlowColor = cColor(1,1,1,1);
    mcGlowOutlineColor = cColor(1,1,1,1);
    mbGlowEnabled = true;

    mpParentBone = NULL;

	msParentBoneName = "";
	msParentEntityName = "";
}

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

iLuxProp::~iLuxProp()
{
	cWorld *pWorld = mpWorld;
	iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();

	////////////////////
	// Attachment
	if(mpAttachmentParent)
	{
		mpAttachmentParent->RemoveAttachedProp(this);
		mpAttachmentParent = NULL;
	}
	for(tLuxProp_AttachedPropListIt it = mlstAttachedProps.begin(); it != mlstAttachedProps.end(); ++it)
	{
		//Map is currently deleting all 
		if(mpMap->IsDeletingAllWorldEntities())
		{
			hplDelete(*it);
		}
		else
		{
			DestroyAttachedProp(*it);
		}
	}
	mlstAttachedProps.clear();


	////////////////////
	// Connections
	STLDeleteAll(mvInteractConnections);


	////////////////////
	// Move stuff
	if(mpMoveLoopSound && pWorld->SoundEntityExists(mpMoveLoopSound,mlMoveLoopSoundID))
	{
		pWorld->DestroySoundEntity(mpMoveLoopSound);
	}
	

	////////////////////
	// Check if entity is used
	// TODO?

	////////////////////
	// Destroy physics
	{
		//Joints
		for(size_t i=0; i<mvJoints.size(); ++i)
		{
			iPhysicsJoint *pJoint = mvJoints[i];

			if(pJoint && pPhysicsWorld->JointExists(pJoint))
			{
				pPhysicsWorld->DestroyJoint(pJoint);
			}
		}

		//Bodies
		for(size_t i=0; i<mvBodies.size(); ++i)
		{
			iPhysicsBody *pBody = mvBodies[i];
				
			pWorld->GetPhysicsWorld()->DestroyBody(pBody);
		}
	}

	////////////////////
	// Destroy graphics
	{
		//Mesh entity
		if(mpMeshEntity) pWorld->DestroyMeshEntity(mpMeshEntity);

		//Lights
		for(size_t i=0; i<mvLights.size(); ++i) pWorld->DestroyLight(mvLights[i]);
		
		//Particle systems
		for(size_t i=0; i<mvParticleSystems.size(); ++i) 
		{
			cParticleSystem *pPS = mvParticleSystems[i];
			if(pPS && pWorld->ParticleSystemExists(pPS)) pPS->Kill();
		}

		//Billboards
		for(size_t i=0; i<mvBillboards.size(); ++i)	pWorld->DestroyBillboard(mvBillboards[i]);
		
		//Beams
		for(size_t i=0; i<mvBeams.size(); ++i)		pWorld->DestroyBeam(mvBeams[i]);
		
		//Sound entities
		for(size_t i=0; i<mvSoundEntities.size(); ++i) 	pWorld->DestroySoundEntity(mvSoundEntities[i]);
	}
}

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

//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////

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

void iLuxProp::SetupAfterLoad(cWorld *apWorld)
{
	//////////////////////
	// Normal setup
	mvDefaultBodySettings.resize(mvBodies.size());
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];
        
		mvDefaultBodySettings[i].mbCollideCharacter = pBody->GetCollideCharacter();
	}

	//Get the main body if any
	if(msMainBodyName != "")
	{
		mpMainBody = (iPhysicsBody*)STLFindByName(mvBodies,msName + "_"+ msMainBodyName);
        if(mpMainBody == NULL)
		{
			Warning("Could not find main physics body '%s'\n", msName.c_str());
		}
	}
	
	///////////////////////
	// Set body data
	mvBodyData.resize(mvBodies.size());
	for(size_t i=0; i<mvBodyData.size(); ++i)
	{
		mvBodyData[i].mfMass = mvBodies[i]->GetMass();
	}

	///////////////////////
	// Effects stuff
	SetupEffectData();
	
	///////////////////////
	// Setup implemented 
	OnSetupAfterLoad(apWorld);
}

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

void iLuxProp::SetParentBone(cBoneState * apParentBone, const cMatrixf & apParentBoneOffsetMatrix, tString asParentName, tString asParentBoneName)
{
    mpParentBone = apParentBone;
    mpParentBoneOffsetMatrix = apParentBoneOffsetMatrix;

	msParentEntityName = asParentName;
	msParentBoneName = asParentBoneName;

    if ( mvBodies.size() > 0 )
    {
        iPhysicsBody *pBody = mvBodies[0];

        mfBodyMassBackup = pBody->GetMass();
        mbBodyCollideBackup = pBody->GetCollide();
        mbBodyCollideCharacterBackup = pBody->GetCollideCharacter();
        mbBodyActiveBackup = pBody->IsActive();
        mBodyMatrixBackup = pBody->GetLocalMatrix();

		pBody->SetMass(0);
		pBody->SetCollide(false);
		pBody->SetCollideCharacter(false);
		pBody->SetActive(false);
		pBody->SetMatrix(cMath::MatrixMul(mpParentBone->GetWorldMatrix(),mpParentBoneOffsetMatrix));
    }
}

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

void iLuxProp::DetachFromParentBone()
{
    if ( mpParentBone != NULL && mvBodies.size() > 0 )
    {
        mpParentBone = NULL;

        iPhysicsBody *pBody = mvBodies[0];
        pBody->SetMass(mfBodyMassBackup);
		pBody->SetCollide(mbBodyCollideBackup);
		pBody->SetCollideCharacter(mbBodyCollideCharacterBackup);
		pBody->SetActive(mbBodyActiveBackup);
		//pBody->SetMatrix(mBodyMatrixBackup);
    }

	msParentBoneName = "";
	msParentEntityName = "";
}

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

void iLuxProp::UpdateParentBone(float afTimeStep)
{
    if ( mpParentBone && mvBodies.size() > 0)
	{
        //this->GetMeshEntity()->SetWorldMatrix(cMath::MatrixMul(mpParentBone->GetWorldMatrix(),mpParentBoneOffsetMatrix));
        iPhysicsBody *pBody = mvBodies[0];

        if ( gpBase->mpDebugHandler->GetPositionAttachedProps() )
        {
    		pBody->SetMatrix(cMath::MatrixMul(mpParentBone->GetWorldMatrix(),gpBase->mpDebugHandler->GetParentBoneOffsetMatrix()));
        }
        else
        {
    		pBody->SetMatrix(cMath::MatrixMul(mpParentBone->GetWorldMatrix(),mpParentBoneOffsetMatrix));
        }
	}
}

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

void iLuxProp::OnUpdate(float afTimeStep)
{
	UpdateParentBone(afTimeStep);

	///////////////////////
	// Prop update
	UpdateMoving(afTimeStep);
	UpdateCheckIfOutsidePlayer(afTimeStep);
	UpdateEffectFading(afTimeStep);
	UpdateMeshFading(afTimeStep);
	UpdateAnimation(afTimeStep);
	UpdateAttachedProps(afTimeStep, false);

	///////////////////////
	// Connections
	for(size_t i=0; i<mvInteractConnections.size(); ++i)
	{
		mvInteractConnections[i]->Update(afTimeStep);
		mvInteractConnections[i]->UpdateProp(afTimeStep);
	}

	//////////////////////
	// Specific update
	UpdatePropSpecific(afTimeStep);

	//if(mvLights.size() > 0)
	//	Log("End Color: %s\n", mvLights[0]->GetDiffuseColor().ToString().c_str());
}

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

void iLuxProp::FlashIfNearPlayer(float afTimeStep)
{
    if ( !mbGlowEnabled ) return;

    /////////////////////////////////
	// If near player, flash
	cCamera *pCam =  gpBase->mpPlayer->GetCamera();
	cVector3f vCameraPos = pCam->GetPosition();
	cVector3f vBodyPos = mvBodies[0]->GetLocalPosition();
	vCameraPos.y=0; 
	vBodyPos.y =0;

	float fDistSqrt = cMath::Vector3DistSqr(vCameraPos, vBodyPos);
	if(fDistSqrt < 4.0f * 4.0f)
	{
		mfFlashAlpha += afTimeStep;
		if(mfFlashAlpha >1)mfFlashAlpha =1;
	}
	else
	{
		mfFlashAlpha -=afTimeStep;
		if(mfFlashAlpha <0)mfFlashAlpha =0;
	}

	if(mfFlashAlpha> 0)
	{
		for(int i=0; i<mpMeshEntity->GetSubMeshEntityNum(); ++i)
		{
			cSubMeshEntity *pSubEnt = mpMeshEntity->GetSubMeshEntity(i);

			if(pCam->GetFrustum()->CollideBoundingVolume(pSubEnt->GetBoundingVolume()) != eCollision_Outside)
				gpBase->mpEffectRenderer->AddFlashObject(pSubEnt, mfFlashAlpha, GetGlowColor());
		}
	}
}

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

void iLuxProp::BeforeEntityDestruction()
{
	if(mbDissolveOnDestruction && mpMeshEntity)
	{
		mpMap->AddDissolveEntity(mpMeshEntity, mfDissolveTime);
	}

	BeforePropDestruction();
}

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

void iLuxProp::OnRenderSolid(cRendererCallbackFunctions* apFunctions)
{
	return;
	if(mvBodies.size()<=0) return;
	
	cBoundingVolume* pBV = mvBodies[0]->GetBoundingVolume();
	apFunctions->GetLowLevelGfx()->DrawBoxMinMax(pBV->GetMin(), pBV->GetMax(),cColor(1,1,1,1));
}

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

void iLuxProp::SetEffectsActive(bool abActive, bool abFadeAndPlaySounds)
{
	if(abActive == mbEffectsActive) return;

	mbEffectsActive = abActive;

	mbEffectAlphaFading = true;

	///////////////////////
	// Effects On stuff
	if(mbEffectsActive)
	{
		////////////////
		// Create particle systems
		for(size_t i=0; i<mvParticleSystems.size(); ++i)
		{
			cLuxProp_PSData *pPSData = &mvEffectPSData[i];
			cParticleSystem *pPS = mpWorld->CreateParticleSystem(pPSData->msName, pPSData->msDataName,1);
			if(pPS)
			{
				pPS->SetMatrix(pPSData->m_mtxLocalTransform);
				if(pPSData->mpParent) pPSData->mpParent->AddChild(pPS);
			}

			mvParticleSystems[i] = pPS;
		}

		////////////////
		// Lights
		for(size_t i=0; i<mvLights.size(); ++i)
		{	
			iLight *pLight = mvLights[i];
			cLuxProp_LightData *pLightData = &mvEffectLightData[i];

			if(abFadeAndPlaySounds)
			{
				pLight->FadeTo(pLightData->mOnColor, pLightData->mfOnRadius, mfEffectsOnTime);
			}
			else 
			{
				pLight->SetDiffuseColor(pLightData->mOnColor);
				pLight->SetRadius(pLightData->mfOnRadius);
				pLight->SetFlickerActive(pLightData->mbFlickering);
			}
		}

		////////////////
		// Sounds
		for(size_t i=0; i<mvSoundEntities.size(); ++i)
		{
			cSoundEntity *pSound = mvSoundEntities[i];

			if(abFadeAndPlaySounds)	pSound->FadeIn(1.0f / mfEffectsOnTime);
			else					pSound->Play(false);
		}

		////////////////
		// Effect alpha
		if(abFadeAndPlaySounds==false) mfEffectsAlpha = 1.0f;
	}
	///////////////////////
	// Effects Off stuff
	else
	{
		////////////////
		// Kill / Destroy particle systems
		for(size_t i=0; i<mvParticleSystems.size(); ++i)
		{
			cParticleSystem *pPS = mvParticleSystems[i];
			mvParticleSystems[i] = NULL;

			if(abFadeAndPlaySounds)	pPS->Kill();
			else					mpWorld->DestroyParticleSystem(pPS);
		}

		////////////////
		// Lights
		for(size_t i=0; i<mvLights.size(); ++i)
		{	
			iLight *pLight = mvLights[i];
			cLuxProp_LightData *pLightData = &mvEffectLightData[i];

			pLight->StopFading();
			pLight->SetFlickerActive(false);
			float fOffRadius = mfEffectsOffLightRadius >=0 ? mfEffectsOffLightRadius : pLightData->mfOnRadius;
			if(abFadeAndPlaySounds)
			{	
				pLight->FadeTo(mEffectsOffLightColor, fOffRadius, mfEffectsOffTime);
			}
			else
			{	
				pLight->SetDiffuseColor(mEffectsOffLightColor);
				pLight->SetRadius(fOffRadius);
			}
		}

		////////////////
		// Sounds
		for(size_t i=0; i<mvSoundEntities.size(); ++i)
		{
			cSoundEntity *pSound = mvSoundEntities[i];

			if(abFadeAndPlaySounds)	pSound->FadeOut(1.0f / mfEffectsOffTime);
			else					pSound->Stop(false);
		}

		////////////////
		// Effect alpha
		if(abFadeAndPlaySounds==false) mfEffectsAlpha = 0.0f;
	}

	////////////////
	// Play sound
	if(abFadeAndPlaySounds)
	{
		tString sSound = mbEffectsActive ? msEffectsOnSound : msEffectsOffSound;

		if(sSound != "")
		{
			cSoundEntity *pSound = mpWorld->CreateSoundEntity(msName + "_lightlitchange",sSound, true);
			if(pSound)
			{
				pSound->SetPosition(GetWorldCenterPos());
			}
		}
	}
}

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

void iLuxProp::GiveDamage(float afAmount, int alStrength)
{
	if(alStrength < mlToughness-1)			afAmount =0;
	else if(alStrength == mlToughness-1)	afAmount *= 0.5;
	
	SetHealth(mfHealth - afAmount);

	OnDamage(afAmount, alStrength);
}

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

void iLuxProp::SetDisableCollisionUntilOutSidePlayer(bool abX)
{
	mbCheckOutsidePlayer = abX;

	if(mbCheckOutsidePlayer)
	{
		for(size_t i=0; i<mvBodies.size(); ++i)
		{
			iPhysicsBody *pBody = mvBodies[i];
			pBody->SetCollideCharacter(false);
		}
	}
}

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

void iLuxProp::MoveLinearTo(const cVector3f& avGoal, float afAcc, float afMaxSpeed, float afSlowdownDist, bool abResetSpeed)
{
	if(mbMoving == false && mfMoveStartCount <=0)
	{
		PlaySound("MoveStart", msMoveStartSound,true, true);
	}
	mfMoveStartCount = 1.0f;

	mbMoving = true;
	mbMovingLinear = true;
	
	mvMoveLinearGoal = avGoal;
	mfMoveLinearAcc = afAcc;
	mfMoveLinearMaxSpeed = afMaxSpeed;
	mfMoveLinearSlowdownDist = afSlowdownDist;

	if(abResetSpeed) mfMoveLinearSpeed =0;

	OnStartMove();
}

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

void iLuxProp::MoveAngularTo(const cMatrixf& a_mtxGoal, float afAcc, float afMaxSpeed, float afSlowdownDist, bool abResetSpeed, 
							 bool abUseOffset, const cVector3f &avWorldOffset, const cVector3f &avLocalOffset)
{
	if(mbMoving == false && mfMoveStartCount <=0)
	{
		PlaySound("MoveStart", msMoveStartSound,true, true);
	}
	mfMoveStartCount = 1.0f;

	mbMoving = true;
	mbMovingAngular = true;
	
	m_mtxMoveAngularGoal = a_mtxGoal;
	mfMoveAngularAcc = afAcc;
	mfMoveAngularMaxSpeed = afMaxSpeed;
	mfMoveAngularSlowdownDist = afSlowdownDist;

	if(abResetSpeed) mfMoveAngularSpeed =0;

	mbMoveAngularUseOffset = abUseOffset;
	mvMoveAngularWorldOffset = avWorldOffset;
	mvMoveAngularLocalOffset = avLocalOffset;

	mbMoveAngularNoGoal = false;

	OnStartMove();
}

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

void iLuxProp::RotateAtSpeed(	float afAcc, float afGoalSpeed, const cVector3f& avAxis, bool abResetSpeed,
								 bool abUseOffset, const cVector3f &avWorldOffset, const cVector3f &avLocalOffset)
{
	if(mbMoving == false && mfMoveStartCount <=0) 
	{
		PlaySound("MoveStart", msMoveStartSound,true, true);
	}
	mfMoveStartCount = 1.0f;

	mbMoving = true;
	mbMovingAngular = true;

	mfMoveAngularAcc = afAcc;
	mfMoveAngularMaxSpeed = afGoalSpeed;
	
	if(abResetSpeed) mfMoveAngularSpeed =0;

	mbMoveAngularNoGoal = true;
	mvMoveAngularNoGoalDir = cMath::Vector3Normalize(avAxis);

	mbMoveAngularUseOffset = abUseOffset;
	mvMoveAngularWorldOffset = avWorldOffset;
	mvMoveAngularLocalOffset = avLocalOffset;

	OnStartMove();
}

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

void iLuxProp::StopMove()
{
	mbMovingLinear=false;
	mbMovingAngular=false;
	mbMoving = false;
	
	if(mfMoveStartCount <=0)
		PlaySound("MoveStop", msMoveStopSound,true, true);
}

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

void iLuxProp::FadeInMeshEntity(float afTime)
{
	mfFadeInAlpha =0;
	mfFadeInSpeed = 1.0f/ afTime;
	if(mpMeshEntity) mpMeshEntity->SetCoverageAmount(0);
}

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

void iLuxProp::ResetProperties()
{
	///////////////////////////////////
	// Reset bodies
    for(size_t i=0; i<mvBodies.size(); ++i)
	{
		cMatrixf mtxBody = cMath::MatrixMul(m_mtxOnLoadTransform, mvBodyExtraData[i].m_mtxLocalTransform);
        iPhysicsBody *pBody = mvBodies[i];

		pBody->SetMatrix(mtxBody);
		pBody->SetLinearVelocity(0);
		pBody->SetAngularVelocity(0);
	}
	
	///////////////////////////////////
	// Reset variables
	mbMoving = false;
	mbMovingAngular = false;
	mbMovingLinear = false;
	mpMoveLoopSound = NULL;

	mfMoveAngularSpeed =0;

	mfHealth = 100;

	///////////////////////////////////
	// Reset type specific
	OnResetProperties();
}

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

void iLuxProp::PlayAnimation(const tString& asName, float afFadeTime, bool abLoop, const tString& asCallback)
{
	if(mpMeshEntity==NULL) return;

	int lIdx = mpMeshEntity->GetAnimationStateIndex(asName);
	if(lIdx<0)
	{
		Error("Could not find animation '%s' for entity '%s'\n", asName.c_str(), msName.c_str());
		return;
	}

	mpMeshEntity->PlayFadeTo(lIdx, abLoop, afFadeTime);
	if(abLoop==false)	mlCurrentNonLoopAnimIndex = lIdx;
	else				mlCurrentNonLoopAnimIndex = -1;
	
	if(abLoop==false)	msAnimCallback = asCallback;
	else				msAnimCallback = "";
}

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

void iLuxProp::StopAnimation()
{
	if(mpMeshEntity==NULL) return;

	mpMeshEntity->Stop();
}

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

void iLuxProp::PlayCurrentAnimation(float afFadeTime, bool abLoop)
{
	if(mpMeshEntity==NULL) return;

	mpMeshEntity->FadeInCurrent(afFadeTime, abLoop);
}
	
void iLuxProp::PauseCurrentAnimation(float afFadeTime)
{
	if(mpMeshEntity==NULL) return;

	mpMeshEntity->FadeOutCurrent(afFadeTime);
}

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

void iLuxProp::SetAnimationSpeed(float afSpeed)
{
	if(mpMeshEntity==NULL) return;

	//////////////
	// Set the animation speed of all states
	for(int i = 0; i < mpMeshEntity->GetAnimationStateNum(); ++i)
	{
		cAnimationState *pState = mpMeshEntity->GetAnimationState(i);

		if(pState)
		{
			////////////
			// Set the speed
			pState->SetSpeed(afSpeed);
		}
	}
}

void iLuxProp::SetAnimationPosition(float afPosition)
{
	if(mpMeshEntity==NULL) return;

	//////////////
	// Set the animation speed of all states
	for(int i = 0; i < mpMeshEntity->GetAnimationStateNum(); ++i)
	{
		cAnimationState *pState = mpMeshEntity->GetAnimationState(i);

		if(pState->IsActive())
		{
			////////////
			// Set the speed
			pState->SetTimePosition(afPosition);
		}
	}
}

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

void iLuxProp::SetHealth(float afX)
{
	mfHealth = afX;

	OnHealthChange();
}

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

iEntity3D* iLuxProp::GetAttachEntity()
{
	if(mvBodies.empty())
	{
		return mpMeshEntity;
	}
	else
	{
		return mpMainBody ? mpMainBody : mvBodies[0];
	}
}

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

void iLuxProp::SetStaticPhysics(bool abX)
{
	if(mbStaticPhysics == abX) return;

	mbStaticPhysics = abX;

    for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];

		pBody->SetMass(mbStaticPhysics ? 0 : mvBodyData[i].mfMass);
		pBody->SetLinearVelocity(0);
		pBody->SetAngularVelocity(0);
	}
}

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

void iLuxProp::AddAndAttachProp(const tString& asName, const tString& asFileName, const cMatrixf& a_mtxOffset)
{
	if(GetMainBody()==NULL)
	{
		Error("Could not attach prop %s to %s because it does not have a main body!\n", msName.c_str(), asFileName.c_str());
		return;
	}

	/////////////////////////////////
	// Create the entity
	mpMap->ResetLatestEntity();
	mpMap->CreateEntity(asName, asFileName, cMatrixf::Identity,1);
    
	iLuxEntity *pEntity = mpMap->GetLatestEntity();
	if(pEntity==NULL || pEntity->GetEntityType() != eLuxEntityType_Prop || pEntity->GetName() != asName)
	{
		Error("Could not create attach entity '%s' for prop '%s'\n", asFileName.c_str(), msName.c_str());
		return;
	}

	iLuxProp* pProp =  static_cast<iLuxProp*>(pEntity);

	/////////////////////////////////
	// This prop shall not be saved!
	pProp->SetIsSaved(false);

	/////////////////////////////////
	// Turn off collision on all bodies and set mass to 0
	for(size_t i=0; i<pProp->mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = pProp->mvBodies[i];
		pBody->SetMass(0);
		pBody->SetCollide(false);
		pBody->SetCollideCharacter(false);
		pBody->SetActive(false);
	}
	
	/////////////////////////////////
	// Create attachment data
	cLuxProp_AttachedProp *pAttachProp = hplNew(cLuxProp_AttachedProp, ());
	pAttachProp->msName = asName;
	pAttachProp->msFileName = asFileName;
	pAttachProp->m_mtxOffset = a_mtxOffset;
	pAttachProp->mpProp = pProp;

	mlstAttachedProps.push_back(pAttachProp);

	//////////////////////////////////
	// Update the attached prop matrix so it starts out properly!
	UpdateAttachedProps(0, true);
}

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

bool iLuxProp::DestroyAttachedProp(const tString& asName)
{
	tLuxProp_AttachedPropListIt it = mlstAttachedProps.begin(); 
	for(; it != mlstAttachedProps.end(); ++it)
	{
		cLuxProp_AttachedProp *pAttachedProp = *it;
        if(pAttachedProp->msName == asName)
		{
			DestroyAttachedProp(pAttachedProp);
			mlstAttachedProps.erase(it);
			return true;
		}
	}

	return false;
}

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

void iLuxProp::DestroyAttachedProp(cLuxProp_AttachedProp *apAttachedProp)
{
	apAttachedProp->mpProp->mpAttachmentParent = NULL; //Must set this null, else it will try and erase itself when delted!
    mpMap->DestroyEntity(apAttachedProp->mpProp);
	hplDelete(apAttachedProp);
}

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

bool iLuxProp::RemoveAttachedProp(iLuxProp *apProp)
{
	tLuxProp_AttachedPropListIt it = mlstAttachedProps.begin(); 
	for(; it != mlstAttachedProps.end(); ++it)
	{
		cLuxProp_AttachedProp *pAttachedProp = *it;
		if(pAttachedProp->mpProp == apProp)
		{
			apProp->SetAttachmentParent(NULL);
			hplDelete(pAttachedProp);

			mlstAttachedProps.erase(it);
			return true;
		}
	}
	return false;
}

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

int iLuxProp::AddInteractConnection(iLuxInteractConnection *apConnection)
{
	mvInteractConnections.push_back(apConnection);
	return (int) mvInteractConnections.size()-1;
}

iLuxInteractConnection* iLuxProp::GetInteractionConnectionFromIndex(int alIndex)
{
	return mvInteractConnections[alIndex];
}

iLuxInteractConnection* iLuxProp::GetInteractionConnectionFromName(const tString& asName)
{
	return (iLuxInteractConnection*)STLFindByName(mvInteractConnections, asName);
}

void iLuxProp::DestroyInteractConnection(iLuxInteractConnection *apConnection)
{
	STLFindAndDelete(mvInteractConnections, apConnection);
}

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

void iLuxProp::InteractConnectionLimit(int alState)
{
	for(size_t i=0; i<mvInteractConnections.size(); ++i)
	{
		iLuxInteractConnection *pConnection = mvInteractConnections[i];
        
		if(pConnection->GetInteractionOnly() && mbIsInteractedWith==false) continue;

		mvInteractConnections[i]->OnLimit(alState);	
	}
}

void iLuxProp::InteractConnectionTurn(float afAngle, float afPrevAngle, float afMinAngle, float afMaxAngle)
{
	//if(abs(afAngle - afPrevAngle) < 0.001f) return;

	float fT = (afAngle-afMinAngle)/(afMaxAngle - afMinAngle);

	for(size_t i=0; i<mvInteractConnections.size(); ++i)
	{
		iLuxInteractConnection *pConnection = mvInteractConnections[i];

		if(pConnection->GetInteractionOnly() && mbIsInteractedWith==false) continue;

		pConnection->OnTurn(afAngle - afPrevAngle, fT);	
	}
}

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

iPhysicsBody* iLuxProp::GetBodyFromID(int alID)
{
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];
		if(pBody->GetUniqueID() == alID) return pBody;
	}

	return NULL;
}

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

int iLuxProp::GetBodyIndexFromName(const tString& asName)
{
	tString sFinalName = msName + '_'+asName;
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];
		if(pBody->GetName() == sFinalName) return (int)i;
	}
	return -1;
}

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


//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////

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

void iLuxProp::OnSetActive(bool abX)
{	
	///////////////
	//Bodies
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		mvBodies[i]->SetActive(abX);
	}

	///////////////////
	//Mesh entity
	if(mpMeshEntity && mbShowMesh)
	{
		mpMeshEntity->SetActive(abX);
		mpMeshEntity->SetVisible(abX);
	}

	///////////////////
	//Lights
	for(size_t i=0; i<mvLights.size(); ++i)
	{
		mvLights[i]->SetVisible(abX);
		mvLights[i]->SetActive(abX);
	}

	///////////////////
	//Particle systems
	for(size_t i=0; i<mvParticleSystems.size(); ++i) 
	{
		cParticleSystem *pPS = mvParticleSystems[i];
		if(pPS)
		{
			pPS->SetVisible(abX);
			pPS->SetActive(abX);
		}
	}

	///////////////////
	//Billboards
	for(size_t i=0; i<mvBillboards.size(); ++i)
	{
		mvBillboards[i]->SetActive(abX);
		mvBillboards[i]->SetVisible(abX);
	}

	///////////////////
	//Beams
	for(size_t i=0; i<mvBeams.size(); ++i)
	{
		mvBeams[i]->SetActive(abX);
		mvBeams[i]->SetVisible(abX);
	}

	///////////////////
	//Sound entities
	for(size_t i=0; i<mvSoundEntities.size(); ++i) 
	{
		cSoundEntity *pSoundEntity = mvSoundEntities[i];

		if(abX)	pSoundEntity->Play(false);
		else	pSoundEntity->Stop(false);	
	}
	
	ImplementedOnSetActive(abX);
}



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

void iLuxProp::SetupEffectData()
{
	/////////////////////
	// Get particle data
	for(size_t i=0; i<mvParticleSystems.size(); ++i)
	{
		cParticleSystem *pPS = mvParticleSystems[i];
		cLuxProp_PSData psData;

		psData.msName = pPS->GetName().c_str();
		psData.msDataName = pPS->GetDataName();
		psData.m_mtxLocalTransform = pPS->GetLocalMatrix();
		psData.mpParent = pPS->GetEntityParent();

		mvEffectPSData.push_back(psData);
	}

	/////////////////////
	// Get light data
	for(size_t i=0; i<mvLights.size(); ++i)
	{
		iLight *pLight = mvLights[i];
		cLuxProp_LightData lightData;

		lightData.mOnColor = pLight->GetDiffuseColor();
		lightData.mfOnRadius = pLight->GetRadius();
		lightData.mbFlickering = pLight->GetFlickerActive();

		//TODO: Check all lights?
		if(i==0 && pLight->GetFlickerActive()) mbHasFlickering = true;

		mvEffectLightData.push_back(lightData);
	}

	/////////////////////
	// Get billboard data
	for(size_t i=0; i<mvBillboards.size(); ++i)
	{
		cBillboard *pBillboard = mvBillboards[i];
		cLuxProp_BillboardData bbData;

		bbData.mbConnectedToLight = BillboardConnectedToLight(pBillboard);
		bbData.mOnColor = pBillboard->GetColor();

		mvEffectBillboardData.push_back(bbData);	
	}
}

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

bool iLuxProp::BillboardConnectedToLight(cBillboard *apBB)
{
	for(size_t light=0; light<mvLights.size(); ++light)
	{
		iLight *pLight = mvLights[light];
		std::vector<cLightBillboardConnection> *pBBVEc = pLight->GetBillboardVec();
		if(pBBVEc->empty()) continue;
        
		for(size_t i=0; i<pBBVEc->size(); ++i)
		{
			if( (*pBBVEc)[i].mpBillboard == apBB) return true;
		}
	}
	return false;
}

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

void iLuxProp::UpdateEffectFading(float afTimeStep)
{
	if(mbEffectAlphaFading==false) return;

	/////////////////
	// Turn on effect
	if(mbEffectsActive)
	{
		mfEffectsAlpha += (1.0f/mfEffectsOnTime) * afTimeStep;
		if(mfEffectsAlpha > 1.0f)
		{
			mfEffectsAlpha = 1.0f;

			for(size_t i=0; i<mvLights.size(); ++i)
			{	
				iLight *pLight = mvLights[i];
				pLight->SetFlickerActive(mvEffectLightData[i].mbFlickering);
			}
			
			mbEffectAlphaFading = false;
		}
	}
	/////////////////
	// Turn off effect
	else
	{
		mfEffectsAlpha -= (1.0f/mfEffectsOffTime) * afTimeStep;
		if(mfEffectsAlpha < 0)
		{
			mfEffectsAlpha = 0;
			mbEffectAlphaFading = false;
		}
	}


	/////////////////////
	// Billboards
	for(size_t i=0; i<mvBillboards.size(); ++i)
	{
		cBillboard *pBillboard = mvBillboards[i];
		if(mvEffectBillboardData[i].mbConnectedToLight) continue;
		
		pBillboard->SetColor(mvEffectBillboardData[i].mOnColor * mfEffectsAlpha);
	}

	/////////////////////
	// Mesh illumination alpha
	if(mpMeshEntity)
	{
		mpMeshEntity->SetIlluminationAmount(mfEffectsAlpha);
	}
}


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

void iLuxProp::UpdateMeshFading(float afTimeStep)
{
	if(mfFadeInAlpha >= 1 || mpMeshEntity==NULL) return;

    mfFadeInAlpha += mfFadeInSpeed * afTimeStep;
	mpMeshEntity->SetCoverageAmount(mfFadeInAlpha);
}

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

void iLuxProp::UpdateAttachedProps(float afTimeStep, bool abForceUpdate)
{
	if(mlstAttachedProps.empty()) return;
	if(abForceUpdate==false && GetMainBody()->GetEnabled()==false) return;

	tLuxProp_AttachedPropListIt it = mlstAttachedProps.begin(); 
	for(; it != mlstAttachedProps.end(); ++it)
	{
		cLuxProp_AttachedProp *pAttachProp = *it;
		iLuxProp *pProp = pAttachProp->mpProp;
		if(pProp->GetBodyNum()>0)
        {
		    iPhysicsBody *pBody = pProp->GetBody(0);
		
		    cMatrixf mtxProp = cMath::MatrixMul(GetMainBody()->GetLocalMatrix(), 
											    cMath::MatrixMul(pAttachProp->m_mtxOffset, pProp->mvBodyExtraData[0].m_mtxLocalTransform));
		    pBody->SetMatrix(mtxProp);
        }
        else if ( pProp->GetMeshEntity() != NULL )
        {
            cMatrixf mtxProp = cMath::MatrixMul(GetMainBody()->GetWorldMatrix(), pAttachProp->m_mtxOffset);

            pProp->GetMeshEntity()->SetWorldMatrix( mtxProp );
        }
	}
}

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

void iLuxProp::UpdateAnimation(float afTimeStep)
{
	if(mlCurrentNonLoopAnimIndex<0) return;

    cAnimationState *pAnimState = mpMeshEntity->GetAnimationState(mlCurrentNonLoopAnimIndex);	
	if(pAnimState->IsOver())
	{
		mlCurrentNonLoopAnimIndex = -1;
		if(msAnimCallback !="")
			mpMap->RunScript(msAnimCallback + "(\""+ msName + "\")");
	}
}

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

void iLuxProp::UpdateMoveSoundVolume()
{
	if(mpMoveLoopSound==NULL) return;

	cSoundEntry *pEntry = mpMoveLoopSound->GetSoundEntry(eSoundEntityType_Main, true);
	if(pEntry) pEntry->SetVolumeMul(mfMovingVolume);
}

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

void iLuxProp::UpdateMoving(float afTimeStep)
{
	////////////////////////////////
	// Update move start count
	if(mfMoveStartCount > 0)
	{
		mfMoveStartCount -= afTimeStep;
	}

	////////////////////////////////
	// Check if moving and update move sound
	if(mbMoving ==false)
	{
		//Fade out move sound
		if(mfMovingVolume >0)
		{
			mfMovingVolume -= afTimeStep;
			if(mfMovingVolume < 0)
			{
				mfMovingVolume =0;
				if(mpMoveLoopSound)
				{
					mpMoveLoopSound->Stop(false);
					mpMap->GetWorld()->DestroySoundEntity(mpMoveLoopSound);

					mpMoveLoopSound = NULL;
				}
			}
			else
			{
				UpdateMoveSoundVolume();
			}
		}

		return;
	}

	//////////////////////////////
	//Check if speed is enough!
	bool bHasMoveSpeed = false;
	if(mbMovingLinear)
	{
		const float fMinLinear = 0.001f * (1.0f/60.0f);

		float fSpeedSqr = cMath::Vector3DistSqr(m_mtxLastBodyMoveMatrix.GetTranslation(), GetMainBody()->GetLocalMatrix().GetTranslation());
		if(fSpeedSqr > fMinLinear*fMinLinear) bHasMoveSpeed = true;
	}
	if(bHasMoveSpeed == false && mbMovingAngular)
	{
		const float fMinAngular = 0.001f * (1.0f/60.0f);

		cVector3f vVel = cMath::MatrixEulerAngleDistance(m_mtxLastBodyMoveMatrix.GetRotation(), GetMainBody()->GetLocalMatrix().GetRotation());
		float fSpeedSqr = vVel.SqrLength();
		if(fSpeedSqr > fMinAngular*fMinAngular) bHasMoveSpeed = true;
	}

	//////////////////////////////
	//Fade in move sound
	if(bHasMoveSpeed && mfMovingVolume < 1)
	{
		//////////////////////////////
		// If no move sound, start it!
		if(mpMoveLoopSound==NULL && msMoveLoopSound != "")
		{
			mpMoveLoopSound = PlaySound("MoveLoop",msMoveLoopSound,false, true);
			if(mpMoveLoopSound)
			{
				mlMoveLoopSoundID = mpMoveLoopSound->GetCreationID();
				mpMoveLoopSound->Play(false);
				
				cSoundEntry* pEntry = mpMoveLoopSound->GetSoundEntry(eSoundEntityType_Main, true);
				if(pEntry) pEntry->SetVolumeMul(0);
			}
		}

		//////////////////////////////
		// Increase volume
		mfMovingVolume += afTimeStep*1.2f;//Important that sounds fades in faster than out! (in case mbMoving "flickers" )
		if(mfMovingVolume >1) mfMovingVolume =1.0f;
		
		//////////////////////////////
		// Update sound
		UpdateMoveSoundVolume();
	}
	//////////////////////////////
	//Fade out move sound
	else if(bHasMoveSpeed==false && mfMovingVolume > 0)
	{
		mfMovingVolume -= afTimeStep;
		if(mfMovingVolume <0)
		{
			mfMovingVolume =0;
			if(mpMoveLoopSound && mpMap->GetWorld()->SoundEntityExists(mpMoveLoopSound, mlMoveLoopSoundID))
			{
				mpMoveLoopSound->Stop(false);
				mpMap->GetWorld()->DestroySoundEntity(mpMoveLoopSound);
				
				mpMoveLoopSound = NULL;
				mlMoveLoopSoundID = -1;
			}
		}
		else
		{
			UpdateMoveSoundVolume();
		}
	}

	///////////////////////////
	// Save the last matrix before updating.
	m_mtxLastBodyMoveMatrix = GetMainBody()->GetLocalMatrix();

	///////////////////////////
	// Update movement matrix
    UpdateLinearMoving(afTimeStep);    
	UpdateAngularMoving(afTimeStep);

	///////////////////////////
	// Stop movement
	if(mbMovingLinear==false && mbMovingAngular==false)
	{
		StopMove();
	}
}

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

void iLuxProp::UpdateLinearMoving(float afTimeStep)
{
	if(mbMovingLinear==false) return;
	
	iPhysicsBody *pBody = GetMainBody();

    cVector3f vDelta = mvMoveLinearGoal - pBody->GetLocalPosition();

	float fDist = vDelta.Length();

	cVector3f vMoveVel = 0;

	/////////////
	//Check if almost at goal
	if(fDist <= mfMoveLinearSpeed*afTimeStep*1.05f || fDist < 0.01)
	{
		mfMoveLinearSpeed =0;
		vMoveVel = vDelta / afTimeStep;
		mbMovingLinear = false;
	}
	/////////////
	//Update speed
	else
	{
		cVector3f vDir = vDelta / fDist;

		if(fDist < mfMoveLinearSlowdownDist)
		{
			mfMoveLinearSpeed = (fDist / mfMoveLinearSlowdownDist) * mfMoveLinearMaxSpeed;
		}
		else
		{
			mfMoveLinearSpeed += mfMoveLinearAcc * afTimeStep;
			if(mfMoveLinearSpeed > mfMoveLinearMaxSpeed) mfMoveLinearSpeed = mfMoveLinearMaxSpeed;
		}

		vMoveVel = vDir * mfMoveLinearSpeed;
	}

	//////////////////////////
	//Check collision, if force == 0 skip!
	if(mbMoveCheckCollision)
		CheckMoveCollision(vMoveVel, mfMoveLinearSpeed, afTimeStep);

	//////////////////////////
	//Update body position
	if(vMoveVel != 0)
	{
		pBody->StaticLinearMove(vMoveVel);
	}
}

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

void iLuxProp::UpdateAngularMoving(float afTimeStep)
{
	if(mbMovingAngular==false) return;

	iPhysicsBody *pBody = GetMainBody();
	cVector3f vMoveVel =0;
	
	//Not used! Old stuff. The angles returned can be quite different at times when no rotaiton is needed (0,0,0) (360,0,360)
	//cVector3f vCurrentAngles =	cMath::MatrixToEulerAngles(pBody->GetLocalMatrix(), eEulerRotationOrder_XYZ);
	//cVector3f vWantedAngles =	cMath::MatrixToEulerAngles(m_mtxMoveAngularGoal, eEulerRotationOrder_XYZ);
	//cVector3f vDelta = cMath::Vector3AngleDistanceRad(vCurrentAngles, vWantedAngles);
	//Log("B---------------\n");
	//Log("Angles %s -> %s\n", cMath::Vector3ToDeg(vCurrentAngles).ToString().c_str(), cMath::Vector3ToDeg(vWantedAngles).ToString().c_str());
	
	/////////////////////////////////
	// Move without goal
	if(mbMoveAngularNoGoal)
	{
		float fAcc = mfMoveAngularMaxSpeed < mfMoveAngularSpeed ? -mfMoveAngularAcc : mfMoveAngularAcc;

		mfMoveAngularSpeed += fAcc * afTimeStep;
		if( (fAcc < 0 && mfMoveAngularSpeed < mfMoveAngularMaxSpeed) ||
			(fAcc > 0 && mfMoveAngularSpeed > mfMoveAngularMaxSpeed) )
		{
			mfMoveAngularSpeed = mfMoveAngularMaxSpeed;
			if(mfMoveAngularSpeed == 0)
			{
				mbMovingAngular = false;
			}
		}

		vMoveVel = mvMoveAngularNoGoalDir * mfMoveAngularSpeed;
	}
	/////////////////////////////////
	// Move with goal
	else
	{
		cVector3f vDelta = cMath::MatrixEulerAngleDistance(pBody->GetLocalMatrix(), m_mtxMoveAngularGoal);

		float fDist = vDelta.Length();
		
		/////////////
		//Check if almost at goal
		if(fDist <= mfMoveAngularSpeed*afTimeStep*1.05f || fDist < 0.01)
		{
			mfMoveAngularSpeed =0;
			vMoveVel = vDelta / afTimeStep;
			mbMovingAngular = false;
		}
		/////////////
		//Update speed
		else
		{
			cVector3f vDir = vDelta / fDist;

			if(fDist < mfMoveAngularSlowdownDist)
			{
				mfMoveAngularSpeed = (fDist / mfMoveAngularSlowdownDist) * mfMoveAngularMaxSpeed;
			}
			else
			{
				mfMoveAngularSpeed += mfMoveAngularAcc * afTimeStep;
				if(mfMoveAngularSpeed > mfMoveAngularMaxSpeed) mfMoveAngularSpeed = mfMoveAngularMaxSpeed;
			}

			vMoveVel = vDir * mfMoveAngularSpeed;
		}
	}

	//Log("Move speed: %s\n", vMoveVel.ToString().c_str());

	/////////////////////////////////
	// Rotate the body
	pBody->StaticAngularMove(vMoveVel);

	/////////////
	//Update position too.
	if(mbMoveAngularUseOffset)
	{
		cMatrixf mtxNewBody = cMath::MatrixMul(cMath::MatrixRotate(vMoveVel*afTimeStep,eEulerRotationOrder_XYZ), pBody->GetLocalMatrix().GetRotation());
		mtxNewBody.SetTranslation(pBody->GetLocalMatrix().GetTranslation());
		cVector3f vOffset = cMath::MatrixMul(mtxNewBody, mvMoveAngularLocalOffset);

		cVector3f vDiff = mvMoveAngularWorldOffset - vOffset;
		pBody->StaticLinearMove(vDiff / afTimeStep );
    }
}


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

void iLuxProp::CheckMoveCollision(cVector3f& avMoveVel, float &afSpeed, float afTimeStep)
{
	if(avMoveVel==0) return;

	iPhysicsWorld *pPhysicsWorld = mpMap->GetPhysicsWorld();
	iPhysicsBody *pBody = GetMainBody();

	//Get matrix after movement
	cMatrixf mtxNew = pBody->GetLocalMatrix();
	cVector3f vAdd = avMoveVel*afTimeStep;
	mtxNew.SetTranslation(mtxNew.GetTranslation() + vAdd);

	//Log("Checking collision!\n");

	//Check collision and get push back
	cVector3f vPushVec(0);
	pPhysicsWorld->CheckShapeWorldCollision(&vPushVec,pBody->GetShape(),mtxNew, NULL, true, false);
	if(vPushVec == 0) return;
	
	//See how much of the velocity that needs to be "removed".
	float fAddLengthSqr  = vAdd.SqrLength();
	float fVelAmount = 1.0f - cMath::Vector3Dot(vPushVec, vAdd*-1.0f)/fAddLengthSqr;
	//fVelAmount -= 0.001f;
	if(fVelAmount < 0) fVelAmount =0;
	
	//Update speed and velocity
	afSpeed = afSpeed * fVelAmount;
	avMoveVel =  avMoveVel * fVelAmount;	
}

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

void iLuxProp::UpdateCheckIfOutsidePlayer(float afTimeStep)
{
	if(mbCheckOutsidePlayer==false) return;

	///////////////////////
	// Set up variables
    bool bNotColliding = true;
	iPhysicsWorld *pPhysicsWorld = mpWorld->GetPhysicsWorld();

	cLuxPlayer *pPlayer = gpBase->mpPlayer;
	iPhysicsBody *pPlayerBody = pPlayer->GetCharacterBody()->GetCurrentBody();

	cCollideData collideData;
	collideData.SetMaxSize(1);
	
	///////////////////////
	// Iterate bodies
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		iPhysicsBody *pBody = mvBodies[i];
		
		//Check this body is disabled.
		if(pBody->GetCollideCharacter()==true || mvDefaultBodySettings[i].mbCollideCharacter==false)
		{
			continue;
		}

        // Check shape collision
        cVector3f vPush(0);
		bool bCollide = pPhysicsWorld->CheckShapeCollision(	pBody->GetShape(),pBody->GetLocalMatrix(),
															pPlayerBody->GetShape(), pPlayerBody->GetLocalMatrix(),
															collideData,1,false);
		if(bCollide)
		{
			bNotColliding = false;

			/////////////////////////////////////////
			//Add a small impulse away from the player
			//  and be sure to make sure the speed in that dir is not too much!
			cVector3f vDir = pBody->GetLocalPosition() - pPlayerBody->GetLocalPosition();
			vDir.y *=0.1f; //Do not have much in the ydir!
			vDir.Normalize();
			
			cVector3f vVel = pBody->GetLinearVelocity();
			cVector3f vRelVel =  vDir * cMath::Vector3Dot(vVel, vDir);
			if(vRelVel.Length() < 0.5f)
			{
				pBody->AddImpulse(vDir *0.5f);
			}
		}
		else
		{
			pBody->SetCollideCharacter(true);
		}
	}

	///////////////////////
	// See if all bodies are outside of player
	if(bNotColliding)
	{
		mbCheckOutsidePlayer = false;
	}
}

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

cVector3f iLuxProp::GetWorldCenterPos()
{
	cVector3f vCenterPos = 0;
	if(mvBodies.empty()==false)
	{
		iPhysicsBody *pBody = mpMainBody ? mpMainBody : mvBodies[0];
		vCenterPos = pBody->GetWorldPosition();
	}
	else
	{
		vCenterPos = mpMeshEntity->GetBoundingVolume()->GetWorldCenter();
	}
	return vCenterPos;
}

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

//////////////////////////////////////////////////////////////////////////
// SAVE DATA STUFF
//////////////////////////////////////////////////////////////////////////

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

kBeginSerializeBase(cLuxPropConnectedProp)
kSerializeVar(msName, eSerializeType_String)
kEndSerialize()


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

kBeginSerializeVirtual(iLuxProp_SaveData, iLuxEntity_SaveData)

kSerializeVar(msFileName, eSerializeType_String)
kSerializeVar(m_mtxOnLoadTransform, eSerializeType_Matrixf)
kSerializeVar(mvOnLoadScale, eSerializeType_Vector3f)

kSerializeVar(mfHealth, eSerializeType_Float32)

kSerializeVar(mbCheckOutsidePlayer, eSerializeType_Bool)

kSerializeVar(mbEffectsActive, eSerializeType_Bool)
kSerializeVar(mfEffectsAlpha, eSerializeType_Float32)

kSerializeVar(mbStaticPhysics, eSerializeType_Bool)

kSerializeVar(mbMoving, eSerializeType_Bool)

kSerializeVar(mbMovingLinear, eSerializeType_Bool)
kSerializeVar(mfMoveLinearMaxSpeed, eSerializeType_Float32)
kSerializeVar(mfMoveLinearAcc, eSerializeType_Float32)
kSerializeVar(mfMoveLinearSpeed, eSerializeType_Float32)
kSerializeVar(mfMoveLinearSlowdownDist, eSerializeType_Float32)
kSerializeVar(mvMoveLinearGoal, eSerializeType_Vector3f)

kSerializeVar(mbMovingAngular, eSerializeType_Bool)
kSerializeVar(mfMoveAngularMaxSpeed, eSerializeType_Float32)
kSerializeVar(mfMoveAngularAcc, eSerializeType_Float32)
kSerializeVar(mfMoveAngularSpeed, eSerializeType_Float32)
kSerializeVar(mfMoveAngularSlowdownDist, eSerializeType_Float32)
kSerializeVar(m_mtxMoveAngularGoal, eSerializeType_Matrixf)
kSerializeVar(mbMoveAngularUseOffset, eSerializeType_Bool)
kSerializeVar(mvMoveAngularWorldOffset, eSerializeType_Vector3f)
kSerializeVar(mvMoveAngularLocalOffset, eSerializeType_Vector3f)

kSerializeVar(mbMoveAngularNoGoal, eSerializeType_Bool)
kSerializeVar(mvMoveAngularNoGoalDir, eSerializeType_Vector3f)

kSerializeVar(mbGlowEnabled, eSerializeType_Bool)

kSerializeVar(mlCurrentNonLoopAnimIndex, eSerializeType_Int32)
kSerializeVar(msAnimCallback, eSerializeType_String)

kSerializeVar(msParentEntityName, eSerializeType_String)
kSerializeVar(msParentBoneName, eSerializeType_String)
kSerializeVar(mpParentBoneOffsetMatrix, eSerializeType_Matrixf)

kSerializeVar(mfBodyMassBackup, eSerializeType_Float32)
kSerializeVar(mbBodyCollideBackup, eSerializeType_Bool)
kSerializeVar(mbBodyCollideCharacterBackup, eSerializeType_Bool)
kSerializeVar(mbBodyActiveBackup, eSerializeType_Bool)
kSerializeVar(mBodyMatrixBackup, eSerializeType_Matrixf)

kSerializeClassContainer(mvAttachedProps, cLuxProp_AttachedProp, eSerializeType_Class)

kSerializeClassContainer(mvConnectedProps, cLuxPropConnectedProp, eSerializeType_Class)

kSerializeVar(mMeshEntity, eSerializeType_Class)
kSerializeClassContainer(mvBodies, cEngineBody_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvJoints, cEngineJoint_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvPS, cEnginePS_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvBeams, cEngineBeam_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvLights, cEngineLight_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvSounds, cEngineSound_SaveData, eSerializeType_Class)
kSerializeClassContainer(mvBillboards, cEngineBillboard_SaveData, eSerializeType_Class)

kSerializeClassContainer(mvInteractConnections, iLuxInteractConnection_SaveData, eSerializeType_ClassPointer)

kEndSerialize()

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

iLuxProp_SaveData::~iLuxProp_SaveData()
{
	for(size_t i=0; i<mvInteractConnections.Size(); ++i)
	{
		iLuxInteractConnection_SaveData *pSaveConn = mvInteractConnections[i];
		hplDelete(pSaveConn);
	}
}

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

cEnginePS_SaveData* iLuxProp_SaveData::GetParticleSystem(cParticleSystem* apPS)
{
	if(apPS==NULL) return NULL;

	for(size_t i=0; i<mvPS.Size();++i)
		if(mvPS[i].msName == apPS->GetName()) return &mvPS[i];

	return NULL;
}
cEngineSound_SaveData* iLuxProp_SaveData::GetSoundEntity(cSoundEntity* apSound)
{
	for(size_t i=0; i<mvSounds.Size();++i)
		if(mvSounds[i].msName == apSound->GetName()) return &mvSounds[i];

	return NULL;
}
cEngineJoint_SaveData* iLuxProp_SaveData::GetJoint(iPhysicsJoint* apJoint)
{
	for(size_t i=0; i<mvJoints.Size();++i)
		if(mvJoints[i].msName == apJoint->GetName()) return &mvJoints[i];

	return NULL;
}

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

iLuxEntity* iLuxProp_SaveData::CreateEntity(cLuxMap *apMap)
{
	cWorld *pWorld = apMap->GetWorld();
	apMap->ResetLatestEntity();
	pWorld->CreateEntity(msName,m_mtxOnLoadTransform, msFileName, mlID, true, mvOnLoadScale);

	return apMap->GetLatestEntity();
}

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

// CreateEntity seems broken. It does not add the entity to the map, but returns map->GetLatestEntity(). I do not want to interfere with the rest at this point, so creating a second method for my means.
iLuxEntity* iLuxProp_SaveData::CreateAndAddEntity(cLuxMap *apMap)
{
	cWorld *pWorld = apMap->GetWorld();
	apMap->ResetLatestEntity();
    iLuxEntity * last_entity_before = apMap->GetLatestEntity();
	pWorld->CreateEntity(msName,m_mtxOnLoadTransform, msFileName, mlID, true, mvOnLoadScale);
    iLuxEntity * last_entity = apMap->GetLatestEntity();
	return last_entity;
}

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

void iLuxProp::SaveToSaveData(iLuxEntity_SaveData* apSaveData)
{
	///////////////////////
	// Init
	super_class::SaveToSaveData(apSaveData);
	iLuxProp_SaveData *pData = static_cast<iLuxProp_SaveData*>(apSaveData);

    cWorld *pWorld = mpWorld;
	iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();

	///////////////////////
	//Properties
	kCopyToVar(pData, msFileName);
	kCopyToVar(pData, m_mtxOnLoadTransform);
	kCopyToVar(pData, mvOnLoadScale);

	kCopyToVar(pData, mfHealth);

	kCopyToVar(pData, mbCheckOutsidePlayer);

	kCopyToVar(pData, mbEffectsActive);
	kCopyToVar(pData, mfEffectsAlpha);

	kCopyToVar(pData, mbStaticPhysics);

	kCopyToVar(pData, mbMoving);

	kCopyToVar(pData, mbMovingLinear);
	kCopyToVar(pData, mfMoveLinearMaxSpeed);
	kCopyToVar(pData, mfMoveLinearAcc);
	kCopyToVar(pData, mfMoveLinearSpeed);
	kCopyToVar(pData, mfMoveLinearSlowdownDist);
	kCopyToVar(pData, mvMoveLinearGoal);

	kCopyToVar(pData, mbMovingAngular);
	kCopyToVar(pData, mfMoveAngularMaxSpeed);
	kCopyToVar(pData, mfMoveAngularAcc);
	kCopyToVar(pData, mfMoveAngularSpeed);
	kCopyToVar(pData, mfMoveAngularSlowdownDist);
	kCopyToVar(pData, m_mtxMoveAngularGoal);

	kCopyToVar(pData, mbGlowEnabled);
	
	kCopyToVar(pData, mbMoveAngularNoGoal);
	kCopyToVar(pData, mvMoveAngularNoGoalDir);

	kCopyToVar(pData, mbMoveAngularUseOffset);
	kCopyToVar(pData, mvMoveAngularWorldOffset);
	kCopyToVar(pData, mvMoveAngularLocalOffset);

	kCopyToVar(pData, mlCurrentNonLoopAnimIndex);
	kCopyToVar(pData, msAnimCallback);

    kCopyToVar(pData, mpParentBoneOffsetMatrix);
    kCopyToVar(pData, msParentEntityName);
	kCopyToVar(pData, msParentBoneName);

	kCopyToVar(pData, mfBodyMassBackup);
	kCopyToVar(pData, mbBodyCollideBackup);
	kCopyToVar(pData, mbBodyCollideCharacterBackup);
	kCopyToVar(pData, mbBodyActiveBackup);
	kCopyToVar(pData, mBodyMatrixBackup);

	///////////////////////
	//Attached props
    tLuxProp_AttachedPropListIt attachIt = mlstAttachedProps.begin();
	for(; attachIt != mlstAttachedProps.end(); ++attachIt)
	{
		cLuxProp_AttachedProp attachProp = *(*attachIt);
		attachProp.mpProp = NULL;
        pData->mvAttachedProps.Add(attachProp);
	}

	///////////////////////
	//Connection
	pData->mvConnectedProps.Resize(mvConnectedProps.size());
	for(size_t i=0; i<mvConnectedProps.size(); ++i)
		pData->mvConnectedProps[i].msName = mvConnectedProps[i];

	///////////////////////
	//Mesh Entity
	if(mpMeshEntity)
		pData->mMeshEntity.FromMeshEntity(mpMeshEntity);

	///////////////////////
	//Bodies
	pData->mvBodies.Resize(mvBodies.size());
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		pData->mvBodies[i].FromBody(mvBodies[i]);
	}

	///////////////////////
	//Joints
	pData->mvJoints.Resize(mvJoints.size());
	for(size_t i=0; i<mvJoints.size(); ++i)
	{
		pData->mvJoints[i].FromJoint(mvJoints[i], pPhysicsWorld);
	}

	///////////////////////
	//Particle Systems
	pData->mvPS.Resize(mvParticleSystems.size());
	for(size_t i=0; i<mvParticleSystems.size(); ++i)
	{
		if(pWorld->ParticleSystemExists(mvParticleSystems[i])==false)
		{
			mvParticleSystems[i] = NULL;
			Warning("particle system %d in %s does not exist anymore!\n",i,GetName().c_str());
		}

		pData->mvPS[i].FromPS(mvParticleSystems[i]);
	}
	
	///////////////////////
	//Lights
	pData->mvLights.Resize(mvLights.size());
	for(size_t i=0; i<mvLights.size(); ++i)
	{
		pData->mvLights[i].FromLight(mvLights[i]);
	}
	
	///////////////////////
	//Sounds
	pData->mvSounds.Resize(mvSoundEntities.size());
	for(size_t i=0; i<mvSoundEntities.size(); ++i)
	{
		pData->mvSounds[i].FromSound(mvSoundEntities[i]);
	}

	///////////////////////
	//Billboards
	pData->mvBillboards.Resize(mvBillboards.size());
	for(size_t i=0; i<mvBillboards.size(); ++i)
	{
		pData->mvBillboards[i].FromBillboard(mvBillboards[i]);
	}

	///////////////////////
	//Interact connections
	for(size_t i=0; i<mvInteractConnections.size(); ++i)
	{
		iLuxInteractConnection *pConn = mvInteractConnections[i];

        iLuxInteractConnection_SaveData *pSaveConn = pConn->CreateSaveData();
		pSaveConn->FromConnection(pConn);
		pData->mvInteractConnections.Add(pSaveConn);
	}
}

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

void iLuxProp::LoadFromSaveData(iLuxEntity_SaveData* apSaveData)
{
	///////////////////////
	// Init
	super_class::LoadFromSaveData(apSaveData);
	iLuxProp_SaveData *pData = static_cast<iLuxProp_SaveData*>(apSaveData);

	cWorld *pWorld = mpWorld;
	iPhysicsWorld *pPhysicsWorld = pWorld->GetPhysicsWorld();

	///////////////////////
	//Properties
	kCopyFromVar(pData, m_mtxOnLoadTransform);
	kCopyFromVar(pData, mvOnLoadScale);
	
	kCopyFromVar(pData, mfHealth);

	kCopyFromVar(pData, mbCheckOutsidePlayer);

	SetEffectsActive(pData->mbEffectsActive, false);
	kCopyFromVar(pData, mfEffectsAlpha); 

	SetStaticPhysics(pData->mbStaticPhysics);

	kCopyFromVar(pData, mbMoving);
	kCopyFromVar(pData, mbMovingLinear);
	kCopyFromVar(pData, mfMoveLinearMaxSpeed);
	kCopyFromVar(pData, mfMoveLinearAcc);
	kCopyFromVar(pData, mfMoveLinearSpeed);
	kCopyFromVar(pData, mfMoveLinearSlowdownDist);
	kCopyFromVar(pData, mvMoveLinearGoal);

	kCopyFromVar(pData, mbMovingAngular);
	kCopyFromVar(pData, mfMoveAngularMaxSpeed);
	kCopyFromVar(pData, mfMoveAngularAcc);
	kCopyFromVar(pData, mfMoveAngularSpeed);
	kCopyFromVar(pData, mfMoveAngularSlowdownDist);
	kCopyFromVar(pData, m_mtxMoveAngularGoal);
	
	kCopyFromVar(pData, mbMoveAngularNoGoal);
	kCopyFromVar(pData, mvMoveAngularNoGoalDir);
	
	kCopyFromVar(pData, mbMoveAngularUseOffset);
	kCopyFromVar(pData, mvMoveAngularWorldOffset);
	kCopyFromVar(pData, mvMoveAngularLocalOffset);

	kCopyFromVar(pData, mbGlowEnabled);

	kCopyFromVar(pData, mlCurrentNonLoopAnimIndex);
	kCopyFromVar(pData, msAnimCallback);

    kCopyFromVar(pData, mpParentBoneOffsetMatrix);
    kCopyFromVar(pData, msParentEntityName);
	kCopyFromVar(pData, msParentBoneName);

	kCopyFromVar(pData, mfBodyMassBackup);
	kCopyFromVar(pData, mbBodyCollideBackup);
	kCopyFromVar(pData, mbBodyCollideCharacterBackup);
	kCopyFromVar(pData, mbBodyActiveBackup);
	kCopyFromVar(pData, mBodyMatrixBackup);

	if(mfBodyMassBackup == 0 || mfBodyMassBackup < 0 || (mfBodyMassBackup == mfBodyMassBackup) == false) mfBodyMassBackup = 1;
	
	///////////////////////
	//Connections
	mvConnectedProps.resize(pData->mvConnectedProps.Size());
	for(size_t i=0; i<mvConnectedProps.size(); ++i)
		mvConnectedProps[i] = pData->mvConnectedProps[i].msName;

	///////////////////////
	//Mesh Entity
	if(mpMeshEntity)
		pData->mMeshEntity.ToMeshEntity(mpMeshEntity);

	///////////////////////
	//Bodies
	for(size_t i=0; i<mvBodies.size(); ++i)
	{
		pData->mvBodies[i].ToBody(mvBodies[i]);
	}

	///////////////////////
	//Joints
	for(size_t i=0; i<mvJoints.size(); ++i)
	{
		if(pData->mvJoints[i].mbBroken)
		{
			pPhysicsWorld->DestroyJoint(mvJoints[i]);
			mvJoints[i] = NULL;
		}
		else
		{
			pData->mvJoints[i].ToJoint(mvJoints[i]);
		}
	}

	///////////////////////
	//Lights
	for(size_t i=0; i<mvLights.size(); ++i)
	{
		pData->mvLights[i].ToLight(mvLights[i]);
	}
	
	///////////////////////
	//Particle Systems
	int lCount=0;
	for(std::vector<cParticleSystem*>::iterator it = mvParticleSystems.begin(); it != mvParticleSystems.end();)
	{
		cParticleSystem *pPS = *it;

		cEnginePS_SaveData *pSavePS = pData->GetParticleSystem(pPS);
		if(pSavePS)
		{
			pSavePS->ToPS(pPS);
			++it;
		}
		else
		{
			//check if a null was previously saved
			if(pData->mvPS.Size() == mvParticleSystems.size() && pData->mvPS[lCount].msType == "")
			{
				++it;
			}
			//a particle system has been removed.
			else
			{
				pWorld->DestroyParticleSystem(pPS);
				it = mvParticleSystems.erase(it);
			}
		}

		++lCount;
	}

	///////////////////////
	//Sounds
	for(std::vector<cSoundEntity*>::iterator it = mvSoundEntities.begin(); it != mvSoundEntities.end(); )
	{
		cSoundEntity *pSound = *it;
		cEngineSound_SaveData *pSaveSound = pData->GetSoundEntity(pSound);
		if(pSaveSound)
		{
			pSaveSound->ToSound(pSound);
			++it;
		}
		else
		{
			pWorld->DestroySoundEntity(pSound);
			it = mvSoundEntities.erase(it);
		}
	}

	///////////////////////
	//Billboards
	for(size_t i=0; i<mvBillboards.size(); ++i)
	{
		pData->mvBillboards[i].ToBillboard(mvBillboards[i]);
	}
}

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

void iLuxProp::SetupSaveData(iLuxEntity_SaveData *apSaveData)
{
	super_class::SetupSaveData(apSaveData);
	iLuxProp_SaveData *pData = static_cast<iLuxProp_SaveData*>(apSaveData);

	///////////////////////
	//Attached props (want it in setup so it does not mess with set)
	for(size_t i=0; i<pData->mvAttachedProps.Size(); ++i)
	{
		cLuxProp_AttachedProp &attachProp = pData->mvAttachedProps[i];

		AddAndAttachProp(attachProp.msName, attachProp.msFileName, attachProp.m_mtxOffset);
	}

	///////////////////////
	//Interact connections
	for(size_t i=0; i<pData->mvInteractConnections.Size(); ++i)
	{
		iLuxInteractConnection_SaveData *pSaveConn = pData->mvInteractConnections[i];
		iLuxInteractConnection *pConn = pSaveConn->CreateConnection(mpMap);

		if(pConn) mvInteractConnections.push_back(pConn);
	}
	

	

	///////////////////
	// Bone attachment
	if(msParentBoneName != "" && msParentEntityName != "")
	{
		iLuxEntity * pParentEntity = mpMap->GetEntityByName(msParentEntityName);

		if(pParentEntity)
		{	
			cMeshEntity * pParentMeshEntity = pParentEntity->GetMeshEntity();

			if ( pParentMeshEntity )
			{
				mpParentBone = pParentMeshEntity->GetBoneStateFromName( msParentBoneName );
			}
		}
	}
}

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

