//----------------------------------------------------------------------- bool CSpectacularKill::ObstacleCheck(const Vec3& vKillerPos, const Vec3& vTargetPos, const SSpectacularKillAnimation& anim) const { // [*DavidR | 13/Sep/2010] ToDo: Find a way to make this asynchronously const float OBSTACLE_CHECK_RADIUS = 0.6f; const float OBSTACLE_CHECK_GROUND_OFFSET = 0.2f; const Vec3& vCapsuleKillerEnd = vKillerPos + anim.vKillerObstacleCheckOffset; primitives::capsule capsPrim; const Vec3& vKillerToTargetDist = vTargetPos - vCapsuleKillerEnd; capsPrim.axis = vKillerToTargetDist.GetNormalized(); capsPrim.r = OBSTACLE_CHECK_RADIUS; // hh is actually half the total height (it's measured from the center) capsPrim.hh = static_cast<float>(__fsel(anim.fObstacleCheckLength, (anim.fObstacleCheckLength * 0.5f) - capsPrim.r, (min(g_pGameCVars->g_spectacularKill.maxDistanceError, vKillerToTargetDist.GetLength()) * 0.5f) - capsPrim.r)); capsPrim.center = vCapsuleKillerEnd + (capsPrim.axis * (capsPrim.hh + capsPrim.r)); capsPrim.center.z += capsPrim.r + OBSTACLE_CHECK_GROUND_OFFSET; geom_contact* pContact = NULL; int collisionEntityTypes = ent_static | ent_terrain | ent_sleeping_rigid | ent_ignore_noncolliding; float d = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(capsPrim.type, &capsPrim, Vec3Constants<float>::fVec3_Zero, collisionEntityTypes, &pContact, 0, geom_colltype0); bool bObstacleFound = (d != 0.0f) && pContact; #ifndef _RELEASE if (bObstacleFound && (g_pGameCVars->g_spectacularKill.debug > 1)) { const float fTime = 6.0f; // visually show why it failed IPersistantDebug* pPersistantDebug = BeginPersistantDebug(); // Draw a capsule using a cylinder and two spheres const ColorF debugColor = Col_Coral * ColorF(1.0f, 1.0f, 1.0f, 0.6f); pPersistantDebug->AddCylinder(capsPrim.center, capsPrim.axis, capsPrim.r, capsPrim.hh * 2.0f, debugColor, fTime); pPersistantDebug->AddSphere(capsPrim.center - (capsPrim.axis * capsPrim.hh), capsPrim.r, debugColor, fTime); pPersistantDebug->AddSphere(capsPrim.center + (capsPrim.axis * capsPrim.hh), capsPrim.r, debugColor, fTime); for (int i = 0; i < (int)d; ++i) { // Draw the collision point geom_contact collisionData(pContact[i]); pPersistantDebug->AddCone(collisionData.pt, -collisionData.n, 0.1f, 0.8f, Col_Red, fTime + 2.0f); } } #endif return bObstacleFound; }
virtual void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo) { #if !defined(_RELEASE) if (event == eFE_Activate) { if (IsPortActive(pActInfo, eIP_Draw)) { IPersistantDebug* pPersistentDebug = CCryAction::GetCryAction()->GetIPersistantDebug(); if (pPersistentDebug) { const Vec3 pos = GetPortVec3(pActInfo, eIP_Pos); const Vec3 dir = GetPortVec3(pActInfo, eIP_Dir); const float radius = GetPortFloat(pActInfo, eIP_Radius); const float height = GetPortFloat(pActInfo, eIP_Height); const float time = GetPortFloat(pActInfo, eIP_Time); const ColorF color = GetPortVec3(pActInfo, eIP_Color); pPersistentDebug->Begin("FG_Cone", false); pPersistentDebug->AddCone(pos, dir, radius, height, color, time); } } } #endif }
bool operator() (const SSpectacularKillAnimation& killAnim) const { const SSpectacularKillCVars& skCVars = g_pGameCVars->g_spectacularKill; const CActor* pOwner = m_spectacularKill.m_pOwner; // 0. the anim shouldn't be redundant if (((gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp) <= skCVars.minTimeBetweenSameKills) && (killAnim.killerAnimation.compare(s_lastKillInfo.killerAnim))) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: This animation was last played %.1f ago, a minimum time of %.1f is required", killAnim.killerAnimation.c_str(), (gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp), skCVars.minTimeBetweenSameKills); return true; } // 1. the killer needs to be within a certain distance from the target IEntity* pTargetEntity = m_pTarget->GetEntity(); IEntity* pKillerEntity = pOwner->GetEntity(); const QuatT& killerTransform = pOwner->GetAnimatedCharacter()->GetAnimLocation(); const QuatT& targetTransform = m_pTarget->GetAnimatedCharacter()->GetAnimLocation(); const Vec3& vKillerPos = killerTransform.t; const Vec3& vTargetPos = targetTransform.t; Vec2 vKillerToTarget = Vec2(vTargetPos) - Vec2(vKillerPos); float distance = vKillerToTarget.GetLength(); const float optimalDistance = killAnim.optimalDist; if ((optimalDistance > 0.0f) && (fabs_tpl(distance - optimalDistance) > skCVars.maxDistanceError)) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddPlanarDisc(vTargetPos, killAnim.optimalDist - skCVars.maxDistanceError, killAnim.optimalDist + skCVars.maxDistanceError, Col_Coral, 6.0f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } SK_DEBUG_LOG("GetValidAnim - %s is not valid: Distance between actors should be %.2f, is %.2f (max error is %f)", killAnim.killerAnimation.c_str(), optimalDistance, distance, skCVars.maxDistanceError); #endif return true; } // 2. The killer needs to be facing the target within cosLookToConeHalfAngleRadians angle Vec2 vKillerDir(killerTransform.GetColumn1()); // In decoupled catchup mode we need the animated character's orientation vKillerDir.Normalize(); if (vKillerToTarget.GetNormalizedSafe().Dot(vKillerDir) <= skCVars.minKillerToTargetDotProduct) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not looking within %.2f degrees towards the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(skCVars.minKillerToTargetDotProduct) * 2.0f)); return true; } // 3. If specified, the killer needs to be within a certain angle range from a given reference orientation from the target // e.g. Specifying referenceAngle 180 means using the back of the target as the center of the angle range // (imagine it as a cone) where the killer has to be for the kill to be valid if (killAnim.targetToKillerAngle >= 0.f) { const float referenceAngle = killAnim.targetToKillerAngle; // Find the reference vector which will be the center of the allowed angle range Vec2 vTargetDir(targetTransform.GetColumn1()); vTargetDir.Normalize(); // 2D rotation Vec2 vReferenceDir((vTargetDir.x * cosf(referenceAngle)) - (vTargetDir.y * sinf(referenceAngle)), (vTargetDir.y * cosf(referenceAngle)) + (vTargetDir.x * sinf(referenceAngle))); if (vKillerToTarget.GetNormalizedSafe().Dot(-vReferenceDir) <= killAnim.targetToKillerMinDot) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddCone(vTargetPos + (vReferenceDir * fConeHeight), -vReferenceDir, killAnim.targetToKillerMinDot * fConeHeight * 2.0f, fConeHeight, Col_Coral, 6.f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } float targetToKillerDot = vTargetDir.GetNormalizedSafe().Dot(-vKillerToTarget); SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not within a %.2f degrees cone centered on the target's %.2f degrees. Killer is at %.2f angles respect the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(killAnim.targetToKillerMinDot) * 2.0f), RAD2DEG(killAnim.targetToKillerAngle), RAD2DEG(acos_tpl(targetToKillerDot))); #endif return true; } } SK_DEBUG_LOG("GetValidAnim - %s is valid", killAnim.killerAnimation.c_str()); return false; }