void CBoidFish::Update( float dt,SBoidContext &bc ) { if (m_dead) return; if (m_physicsControlled) { if (m_pPhysics) { // If fish is dead, get it position from physics. pe_status_pos ppos; m_pPhysics->GetStatus(&ppos); m_pos = ppos.pos; { m_dyingTime += Boid::Frand()*0.2f; // Apply force on this body. pe_action_impulse theAction; theAction.impulse = Vec3(sinf(m_dyingTime*0.1f),cosf(m_dyingTime*0.13f),cosf(m_dyingTime*0.171f)*2.8f) * 0.01f; theAction.point = m_pos + Vec3(Boid::Frand(),Boid::Frand(),Boid::Frand())*0.1f; theAction.iApplyTime = 0; theAction.ipart = 0; m_pPhysics->Action(&theAction); pe_simulation_params sym; sym.density = 950.0f + 200.0f*sinf(m_dyingTime); if (sym.density < FISH_PHYSICS_DENSITY) sym.density = FISH_PHYSICS_DENSITY; m_pPhysics->SetParams( &sym ); } } } if (m_dying) { // If fish is dying it floats up to the water surface, and die there. //UpdateDying(dt,bc); m_dyingTime += dt; if (m_dyingTime > 60) { m_dead = true; m_dying = false; if (m_object) m_object->GetISkeletonAnim()->StopAnimationsAllLayers(); } return; } ////////////////////////////////////////////////////////////////////////// if (bc.followPlayer) { if (m_pos.GetSquaredDistance(bc.playerPos) > MAX_FISH_DISTANCE*MAX_FISH_DISTANCE) { float z = bc.MinHeight + (Boid::Frand()+1)/2.0f*(bc.MaxHeight - bc.MinHeight); m_pos = bc.playerPos + Vec3(Boid::Frand()*MAX_FISH_DISTANCE,Boid::Frand()*MAX_FISH_DISTANCE,z ); m_speed = bc.MinSpeed + ((Boid::Frand()+1)/2.0f) / (bc.MaxSpeed - bc.MinSpeed); m_heading = Vec3(Boid::Frand(),Boid::Frand(),0).GetNormalized(); } } float height = m_pos.z - bc.terrainZ; m_accel.Set(0,0,0); m_accel = bc.factorRandomAccel*Vec3(Boid::Frand(),Boid::Frand(),Boid::Frand()); // Continue accelerating in same dir until target speed reached. // Try to maintain average speed of (maxspeed+minspeed)/2 float targetSpeed = (bc.MaxSpeed + bc.MinSpeed)/2; m_accel -= m_heading*(m_speed-targetSpeed)*0.2f; if (bc.factorAlignment != 0) { Vec3 alignmentAccel; Vec3 cohesionAccel; Vec3 separationAccel; CalcFlockBehavior(bc,alignmentAccel,cohesionAccel,separationAccel); m_accel += alignmentAccel*bc.factorAlignment; m_accel += cohesionAccel*bc.factorCohesion; m_accel += separationAccel; } // Avoid water. if (m_pos.z > bc.waterLevel-1) { float h = bc.waterLevel - m_pos.z; float v = (1.0f - h); float vv = v*v; m_accel.z += (-vv)*bc.factorAvoidLand; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( m_pos,ColorB(0,0,255,255),m_pos+m_accel,ColorB(0,0,255,255) ); } // Avoid land. if (height < bc.MinHeight) { float v = (1.0f - height/(bc.MinHeight+0.01f)); float vv = v*v; m_accel.z += vv*bc.factorAvoidLand; // Slow down fast. m_accel -= m_heading*(m_speed-0.1f)*vv*bc.factorAvoidLand; // Go to origin. m_accel += (bc.flockPos - m_pos) * vv * bc.factorAvoidLand; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( m_pos,ColorB(255,0,0,255),Vec3(m_pos.x,m_pos.y,bc.terrainZ),ColorB(255,0,0,255) ); } if (fabs(m_heading.z) > 0.5f) { // Always try to accelerate in direction opposite to the current in Z axis. m_accel.z += -m_heading.z * 0.8f; } // Attract to the origin point. if (bc.followPlayer) { m_accel += (bc.playerPos - m_pos) * bc.factorAttractToOrigin; } else { m_accel += (bc.randomFlockCenter - m_pos) * bc.factorAttractToOrigin; } bool bAvoidObstacles = bc.avoidObstacles; ////////////////////////////////////////////////////////////////////////// // High Terrain avoidance. ////////////////////////////////////////////////////////////////////////// Vec3 fwd_pos = m_pos + m_heading*1.0f; // Look ahead 1 meter. float fwd_z = bc.engine->GetTerrainElevation(fwd_pos.x,fwd_pos.y); if (fwd_z >= m_pos.z-bc.fBoidRadius) { // If terrain in front of the fish is high, enable obstacle avoidance. bAvoidObstacles = true; } ////////////////////////////////////////////////////////////////////////// // Avoid collision with Terrain and Static objects. float fCollisionAvoidanceWeight = 10.0f; float fCollisionDistance = 2.0f; if (bAvoidObstacles) { // Avoid obstacles & terrain. IPhysicalWorld *physWorld = bc.physics; Vec3 vPos = m_pos; Vec3 vDir = m_heading*fCollisionDistance; // Add some random variation in probe ray. vDir.x += Boid::Frand()*0.8f; vDir.y += Boid::Frand()*0.8f; int objTypes = ent_all|ent_no_ondemand_activation; int flags = rwi_stop_at_pierceable|rwi_ignore_terrain_holes; ray_hit hit; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( vPos,ColorB(0,0,255,255),vPos+vDir,ColorB(0,0,255,255) ); int col = physWorld->RayWorldIntersection( vPos,vDir,objTypes,flags,&hit,1 ); if (col != 0 && hit.dist > 0) { // Turn from collided surface. Vec3 normal = hit.n; //normal.z = 0; // Only turn left/right. float w = (1.0f - hit.dist/fCollisionDistance); Vec3 R = m_heading - (2.0f*m_heading.Dot(normal))*normal; Boid::Normalize_fast(R); R += normal; R.z = R.z*0.2f; m_accel += R*(w*w)*bc.factorAvoidLand * fCollisionAvoidanceWeight; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( m_pos,ColorB(0,0,255,255),hit.pt,ColorB(0,0,255,255) ); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( hit.pt,ColorB(255,0,0,255),hit.pt+R*2,ColorB(255,0,0,255) ); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine( m_pos,ColorB(255,0,0,255),m_pos+R*2,ColorB(255,0,0,255) ); } } /* if (rand()%80 == 1) { if (m_pos.GetSquaredDistance(bc.playerPos) < 10.0f*10.0f) { // Spawn bubble. SpawnParticleEffect( m_pos,bc,SPAWN_BUBBLE ); } } */ ////////////////////////////////////////////////////////////////////////// // Player must scare fishes off. ////////////////////////////////////////////////////////////////////////// float sqrPlayerDist = m_pos.GetSquaredDistance(bc.playerPos); if (sqrPlayerDist < SCARE_DISTANCE*SCARE_DISTANCE) { Vec3 retreatDir = m_pos - bc.playerPos; Boid::Normalize_fast(retreatDir); float scareFactor = (1.0f - sqrPlayerDist/(SCARE_DISTANCE*SCARE_DISTANCE)); m_accel.x += retreatDir.x*scareFactor*bc.factorAvoidLand; m_accel.y += retreatDir.y*scareFactor*bc.factorAvoidLand; } ////////////////////////////////////////////////////////////////////////// // Calc movement. CalcMovement( dt,bc,false ); m_accel.Set(0,0,0); // Limits fishes to under water and above terrain. if (m_pos.z > bc.waterLevel-0.2f) { m_pos.z = bc.waterLevel-0.2f; if (rand()%40 == 1) { if (m_pos.GetSquaredDistance(bc.playerPos) < 10.0f*10.0f) { // Spawn splash. SpawnParticleEffect( m_pos,bc,SPAWN_SPLASH ); } } } else if (m_pos.z < bc.terrainZ+0.2f && bc.terrainZ < bc.waterLevel) { m_pos.z = bc.terrainZ+0.2f; } }
void CBoidBird::ThinkWalk( float dt,SBoidContext &bc ) { m_accel = -m_heading*(m_speed-m_walkSpeed)*0.4f; if (bc.factorAlignment != 0) { Vec3 alignmentAccel; Vec3 cohesionAccel; Vec3 separationAccel; CalcFlockBehavior(bc,alignmentAccel,cohesionAccel,separationAccel); m_accel += alignmentAccel*bc.factorAlignmentGround; m_accel += cohesionAccel*bc.factorCohesionGround; m_accel += separationAccel*bc.factorSeparationGround; } // Attract to origin point. if (bc.followPlayer) { m_accel += (bc.playerPos - m_pos) * bc.factorAttractToOriginGround; } else { if ((cry_rand()&31) == 1) { m_birdOriginPos = Vec3( bc.flockPos.x+Boid::Frand()*bc.fSpawnRadius,bc.flockPos.y+Boid::Frand()*bc.fSpawnRadius,bc.flockPos.z+Boid::Frand()*bc.fSpawnRadius ); if (m_birdOriginPos.z - bc.terrainZ < bc.MinHeight) { m_birdOriginPos.z = bc.terrainZ + bc.MinHeight; } } m_accel += (m_birdOriginPos - m_pos) * bc.factorAttractToOriginGround; } // Avoid collision with Terrain and Static objects. float fCollisionAvoidanceWeight = 10.0f; // Do walk sounds. // if ((cry_rand()&0xFF) == 0) // PlaySound(CHICKEN_SOUND_CLUCK); m_accel.z = 0; ////////////////////////////////////////////////////////////////////////// // Avoid water ocean.. if (m_pos.z < bc.waterLevel && bc.bAvoidWater) { Vec3 nextpos = m_pos + m_heading; float farz = bc.engine->GetTerrainElevation(nextpos.x,nextpos.y) + bc.fBoidRadius*0.5f; if (farz > m_pos.z) m_accel += m_heading*bc.factorAvoidLand; else m_accel += -m_heading*bc.factorAvoidLand; m_accel.z = 0; } ////////////////////////////////////////////////////////////////////////// }
void CChickenBoid::Think( float dt,SBoidContext &bc ) { Vec3 flockHeading(0,0,0); m_accel(0,0,0); // float height = m_pos.z - bc.terrainZ; if (m_bThrown) { m_accel.Set(0,0,-10.0f); //float z = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y) + bc.fBoidRadius*0.5f; m_pos.z = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y) + bc.fBoidRadius*0.5f; //pe_status_pos ppos; //m_pPhysics->GetStatus(&ppos); //if (m_pos.z < z) { m_physicsControlled = false; m_bThrown = false; m_heading.z = 0; if (m_heading.IsZero()) m_heading = Vec3(1,0,0); m_heading.Normalize(); m_accel.Set(0,0,0); m_speed = bc.MinSpeed; m_heading.z = 0; } return; } // Free will. // Continue accelerating in same dir untill target speed reached. // Try to maintain average speed of (maxspeed+minspeed)/2 float targetSpeed = bc.MinSpeed; m_accel -= m_heading*(m_speed-targetSpeed)*0.4f; // Gaussian weight. m_accel.z = 0; m_bScared = false; if (bc.factorAlignment != 0) { //CalcCohesion(); Vec3 alignmentAccel; Vec3 cohesionAccel; Vec3 separationAccel; CalcFlockBehavior(bc,alignmentAccel,cohesionAccel,separationAccel); //! Adjust for allignment, //m_accel += alignmentAccel.Normalized()*ALIGNMENT_FACTOR; m_accel += alignmentAccel*bc.factorAlignment; m_accel += cohesionAccel*bc.factorCohesion; m_accel += separationAccel; } /* // Avoid land. if (height < bc.MinHeight && !m_landing) { float v = (1.0f - height/bc.MinHeight); m_accel += Vec3(0,0,v*v)*bc.factorAvoidLand; } else if (height > bc.MaxHeight) // Avoid max height. { float v = (height - bc.MaxHeight)*0.1f; m_accel += Vec3(0,0,-v); } else { // Always try to accelerate in direction oposite to current in Z axis. m_accel.z = -(m_heading.z*m_heading.z*m_heading.z * 100.0f); } */ // Attract to origin point. if (bc.followPlayer) { m_accel += (bc.playerPos - m_pos) * bc.factorAttractToOrigin; } else { //m_accel += (m_birdOriginPos - m_pos) * bc.factorAttractToOrigin; if ((cry_rand()&31) == 1) { m_birdOriginPos = Vec3( bc.flockPos.x+frand()*bc.fSpawnRadius,bc.flockPos.y+frand()*bc.fSpawnRadius,bc.flockPos.z+frand()*bc.fSpawnRadius ); if (m_birdOriginPos.z - bc.terrainZ < bc.MinHeight) { m_birdOriginPos.z = bc.terrainZ + bc.MinHeight; } } /* if (m_pos.x < bc.flockPos.x-bc.fSpawnRadius || m_pos.x > bc.flockPos.x+bc.fSpawnRadius || m_pos.y < bc.flockPos.y-bc.fSpawnRadius || m_pos.y > bc.flockPos.y+bc.fSpawnRadius || m_pos.z < bc.flockPos.z-bc.fSpawnRadius || m_pos.z > bc.flockPos.z+bc.fSpawnRadius) */ { m_accel += (m_birdOriginPos - m_pos) * bc.factorAttractToOrigin; } } // Avoid collision with Terrain and Static objects. float fCollisionAvoidanceWeight = 10.0f; // Do walk sounds. if ((cry_rand()&0xFF) == 0) PlaySound(CHICKEN_SOUND_CLUCK); ////////////////////////////////////////////////////////////////////////// // Player must scare chickens off. ////////////////////////////////////////////////////////////////////////// float fScareDist = 5.0f; float sqrPlayerDist = m_pos.GetSquaredDistance(bc.playerPos); if (sqrPlayerDist < fScareDist*fScareDist) { Vec3 retreatDir = (m_pos - bc.playerPos) + Vec3(frand()*2.0f,frand()*2.0f,0); retreatDir.NormalizeFast(); float scareFactor = (1.0f - sqrPlayerDist/(fScareDist*fScareDist)); m_accel.x += retreatDir.x*scareFactor*bc.factorAvoidLand; m_accel.y += retreatDir.y*scareFactor*bc.factorAvoidLand; m_bScared = true; if (m_landing) m_flightTime = m_maxIdleTime+1.0f; // Stop idle. // Do walk sounds. if ((cry_rand()&0xFF) == 0) PlaySound(CHICKEN_SOUND_SCARED); } ////////////////////////////////////////////////////////////////////////// // Scare points also scare chicken off. ////////////////////////////////////////////////////////////////////////// if (bc.scareRatio > 0) { float sqrScareDist = m_pos.GetSquaredDistance(bc.scarePoint); if (sqrScareDist < bc.scareRadius*bc.scareRadius) { float fScareMultiplier = 10.0f; Vec3 retreatDir = m_pos - bc.scarePoint; retreatDir.NormalizeFast(); float scareFactor = (1.0f - sqrScareDist/(bc.scareRadius*bc.scareRadius)); m_accel.x += retreatDir.x*scareFactor*fScareMultiplier; m_accel.y += retreatDir.y*scareFactor*fScareMultiplier; if (m_landing) m_flightTime = m_maxIdleTime+1.0f; // Stop idle. m_bScared = true; // Do walk sounds. if ((cry_rand()&0xF) == 0) PlaySound(CHICKEN_SOUND_CLUCK); } } ////////////////////////////////////////////////////////////////////////// if (bc.avoidObstacles) { // Avoid obstacles & terrain. IPhysicalWorld *physWorld = bc.physics; Vec3 vDir0 = m_heading*bc.fBoidRadius*0.5f; Vec3 vPos = m_pos + Vec3(0,0,bc.fBoidRadius*0.5f) + vDir0; Vec3 vDir = m_heading*(bc.fBoidRadius*2) + Vec3(0,0,bc.fBoidRadius*0.5f); // Add some random variation in probe ray. vDir.x += frand()*0.5f; vDir.y += frand()*0.5f; int objTypes = ent_all|ent_no_ondemand_activation; int flags = rwi_stop_at_pierceable|rwi_ignore_terrain_holes; ray_hit hit; int col = physWorld->RayWorldIntersection( vPos,vDir,objTypes,flags,&hit,1 ); if (col != 0 && hit.dist > 0) { // Turn from collided surface. Vec3 normal = hit.n; float rayLen = vDir.GetLength(); float w = (1.0f - hit.dist/rayLen); Vec3 R = m_heading - (2.0f*m_heading.Dot(normal))*normal; R.NormalizeFast(); R += normal; //m_accel += R*(w*w)*bc.factorAvoidLand * fCollisionAvoidanceWeight; Vec3 accel = R*w*bc.factorAvoidLand * fCollisionAvoidanceWeight; m_avoidanceAccel = m_avoidanceAccel*bc.fSmoothFactor + accel*(1.0f-bc.fSmoothFactor); } } m_accel += m_avoidanceAccel; m_avoidanceAccel = m_avoidanceAccel*bc.fSmoothFactor; if (!m_landing) { m_flightTime += dt; if (m_flightTime > m_maxNonIdleTime && (m_pos.z > bc.waterLevel && bc.bAvoidWater)) { // Play idle. PlayAnimationId( CHICKEN_IDLE_ANIM + (cry_rand()%CHICKEN_IDLE_ANIM_NUM),true ); m_maxIdleTime = 2.0f + cry_frand()*MAX_REST_TIME; m_landing = true; m_flightTime = 0; m_accel.Set(0,0,0); m_speed = 0; } } else { m_accel = m_heading; m_speed = 0.1f; m_flightTime += dt; if (m_flightTime > m_maxIdleTime) { m_maxNonIdleTime = cry_frand()*MAX_WALK_TIME; PlayAnimationId( CHICKEN_WALK_ANIM,true ); m_landing = false; m_flightTime = 0; } } // Limits birds to above water and land. m_pos.z = bc.engine->GetTerrainElevation(m_pos.x,m_pos.y) + bc.fBoidRadius*0.5f; m_accel.z = 0; ////////////////////////////////////////////////////////////////////////// // Avoid water ocean.. if (m_pos.z < bc.waterLevel && bc.bAvoidWater) { if (m_landing) m_flightTime = m_maxIdleTime; Vec3 nextpos = m_pos + m_heading; float farz = bc.engine->GetTerrainElevation(nextpos.x,nextpos.y) + bc.fBoidRadius*0.5f; if (farz > m_pos.z) m_accel += m_heading*bc.factorAvoidLand; else m_accel += -m_heading*bc.factorAvoidLand; m_accel.z = 0; } ////////////////////////////////////////////////////////////////////////// }
void CBoidBird::Think( float dt,SBoidContext &bc ) { m_accel.zero(); float factorAlignment = bc.factorAlignment; float factorCohesion = bc.factorCohesion; float factorSeparation = bc.factorSeparation; float factorAvoidLand = bc.factorAvoidLand; float factorAttractToOrigin = bc.factorAttractToOrigin; float factorKeepHeight = bc.factorKeepHeight; if(m_status == Bird::LANDING) { //factorAlignment /= 3.f; factorCohesion *=10.f; factorSeparation = 0; factorAvoidLand = 0; factorAttractToOrigin *=10; factorKeepHeight = 0; } if (m_spawnFromPt) { // Set the acceleration of the boid float fHalfDesired = m_desiredHeigh * .5f; if (m_pos.z < fHalfDesired) { m_accel.z = 3.f; } else { m_accel.z = .5f; // Set z-acceleration float fRange = 1.f - (m_pos.z-fHalfDesired)/fHalfDesired; // 1..0 range if (m_heading.z > 0.2f) m_accel.z = .5f + fRange; // 2..1 m_accel.x += m_heading.x * .1f; m_accel.y += m_heading.y * .1f; } if (m_pos.z > m_desiredHeigh) SetSpawnFromPt(false); // Limits birds to above water and land. if (m_pos.z < bc.terrainZ-0.2f) { m_pos.z = bc.terrainZ-0.2f; } if (m_pos.z < bc.waterLevel-0.2f) { m_pos.z = bc.waterLevel-0.2f; } return; } float height = m_pos.z - bc.flockPos.z; // Free will. // Continue accelerating in same dir until target speed reached. // Try to maintain average speed of (maxspeed+minspeed)/2 float targetSpeed = (bc.MaxSpeed + bc.MinSpeed)/2; m_accel -= m_heading*(m_speed-targetSpeed)*0.5f; // Desired height. float dh = m_desiredHeigh - m_pos.z; float dhexp = -(dh*dh)/(3*3); dhexp = clamp_tpl(dhexp,-70.0f,70.0f); // Max values for expf // Gaussian weight. float fKeepHeight = factorKeepHeight - m_fNoKeepHeight; m_fNoKeepHeight = max(0.f, m_fNoKeepHeight - .01f*dt); m_accel.z = exp_tpl(dhexp) * fKeepHeight; if (factorAlignment != 0) { //CalcCohesion(); Vec3 alignmentAccel; Vec3 cohesionAccel; Vec3 separationAccel; CalcFlockBehavior(bc,alignmentAccel,cohesionAccel,separationAccel); //! Adjust for allignment, //m_accel += alignmentAccel.Normalized()*ALIGNMENT_FACTOR; m_accel += alignmentAccel*factorAlignment; m_accel += cohesionAccel*factorCohesion; m_accel += separationAccel*factorSeparation; } // Avoid land. if (height < bc.MinHeight && m_status != Bird::LANDING) { float v = (1.0f - height/bc.MinHeight); m_accel += Vec3(0,0,v*v)*bc.factorAvoidLand; } else if (height > bc.MaxHeight) { // Avoid max height. float v = (height - bc.MaxHeight)*0.1f; m_accel += Vec3(0,0,-v); } else if(m_status != Bird::TAKEOFF) { // Always try to accelerate in direction opposite to current in Z axis. m_accel.z = -(m_heading.z*m_heading.z*m_heading.z * 100.0f); } // Attract to origin point. float fAttractToOrigin = factorAttractToOrigin - m_fNoCenterAttract; m_fNoCenterAttract = max(0.f, m_fNoCenterAttract - .01f*dt); if (bc.followPlayer) { m_accel += (bc.playerPos - m_pos) * fAttractToOrigin; } else { if ((rand()&31) == 1) { m_birdOriginPos = Vec3( bc.flockPos.x+Boid::Frand()*bc.fSpawnRadius,bc.flockPos.y+Boid::Frand()*bc.fSpawnRadius,bc.flockPos.z+Boid::Frand()*bc.fSpawnRadius ); if (m_birdOriginPos.z - bc.terrainZ < bc.MinHeight) { m_birdOriginPos.z = bc.terrainZ + bc.MinHeight; } } m_accel += (m_birdOriginPos - m_pos) * fAttractToOrigin; } // Attract to bc.attractionPoint if (m_pos.GetSquaredDistance2D(bc.attractionPoint) < 5.f) { SetAttracted(false); CBirdsFlock *pFlock = (CBirdsFlock *)m_flock; if (!pFlock->GetAttractOutput()) { // Activate the AttractTo flownode output pFlock->SetAttractOutput(true); // Activate the flow node output IEntity *pFlockEntity = pFlock->GetEntity(); if (pFlockEntity) { IScriptTable *scriptObject = pFlockEntity->GetScriptTable(); if (scriptObject) Script::CallMethod(scriptObject, "OnAttractPointReached"); } } } if (m_attractedToPt) { if(m_status == Bird::ON_GROUND) TakeOff(bc); else SetStatus(Bird::FLYING); m_accel += (bc.attractionPoint - m_pos) * m_fAttractionFactor; if (m_fAttractionFactor < 300.f * factorAttractToOrigin) m_fAttractionFactor += 3.f*dt; } // Avoid collision with Terrain and Static objects. float fCollisionAvoidanceWeight = 1.0f; m_alignHorizontally = 0; if(m_status == Bird::TAKEOFF) { if(m_accel.z < 0) m_accel.z = -m_accel.z; m_accel.z *= bc.factorTakeOff; } if (bc.avoidObstacles) { // Avoid obstacles & terrain. float fCollDistance = m_collisionInfo.CheckDistance() ; if(m_collisionInfo.IsColliding()) { float w = (1.0f - m_collisionInfo.Distance()/fCollDistance); m_accel += m_collisionInfo.Normal() * w*w * bc.factorAvoidLand * fCollisionAvoidanceWeight; } } // Limits birds to above water and land. if (m_pos.z < bc.terrainZ-0.2f) { m_pos.z = bc.terrainZ-0.2f; } if (m_pos.z < bc.waterLevel-0.2f) { m_pos.z = bc.waterLevel-0.2f; } }