Files
HL2Overcharged/game/server/overcharged/npc_surface.cpp
2025-05-21 21:20:08 +03:00

2365 lines
67 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "npc_surface.h"
#include "ai_hull.h"
#include "saverestore_utlvector.h"
#include "dt_utlvector_send.h"
#include "physics_saverestore.h"
#include "vphysics/constraints.h"
#include "vcollide_parse.h"
#include "ragdoll_shared.h"
#include "physics_prop_ragdoll.h"
#include "collisionutils.h"
#include "te_effect_dispatch.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//
// CNPC_Surface
//
#define SURFACE_MAX_LENGTH 500
//---------------------------------------------------------
// Custom Client entity
//---------------------------------------------------------
IMPLEMENT_SERVERCLASS_ST(CNPC_Surface, DT_NPC_Surface)
SendPropInt( SENDINFO( m_nActiveParticles ), 12, SPROP_UNSIGNED ),
SendPropFloat( SENDINFO( m_flRadius ), 12, 0, 0.0, 100.0 ),
SendPropUtlVector(
SENDINFO_UTLVECTOR( m_vecSurfacePos ),
MAX_SURFACE_ELEMENTS, // max elements4
SendPropVector( NULL, 0, sizeof( Vector ), -1, SPROP_COORD )),
SendPropUtlVector(
SENDINFO_UTLVECTOR( m_flSurfaceV ),
MAX_SURFACE_ELEMENTS, // max elements
SendPropFloat( NULL, 0, sizeof( float ), 6, 0, 0.0, 1.0 )),
SendPropUtlVector(
SENDINFO_UTLVECTOR( m_flSurfaceR ),
MAX_SURFACE_ELEMENTS, // max elements
SendPropFloat( NULL, 0, sizeof( float ), 6, 0, 0.0, 2.0 )),
END_SEND_TABLE()
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Surface )
DEFINE_UTLVECTOR( m_vecSurfacePos, FIELD_POSITION_VECTOR ),
DEFINE_UTLVECTOR( m_flSurfaceV, FIELD_FLOAT ),
DEFINE_UTLVECTOR( m_flSurfaceR, FIELD_FLOAT ),
DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
DEFINE_AUTO_ARRAY( m_bContact, FIELD_BOOLEAN ),
END_DATADESC()
//-------------------------------------
static ConVar sv_surface_tension( "surface_tension", "5", 0, "How strong the surface tries to keep its shape" );
static ConVar sv_surface_radius( "surface_radius", "8", 0, "Radius of each sphere" );
static ConVar sv_surface_ideal( "surface_ideal", "2", 0, "ideal distance (N * radius) between each sphere" );
static ConVar sv_surface_nearby( "surface_nearby", "3", 0, "acceptable distance (N * radius) between each and still be considered touching" );
static ConVar sv_surface_scale( "surface_scale", "0.5" );
static ConVar sv_lj_strength( "lj_strength", "1", 0 );
//-------------------------------------
/*
BEGIN_SIMPLE_DATADESC( CSurfaceController )
DEFINE_FIELD( m_vecAngular, FIELD_VECTOR ),
DEFINE_FIELD( m_vecLinear, FIELD_VECTOR ),
DEFINE_FIELD( m_fIsStopped, FIELD_BOOLEAN ),
END_DATADESC()
*/
//-------------------------------------
// Purpose: Initialize the custom schedules
//-------------------------------------
//-------------------------------------
void CNPC_Surface::Precache()
{
PrecacheModel( "models/Hydra.mdl" );
BaseClass::Precache();
}
void CNPC_Surface::Activate( void )
{
BaseClass::Activate();
}
//-----------------------------------------------------------------------------
// Purpose: Returns this monster's place in the relationship table.
//-----------------------------------------------------------------------------
Class_T CNPC_Surface::Classify( void )
{
return CLASS_BARNACLE;
}
//-------------------------------------
void CNPC_Surface::Spawn()
{
Precache();
BaseClass::Spawn();
SetModel( "models/Hydra.mdl" ); // ep3 hydra ???
SetHullType(HULL_SMALL_CENTERED);
SetHullSizeNormal();
// setup model
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED | FSOLID_NOT_STANDABLE );
SetCollisionBounds( -Vector(400,400,100), Vector(400,400,400) );
SetMoveType( MOVETYPE_VPHYSICS );
AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
AddEFlags( EFL_NO_DISSOLVE );
SetBloodColor( BLOOD_COLOR_YELLOW );
ClearEffects();
m_iHealth = 200;
m_flFieldOfView = -1.0;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
m_NPCState = NPC_STATE_NONE;
SetAbsAngles( QAngle( 0, 0, 0 ) );
m_vecSurfacePos.EnsureCount( MAX_SURFACE_ELEMENTS );
m_flSurfaceV.EnsureCount( MAX_SURFACE_ELEMENTS );
m_flSurfaceR.EnsureCount( MAX_SURFACE_ELEMENTS );
m_vecStart = GetAbsOrigin( );
m_vecSurfacePos[0] = m_vecStart;
m_flSurfaceV[0] = 0.0;
m_flSurfaceR[0] = 1.0;
m_flRadius = sv_surface_radius.GetFloat();
for (int i = 1; i < MAX_SURFACE_ELEMENTS; i++)
{
m_vecSurfacePos[i] = m_vecSurfacePos[i-1];
m_flSurfaceV[i] = m_flSurfaceV[i-1];
m_flSurfaceR[i] = m_flSurfaceR[i-1];
}
NPCInit();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Surface::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
{
BaseClass::VPhysicsCollision( index, pEvent );
// the sphere hit is: i = pEvent->pObjects[index]->GetGameIndex();
// CBaseEntity *pHit = pEvent->pEntities[!index];
int nSphere = pEvent->pObjects[index]->GetGameIndex();
m_bContact[nSphere] = true;
#if 0
static int count = 0;
count ++;
//if((float(rand()) / float(RAND_MAX)) < 0.1f)
if(count % 10 == 0)
{
// Create splash effect
CEffectData data;
data.m_fFlags = 0;
Vector pos;
pEvent->pObjects[index]->GetPosition( &pos, NULL );
Vector fountainOrigin(-1980, -1792, 1);
Vector rad = pos - fountainOrigin;
//pos += (rad * 0.25f);
data.m_vOrigin = pos + Vector(0, 0, 5.0f);
// FIXME: needs to be the correct vector for the impact!
rad.NormalizeInPlace();
data.m_vNormal = rad;//Vector( 0, 0, 1 );
data.m_flScale = 10.0f;
DispatchEffect( "watersplash", data );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Surface::OnSave( IEntitySaveUtils *pUtils )
{
// TODO: copy current physics state to the temp arrays
BaseClass::OnSave( pUtils );
}
void CNPC_Surface::OnRestore( void )
{
BaseClass::OnRestore();
// TODO: recreate physics state from the temp arrays
CreateVPhysics( true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_Surface::CreateVPhysics()
{
return CreateVPhysics( false );
}
bool CNPC_Surface::CreateVPhysics( bool bFromRestore )
{
// setup individual spheres
objectparams_t params = g_PhysDefaultObjectParams;
params.pGameData = static_cast<void *>(this);
int nMaterialIndex = physprops->GetSurfaceIndex("water");
int i;
for (i = 0; i < m_nActiveParticles; i++)
{
if (!bFromRestore)
{
m_vecSurfacePos[i] = GetAbsOrigin() + Vector( RandomFloat( -10, 10 ), RandomFloat( -10, 10 ), RandomFloat( 0, 2 ) ) * m_flRadius;
}
m_pSpheres[i] = physenv->CreateSphereObject( m_flRadius, nMaterialIndex, m_vecSurfacePos[i], GetAbsAngles(), &params, false );
if ( m_pSpheres[i] )
{
Vector vVelocity = Vector( RandomFloat( -1, 1 ), RandomFloat( -1, 1 ), RandomFloat( 1, 2 ) ) * 10.0f;
m_pSpheres[i]->SetVelocity( &vVelocity, NULL );
//PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_MULTIOBJECT_ENTITY );
PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_MULTIOBJECT_ENTITY ); // call collisionruleschanged if this changes dynamically
m_pSpheres[i]->SetGameIndex( i );
m_pSpheres[i]->SetMass( 10.0f );
m_pSpheres[i]->EnableGravity( true );
m_pSpheres[i]->EnableDrag( true );
// m_pSpheres[i]->EnableMotion( false );
float flDamping = 0.5f;
float flAngDamping = 0.5f;
m_pSpheres[i]->SetDamping( &flDamping, &flAngDamping );
//m_pSpheres[i]->SetInertia( Vector( 1e30, 1e30, 1e30 ) );
}
// VPhysicsSetObject( m_pSpheres[i] );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: return a list of all the physics objects
//-----------------------------------------------------------------------------
int CNPC_Surface::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax )
{
int count = 0;
for ( int i = 0; i < listMax && i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f)
{
pList[count++] = m_pSpheres[i];
}
}
return count;
}
bool CNPC_Surface::VPhysicsIsFlesh( void )
{
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Surface::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
{
CDefaultPlayerPickupVPhysics::OnPhysGunPickup( pPhysGunUser, reason );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNPC_Surface::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
{
CDefaultPlayerPickupVPhysics::OnPhysGunDrop( pPhysGunUser, Reason );
}
//-----------------------------------------------------------------------------
// Purpose: Detect that the physgun is trying to punt us. Currently guess about damage
//-----------------------------------------------------------------------------
bool CNPC_Surface::OnAttemptPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
{
if ( reason == PUNTED_BY_CANNON )
{
Vector forward;
pPhysGunUser->EyeVectors( &forward );
Vector start, end;
start = pPhysGunUser->Weapon_ShootPosition();
float d1, d2;
for ( int i = 0; i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f && IntersectInfiniteRayWithSphere( start, forward, m_vecSurfacePos[i], m_flRadius * 3, &d1, &d2 ))
{
Vector p1 = start + d1 * forward;
// no idea what sort of forces to use when punting
// also, forceoffset just applies a spin, it doesn't act like being hit with a larger sphere
m_pSpheres[i]->ApplyForceOffset( forward * 1000.0f, p1 );
}
}
return false;
}
return CDefaultPlayerPickupVPhysics::OnAttemptPhysGunPickup( pPhysGunUser, reason );
}
//-----------------------------------------------------------------------------
// Purpose: Apply collisions to multiple spheres instead of just the one hit
//-----------------------------------------------------------------------------
void CNPC_Surface::ApplyDamageForce( const CTakeDamageInfo &info )
{
float flMinDist2 = (10 * m_flRadius);
flMinDist2 = flMinDist2 * flMinDist2;
// FIXME: this needs a better algorithm for radiating the force
float flForce = info.GetDamageForce().Length();
flMinDist2 *= max( 1.0, sqrt( flForce / 1000 ));
if (! (info.GetDamageType() & DMG_BLAST))
{
// apply non-blast damage in the direction of the force
for ( int i = 0; i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f)
{
float flDist2 = (m_vecSurfacePos[i] - info.GetDamagePosition()).LengthSqr();
if (flDist2 < flMinDist2)
{
m_pSpheres[i]->ApplyForceOffset( info.GetDamageForce() * (1.0 - flDist2 / flMinDist2), info.GetDamagePosition() );
}
}
}
}
else
{
// blast damage goes out from a point
for ( int i = 0; i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f)
{
float flDist2 = (m_vecSurfacePos[i] - info.GetDamagePosition()).LengthSqr();
if (flDist2 < flMinDist2)
{
Vector dir = (m_vecSurfacePos[i] - info.GetDamagePosition()) / sqrt( flDist2 );
m_pSpheres[i]->ApplyForceCenter( dir * flForce * (1.0 - flDist2 / flMinDist2) );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CNPC_Surface::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
CTakeDamageInfo tdInfo( info );
// don't ever actually take damage
tdInfo.SetDamage( 0 );
if (tdInfo.GetDamageType() & (DMG_BULLET | DMG_CLUB))
{
ApplyDamageForce( tdInfo );
}
else if (info.GetDamageType() & DMG_BLAST)
{
ApplyDamageForce( tdInfo );
}
return BaseClass::OnTakeDamage_Alive( tdInfo );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CNPC_Surface::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
{
int nLastHit = -1;
if (ray.m_IsRay)
{
float d1, d2;
for ( int i = 0; i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f && IntersectRayWithSphere( ray.m_Start, ray.m_Delta, m_vecSurfacePos[i], m_flRadius, &d1, &d2 ))
{
if (d1 < tr.fraction)
{
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
nLastHit = i;
tr.fraction = d1;
}
}
}
if (nLastHit != -1)
{
tr.m_pEnt = this;
tr.startpos = ray.m_Start;
tr.endpos = ray.m_Start + ray.m_Delta * tr.fraction;
tr.contents = CONTENTS_SOLID;
tr.hitbox = nLastHit;
tr.hitgroup = HITGROUP_GENERIC;
tr.plane.dist = tr.endpos.Length();
tr.plane.normal = (tr.endpos - m_vecSurfacePos[nLastHit]) * (1 / m_flRadius);
tr.plane.type = 0;
tr.physicsbone = nLastHit;
}
}
else
{
// FIXME: This isn't a valid test, Jay needs to make it real
Vector vecMin = Vector( -m_flRadius, -m_flRadius, -m_flRadius) - ray.m_Extents;
Vector vecMax = Vector( m_flRadius, m_flRadius, m_flRadius) + ray.m_Extents;
trace_t boxtrace;
tr.fraction = 1.0;
for ( int i = 0; i < m_nActiveParticles; i++ )
{
if (m_flSurfaceR[i] > 0.0f && IntersectRayWithBox( m_vecSurfacePos[i] - ray.m_Start, -ray.m_Delta, vecMin, vecMax, 0.0, &boxtrace ))
{
if (boxtrace.fraction < tr.fraction)
{
if (tr.startsolid && !IsBoxIntersectingSphere( ray.m_Start - ray.m_Extents, ray.m_Start + ray.m_Extents, m_vecSurfacePos[i], m_flRadius ))
{
tr.startsolid = false;
tr.allsolid = false;
}
else if (tr.allsolid && !IsBoxIntersectingSphere( ray.m_Start + ray.m_Delta - ray.m_Extents, ray.m_Start + ray.m_Delta + ray.m_Extents, m_vecSurfacePos[i], m_flRadius ))
{
tr.allsolid = false;
}
tr = boxtrace;
tr.startpos = ray.m_Start;
tr.endpos = ray.m_Start + tr.fraction * ray.m_Delta;
nLastHit = i;
}
}
}
/*
if (tr.startsolid)
{
NDebugOverlay::Box( ray.m_Start, -ray.m_Extents, ray.m_Extents, 255, 0, 0, 0, 10.0);
}
*/
//Assert( !tr.startsolid );
//Assert( !tr.allsolid );
//Msg("%5.2f (%6.1f %6.1f %6.1f ) : ", gpGlobals->curtime, ray.m_Start.x, ray.m_Start.y, ray.m_Start.z );
//Msg("%6.1f %6.1f %6.1f : %4.2f : %6.1f %6.1f %6.1f\n", tr.startpos.x, tr.startpos.y, tr.startpos.z, tr.fraction, tr.endpos.x, tr.endpos.y, tr.endpos.z );
if (tr.fraction < 1.0)
{
tr.contents = CONTENTS_SOLID;
tr.m_pEnt = this;
tr.hitbox = nLastHit;
tr.hitgroup = HITGROUP_GENERIC;
tr.physicsbone = nLastHit;
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
//-------------------------------------
void CNPC_Surface::RunAI( void )
{
BaseClass::RunAI();
}
//-------------------------------------
float CNPC_Surface::MaxYawSpeed()
{
return 180;
}
//-------------------------------------
int CNPC_Surface::TranslateSchedule( int scheduleType )
{
return BaseClass::TranslateSchedule( scheduleType );
}
//-------------------------------------
void CNPC_Surface::HandleAnimEvent( animevent_t *pEvent )
{
BaseClass::HandleAnimEvent( pEvent );
}
//-------------------------------------
void CNPC_Surface::PrescheduleThink()
{
BaseClass::PrescheduleThink();
}
//-------------------------------------
int CNPC_Surface::SelectSchedule ()
{
return BaseClass::SelectSchedule();
}
//-------------------------------------
void CNPC_Surface::StartTask( const Task_t *pTask )
{
BaseClass::StartTask( pTask );
}
//-------------------------------------
void CNPC_Surface::RunTask( const Task_t *pTask )
{
BaseClass::RunTask( pTask );
}
//-------------------------------------
Vector CNPC_Surface::EyePosition( )
{
return GetAbsOrigin();
}
const QAngle &CNPC_Surface::EyeAngles()
{
return GetAbsAngles();
}
Vector CNPC_Surface::BodyTarget( const Vector &posSrc, bool bNoisy)
{
int iShortest = 0;
float flShortestDist = (posSrc - m_vecSurfacePos[iShortest]).LengthSqr();
for (int i = 1; i < m_nActiveParticles; i++)
{
if (m_flSurfaceR[i] > 0.0f)
{
float flDist = (posSrc - m_vecSurfacePos[i]).LengthSqr();
if (flDist < flShortestDist)
{
iShortest = i;
flShortestDist = flDist;
}
}
}
// NDebugOverlay::Box(m_body[iShortest].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 0, 255, 20, .1);
return m_vecSurfacePos[iShortest];
}
#if !defined(_X360) && defined( USE_BLOBULATOR )
//-------------------------------------
static ConVar lj_InteractionRadius( "lj_InteractionRadius", "3", 0 );
static ConVar lj_SurfaceTension( "lj_SurfaceTension", "1", 0 );
static ConVar lj_Repulsion( "lj_Repulsion", "0.1", 0 );
static ConVar lj_Attraction( "lj_Attraction", "0.1", 0 );
static ConVar lj_MaxRepulsion( "lj_MaxRepulsion", "1", 0 );
static ConVar lj_MaxAttraction( "lj_MaxAttraction", "1", 0 );
CLennardJonesForce::CLennardJonesForce( )
{
m_fInteractionRadius = lj_InteractionRadius.GetFloat();
m_fSurfaceTension = lj_SurfaceTension.GetFloat();
m_fLennardJonesRepulsion = lj_Repulsion.GetFloat();
m_fLennardJonesAttraction = lj_Attraction.GetFloat();
m_fMaxRepulsion = lj_MaxRepulsion.GetFloat();
m_fMaxAttraction = lj_MaxAttraction.GetFloat();
m_pPhysTiler = new PhysTiler(m_fInteractionRadius);
}
CLennardJonesForce::~CLennardJonesForce( )
{
delete m_pPhysTiler;
}
// TODO: I should make sure I don't have divide by zero errors.
// TODO: ts is not used
void CLennardJonesForce::addParticleForce(ImpParticle* a, ImpParticle* b, float distSq, float flStrength, float ts) const
{
float d = sqrtf(distSq);
//========================================================
// based on equation of force between two molecules which is
// factor * ((distance/bond_length)^-7 - (distance/bond_length)^-13)
float f;
if(a->group == b->group) // In the same group
{
float p = a->radius * 2.0f / (d+FLT_EPSILON);
float p2 = p * p;
float p4 = p2 * p2;
// Surface tension:
//Notes:
// Can average the neighbor count between the two particles...
// I tried this, and discovered that rather than averaging, I can take maybe take the
// larger of the two neighbor counts, so the attraction between two particles on the surface will be strong, but
// the attraction between a particle inside and a particle on the surface will be weak. I can also try
// taking the min so that the attraction between a particle on the surface and a particle inside the fluid will
// be strong, but the attraction between two particles completely on the inside will be weak.
//
// int symmetric_neighbor_count = min(a->neighbor_count, b->neighbor_count);
//
// Can try having neighbors only cause stronger attraction (no repulsion)
// Can try lower exponents for the LennardJones forces.
// This is a trick to prevent single particles from floating off... the less neighbors a particle has.. the more it sticks
// This also tends to simulate surface tension
float surface_tension_modifier = ((24.0f * m_fSurfaceTension) / (a->neighbor_count + b->neighbor_count + 0.1f)) + 1.0f;
//float lennard_jones_force = fLennardJones * 2.0f * (p2 - (p4 * p4));
float lennard_jones_force = m_fLennardJonesAttraction * p2 - m_fLennardJonesRepulsion*p4;
f = surface_tension_modifier * lennard_jones_force;
// This is some older code:
//f = ((35.0f * LampScene::simulationSurfaceTension) / (a->neighbor_count + 0.1f)) * (p2 - (p4 * p4));
// used to be 68'
//float factor = (b->neighbor_count < 13 && neighbor_count < 13 ? 4.0f : 0.5f);
//f = factor * (p2 - (p2 * p2 * p2 * p2));
}
else
{
// This was 3.5 ... made 3.0 so particles get closer when they collide
if(d > a->radius * 3.0f) return;
float p = a->radius * 4.0f / d;
f = -1.0f * p * p;
}
// These checks are great to have, but are they really necessary?
// It might also be good to have a limit on velocity
// Attraction is a positive value.
// Repulsion is negative.
if(f < -m_fMaxRepulsion) f = -m_fMaxRepulsion;
if(f > m_fMaxAttraction) f = m_fMaxAttraction;
Point3D scaledr = (b->center - a->center) * (f/(d+FLT_EPSILON)) * flStrength; // Dividing by d scales distance down to a unit vector
a->force.add(scaledr);
b->force.subtract(scaledr);
}
void CLennardJonesForce::AddForces( IPhysicsObject **pObject, int nObjects, float flRadius, float flStrength, Vector *pForces )
{
int nParticles = nObjects;
// hack: copy cvars into settings so it can be edited live
m_fInteractionRadius = lj_InteractionRadius.GetFloat();
m_fSurfaceTension = lj_SurfaceTension.GetFloat();
m_fLennardJonesRepulsion = lj_Repulsion.GetFloat();
m_fLennardJonesAttraction = lj_Attraction.GetFloat();
m_fMaxRepulsion = lj_MaxRepulsion.GetFloat();
m_fMaxAttraction = lj_MaxAttraction.GetFloat();
// FIXME: this isn't thread safe
static SmartArray<ImpParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
while(imp_particles_sa.size < nObjects)
{
imp_particles_sa.pushAutoSize(ImpParticle());
}
// centered and scaled?
m_pPhysTiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f));
// Move the spheres into particles
for(int i=0;i<nObjects;i++)
{
ImpParticle* particle = &(imp_particles_sa[i]);
particle->force.clear();
Vector pos;
QAngle ang;
pObject[i]->GetPosition( &pos, &ang );
particle->center = pos * (1.0 / flRadius);
particle->group = i/20;
particle->neighbor_count = 0;
m_pPhysTiler->insertParticle(particle);
}
m_pPhysTiler->processTiles();
float timeStep = 1.0f; // This should be customizable
float nearNeighborInteractionRadius = 2.3f;
float nearNeighborInteractionRadiusSq = nearNeighborInteractionRadius * nearNeighborInteractionRadius;
PhysParticleCache* pCache = m_pPhysTiler->getParticleCache();
// Calculate number of near neighbors for each particle
for(int i = 0; i < nParticles; i++)
{
ImpParticle *b1 = &(imp_particles_sa[i]);
ImpParticleAndDist* node = pCache->get(b1);
while(node->particle != NULL)
{
ImpParticle* b2 = node->particle;
// Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
if(b1 < b2 && node->distSq < nearNeighborInteractionRadiusSq)
{
b1->neighbor_count++;
b2->neighbor_count++;
}
node++;
}
}
// Calculate forces on particles due to other particles
for(int i = 0; i < nParticles; i++)
{
ImpParticle *b1 = &(imp_particles_sa[i]);
ImpParticleAndDist* node = pCache->get(b1);
while(node->particle != NULL)
{
ImpParticle* b2 = node->particle;
// Compare addresses of the two particles. This makes sure we apply a force only once between a pair of particles.
if(b1 < b2)
{
addParticleForce(b1, b2, node->distSq, flStrength, timeStep);
}
node++;
}
}
m_pPhysTiler->endFrame();
// forces into output array
for(int i=0;i<nObjects;i++)
{
pForces[i] = imp_particles_sa[i].force.AsVector() * flRadius;
}
}
//-----------------------------------------------------------------------------
//
// CNPC_Surface
//
LINK_ENTITY_TO_CLASS( npc_blob_fountain, CNPC_BlobFountain );
#if 1
IMotionEvent::simresult_e CBlobFountainController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
if(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
{
pObject->EnableMotion(false);
return IMotionEvent::SIM_NOTHING;
}
//if(m_pOwner->m_bPause) return IMotionEvent::SIM_NOTHING;
//if(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) return IMotionEvent::SIM_NOTHING;
/*
bool newPause = CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI;
if
if(newPause != m_bPause)
{
if(newPause)
{
for (int i = 0; i < m_nActiveParticles; i++)
{
m_pSpheres[i]->EnableMotion(false);
}
}
else
{
for (int i = 0; i < m_nActiveParticles; i++)
{
m_pSpheres[i]->EnableMotion(true);
}
}
m_bPause = newPause;
}
if(m_bPause) return;
*/
int nSphere = pObject->GetGameIndex();
#if 0
if (m_pOwner && m_flLennardJonesTime != gpGlobals->curtime)
{
m_pOwner->Simulate( pController, pObject, deltaTime, linear, angular );
m_flLennardJonesTime = gpGlobals->curtime;
}
linear += m_vecLennardJonesForce[nSphere] * (1.0f / deltaTime);
#endif
#if 1
Vector pos, vecVel;
QAngle ang;
pObject->GetPosition( &pos, NULL );
pObject->GetVelocity( &vecVel, NULL );
float d = sv_surface_radius.GetFloat();
Vector nozzle = m_pOwner->GetNozzle();
Vector start = nozzle + Vector(0.0f,0.0f,d);
pos = pos - nozzle;
float dist = pos.Length();
if(m_pOwner->m_iMode[nSphere] == 0)
{
if (dist < 10.0f)
{
if(vecVel.z < 0.1f && abs(vecVel.x) < 0.05f && abs(vecVel.y) < 0.05f)
{
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_fRadius[nSphere] = 0.5f;
m_pOwner->m_iMode[nSphere] = 1;
m_pOwner->m_bContact[nSphere] = false;
//linear.z += 10000 * (1.0f / deltaTime);
//linear.z = 500000.0f;
//linear = -vecVel / deltaTime;
float v = 6000.0f / deltaTime;
float azimuth = (2.0f * M_PI * (float(rand()) / float(RAND_MAX)));
float polar = 0.04f * M_PI + (0.01f * (float(rand()) / float(RAND_MAX)));
//static float azimuth = 0.0f;
//azimuth += (0.05f * M_PI);
//float cur_azimuth = azimuth + RandomFloat(-0.01, 0.01);
linear.x = v * cos(azimuth) * sin(polar);
linear.y = v * sin(azimuth) * sin(polar);
linear.z = v * cos(polar);
}
//else
//{
// pObject->SetVelocity( &Vector(0.0f, 0.0f, 0.0f), NULL );
//}
}
}
else if(m_pOwner->m_iMode[nSphere] == 1)
{
m_pOwner->m_fRadius[nSphere] = min(1.0f, m_pOwner->m_fRadius[nSphere] + 3.0f*deltaTime);
if(m_pOwner->m_bContact[nSphere]) // vecVel.z < 0.1f &&
{
if(dist < 80.0f)
{
m_pOwner->m_fRadius[nSphere] = 0.0f;
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector zero(0.0f, 0.0f, 0.0f);
pObject->SetVelocity( &zero, NULL );
m_pOwner->m_iMode[nSphere] = 0;
m_pOwner->m_iContactTime[nSphere] = 0;
}
else
{
m_pOwner->m_fRadius[nSphere] = 0.0f; //max(0.0f, m_pOwner->m_fRadius[nSphere] - 2.0f*deltaTime);
m_pOwner->m_iContactTime[nSphere] += deltaTime;
if(m_pOwner->m_iContactTime[nSphere] >= 1.0f)
{
m_pOwner->m_iMode[nSphere] = 2;
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_bContact[nSphere] = false;
}
}
}
}
else if(m_pOwner->m_iMode[nSphere] == 2)
{
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector zero(0.0f, 0.0f, 0.0f);
pObject->SetVelocity( &zero, NULL );
//Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
//pObject->SetVelocity( &vVelocity, NULL );
m_pOwner->m_iContactTime[nSphere] += deltaTime;
if(m_pOwner->m_iContactTime[nSphere] >= 1.0f)
{
m_pOwner->m_iMode[nSphere] = 0;
m_pOwner->m_iContactTime[nSphere] = 0;
}
}
#if 0
if(m_pOwner->m_bContact[nSphere]) // || (vecVel.z < -20.0f && pos.z < 40.0f))
{
m_pOwner->m_flSurfaceR[nSphere] = 0.0f; //max(0.0f, m_pOwner->m_flSurfaceR[nSphere] - 0.1f);
m_pOwner->m_iContactTime[nSphere] ++;
//if(m_pOwner->m_flSurfaceR[nSphere] <= 0.0f)
if(m_pOwner->m_iContactTime[nSphere] >= 10)
{
m_pOwner->m_bContact[nSphere] = false;
if(dist > 10.0f)
{
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
pObject->SetVelocity( &vVelocity, NULL );
//m_pOwner->m_flSurfaceR[nSphere] = 0.1f
//Vector vVelocity(0.0f, 0.0f, 0.0f);
//pObject->SetVelocity( &vVelocity, NULL );
}
}
}
else
{
//pObject->SetVelocity( &Vector(), &AngularImpulse());
if (dist < 10.0f)
{
if(vecVel.z < 0.1f && abs(vecVel.x) < 0.1f && abs(vecVel.y) < 0.1f)
{
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_flSurfaceR[nSphere] = 1.0f;
//linear.z += 10000 * (1.0f / deltaTime);
//linear.z = 500000.0f;
//linear = -vecVel / deltaTime;
float v = 6000.0f / deltaTime;
float azimuth = (2.0f * M_PI * (float(rand()) / float(RAND_MAX)));
float polar = 0.03f * M_PI; //(0.03f * M_PI * (float(rand()) / float(RAND_MAX))); //
linear.x = v * cos(azimuth) * sin(polar);
linear.y = v * sin(azimuth) * sin(polar);
linear.z = v * cos(polar);
}
}
}
#endif
#endif
return IMotionEvent::SIM_GLOBAL_FORCE;
}
void CNPC_BlobFountain::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
m_force.AddForces( m_pSpheres, m_nActiveParticles, m_flRadius, sv_lj_strength.GetFloat(), m_pBlobFountainController->m_vecLennardJonesForce );
//NetworkProp()->NetworkStateForceUpdate();
}
bool CNPC_BlobFountain::CreateVPhysics( bool bFromRestore )
{
objectparams_t params = g_PhysDefaultObjectParams;
params.pGameData = static_cast<void *>(this);
int nMaterialIndex = physprops->GetSurfaceIndex("water");
// FIXME: don't hardcode the number of particles
m_nActiveParticles = 1000;
//bool result = BaseClass::CreateVPhysics( bFromRestore );
m_pBlobFountainController = new CBlobFountainController( this );
m_pMotionController = physenv->CreateMotionController( m_pBlobFountainController );
// find the ground under the starting position
trace_t tr;
UTIL_TraceLine( m_vecStart+Vector(0,0,1), m_vecStart-Vector(0,0,64), MASK_SOLID_BRUSHONLY | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, this, COLLISION_GROUP_NONE, &tr );
m_vecStart = tr.endpos;
int i;
for (i = 0; i < m_nActiveParticles; i++)
{
m_vecSurfacePos[i] = m_vecStart + Vector(0,0,10.0f);
m_pSpheres[i] = physenv->CreateSphereObject( m_flRadius, nMaterialIndex, m_vecSurfacePos[i], GetAbsAngles(), &params, false );
if ( m_pSpheres[i] )
{
Vector vVelocity = Vector( RandomFloat( -1, 1 ), RandomFloat( -1, 1 ), RandomFloat( -1, 1 ) ) * 10.0f;
m_pSpheres[i]->SetVelocity( &vVelocity, NULL );
//PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_MULTIOBJECT_ENTITY );
PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_MULTIOBJECT_ENTITY ); // call collisionruleschanged if this changes dynamically
m_pSpheres[i]->SetGameIndex( i );
m_pSpheres[i]->SetMass( 10.0f );
m_pSpheres[i]->EnableGravity( true );
m_pSpheres[i]->EnableDrag( true );
// m_pSpheres[i]->EnableMotion( false );
float flDamping = 0.5f;
float flAngDamping = 0.5f;
m_pSpheres[i]->SetDamping( &flDamping, &flAngDamping );
//m_pSpheres[i]->SetInertia( Vector( 1e30, 1e30, 1e30 ) );
m_pSpheres[i]->EnableGravity( true );
//m_pSpheres[i]->SetPosition(m_vecStart + Vector(0,0,10.0f), QAngle(0.0f, 0.0f, 0.0f), false);
m_pMotionController->AttachObject( m_pSpheres[i], true );
m_iContactTime[i] = 0;
m_iMode[i] = 0;
// 0: ready to launch, 1: launched, 2: contact
}
}
return true;
}
void CNPC_BlobFountain::RunAI( void )
{
/*
bool newPause = ((CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) != 0);
//(sv_lj_strength.GetFloat() > 0.0f);
if(newPause != m_bPause)
{
if(newPause)
{
for (int i = 0; i < m_nActiveParticles; i++)
{
m_pSpheres[i]->EnableMotion(false);
}
}
else
{
for (int i = 0; i < m_nActiveParticles; i++)
{
m_pSpheres[i]->EnableMotion(true);
}
}
m_bPause = newPause;
}
if(m_bPause) return;
*/
m_pMotionController->WakeObjects();
// push spheres around to meet position targets
//Vector vecGoal = m_vecStart;
for (int i = 0; i < m_nActiveParticles; i++)
{
m_pSpheres[i]->EnableMotion(true);
//m_pSpheres[i]->EnableGravity( true );
{
Vector pos;
m_pSpheres[i]->GetPosition( &pos, NULL );
m_vecSurfacePos[i] = pos;
m_flSurfaceR[i] = m_fRadius[i];
}
#if 0
if (m_bContact[i])
{
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel;
Vector delta( 0, 0, 0 );
float dist(0.0f);
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), m_bFloat[i] * 255, 255, m_bContact[i] * 255, 20, .1);
// move sphere towards center target
delta = vecGoal - estPos;
dist = VectorNormalize( delta );
// delta = delta * min( max( dist - m_flRadius * 4, 0 ), 500 ) * sv_surface_tension.GetFloat();
delta = delta * min( dist, 100 ) * 0.2; // sv_surface_tension.GetFloat();
/*
if(dist > 10.0f)
{
m_pSpheres[i]->SetPosition(m_vecStart + Vector(0,0,m_flRadius), QAngle(), true);
Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
m_pSpheres[i]->SetVelocity( &vVelocity, NULL );
}
m_bContact[i] = false;
*/
/*
if(dist < 10.0f)
{
m_pSpheres[i]->ApplyForceCenter( Vector(0.0f, 0.0f, 10000.0f) );
}
*/
/*
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
m_pSpheres[i]->ApplyForceCenter( delta );
*/
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
}
#endif
}
NetworkProp()->NetworkStateForceUpdate();
}
#else
IMotionEvent::simresult_e CBlobFountainController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
int nSphere = pObject->GetGameIndex();
if (m_pOwner && m_flLennardJonesTime != gpGlobals->curtime)
{
m_pOwner->Simulate( pController, pObject, deltaTime, linear, angular );
m_flLennardJonesTime = gpGlobals->curtime;
}
linear += m_vecLennardJonesForce[nSphere] * (1.0f / deltaTime);
Vector pos, vecVel;
//QAngle ang;
pObject->GetPosition( &pos, NULL );
pObject->GetVelocity( &vecVel, NULL );
//float d = sv_surface_radius.GetFloat();
Vector nozzle = m_pOwner->GetNozzle();
pos = pos - nozzle;
float dist = pos.Length();
pos.NormalizeInPlace();
/*
if(nSphere <= 200)
{
if(dist >= 150.0f)
{
linear -= pos * ((dist-150.0f) / deltaTime);
}
}
else
{
*/
if(dist >= 500.0f)
{
linear -= pos * ((dist-500.0f) / deltaTime);
}
//}
float v = vecVel.Length();
linear -= vecVel * (1.0f / ((v+FLT_EPSILON) * deltaTime));
#if 0
Vector pos, vecVel;
QAngle ang;
pObject->GetPosition( &pos, NULL );
pObject->GetVelocity( &vecVel, NULL );
float d = sv_surface_radius.GetFloat();
Vector nozzle = m_pOwner->GetNozzle();
Vector start = nozzle + Vector(0.0f,0.0f,d);
pos = pos - nozzle;
float dist = pos.Length();
if(m_pOwner->m_iMode[nSphere] == 0)
{
if (dist < 10.0f)
{
if(vecVel.z < 0.1f && abs(vecVel.x) < 0.05f && abs(vecVel.y) < 0.05f)
{
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_fRadius[nSphere] = 0.5f;
m_pOwner->m_iMode[nSphere] = 1;
m_pOwner->m_bContact[nSphere] = false;
//linear.z += 10000 * (1.0f / deltaTime);
//linear.z = 500000.0f;
//linear = -vecVel / deltaTime;
float v = 6000.0f / deltaTime;
float azimuth = (2.0f * M_PI * (float(rand()) / float(RAND_MAX)));
float polar = 0.04f * M_PI + (0.01f * (float(rand()) / float(RAND_MAX)));
//static float azimuth = 0.0f;
//azimuth += (0.05f * M_PI);
//float cur_azimuth = azimuth + RandomFloat(-0.01, 0.01);
linear.x = v * cos(azimuth) * sin(polar);
linear.y = v * sin(azimuth) * sin(polar);
linear.z = v * cos(polar);
}
//else
//{
// pObject->SetVelocity( &Vector(0.0f, 0.0f, 0.0f), NULL );
//}
}
}
else if(m_pOwner->m_iMode[nSphere] == 1)
{
m_pOwner->m_fRadius[nSphere] = min(1.0f, m_pOwner->m_fRadius[nSphere] + 3.0f*deltaTime);
if(m_pOwner->m_bContact[nSphere]) // vecVel.z < 0.1f &&
{
if(dist < 80.0f)
{
m_pOwner->m_fRadius[nSphere] = 0.0f;
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector zero(0.0f, 0.0f, 0.0f);
pObject->SetVelocity( &zero, NULL );
m_pOwner->m_iMode[nSphere] = 0;
m_pOwner->m_iContactTime[nSphere] = 0;
}
else
{
m_pOwner->m_fRadius[nSphere] = 0.0f; //max(0.0f, m_pOwner->m_fRadius[nSphere] - 2.0f*deltaTime);
m_pOwner->m_iContactTime[nSphere] += deltaTime;
if(m_pOwner->m_iContactTime[nSphere] >= 1.0f)
{
m_pOwner->m_iMode[nSphere] = 2;
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_bContact[nSphere] = false;
}
}
}
}
else if(m_pOwner->m_iMode[nSphere] == 2)
{
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector zero(0.0f, 0.0f, 0.0f);
pObject->SetVelocity( &zero, NULL );
//Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
//pObject->SetVelocity( &vVelocity, NULL );
m_pOwner->m_iContactTime[nSphere] += deltaTime;
if(m_pOwner->m_iContactTime[nSphere] >= 1.0f)
{
m_pOwner->m_iMode[nSphere] = 0;
m_pOwner->m_iContactTime[nSphere] = 0;
}
}
#if 0
if(m_pOwner->m_bContact[nSphere]) // || (vecVel.z < -20.0f && pos.z < 40.0f))
{
m_pOwner->m_flSurfaceR[nSphere] = 0.0f; //max(0.0f, m_pOwner->m_flSurfaceR[nSphere] - 0.1f);
m_pOwner->m_iContactTime[nSphere] ++;
//if(m_pOwner->m_flSurfaceR[nSphere] <= 0.0f)
if(m_pOwner->m_iContactTime[nSphere] >= 10)
{
m_pOwner->m_bContact[nSphere] = false;
if(dist > 10.0f)
{
pObject->SetPosition(nozzle, QAngle(0.0f, 0.0f, 0.0f), true);
Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
pObject->SetVelocity( &vVelocity, NULL );
//m_pOwner->m_flSurfaceR[nSphere] = 0.1f
//Vector vVelocity(0.0f, 0.0f, 0.0f);
//pObject->SetVelocity( &vVelocity, NULL );
}
}
}
else
{
//pObject->SetVelocity( &Vector(), &AngularImpulse());
if (dist < 10.0f)
{
if(vecVel.z < 0.1f && abs(vecVel.x) < 0.1f && abs(vecVel.y) < 0.1f)
{
m_pOwner->m_iContactTime[nSphere] = 0;
m_pOwner->m_flSurfaceR[nSphere] = 1.0f;
//linear.z += 10000 * (1.0f / deltaTime);
//linear.z = 500000.0f;
//linear = -vecVel / deltaTime;
float v = 6000.0f / deltaTime;
float azimuth = (2.0f * M_PI * (float(rand()) / float(RAND_MAX)));
float polar = 0.03f * M_PI; //(0.03f * M_PI * (float(rand()) / float(RAND_MAX))); //
linear.x = v * cos(azimuth) * sin(polar);
linear.y = v * sin(azimuth) * sin(polar);
linear.z = v * cos(polar);
}
}
}
#endif
#endif
return IMotionEvent::SIM_GLOBAL_FORCE;
}
void CNPC_BlobFountain::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
m_force.AddForces( m_pSpheres, m_nActiveParticles, m_flRadius * 3.0f, sv_lj_strength.GetFloat(), m_pBlobFountainController->m_vecLennardJonesForce );
//NetworkProp()->NetworkStateForceUpdate();
}
bool CNPC_BlobFountain::CreateVPhysics( bool bFromRestore )
{
objectparams_t params = g_PhysDefaultObjectParams;
params.pGameData = static_cast<void *>(this);
int nMaterialIndex = physprops->GetSurfaceIndex("water");
// FIXME: don't hardcode the number of particles
m_nActiveParticles = 300;
//bool result = BaseClass::CreateVPhysics( bFromRestore );
m_pBlobFountainController = new CBlobFountainController( this );
m_pMotionController = physenv->CreateMotionController( m_pBlobFountainController );
// find the ground under the starting position
trace_t tr;
UTIL_TraceLine( m_vecStart+Vector(0,0,1), m_vecStart-Vector(0,0,64), MASK_SOLID_BRUSHONLY | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, this, COLLISION_GROUP_NONE, &tr );
m_vecStart = tr.endpos;
m_vecStart.z += 600.0f;
for (int i = 0; i < m_nActiveParticles; i++)
{
m_vecSurfacePos[i] = m_vecStart + Vector( RandomFloat( -1, 1 ), RandomFloat( -1, 1 ), RandomFloat( -1, 1 ) ) * 400.0f;;
m_pSpheres[i] = physenv->CreateSphereObject( m_flRadius, nMaterialIndex, m_vecSurfacePos[i], GetAbsAngles(), &params, false );
if ( m_pSpheres[i] )
{
Vector vVelocity = Vector( RandomFloat( -1, 1 ), RandomFloat( -1, 1 ), RandomFloat( -1, 1 ) ) * 0.0f;
m_pSpheres[i]->SetVelocity( &vVelocity, NULL );
//PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_MULTIOBJECT_ENTITY );
PhysSetGameFlags( m_pSpheres[i], FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_MULTIOBJECT_ENTITY ); // call collisionruleschanged if this changes dynamically
m_pSpheres[i]->SetGameIndex( i );
m_pSpheres[i]->SetMass( 10.0f );
m_pSpheres[i]->EnableGravity( false );
m_pSpheres[i]->EnableDrag( true );
float drag = 100.0f;
m_pSpheres[i]->SetDragCoefficient(&drag, &drag);
m_pSpheres[i]->EnableCollisions(false);
// m_pSpheres[i]->EnableMotion( false );
float flDamping = 0.5f;
float flAngDamping = 0.5f;
m_pSpheres[i]->SetDamping( &flDamping, &flAngDamping );
//m_pSpheres[i]->SetInertia( Vector( 1e30, 1e30, 1e30 ) );
//m_pSpheres[i]->EnableGravity( true );
//m_pSpheres[i]->SetPosition(m_vecStart + Vector(0,0,10.0f), QAngle(0.0f, 0.0f, 0.0f), false);
m_pMotionController->AttachObject( m_pSpheres[i], true );
m_iContactTime[i] = 0;
m_iMode[i] = 0;
m_fRadius[i] = 1.0f;
// 0: ready to launch, 1: launched, 2: contact
}
}
return true;
}
void CNPC_BlobFountain::RunAI( void )
{
m_pMotionController->WakeObjects();
// push spheres around to meet position targets
//Vector vecGoal = m_vecStart;
for (int i = 0; i < m_nActiveParticles; i++)
{
//m_pSpheres[i]->EnableGravity( true );
{
Vector pos;
m_pSpheres[i]->GetPosition( &pos, NULL );
m_vecSurfacePos[i] = pos;
m_flSurfaceR[i] = m_fRadius[i];
}
#if 0
if (m_bContact[i])
{
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel;
Vector delta( 0, 0, 0 );
float dist(0.0f);
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), m_bFloat[i] * 255, 255, m_bContact[i] * 255, 20, .1);
// move sphere towards center target
delta = vecGoal - estPos;
dist = VectorNormalize( delta );
// delta = delta * min( max( dist - m_flRadius * 4, 0 ), 500 ) * sv_surface_tension.GetFloat();
delta = delta * min( dist, 100 ) * 0.2; // sv_surface_tension.GetFloat();
/*
if(dist > 10.0f)
{
m_pSpheres[i]->SetPosition(m_vecStart + Vector(0,0,m_flRadius), QAngle(), true);
Vector vVelocity = Vector( RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ), RandomFloat( -1.0f, 1.0f ) ) * 0.2f;
m_pSpheres[i]->SetVelocity( &vVelocity, NULL );
}
m_bContact[i] = false;
*/
/*
if(dist < 10.0f)
{
m_pSpheres[i]->ApplyForceCenter( Vector(0.0f, 0.0f, 10000.0f) );
}
*/
/*
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
m_pSpheres[i]->ApplyForceCenter( delta );
*/
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
}
#endif
}
NetworkProp()->NetworkStateForceUpdate();
}
#endif
//-----------------------------------------------------------------------------
//
// CNPC_BlobArmTest
//
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_BlobArmTest )
DEFINE_AUTO_ARRAY( m_nOwnedSlot, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_nTargetSlot, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_bFloat, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bDoArms, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_flSimTime, FIELD_TIME ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_surface, CNPC_BlobArmTest );
LINK_ENTITY_TO_CLASS( npc_blob_armtest, CNPC_BlobArmTest );
void CNPC_BlobArmTest::Spawn( void )
{
BaseClass::Spawn( );
m_flSimTime = 0;
m_bDoArms = false;
}
bool CNPC_BlobArmTest::CreateVPhysics( bool bFromRestore )
{
// FIXME: don't hardcode the number of particles
m_nActiveParticles = 128;
bool result = BaseClass::CreateVPhysics( bFromRestore );
return result;
}
void CNPC_BlobArmTest::RunAI( void )
{
float flIdealDistance = m_flRadius * sv_surface_ideal.GetFloat();
float flNearbyDistance = m_flRadius * sv_surface_nearby.GetFloat();
int i, j;
// copy physics positions into networked array
for (i = 0; i < m_nActiveParticles; i++)
{
Vector pos;
QAngle ang;
m_pSpheres[i]->GetPosition( &pos, &ang );
m_vecSurfacePos[i] = pos;
}
int nArmLength = MoveTowardsGoal( );
// FIXME: this isn't thread safe
static SmartArray<ImpParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
while(imp_particles_sa.size < m_nActiveParticles)
{
imp_particles_sa.pushAutoSize(ImpParticle());
}
PhysTiler *m_pPhysTiler = new PhysTiler(sv_surface_nearby.GetFloat());
// centered and scaled?
m_pPhysTiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f));
int nParticles = 0;
// Move the spheres into particles
float projection = 0.5;
for(i = 0; i < m_nActiveParticles; i++)
{
if (m_flSurfaceR[i] > 0.0f)
{
ImpParticle* particle = &(imp_particles_sa[nParticles++]);
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel * projection;
particle->center = estPos * (1.0 / m_flRadius);
particle->group = 0;
particle->neighbor_count = 0;
particle->temp1 = i;
m_pPhysTiler->insertParticle(particle);
}
}
m_pPhysTiler->processTiles();
PhysParticleCache* pCache = m_pPhysTiler->getParticleCache();
for (int k = 0; k < nParticles; k++)
{
ImpParticle *b1 = &(imp_particles_sa[k]);
i = b1->temp1;
Vector estPos = b1->center.AsVector() * m_flRadius;
Vector delta( 0, 0, 0 );
// push against nearby spheres
float flIdealDist2 = (flIdealDistance * flIdealDistance);
float flNearbyDist2 = (flNearbyDistance * flNearbyDistance);
bool bFloat = false;
float flDist2;
Vector dir;
ImpParticleAndDist* node = pCache->get(b1);
while(node->particle != NULL)
{
ImpParticle* b2 = node->particle;
if (b2 == b1)
{
node++;
continue;
}
j = b2->temp1;
int bSameArm = (m_nArm[i] == m_nArm[j]) && (m_nArm[i] > -1);
Vector estEffectorPos = b2->center.AsVector() * m_flRadius;
flDist2 = (estPos - estEffectorPos).LengthSqr();
flNearbyDist2 = (m_flSurfaceR[i] + m_flSurfaceR[j]) * 0.5 * flNearbyDistance;
flNearbyDist2 = flNearbyDist2 * flNearbyDist2;
// NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 0, 255, 0, true, .1);
if (!bSameArm && m_nTargetSlot[j] != 0)
{
if (flDist2 < flIdealDist2)
{
// repluse if they're too close, and they're not in the same group, and they're on an arm
dir = (estPos - estEffectorPos);
VectorNormalize( dir );
delta += dir * min( (flIdealDist2 - flDist2), 100 );
//NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
/*
else if (flDist2 < flNearbyDist2)
{
dir = (estPos - m_vecSurfacePos[j] - vecVel2);
VectorNormalize( dir );
delta -= dir * 30;
NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
*/
// NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
if (!bFloat && flDist2 < flNearbyDist2 && ((m_vecSurfacePos[j].z <= m_vecSurfacePos[i].z) || (bSameArm && ((j % nArmLength) < (i % nArmLength)) && m_bFloat[j] ) ) )
{
//NDebugOverlay::Line(m_vecSurfacePos[j], m_vecSurfacePos[i], 0, 255, 0, true, .1);
bFloat = true;
}
node++;
}
// figure out what to do with gravity
if (!bFloat /* && (i % nArmLength == 0) */)
{
/*
if (delta.z > 0)
delta.z = 0;
delta.z -= 300 * 0.1 * m_pSpheres[i]->GetMass();
*/
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
}
m_pSpheres[i]->EnableGravity( !bFloat && !m_bContact[i] );
m_bFloat[i] = bFloat;
m_bContact[i] = false;
#if 0
// pull toward next in chain
if (i != 0 /* && i == ((int)gpGlobals->curtime) % m_vecSurfacePos.Count()*/)
{
dir = (m_vecSurfacePos[i-1] - estPos);
if (DotProduct( dir, vecVel ) < 0.0)
{
float dist = VectorNormalize( dir );
delta += dir * min( dist, 100 );
}
//NDebugOverlay::Line(m_vecSurfacePos[i-1], estPos, 0, 255, 0, true, .1);
//Msg("%d : %.1f %.1f %.1f\n", i, delta.x, delta.y, delta.z );
}
#endif
#if 0
float dot = DotProduct( vVel, delta );
if (dot > 0.0)
delta.Init();
#endif
// apply the force
m_pSpheres[i]->ApplyForceCenter( delta );
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
}
m_pPhysTiler->endFrame();
NetworkProp()->NetworkStateForceUpdate();
}
int CNPC_BlobArmTest::MoveTowardsGoal( void )
{
int i;
// alternate between arm mode and walk mode
bool bDoArms = ((int)(gpGlobals->curtime / 17.0) % 2) == 1;
if (bDoArms != m_bDoArms)
{
for (i = 0; i < m_nActiveParticles; i++)
{
m_nOwnedSlot[ i ] = 0;
m_nTargetSlot[ i ] = 0;
}
}
m_bDoArms = bDoArms;
int nArmLength = 1;
if (bDoArms)
{
nArmLength = 8;
}
else
{
m_flSimTime += 0.1;
}
Vector vecGoal( 0, sin( m_flSimTime * 0.2 ) * 250, 0 );
vecGoal = vecGoal + m_vecStart;
vecGoal.z = m_flRadius;
float tension = sv_surface_tension.GetFloat(); // * (1 - sqrt( fabs( sin( gpGlobals->curtime * 0.3 ) ) ) );
float projection = 0.5;
// push spheres around to meet position targets
for (i = 0; i < m_nActiveParticles; i++)
{
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel * projection;
Vector delta( 0, 0, 0 );
float dist(0.0f);
m_nArm[i] = (i / nArmLength);
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), m_bFloat[i] * 255, 255, m_bContact[i] * 255, 20, .1);
if (m_bFloat[i] || m_bContact[i] || fabs( vecVel.z ) < 1.0 )
{
int k = (i % nArmLength);
int j = i - k;
if (!bDoArms || i > m_nActiveParticles / 2)
{
m_nArm[i] = -1;
delta = vecGoal - estPos;
//delta.z *= fabs( cos( gpGlobals->curtime * 0.5 ) );
dist = VectorNormalize( delta );
delta = delta * min( max( dist - m_flRadius * (bDoArms ? 8 : 10), 0 ), 500 ) * tension;
m_nOwnedSlot[i] = 0;
m_nTargetSlot[i] = 1;
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
}
else if (k == 0)
{
// move sphere towards center target
delta = vecGoal - estPos;
//delta.z *= fabs( cos( gpGlobals->curtime * 0.5 ) );
dist = VectorNormalize( delta );
delta = delta * min( max( dist - m_flRadius * (bDoArms ? 8 : 10), 0 ), 500 ) * tension;
m_nOwnedSlot[i] = 0;
m_nTargetSlot[i] = 1;
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
}
else
{
// move sphere outward towards end of arm
Vector out = m_vecSurfacePos[j] - vecGoal;
VectorNormalize( out );
if (m_nTargetSlot[i] > m_nOwnedSlot[j] + 1)
{
m_nTargetSlot[i] = m_nOwnedSlot[j] + 1;
}
// Vector target = m_vecSurfacePos[j] + out * flIdealDistance * (m_nTargetSlot[i]);
Vector target = m_vecSurfacePos[j];
if (i != j)
{
Vector vecTargetVel;
m_pSpheres[i-1]->GetVelocity( &vecTargetVel, NULL );
target = m_vecSurfacePos[i-1] + vecTargetVel * 0.1 + out * m_flRadius * (m_flSurfaceR[i-1] + m_flSurfaceR[i]);
// Msg("%d : %.1f : %.1f %.1f\n", i, (m_flSurfaceR[i-1] + m_flSurfaceR[i]), m_flSurfaceR[i-1], m_flSurfaceR[i] ) ;
}
//target += Vector( 0, 0, m_flRadius ) * sin( j + gpGlobals->curtime * 3.0 + M_PI * (m_nTargetSlot[i] / (float)(nArmLength-1)) );
delta = target - estPos;
//NDebugOverlay::Line(m_vecSurfacePos[i], estPos + delta, 0, 255, 0, true, .1);
//NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j] + out * flIdealDistance * (m_nTargetSlot[i]), 0, 255, 0, true, .1);
dist = VectorNormalize( delta );
if (dist < m_flRadius * m_flSurfaceR[i])
{
if (m_nTargetSlot[i] == k)
{
m_nOwnedSlot[j] = max( m_nOwnedSlot[j], k );
}
else
{
m_nTargetSlot[i]++;
}
}
else if (dist >= m_flRadius * m_flSurfaceR[i] * 2 && m_nTargetSlot[i] > 0)
{
m_nTargetSlot[i]--;
if (m_nOwnedSlot[j] > m_nTargetSlot[i])
{
m_nOwnedSlot[j] = m_nTargetSlot[i];
}
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
}
//NDebugOverlay::Line( m_vecSurfacePos[j], m_vecSurfacePos[j] + out * flIdealDistance * m_nOwnedSlot[j], 255, 0, 0, true, .1);
delta = delta * min( max( dist - 0, 0), 250 ) * m_pSpheres[i]->GetMass();
//NDebugOverlay::Line(m_vecSurfacePos[j], m_vecSurfacePos[j] + out * flIdealDistance, 255, 0, 0, true, .1);
// m_flSurfaceV[i] = clamp( Approach( (m_nTargetSlot[i]) / (float)max( m_nOwnedSlot[j] + 1, m_nTargetSlot[i]), m_flSurfaceV[i], 0.2f ), 0.0f, 1.0f );
m_flSurfaceV[i] = Approach( (m_nTargetSlot[i]) / (float)nArmLength, m_flSurfaceV[i], 0.2f );
m_flSurfaceV[i] = clamp( m_flSurfaceV[i], 0.0f, 1.0f );
}
}
// tweak radius
//float a = (m_bContact[i] || bFloat) ? 1.0 : sv_surface_scale.GetFloat();
float a = sv_surface_scale.GetFloat();
if (m_nTargetSlot[i] <= 1 ) a = 1.0;
if (nArmLength > 1 && m_nTargetSlot[i] >= nArmLength - 1) a = (1.0 + sv_surface_scale.GetFloat()) * 0.5;
float b = m_flSurfaceR[i];
float c = Approach( a, b, 0.2f );
m_flSurfaceR[i] = clamp( c, 0.0f, 1.0f );
if (!m_bFloat[i])
{
delta.z = 0;
}
// apply the force
m_pSpheres[i]->ApplyForceCenter( delta );
}
return nArmLength;
}
//-----------------------------------------------------------------------------
//
// CNPC_BlobDemoMonster
//
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_BlobDemoMonster )
DEFINE_AUTO_ARRAY( m_nOwnedSlot, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_nTargetSlot, FIELD_INTEGER ),
DEFINE_AUTO_ARRAY( m_bFloat, FIELD_BOOLEAN ),
DEFINE_AUTO_ARRAY( m_nArm, FIELD_INTEGER ),
DEFINE_FIELD( m_vecGoal, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_vecPrevGoal, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_bDoArms, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bDoContactZ, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_flSimTime, FIELD_TIME ),
// Inputs
DEFINE_INPUTFUNC( FIELD_STRING, "MoveToPosition", InputMoveToPosition ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "DoArms", InputDoArms ),
DEFINE_INPUTFUNC( FIELD_BOOLEAN, "DoContactZ", InputDoContactZ ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( npc_blob_demomonster, CNPC_BlobDemoMonster );
void CNPC_BlobDemoMonster::Spawn( void )
{
BaseClass::Spawn( );
m_flSimTime = 0;
m_bDoArms = false;
m_vecPrevGoal = GetAbsOrigin();
}
bool CNPC_BlobDemoMonster::CreateVPhysics( bool bFromRestore )
{
// FIXME: don't hardcode the number of particles
m_nActiveParticles = 256;
m_flRadius = 6.5;
bool result = BaseClass::CreateVPhysics( bFromRestore );
return result;
}
// Input handlers
void CNPC_BlobDemoMonster::InputMoveToPosition( inputdata_t &inputdata )
{
if ( inputdata.value.Entity() != NULL )
{
SetTarget( inputdata.value.Entity() );
}
else if ( inputdata.value.String() != NULL )
{
CBaseEntity *pTargetEnt = gEntList.FindEntityByName( NULL, inputdata.value.String() );
SetTarget( pTargetEnt );
}
}
void CNPC_BlobDemoMonster::InputDoArms( inputdata_t &inputdata )
{
m_bDoArms = inputdata.value.Bool();
}
void CNPC_BlobDemoMonster::InputDoContactZ( inputdata_t &inputdata )
{
m_bDoContactZ = inputdata.value.Bool();
}
int CNPC_BlobDemoMonster::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
if (info.GetDamageType() & (DMG_BULLET | DMG_CLUB | DMG_BLAST))
{
Vector dir = m_vecGoal - info.GetInflictor()->GetAbsOrigin();
VectorNormalize( dir );
m_vecPrevGoal = m_vecPrevGoal + dir * 8;
}
return BaseClass::OnTakeDamage_Alive( info );
}
void CNPC_BlobDemoMonster::RunAI( void )
{
int i;
// copy physics positions into networked array
for (i = 0; i < m_nActiveParticles; i++)
{
Vector pos;
QAngle ang;
m_pSpheres[i]->GetPosition( &pos, &ang );
m_vecSurfacePos[i] = pos;
}
#if 0
// alternate between arm mode and walk mode
bool bDoArms = ((int)(gpGlobals->curtime / 17.0) % 2) == 1;
if (bDoArms != m_bDoArms)
{
for (i = 0; i < m_nActiveParticles; i++)
{
m_nOwnedSlot[ i ] = 0;
m_nTargetSlot[ i ] = 0;
}
}
m_bDoArms = bDoArms;
#endif
int nArmLength = 1;
if (m_bDoArms)
{
nArmLength = 16;
m_flSimTime += 0.025;
}
else
{
m_flSimTime += 0.1;
}
Vector vecGoal = m_vecPrevGoal; // ( cos( m_flSimTime * 0.3 ) * 250, sin( m_flSimTime * 0.2 ) * 250, 0 );
if (GetTarget() != NULL)
{
vecGoal = GetTarget()->GetAbsOrigin();
}
// keep track of direction of movement
Vector forward = vecGoal - m_vecPrevGoal;
float dist = VectorNormalize( forward );
if (dist < 1.0)
{
forward *= dist;
}
else
{
vecGoal = m_vecPrevGoal + forward * min( dist, m_bDoArms ? 16 : 48 );
}
m_vecPrevGoal = m_vecPrevGoal * 0.8 + vecGoal * 0.2;
vecGoal.z = m_flRadius;
m_vecGoal = vecGoal;
if (!m_bDoArms)
{
MoveTowardsGoal( );
}
else
{
CreateArms( forward );
}
RepulseNeighbors( );
}
void CNPC_BlobDemoMonster::RepulseNeighbors()
{
float flIdealDistance = m_flRadius * sv_surface_ideal.GetFloat();
float flNearbyDistance = m_flRadius * sv_surface_nearby.GetFloat();
int i, j;
// FIXME: this isn't thread safe
static SmartArray<ImpParticle> imp_particles_sa; // This doesn't specify alignment, might have problems with SSE
while(imp_particles_sa.size < m_nActiveParticles)
{
imp_particles_sa.pushAutoSize(ImpParticle());
}
PhysTiler *m_pPhysTiler = new PhysTiler(sv_surface_nearby.GetFloat());
// centered and scaled?
m_pPhysTiler->beginFrame(Point3D(0.0f, 0.0f, 0.0f));
int nParticles = 0;
// Move the spheres into particles
float projection = 0.5;
for(i = 0; i < m_nActiveParticles; i++)
{
if (m_flSurfaceR[i] > 0.0f)
{
ImpParticle* particle = &(imp_particles_sa[nParticles++]);
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel * projection;
particle->center = estPos * (1.0 / m_flRadius);
particle->group = 0;
particle->neighbor_count = 0;
particle->temp1 = i;
m_pPhysTiler->insertParticle(particle);
}
}
m_pPhysTiler->processTiles();
PhysParticleCache* pCache = m_pPhysTiler->getParticleCache();
for (int k = 0; k < nParticles; k++)
{
ImpParticle *b1 = &(imp_particles_sa[k]);
i = b1->temp1;
Vector estPos = b1->center.AsVector() * m_flRadius;
Vector delta( 0, 0, 0 );
// push against nearby spheres
float flIdealDist2 = (flIdealDistance * flIdealDistance);
float flNearbyDist2 = (flNearbyDistance * flNearbyDistance);
bool bFloat = false;
float flDist2;
Vector dir;
ImpParticleAndDist* node = pCache->get(b1);
while(node->particle != NULL)
{
ImpParticle* b2 = node->particle;
if (b2 == b1)
{
node++;
continue;
}
j = b2->temp1;
int bSameArm = (m_nArm[i] == m_nArm[j]) && (m_nArm[i] > -1);
Vector estEffectorPos = b2->center.AsVector() * m_flRadius;
flDist2 = (estPos - estEffectorPos).LengthSqr();
flNearbyDist2 = (m_flSurfaceR[i] + m_flSurfaceR[j]) * 0.5 * flNearbyDistance;
flNearbyDist2 = flNearbyDist2 * flNearbyDist2;
// NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 0, 255, 0, true, .1);
if (!bSameArm && m_nTargetSlot[j] != 0)
{
if (flDist2 < flIdealDist2)
{
// repluse if they're too close, and they're not in the same group, and they're on an arm
dir = (estPos - estEffectorPos);
VectorNormalize( dir );
delta += dir * min( (flIdealDist2 - flDist2), 100 );
//NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
/*
else if (flDist2 < flNearbyDist2)
{
dir = (estPos - m_vecSurfacePos[j] - vecVel2);
VectorNormalize( dir );
delta -= dir * 30;
NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
*/
// NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j], 255, 0, 0, true, .1);
}
if (!bFloat && flDist2 < flNearbyDist2 && ((m_vecSurfacePos[j].z <= m_vecSurfacePos[i].z) || (bSameArm && j < i) && m_bFloat[j] ) )
{
//NDebugOverlay::Line(m_vecSurfacePos[j], m_vecSurfacePos[i], 0, 255, 0, true, .1);
bFloat = true;
}
node++;
}
// figure out what to do with gravity
if (!bFloat /* && (i % nArmLength == 0) */)
{
/*
if (delta.z > 0)
delta.z = 0;
delta.z -= 300 * 0.1 * m_pSpheres[i]->GetMass();
*/
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
}
m_pSpheres[i]->EnableGravity( !bFloat && !m_bContact[i] );
m_bFloat[i] = bFloat;
m_bContact[i] = false;
#if 0
// pull toward next in chain
if (i != 0 /* && i == ((int)gpGlobals->curtime) % m_vecSurfacePos.Count()*/)
{
dir = (m_vecSurfacePos[i-1] - estPos);
if (DotProduct( dir, vecVel ) < 0.0)
{
float dist = VectorNormalize( dir );
delta += dir * min( dist, 100 );
}
//NDebugOverlay::Line(m_vecSurfacePos[i-1], estPos, 0, 255, 0, true, .1);
//Msg("%d : %.1f %.1f %.1f\n", i, delta.x, delta.y, delta.z );
}
#endif
#if 0
float dot = DotProduct( vVel, delta );
if (dot > 0.0)
delta.Init();
#endif
// apply the force
m_pSpheres[i]->ApplyForceCenter( delta );
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0, 255, 0, 20, .1);
}
m_pPhysTiler->endFrame();
NetworkProp()->NetworkStateForceUpdate();
}
void CNPC_BlobDemoMonster::MoveTowardsGoal( void )
{
int i;
float tension = sv_surface_tension.GetFloat(); // * (1 - sqrt( fabs( sin( gpGlobals->curtime * 0.3 ) ) ) );
float projection = 0.5;
// push spheres around to meet position targets
for (i = 0; i < m_nActiveParticles; i++)
{
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel * projection;
Vector delta( 0, 0, 0 );
float dist(0.0f);
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), m_bFloat[i] * 255, 255, m_bContact[i] * 255, 20, .1);
if (m_bFloat[i] || m_bContact[i] || fabs( vecVel.z ) < 1.0 )
{
m_nArm[i] = -1;
delta = m_vecGoal - estPos;
if (m_bDoContactZ)
delta.z = 0;
//delta.z *= fabs( cos( gpGlobals->curtime * 0.5 ) );
dist = VectorNormalize( delta );
delta = delta * min( max( dist - m_flRadius * 8, 0 ), 500 ) * tension;
m_nOwnedSlot[i] = 0;
m_nTargetSlot[i] = 1;
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
}
// tweak radius
//float a = (m_bContact[i] || bFloat) ? 1.0 : sv_surface_scale.GetFloat();
float a = 1.0f; // sv_surface_scale.GetFloat();
float b = m_flSurfaceR[i];
float c = Approach( a, b, 0.2f );
m_flSurfaceR[i] = clamp( c, 0.0f, 1.0f );
if (!(m_bFloat[i] || (m_bDoContactZ && m_bContact[i])))
{
delta.z = 0;
}
// apply the force
m_pSpheres[i]->ApplyForceCenter( delta );
}
}
void CNPC_BlobDemoMonster::CreateArms( const Vector &vecForward )
{
int i;
if (!m_bDoArms)
{
for (i = 0; i < m_nActiveParticles; i++)
{
m_nOwnedSlot[ i ] = 0;
m_nTargetSlot[ i ] = 0;
}
}
m_bDoArms = true;
// NDebugOverlay::HorzArrow( GetAbsOrigin(), GetAbsOrigin() + vecForward * 32, 8, 255, 255, 255, 255, true, 0.1 );
int nArmLength = 8;
float tension = sv_surface_tension.GetFloat(); // * (1 - sqrt( fabs( sin( gpGlobals->curtime * 0.3 ) ) ) );
float projection = 0.5;
// push spheres around to meet position targets
for (i = 0; i < m_nActiveParticles; i++)
{
Vector vecVel;
m_pSpheres[i]->GetVelocity( &vecVel, NULL );
Vector estPos = m_vecSurfacePos[i] + vecVel * projection;
Vector delta( 0, 0, 0 );
float dist(0.0f);
m_nArm[i] = (i / nArmLength);
if (!m_bFloat[i])
{
m_nTargetSlot[ i ] = 0;
}
// NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), m_bFloat[i] * 255, 255, m_bContact[i] * 255, 20, .1);
if (m_bFloat[i] || m_bContact[i] || fabs( vecVel.z ) < 1.0 )
{
int k = (i % nArmLength);
int j = i - k;
if (k == 0)
{
// move sphere towards center target
delta = m_vecGoal - estPos;
//delta.z *= fabs( cos( gpGlobals->curtime * 0.5 ) );
dist = VectorNormalize( delta );
delta = delta * min( max( dist - m_flRadius * 5, 0 ), 500 ) * tension;
if (m_bDoContactZ)
delta.z = 0;
m_nOwnedSlot[i] = 0;
m_nTargetSlot[i] = 1;
m_flSurfaceV[i] = Approach( 0.0f, m_flSurfaceV[i], 0.2f );
}
else
{
// move sphere outward towards end of arm
Vector out = m_vecSurfacePos[j] - m_vecGoal;
VectorNormalize( out );
if (m_nTargetSlot[i] > m_nOwnedSlot[j] + 1)
{
m_nTargetSlot[i] = m_nOwnedSlot[j] + 1;
}
// Vector target = m_vecSurfacePos[j] + out * flIdealDistance * (m_nTargetSlot[i]);
int n = min( j + m_nTargetSlot[i], i - 1);
Vector target = m_vecSurfacePos[j];
if (i != j)
{
Vector vecTargetVel;
m_pSpheres[i-1]->GetVelocity( &vecTargetVel, NULL );
Vector dir = out + 2 * vecForward * (n - j) / nArmLength;
VectorNormalize( dir );
target = m_vecSurfacePos[n] + vecTargetVel * 0.1f + dir * m_flRadius * (m_flSurfaceR[n] + m_flSurfaceR[i]);
// Msg("%d : %d : %.1f : %.1f %.1f\n", i, n, (m_flSurfaceR[j+n] + m_flSurfaceR[i]), m_flSurfaceR[j+n], m_flSurfaceR[i] ) ;
}
//target += Vector( 0, 0, m_flRadius ) * sin( j + gpGlobals->curtime * 3.0 + M_PI * (m_nTargetSlot[i] / (float)(nArmLength-1)) );
delta = target - estPos;
//NDebugOverlay::Line(m_vecSurfacePos[i], estPos + delta, 0, 255, 0, true, .1);
//NDebugOverlay::Line(m_vecSurfacePos[i], m_vecSurfacePos[j] + out * flIdealDistance * (m_nTargetSlot[i]), 0, 255, 0, true, .1);
dist = VectorNormalize( delta );
if (dist < m_flRadius * m_flSurfaceR[i])
{
if (m_nTargetSlot[i] == k)
{
m_nOwnedSlot[j] = max( m_nOwnedSlot[j], k );
}
else
{
m_nTargetSlot[i]++;
}
}
else if (dist >= m_flRadius * m_flSurfaceR[i] * 2 && m_nTargetSlot[i] > 0)
{
m_nTargetSlot[i]--;
if (m_nOwnedSlot[j] > m_nTargetSlot[i])
{
m_nOwnedSlot[j] = m_nTargetSlot[i];
}
//NDebugOverlay::Box(m_vecSurfacePos[i], Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255, 0, 0, 20, .1);
}
//NDebugOverlay::Line( m_vecSurfacePos[j], m_vecSurfacePos[j] + out * flIdealDistance * m_nOwnedSlot[j], 255, 0, 0, true, .1);
delta = delta * min( max( dist - 0, 0), 250 ) * m_pSpheres[i]->GetMass();
//NDebugOverlay::Line(m_vecSurfacePos[j], m_vecSurfacePos[j] + out * flIdealDistance, 255, 0, 0, true, .1);
// m_flSurfaceV[i] = clamp( Approach( (m_nTargetSlot[i]) / (float)max( m_nOwnedSlot[j] + 1, m_nTargetSlot[i]), m_flSurfaceV[i], 0.2f ), 0.0f, 1.0f );
m_flSurfaceV[i] = Approach( (m_nTargetSlot[i]) / (float)nArmLength, m_flSurfaceV[i], 0.2f );
m_flSurfaceV[i] = clamp( m_flSurfaceV[i], 0.0f, 1.0f );
}
}
// tweak radius
//float a = (m_bContact[i] || bFloat) ? 1.0 : sv_surface_scale.GetFloat();
float a = sv_surface_scale.GetFloat();
if (m_nTargetSlot[i] <= 1 ) a = 1.0;
if (nArmLength > 1 && m_nTargetSlot[i] >= nArmLength - 1) a = (1.0 + sv_surface_scale.GetFloat()) * 0.5;
float b = m_flSurfaceR[i];
// a = 1.0f;
float c = Approach( a, b, 0.2f );
m_flSurfaceR[i] = clamp( c, 0.0f, 1.0f );
if (!m_bFloat[i])
{
delta.z = 0;
}
// apply the force
m_pSpheres[i]->ApplyForceCenter( delta );
}
}
#endif