void CScriptSurfaceTypesLoader::UnloadSurfaceTypes()
{
	ISurfaceTypeManager *pSurfaceManager = NULL;
	I3DEngine *pEngine = gEnv->p3DEngine;
	if (pEngine)
	{
		pSurfaceManager = pEngine->GetMaterialManager()->GetSurfaceTypeManager();
	}
	for (unsigned int i = 0; i < m_surfaceTypes.size(); i++)
	{
		if (pSurfaceManager)
			pSurfaceManager->UnregisterSurfaceType(m_surfaceTypes[i]);
		m_surfaceTypes[i]->Release();
	}
	m_surfaceTypes.clear();
}
//------------------------------------------------------------------------
void CMelee::Impulse(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, int partId, int ipart, int surfaceIdx, float impulseScale)
{
	if(m_noImpulse)
	{
		m_noImpulse = false;
		return;
	}

	if (pCollider && m_pShared->meleeparams.impulse > 0.001f)
	{
		bool strengthMode = false;
		CPlayer *pPlayer = (CPlayer *)m_pWeapon->GetOwnerActor();
		pe_status_dynamics dyn;

		if (!pCollider->GetStatus(&dyn))
		{
			if(strengthMode)
			{
				impulseScale *= 3.0f;
			}
		}
		else
		{
			impulseScale *= clamp((dyn.mass * 0.01f), 1.0f, 15.0f);
		}

		//[kirill] add impulse to phys proxy - to make sure it's applied to cylinder as well (not only skeleton) - so that entity gets pushed
		// if no pEntity - do it old way
		IEntity *pEntity = (IEntity *) pCollider->GetForeignData(PHYS_FOREIGN_ID_ENTITY);

		if(gEnv->bMultiplayer && pEntity)
		{
			if(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId()) == NULL)
			{
				impulseScale *= 0.33f;
			}
		}

		if(pEntity)
		{
			bool crapDollFilter = false;
#ifdef CRAPDOLLS
			static IEntityClass *pDeadBodyClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass("DeadBody");

			if (pEntity->GetClass() == pDeadBodyClass)
			{
				crapDollFilter = true;
			}

#endif //CRAPDOLLS

			if (!crapDollFilter)
			{
				IEntityPhysicalProxy *pPhysicsProxy = (IEntityPhysicalProxy *)pEntity->GetProxy(ENTITY_PROXY_PHYSICS);
				CActor *pActor = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId());

				if (pActor)
				{
					SActorStats *pAS = pActor->GetActorStats();

					if (pAS && pAS->isRagDoll)
					{
						//marcok: talk to me before touching this
						impulseScale = 1.0f; //jan: melee impulses were scaled down, I made sure it still "barely moves"
#ifdef CRAPDOLLS
						crapDollFilter = true;
#endif //CRAPDOLLS
					}
				}

				// scale impulse up a bit for player
				if (!crapDollFilter)
				{
					pPhysicsProxy->AddImpulse(partId, pt, dir * m_pShared->meleeparams.impulse * impulseScale * m_meleeScale, true, 1.f);
				}
			}
		}
		else
		{
			pe_action_impulse ai;
			ai.partid = partId;
			ai.ipart = ipart;
			ai.point = pt;
			ai.iApplyTime = 0;
			ai.impulse = dir * (m_pShared->meleeparams.impulse * impulseScale * m_meleeScale);
			pCollider->Action(&ai);
		}

		ISurfaceTypeManager *pSurfaceTypeManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();
		int invId = pSurfaceTypeManager->GetSurfaceTypeByName("mat_invulnerable")->GetId();
		// create a physical collision to break trees
		pe_action_register_coll_event collision;
		collision.collMass = 0.005f; // this is actually ignored
		collision.partid[1] = partId;
		// collisions involving partId<-1 are to be ignored by game's damage calculations
		// usually created articially to make stuff break.
		collision.partid[0] = -2;
		collision.idmat[1] = surfaceIdx;
		collision.idmat[0] = invId;
		collision.n = normal;
		collision.pt = pt;
		// scar bullet
		// m = 0.0125
		// v = 800
		// energy: 4000
		// in this case the mass of the active collider is a player part
		// so we must solve for v given the same energy as a scar bullet
		Vec3	v = dir;
		float speed = cry_sqrtf(4000.0f / (80.0f * 0.5f)); // 80.0f is the mass of the player
		// [marco] Check if an object. Should take lots of time to break stuff if not in nanosuit strength mode;
		// and still creates a very low impulse for stuff that might depend on receiving an impulse.
		IRenderNode *pBrush = (IRenderNode *)pCollider->GetForeignData(PHYS_FOREIGN_ID_STATIC);
		collision.vSelf = (v.normalized() * speed * m_meleeScale);
		collision.v = Vec3(0, 0, 0);
		collision.pCollider = pCollider;
		IEntity *pOwner = m_pWeapon->GetOwner();

		if (pOwner && pOwner->GetCharacter(0) && pOwner->GetCharacter(0)->GetISkeletonPose()->GetCharacterPhysics())
		{
			if (ISkeletonPose *pSkeletonPose = pOwner->GetCharacter(0)->GetISkeletonPose())
			{
				if (pSkeletonPose && pSkeletonPose->GetCharacterPhysics())
				{
					pSkeletonPose->GetCharacterPhysics()->Action(&collision);
				}
			}
		}
	}
}
//------------------------------------------------------------------------
void CVehiclePartAnimated::SetDrivingProxy(bool bDrive)
{
	IVehicleMovement* pMovement = m_pVehicle->GetMovement();
	if (!(pMovement && pMovement->UseDrivingProxy()))
		return;

	if (0 == m_hullMatId[bDrive]) // 0 means, nothin to do
		return;

	if (!m_pCharInstance)
		return;

	ISkeletonPose* pSkeletonPose = m_pCharInstance->GetISkeletonPose();
	if (!pSkeletonPose)
		return;
	IDefaultSkeleton &rIDefaultSkeleton = m_pCharInstance->GetIDefaultSkeleton();
	IPhysicalEntity*  pPhysics          = m_pVehicle->GetEntity()->GetPhysics();
	if (!pPhysics)
		return;

	int id = rIDefaultSkeleton.GetJointIDByName("hull_proxy");

	if (id < 0)
	{
		m_hullMatId[0] = m_hullMatId[1] = 0;
		return;
	}

	int partid = pSkeletonPose->GetPhysIdOnJoint(id);

	if (partid == -1)
		return;

	pe_params_part params;
	params.partid = partid;
	params.ipart  = -1;
	if (!pPhysics->GetParams(&params) || !params.nMats)
		return;

	phys_geometry* pGeom = params.pPhysGeom;
	if (pGeom && pGeom->surface_idx < pGeom->nMats)
	{
		ISurfaceTypeManager* pSurfaceMan = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();

		// initialize once
		if (m_hullMatId[0] < 0)
		{
			int         idDriving = 0;
			int         idOrig    = pGeom->pMatMapping[pGeom->surface_idx];
			const char* matOrig   = pSurfaceMan->GetSurfaceType(idOrig)->GetName();

			if (strstr(matOrig, "mat_metal"))
				idDriving = pSurfaceMan->GetSurfaceTypeByName("mat_metal_nofric")->GetId();
			else
			{
				string mat(matOrig);
				mat.append("_nofric");

				idDriving = pSurfaceMan->GetSurfaceTypeByName(mat.c_str(), NULL, false)->GetId();
			}

			//if (pDebug->GetIVal())
			//CryLog("%s looking up driving surface replacement for %s (id %i) -> got id %i", m_pVehicle->GetEntity()->GetName(), matOrig, idOrig, idDriving);

			if (idDriving > 0)
			{
				// store old and new id
				m_hullMatId[0] = idOrig;
				m_hullMatId[1] = idDriving;

				/*if (pDebug->GetIVal())
				   {
				   const char* matDriving = pSurfaceMan->GetSurfaceType(idDriving)->GetName();
				   CryLog("%s storing hull matId for swapping: %i (%s) -> %i (%s)", m_pVehicle->GetEntity()->GetName(), m_hullMatId[0], matOrig, m_hullMatId[1], matDriving);
				   }*/
			}
			else
				m_hullMatId[0] = m_hullMatId[1] = 0;
		}

		// only swap if materials available
		if (m_hullMatId[bDrive] > 0)
		{
#if ENABLE_VEHICLE_DEBUG
			if (VehicleCVars().v_debugdraw == eVDB_Parts)
				CryLog("%s swapping hull proxy from %i (%s) to matId %i (%s)", m_pVehicle->GetEntity()->GetName(), m_hullMatId[bDrive ^ 1], pSurfaceMan->GetSurfaceType(m_hullMatId[bDrive ^ 1])->GetName(), m_hullMatId[bDrive], pSurfaceMan->GetSurfaceType(m_hullMatId[bDrive])->GetName());
#endif

			for (int n = 0; n < pGeom->nMats; ++n)
			{
				pGeom->pMatMapping[n] = m_hullMatId[bDrive];
			}
		}
	}
}
//--------------------------------------------------------------------------------------------------
// Name: BreakGlassObject
// Desc: Replaces a physical StatObj with a glass node where supported
// Note: We're currently duplicating some work done in CryAction, so should be reading that in.
//			 Similarly, we should also be accepting the SProcessImpactIn struct instead of the event
//--------------------------------------------------------------------------------------------------
bool CBreakableGlassSystem::BreakGlassObject(const EventPhysCollision& physEvent, const bool forceGlassBreak)
{
	bool bSuccess = false;
	SBreakableGlassInitParams initParams;

	// Get surface type's breaking effect
	ISurfaceTypeManager* pSurfaceTypeManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();
	initParams.surfaceTypeId = physEvent.idmat[PHYSEVENT_COLLIDEE];

	if (ISurfaceType* pMat = pSurfaceTypeManager->GetSurfaceType(initParams.surfaceTypeId))
	{
		if (ISurfaceType::SBreakable2DParams* pBreakableParams = pMat->GetBreakable2DParams())
		{
			initParams.pShatterEffect = pBreakableParams->full_fracture_fx ? gEnv->pParticleManager->FindEffect(pBreakableParams->full_fracture_fx) : NULL;
		}
	}

	// Calculate glass data
	SBreakableGlassPhysData physData;

	if (ExtractPhysDataFromEvent(physEvent, physData, initParams) &&
			ValidateExtractedOutline(physData, initParams))
	{
		Vec3 size;
		Matrix34 transMat;

		CalculateGlassBounds(physData.pPhysGeom, size, transMat);

		// Update initialisation parameters
		if (!physData.defaultFrag.empty())
		{
			initParams.pInitialFrag = physData.defaultFrag.begin();
			initParams.numInitialFragPts = physData.defaultFrag.size();
		}
		else
		{
			initParams.pInitialFrag = NULL;
			initParams.numInitialFragPts = 0;
		}

		initParams.size.set(size.x, size.y);
		initParams.thickness = size.z;

		// Fix-up mesh and calculate anchors
		CalculateGlassAnchors(physData, initParams);

		// Calculate UV coords at axis-aligned points
		Vec2 origin = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_Zero);
		Vec2 xAxisPt = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_OneX);
		Vec2 yAxisPt = InterpolateUVCoords(physData.uvBasis[0], physData.uvBasis[1], physData.uvBasis[2], Vec2Constants<f32>::fVec2_OneY);

		// Convert the UV basis to X and Y axes w/ an offset
		initParams.uvOrigin = origin;
		initParams.uvXAxis = xAxisPt - origin;
		initParams.uvYAxis = yAxisPt - origin;

		// Create a replacement glass node
		IBreakableGlassRenderNode* pRenderNode = InitGlassNode(physData, initParams, transMat);
		
		// Duplicate the collision event and patch it up for the new node
		EventPhysCollision dupeEvent = physEvent;	

		dupeEvent.pEntity[PHYSEVENT_COLLIDEE] = pRenderNode->GetPhysics();
		dupeEvent.pForeignData[PHYSEVENT_COLLIDEE] = (void*)pRenderNode;
		dupeEvent.iForeignData[PHYSEVENT_COLLIDEE] = PHYS_FOREIGN_ID_BREAKABLE_GLASS;

		pe_params_part partParams;
		if (dupeEvent.pEntity[PHYSEVENT_COLLIDEE]->GetParams(&partParams) && partParams.nMats > 0)
		{
			dupeEvent.idmat[PHYSEVENT_COLLIDEE] = partParams.pMatMapping[0];
		}

		if (forceGlassBreak)
		{
			ModifyEventToForceBreak(&dupeEvent);			
		}

		// Send it directly to the new node
		PassImpactToNode(pRenderNode, &dupeEvent);

		bSuccess = true;
	}

	return bSuccess;
}//-------------------------------------------------------------------------------------------------
bool CScriptSurfaceTypesLoader::LoadSurfaceTypes( const char *sFolder,bool bReload )
{
	{
		if (!gEnv->p3DEngine)
			return false;

		I3DEngine *pEngine = gEnv->p3DEngine;
		ISurfaceTypeEnumerator *pEnum = pEngine->GetMaterialManager()->GetSurfaceTypeManager()->GetEnumerator();
		if (pEnum)
		{
			for (ISurfaceType *pSurfaceType = pEnum->GetFirst(); pSurfaceType; pSurfaceType = pEnum->GetNext())
			{
				SmartScriptTable mtlTable(gEnv->pScriptSystem);
				gEnv->pScriptSystem->SetGlobalValue( pSurfaceType->GetName(),mtlTable );

				SmartScriptTable aiTable(gEnv->pScriptSystem);
				mtlTable->SetValue("AI",aiTable);
				aiTable->SetValue( "fImpactRadius",5.0f );
				aiTable->SetValue( "fFootStepRadius",15.0f );
				aiTable->SetValue( "proneMult",0.2f );
				aiTable->SetValue( "crouchMult",0.5f );
				aiTable->SetValue( "movingMult",2.5f );
			}

			pEnum->Release();
		}
	}

	return true; // Do not load surface types from script anymore.

	m_root = GetISystem()->CreateXmlNode("SurfaceTypes");

	IScriptSystem *pScriptSystem = gEnv->pScriptSystem;
	//////////////////////////////////////////////////////////////////////////
	// Make sure Materials table exist.
	//////////////////////////////////////////////////////////////////////////
	SmartScriptTable mtlTable;

	if (!pScriptSystem->GetGlobalValue("Materials", mtlTable) || bReload)
	{
		mtlTable = pScriptSystem->CreateTable();
		pScriptSystem->SetGlobalValue("Materials", mtlTable);
	}

	ICryPak *pIPak = gEnv->pCryPak;

	ISurfaceTypeManager *pSurfaceManager = gEnv->p3DEngine->GetMaterialManager()->GetSurfaceTypeManager();

	if (!bReload)
		stl::push_back_unique( m_folders,sFolder );

	string searchFolder = string(sFolder) + "/";;
	string searchFilter = searchFolder + "mat_*.lua";

	gEnv->pScriptSystem->ExecuteFile(searchFolder+"common.lua", false, bReload);

	_finddata_t fd;
	intptr_t fhandle;
	fhandle = pIPak->FindFirst( searchFilter,&fd );
	if (fhandle != -1)
	{
		do {
			// Skip back folders.
			if (fd.attrib & _A_SUBDIR) // skip if directory.
				continue;

			char name[_MAX_PATH];
			_splitpath( fd.name,NULL,NULL,name,NULL );

			if (strlen(name) == 0)
				continue;

			if (bReload)
			{
				ISurfaceType *pSurfaceType = pSurfaceManager->GetSurfaceTypeByName(name);
				if (pSurfaceType)
				{
					pSurfaceType->Load( pSurfaceType->GetId() );
					continue;
				}
			}

			ISurfaceType *pSurfaceType = new CScriptSurfaceType( this,name,searchFolder+fd.name,0 );
			if (pSurfaceManager->RegisterSurfaceType( pSurfaceType ))
				m_surfaceTypes.push_back(pSurfaceType);
			else
				pSurfaceType->Release();
		} while (pIPak->FindNext( fhandle,&fd ) == 0);
		pIPak->FindClose(fhandle);
	}

	if (m_root)
	{
		m_root->saveToFile( "SurfaceTypes.xml" );
	}

	return true;
}