//----------------------------------------------------------------------------- // Step iteratively toward a destination position //----------------------------------------------------------------------------- AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, AIMoveTrace_t *pTraceResult ) { // By definition, this will produce different results than GroundMoveLimit() // because there's no guarantee that it will step exactly one step // See how far toward the new position we can step... // But don't actually test for ground geometric validity; // if it isn't valid, there's not much we can do about it AIMoveTrace_t moveTrace; GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, MASK_NPCSOLID, AITGM_IGNORE_FLOOR, &moveTrace ); if ( pTraceResult ) { *pTraceResult = moveTrace; } bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction )); // Move forward either if there was no obstruction or if we're told to // move as far as we can, regardless bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus); if ( !bIsBlocked || bAsFarAsCan || bHitTarget ) { // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); // skip tiny steps, but notify the shadow object of any large steps if ( moveTrace.flStepUpDistance > 0.1f ) { float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() ); IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); if ( pPhysicsObject ) { IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController(); if ( pShadow ) { pShadow->StepUp( height ); } } } if ( yaw != -1 ) { QAngle angles = GetLocalAngles(); angles.y = yaw; SetLocalAngles( angles ); } if ( bHitTarget ) return AIM_PARTIAL_HIT_TARGET; if ( !bIsBlocked ) return AIM_SUCCESS; if ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) return AIM_PARTIAL_HIT_NPC; return AIM_PARTIAL_HIT_WORLD; } return AIM_FAILED; }
bool CBoneFollower::Init( CBaseEntity *pOwner, const char *pModelName, solid_t &solid, const Vector &position, const QAngle &orientation ) { SetOwnerEntity( pOwner ); UTIL_SetModel( this, pModelName ); AddEffects( EF_NODRAW ); // invisible m_modelIndex = modelinfo->GetModelIndex( pModelName ); m_solidIndex = solid.index; SetAbsOrigin( position ); SetAbsAngles( orientation ); SetMoveType( MOVETYPE_PUSH ); SetSolid( SOLID_VPHYSICS ); SetCollisionGroup( pOwner->GetCollisionGroup() ); AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST ); solid.params.pGameData = (void *)this; IPhysicsObject *pPhysics = VPhysicsInitShadow( false, false, &solid ); if ( !pPhysics ) return false; // we can't use the default model bounds because each entity is only one bone of the model // so compute the OBB of the physics model and use that. Vector mins, maxs; physcollision->CollideGetAABB( &mins, &maxs, pPhysics->GetCollide(), vec3_origin, vec3_angle ); SetCollisionBounds( mins, maxs ); pPhysics->SetCallbackFlags( pPhysics->GetCallbackFlags() | CALLBACK_GLOBAL_TOUCH ); pPhysics->EnableGravity( false ); // This is not a normal shadow controller that is trying to go to a space occupied by an entity in the game physics // This entity is not running PhysicsPusher(), so Vphysics is supposed to move it // This line of code informs vphysics of that fact if ( pOwner->IsNPC() ) { pPhysics->GetShadowController()->SetPhysicallyControlled( true ); } return true; }
//----------------------------------------------------------------------------- // Step iteratively toward a destination position //----------------------------------------------------------------------------- AIMotorMoveResult_t CAI_Motor::MoveGroundStep( const Vector &newPos, CBaseEntity *pMoveTarget, float yaw, bool bAsFarAsCan, bool bTestZ, AIMoveTrace_t *pTraceResult ) { // By definition, this will produce different results than GroundMoveLimit() // because there's no guarantee that it will step exactly one step // See how far toward the new position we can step... // But don't actually test for ground geometric validity; // if it isn't valid, there's not much we can do about it AIMoveTrace_t moveTrace; unsigned testFlags = AITGM_IGNORE_FLOOR; char *pchHackBoolToInt = (char*)(&bTestZ); if ( *pchHackBoolToInt == 2 ) { testFlags |= AITGM_CRAWL_LARGE_STEPS; } else { if ( !bTestZ ) testFlags |= AITGM_2D; } #ifdef DEBUG if ( ai_draw_motor_movement.GetBool() ) testFlags |= AITGM_DRAW_RESULTS; #endif GetMoveProbe()->TestGroundMove( GetLocalOrigin(), newPos, GetOuter()->GetAITraceMask(), testFlags, &moveTrace ); if ( pTraceResult ) { *pTraceResult = moveTrace; } bool bHitTarget = (moveTrace.pObstruction && (pMoveTarget == moveTrace.pObstruction )); // Move forward either if there was no obstruction or if we're told to // move as far as we can, regardless bool bIsBlocked = IsMoveBlocked(moveTrace.fStatus); if ( !bIsBlocked || bAsFarAsCan || bHitTarget ) { #ifdef DEBUG if ( GetMoveProbe()->CheckStandPosition( GetLocalOrigin(), GetOuter()->GetAITraceMask() ) && !GetMoveProbe()->CheckStandPosition( moveTrace.vEndPosition, GetOuter()->GetAITraceMask() ) ) { DevMsg( 2, "Warning: AI motor probably given invalid instructions\n" ); } #endif // The true argument here causes it to touch all triggers // in the volume swept from the previous position to the current position UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true); // check to see if our ground entity has changed // NOTE: This is to detect changes in ground entity as the movement code has optimized out // ground checks. So now we have to do a simple recheck to make sure we detect when we've // stepped onto a new entity. if ( GetOuter()->GetFlags() & FL_ONGROUND ) { GetOuter()->PhysicsStepRecheckGround(); } // skip tiny steps, but notify the shadow object of any large steps if ( moveTrace.flStepUpDistance > 0.1f ) { float height = clamp( moveTrace.flStepUpDistance, 0, StepHeight() ); IPhysicsObject *pPhysicsObject = GetOuter()->VPhysicsGetObject(); if ( pPhysicsObject ) { IPhysicsShadowController *pShadow = pPhysicsObject->GetShadowController(); if ( pShadow ) { pShadow->StepUp( height ); } } } if ( yaw != -1 ) { QAngle angles = GetLocalAngles(); angles.y = yaw; SetLocalAngles( angles ); } if ( bHitTarget ) return AIM_PARTIAL_HIT_TARGET; if ( !bIsBlocked ) return AIM_SUCCESS; if ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) return AIM_PARTIAL_HIT_NPC; return AIM_PARTIAL_HIT_WORLD; } return AIM_FAILED; }
void CPhysicsSystem::PhysicsSimulate() { CMiniProfilerGuard mpg(&g_mp_PhysicsSimulate); VPROF_BUDGET( "CPhysicsSystem::PhysicsSimulate", VPROF_BUDGETGROUP_PHYSICS ); float frametime = gpGlobals->frametime; if ( physenv ) { g_Collisions.BufferTouchEvents( true ); #ifdef _DEBUG physenv->DebugCheckContacts(); #endif frametime *= cl_phys_timescale.GetFloat(); int maxTicks = cl_phys_maxticks.GetInt(); if ( maxTicks ) { float maxFrameTime = physenv->GetDeltaFrameTime( maxTicks ) - 1e-4f; frametime = clamp( frametime, 0, maxFrameTime ); } physenv->Simulate( frametime ); int activeCount = physenv->GetActiveObjectCount(); g_mp_active_object_count.Add(activeCount); IPhysicsObject **pActiveList = NULL; if ( activeCount ) { PHYS_PROFILE(aUpdateActiveObjects) pActiveList = (IPhysicsObject **)stackalloc( sizeof(IPhysicsObject *)*activeCount ); physenv->GetActiveObjects( pActiveList ); for ( int i = 0; i < activeCount; i++ ) { C_BaseEntity *pEntity = reinterpret_cast<C_BaseEntity *>(pActiveList[i]->GetGameData()); if ( pEntity ) { //const CCollisionProperty *collProp = pEntity->CollisionProp(); //debugoverlay->AddBoxOverlay( collProp->GetCollisionOrigin(), collProp->OBBMins(), collProp->OBBMaxs(), collProp->GetCollisionAngles(), 190, 190, 0, 0, 0.01 ); if ( pEntity->CollisionProp()->DoesVPhysicsInvalidateSurroundingBox() ) { pEntity->CollisionProp()->MarkSurroundingBoundsDirty(); } pEntity->VPhysicsUpdate( pActiveList[i] ); IPhysicsShadowController *pShadow = pActiveList[i]->GetShadowController(); if ( pShadow ) { // active shadow object, check for error Vector pos, targetPos; QAngle rot, targetAngles; pShadow->GetTargetPosition( &targetPos, &targetAngles ); pActiveList[i]->GetPosition( &pos, &rot ); Vector delta = targetPos - pos; float dist = VectorNormalize(delta); bool bBlocked = false; if ( dist > cl_phys_block_dist.GetFloat() ) { Vector vel; pActiveList[i]->GetImplicitVelocity( &vel, NULL ); float proj = DotProduct(vel, delta); if ( proj < dist * cl_phys_block_fraction.GetFloat() ) { bBlocked = true; //Msg("%s was blocked %.3f (%.3f proj)!\n", pEntity->GetClassname(), dist, proj ); } } Vector targetAxis; float deltaTargetAngle; RotationDeltaAxisAngle( rot, targetAngles, targetAxis, deltaTargetAngle ); if ( fabsf(deltaTargetAngle) > 0.5f ) { AngularImpulse angVel; pActiveList[i]->GetImplicitVelocity( NULL, &angVel ); float proj = DotProduct( angVel, targetAxis ) * Sign(deltaTargetAngle); if ( proj < (fabsf(deltaTargetAngle) * cl_phys_block_fraction.GetFloat()) ) { bBlocked = true; //Msg("%s was rot blocked %.3f proj %.3f!\n", pEntity->GetClassname(), deltaTargetAngle, proj ); } } if ( bBlocked ) { C_BaseEntity *pBlocker = FindPhysicsBlocker( pActiveList[i] ); if ( pBlocker ) { if ( IsBlockedShouldDisableCollisions( pEntity ) ) { PhysDisableEntityCollisions( pEntity, pBlocker ); pActiveList[i]->RecheckContactPoints(); // GetClassname returns a pointer to the same buffer always! //Msg("%s blocked !", pEntity->GetClassname() ); Msg("by %s\n", pBlocker->GetClassname() ); } } } } } } } #if 0 if ( cl_visualize_physics_shadows.GetBool() ) { int entityCount = NUM_ENT_ENTRIES; for ( int i = 0; i < entityCount; i++ ) { IClientEntity *pClientEnt = cl_entitylist->GetClientEntity(i); if ( !pClientEnt ) continue; C_BaseEntity *pEntity = pClientEnt->GetBaseEntity(); if ( !pEntity ) continue; Vector pos; QAngle angle; IPhysicsObject *pObj = pEntity->VPhysicsGetObject(); if ( !pObj || !pObj->GetShadowController() ) continue; pObj->GetShadowPosition( &pos, &angle ); debugoverlay->AddBoxOverlay( pos, pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs(), angle, 255, 255, 0, 32, 0 ); char tmp[256]; V_snprintf( tmp, sizeof(tmp),"%s, (%s)\n", pEntity->GetClassname(), VecToString(angle) ); debugoverlay->AddTextOverlay( pos, 0, tmp ); } } #endif g_Collisions.BufferTouchEvents( false ); g_Collisions.FrameUpdate(); } physicssound::PlayImpactSounds( m_impactSounds ); }
void CNPC_Dog::ThrowObject( const char *pAttachmentName ) { if ( m_hPhysicsEnt ) { m_bHasObject = false; IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject(); if ( pPhysObj ) { Vector vGunPos; QAngle angGunAngles; AngularImpulse angVelocity = RandomAngularImpulse( -250 , -250 ) / pPhysObj->GetMass(); InvalidateBoneCache(); int iAttachment = LookupAttachment( pAttachmentName ); if ( iAttachment == 0 ) iAttachment = m_iPhysGunAttachment; GetAttachment( iAttachment, vGunPos, angGunAngles ); pPhysObj->Wake(); if ( pPhysObj->GetShadowController() ) { m_hPhysicsEnt->SetParent( NULL ); m_hPhysicsEnt->SetMoveType( (MoveType_t)m_iContainerMoveType ); m_hPhysicsEnt->SetOwnerEntity( this ); pPhysObj->RemoveShadowController(); pPhysObj->SetPosition( m_hPhysicsEnt->GetLocalOrigin(), m_hPhysicsEnt->GetLocalAngles(), true ); pPhysObj->RecheckCollisionFilter(); pPhysObj->RecheckContactPoints(); } if ( m_hThrowTarget == NULL ) #ifdef SecobMod__Enable_Fixed_Multiplayer_AI m_hThrowTarget = UTIL_GetNearestVisiblePlayer(this); #else m_hThrowTarget = AI_GetSinglePlayer(); #endif //SecobMod__Enable_Fixed_Multiplayer_AI Vector vThrowDirection; if ( m_hThrowTarget ) { Vector vThrowOrigin = m_hThrowTarget->GetAbsOrigin(); if ( m_hThrowTarget->IsPlayer() ) vThrowOrigin = vThrowOrigin + Vector( random->RandomFloat( -128, 128 ), random->RandomFloat( -128, 128 ), 0 ); Vector vecToss = VecCheckToss( this, vGunPos, vThrowOrigin, m_flThrowArcModifier, 1.0f, true ); if( vecToss == vec3_origin ) { // Fix up an impossible throw so dog will at least toss the box in the target's general direction instead of dropping it. // Also toss it up in the air so it will fall down and break. (Just throw the box up at a 45 degree angle) Vector forward, up; GetVectors( &forward, NULL, &up ); vecToss = forward + up; VectorNormalize( vecToss ); vecToss *= pPhysObj->GetMass() * 30.0f; } vThrowDirection = vecToss + ( m_hThrowTarget->GetSmoothedVelocity() / 2 ); Vector vLinearDrag; Vector unitVel = vThrowDirection; VectorNormalize( unitVel ); float flTest = 1000 / vThrowDirection.Length(); float flDrag = pPhysObj->CalculateLinearDrag( vThrowDirection ); vThrowDirection = vThrowDirection + ( unitVel * ( flDrag * flDrag ) ) / flTest; pPhysObj->SetVelocity( &vThrowDirection, &angVelocity ); m_flTimeToCatch = gpGlobals->curtime + dog_max_wait_time.GetFloat(); //Don't start pulling until the object is away from me. //We base the time on the throw velocity. m_flTimeToPull = gpGlobals->curtime + ( 1000 / vThrowDirection.Length() ); } //Fire Output! m_OnThrow.FireOutput( this, this ); ClearBeams(); if ( m_bBeamEffects == true ) { EmitSound( "Weapon_PhysCannon.Launch" ); CBeam *pBeam = CBeam::BeamCreate( "sprites/orangelight1.vmt", 1.8 ); if ( pBeam != NULL ) { pBeam->PointEntInit( m_hPhysicsEnt->WorldSpaceCenter(), this ); pBeam->SetEndAttachment( m_iPhysGunAttachment ); pBeam->SetWidth( 6.4 ); pBeam->SetEndWidth( 12.8 ); pBeam->SetBrightness( 255 ); pBeam->SetColor( 255, 255, 255 ); pBeam->LiveForTime( 0.2f ); pBeam->RelinkBeam(); pBeam->SetNoise( 2 ); } Vector shotDir = ( m_hPhysicsEnt->WorldSpaceCenter() - vGunPos ); VectorNormalize( shotDir ); CPVSFilter filter( m_hPhysicsEnt->WorldSpaceCenter() ); te->GaussExplosion( filter, 0.0f, m_hPhysicsEnt->WorldSpaceCenter() - ( shotDir * 4.0f ), RandomVector(-1.0f, 1.0f), 0 ); } } } }