float CCannonBall::GetFinalDamage( const Vec3& hitPos ) const
{
	float damage = (float)m_damage - m_accumulatedDamageFallOffAfterPenetration;

	if(m_damageFallOffAmount > 0.f)
	{
		float distTravelledSq = (m_initial_pos - hitPos).GetLengthSquared();
		bool fallof = distTravelledSq > (m_damageFallOffStart*m_damageFallOffStart);
		bool pointBlank = distTravelledSq < (m_pointBlankFalloffDistance*m_pointBlankFalloffDistance) && m_ownerIsPlayer;
		float distTravelled = (fallof || pointBlank) ? cry_sqrtf(distTravelledSq) : 0.0f;

		if (fallof)
		{
			distTravelled -= m_damageFallOffStart;
			damage -= distTravelled * m_damageFallOffAmount;
		}

		if (pointBlank)
		{
			damage *= Projectile::GetPointBlankMultiplierAtRange(distTravelled, m_pointBlankDistance, m_pointBlankFalloffDistance, m_pointBlankAmount);
		}
	}

	damage = max(damage, m_damageFalloffMin);

	return damage;
}
//------------------------------------------------------------------------
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 CMelee::Impulse(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, EntityId collidedEntityId, int partId, int ipart, int surfaceIdx, int hitTypeID, int iPrim)
{
	if (pCollider && m_pMeleeParams->meleeparams.impulse>0.001f)
	{
		CActor* pOwnerActor = m_pWeapon->GetOwnerActor();
		const SPlayerMelee& meleeCVars = g_pGameCVars->pl_melee;
		const SMeleeParams& meleeParams = m_pMeleeParams->meleeparams;
		float impulse = meleeParams.impulse;
		bool aiShooter = pOwnerActor ? !pOwnerActor->IsPlayer() : true;
		bool delayImpulse = false;

		float impulseScale = 1.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 = gEnv->pEntitySystem->GetEntity(collidedEntityId);
		IGameFramework* pGameFramework = g_pGame->GetIGameFramework();
		CActor* pTargetActor = static_cast<CActor*>(pGameFramework->GetIActorSystem()->GetActor(collidedEntityId));
		if (pEntity && pTargetActor)
		{
			//If it's an entity, use the specific impulses if needed, and apply to physics proxy
			if ( meleeCVars.impulses_enable != SPlayerMelee::ei_Disabled )
			{
				impulse = meleeParams.impulse_actor;

				bool aiTarget = !pTargetActor->IsPlayer();

				if (aiShooter && !aiTarget)
				{
					float impulse_ai_to_player = GetImpulseAiToPlayer();
					if (impulse_ai_to_player != -1.f)
					{
						impulse = impulse_ai_to_player;
					}
				}

				//Delay a bit on death actors, when switching from alive to death, impulses don't apply
				//I schedule an impulse here, to get rid off the ugly .lua code which was calculating impulses on its own
				if (pTargetActor->IsDead())
				{
					if (meleeCVars.impulses_enable != SPlayerMelee::ei_OnlyToAlive)
					{
						delayImpulse = true;
						const float actorCustomScale = 1.0f;

						impulseScale *= actorCustomScale;
					}
					else
					{
						impulse = 0.0f;
					}
				}
				else if (meleeCVars.impulses_enable == SPlayerMelee::ei_OnlyToDead)
				{
					// Always allow impulses for melee from AI to local player
					// [*DavidR | 27/Oct/2010] Not sure about this
					if (!(aiShooter && !aiTarget))
						impulse = 0.0f;
				}
			}
			else if (pGameFramework->GetIVehicleSystem()->GetVehicle(collidedEntityId))
			{
				impulse = m_pMeleeParams->meleeparams.impulse_vehicle;
				impulseScale = 1.0f;
			}
		}

		const float fScaledImpulse = impulse * impulseScale;
		if (fScaledImpulse > 0.0f)
		{
			if (!delayImpulse)
			{
				m_collisionHelper.Impulse(pCollider, pt, dir * fScaledImpulse, partId, ipart, hitTypeID);
			}
			else
			{
				//Force up impulse, to make the enemy fly a bit
				Vec3 newDir = (dir.z < 0.0f) ? Vec3(dir.x, dir.y, 0.1f) : dir;
				newDir.Normalize();
				newDir.x *= fScaledImpulse;
				newDir.y *= fScaledImpulse;
				newDir.z *= impulse;

				if( pTargetActor )
				{
					pe_action_impulse imp;
					imp.iApplyTime = 0;
					imp.impulse = newDir;
					//imp.ipart = ipart;
					imp.partid = partId;
					imp.point = pt;
					pTargetActor->GetImpulseHander()->SetOnRagdollPhysicalizedImpulse( imp );
				}
				else
				{
					m_pWeapon->GetScheduler()->TimerAction(100, CSchedulerAction<DelayedImpulse>::Create(DelayedImpulse(*this, collidedEntityId, pt, newDir, partId, ipart, hitTypeID)), true);
				}
			}
		}

		// 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
		float speed = cry_sqrtf(4000.0f/(80.0f*0.5f)); // 80.0f is the mass of the player

		if( IRenderNode *pBrush = (IRenderNode*)pCollider->GetForeignData(PHYS_FOREIGN_ID_STATIC) )
		{
			speed = 0.003f;
		}

		m_collisionHelper.GenerateArtificialCollision(m_pWeapon->GetOwner(), pCollider, pt, normal, dir * speed, partId, ipart, surfaceIdx, iPrim);
	}
}