void CDeflectorShield::ShootDeflectedEnergy(const CDeflectorShield::SDeflectedEnergy& energy) { if (!m_pAmmoClass) return; CProjectile* pEnergyBlast = g_pGame->GetWeaponSystem()->SpawnAmmo(m_pAmmoClass, false); if (!pEnergyBlast) return; const Matrix34& worldTransform = GetEntity()->GetWorldTM(); const float positionBias = 0.05f; const Vec3 worldReflectPos = worldTransform.TransformPoint(energy.m_localPosition); const Vec3 worldReflectDir = worldTransform.TransformVector(energy.m_localDirection); const Vec3 worldSpreadU = worldReflectDir.GetOrthogonal(); const Vec3 worldSpreadV = worldReflectDir.Cross(worldSpreadU); const float spreadOffset = cry_random(0.0f, m_spread); const Vec3 position = worldReflectPos + worldReflectDir * positionBias; Vec3 direction = worldReflectDir + (worldSpreadU * cry_random(0.0f, m_spread)) + (worldSpreadV * cry_random(0.0f, m_spread)); direction.Normalize(); CProjectile::SProjectileDesc projectileDesc( 0, 0, 0, energy.m_damage, m_dropMinDistance, m_dropPerMeter, float(m_minDamage), m_hitTypeId, 0, false); pEnergyBlast->SetParams(projectileDesc); pEnergyBlast->Launch(position, direction, Vec3(ZERO)); }
void CView::GetRandomVector( Vec3 &vec, SShake* pShake ) { vec = pShake->amountVector; float randomAmt(pShake->randomness); float len = fabs(pShake->amountVector.x) + fabs(pShake->amountVector.y) + fabs(pShake->amountVector.z); len /= 3.f; float r = len * randomAmt; vec += Vec3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r)); }
void CView::GetRandomQuat( Quat &quat, SShake* pShake ) { quat.SetRotationXYZ(pShake->amount); float randomAmt(pShake->randomness); float len(fabs(pShake->amount.x) + fabs(pShake->amount.y) + fabs(pShake->amount.z)); len /= 3.f; float r = len * randomAmt; quat *= Quat::CreateRotationXYZ(Ang3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r))); }
void UpdateLookTarget() { const SLookAroundParams& params = GetParams(); //--- TODO! Context use of random number generator! float yaw = cry_random(params.yawMin, params.yawMax); float pitch = cry_random(params.pitchMin, params.pitchMax); m_lookOffset.Set(sin_tpl(yaw), cos_tpl(yaw), 0.0f); m_lookAroundTime = cry_random(params.timeMin, params.timeMax); }
void CalcFiringPosition(SActivationInfo* pActInfo, IWeapon* pWeapon) { Vec3 realTargetPos = GetTargetPos( pActInfo ); m_firingPos = realTargetPos; float acc = GetPortFloat( pActInfo, IN_ACCURACY ); if (acc<1) { bool itHits = cry_random(0.0f, 1.0f) < acc; if (!itHits) { // what this code does is: // - in the plane perpendicular to the shooting vector, and located at the target, it builds a vector, centered in the target, and rotates it to point in a random direction, but always in the plane // - then it moves along that vector, a random distance (error distance) // - and that is the final to-fire position const float MAX_ERROR_LENGTH = 6.f; // meters const float MIN_ERROR_ANGLE = 2.f; // degrees const float MAX_ERROR_ANGLE = 5.f; // degrees // error angle from weapon to target float errorAngle = cry_random(MIN_ERROR_ANGLE, MAX_ERROR_ANGLE); // 2d angle, in the plane normal to the vector from weapon to target. float dirErrorAngle = cry_random(0.0f, 360.0f); // could be done with just 1 sqrt instead 2, but is not really worth it here. Vec3 vecToTarget = pActInfo->pEntity->GetPos() - realTargetPos; Vec3 vecToTargetNorm = vecToTarget.GetNormalizedSafe(); Vec3 dirError2D = vecToTargetNorm.GetOrthogonal(); dirError2D = dirError2D.GetRotated( vecToTargetNorm, DEG2RAD(dirErrorAngle) ); float dist = vecToTarget.len(); float errorLen = std::min( dist * tanf( DEG2RAD(errorAngle) ), MAX_ERROR_LENGTH ); m_firingPos = realTargetPos + ( dirError2D * errorLen ); #ifdef DEBUG_NODEFIREWEAPON posTarget = realTargetPos; posOrig = pActInfo->pEntity->GetPos(); posShot = m_firingPos; #endif } } if (GetPortBool( pActInfo, IN_ALIGNTOTARGET )) { UpdateWeaponTM( pActInfo, m_firingPos ); m_lastPos = pActInfo->pEntity->GetWorldPos(); m_lastRotation = pActInfo->pEntity->GetWorldRotation(); } pWeapon->SetDestination( m_firingPos ); }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorImpulse::OnDamageEvent(EVehicleDamageBehaviorEvent event, const SVehicleDamageBehaviorEventParams& behaviorParams) { if (event == eVDBE_Hit || event == eVDBE_VehicleDestroyed || event == eVDBE_ComponentDestroyed) { IEntity* pEntity = m_pVehicle->GetEntity(); CRY_ASSERT(pEntity); IPhysicalEntity* pPhysEntity = pEntity->GetPhysics(); if (!pPhysEntity) return; pe_status_dynamics dyn; pPhysEntity->GetStatus(&dyn); float vehicleMass = dyn.mass; float r = cry_random(0.0f, 2.f); float impulseForce = cry_random(m_forceMin, m_forceMax) * vehicleMass; Vec3 impulseDir(m_impulseDir); if (!m_worldSpace) impulseDir = m_pVehicle->GetEntity()->GetWorldTM().TransformVector(impulseDir); pe_action_impulse actionImpulse; Vec3& impulse = actionImpulse.impulse; Vec3& angImpulse = actionImpulse.angImpulse; impulse = impulseDir * impulseForce; angImpulse = m_pVehicle->GetEntity()->GetWorldTM().TransformVector(m_angImpulse); if (r <= 0.75f) { float r1 = cry_random(-0.35f, 0.35f); angImpulse += dyn.v * r1 * max(1.0f, dyn.w.GetLength()); angImpulse *= vehicleMass; } else { float r1 = cry_random(-0.25f, 0.25f); float r2 = cry_random(-0.5f, 0.5f); impulse.z += abs(dyn.v.y) * r1 * vehicleMass; angImpulse.x += dyn.v.y * r2 * vehicleMass * max(1.0f, dyn.w.GetLength() * 1.5f); } if (m_pImpulseLocation) actionImpulse.point = m_pImpulseLocation->GetWorldSpaceTranslation(); pPhysEntity->Action(&actionImpulse); } }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo * pActInfo ) { switch (event) { case eFE_Initialize: case eFE_Activate: if (IsPortActive(pActInfo, 0)) { int minOut = GetPortInt(pActInfo,1); int maxOut = GetPortInt(pActInfo,2); minOut = CLAMP(minOut, 0, NUM_OUTPUTS); maxOut = CLAMP(maxOut, 0, NUM_OUTPUTS); if (maxOut < minOut) std::swap(minOut, maxOut); int n = cry_random(minOut, maxOut); // Collect the outputs to use static int out[NUM_OUTPUTS]; for (unsigned i = 0; i < NUM_OUTPUTS; i++) out[i] = -1; int nout = 0; for (int i = 0; i < NUM_OUTPUTS; i++) { if (IsOutputConnected(pActInfo, i)) { out[nout] = i; nout++; } } if (n > nout) n = nout; // Shuffle for (int i = 0; i < n; i++) std::swap(out[i], out[cry_random(0, nout - 1)]); // Set outputs. for (int i = 0; i < n; i++) { if (out[i] == -1) continue; ActivateOutput(pActInfo,out[i],GetPortAny(pActInfo, 0)); } } break; } }
//----------------------------------------------------------------------- bool CSpectacularKill::GetValidAnim(const CActor* pTarget, SSpectacularKillAnimation& anim) const { CRY_ASSERT(pTarget); bool bSuccess = false; const SSpectacularKillParams* pTargetClassParams = GetParamsForClass(pTarget->GetEntity()->GetClass()); if (pTargetClassParams) { TSpectacularKillAnimVector validAnims; std::remove_copy_if(pTargetClassParams->animations.begin(), pTargetClassParams->animations.end(), std::back_inserter(validAnims), SPredNotValidAnim(*this, pTarget)); int iNumValidAnims = static_cast<int>(validAnims.size()); if (iNumValidAnims > 0) { anim = validAnims[cry_random(0, iNumValidAnims - 1)]; bSuccess = true; } else { DebugLog("Can't start from %s to %s: no animation matches current context", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName()); } } else { DebugLog("Can't start from %s to %s: no parameters for target class %s", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName(), pTarget->GetEntity()->GetClass()->GetName()); } return bSuccess; }
TMFXEffectBasePtr CMFXRandomizerContainer::ChooseCandidate( const SMFXRunTimeEffectParams& params ) const { const size_t nEffects = m_effects.size(); if (nEffects == 0) return TMFXEffectBasePtr(NULL); CryFixedArray<TMFXEffectBasePtr, 16> candidatesArray; TMFXEffects::const_iterator it = m_effects.begin(); TMFXEffects::const_iterator itEnd = m_effects.end(); while ((it != itEnd) && !candidatesArray.isfull()) { const TMFXEffectBasePtr& pEffect = *it; if (pEffect->CanExecute(params)) { candidatesArray.push_back(pEffect); } ++it; } TMFXEffectBasePtr pChosenEffect = NULL; if (!candidatesArray.empty()) { const uint32 randChoice = cry_random(0U, candidatesArray.size() - 1); pChosenEffect = candidatesArray[randChoice]; } return pChosenEffect; }
void CView::ProcessShakeNormal_DoShaking( SShake* pShake, float frameTime) { float t; if (pShake->nextShake <= 0.0f) { //angular pShake->goalShake.SetRotationXYZ(pShake->amount); if (pShake->flip) pShake->goalShake.Invert(); //translational pShake->goalShakeVector = pShake->amountVector; if (pShake->flip) pShake->goalShakeVector = -pShake->goalShakeVector; if (pShake->doFlip) pShake->flip = !pShake->flip; //randomize it a little float randomAmt(pShake->randomness); float len(fabs(pShake->amount.x) + fabs(pShake->amount.y) + fabs(pShake->amount.z)); len /= 3.0f; float r = len * randomAmt; pShake->goalShake *= Quat::CreateRotationXYZ(Ang3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r))); //translational randomization len = fabs(pShake->amountVector.x) + fabs(pShake->amountVector.y) + fabs(pShake->amountVector.z); len /= 3.0f; r = len * randomAmt; pShake->goalShakeVector += Vec3(cry_random(-r, r), cry_random(-r, r), cry_random(-r, r)); //damp & bounce it in a non linear fashion t = 1.0f - (pShake->ratio * pShake->ratio); pShake->goalShake = Quat::CreateSlerp(pShake->goalShake,IDENTITY,t); pShake->goalShakeVector = Vec3::CreateLerp(pShake->goalShakeVector,ZERO,t); pShake->nextShake = pShake->frequency; } pShake->nextShake = max(0.0f,pShake->nextShake - frameTime); t = min(1.0f,frameTime * (1.0f / pShake->frequency)); pShake->shakeQuat = Quat::CreateSlerp(pShake->shakeQuat,pShake->goalShake,t); pShake->shakeQuat.Normalize(); pShake->shakeVector = Vec3::CreateLerp(pShake->shakeVector,pShake->goalShakeVector,t); }
Vec3 CRateOfDeathSimple::CalculateTargetOffset( const float minRange, const float maxRange ) const { // TODO: Change this later! const float randomRange = cry_random( minRange, maxRange ); const Vec3 randomOffset = cry_random_unit_vector<Vec3>() * randomRange; return randomOffset; }
// Description: // // Arguments: // // Return: // void CPersonalSignalTimer::Reset( bool bAlsoEnable ) { CRY_ASSERT( m_bInit == true ); if( m_fRateMin < m_fRateMax ) { m_fTimer = cry_random( m_fRateMin, m_fRateMax ); } else { m_fTimer = m_fRateMin; } SetEnabled( bAlsoEnable ); }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorDetachPart::AttachParticleEffect(IEntity* pDetachedEntity, IParticleEffect* pEffect) { if (pEffect) { int slot = pDetachedEntity->LoadParticleEmitter(-1, pEffect, NULL, false, true); if (IParticleEmitter* pParticleEmitter = pDetachedEntity->GetParticleEmitter(slot)) { SpawnParams spawnParams; spawnParams.fSizeScale = cry_random(0.5f, 1.0f); pParticleEmitter->SetSpawnParams(spawnParams); } } }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorEffect::UpdateEffect(float randomness, float damageRatio) { CRY_ASSERT(m_pDamageEffect); if(m_pDamageEffect) { if(IParticleEmitter *pParticleEmitter = m_pVehicle->GetEntity()->GetParticleEmitter(m_slot)) { SpawnParams spawnParams; spawnParams.fPulsePeriod = m_pDamageEffect->pulsePeriod * ((1.0f - randomness) * cry_random(0.0f, 1.0f)); pParticleEmitter->SetSpawnParams(spawnParams); } } }
void CAntiCheatManager::KickPlayerDelayed(uint16 channelId, EDisconnectionCause reason, int nConfidence) { if (CGameLobby* pGameLobby = g_pGame->GetGameLobby()) { CryUserID userId = pGameLobby->GetUserIDFromChannelID(channelId); SDelayedKickData kickData; const float fMinDelay = GetAntiCheatVar(eAV_KD_Min, 5.0f); kickData.fKickCountdown = (cry_random(0.0f, 1.0f) * (GetAntiCheatVar(eAV_KD_Max, 25.0f) - fMinDelay)) + fMinDelay; kickData.userId = userId; kickData.channelId = channelId; kickData.reason = reason; m_DelayedKickData.push_back(kickData); } }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::Reset() { CVehicleMovementBase::Reset(); Lift(false); m_waveTimer = cry_random(0.0f, gf_PI); m_prevAngle = 0.0f; m_diving = false; m_wakeSlot = -1; m_rpmPitchDir = 0; m_waveSoundPitch = 0.f; m_inWater = false; m_factorMaxSpeed = 1.f; m_factorAccel = 1.f; }
void CBoidBug::UpdateBugsBehavior( float dt,SBoidContext &bc ) { if (cry_random(0, 9) == 0) { // Randomally modify heading vector. m_heading.x += Boid::Frand()*0.2f*bc.factorAlignment; // Used as random movement. m_heading.y += Boid::Frand()*0.2f*bc.factorAlignment; m_heading.z += Boid::Frand()*0.1f*bc.factorAlignment; m_heading = m_heading.GetNormalized(); if (bc.behavior == EBUGS_DRAGONFLY) m_speed = bc.MinSpeed + (bc.MaxSpeed - bc.MinSpeed)*Boid::Frand(); } // Avoid player. Vec3 fromPlayerVec = Vec3( m_pos.x-bc.playerPos.x, m_pos.y-bc.playerPos.y, 0 ); if ((fromPlayerVec).GetLengthSquared() < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE) // 2 meters. { float d = (BUGS_SCARE_DISTANCE - fromPlayerVec.GetLength()); m_accel += 5.0f * fromPlayerVec * d; } // Maintain average speed. float targetSpeed = (bc.MaxSpeed + bc.MinSpeed)/2; m_accel -= m_heading*(m_speed-targetSpeed)*0.5f; //m_accel = (m_targetPos - m_pos)*bc.factorAttractToOrigin; if (m_pos.z < bc.terrainZ+bc.MinHeight) { m_accel.z = (bc.terrainZ+bc.MinHeight-m_pos.z)*bc.factorAttractToOrigin; } else if (m_pos.z > bc.terrainZ+bc.MaxHeight) { m_accel.z = -(m_pos.z-bc.terrainZ+bc.MinHeight)*bc.factorAttractToOrigin; } else { // Always try to accelerate in direction oposite to current in Z axis. m_accel.z += -m_heading.z * 0.2f; } }
void CVTOLVehicleManager::CreateExplosion(IParticleEffect *inParticleEffect, const Vec3& pos, const float inEffectScale, TAudioSignalID inAudioSignal/*=INVALID_AUDIOSIGNAL_ID*/) { #if !defined(DEDICATED_SERVER) CRY_ASSERT_MESSAGE(inParticleEffect, "CreateExplosion() passsed a NULL inParticleEffect"); #endif if (gEnv->bServer) { // server wants to create actual explosions CGameRules *pGameRules = g_pGame->GetGameRules(); ExplosionInfo explosionInfo; explosionInfo.pParticleEffect = inParticleEffect; explosionInfo.effect_name = s_explosionEffectName;// needed so the explosion can be synced to clients explosionInfo.type = pGameRules->GetHitTypeId("explosion"); explosionInfo.effect_scale = inEffectScale; // this is likely not connected up in the particle effect but should be able to be so that the effect can be adjusted dynamically explosionInfo.pos = pos; explosionInfo.dir.Set(0.0f,-1.0f,0.0f); explosionInfo.effect_scale = cry_random(0.9f,1.1f); explosionInfo.pressure = 1000.0f; explosionInfo.maxblurdistance = 10.0; explosionInfo.radius = 12.0f; explosionInfo.blindAmount = 0.0f; explosionInfo.flashbangScale = 8.0f; explosionInfo.damage = 0.0f; explosionInfo.hole_size = 0.0f; pGameRules->QueueExplosion(explosionInfo); } // the gamerules SHOULD be syncing the server queued ones above to clients! if (inAudioSignal != INVALID_AUDIOSIGNAL_ID) { CAudioSignalPlayer::JustPlay(inAudioSignal, pos); } }
//-------------------------------------------------------------------------------------------------- // Name: SpawnScreenExplosionEffect // Desc: Spawns screen explosion effect //-------------------------------------------------------------------------------------------------- void CExplosionGameEffect::SpawnScreenExplosionEffect(const SExplosionContainer &explosionContainer) { // Disclaimer: this code was originally from GameRulesClientServer::ProcessClientExplosionScreenFX() const ExplosionInfo& explosionInfo = explosionContainer.m_explosionInfo; if(explosionInfo.pressure < 1.0f) return; IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor(); if(pClientActor != NULL && !pClientActor->IsDead()) { CPlayer* pPlayer = static_cast<CPlayer*>(pClientActor); bool hasFlashBangEffect = explosionInfo.blindAmount > 0.0f; // Flashbang friends and self?... if(hasFlashBangEffect) { bool flashBangSelf = true; bool flashBangFriends = false; #ifndef _RELEASE flashBangSelf = g_pGameCVars->g_flashBangSelf != 0; flashBangFriends = g_pGameCVars->g_flashBangFriends != 0; #endif bool ownFlashBang = pPlayer->GetEntityId() == explosionInfo.shooterId; if((!flashBangSelf && ownFlashBang) || // FlashBang self? ((g_pGame->GetGameRules()->GetFriendlyFireRatio()<=0.0f) && (!flashBangFriends) && (!ownFlashBang) && pPlayer->IsFriendlyEntity(explosionInfo.shooterId))) // FlashBang friends? { return; } } // Distance float dist = (pClientActor->GetEntity()->GetWorldPos() - explosionInfo.pos).len(); // Is the explosion in Player's FOV (let's suppose the FOV a bit higher, like 80) SMovementState state; if(IMovementController *pMV = pClientActor->GetMovementController()) { pMV->GetMovementState(state); } Vec3 eyeToExplosion = explosionInfo.pos - state.eyePosition; Vec3 eyeDir = pClientActor->GetLinkedVehicle() ? pPlayer->GetVehicleViewDir() : state.eyeDirection; eyeToExplosion.Normalize(); float eyeDirectionDP = eyeDir.Dot(eyeToExplosion); bool inFOV = (eyeDirectionDP > 0.68f); // All explosions have radial blur (default 30m radius) const float maxBlurDistance = (explosionInfo.maxblurdistance >0.0f) ? explosionInfo.maxblurdistance : 30.0f; if((maxBlurDistance > 0.0f) && (g_pGameCVars->g_radialBlur > 0.0f) && (explosionInfo.radius > 0.5f)) { if (inFOV && (dist < maxBlurDistance)) { const int intersectionObjTypes = ent_static | ent_terrain; const unsigned int intersectionFlags = rwi_stop_at_pierceable|rwi_colltype_any; m_deferredScreenEffects.RequestRayCast(CDeferredExplosionEffect::eDET_RadialBlur, explosionInfo.pos, -eyeToExplosion, dist, maxBlurDistance, intersectionObjTypes, intersectionFlags, NULL, 0); } } // Flashbang effect if(hasFlashBangEffect && ((dist < (explosionInfo.radius*g_pGameCVars->g_flashBangNotInFOVRadiusFraction)) || (inFOV && (dist < explosionInfo.radius)))) { ray_hit hit; const int intersectionObjTypes = ent_static | ent_terrain; const unsigned int intersectionFlags = rwi_stop_at_pierceable|rwi_colltype_any; const int intersectionMaxHits = 1; int collision = gEnv->pPhysicalWorld->RayWorldIntersection( explosionInfo.pos, -eyeToExplosion*dist, intersectionObjTypes, intersectionFlags, &hit, intersectionMaxHits); // If there was no obstacle between flashbang grenade and player if(!collision) { bool enabled = true; if(enabled) { CCCPOINT (FlashBang_Explode_BlindLocalPlayer); float timeScale = max(0.0f, 1 - (dist/explosionInfo.radius)); float lookingAt = max(g_pGameCVars->g_flashBangMinFOVMultiplier, (eyeDirectionDP + 1)*0.5f); float time = explosionInfo.flashbangScale * timeScale *lookingAt; // time is determined by distance to explosion CRY_ASSERT_MESSAGE(pClientActor->IsPlayer(),"Effect shouldn't be spawned if not a player"); SPlayerStats* pStats = static_cast<SPlayerStats*>(pPlayer->GetActorStats()); NET_BATTLECHATTER(BC_Blinded, pPlayer); if(pClientActor->GetEntityId() == explosionInfo.shooterId) { g_pGame->GetPersistantStats()->IncrementClientStats(EIPS_BlindSelf); } else { g_pGame->GetGameRules()->SuccessfulFlashBang(explosionInfo, time); } pPlayer->StartFlashbangEffects(time, explosionInfo.shooterId); gEnv->p3DEngine->SetPostEffectParam("Flashbang_Time", time); gEnv->p3DEngine->SetPostEffectParam("FlashBang_BlindAmount", explosionInfo.blindAmount); gEnv->p3DEngine->SetPostEffectParam("Flashbang_DifractionAmount", time); gEnv->p3DEngine->SetPostEffectParam("Flashbang_Active", 1.0f); CRecordingSystem *pRecordingSystem = g_pGame->GetRecordingSystem(); if (pRecordingSystem) { pRecordingSystem->OnPlayerFlashed(time, explosionInfo.blindAmount); } } } else { CCCPOINT (FlashBang_Explode_NearbyButBlockedByGeometry); } } else if(inFOV && (dist < explosionInfo.radius)) { if(explosionInfo.damage>10.0f || explosionInfo.pressure>100.0f) { // Add some angular impulse to the client actor depending on distance, direction... float dt = (1.0f - dist/explosionInfo.radius); dt = dt * dt; float angleZ = gf_PI*0.15f*dt; float angleX = gf_PI*0.15f*dt; if (pClientActor) { static_cast<CActor*>(pClientActor)->AddAngularImpulse(Ang3(cry_random(-angleX*0.5f,angleX),0.0f,cry_random(-angleZ,angleZ)),0.0f,dt*2.0f); } } } } }//-------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------ bool CVehicleMovementStdBoat::Init(IVehicle* pVehicle, const CVehicleParams& table) { if (!CVehicleMovementBase::Init(pVehicle, table)) return false; MOVEMENT_VALUE("velMax", m_velMax); MOVEMENT_VALUE("velMaxReverse", m_velMaxReverse); MOVEMENT_VALUE("acceleration", m_accel); MOVEMENT_VALUE("accelerationVelMax", m_accelVelMax); MOVEMENT_VALUE("accelerationMultiplier", m_accelCoeff); MOVEMENT_VALUE("pushTilt", m_pushTilt); MOVEMENT_VALUE("turnRateMax", m_turnRateMax); MOVEMENT_VALUE("turnAccel", m_turnAccel); MOVEMENT_VALUE("cornerForce", m_cornerForceCoeff); MOVEMENT_VALUE("cornerTilt", m_cornerTilt); MOVEMENT_VALUE("turnDamping", m_turnDamping); MOVEMENT_VALUE("turnAccelMultiplier", m_turnAccelCoeff); MOVEMENT_VALUE_OPT("rollAccel", m_rollAccel, table); MOVEMENT_VALUE_OPT("pedalLimitReverse", m_pedalLimitReverse, table); MOVEMENT_VALUE_OPT("turnVelocityMult", m_turnVelocityMult, table); MOVEMENT_VALUE_OPT("velLift", m_velLift, table); MOVEMENT_VALUE_OPT("lateralDamping", m_lateralDamping, table); table.getAttr("waveIdleStrength", m_waveIdleStrength); table.getAttr("waveSpeedMult", m_waveSpeedMult); if (table.haveAttr("cornerHelper")) { if (IVehicleHelper* pHelper = m_pVehicle->GetHelper(table.getAttr("cornerHelper"))) m_cornerOffset = pHelper->GetVehicleSpaceTranslation(); } if (table.haveAttr("pushHelper")) { if (IVehicleHelper* pHelper = m_pVehicle->GetHelper(table.getAttr("pushHelper"))) m_pushOffset = pHelper->GetVehicleSpaceTranslation(); } // compute inertia [assumes box] AABB bbox; IVehiclePart* pMassPart = pVehicle->GetPart("mass"); if (!pMassPart) pMassPart = pVehicle->GetPart("massBox"); if (pMassPart) { bbox = pMassPart->GetLocalBounds(); } else { GameWarning("[CVehicleMovementStdBoat]: initialization: No \"mass\" geometry found!"); m_pEntity->GetLocalBounds(bbox); } m_maxSpeed = m_velMax; float mass = pVehicle->GetMass(); float width = bbox.max.x - bbox.min.x; float length = bbox.max.y - bbox.min.y; float height = bbox.max.z - bbox.min.z; m_Inertia.x = mass * (sqr(length)+ sqr(height)) / 12; m_Inertia.y = mass * (sqr(width) + sqr(height)) / 12; m_Inertia.z = mass * (sqr(width) + sqr(length)) / 12; m_massOffset = bbox.GetCenter(); //CryLog("[StdBoat movement]: got mass offset (%f, %f, %f)", m_massOffset.x, m_massOffset.y, m_massOffset.z); m_pSplashPos = m_pVehicle->GetHelper("splashPos"); if (m_pSplashPos) m_lastWakePos = m_pSplashPos->GetWorldSpaceTranslation(); else m_lastWakePos = m_pVehicle->GetEntity()->GetWorldTM().GetTranslation(); const char* waveEffect = ""; MOVEMENT_VALUE_OPT("waveEffect", &waveEffect, table); m_pWaveEffect = gEnv->pParticleManager->FindEffect(waveEffect, "MovementStdBoat"); m_waveTimer = cry_random(0.0f, gf_PI); m_diving = false; m_wakeSlot = -1; m_waveSoundPitch = 0.f; m_rpmPitchDir = 0; m_waveSoundAmount = 0.1f; // AI related m_prevAngle = 0.0f; m_factorMaxSpeed = 1.f; m_factorAccel = 1.f; return true; }
//-------------------------------------------------------------------------------------------------- // Name: SpawnParticlesOnSkeleton // Desc: Spawn particles on Skeleton //-------------------------------------------------------------------------------------------------- void CGameEffect::SpawnParticlesOnSkeleton(IEntity* pEntity, IParticleEmitter* pParticleEmitter, uint32 numParticles,float maxHeightScale) const { if((pEntity) && (numParticles>0) && (pParticleEmitter) && (maxHeightScale>0.0f)) { ICharacterInstance* pCharacter = pEntity->GetCharacter(0); if(pCharacter) { IDefaultSkeleton& rIDefaultSkeleton = pCharacter->GetIDefaultSkeleton(); ISkeletonPose* pPose = pCharacter->GetISkeletonPose(); if(pPose) { Vec3 animPos; Quat animRot; IActor* pActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntity->GetId()); if(pActor) // First try to get animation data { QuatT animLoc = pActor->GetAnimatedCharacter()->GetAnimLocation(); animPos = animLoc.t; animRot = animLoc.q; } else // If no actor, then use entity data { animPos = pEntity->GetWorldPos(); animRot = pEntity->GetWorldRotation(); } animRot.Invert(); AABB bbox; pEntity->GetLocalBounds(bbox); float bbHeight = bbox.max.z - bbox.min.z; // Avoid division by 0 if(bbHeight == 0) { bbHeight = 0.0001f; } const uint32 numJoints = rIDefaultSkeleton.GetJointCount(); for (uint32 i = 0; i < numParticles; ++i) { int id = cry_random(0U, numJoints - 1); int parentId = rIDefaultSkeleton.GetJointParentIDByID(id); if(parentId>0) { QuatT boneQuat = pPose->GetAbsJointByID(id); QuatT parentBoneQuat= pPose->GetAbsJointByID(parentId); float lerpScale = cry_random(0.0f, 1.0f); QuatTS loc(IDENTITY); loc.t = LERP(boneQuat.t,parentBoneQuat.t,lerpScale); float heightScale = ((loc.t.z - bbox.min.z) / bbHeight); if(heightScale < maxHeightScale) { loc.t = loc.t * animRot; loc.t = loc.t + animPos; pParticleEmitter->EmitParticle(NULL, NULL, &loc); } } } } } } }//-------------------------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementStdBoat::ProcessMovement(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); static const float fWaterLevelMaxDiff = 0.15f; // max allowed height difference between propeller center and water level static const float fSubmergedMin = 0.01f; static const float fMinSpeedForTurn = 0.5f; // min speed so that turning becomes possible if (m_bNetSync) m_netActionSync.UpdateObject(this); CryAutoCriticalSection lk(m_lock); CVehicleMovementBase::ProcessMovement(deltaTime); IEntity* pEntity = m_pVehicle->GetEntity(); IPhysicalEntity* pPhysics = pEntity->GetPhysics(); SVehiclePhysicsStatus* physStatus = &m_physStatus[k_physicsThread]; assert(pPhysics); float frameTime = min(deltaTime, 0.1f); if (abs(m_movementAction.power) < 0.001f) m_movementAction.power = 0.f; if (abs(m_movementAction.rotateYaw) < 0.001f) m_movementAction.rotateYaw = 0.f; Matrix34 wTM( physStatus->q ); wTM.AddTranslation( physStatus->pos ); Matrix34 wTMInv = wTM.GetInvertedFast(); Vec3 localVel = wTMInv.TransformVector( physStatus->v ); Vec3 localW = wTMInv.TransformVector( physStatus->w ); const Vec3 xAxis = wTM.GetColumn0(); const Vec3 yAxis = wTM.GetColumn1(); const Vec3 zAxis = wTM.GetColumn2(); // check if propeller is in water Vec3 worldPropPos = wTM * m_pushOffset; float waterLevelWorld = gEnv->p3DEngine->GetWaterLevel( &worldPropPos ); float fWaterLevelDiff = worldPropPos.z - waterLevelWorld; bool submerged = physStatus->submergedFraction > fSubmergedMin; m_inWater = submerged && fWaterLevelDiff < fWaterLevelMaxDiff; float speed = physStatus->v.len2() > 0.001f ? physStatus->v.len() : 0.f; float speedRatio = min(1.f, speed/(m_maxSpeed*m_factorMaxSpeed)); float absPedal = abs(m_movementAction.power); float absSteer = abs(m_movementAction.rotateYaw); // wave stuff float waveFreq = 1.f; waveFreq += 3.f*speedRatio; float waveTimerPrev = m_waveTimer; m_waveTimer += frameTime*waveFreq; // new randomized amount for this oscillation if (m_waveTimer >= gf_PI && waveTimerPrev < gf_PI) m_waveRandomMult = cry_random(0.0f, 1.0f); if (m_waveTimer >= 2*gf_PI) m_waveTimer -= 2*gf_PI; float kx = m_waveIdleStrength.x*(m_waveRandomMult+0.3f) * (1.f-speedRatio + m_waveSpeedMult*speedRatio); float ky = m_waveIdleStrength.y * (1.f - 0.5f*absPedal - 0.5f*absSteer); Vec3 waveLoc = m_massOffset; waveLoc.y += speedRatio*min(0.f, m_pushOffset.y-m_massOffset.y); waveLoc = wTM * waveLoc; bool visible = m_pVehicle->GetGameObject()->IsProbablyVisible(); bool doWave = visible && submerged && physStatus->submergedFraction < 0.99f; if (doWave && !m_isEnginePowered) m_pVehicle->NeedsUpdate(IVehicle::eVUF_AwakePhysics); if (m_isEnginePowered || (visible && !m_pVehicle->IsProbablyDistant())) { if (doWave && (m_isEnginePowered || g_pGameCVars->v_rockBoats)) { pe_action_impulse waveImp; waveImp.angImpulse.x = Boosting() ? 0.f : sinf(m_waveTimer) * frameTime * m_Inertia.x * kx; if (isneg(waveImp.angImpulse.x)) waveImp.angImpulse.x *= (1.f - min(1.f, 2.f*speedRatio)); // less amplitude for negative impulse waveImp.angImpulse.y = sinf(m_waveTimer-0.5f*gf_PI) * frameTime * m_Inertia.y * ky; waveImp.angImpulse.z = 0.f; waveImp.angImpulse = wTM.TransformVector(waveImp.angImpulse); waveImp.point = waveLoc; if (!m_movementAction.isAI) pPhysics->Action(&waveImp, 1); } } // ~wave stuff if (!m_isEnginePowered) return; pe_action_impulse linearImp, angularImp, dampImp, liftImp; float turnAccel = 0, turnAccelNorm = 0; if (m_inWater) { // Lateral damping if (m_lateralDamping>0.f) { pe_action_impulse impulse; impulse.impulse = - physStatus->mass * xAxis * (localVel.x * (frameTime * m_lateralDamping)/(1.f + frameTime*m_lateralDamping)); pPhysics->Action(&impulse, 1); } // optional lifting (catamarans) if (m_velLift > 0.f) { if (localVel.y > m_velLift && !IsLifted()) Lift(true); else if (localVel.y < m_velLift && IsLifted()) Lift(false); } if (Boosting() && IsLifted()) { // additional lift force liftImp.impulse = Vec3(0,0,physStatus->mass*frameTime*(localVel.y/(m_velMax*m_factorMaxSpeed))*3.f); liftImp.point = wTM * m_massOffset; pPhysics->Action(&liftImp, 1); } // apply driving force float a = m_movementAction.power; if (sgn(a)*sgn(localVel.y) > 0) { // reduce acceleration with increasing speed float ratio = (localVel.y > 0.f) ? localVel.y/(m_velMax*m_factorMaxSpeed) : -localVel.y/(m_velMaxReverse*m_factorMaxSpeed); a = (ratio>1.f) ? 0.f : sgn(a)*min(abs(a), 1.f-((1.f-m_accelVelMax)*sqr(ratio))); } if (a != 0) { if (sgn(a) * sgn(localVel.y) < 0) // "braking" a *= m_accelCoeff; else a = max(a, -m_pedalLimitReverse); Vec3 pushDir(FORWARD_DIRECTION); // apply force downwards a bit for more realistic response if (a > 0) pushDir = Quat_tpl<float>::CreateRotationAA( DEG2RAD(m_pushTilt), Vec3(-1,0,0) ) * pushDir; pushDir = wTM.TransformVector( pushDir ); linearImp.impulse = pushDir * physStatus->mass * a * m_accel * m_factorAccel * frameTime; linearImp.point = m_pushOffset; linearImp.point.x = m_massOffset.x; linearImp.point = wTM * linearImp.point; pPhysics->Action(&linearImp, 1); } float roll = (float)__fsel(zAxis.z - 0.2f, xAxis.z / (frameTime + frameTime*frameTime), 0.f); // Roll damping (with a exp. time constant of 1 sec) // apply steering if (m_movementAction.rotateYaw != 0) { if (abs(localVel.y) < fMinSpeedForTurn){ // if forward speed too small, no turning possible turnAccel = 0; } else { int iDir = m_movementAction.power != 0.f ? sgn(m_movementAction.power) : sgn(localVel.y); turnAccelNorm = m_movementAction.rotateYaw * iDir * max(1.f, m_turnVelocityMult * speedRatio); // steering and current w in same direction? int sgnSteerW = sgn(m_movementAction.rotateYaw) * iDir * sgn(-localW.z); if (sgnSteerW < 0) { // "braking" turnAccelNorm *= m_turnAccelCoeff; } else { // reduce turn vel towards max float maxRatio = 1.f - 0.15f*min(1.f, abs(localW.z)/m_turnRateMax); turnAccelNorm = sgn(turnAccelNorm) * min(abs(turnAccelNorm), maxRatio); } turnAccel = turnAccelNorm * m_turnAccel; //roll = 0.2f * turnAccel; // slight roll } } // Use the centripetal acceleration to determine the amount of roll float centripetalAccel = clamp_tpl(speed * localW.z, -10.f, +10.f); roll -= (1.f - 2.f*fabsf(xAxis.z)) * m_rollAccel * centripetalAccel; // Always damp rotation! turnAccel += localW.z * m_turnDamping; if (turnAccel != 0) { Vec3& angImp = angularImp.angImpulse; angImp.x = 0.f; angImp.y = roll * frameTime * m_Inertia.y; angImp.z = -turnAccel * frameTime * m_Inertia.z; angImp = wTM.TransformVector( angImp ); pPhysics->Action(&angularImp, 1); } if (abs(localVel.x) > 0.01f) { // lateral force Vec3& cornerForce = dampImp.impulse; cornerForce.x = -localVel.x * m_cornerForceCoeff * physStatus->mass * frameTime; cornerForce.y = 0.f; cornerForce.z = 0.f; if (m_cornerTilt != 0) cornerForce = Quat_tpl<float>::CreateRotationAA( sgn(localVel.x)*DEG2RAD(m_cornerTilt), Vec3(0,1,0) ) * cornerForce; dampImp.impulse = wTM.TransformVector(cornerForce); dampImp.point = m_cornerOffset; dampImp.point.x = m_massOffset.x; dampImp.point = wTM.TransformPoint( dampImp.point ); pPhysics->Action(&dampImp, 1); } } EjectionTest(deltaTime); if (!m_pVehicle->GetStatus().doingNetPrediction) { if (m_bNetSync && m_netActionSync.PublishActions( CNetworkMovementStdBoat(this) )) CHANGED_NETWORK_STATE(m_pVehicle, CNetworkMovementStdBoat::CONTROLLED_ASPECT ); } }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo * pActInfo ) { switch (event) { case eFE_ConnectOutputPort: if (pActInfo->connectPort < NUM_OUTPUTS) { ++m_nConnectionCounts[pActInfo->connectPort]; // check if already connected for (int i=0; i<m_nOutputCount; ++i) { if (m_nConnectedPorts[i] == pActInfo->connectPort) return; } m_nConnectedPorts[m_nOutputCount++] = pActInfo->connectPort; Reset(); } break; case eFE_DisconnectOutputPort: if (pActInfo->connectPort < NUM_OUTPUTS) { for (int i=0; i<m_nOutputCount; ++i) { // check if really connected if (m_nConnectedPorts[i] == pActInfo->connectPort) { if (m_nConnectionCounts[pActInfo->connectPort] == 1) { m_nConnectedPorts[i] = m_nPorts[m_nOutputCount - 1]; // copy last value to here m_nConnectedPorts[m_nOutputCount - 1] = -1; --m_nOutputCount; Reset(); } --m_nConnectionCounts[pActInfo->connectPort]; return; } } } break; case eFE_Initialize: Reset(); break; case eFE_Activate: if (IsPortActive(pActInfo, EIP_Reset)) { Reset(); } if (IsPortActive(pActInfo, EIP_Input)) { int numCandidates = m_nOutputCount - m_nTriggered; if (numCandidates <= 0) return; const int cand = cry_random(0, numCandidates - 1); const int whichPort = m_nPorts[cand]; m_nPorts[cand] = m_nPorts[numCandidates-1]; m_nPorts[numCandidates-1] = -1; ++m_nTriggered; assert (whichPort >= 0 && whichPort < NUM_OUTPUTS); // CryLogAlways("CFlowNode_RandomTrigger: Activating %d", whichPort); ActivateOutput(pActInfo, whichPort, GetPortAny(pActInfo, EIP_Input)); assert (m_nTriggered > 0 && m_nTriggered <= m_nOutputCount); if (m_nTriggered == m_nOutputCount) { // CryLogAlways("CFlowNode_RandomTrigger: Done."); // Done ActivateOutput(pActInfo, NUM_OUTPUTS, true); Reset(); } } break; } }
bool CShotgun::Shoot(bool resetAnimation, bool autoreload/* =true */, bool isRemote) { CCCPOINT(Shotgun_TryShoot); m_firePending = false; m_shotIndex++; IEntityClass* ammo = m_fireParams->fireparams.ammo_type_class; int ammoCount = m_pWeapon->GetAmmoCount(ammo); int clipSize = GetClipSize(); if (clipSize == 0) ammoCount = m_pWeapon->GetInventoryAmmoCount(ammo); CActor *pActor = m_pWeapon->GetOwnerActor(); bool playerIsShooter = pActor ? pActor->IsPlayer() : false; bool shooterIsClient = pActor ? pActor->IsClient() : false; if (!CanFire(true)) { if ((ammoCount <= 0) && (!m_reloading)) { m_pWeapon->PlayAction(GetFragmentIds().empty_clip); m_pWeapon->OnFireWhenOutOfAmmo(); CCCPOINT(Shotgun_TryShootWhileOutOfAmmo); } else { CCCPOINT(Shotgun_TryShootWhileCannotBeFired); } return false; } if (m_reloading) { if(m_pWeapon->IsBusy()) m_pWeapon->SetBusy(false); if(CanFire(true) && !m_break_reload) { m_break_reload = true; m_pWeapon->RequestCancelReload(); } CCCPOINT(Shotgun_TryShootWhileReloading); return false; } uint32 flags = CItem::eIPAF_Default; if (IsProceduralRecoilEnabled() && pActor) { pActor->ProceduralRecoil(m_fireParams->proceduralRecoilParams.duration, m_fireParams->proceduralRecoilParams.strength, m_fireParams->proceduralRecoilParams.kickIn, m_fireParams->proceduralRecoilParams.arms); } float speedOverride = -1.f; m_pWeapon->PlayAction(GetFragmentIds().fire, 0, false, flags, speedOverride); Vec3 hit = GetProbableHit(WEAPON_HIT_RANGE); Vec3 pos = GetFiringPos(hit); Vec3 fdir = GetFiringDir(hit, pos); Vec3 vel = GetFiringVelocity(fdir); Vec3 dir; const float hitDist = hit.GetDistance(pos); CheckNearMisses(hit, pos, fdir, WEAPON_HIT_RANGE, m_fireParams->shotgunparams.spread); CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId, string().Format("Invalid hit type '%s' in fire params for '%s'", m_fireParams->fireparams.hit_type.c_str(), m_pWeapon->GetEntity()->GetName())); CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId == g_pGame->GetGameRules()->GetHitTypeId(m_fireParams->fireparams.hit_type.c_str()), "Sanity Check Failed: Stored hit type id does not match the type string, possibly CacheResources wasn't called on this weapon type"); int quad = cry_random(0, 3); const int numPellets = m_fireParams->shotgunparams.pellets; std::vector<CProjectile*> projList; projList.reserve(numPellets); int ammoCost = (m_fireParams->fireparams.fake_fire_rate && playerIsShooter) ? m_fireParams->fireparams.fake_fire_rate : 1; ammoCost = min(ammoCost, ammoCount); EntityId firstAmmoId = 0; // SHOT HERE for (int i = 0; i < numPellets; i++) { CProjectile *pAmmo = m_pWeapon->SpawnAmmo(m_fireParams->fireparams.spawn_ammo_class, false); if (pAmmo) { if(!firstAmmoId) { firstAmmoId = pAmmo->GetEntityId(); } projList.push_back(pAmmo); dir = ApplySpread(fdir, m_fireParams->shotgunparams.spread, quad); quad = (quad+1)%4; int pelletDamage = m_fireParams->shotgunparams.pelletdamage; if (!playerIsShooter) pelletDamage += m_fireParams->shotgunparams.npc_additional_damage; const bool canOvercharge = m_pWeapon->GetSharedItemParams()->params.can_overcharge; const float overchargeModifier = pActor ? pActor->GetOverchargeDamageScale() : 1.0f; if (canOvercharge) { pelletDamage = int(pelletDamage * overchargeModifier); } CProjectile::SProjectileDesc projectileDesc( m_pWeapon->GetOwnerId(), m_pWeapon->GetHostId(), m_pWeapon->GetEntityId(), pelletDamage, m_fireParams->fireparams.damage_drop_min_distance, m_fireParams->fireparams.damage_drop_per_meter, m_fireParams->fireparams.damage_drop_min_damage, m_fireParams->fireparams.hitTypeId, m_fireParams->fireparams.bullet_pierceability_modifier, m_pWeapon->IsZoomed()); projectileDesc.pointBlankAmount = m_fireParams->fireparams.point_blank_amount; projectileDesc.pointBlankDistance = m_fireParams->fireparams.point_blank_distance; projectileDesc.pointBlankFalloffDistance = m_fireParams->fireparams.point_blank_falloff_distance; if (m_fireParams->fireparams.ignore_damage_falloff) projectileDesc.damageFallOffAmount = 0.0f; const Vec3 pelletDestination = pos + (dir * hitDist); pAmmo->SetParams(projectileDesc); pAmmo->SetDestination(m_pWeapon->GetDestination()); pAmmo->Launch(pos, dir, vel); pAmmo->CreateBulletTrail( pelletDestination ); pAmmo->SetKnocksTargetInfo( GetShared() ); if ((!m_fireParams->tracerparams.geometry.empty() || !m_fireParams->tracerparams.effect.empty()) && ((ammoCount == clipSize) || (ammoCount%m_fireParams->tracerparams.frequency==0))) { EmitTracer(pos, pelletDestination, &m_fireParams->tracerparams, pAmmo); } if(shooterIsClient) { pAmmo->RegisterLinkedProjectile(m_shotIndex); if(gEnv->bMultiplayer) { float damageCap = g_pGameCVars->pl_shotgunDamageCap; pAmmo->SetDamageCap(damageCap); } } m_projectileId = pAmmo->GetEntity()->GetId(); pAmmo->SetAmmoCost(ammoCost); } } if (m_pWeapon->IsServer()) { const char *ammoName = ammo != NULL ? ammo->GetName() : NULL; g_pGame->GetIGameFramework()->GetIGameplayRecorder()->Event(m_pWeapon->GetOwner(), GameplayEvent(eGE_WeaponShot, ammoName, m_fireParams->shotgunparams.pellets, (void *)(EXPAND_PTR)m_pWeapon->GetEntityId())); } m_muzzleEffect.Shoot(this, hit, m_barrelId); m_fired = true; SetNextShotTime(m_next_shot + m_next_shot_dt); ammoCount -= ammoCost; if (ammoCount < m_fireParams->fireparams.minimum_ammo_count) ammoCount = 0; if (clipSize != -1) { if (clipSize != 0) m_pWeapon->SetAmmoCount(ammo, ammoCount); else m_pWeapon->SetInventoryAmmoCount(ammo, ammoCount); } OnShoot(m_pWeapon->GetOwnerId(), firstAmmoId, ammo, pos, dir, vel); const SThermalVisionParams& thermalParams = m_fireParams->thermalVisionParams; m_pWeapon->AddShootHeatPulse(pActor, thermalParams.weapon_shootHeatPulse, thermalParams.weapon_shootHeatPulseTime, thermalParams.owner_shootHeatPulse, thermalParams.owner_shootHeatPulseTime); if (OutOfAmmo()) { m_pWeapon->OnOutOfAmmo(ammo); if (autoreload) { uint32 scheduleTime = max(m_pWeapon->GetCurrentAnimationTime(eIGS_Owner), (uint)(m_next_shot*1000)); m_pWeapon->GetScheduler()->TimerAction(scheduleTime, CSchedulerAction<ScheduleReload>::Create(ScheduleReload(this, m_pWeapon)), false); m_autoReloading = true; } } m_pWeapon->RequestShoot(ammo, pos, dir, vel, hit, 1.0f, 0, false); #if defined(ANTI_CHEAT) const int numProjectiles = projList.size(); uint32 shotId = m_pWeapon->GetLastShotId(); for(int i = 0; i < numProjectiles; i++) { CProjectile * pAmmo = projList[i]; pAmmo->SetShotId(shotId); shotId -= (1 << CWeapon::GetShotIdCountOffset()); } #endif CCCPOINT(Shotgun_Fired); return true; }
//------------------------------------------------------------------------ void CShotgun::NetShootEx(const Vec3 &pos, const Vec3 &dir, const Vec3 &vel, const Vec3 &hit, float extra, int ph) { CCCPOINT(Shotgun_NetShoot); assert(0 == ph); IEntityClass* ammo = m_fireParams->fireparams.ammo_type_class; FragmentID action = m_fireParams->fireparams.no_cock ? GetFragmentIds().fire : GetFragmentIds().fire_cock; CActor *pActor = m_pWeapon->GetOwnerActor(); bool playerIsShooter = pActor?pActor->IsPlayer():false; int ammoCount = m_pWeapon->GetAmmoCount(ammo); int clipSize = GetClipSize(); if (clipSize == 0) ammoCount = m_pWeapon->GetInventoryAmmoCount(ammo); if (ammoCount == 1) action = GetFragmentIds().fire; if (IsProceduralRecoilEnabled() && pActor) { pActor->ProceduralRecoil(m_fireParams->proceduralRecoilParams.duration, m_fireParams->proceduralRecoilParams.strength, m_fireParams->proceduralRecoilParams.kickIn,m_fireParams->proceduralRecoilParams.arms); } m_pWeapon->PlayAction(action, 0, false, CItem::eIPAF_Default); Vec3 pdir; int quad = cry_random(0, 3); CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId, string().Format("Invalid hit type '%s' in fire params for '%s'", m_fireParams->fireparams.hit_type.c_str(), m_pWeapon->GetEntity()->GetName())); CRY_ASSERT_MESSAGE(m_fireParams->fireparams.hitTypeId == g_pGame->GetGameRules()->GetHitTypeId(m_fireParams->fireparams.hit_type.c_str()), "Sanity Check Failed: Stored hit type id does not match the type string, possibly CacheResources wasn't called on this weapon type"); int ammoCost = m_fireParams->fireparams.fake_fire_rate ? m_fireParams->fireparams.fake_fire_rate : 1; ammoCost = min(ammoCost, ammoCount); // SHOT HERE for (int i = 0; i < m_fireParams->shotgunparams.pellets; i++) { CProjectile *pAmmo = m_pWeapon->SpawnAmmo(m_fireParams->fireparams.spawn_ammo_class, true); if (pAmmo) { pdir = ApplySpread(dir, m_fireParams->shotgunparams.spread, quad); quad = (quad+1)%4; CProjectile::SProjectileDesc projectileDesc( m_pWeapon->GetOwnerId(), m_pWeapon->GetHostId(), m_pWeapon->GetEntityId(), m_fireParams->shotgunparams.pelletdamage, m_fireParams->fireparams.damage_drop_min_distance, m_fireParams->fireparams.damage_drop_min_damage, m_fireParams->fireparams.damage_drop_per_meter, m_fireParams->fireparams.hitTypeId, m_fireParams->fireparams.bullet_pierceability_modifier, m_pWeapon->IsZoomed()); projectileDesc.pointBlankAmount = m_fireParams->fireparams.point_blank_amount; projectileDesc.pointBlankDistance = m_fireParams->fireparams.point_blank_distance; projectileDesc.pointBlankFalloffDistance = m_fireParams->fireparams.point_blank_falloff_distance; if (m_fireParams->fireparams.ignore_damage_falloff) projectileDesc.damageFallOffAmount = 0.0f; pAmmo->SetParams(projectileDesc); pAmmo->SetDestination(m_pWeapon->GetDestination()); pAmmo->SetRemote(true); pAmmo->Launch(pos, pdir, vel); bool emit = false; if(m_pWeapon->GetStats().fp) emit = (!m_fireParams->tracerparams.geometryFP.empty() || !m_fireParams->tracerparams.effectFP.empty()) && ((ammoCount == clipSize) || (ammoCount%m_fireParams->tracerparams.frequency==0)); else emit = (!m_fireParams->tracerparams.geometry.empty() || !m_fireParams->tracerparams.effect.empty()) && ((ammoCount == clipSize) || (ammoCount%m_fireParams->tracerparams.frequency==0)); if (emit) { EmitTracer(pos, hit, &m_fireParams->tracerparams, pAmmo); } m_projectileId = pAmmo->GetEntity()->GetId(); pAmmo->SetAmmoCost(ammoCost); } } if (m_pWeapon->IsServer()) { const char *ammoName = ammo != NULL ? ammo->GetName() : NULL; g_pGame->GetIGameFramework()->GetIGameplayRecorder()->Event(m_pWeapon->GetOwner(), GameplayEvent(eGE_WeaponShot, ammoName, m_fireParams->shotgunparams.pellets, (void *)(EXPAND_PTR)m_pWeapon->GetEntityId())); } m_muzzleEffect.Shoot(this, hit, m_barrelId); m_fired = true; m_next_shot = 0.0f; ammoCount -= ammoCost; if (m_pWeapon->IsServer()) { if (clipSize != -1) { if (clipSize != 0) m_pWeapon->SetAmmoCount(ammo, ammoCount); else m_pWeapon->SetInventoryAmmoCount(ammo, ammoCount); } } OnShoot(m_pWeapon->GetOwnerId(), 0, ammo, pos, dir, vel); m_pWeapon->RequireUpdate(eIUS_FireMode); }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorDetachPart::OnDamageEvent(EVehicleDamageBehaviorEvent event, const SVehicleDamageBehaviorEventParams& behaviorParams) { if (event == eVDBE_Repair) return; if (!m_detachedEntityId && behaviorParams.componentDamageRatio >= 1.0f) { CVehiclePartBase* pPart = (CVehiclePartBase*)m_pVehicle->GetPart(m_partName.c_str()); if (!pPart || !pPart->GetStatObj()) return; if (max(1.f-behaviorParams.randomness, pPart->GetDetachProbability()) < cry_random(0.0f, 1.0f)) return; IEntity* pDetachedEntity = SpawnDetachedEntity(); if (!pDetachedEntity) return; m_detachedEntityId = pDetachedEntity->GetId(); const Matrix34& partWorldTM = pPart->GetWorldTM(); pDetachedEntity->SetWorldTM(partWorldTM); MovePartToTheNewEntity(pDetachedEntity, pPart); SEntityPhysicalizeParams physicsParams; physicsParams.mass = pPart->GetMass(); physicsParams.type = PE_RIGID; physicsParams.nSlot = 0; pDetachedEntity->Physicalize(physicsParams); IPhysicalEntity* pPhysics = pDetachedEntity->GetPhysics(); if (pPhysics) { pe_params_part params; params.flagsOR = geom_collides|geom_floats; params.flagsColliderAND = ~geom_colltype3; params.flagsColliderOR = geom_colltype0; pPhysics->SetParams(¶ms); pe_action_add_constraint ac; ac.flags = constraint_inactive|constraint_ignore_buddy; ac.pBuddy = m_pVehicle->GetEntity()->GetPhysics(); ac.pt[0].Set(0,0,0); pPhysics->Action(&ac); // after 1s, remove the constraint again m_pVehicle->SetTimer(-1, 1000, this); // set the impulse const Vec3& velocity = m_pVehicle->GetStatus().vel; Vec3 baseForce = m_pVehicle->GetEntity()->GetWorldTM().TransformVector(pPart->GetDetachBaseForce()); baseForce *= cry_random(6.0f, 10.0f); pe_action_impulse actionImpulse; actionImpulse.impulse = physicsParams.mass * (velocity + baseForce); actionImpulse.angImpulse = physicsParams.mass * Vec3(cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f), cry_random(-1.0f,1.0f)); actionImpulse.iApplyTime = 1; pPhysics->Action(&actionImpulse); } // copy vehicle's material to new entity (fixes detaching parts from vehicles with different paints), // or specify the destroyed material if it exists IStatObj* pExternalStatObj = pPart->GetExternalGeometry(false); // Get undamaged external geometry (if any) IMaterial *pMaterial = pExternalStatObj ? pExternalStatObj->GetMaterial() : m_pVehicle->GetEntity()->GetMaterial(); if(event == eVDBE_VehicleDestroyed || event == eVDBE_Hit) { if (pExternalStatObj) { if (IStatObj* pStatObj = pPart->GetExternalGeometry(true)) // Try to get the destroyed, external geometry material pMaterial = pStatObj->GetMaterial(); } else if (m_pVehicle->GetDestroyedMaterial()) // If there is no external geometry, try the vehicle's destroyed material { pMaterial = m_pVehicle->GetDestroyedMaterial(); } } pDetachedEntity->SetMaterial(pMaterial); AttachParticleEffect(pDetachedEntity, m_pEffect); if (m_notifyMovement) { SVehicleMovementEventParams params; params.iValue = pPart->GetIndex(); m_pVehicle->GetMovement()->OnEvent(IVehicleMovement::eVME_PartDetached, params); } } }
void CBoidBug::UpdateFrogsBehavior( float dt,SBoidContext &bc ) { if (m_onGround) { if ((cry_random(0, 99) == 1) || ((Vec3(bc.playerPos.x-m_pos.x,bc.playerPos.y-m_pos.y,0).GetLengthSquared()) < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE)) { // Sacred by player or random jump. m_onGround = false; m_heading = m_pos - bc.playerPos; if (m_heading != Vec3(0,0,0)) m_heading = m_heading.GetNormalized(); else m_heading = Vec3(Boid::Frand(),Boid::Frand(),Boid::Frand()).GetNormalized(); m_heading.z = 0.2f + (Boid::Frand()+1.0f)*0.5f; m_heading += Vec3(Boid::Frand()*0.3f,Boid::Frand()*0.3f,0 ); if (m_heading != Vec3(0,0,0)) m_heading = m_heading.GetNormalized(); else m_heading = Vec3(Boid::Frand(),Boid::Frand(),Boid::Frand()).GetNormalized(); m_speed = bc.MaxSpeed; } } bc.terrainZ = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y); float range = bc.MaxAttractDistance; Vec3 origin = bc.flockPos; if (bc.followPlayer) { origin = bc.playerPos; } // Keep in range. if (bc.followPlayer) { if (m_pos.x < origin.x - range) m_pos.x = origin.x + range; if (m_pos.y < origin.y - range) m_pos.y = origin.y + range; if (m_pos.x > origin.x + range) m_pos.x = origin.x - range; if (m_pos.y > origin.y + range) m_pos.y = origin.y - range; } else { /* if (bc.behavior == EBUGS_BUG || bc.behavior == EBUGS_DRAGONFLY) { if (m_pos.x < origin.x-range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.y < origin.y-range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.x > origin.x+range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.y > origin.y+range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; } */ } m_accel.Set( 0,0,-10 ); bool bBanking = m_object != 0; CalcMovement( dt,bc,bBanking ); UpdateAnimationSpeed(bc); if (m_pos.z < bc.terrainZ+0.1f) { // Land. m_pos.z = bc.terrainZ+0.1f; m_onGround = true; m_speed = 0; } }
void CBoidBug::Update( float dt,SBoidContext &bc ) { if (bc.behavior == EBUGS_FROG) { UpdateFrogsBehavior( dt,bc ); return; } if (m_onGround) { if ((Vec3(bc.playerPos.x-m_pos.x,bc.playerPos.y-m_pos.y,0).GetLengthSquared()) < BUGS_SCARE_DISTANCE*BUGS_SCARE_DISTANCE) { // Sacred by player, fast takeoff. m_onGround = false; m_heading = (m_pos - bc.playerPos).GetNormalized(); m_heading.z = 0.3f; m_heading = (m_heading).GetNormalized(); m_speed = 1; } else if (cry_random(0, 49) == 0) { // take off. m_onGround = false; m_heading.z = 0.2f; m_heading = (m_heading).GetNormalized(); } return; } // Keep in range. bc.terrainZ = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y); float range = bc.MaxAttractDistance; Vec3 origin = bc.flockPos; if (bc.followPlayer) { origin = bc.playerPos; } if (bc.followPlayer) { if (m_pos.x < origin.x - range) m_pos.x = origin.x + range; if (m_pos.y < origin.y - range) m_pos.y = origin.y + range; if (m_pos.x > origin.x + range) m_pos.x = origin.x - range; if (m_pos.y > origin.y + range) m_pos.y = origin.y - range; } else { if (bc.behavior == EBUGS_BUG || bc.behavior == EBUGS_DRAGONFLY) { if (m_pos.x < origin.x-range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.y < origin.y-range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.x > origin.x+range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; if (m_pos.y > origin.y+range) m_accel = (origin - m_pos)*bc.factorAttractToOrigin; } } if (bc.behavior == EBUGS_BUG) { UpdateBugsBehavior( dt,bc ); } else if (bc.behavior == EBUGS_DRAGONFLY) { UpdateDragonflyBehavior( dt,bc ); } else if (bc.behavior == EBUGS_FROG) { UpdateFrogsBehavior( dt,bc ); } else { UpdateBugsBehavior( dt,bc ); } bool bBanking = m_object != 0; CalcMovement( dt,bc,bBanking ); UpdateAnimationSpeed(bc); if (m_pos.z < bc.terrainZ+0.1f) { // Land. m_pos.z = bc.terrainZ+0.1f; if (!bc.noLanding && cry_random(0, 9) == 0) { m_onGround = true; m_speed = 0; m_accel.Set(0,0,0); } } if (m_pos.z < bc.waterLevel) m_pos.z = bc.waterLevel; }
//------------------------------------------------------------------------ void CVehicleDamageBehaviorBlowTire::Activate(bool activate) { if (activate == m_isActive) return; if (activate && m_pVehicle->IsDestroyed()) return; IVehicleComponent* pComponent = m_pVehicle->GetComponent(m_component.c_str()); if (!pComponent) return; IVehiclePart* pPart = pComponent->GetPart(0); if (!pPart) return; // if IVehicleWheel available, execute full damage behavior. if null, only apply effects IVehicleWheel* pWheel = pPart->GetIWheel(); if (activate) { IEntity* pEntity = m_pVehicle->GetEntity(); IPhysicalEntity* pPhysics = pEntity->GetPhysics(); const Matrix34& wheelTM = pPart->GetLocalTM(false); const SVehicleStatus& status = m_pVehicle->GetStatus(); if (pWheel) { const pe_cargeomparams* pParams = pWheel->GetCarGeomParams(); // handle destroyed wheel pe_params_wheel wheelParams; wheelParams.iWheel = pWheel->GetWheelIndex(); wheelParams.minFriction = wheelParams.maxFriction = 0.5f * pParams->maxFriction; pPhysics->SetParams(&wheelParams); if (IVehicleMovement* pMovement = m_pVehicle->GetMovement()) { SVehicleMovementEventParams params; params.pComponent = pComponent; params.iValue = pWheel->GetWheelIndex(); pMovement->OnEvent(IVehicleMovement::eVME_TireBlown, params); } if (status.speed > 0.1f) { // add angular impulse pe_action_impulse angImp; float amount = m_pVehicle->GetMass() * status.speed * cry_random(0.25f, 0.45f) * -sgn(wheelTM.GetTranslation().x); angImp.angImpulse = pEntity->GetWorldTM().TransformVector(Vec3(0,0,amount)); pPhysics->Action(&angImp); } m_pVehicle->SetTimer(-1, AI_IMMOBILIZED_TIME*1000, this); } if (!gEnv->pSystem->IsSerializingFile()) { // add linear impulse pe_action_impulse imp; imp.point = pPart->GetWorldTM().GetTranslation(); float amount = m_pVehicle->GetMass() * cry_random(0.1f, 0.15f); if (pWheel) { amount *= max(0.5f, min(10.f, status.speed)); if (status.speed < 0.1f) amount = -0.5f*amount; } else amount *= 0.5f; imp.impulse = pEntity->GetWorldTM().TransformVector(Vec3(0,0,amount)); pPhysics->Action(&imp); // remove affected decals { Vec3 pos = pPart->GetWorldTM().GetTranslation(); AABB aabb = pPart->GetLocalBounds(); float radius = aabb.GetRadius(); Vec3 vRadius(radius,radius,radius); AABB areaBox(pos-vRadius, pos+vRadius); IRenderNode * pRenderNode = NULL; if (IComponentRender *pRenderProxy = (IComponentRender*)pEntity->GetComponent<IComponentRender>().get()) pRenderNode = pRenderProxy->GetRenderNode(); gEnv->p3DEngine->DeleteDecalsInRange(&areaBox, pRenderNode); } } } else { if (pWheel) { // restore wheel properties IPhysicalEntity* pPhysics = m_pVehicle->GetEntity()->GetPhysics(); pe_params_wheel wheelParams; for (int i=0; i<m_pVehicle->GetWheelCount(); ++i) { const pe_cargeomparams* pParams = m_pVehicle->GetWheelPart(i)->GetIWheel()->GetCarGeomParams(); wheelParams.iWheel = i; wheelParams.bBlocked = 0; wheelParams.suspLenMax = pParams->lenMax; wheelParams.bDriving = pParams->bDriving; wheelParams.minFriction = pParams->minFriction; wheelParams.maxFriction = pParams->maxFriction; pPhysics->SetParams(&wheelParams); } if (IVehicleMovement* pMovement = m_pVehicle->GetMovement()) { SVehicleMovementEventParams params; params.pComponent = pComponent; params.iValue = pWheel->GetWheelIndex(); // reset the particle status pMovement->OnEvent(IVehicleMovement::eVME_TireRestored, params); } } } m_isActive = activate; }
//------------------------------------------------------------------------ void CVehicleMovementStdBoat::UpdateSurfaceEffects(const float deltaTime) { FUNCTION_PROFILER( GetISystem(), PROFILE_GAME ); if (0 == g_pGameCVars->v_pa_surface) { ResetParticles(); return; } IEntity* pEntity = m_pVehicle->GetEntity(); const Matrix34& worldTM = pEntity->GetWorldTM(); float distSq = worldTM.GetTranslation().GetSquaredDistance(gEnv->pRenderer->GetCamera().GetPosition()); if (distSq > sqr(300.f) || (distSq > sqr(50.f) && !m_pVehicle->GetGameObject()->IsProbablyVisible())) return; Matrix34 worldTMInv = worldTM.GetInverted(); const SVehicleStatus& status = m_pVehicle->GetStatus(); float velDot = status.vel * worldTM.GetColumn1(); float powerNorm = min(abs(m_movementAction.power), 1.f); SEnvironmentParticles* envParams = m_pPaParams->GetEnvironmentParticles(); SEnvParticleStatus::TEnvEmitters::iterator end = m_paStats.envStats.emitters.end(); for (SEnvParticleStatus::TEnvEmitters::iterator emitterIt = m_paStats.envStats.emitters.begin(); emitterIt!=end; ++emitterIt) { if (emitterIt->layer < 0) { assert(0); continue; } const SEnvironmentLayer& layer = envParams->GetLayer(emitterIt->layer); SEntitySlotInfo info; info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); float countScale = 1.f; float sizeScale = 1.f; float speedScale = 1.f; float speed = 0.f; // check if helper position is beneath water level Vec3 emitterWorldPos = worldTM * emitterIt->quatT.t; float waterLevel = gEnv->p3DEngine->GetWaterLevel(&emitterWorldPos); int matId = 0; if (emitterWorldPos.z <= waterLevel+0.1f && m_physStatus[k_mainThread].submergedFraction<0.999f) { matId = gEnv->pPhysicalWorld->GetWaterMat(); speed = status.speed; bool spray = !strcmp(layer.GetName(), "spray"); if (spray) { // slip based speed -= abs(velDot); } GetParticleScale(layer, speed, powerNorm, countScale, sizeScale, speedScale); } else { countScale = 0.f; } if (matId && matId != emitterIt->matId) { // change effect IParticleEffect* pEff = 0; const char* effect = GetEffectByIndex( matId, layer.GetName() ); if (effect && (pEff = gEnv->pParticleManager->FindEffect(effect))) { #if ENABLE_VEHICLE_DEBUG if (DebugParticles()) CryLog("%s changes water sfx to %s (slot %i)", pEntity->GetName(), effect, emitterIt->slot); #endif if (info.pParticleEmitter) { info.pParticleEmitter->Activate(false); pEntity->FreeSlot(emitterIt->slot); } emitterIt->slot = pEntity->LoadParticleEmitter(emitterIt->slot, pEff); if (emitterIt->slot != -1) pEntity->SetSlotLocalTM(emitterIt->slot, Matrix34(emitterIt->quatT)); info.pParticleEmitter = 0; pEntity->GetSlotInfo(emitterIt->slot, info); } else countScale = 0.f; } if (matId) emitterIt->matId = matId; if (info.pParticleEmitter) { SpawnParams sp; sp.fSizeScale = sizeScale; sp.fCountScale = countScale; sp.fSpeedScale = speedScale; info.pParticleEmitter->SetSpawnParams(sp); if (layer.alignToWater && countScale > 0.f) { Vec3 worldPos(emitterWorldPos.x, emitterWorldPos.y, waterLevel+0.05f); Matrix34 localTM(emitterIt->quatT); localTM.SetTranslation(worldTMInv * worldPos); pEntity->SetSlotLocalTM(emitterIt->slot, localTM); } } #if ENABLE_VEHICLE_DEBUG if (DebugParticles() && m_pVehicle->IsPlayerDriving()) { float color[] = {1,1,1,1}; ColorB red(255,0,0,255); IRenderAuxGeom* pAuxGeom = gEnv->pRenderer->GetIRenderAuxGeom(); const char* effect = info.pParticleEmitter ? info.pParticleEmitter->GetName() : ""; const Matrix34& slotTM = m_pEntity->GetSlotWorldTM(emitterIt->slot); Vec3 ppos = slotTM.GetTranslation(); pAuxGeom->DrawSphere(ppos, 0.2f, red); pAuxGeom->DrawCone(ppos, slotTM.GetColumn1(), 0.1f, 0.5f, red); gEnv->pRenderer->Draw2dLabel(50.f, (float)(400+10*emitterIt->slot), 1.2f, color, false, "<%s> water fx: slot %i [%s], speed %.1f, sizeScale %.2f, countScale %.2f (pos %.0f,%0.f,%0.f)", pEntity->GetName(), emitterIt->slot, effect, speed, sizeScale, countScale, ppos.x, ppos.y, ppos.z); } #endif } // generate water splashes Vec3 wakePos; if(m_pSplashPos) { wakePos = m_pSplashPos->GetWorldSpaceTranslation(); } else { wakePos = worldTM.GetTranslation(); } float wakeWaterLevel = gEnv->p3DEngine->GetWaterLevel(&wakePos); const Vec3& localW = m_localSpeed; if (localW.x >= 0.f) m_diving = false; if (!m_diving && localW.x < -0.03f && status.speed > 10.f && wakePos.z < m_lastWakePos.z && wakeWaterLevel+0.1f >= wakePos.z) { float speedRatio = min(1.f, status.speed/(m_maxSpeed*m_factorMaxSpeed)); m_diving = true; if (m_pWaveEffect) { if (IParticleEmitter* pEmitter = pEntity->GetParticleEmitter(m_wakeSlot)) { pEmitter->Activate(false); pEntity->FreeSlot(m_wakeSlot); m_wakeSlot = -1; } SpawnParams spawnParams; spawnParams.fSizeScale = spawnParams.fCountScale = 0.5f + 0.25f*speedRatio; spawnParams.fSizeScale += 0.4f*m_waveRandomMult; spawnParams.fCountScale += cry_random(0.0f, 0.4f); m_wakeSlot = pEntity->LoadParticleEmitter(m_wakeSlot, m_pWaveEffect, &spawnParams); } // handle splash sound ExecuteTrigger(eSID_Splash); SetSoundParam(eSID_Splash, "intensity", 0.2f*speedRatio + 0.5f*m_waveRandomMult); if (m_rpmPitchDir == 0) { m_rpmPitchDir = -1; m_waveSoundPitch = 0.f; m_waveSoundAmount = 0.02f + m_waveRandomMult*0.08f; } } if (m_wakeSlot != -1) { // update emitter local pos to short above waterlevel Matrix34 tm; if(m_pSplashPos) m_pSplashPos->GetVehicleTM(tm); else tm.SetIdentity(); Vec3 pos = tm.GetTranslation(); pos.z = worldTMInv.TransformPoint(Vec3(wakePos.x,wakePos.y,wakeWaterLevel)).z + 0.2f; tm.SetTranslation(pos); pEntity->SetSlotLocalTM(m_wakeSlot, tm); #if ENABLE_VEHICLE_DEBUG if (IsProfilingMovement()) { Vec3 wPos = worldTM * tm.GetTranslation(); ColorB col(128, 128, 0, 200); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(wPos, 0.4f, col); gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(wPos, col, wPos+Vec3(0,0,1.5f), col); } #endif } m_lastWakePos = wakePos; }