static void* SendProxy_SendPredictableId( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) { CBaseEntity *pEntity = (CBaseEntity *)pStruct; if ( !pEntity || !pEntity->m_PredictableID->IsActive() ) return NULL; if ( !pEntity->GetOwnerEntity() ) return NULL; CBaseEntity *owner = pEntity->GetOwnerEntity(); if ( !owner || !owner->IsPlayer() ) return NULL; CBasePlayer *pOwner = static_cast< CBasePlayer * >( owner ); if ( !pOwner ) return NULL; int id_player_index = pEntity->m_PredictableID->GetPlayer(); int owner_player_index = pOwner->entindex() - 1; // Only send to owner player // FIXME: Is this ever not the case due to the SetOnly call? if ( id_player_index != owner_player_index ) return NULL; pRecipients->SetOnly( owner_player_index ); return ( void * )pVarData; }
void EmitCloseCaption( IRecipientFilter& filter, int entindex, const CSoundParameters & params, const EmitSound_t & ep ) { // No close captions in multiplayer... if ( gpGlobals->maxClients > 1 || (gpGlobals->maxClients==1 && !g_pClosecaption->GetBool())) { return; } if ( !ep.m_bEmitCloseCaption ) { return; } // NOTE: We must make a copy or else if the filter is owned by a SoundPatch, we'll end up destructively removing // all players from it!!!! CRecipientFilter filterCopy; filterCopy.CopyFrom( (CRecipientFilter &)filter ); // Remove any players who don't want close captions CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( (CRecipientFilter &)filterCopy ); // Anyone left? if ( filterCopy.GetRecipientCount() <= 0 ) { return; } float duration = 0.0f; if ( ep.m_pflSoundDuration ) { duration = *ep.m_pflSoundDuration; } else { duration = enginesound->GetSoundDuration( params.soundname ); } bool fromplayer = false; CBaseEntity *ent = CBaseEntity::Instance( entindex ); if ( ent ) { while ( ent ) { if ( ent->IsPlayer() ) { fromplayer = true; break; } ent = ent->GetOwnerEntity(); } } EmitCloseCaption( filter, entindex, fromplayer, ep.m_pSoundName, ep.m_UtlVecSoundOrigin, duration, ep.m_bWarnOnMissingCloseCaption ); }
void CHL1MP_Player::DetonateSatchelCharges( void ) { CBaseEntity *pSatchel = NULL; while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL) { if ( pSatchel->GetOwnerEntity() == this ) { pSatchel->Use( this, this, USE_ON, 0 ); } } }
//----------------------------------------------------------------------------- // Purpose: Handle a collision using our special behavior //----------------------------------------------------------------------------- void CWeaponStriderBuster::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { // Find out what we hit. // Don't do anything special if we're already attached to a strider. CBaseEntity *pVictim = pEvent->pEntities[!index]; if ( pVictim == NULL || m_pConstraint != NULL ) { BaseClass::VPhysicsCollision( index, pEvent ); return; } // Don't attach if we're being held by the player if ( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { BaseClass::VPhysicsCollision( index, pEvent ); return; } // Save off the speed of the object m_flCollisionSpeedSqr = ( pEvent->preVelocity[ index ] ).LengthSqr(); // Break if we hit the world while going fast enough. // Launched duds detonate if they hit the world at any speed. if ( pVictim->IsWorld() && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) { m_OnShatter.FireOutput( this, this ); Shatter( pVictim ); return; } // We'll handle this later in our touch call if ( ShouldStickToEntity( pVictim ) ) return; // Determine if we should shatter CBaseEntity *pOwnerEntity = pVictim->GetOwnerEntity(); bool bVictimIsStrider = ( ( pOwnerEntity != NULL ) && FClassnameIs( pOwnerEntity, "npc_strider" ) ); // Break if we hit anything other than a strider while going fast enough. // Launched duds detonate if they hit anything other than a strider any speed. if ( ( bVictimIsStrider == false ) && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) { m_OnShatter.FireOutput( this, this ); Shatter( pVictim ); return; } // Just bounce BaseClass::VPhysicsCollision( index, pEvent ); }
//----------------------------------------------------------------------------- // Purpose: Check the weapon LOS for an owner at an arbitrary position // If bSetConditions is true, LOS related conditions will also be set //----------------------------------------------------------------------------- bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) { // -------------------- // Check for occlusion // -------------------- CAI_BaseNPC* npcOwner = m_hOwner.Get()->MyNPCPointer(); // Find its relative shoot position Vector vecRelativeShootPosition; VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); Vector barrelPos = ownerPos + vecRelativeShootPosition; // Use the custom LOS trace filter CWeaponLOSFilter traceFilter( m_hOwner.Get(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS ); trace_t tr; UTIL_TraceLine( barrelPos, targetPos, MASK_SHOT, &traceFilter, &tr ); // See if we completed the trace without interruption if ( tr.fraction == 1.0 ) { if ( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); } return true; } CBaseEntity *pHitEnt = tr.m_pEnt; CBasePlayer *pEnemyPlayer = ToBasePlayer( npcOwner->GetEnemy() ); // is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle) if ( pEnemyPlayer && pEnemyPlayer->IsInAVehicle() ) { // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. // This catches vehicles that use bone followers. CBaseEntity *pVehicle = pEnemyPlayer->GetVehicle()->GetVehicleEnt(); if ( pHitEnt == pVehicle || pHitEnt->GetOwnerEntity() == pVehicle ) return true; } // Hitting our enemy is a success case if ( pHitEnt == npcOwner->GetEnemy() ) { if ( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); } return true; } // If a vehicle is blocking the view, grab its driver and use that as the combat character CBaseCombatCharacter *pBCC; IServerVehicle *pVehicle = pHitEnt->GetServerVehicle(); if ( pVehicle ) { pBCC = pVehicle->GetPassenger( ); } else { pBCC = ToBaseCombatCharacter( pHitEnt ); } if ( pBCC ) { if ( npcOwner->IRelationType( pBCC ) == D_HT ) return true; if ( bSetConditions ) { npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); } } else if ( bSetConditions ) { npcOwner->SetCondition( COND_WEAPON_SIGHT_OCCLUDED ); npcOwner->SetEnemyOccluder( pHitEnt ); if( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 ); } } return false; }
float CalculateObjectStress( IPhysicsObject *pObject, CBaseEntity *pInputOwnerEntity, vphysics_objectstress_t *pOutput ) { CUtlVector< CBaseEntity * > pObjectList; CUtlVector< Vector > objectForce; bool hasLargeObject = false; // add a slot for static objects pObjectList.AddToTail( NULL ); objectForce.AddToTail( vec3_origin ); // add a slot for friendly objects pObjectList.AddToTail( NULL ); objectForce.AddToTail( vec3_origin ); CBaseCombatCharacter *pBCC = pInputOwnerEntity->MyCombatCharacterPointer(); IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot(); float objMass = pObject->GetMass(); while ( pSnapshot->IsValid() ) { float force = pSnapshot->GetNormalForce(); if ( force > 0.0f ) { IPhysicsObject *pOther = pSnapshot->GetObject(1); CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData()); if ( !pOtherEntity ) { // object was just deleted, but we still have a contact point this frame... // just assume it came from the world. pOtherEntity = GetWorldEntity(); } CBaseEntity *pOtherOwner = pOtherEntity; if ( pOtherEntity->GetOwnerEntity() ) { pOtherOwner = pOtherEntity->GetOwnerEntity(); } int outIndex = 0; if ( !pOther->IsMoveable() ) { outIndex = 0; } // NavIgnored objects are often being pushed by a friendly else if ( pBCC && (pBCC->IRelationType( pOtherOwner ) == D_LI || pOtherEntity->IsNavIgnored()) ) { outIndex = 1; } // player held objects do no stress else if ( pOther->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { outIndex = 1; } else { if ( pOther->GetMass() >= VPHYSICS_LARGE_OBJECT_MASS ) { if ( pInputOwnerEntity->GetGroundEntity() != pOtherEntity) { hasLargeObject = true; } } // moveable, non-friendly // aggregate contacts over each object to avoid greater stress in multiple contact cases // NOTE: Contacts should be in order, so this shouldn't ever search, but just in case outIndex = pObjectList.Count(); for ( int i = pObjectList.Count()-1; i >= 2; --i ) { if ( pObjectList[i] == pOtherOwner ) { outIndex = i; break; } } if ( outIndex == pObjectList.Count() ) { pObjectList.AddToTail( pOtherOwner ); objectForce.AddToTail( vec3_origin ); } } if ( outIndex != 0 && pInputOwnerEntity->GetMoveType() != MOVETYPE_VPHYSICS && !IsPhysicallyControlled(pOtherEntity, pOther) ) { // UNDONE: Test this! This is to remove any shadow/shadow stress. The game should handle this with blocked/damage force = 0.0f; } Vector normal; pSnapshot->GetSurfaceNormal( normal ); objectForce[outIndex] += normal * force; } pSnapshot->NextFrictionData(); } pObject->DestroyFrictionSnapshot( pSnapshot ); pSnapshot = NULL; // clear out all friendly force objectForce[1].Init(); float sum = 0; Vector negativeForce = vec3_origin; Vector positiveForce = vec3_origin; Assert( pObjectList.Count() == objectForce.Count() ); for ( int objectIndex = pObjectList.Count()-1; objectIndex >= 0; --objectIndex ) { sum += objectForce[objectIndex].Length(); for ( int i = 0; i < 3; i++ ) { if ( objectForce[objectIndex][i] < 0 ) { negativeForce[i] -= objectForce[objectIndex][i]; } else { positiveForce[i] += objectForce[objectIndex][i]; } } } // "external" stress is two way (something pushes on the object and something else pushes back) // so the set of minimum values per component are the projections of the two-way force // "internal" stress is one way (the object is pushing against something OR something pushing back) // the momentum must have come from inside the object (gravity, controller, etc) Vector internalForce = vec3_origin; Vector externalForce = vec3_origin; for ( int i = 0; i < 3; i++ ) { if ( negativeForce[i] < positiveForce[i] ) { internalForce[i] = positiveForce[i] - negativeForce[i]; externalForce[i] = negativeForce[i]; } else { internalForce[i] = negativeForce[i] - positiveForce[i]; externalForce[i] = positiveForce[i]; } } // sum is kg in / s Vector gravVector; physenv->GetGravity( &gravVector ); float gravity = gravVector.Length(); if ( pInputOwnerEntity->GetMoveType() != MOVETYPE_VPHYSICS && pObject->IsMoveable() ) { Vector lastVel; lastVel.Init(); if ( pObject->GetShadowController() ) { pObject->GetShadowController()->GetLastImpulse( &lastVel ); } else { if ( ( pObject->GetCallbackFlags() & CALLBACK_IS_PLAYER_CONTROLLER ) ) { CBasePlayer *pPlayer = ToBasePlayer( pInputOwnerEntity ); IPhysicsPlayerController *pController = pPlayer ? pPlayer->GetPhysicsController() : NULL; if ( pController ) { pController->GetLastImpulse( &lastVel ); } } } // Work in progress... // Peek into the controller for this object. Look at the input velocity and make sure it's all // accounted for in the computed stress. If not, redistribute external to internal as it's // probably being reflected in a way we can't measure here. float inputLen = lastVel.Length() * (1.0f / physenv->GetSimulationTimestep()) * objMass; if ( inputLen > 0.0f ) { float internalLen = internalForce.Length(); if ( internalLen < inputLen ) { float ratio = internalLen / inputLen; Vector delta = internalForce * (1.0f - ratio); internalForce += delta; float deltaLen = delta.Length(); sum -= deltaLen; float extLen = VectorNormalize(externalForce) - deltaLen; if ( extLen < 0 ) { extLen = 0; } externalForce *= extLen; } } } float invGravity = gravity; if ( invGravity <= 0 ) { invGravity = 1.0f; } else { invGravity = 1.0f / invGravity; } sum *= invGravity; internalForce *= invGravity; externalForce *= invGravity; if ( !pObject->IsMoveable() ) { // the above algorithm will see almost all force as internal if the object is not moveable // (it doesn't push on anything else, so nothing is reciprocated) // exceptions for friction of a single other object with multiple contact points on this object // But the game wants to see it all as external because obviously the object can't move, so it can't have // internal stress externalForce = internalForce; internalForce.Init(); if ( !pObject->IsStatic() ) { sum += objMass; } } else { // assume object is at rest if ( sum > objMass ) { sum = objMass + (sum-objMass) * 0.5; } } if ( pOutput ) { pOutput->exertedStress = internalForce.Length(); pOutput->receivedStress = externalForce.Length(); pOutput->hasNonStaticStress = pObjectList.Count() > 2 ? true : false; pOutput->hasLargeObjectContact = hasLargeObject; } // sum is now kg return sum; }