Files
FC1/CryGame/Flock.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1679 lines
42 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
// File name: flock.cpp
// Version: v1.00
// Created: 5/4/2002 by Timur.
// Compilers: Visual C++ 7.0
// Description:
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "Flock.h"
#include "BugsFlock.h"
#include "WeaponClass.h"
#include <float.h>
#include <limits.h>
#include <IScriptSystem.h>
#include <ScriptObjectVector.h>
#include <CryCharAnimationParams.h>
#define BIRDS_PHYSICS_DENSITY 200
#define BIRDS_PHYSICS_INWATER_DENSITY 800
#define FISH_PHYSICS_DENSITY 800
#define MAX_BIRDS_DISTANCE 300
#define MAX_SPEED 15
#define MIN_SPEED 2.5f
#define MAX_ATTRACT_DISTANCE 20
#define MIN_ATTRACT_DISTANCE 5
#define MAX_FLIGHT_HEIGHT 40
#define MIN_FLIGHT_HEIGHT 5
#define MAX_REST_TIME 5
//#define LANDING_SPEED 1.0f
#define LANDING_FORCE 2.0f
#define MAX_FLIGHT_TIME 30
#define MIN_FLIGHT_TIME 10
#define SCARE_DISTANCE 10
#define ALIGNMENT_FACTOR 1.0f
#define COHESION_FACTOR 1.0f
#define SEPARATION_FACTOR 10.0f
#define ORIGIN_ATTRACT_FACTOR 0.1f
#define DESIRED_HEIGHT_FACTOR 0.4f
#define AVOID_LAND_FACTOR 10.0f
#define MAX_ANIMATION_SPEED 1.7f
//! Return random value in [-1,1] range.
inline float frand()
{
return ((float)rand()*2.0f / RAND_MAX) - 1.0f;
}
struct MathUtil
{
//! the Gaussian a.k.a the "bell curve", is a good function to
//! model fields of influence. You get a nice round peak and falls off
//! to zero with distance.
//! As space_metric is basically how wide the standard deviation of the the bell curve gets.
//! space_metric_r = 1/(space_metric * space_metric)
static float CalcGaussianWeight( const Vec3 &v1,const Vec3 &v2, float space_metric_r)
{
Vec3 d = v2 - v1;
return cry_expf(-(d.x*d.x*space_metric_r))*cry_expf(-(d.y*d.y*space_metric_r))*cry_expf(-(d.z*d.z*space_metric_r));
}
//////////////////////////////////////////////////////////////////////////
static Matrix44 Vector2Matrix( const Vec3 &dir,const Vec3 &up,float rollAngle=0 )
{
Matrix44 M;
// LookAt transform.
Vec3 xAxis,yAxis,zAxis;
Vec3 upVector = -up;
yAxis = GetNormalized(dir);
//if (zAxis.x == 0.0 && zAxis.z == 0) up.Set( -zAxis.y,0,0 ); else up.Set( 0,1.0f,0 );
xAxis = GetNormalized((upVector.Cross(yAxis)));
zAxis = GetNormalized(xAxis.Cross(yAxis));
// OpenGL kind of matrix.
M[0][0] = xAxis.x;
M[1][0] = yAxis.x;
M[2][0] = zAxis.x;
M[3][0] = 0;
M[0][1] = xAxis.y;
M[1][1] = yAxis.y;
M[2][1] = zAxis.y;
M[3][1] = 0;
M[0][2] = xAxis.z;
M[1][2] = yAxis.z;
M[2][2] = zAxis.z;
M[3][2] = 0;
M[0][3] = 0;
M[1][3] = 0;
M[2][3] = 0;
M[3][3] = 1;
if (rollAngle != 0)
{
Matrix44 RollMtx;
RollMtx.SetIdentity();
float s = cry_sinf(rollAngle);
float c = cry_cosf(rollAngle);
RollMtx[0][0] = c; RollMtx[2][0] = -s;;
RollMtx[0][2] = s; RollMtx[2][2] = c;
// Matrix multiply.
M = RollMtx * M;
}
return M;
}
};
//////////////////////////////////////////////////////////////////////////
CBoidObject::CBoidObject( SBoidContext &bc )
{
m_flock = 0;
m_pPhysics = NULL;
m_heading(1,0,0);
m_accel(0,0,0);
m_speed = 0;
m_object = 0;
m_banking = 0;
m_alignHorizontally = 0;
// flags
m_dead = false;
m_dying = false;
m_physicsControlled = false;
m_inwater = false;
m_nodraw = false;
m_speed = bc.MinSpeed + (frand()+1)/2.0f*(bc.MaxSpeed - bc.MinSpeed);
m_heading = GetNormalized( Vec3(frand(),frand(),0) );
}
//////////////////////////////////////////////////////////////////////////
CBoidObject::~CBoidObject()
{
if (m_pPhysics)
m_pPhysics->GetWorld()->DestroyPhysicalEntity(m_pPhysics);
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::CalcMatrix( Matrix44 &mtx )
{
if (m_physicsControlled && m_pPhysics)
{
pe_status_pos ppos;
mtx.SetIdentity();
ppos.pMtx4x4T = mtx.data;
m_pPhysics->GetStatus(&ppos);
/*
mtx = GetTransposed44( Matrix44(ppos.q) );
mtx.SetTranslationOLD( ppos.pos );
*/
return;
}
Vec3 up = Vec3(0,0,1);
Vec3 dir = -m_heading;
dir.z *= (1.0f-m_alignHorizontally);
mtx = MathUtil::Vector2Matrix( dir,up,m_banking*0.5f );
mtx.SetTranslationOLD( m_pos );
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::Render( SRendParams &rp,CCamera &cam,SBoidContext &bc )
{
if (m_nodraw)
return;
if (m_object)
{
// Cull boid.
if (!cam.IsSphereVisibleFast( Sphere(m_pos,bc.fBoidRadius*bc.boidScale) ))
return;
Matrix44 mtx;
CalcMatrix( mtx );
Matrix44 ms;
ms.SetIdentity();
ms=Matrix33::CreateScale( Vec3(bc.boidScale,bc.boidScale,bc.boidScale) )*ms;
mtx = ms * mtx;
//m_object->Update();
rp.pMatrix = &mtx;
m_object->Update();
m_object->Draw( rp, mtx.GetTranslationOLD() );
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::CalcMovement( float dt,SBoidContext &bc,bool banking )
{
// Calc movement with current velocity.
if (m_speed > bc.MaxSpeed)
m_speed = bc.MaxSpeed;
if (m_speed < bc.MinSpeed)
m_speed = bc.MinSpeed;
Vec3 prevAccel;
if (banking)
{
if (m_accel.x != 0 && m_accel.y != 0 && m_accel.z != 0)
prevAccel = GetNormalized(m_accel);
else
banking = false;
}
Vec3 velocity = m_heading*m_speed;
m_pos = m_pos + velocity*dt;
velocity = velocity + m_accel*dt;
m_speed = velocity.Length();
if (fabs(m_speed) > 0.0001f)
m_heading = velocity * (1.0f/m_speed); // Normilized velocity vector is our heading.
if (banking)
{
Vec3 sideDir = m_heading.Cross(Vec3(0,0,1));
m_banking = prevAccel.Dot(GetNormalized(sideDir));
}
if (m_object)
{
float animSpeed = m_speed / (bc.MaxSpeed - bc.MinSpeed + 0.1f) * bc.MaxAnimationSpeed;
m_object->SetAnimationSpeed( animSpeed );
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::CalcFlockBehavior( SBoidContext &bc,Vec3 &vAlignment,Vec3 &vCohesion,Vec3 &vSeparation )
{
// Vector of sight between boids.
Vec3 sight;
float maxAttractDist2 = bc.MaxAttractDistance*bc.MaxAttractDistance;
vSeparation(0,0,0);
vAlignment(0,0,0);
vCohesion(0,0,0);
Vec3 v;
// Avarage alignment and speed.
Vec3 avgAlignment(0,0,0);
Vec3 avgNeighborsCenter(0,0,0);
int numMates = 0;
int numBoids = m_flock->GetBoidsCount();
for (int i = 0; i < numBoids; i++)
{
CBoidObject *boid = m_flock->GetBoid(i);
if (boid == this) // skip myself.
continue;
// Check if this boid is in our range of sight.
float dist2 = GetLengthSquared((boid->m_pos - m_pos));
if (dist2 > maxAttractDist2)
continue;
// If this neighbour is in our field of view.
// Calc distance between two boids.
v = boid->m_pos - m_pos;
float distance = v.Length();
// Normilize direction vector between boids.
Vec3 sight = v * (1.0f/distance);
if (m_heading.Dot(sight) < bc.cosFovAngle)
continue;
numMates++;
// Alignment with boid direction.
avgAlignment += boid->m_heading * boid->m_speed;
// Calculate avarage center of all neightbour boids.
avgNeighborsCenter += boid->m_pos;
// Distraction from other boids.
if (distance < bc.MinAttractDistance)
{
// Boid too close, distract from him.
float w = (1.0f - distance/bc.MinAttractDistance);
float weight = w*w;
vSeparation -= sight*weight * bc.factorSeparation;
}
/*
else
{
// Attracted to boid.
float w = (distance-bc.MinAttractDistance)/(bc.MaxAttractDistance-bc.MinAttractDistance);
separationAccel = attractWeight*w*w;
}
*/
}
if (numMates > 0)
{
avgAlignment = avgAlignment * (1.0f/numMates);
//float avgSpeed = avgAlignment.Length();
vAlignment = avgAlignment;
// Attraction to mates.
avgNeighborsCenter = avgNeighborsCenter * (1.0f/numMates);
Vec3 cohesionDir = avgNeighborsCenter - m_pos;
float distance = cohesionDir.Length();
cohesionDir = cohesionDir * (1.0f/distance);
float w = (distance - bc.MinAttractDistance)/(bc.MaxAttractDistance - bc.MinAttractDistance);
vCohesion = cohesionDir*w*w;
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::Physicalize( SBoidContext &bc )
{
pe_params_particle ppart;
ppart.gravity = vectorf(0,0,0);
ppart.flags = particle_traceable | particle_no_roll | pef_never_affect_triggers;
ppart.mass = bc.fBoidMass;
ppart.size = bc.fBoidRadius;
ppart.thickness = 0.1f;
m_pPhysics = bc.physics->CreatePhysicalEntity(PE_PARTICLE,&ppart,this,OT_BOID);
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::CreateRigidBox( SBoidContext &bc,const Vec3 &boxSize,float density )
{
if (m_pPhysics)
{
bc.physics->DestroyPhysicalEntity(m_pPhysics);
m_pPhysics = 0;
}
Matrix44 mtx;
CalcMatrix( mtx );
pe_params_pos bodypos;
bodypos.pos = m_pos;
bodypos.q = Quat( GetTransposed44(mtx) );
m_pPhysics = bc.physics->CreatePhysicalEntity(PE_RIGID,&bodypos,this,OT_BOID);
pe_params_flags pf;
pf.flagsOR = pef_never_affect_triggers;
m_pPhysics->SetParams(&pf);
primitives::box geomBox;
geomBox.Basis.SetIdentity();
geomBox.center.Set(0,0,0);
geomBox.size = boxSize;
geomBox.bOriented = 0;
IGeometry *pGeom = bc.physics->GetGeomManager()->CreatePrimitive( primitives::box::type,&geomBox );
phys_geometry *physGeom = bc.physics->GetGeomManager()->RegisterGeometry( pGeom );
pe_geomparams partpos;
partpos.pos.Set(0,0,0);
partpos.density = density; // some fish density.
partpos.surface_idx = 255; // default.
m_pPhysics->AddGeometry( physGeom,&partpos,0 );
bc.physics->GetGeomManager()->UnregisterGeometry( physGeom );
pe_simulation_params symparams;
symparams.damping = 0.3f;
symparams.dampingFreefall = 0.3f;
m_pPhysics->SetParams(&symparams);
pe_params_buoyancy pb;
pb.waterDensity = 1000.0f;
pb.waterDamping = 1;
pb.waterResistance = 1000;
pb.waterPlane.n.Set(0,0,1);
//pb.waterPlane.origin.set(0,0,m_pISystem->GetI3DEngine()->GetWaterLevel(&m_center));
pb.waterPlane.origin.Set( 0,0,bc.waterLevel );
m_pPhysics->SetParams(&pb);
}
//////////////////////////////////////////////////////////////////////////
void CBoidObject::CreateArticulatedCharacter( SBoidContext &bc,const Vec3 &size,float density )
{
if (m_pPhysics)
{
bc.physics->DestroyPhysicalEntity(m_pPhysics);
m_pPhysics = 0;
}
Matrix44 mtx;
CalcMatrix( mtx );
pe_params_pos bodypos;
bodypos.pos = m_pos;
bodypos.q = Quat( GetTransposed44(mtx) );
bodypos.iSimClass = 2;
m_pPhysics = bc.physics->CreatePhysicalEntity(PE_ARTICULATED,&bodypos,this,OT_BOID);
//m_pPhysics = m_object->RelinquishCharacterPhysics();
pe_params_flags pf;
pf.flagsOR = pef_never_affect_triggers;
m_pPhysics->SetParams(&pf);
pe_params_articulated_body pab;
pab.bGrounded = 0;
pab.bCheckCollisions = 1;
pab.bCollisionResp = 1;
m_pPhysics->SetParams(&pab);
m_object->BuildPhysicalEntity( m_pPhysics,bc.fBoidMass,255,1,0 );
pe_simulation_params symparams;
symparams.damping = 0.3f;
symparams.dampingFreefall = 0.3f;
m_pPhysics->SetParams(&symparams);
pe_params_buoyancy pb;
pb.waterDensity = 1000.0f;
pb.waterDamping = 1;
pb.waterResistance = 1000;
pb.waterPlane.n.Set(0,0,1);
//pb.waterPlane.origin.set(0,0,m_pISystem->GetI3DEngine()->GetWaterLevel(&m_center));
pb.waterPlane.origin.Set( 0,0,bc.waterLevel );
m_pPhysics->SetParams(&pb);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CBoidBird::CBoidBird( SBoidContext &bc )
: CBoidObject( bc )
{
m_flightTime = 0;
m_lastThinkTime = 0;
m_maxFlightTime = 0;
m_landing = false;
m_takingoff = true;
m_onGround = false;
m_maxFlightTime = MIN_FLIGHT_TIME + (frand()+1)/2*(MAX_FLIGHT_TIME-MIN_FLIGHT_TIME);
m_desiredHeigh = bc.MaxHeight + (frand()+1)/2*(bc.MaxHeight - bc.MinHeight);
m_birdOriginPos = bc.flockPos;
}
CBoidBird::~CBoidBird()
{
}
void CBoidBird::OnFlockMove( SBoidContext &bc )
{
m_birdOriginPos = bc.flockPos;
}
//////////////////////////////////////////////////////////////////////////
void CBoidBird::Update( float dt,SBoidContext &bc )
{
if (m_physicsControlled)
{
if (m_pPhysics)
{
pe_status_pos ppos;
m_pPhysics->GetStatus(&ppos);
m_pos = ppos.pos;
Vec3 pos = m_pos;
// When hitting water surface, increase physics desnity.
if (!m_inwater && m_pos.z+bc.fBoidRadius <= bc.engine->GetWaterLevel( &pos ))
{
m_inwater = true;
pe_simulation_params sym;
sym.density = BIRDS_PHYSICS_INWATER_DENSITY;
m_pPhysics->SetParams( &sym );
}
bool bAwake = m_pPhysics->GetStatus(&pe_status_awake()) != 0;
if (bAwake && m_pPhysics->GetType() == PE_ARTICULATED)
{
m_object->SynchronizeWithPhysicalEntity(m_pPhysics);
}
if (!m_inwater && !bAwake)
//if (m_pPhysics->GetStatus(&pe_status_awake()))
{
// Resting. disable physics.
//m_physicsControlled = false;
//bc.physics->DestroyPhysicalEntity( m_pPhysics );
//m_pPhysics = 0;
m_dead = true;
}
}
return;
}
if (m_dead)
return;
m_lastThinkTime += dt;
if (bc.waterLevel > bc.terrainZ)
bc.terrainZ = bc.waterLevel;
//if (m_lastThinkTime)
{
// /*
if (bc.followPlayer)
{
if (GetSquaredDistance(m_pos,bc.playerPos) > MAX_BIRDS_DISTANCE*MAX_BIRDS_DISTANCE)
{
float z = bc.MinHeight + (frand()+1)/2.0f*(bc.MaxHeight - bc.MinHeight);
m_pos = bc.playerPos + Vec3(frand()*MAX_BIRDS_DISTANCE,frand()*MAX_BIRDS_DISTANCE,z );
m_speed = bc.MinSpeed + ((frand()+1)/2.0f) / (bc.MaxSpeed - bc.MinSpeed);
m_heading = GetNormalized(Vec3(frand(),frand(),0));
}
}
// */
if (!m_onGround)
{
Think(bc);
// Calc movement with current velocity.
CalcMovement( dt,bc,true );
m_accel.Set(0,0,0);
}
// Check if landed.
if ((m_landing || m_dying) && !m_onGround)
{
float LandEpsilon = 0.5f;
if (m_pos.z - bc.terrainZ < LandEpsilon)
{
m_onGround = true;
m_landing = false;
m_flightTime = 0;
if (m_object)
m_object->StartAnimation( "landing", CryCharAnimationParams() );
// Check if landed on water.
if (m_pos.z-bc.waterLevel < LandEpsilon+0.1f && !m_dying)
{
//! From water immidiatly take off.
//! Gives fishing effect.
TakeOff(bc);
}
}
}
if (m_takingoff)
{
if (m_pos.z - bc.terrainZ > bc.MinHeight)
{
m_takingoff = false;
}
}
if (!bc.noLanding)
{
if (!m_onGround)
{
m_flightTime += dt;
if (m_flightTime > m_maxFlightTime /*&& (m_pos.z - bc.terrainZ) < bc.MinHeight*2*/)
{
// Wants to land.
if (!m_landing)
{
// Only now wants to land.
//m_object->StartAnimation( "takeoff" );
}
m_landing = true;
}
}
else
{
m_flightTime += dt;
if (m_flightTime > MAX_REST_TIME || GetDistance(m_pos,bc.playerPos) < SCARE_DISTANCE)
{
// Take-off.
TakeOff(bc);
}
}
}
}
if (m_pPhysics)
{
pe_params_pos ppos;
ppos.pos = vectorf(m_pos);
m_pPhysics->SetParams(&ppos);
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidBird::TakeOff( SBoidContext &bc )
{
// Take-off.
m_flightTime = 0;
m_landing = false;
m_onGround = false;
m_maxFlightTime = MIN_FLIGHT_TIME + (frand()+1)/2*(MAX_FLIGHT_TIME-MIN_FLIGHT_TIME);
//m_desiredHeigh = bc.MinHeight + (frand()+1)/2*(MAX_FLIGHT_HEIGHT - bc.MinHeight);
m_desiredHeigh = bc.flockPos.z;
m_takingoff = true;
m_heading.z = 0;
if (m_object)
m_object->StartAnimation( "fly_loop" , CryCharAnimationParams());
}
//////////////////////////////////////////////////////////////////////////
void CBoidBird::Think( SBoidContext &bc )
{
Vec3 flockHeading(0,0,0);
float flockSpeed = 0;
m_accel(0,0,0);
float height = m_pos.z - bc.terrainZ;
// Free will.
// Continue accelerating in same dir untill target speed reached.
// Try to maintain avarage 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;
// Gaussian weight.
m_accel.z = cry_expf(-(dh*dh)/(3*3)) * bc.factorKeepHeight;
//m_accel.z = dh * DESIRED_HEIGHT_FACTOR;
//exp(-(d.x*d.x*space_metric_r))
//m_accel.z = (m_desiredHeigh+bc.terrainZ - m_pos.z) * DESIRED_HEIGHT_FACTOR;
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
// Avoid max height.
if (height > bc.MaxHeight)
{
float v = (height - bc.MaxHeight)*0.1f;
m_accel += Vec3(0,0,-v);
}
else
{
// Allways try to accelerate in direction oposite to current in Z axis.
m_accel.z = -m_heading.z * 2.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 (rand()%80 == 1)
{
m_birdOriginPos = Vec3(bc.flockPos.x+frand()*bc.MaxAttractDistance,bc.flockPos.y+frand()*bc.MaxAttractDistance,bc.flockPos.z );
}
// Avoid collision with Terrain and Static objects.
float fCollisionAvoidanceWeight = 1.0f;
float fCollisionDistance = 30;
m_alignHorizontally = 0;
// Wants to land.
if (m_landing)
{
// Go down.
m_accel.z = -LANDING_FORCE;
fCollisionDistance = 4.0f;
if (m_pos.z - bc.terrainZ < bc.MinHeight)
{
// Landing procedure.
// Slow down bird, and align horizontally.
float l = (bc.MinHeight - (m_pos.z - bc.terrainZ))/bc.MinHeight;
//m_accel = m_accel.Normalized()*l;
m_accel += -m_heading*m_speed*l;
//m_speed = m_speed*(1.2f - l);
m_alignHorizontally = l;
}
}
if (bc.avoidObstacles)
{
// Avoid obstacles & terrain.
IPhysicalWorld *physWorld = bc.physics;
vectorf vPos = m_pos;
vectorf vDir = m_heading*fCollisionDistance;
int objTypes = ent_terrain|ent_static;
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)
{
//GetIEditor()->GetRenderer()->SetMaterialColor( 1,0,0,1 );
//GetIEditor()->GetRenderer()->DrawLine( m_pos,m_pos+m_heading*hit.dist );
if (m_landing)
{
// Decrease speed of bird.
m_accel = -m_heading*(fCollisionDistance - hit.dist)/fCollisionDistance;
}
else
{
// Turn right/up.
Vec3 normal = hit.n;
//if (m_banking > 0) sideDir = -sideDir;
//m_accel -= m_heading*(fCollisionDistance - hit.dist)*fCollisionAvoidanceWeight;
//m_accel += (sideDir*2.0f+Vec3(0,0,1))*(fCollisionDistance - hit.dist)*fCollisionAvoidanceWeight;
float w = (1.0f - hit.dist/fCollisionDistance);
//Vec3 R = (m_heading - (2.0f*normal)).Normalized();
m_accel += normal*w*w*bc.factorAvoidLand * fCollisionAvoidanceWeight;
}
}
}
if (m_landing)
{
//GetIEditor()->GetRenderer()->SetMaterialColor( 1,0,1,1 );
//GetIEditor()->GetRenderer()->DrawLine( m_pos,m_pos+Vec3(0,0,-10) );
}
// 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;
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidBird::Kill( const Vec3 &hitPoint,const Vec3 &force,string &surfaceName )
{
surfaceName = "mat_feathers";
if (!m_dead)
{
if (m_pPhysics)
m_pPhysics->GetWorld()->DestroyPhysicalEntity(m_pPhysics);
m_pPhysics = 0;
}
if (CFlockManager::m_e_flocks_hunt == 0)
{
m_physicsControlled = false;
m_dead = true;
m_nodraw = true;
// No physics at all.
return;
}
SBoidContext bc;
m_flock->GetBoidSettings(bc);
Vec3 impulse = force;
if (impulse.GetLength() > 100.0f)
{
impulse.Normalize();
impulse *= 100.0f;
}
if (!m_physicsControlled)
{
if (!m_object)
return;
Vec3 mins,maxs;
m_object->GetBBox( mins,maxs );
Vec3 size = ((maxs - mins)/2.2f)*bc.boidScale;
//CreateRigidBox( bc,size,BIRDS_PHYSICS_DENSITY );
CreateArticulatedCharacter( bc,size,BIRDS_PHYSICS_DENSITY );
impulse += m_heading * (size.x*size.y*size.z)*BIRDS_PHYSICS_DENSITY;
m_physicsControlled = true;
// Small impulse.
// Apply force on this body.
pe_action_impulse theAction;
Vec3 someforce;
someforce = impulse;
someforce.Normalize();
theAction.impulse = someforce;
theAction.ipart = 0;
theAction.iApplyTime = 0;
m_pPhysics->Action(&theAction);
}
if (m_physicsControlled)
{
// Apply force on this body.
pe_action_impulse theAction;
theAction.impulse = impulse;
theAction.point = hitPoint;
theAction.iApplyTime = 0;
m_pPhysics->Action(&theAction);
}
if (m_object && !m_dying && !m_dead)
{
m_object->ResetAnimations();
//m_object->StartAnimation( "death",CryCharAnimationParams() );
}
m_dead = true;
}
//////////////////////////////////////////////////////////////////////////
CBoidFish::CBoidFish( SBoidContext &bc )
: CBoidObject( bc )
{
m_dead = 0;
m_dying = 0;
m_pOnSpawnBubbleFunc = NULL;
}
CBoidFish::~CBoidFish()
{
if (m_pOnSpawnBubbleFunc)
{
IScriptSystem *pScriptSystem = m_flock->m_flockMgr->GetSystem()->GetIScriptSystem();
if (pScriptSystem)
pScriptSystem->ReleaseFunc( m_pOnSpawnBubbleFunc );
}
}
void CBoidFish::Update( float dt,SBoidContext &bc )
{
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;
}
}
if (m_dead)
return;
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->ResetAnimations();
}
return;
}
//////////////////////////////////////////////////////////////////////////
if (bc.followPlayer)
{
if (GetSquaredDistance(m_pos,bc.playerPos) > MAX_BIRDS_DISTANCE*MAX_BIRDS_DISTANCE)
{
float z = bc.MinHeight + (frand()+1)/2.0f*(bc.MaxHeight - bc.MinHeight);
m_pos = bc.playerPos + Vec3(frand()*MAX_BIRDS_DISTANCE,frand()*MAX_BIRDS_DISTANCE,z );
m_speed = bc.MinSpeed + ((frand()+1)/2.0f) / (bc.MaxSpeed - bc.MinSpeed);
m_heading = GetNormalized(Vec3(frand(),frand(),0));
}
}
float height = m_pos.z - bc.terrainZ;
// Continue accelerating in same dir untill target speed reached.
// Try to maintain avarage speed of (maxspeed+minspeed)/2
float targetSpeed = (bc.MaxSpeed + bc.MinSpeed)/2;
m_accel -= m_heading*(m_speed-targetSpeed)*0.5f;
/*
//m_lastThinkTime += dt;
if (rand() % 10 == 1)
{
bc.flockPos = bc.flockPos + Vec3(frand()*30,frand()*30,0 );
//m_accel.x = frand()*120;
//m_accel.y = frand()*120;
}
*/
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);
m_accel.z += (-v*v)*bc.factorAvoidLand;
}
else
// Avoid land.
if (height < bc.MinHeight)
{
float v = (1.0f - height/bc.MinHeight);
m_accel.z += (v*v)*bc.factorAvoidLand;
}
else
{
// Allways try to accelerate in direction oposite to current in Z axis.
m_accel.z = -m_heading.z * 2.0f;
}
// Attract to origin point.
if (bc.followPlayer)
{
m_accel += (bc.playerPos - m_pos) * bc.factorAttractToOrigin;
}
else
{
//float originDistance2 = GetLengthSquared(bc.flockPos-m_pos);
//if (originDistance2 > bc.MaxAttractDistance*bc.MaxAttractDistance)
{
float randomRadius = bc.MaxAttractDistance * 0.5f;
Vec3 origin(bc.flockPos.x+frand()*randomRadius,bc.flockPos.y+frand()*randomRadius,bc.flockPos.z );
m_accel += (origin - m_pos) * bc.factorAttractToOrigin;
}
//m_accel += (bc.flockPos - m_pos) * bc.factorAttractToOrigin;
/*
float originDistance2 = GetLengthSquared(bc.flockPos-m_pos);
if (originDistance2 > bc.MaxAttractDistance*bc.MaxAttractDistance)
{
Vec3 origin(bc.flockPos.x+frand(),bc.flockPos.y+frand(),bc.flockPos.z );
m_accel += (origin - m_pos) * bc.factorAttractToOrigin;
}
*/
}
// Avoid collision with Terrain and Static objects.
float fCollisionAvoidanceWeight = 1.0f;
float fCollisionDistance = 10;
if (bc.avoidObstacles)
{
// Avoid obstacles & terrain.
IPhysicalWorld *physWorld = bc.physics;
vectorf vPos = m_pos;
vectorf vDir = m_heading*fCollisionDistance;
int objTypes = ent_terrain|ent_static;
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 w = (1.0f - hit.dist/fCollisionDistance);
//Vec3 R = (m_heading - (2.0f*normal)).Normalized();
m_accel += normal*w*bc.factorAvoidLand * fCollisionAvoidanceWeight;
}
}
if (rand()%40 == 1)
{
// Aplying random horizontal force.
Vec3 v(frand(),frand(),frand()/2);
//v = v.Cross(m_heading);
m_accel += v*30;
// Spawn bubble.
SpawnBubble( m_pos,bc );
}
//////////////////////////////////////////////////////////////////////////
// Player must scare fishes off.
//////////////////////////////////////////////////////////////////////////
float playerDist = GetDistance(m_pos,bc.playerPos);
if (playerDist < SCARE_DISTANCE)
{
Vec3 retreatDir = m_pos - bc.playerPos;
retreatDir.Normalize();
float scareFactor = (1.0f - playerDist/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;
}
else if (m_pos.z < bc.terrainZ+0.2f && bc.terrainZ < bc.waterLevel)
{
m_pos.z = bc.terrainZ+0.2f;
}
// Update physics position.
if (m_pPhysics)
{
pe_params_pos ppos;
ppos.pos = vectorf(m_pos);
m_pPhysics->SetParams(&ppos);
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidFish::SpawnBubble( const Vec3 &pos,SBoidContext &bc )
{
if (!bc.entity)
return;
IScriptObject *pScriptObject = bc.entity->GetScriptObject();
if (!pScriptObject)
return;
IScriptSystem *pScriptSystem = m_flock->m_flockMgr->GetSystem()->GetIScriptSystem();
if (!m_pOnSpawnBubbleFunc)
{
pScriptObject->GetValue( "OnSpawnBubble",m_pOnSpawnBubbleFunc );
}
if (m_pOnSpawnBubbleFunc)
{
if (!vec_Bubble)
{
vec_Bubble.Create(pScriptSystem);
}
vec_Bubble = pos;
pScriptSystem->BeginCall( m_pOnSpawnBubbleFunc );
pScriptSystem->PushFuncParam( pScriptObject );
pScriptSystem->PushFuncParam( *vec_Bubble );
pScriptSystem->EndCall();
}
}
//////////////////////////////////////////////////////////////////////////
void CBoidFish::Kill( const Vec3 &hitPoint,const Vec3 &force,string &surfaceName )
{
SBoidContext bc;
m_flock->GetBoidSettings(bc);
surfaceName = "mat_fish";
// Can`t Kill fish in MP game.
IGame *pGame = GetISystem()->GetIGame();
if (pGame && pGame->GetModuleState( EGameMultiplayer ) )
return;
float boxSize = bc.fBoidRadius/2;
float mass = ((boxSize/4)*boxSize*boxSize)*FISH_PHYSICS_DENSITY; // box volume * density
Vec3 impulse = force * mass * 0.1f;
if (!m_physicsControlled)
{
CreateRigidBox( bc,Vec3(boxSize/4,boxSize,boxSize),FISH_PHYSICS_DENSITY );
//impulse += m_heading*m_speed*mass;
m_physicsControlled = true;
}
if (m_physicsControlled)
{
// Apply force on this body.
pe_action_impulse theAction;
theAction.impulse = impulse;
theAction.point = hitPoint;
theAction.iApplyTime = 0;
m_pPhysics->Action(&theAction);
}
if (m_object && !m_dead && !m_dying)
{
//m_object->ResetAnimations();
//m_object->StartAnimation( "death" );
m_object->SetAnimationSpeed( 1 );
}
m_dying = true;
m_dyingTime = 0;
}
//////////////////////////////////////////////////////////////////////////
CFlock::CFlock( int id,CFlockManager *mgr )
{
m_flockMgr = mgr;
m_id = id;
m_bEnabled = true;
m_pEntity = NULL;
m_updateFrameID = 0;
m_percentEnabled = 100;
m_type = EFLOCK_BIRDS;
m_bounds.min = Vec3(-1,-1,-1);
m_bounds.max = Vec3(1,1,1);
GetDefaultBoidsContext(m_bc);
m_bc.engine = m_flockMgr->GetSystem()->GetI3DEngine();
m_bc.physics = m_flockMgr->GetSystem()->GetIPhysicalWorld();
m_bc.waterLevel = m_bc.engine->GetWaterLevel();
m_bc.fBoidMass = 1;
m_bc.fBoidRadius = 1;
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetName( const char *name )
{
strcpy( m_name,name );
}
//////////////////////////////////////////////////////////////////////////
void CFlock::GetDefaultBoidsContext( SBoidContext &bc )
{
ZeroStruct(bc);
bc.MinHeight = MIN_FLIGHT_HEIGHT;
bc.MaxHeight = MAX_FLIGHT_HEIGHT;
bc.MaxAttractDistance = MAX_ATTRACT_DISTANCE;
bc.MinAttractDistance = MIN_ATTRACT_DISTANCE;
bc.MaxSpeed = MAX_SPEED;
bc.MinSpeed = MIN_SPEED;
bc.factorAlignment = ALIGNMENT_FACTOR;
bc.factorCohesion = COHESION_FACTOR;
bc.factorSeparation = SEPARATION_FACTOR;
bc.factorAttractToOrigin = ORIGIN_ATTRACT_FACTOR;
bc.factorKeepHeight = DESIRED_HEIGHT_FACTOR;
bc.factorAvoidLand = AVOID_LAND_FACTOR;
bc.MaxAnimationSpeed = MAX_ANIMATION_SPEED;
bc.followPlayer = false;
bc.avoidObstacles = true;
bc.cosFovAngle = cry_cosf(100.0f*3.1415f/180.0f);
bc.maxVisibleDistance = 300;
bc.boidScale = 5;
}
CFlock::~CFlock()
{
ClearBoids();
if (m_pEntity)
m_pEntity->SetContainer(0);
}
//////////////////////////////////////////////////////////////////////////
void CFlock::ClearBoids()
{
I3DEngine *engine = m_flockMgr->GetSystem()->GetI3DEngine();
for (Boids::iterator it = m_boids.begin(); it != m_boids.end(); ++it)
{
CBoidObject* boid = *it;
if (boid->m_object)
{
engine->RemoveCharacter(boid->m_object);
}
delete boid;
}
m_boids.clear();
}
//////////////////////////////////////////////////////////////////////////
void CFlock::AddBoid( CBoidObject *boid )
{
boid->m_flock = this;
m_boids.push_back(boid);
}
//////////////////////////////////////////////////////////////////////////
bool CFlock::IsFlockActive() const
{
if (!m_bEnabled)
return false;
if (m_percentEnabled <= 0)
return false;
if (m_bc.followPlayer)
return true;
float d = m_bc.maxVisibleDistance;
if (d*d < GetSquaredDistance(m_flockMgr->GetSystem()->GetViewCamera().GetPos(),m_origin))
{
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetEnabled( bool bEnabled )
{
if (m_bEnabled != bEnabled)
{
m_bEnabled = bEnabled;
if (m_pEntity)
{
if (m_bEnabled)
{
m_pEntity->SetUpdateVisLevel( eUT_PotVisible );
m_pEntity->SetUpdateRadius( GetMaxVisibilityDistance() );
}
else
m_pEntity->SetUpdateVisLevel( eUT_Never );
}
}
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetPercentEnabled( int percent )
{
if (percent < 0)
percent = 0;
if (percent > 100)
percent = 100;
m_percentEnabled = percent;
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetEntity( IEntity* entity )
{
assert( entity );
m_pEntity = entity;
m_pEntity->SetContainer( this );
m_pEntity->SetNeedUpdate(true);
m_pEntity->SetUpdateVisLevel( eUT_PotVisible );
m_pEntity->SetUpdateRadius( GetMaxVisibilityDistance() );
m_pEntity->InitEntityRenderState();
m_pEntity->SetRadius(1);
};
//////////////////////////////////////////////////////////////////////////
void CFlock::Update()
{
FUNCTION_PROFILER( GetISystem(),PROFILE_GAME );
if (!IsFlockActive())
return;
if (!m_flockMgr->IsFlocksEnabled())
return;
float dt = m_flockMgr->GetSystem()->GetITimer()->GetFrameTime();
// Make sure delta time is limited.
if (dt > 1.0f)
dt = 0.01f;
if (dt > 0.1f)
dt = 0.1f;
/*
for (Boids::iterator it = m_boids.begin(); it != m_boids.end(); ++it)
{
CBoidObject *boid = *it;
boid->Think();
}
*/
//m_bc.playerPos = m_flockMgr->GetPlayerPos();
m_bc.playerPos = m_flockMgr->GetSystem()->GetViewCamera().GetPos(); // Player position is position of camera.
m_bc.flockPos = m_origin;
m_bc.waterLevel = m_bc.engine->GetWaterLevel( &m_origin );
m_bounds.min = Vec3(FLT_MAX,FLT_MAX,FLT_MAX);
m_bounds.max = Vec3(-FLT_MAX,-FLT_MAX,-FLT_MAX);
int numBoids = m_boids.size();
if (m_percentEnabled < 100)
{
numBoids = (m_percentEnabled*numBoids)/100;
}
int num = 0;
for (Boids::iterator it = m_boids.begin(); it != m_boids.end(); ++it)
{
if (num++ > numBoids)
break;
CBoidObject* boid = *it;
m_bc.terrainZ = m_bc.engine->GetTerrainElevation(boid->m_pos.x,boid->m_pos.y);
boid->Update(dt,m_bc);
// Update bounding box of flock.
m_bounds.min.x = __min( m_bounds.min.x,boid->m_pos.x-m_bc.fBoidRadius );
m_bounds.min.y = __min( m_bounds.min.y,boid->m_pos.y-m_bc.fBoidRadius );
m_bounds.min.z = __min( m_bounds.min.z,boid->m_pos.z-m_bc.fBoidRadius );
m_bounds.max.x = __max( m_bounds.max.x,boid->m_pos.x+m_bc.fBoidRadius );
m_bounds.max.y = __max( m_bounds.max.y,boid->m_pos.y+m_bc.fBoidRadius );
m_bounds.max.z = __max( m_bounds.max.z,boid->m_pos.z+m_bc.fBoidRadius );
}
if (m_pEntity && !m_boids.empty())
{
// Entity Bbox must be local.
AABB box;
box.min = m_bounds.min - m_origin;
box.max = m_bounds.max - m_origin;
m_pEntity->SetBBox( box.min,box.max );
}
m_updateFrameID = m_flockMgr->GetSystem()->GetIRenderer()->GetFrameID();
//m_flockMgr->GetSystem()->GetILog()->Log( "Birds Update" );
}
//////////////////////////////////////////////////////////////////////////
void CFlock::OnDraw(const SRendParams & EntDrawParams)
{
FUNCTION_PROFILER( GetISystem(),PROFILE_GAME );
if (!m_flockMgr->IsFlocksEnabled())
return;
// Only draw birds flock on the same frame id, as update call.
int frameId = m_flockMgr->GetSystem()->GetIRenderer()->GetFrameID();
if (abs(frameId-m_updateFrameID) > 2)
return;
SRendParams rp( EntDrawParams );
CCamera &cam = m_flockMgr->GetSystem()->GetViewCamera();
// Check if flock bounding box is visible.
if (!cam.IsAABBVisibleFast( AABB(m_bounds.min,m_bounds.max) ))
return;
int numBoids = m_boids.size();
if (m_percentEnabled < 100)
{
numBoids = (m_percentEnabled*numBoids)/100;
}
int num = 0;
for (Boids::iterator it = m_boids.begin(); it != m_boids.end(); ++it)
{
if (num++ > numBoids)
break;
CBoidObject *boid = *it;
boid->Render(rp,cam,m_bc);
}
//m_flockMgr->GetSystem()->GetILog()->Log( "Birds Draw" );
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetBoidSettings( SBoidContext &bc )
{
m_bc = bc;
if (m_bc.MinHeight == 0)
m_bc.MinHeight = 0.01f;
if (m_pEntity)
m_pEntity->SetUpdateRadius( GetMaxVisibilityDistance() );
}
//////////////////////////////////////////////////////////////////////////
void CFlock::SetPos( const Vec3& pos )
{
Vec3 ofs = pos - m_origin;
m_origin = pos;
m_bc.flockPos = m_origin;
for (Boids::iterator it = m_boids.begin(); it != m_boids.end(); ++it)
{
CBoidObject *boid = *it;
boid->m_pos += ofs;
boid->OnFlockMove( m_bc );
}
// Update bounding box of flock entity.
if (m_pEntity)
{
//float s = m_bc.MaxAttractDistance;
float s = 1;
//m_pEntity->SetBBox( pos-Vec3(s,s,s),pos+Vec3(s,s,s) );
//m_pEntity->ForceRegisterInSectors();
}
}
//////////////////////////////////////////////////////////////////////////
bool CFlock::RayTest( Vec3 &raySrc,Vec3 &rayTrg,SFlockHit &hit )
{
// Vec3 v;
Vec3 p1,p2;
// Check all boids.
for (unsigned int i = 0; i < m_boids.size(); i++)
{
CBoidObject *boid = m_boids[i];
if ( Intersect::Lineseg_Sphere( raySrc,rayTrg, boid->m_pos,m_bc.boidScale, p1,p2 ) > 0)
{
hit.object = boid;
hit.dist = (raySrc - p1).Length();
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
// Flock/Birds flocks.
//////////////////////////////////////////////////////////////////////////
void CBirdsFlock::CreateBoids( SBoidsCreateContext &ctx )
{
ClearBoids();
string model;
if (!ctx.models.empty())
model = ctx.models[0];
// Different boids.
for (int i = 0; i < ctx.boidsCount; i++)
{
CBoidObject *boid = new CBoidBird( m_bc );
float radius = m_bc.fSpawnRadius;
boid->m_pos = m_origin + Vec3(radius*frand(),radius*frand(),frand()*radius);
boid->m_heading = GetNormalized( Vec3(frand(),frand(),0) );
boid->m_object = m_flockMgr->GetSystem()->GetI3DEngine()->MakeCharacter( model.c_str() );
if (boid->m_object)
{
boid->m_object->StartAnimation( "fly_loop" , CryCharAnimationParams());
Vec3 mins,maxs;
boid->m_object->GetBBox( mins,maxs );
m_bc.fBoidRadius = (GetLength(maxs-mins)/2.0f) * m_bc.boidScale;
boid->Physicalize(m_bc);
}
AddBoid(boid);
}
}
//////////////////////////////////////////////////////////////////////////
void CFishFlock::CreateBoids( SBoidsCreateContext &ctx )
{
ClearBoids();
string model;
if (!ctx.models.empty())
model = ctx.models[0];
// Different boids.
for (int i = 0; i < ctx.boidsCount; i++)
{
CBoidObject *boid = new CBoidFish( m_bc );
float radius = m_bc.MaxAttractDistance*2;
radius = 2.0f;
boid->m_pos = m_origin + Vec3(radius*frand(),radius*frand(),frand()*10);
float terrainZ = m_bc.engine->GetTerrainElevation(boid->m_pos.x,boid->m_pos.y);
if (boid->m_pos.z <= terrainZ)
boid->m_pos.z = terrainZ + 0.01f;
if (boid->m_pos.z > m_bc.waterLevel)
boid->m_pos.z = m_bc.waterLevel-1;
boid->m_speed = m_bc.MinSpeed + (frand()+1)/2.0f*(m_bc.MaxSpeed - m_bc.MinSpeed);
boid->m_heading = GetNormalized( Vec3(frand(),frand(),0) );
boid->m_object = m_flockMgr->GetSystem()->GetI3DEngine()->MakeCharacter( model.c_str() );
if (boid->m_object)
{
boid->m_object->StartAnimation( "swim_loop", CryCharAnimationParams() );
Vec3 mins,maxs;
boid->m_object->GetBBox( mins,maxs );
m_bc.fBoidRadius = GetLength(maxs-mins) * m_bc.boidScale;
boid->Physicalize(m_bc);
}
AddBoid(boid);
}
}
//////////////////////////////////////////////////////////////////////////
// CFlockManager update.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CFlockManager::Update( float dt,Vec3 &playerPos )
{
m_playerPos = playerPos;
}
//////////////////////////////////////////////////////////////////////////
void CFlockManager::Render()
{
}
int CFlockManager::m_e_flocks = 1;
int CFlockManager::m_e_flocks_hunt = 0;
//////////////////////////////////////////////////////////////////////////
CFlockManager::CFlockManager( ISystem *system )
{
// Create one flock.
m_system = system;
m_lastFlockId = 1;
//m_object = system->GetI3DEngine()->MakeCharacter( "Objects\\Other\\Seagull\\Seagull.cgf" );
//m_object = system->GetI3DEngine()->MakeObject( "Objects\\Other\\Seagull\\Seagull.cgf" );
system->GetIConsole()->Register( "e_flocks",&m_e_flocks,1,VF_DUMPTODISK,"Enable Flocks (Birds/Fishes)" );
system->GetIConsole()->Register( "e_flocks_hunt",&m_e_flocks_hunt,0,0,"Birds will fall down..." );
}
//////////////////////////////////////////////////////////////////////////
CFlockManager::~CFlockManager()
{
ClearFlocks();
}
//////////////////////////////////////////////////////////////////////////
CFlock* CFlockManager::CreateFlock( EFlockType type )
{
CFlock *flock = NULL;
m_lastFlockId++;
switch (type)
{
case EFLOCK_BIRDS:
flock = new CBirdsFlock( m_lastFlockId,this );
break;
case EFLOCK_FISH:
flock = new CFishFlock( m_lastFlockId,this );
break;
case EFLOCK_BUGS:
flock = new CBugsFlock( m_lastFlockId,this );
break;
}
if (!flock)
return 0;
flock->m_type = type;
m_flocks.push_back(flock);
return flock;
}
//////////////////////////////////////////////////////////////////////////
void CFlockManager::RemoveFlock( CFlock *flock )
{
for (Flocks::iterator it = m_flocks.begin(); it != m_flocks.end(); ++it)
{
if (flock == *it)
{
delete flock;
m_flocks.erase( it );
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CFlockManager::ClearFlocks()
{
for (Flocks::iterator it = m_flocks.begin(); it != m_flocks.end(); ++it)
{
delete *it;
}
m_flocks.clear();
}
//////////////////////////////////////////////////////////////////////////
bool CFlockManager::IsFlockVisible( CFlock *flock )
{
if (flock->IsFollowPlayer())
return true;
float d = flock->GetMaxVisibilityDistance();
if (d*d < GetSquaredDistance(m_playerPos,flock->GetPos()))
{
return false;
}
if (flock->GetEntity())
{
// Check if flock entity is potentially visible.
//if (!m_system->GetI3DEngine()->IsPotentiallyVisible( flock->GetEntity(),0 ))
//return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CFlockManager::RayTest( Vec3 &raySrc,Vec3 &rayTrg,SFlockHit &hit,bool onlyVisible )
{
for (Flocks::iterator it = m_flocks.begin(); it != m_flocks.end(); ++it)
{
CFlock *flock = *it;
// Can only hit visible flocks.
if (onlyVisible && !IsFlockVisible(flock))
continue;
if (flock->RayTest( raySrc,rayTrg,hit ))
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
CFlock* CFlockManager::GetFlock( int id )
{
//@FIXME: Linear search!! later replace with map.
for (Flocks::iterator it = m_flocks.begin(); it != m_flocks.end(); ++it)
{
CFlock *flock = *it;
if (flock->GetId() == id)
return flock;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
CFlock* CFlockManager::FindFlock( const char *sFlockName )
{
//@FIXME: Linear search!!
for (Flocks::iterator it = m_flocks.begin(); it != m_flocks.end(); ++it)
{
CFlock *flock = *it;
if (stricmp(sFlockName,flock->GetName()) == 0)
return flock;
}
return 0;
}
void CFlock::PreloadInstanceResources(Vec3d vPrevPortalPos, float fPrevPortalDistance, float fTime)
{
for (int i = 0; i < GetBoidsCount(); i++)
{
CBoidObject * pBoid = GetBoid(i);
float fDistance = fPrevPortalDistance + vPrevPortalPos.GetDistance((m_bounds.min+m_bounds.min)*0.5f);
pBoid->m_object->PreloadResources(fDistance, fTime, 0);
}
}