void CLocalPlayerComponent::UpdateScreenFadeEffect() { IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); if(pMaterialEffects) { m_screenFadeEffectId = pMaterialEffects->GetEffectIdByName("cw2_player_fx", "c2mp_fallDeath_fadeOut"); } }
//------------------------------------------------------------------------ /* static */ void CMelee::PlayHitMaterialEffect(const Vec3 &position, const Vec3 &normal, bool bBoostedMelee, int surfaceIdx) { //Play Material FX const char* meleeFXType = bBoostedMelee ? "melee_combat" : "melee"; //Benito: Check with fx guys to update names IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId(meleeFXType, surfaceIdx); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; params.pos = position; params.normal = normal; params.playflags = MFX_PLAY_ALL | MFX_DISABLE_DELAY; params.soundSemantic = eSoundSemantic_Player_Foley; pMaterialEffects->ExecuteEffect(effectId, params); } }
//-------------------------------------------------------------------------------------------------- // Name: SpawnMaterialEffect // Desc: Spawns material effect //-------------------------------------------------------------------------------------------------- void CExplosionGameEffect::SpawnMaterialEffect(const SExplosionContainer &explosionContainer) { // Disclaimer: this code was originally from GameRulesClientServer::ProcessExplosionMaterialFX() const ExplosionInfo& explosionInfo = explosionContainer.m_explosionInfo; // impact stuff here SMFXRunTimeEffectParams params; //params.soundSemantic = eSoundSemantic_Explosion; params.pos = params.decalPos = explosionInfo.pos; params.trg = 0; params.trgRenderNode = 0; params.trgSurfaceId = 0; if(explosionInfo.impact && (explosionInfo.impact_velocity.len2() > 0.000001f)) { params.dir[0] = explosionInfo.impact_velocity.normalized(); params.normal = explosionInfo.impact_normal; } else { const Vec3 gravityDir = Vec3(0.0f, 0.0f, -1.0f); params.dir[0] = gravityDir; params.normal = -gravityDir; } const SDeferredMfxExplosion& mfxInfo = explosionContainer.m_mfxInfo; if(mfxInfo.m_state == eDeferredMfxExplosionState_ResultImpact) { params.trgSurfaceId = mfxInfo.m_mfxTargetSurfaceId; if (mfxInfo.m_pMfxTargetPhysEnt.get()) { if (mfxInfo.m_pMfxTargetPhysEnt->GetiForeignData() == PHYS_FOREIGN_ID_STATIC) { params.trgRenderNode = (IRenderNode*)mfxInfo.m_pMfxTargetPhysEnt->GetForeignData(PHYS_FOREIGN_ID_STATIC); } } } // Create query name stack_string effectClass = explosionInfo.effect_class; if(effectClass.empty()) effectClass = "generic"; const float waterLevel = gEnv->p3DEngine->GetWaterLevel(¶ms.pos); stack_string query = effectClass + "_explode"; if(waterLevel > explosionInfo.pos.z) query = query + "_underwater"; // Get material effect id IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId(query.c_str(), params.trgSurfaceId); if(effectId == InvalidEffectId) { // Get default surface id effectId = pMaterialEffects->GetEffectId(query.c_str(), pMaterialEffects->GetDefaultSurfaceIndex()); } // Execute material effect if(effectId != InvalidEffectId) { pMaterialEffects->ExecuteEffect(effectId, params); bool hasFlashBangEffect = explosionInfo.blindAmount > 0.0f; if(hasFlashBangEffect) { // Calc screen pos Vec3 screenspace; gEnv->pRenderer->ProjectToScreen(explosionInfo.pos.x, explosionInfo.pos.y, explosionInfo.pos.z, &screenspace.x, &screenspace.y, &screenspace.z); // Pass screen pos to flow graph node SMFXCustomParamValue paramPosX; paramPosX.fValue = screenspace.x*0.01f; pMaterialEffects->SetCustomParameter(effectId,"Intensity",paramPosX); // Use intensity param to pass x pos SMFXCustomParamValue paramPosY; paramPosY.fValue = screenspace.y*0.01f; pMaterialEffects->SetCustomParameter(effectId,"BlendOutTime",paramPosY); // Use blendOutTime param to pass y pos } } }//-------------------------------------------------------------------------------------------------
//--------------------------------------------------- void CGameRules::ProcessExplosionMaterialFX(const ExplosionInfo &explosionInfo) { // if an effect was specified, don't use MFX if (explosionInfo.pParticleEffect) return; // impact stuff here SMFXRunTimeEffectParams params; params.soundSemantic = eSoundSemantic_Explosion; params.pos = params.decalPos = explosionInfo.pos; params.trg = 0; params.trgRenderNode = 0; Vec3 gravity; pe_params_buoyancy buoyancy; gEnv->pPhysicalWorld->CheckAreas(params.pos, gravity, &buoyancy); // 0 for water, 1 for air Vec3 pos=params.pos; params.inWater = (buoyancy.waterPlane.origin.z > params.pos.z) && (gEnv->p3DEngine->GetWaterLevel(&pos)>=params.pos.z); params.inZeroG = (gravity.len2() < 0.0001f); params.trgSurfaceId = 0; static const int objTypes = ent_all; static const unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any; ray_hit ray; if (explosionInfo.impact) { params.dir[0] = explosionInfo.impact_velocity.normalized(); params.normal = explosionInfo.impact_normal; if (gEnv->pPhysicalWorld->RayWorldIntersection(params.pos-params.dir[0]*0.0125f, params.dir[0]*0.25f, objTypes, flags, &ray, 1)) { params.trgSurfaceId = ray.surface_idx; if (ray.pCollider->GetiForeignData()==PHYS_FOREIGN_ID_STATIC) params.trgRenderNode = (IRenderNode*)ray.pCollider->GetForeignData(PHYS_FOREIGN_ID_STATIC); } } else { params.dir[0] = gravity; params.normal = -gravity.normalized(); if (gEnv->pPhysicalWorld->RayWorldIntersection(params.pos, gravity, objTypes, flags, &ray, 1)) { params.trgSurfaceId = ray.surface_idx; if (ray.pCollider->GetiForeignData()==PHYS_FOREIGN_ID_STATIC) params.trgRenderNode = (IRenderNode*)ray.pCollider->GetForeignData(PHYS_FOREIGN_ID_STATIC); } } string effectClass = explosionInfo.effect_class; if (effectClass.empty()) effectClass = "generic"; string query = effectClass + "_explode"; if(gEnv->p3DEngine->GetWaterLevel(&explosionInfo.pos)>explosionInfo.pos.z) query = query + "_underwater"; IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId(query.c_str(), params.trgSurfaceId); if (effectId == InvalidEffectId) effectId = pMaterialEffects->GetEffectId(query.c_str(), pMaterialEffects->GetDefaultSurfaceIndex()); if (effectId != InvalidEffectId) pMaterialEffects->ExecuteEffect(effectId, params); }
//------------------------------------------------------------------------ void CMelee::Hit(const Vec3 &pt, const Vec3 &dir, const Vec3 &normal, IPhysicalEntity *pCollider, int partId, int ipart, int surfaceIdx, float damageScale, bool remote) { // generate the damage IEntity *pTarget = gEnv->pEntitySystem->GetEntityFromPhysics(pCollider); // Report punch to AI system. // The AI notification must come before the game rules are // called so that the death handler in AIsystem understands that the hit // came from the player. CActor *pActor = m_pWeapon->GetOwnerActor(); if (pActor && pActor->GetActorClass() == CPlayer::GetActorClassType()) { CPlayer *pPlayer = (CPlayer *)pActor; if (pPlayer && pPlayer->GetNanoSuit()) { if (pPlayer->GetEntity() && pPlayer->GetEntity()->GetAI()) { SAIEVENT AIevent; AIevent.targetId = pTarget ? pTarget->GetId() : 0; // pPlayer->GetNanoSuit()->GetMode() == NANOMODE_STRENGTH pPlayer->GetEntity()->GetAI()->Event(AIEVENT_PLAYER_STUNT_PUNCH, &AIevent); } } } bool ok = true; if(pTarget) { if(!gEnv->bMultiplayer && pActor && pActor->IsPlayer()) { IActor* pAITarget = g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pTarget->GetId()); if(pAITarget && pTarget->GetAI() && !pTarget->GetAI()->IsHostile(pActor->GetEntity()->GetAI(),false)) { ok = false; m_noImpulse = true; } } if(ok) { CGameRules *pGameRules = g_pGame->GetGameRules(); HitInfo info(m_pWeapon->GetOwnerId(), pTarget->GetId(), m_pWeapon->GetEntityId(), m_meleeparams.damage*damageScale*m_meleeScale, 0.0f, pGameRules->GetHitMaterialIdFromSurfaceId(surfaceIdx), partId, pGameRules->GetHitTypeId(m_meleeparams.hit_type.c_str()), pt, dir, normal); info.remote = remote; if (m_pWeapon->GetForcedHitMaterial() != -1) info.material=pGameRules->GetHitMaterialIdFromSurfaceId(m_pWeapon->GetForcedHitMaterial()); pGameRules->ClientHit(info); } } // play effects if(ok) { IMaterialEffects* pMaterialEffects = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId("melee", surfaceIdx); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; params.pos = pt; params.playflags = MFX_PLAY_ALL | MFX_DISABLE_DELAY; params.soundSemantic = eSoundSemantic_Player_Foley; pMaterialEffects->ExecuteEffect(effectId, params); } } ApplyCameraShake(true); m_pWeapon->PlayAction(m_meleeactions.hit.c_str()); }
void CPlayerStateJump::Landed(CPlayer& player, const bool isHeavyWeapon, float fallSpeed) { #ifdef STATE_DEBUG bool remoteControlled = false; IVehicle* pVehicle = player.GetLinkedVehicle(); if(pVehicle) { IVehicleSeat* pVehicleSeat = pVehicle->GetSeatForPassenger(player.GetEntityId()); if(pVehicleSeat && pVehicleSeat->IsRemoteControlled()) { remoteControlled = true; } } CRY_ASSERT_MESSAGE( player.GetLinkedEntity()==NULL || remoteControlled, "Cannot 'land' when you're linked to another entity!" ); #endif const SPlayerStats& stats = player.m_stats; Vec3 playerPosition = player.GetEntity()->GetWorldPos(); IPhysicalEntity *phys = player.GetEntity()->GetPhysics(); IMaterialEffects *mfx = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); const SActorPhysics& actorPhysics = player.GetActorPhysics(); int matID = actorPhysics.groundMaterialIdx != -1 ? actorPhysics.groundMaterialIdx : mfx->GetDefaultSurfaceIndex(); const float fHeightofEntity = playerPosition.z; const float worldWaterLevel = player.m_playerStateSwim_WaterTestProxy.GetWaterLevel(); TMFXEffectId effectId = mfx->GetEffectId("bodyfall", matID); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; Vec3 direction = Vec3(0,0,0); if (IMovementController *pMV = player.GetMovementController()) { SMovementState state; pMV->GetMovementState(state); direction = state.aimDirection; } params.pos = playerPosition + direction; //params.soundSemantic = eSoundSemantic_Player_Foley; float landFallParamVal = (float)__fsel( -(fallSpeed - 7.5f), 0.25f, 0.75f); params.AddAudioRtpc("landfall", landFallParamVal); const float speedParamVal = min(fabsf((actorPhysics.velocity.z * 0.1f)), 1.0f); params.AddAudioRtpc("speed", speedParamVal); mfx->ExecuteEffect(effectId, params); } bool heavyLanded = false; IItem* pCurrentItem = player.GetCurrentItem(); CWeapon* pCurrentWeapon = pCurrentItem ? static_cast<CWeapon*>(pCurrentItem->GetIWeapon()) : NULL; if (fallSpeed > 0.0f && player.IsPlayer()) { if(!gEnv->bMultiplayer) { const float verticalSpeed = fabs(fallSpeed); const float speedForHeavyLand = g_pGameCVars->pl_health.fallSpeed_HeavyLand; if ((verticalSpeed >= speedForHeavyLand) && (player.GetPickAndThrowEntity() == 0) && !player.IsDead()) { if ( !isHeavyWeapon ) { if (pCurrentWeapon) { pCurrentWeapon->FumbleGrenade(); pCurrentWeapon->CancelCharge(); } player.StartInteractiveActionByName("HeavyLand", false); } heavyLanded = true; } } } if(player.m_isClient) { if (fallSpeed > 0.0f) { const float fallIntensityMultiplier = stats.wasHit ? g_pGameCVars->pl_fall_intensity_hit_multiplier : g_pGameCVars->pl_fall_intensity_multiplier; const float fallIntensityMax = g_pGameCVars->pl_fall_intensity_max; const float fallTimeMultiplier = g_pGameCVars->pl_fall_time_multiplier; const float fallTimeMax = g_pGameCVars->pl_fall_time_max; const float zoomMultiplayer = (pCurrentWeapon && pCurrentWeapon->IsZoomed()) ? 0.2f : 1.0f; const float direction = ((cry_rand()%2)==0) ? -1.0f : 1.0f; const float intensity = clamp_tpl(fallIntensityMultiplier*fallSpeed*zoomMultiplayer, 0.0f, fallIntensityMax); const float shakeTime = clamp_tpl(fallTimeMultiplier*fallSpeed*zoomMultiplayer, 0.0f, fallTimeMax); const Vec3 rotation = Vec3(-0.5f, 0.15f*direction, 0.05f*direction); if (CScreenEffects* pGameScreenEffects = g_pGame->GetScreenEffects()) { pGameScreenEffects->CamShake(rotation*intensity, Vec3(0, 0, 0), shakeTime, shakeTime, 0.05f, CScreenEffects::eCS_GID_Player); } IForceFeedbackSystem* pForceFeedback = g_pGame->GetIGameFramework()->GetIForceFeedbackSystem(); assert(pForceFeedback); ForceFeedbackFxId fxId = pForceFeedback->GetEffectIdByName("landFF"); pForceFeedback->PlayForceFeedbackEffect(fxId, SForceFeedbackRuntimeParams(intensity, 0.0f)); if(fallSpeed > 7.0f) { player.PlaySound(CPlayer::ESound_Fall_Drop); } CPlayer::EPlayerSounds playerSound = heavyLanded ? CPlayer::ESound_Gear_HeavyLand : CPlayer::ESound_Gear_Land; player.PlaySound(playerSound, true); } CCCPOINT(PlayerMovement_LocalPlayerLanded); } if( gEnv->pAISystem ) { // Notify AI //If silent feet active, ignore here const float noiseSupression = 0.0f; const float fAISoundRadius = (g_pGameCVars->ai_perception.landed_baseRadius + (g_pGameCVars->ai_perception.landed_speedMultiplier * fallSpeed)) * (1.0f - noiseSupression); SAIStimulus stim(AISTIM_SOUND, AISOUND_MOVEMENT_LOUD, player.GetEntityId(), 0, player.GetEntity()->GetWorldPos() + player.GetEyeOffset(), ZERO, fAISoundRadius); gEnv->pAISystem->RegisterStimulus(stim); } // Record 'Land' telemetry stats. CStatsRecordingMgr::TryTrackEvent(&player, eGSE_Land, fallSpeed); if (fallSpeed > 0.0f) { player.CreateScriptEvent( heavyLanded ? "heavylanded" : "landed",stats.fallSpeed); } }
//------------------------------------------------------------------------ void CTornado::Update(SEntityUpdateContext &ctx, int updateSlot) { if (g_pGame->GetIGameFramework()->IsEditing()) return; // wandering Matrix34 m = GetEntity()->GetWorldTM(); Vec3 dir(m.GetColumn(1)); Vec3 pos(GetEntity()->GetWorldPos()); if(!gEnv->bServer) pos = m_currentPos; Vec3 wanderPos(dir * 1.414214f); float wanderStrength(1.0f); float wanderRate(0.6f); Vec3 wanderOffset; wanderOffset.SetRandomDirection(); wanderOffset.z = 0.0f; wanderOffset.NormalizeSafe(Vec3(1,0,0)); m_wanderDir += wanderOffset * wanderRate + (m_wanderDir - wanderPos) * wanderStrength; m_wanderDir = (m_wanderDir - wanderPos).GetNormalized() + wanderPos; Vec3 wanderSteer = (dir + m_wanderDir * gEnv->pTimer->GetFrameTime()); wanderSteer.z = 0; wanderSteer.NormalizeSafe(Vec3(1,0,0)); Vec3 targetSteer(0,0,0); // go to target if (m_pTargetEntity) { Vec3 target = m_pTargetEntity->GetWorldPos() - pos; if (target.GetLength() < 10.0f) { // emit target reached event SEntityEvent event( ENTITY_EVENT_SCRIPT_EVENT ); event.nParam[0] = (INT_PTR)"TargetReached"; event.nParam[1] = IEntityClass::EVT_BOOL; bool bValue = true; event.nParam[2] = (INT_PTR)&bValue; GetEntity()->SendEvent( event ); if (m_pTargetCallback) m_pTargetCallback->Done(); m_pTargetEntity = 0; m_pTargetCallback = 0; } targetSteer = (target - dir); targetSteer.z = 0; targetSteer.NormalizeSafe(Vec3(1,0,0)); } Vec3 steerDir = (0.4f * wanderSteer + 0.6f * targetSteer).GetNormalized(); Matrix34 tm = Matrix34(Matrix33::CreateRotationVDir(steerDir)); pos = pos + steerDir * gEnv->pTimer->GetFrameTime() * m_wanderSpeed; pos.z = gEnv->p3DEngine->GetTerrainElevation(pos.x, pos.y); float waterLevel = gEnv->p3DEngine->GetWaterLevel(&pos); bool prevIsOnWater = m_isOnWater; m_isOnWater = (pos.z < waterLevel); if (m_isOnWater) { pos.z = waterLevel; } // raycast does not work for oceans if (prevIsOnWater != m_isOnWater && m_isOnWater) { m_pGroundEffect->SetParticleEffect("weather.tornado.water"); } else if (!m_isOnWater) { IMaterialEffects *mfx = gEnv->pGame->GetIGameFramework()->GetIMaterialEffects(); Vec3 down = Vec3(0,0,-1.0f); int matID = mfx->GetDefaultSurfaceIndex(); static const int objTypes = ent_all; static const unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any; ray_hit hit; int col = gEnv->pPhysicalWorld->RayWorldIntersection(pos, (down * 5.0f), objTypes, flags, &hit, 1, GetEntity()->GetPhysics()); if (col) { matID = hit.surface_idx; } if (m_curMatID != matID) { TMFXEffectId effectId = mfx->GetEffectId("tornado", matID); SMFXResourceListPtr pList = mfx->GetResources(effectId); if (pList && pList->m_particleList) { m_pGroundEffect->SetParticleEffect(pList->m_particleList->m_particleParams.name); } m_curMatID = matID; } } if(gEnv->bServer) { tm.SetTranslation(pos); m_currentPos = pos; CHANGED_NETWORK_STATE(this, POSITION_ASPECT); GetEntity()->SetWorldTM(tm); } else { tm.SetTranslation(m_currentPos); GetEntity()->SetWorldTM(tm); } UpdateParticleEmitters(); UpdateTornadoSpline(); UpdateFlow(); }
void CClientHitEffectsMP::ProcessEffectInfo(SHitEffectInfoSet& hitEffectSet, XmlNodeRef xmlNode, const char* libraryName) { bool foundDefault = false; bool foundMelee = false; const uint numEffects = xmlNode->getChildCount(); IMaterialEffects* pMaterialEffects = gEnv->pMaterialEffects; IEntityClassRegistry* pClassRegistry = gEnv->pEntitySystem->GetClassRegistry(); hitEffectSet.m_effectInfos.reserve(numEffects); for (uint i = 0; i < numEffects; i++) { if(XmlNodeRef childNode = xmlNode->getChild(i)) { if(const char* nameTag = childNode->getTag()) { if(!foundDefault && !strcmp("default", nameTag)) { const char* effectName = childNode->getAttr("effect"); if(effectName) { hitEffectSet.m_default = pMaterialEffects->GetEffectIdByName(libraryName, effectName); } foundDefault = true; } else if(!foundMelee && !strcmp("melee", nameTag)) { const char* effectName = childNode->getAttr("effect"); if(effectName) { hitEffectSet.m_melee = pMaterialEffects->GetEffectIdByName(libraryName, effectName); } foundMelee = true; } else { SHitEffectInfo newInfo; newInfo.pAmmoClass = pClassRegistry->FindClass(nameTag); const char* effectName = childNode->getAttr("effect"); if(effectName) { newInfo.effectId = pMaterialEffects->GetEffectIdByName(libraryName, effectName); } if(newInfo.pAmmoClass && newInfo.effectId) { hitEffectSet.m_effectInfos.push_back(newInfo); } else { if(!newInfo.pAmmoClass) { GameWarning("Class type %s does not exist", nameTag); } if(!newInfo.effectId) { GameWarning("Material Effect %s does not exist", effectName ? effectName : ""); } } } } } } if(!hitEffectSet.m_melee) { hitEffectSet.m_melee = hitEffectSet.m_default; } }
void CCannonBall::HandlePierceableSurface( const EventPhysCollision* pCollision, IEntity* pHitTarget, const Vec3& hitDirection, bool bProcessedCollisionEvent ) { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); const SPierceabilityParams& pierceabilityParams = m_pAmmoParams->pierceabilityParams; const int maxPenetrationCount = 4; const float entryAngleDot = pCollision->n.Dot(hitDirection); bool backFace = (entryAngleDot >= 0); #ifdef DEBUG_CannonBall_PENETRATION bool debugCannonBallPenetration = (g_pGameCVars->g_bulletPenetrationDebug != 0); #endif if (backFace == false) { //Front face hit, accumulate damage falloff after penetration float bouncy, friction; uint32 pierceabilityMat; gEnv->pPhysicalWorld->GetSurfaceParameters(pCollision->idmat[1], bouncy, friction, pierceabilityMat); pierceabilityMat &= sf_pierceable_mask; #ifdef DEBUG_CannonBall_PENETRATION const float damageBeforePenetration = GetDamageAfterPenetrationFallOff(); #endif m_penetrationCount++; //1- Check if collided surface might stop the Cannon Ball const bool collisionStopsCannonBall = (!bProcessedCollisionEvent) || (pCollision->idCollider == -1) || ((int16)pierceabilityMat <= GetCannonBallPierceability()) || (m_penetrationCount >= maxPenetrationCount); if (collisionStopsCannonBall) { #ifdef DEBUG_CannonBall_PENETRATION if (debugCannonBallPenetration) { s_debugCannonBallPenetration.AddCannonBallHit(pCollision->pt, hitDirection, damageBeforePenetration, (pCollision->idCollider == -1) ? -1 : pierceabilityMat, false, true, false); } #endif m_accumulatedDamageFallOffAfterPenetration += (float)m_damage; return; } //2- If not stopped, add fall off damage, and see if can still penetrate m_accumulatedDamageFallOffAfterPenetration += (float)m_damage * (pierceabilityParams.GetDamageFallOffForPierceability(pierceabilityMat) * 0.01f); bool needsBackFaceCheck = (GetDamageAfterPenetrationFallOff() > 0.0f) && pierceabilityParams.SurfaceRequiresBackFaceCheck(pierceabilityMat); #ifdef DEBUG_CannonBall_PENETRATION if (debugCannonBallPenetration) { if (ShouldDestroyCannonBall()) { s_debugCannonBallPenetration.AddCannonBallHit(pCollision->pt, hitDirection, damageBeforePenetration, pierceabilityMat, false, true, false); } } #endif if (needsBackFaceCheck) { //3- Raytrace backwards, to check thickness & exit point if any const float angleFactor = 1.0f/max(0.2f, -entryAngleDot); const float distCheck = pierceabilityParams.maxPenetrationThickness * angleFactor; SBackHitInfo hit; bool exitPointFound = RayTraceGeometry(pCollision, pCollision->pt + (hitDirection * (distCheck + 0.035f)), -hitDirection * distCheck ,&hit); if (exitPointFound) { //Exit point found if(ShouldSpawnBackSideEffect(pHitTarget)) { //Spawn effect IMaterialEffects* pMaterialEffects = g_pGame->GetIGameFramework()->GetIMaterialEffects(); TMFXEffectId effectId = pMaterialEffects->GetEffectId(GetEntity()->GetClass(), pCollision->idmat[1]); if (effectId != InvalidEffectId) { SMFXRunTimeEffectParams params; params.src = GetEntityId(); params.trg = pHitTarget ? pHitTarget->GetId() : 0; params.srcSurfaceId = pCollision->idmat[0]; params.trgSurfaceId = pCollision->idmat[1]; params.soundSemantic = eSoundSemantic_Physics_Collision; params.srcRenderNode = (pCollision->iForeignData[0] == PHYS_FOREIGN_ID_STATIC) ? (IRenderNode*)pCollision->pForeignData[0] : NULL; params.trgRenderNode = (pCollision->iForeignData[1] == PHYS_FOREIGN_ID_STATIC) ? (IRenderNode*)pCollision->pForeignData[1] : NULL; params.pos = hit.pt; params.normal = hitDirection; //Use Cannon direction, more readable for exits than normal params.partID = pCollision->partid[1]; params.dir[0] = -hitDirection; params.playflags = MFX_PLAY_ALL&(~MFX_PLAY_SOUND); //Do not play the sound on backface params.playflags &= ~MFX_PLAY_DECAL; //We disable also decals, since hit.pt is not refined with render mesh params.fDecalPlacementTestMaxSize = pCollision->fDecalPlacementTestMaxSize; pMaterialEffects->ExecuteEffect(effectId, params); } } #ifdef DEBUG_CannonBall_PENETRATION if (debugCannonBallPenetration) { s_debugCannonBallPenetration.AddCannonBallHit(pCollision->pt, hitDirection, damageBeforePenetration, pierceabilityMat, false, false, false); s_debugCannonBallPenetration.AddCannonBallHit(hit.pt, hitDirection, GetDamageAfterPenetrationFallOff(), pierceabilityMat, true, false, false); } #endif } else { #ifdef DEBUG_CannonBall_PENETRATION if (debugCannonBallPenetration) { s_debugCannonBallPenetration.AddCannonBallHit(pCollision->pt, hitDirection, damageBeforePenetration, pierceabilityMat, false, true, true); } #endif //Surface must be too thick, add enough fall off to destroy the Cannon Ball m_accumulatedDamageFallOffAfterPenetration += (float)m_damage; } } } }