const Vector &C_EnvParticleScript::GetSortOrigin() { return GetAbsOrigin(); }
void C_EnergyWave::ComputePoint( float s, float t, Vector& pt, Vector& normal, float& opacity ) { int is = (int)s; int it = (int)t; if( is >= EWAVE_NUM_HORIZONTAL_POINTS ) is -= 1; if( it >= EWAVE_NUM_VERTICAL_POINTS ) it -= 1; int idx[16]; ComputeIndices( is, it, idx ); // The patch equation is: // px = S * M * Gx * M^T * T^T // py = S * M * Gy * M^T * T^T // pz = S * M * Gz * M^T * T^T // where S = [s^3 s^2 s 1], T = [t^3 t^2 t 1] // M is the patch type matrix, in my case I'm using a catmull-rom // G is the array of control points. rows have constant t static VMatrix catmullRom( -0.5, 1.5, -1.5, 0.5, 1, -2.5, 2, -0.5, -0.5, 0, 0.5, 0, 0, 1, 0, 0 ); VMatrix controlPointsX, controlPointsY, controlPointsZ, controlPointsO; Vector pos; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { const Vector& v = m_EWaveEffect.GetPoint( idx[i * 4 + j] ); controlPointsX[j][i] = v.x; controlPointsY[j][i] = v.y; controlPointsZ[j][i] = v.z; controlPointsO[j][i] = m_EWaveEffect.ComputeOpacity( v, GetAbsOrigin() ); } } float fs = s - is; float ft = t - it; VMatrix temp, mgm[4]; MatrixTranspose( catmullRom, temp ); MatrixMultiply( controlPointsX, temp, mgm[0] ); MatrixMultiply( controlPointsY, temp, mgm[1] ); MatrixMultiply( controlPointsZ, temp, mgm[2] ); MatrixMultiply( controlPointsO, temp, mgm[3] ); MatrixMultiply( catmullRom, mgm[0], mgm[0] ); MatrixMultiply( catmullRom, mgm[1], mgm[1] ); MatrixMultiply( catmullRom, mgm[2], mgm[2] ); MatrixMultiply( catmullRom, mgm[3], mgm[3] ); Vector4D svec, tvec; float ft2 = ft * ft; tvec[0] = ft2 * ft; tvec[1] = ft2; tvec[2] = ft; tvec[3] = 1.0f; float fs2 = fs * fs; svec[0] = fs2 * fs; svec[1] = fs2; svec[2] = fs; svec[3] = 1.0f; Vector4D tmp; Vector4DMultiply( mgm[0], tvec, tmp ); pt[0] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[1], tvec, tmp ); pt[1] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[2], tvec, tmp ); pt[2] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[3], tvec, tmp ); opacity = DotProduct4D( tmp, svec ); if ((s == 0.0f) || (t == 0.0f) || (s == (EWAVE_NUM_HORIZONTAL_POINTS-1.0f)) || (t == (EWAVE_NUM_VERTICAL_POINTS-1.0f)) ) { opacity = 0.0f; } if ((s <= 0.3) || (t < 0.3)) { opacity *= 0.35f; } if ((s == (EWAVE_NUM_HORIZONTAL_POINTS-0.7f)) || (t == (EWAVE_NUM_VERTICAL_POINTS-0.7f)) ) { opacity *= 0.35f; } if (opacity < 0.0f) opacity = 0.0f; else if (opacity > 255.0f) opacity = 255.0f; // Normal computation Vector4D dsvec, dtvec; dsvec[0] = 3.0f * fs2; dsvec[1] = 2.0f * fs; dsvec[2] = 1.0f; dsvec[3] = 0.0f; dtvec[0] = 3.0f * ft2; dtvec[1] = 2.0f * ft; dtvec[2] = 1.0f; dtvec[3] = 0.0f; Vector ds, dt; Vector4DMultiply( mgm[0], tvec, tmp ); ds[0] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[1], tvec, tmp ); ds[1] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[2], tvec, tmp ); ds[2] = DotProduct4D( tmp, dsvec ); Vector4DMultiply( mgm[0], dtvec, tmp ); dt[0] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[1], dtvec, tmp ); dt[1] = DotProduct4D( tmp, svec ); Vector4DMultiply( mgm[2], dtvec, tmp ); dt[2] = DotProduct4D( tmp, svec ); CrossProduct( ds, dt, normal ); VectorNormalize( normal ); }
void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) { switch( pEvent->event ) { case EVENT_WEAPON_MELEE_HIT: { // Trace up or down based on where the enemy is... // But only if we're basically facing that direction Vector vecDirection; AngleVectors( GetAbsAngles(), &vecDirection ); CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL; if ( pEnemy ) { Vector vecDelta; VectorSubtract( pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta ); VectorNormalize( vecDelta ); Vector2D vecDelta2D = vecDelta.AsVector2D(); Vector2DNormalize( vecDelta2D ); if ( DotProduct2D( vecDelta2D, vecDirection.AsVector2D() ) > 0.8f ) { vecDirection = vecDelta; } } Vector vecEnd; VectorMA( pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd ); // Stretch the swing box down to catch low level physics objects CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false ); // did I hit someone? if ( pHurt ) { // play sound WeaponSound( MELEE_HIT ); CBasePlayer *pPlayer = ToBasePlayer( pHurt ); CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(pOperator); bool bFlashed = false; if ( pCop != NULL && pPlayer != NULL ) { // See if we need to knock out this target if ( pCop->ShouldKnockOutTarget( pHurt ) ) { float yawKick = random->RandomFloat( -48, -24 ); //Kick the player angles pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) ); color32 white = {255,255,255,255}; UTIL_ScreenFade( pPlayer, white, 0.2f, 1.0f, FFADE_OUT|FFADE_PURGE|FFADE_STAYOUT ); bFlashed = true; pCop->KnockOutTarget( pHurt ); break; } else { // Notify that we've stunned a target pCop->StunnedTarget( pHurt ); } } // Punch angles if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) ) { float yawKick = random->RandomFloat( -48, -24 ); //Kick the player angles pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) ); Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin(); // If the player's on my head, don't knock him up if ( pPlayer->GetGroundEntity() == pOperator ) { dir = vecDirection; dir.z = 0; } VectorNormalize(dir); dir *= 500.0f; //If not on ground, then don't make them fly! if ( !(pPlayer->GetFlags() & FL_ONGROUND ) ) dir.z = 0.0f; //Push the target back pHurt->ApplyAbsVelocityImpulse( dir ); if ( !bFlashed ) { color32 red = {128,0,0,128}; UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN ); } // Force the player to drop anyting they were holding pPlayer->ForceDropOfCarriedPhysObjects(); } // do effect? } else { WeaponSound( MELEE_MISS ); } } break; default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; } }
void CNPC_Zombine::GatherGrenadeConditions( void ) { if ( m_iGrenadeCount <= 0 ) return; if ( g_flZombineGrenadeTimes > gpGlobals->curtime ) return; if ( m_flGrenadePullTime > gpGlobals->curtime ) return; if ( m_flSuperFastAttackTime >= gpGlobals->curtime ) return; if ( HasGrenade() ) return; if ( GetEnemy() == NULL ) return; if ( FVisible( GetEnemy() ) == false ) return; if ( IsSprinting() ) return; if ( IsOnFire() ) return; if ( IsRunningDynamicInteraction() == true ) return; if ( m_ActBusyBehavior.IsActive() ) return; //Secobmod FixMe //CBasePlayer *pPlayer = AI_GetSinglePlayer(); CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer && pPlayer->FVisible( this ) ) { float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length(); float flLengthToEnemy = flLengthToPlayer; if ( pPlayer != GetEnemy() ) { flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length(); } if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE ) { float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE ); m_flGrenadePullTime = gpGlobals->curtime + 0.5f; if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) ) { g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f; SetCondition( COND_ZOMBINE_GRENADE ); } } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPropAPC::Event_Killed( const CTakeDamageInfo &info ) { m_OnDeath.FireOutput( info.GetAttacker(), this ); Vector vecAbsMins, vecAbsMaxs; CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); Vector vecNormalizedMins, vecNormalizedMaxs; CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins ); CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs ); Vector vecAbsPoint; CPASFilter filter( GetAbsOrigin() ); for (int i = 0; i < 5; i++) { CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint ); te->Explosion( filter, random->RandomFloat( 0.0, 1.0 ), &vecAbsPoint, g_sModelIndexFireball, random->RandomInt( 4, 10 ), random->RandomInt( 8, 15 ), ( i < 2 ) ? TE_EXPLFLAG_NODLIGHTS : TE_EXPLFLAG_NOPARTICLES | TE_EXPLFLAG_NOFIREBALLSMOKE | TE_EXPLFLAG_NODLIGHTS, 100, 0 ); } // TODO: make the gibs spawn in sync with the delayed explosions int nGibs = random->RandomInt( 1, 4 ); for ( int i = 0; i < nGibs; i++) { // Throw a flaming, smoking chunk. CGib *pChunk = CREATE_ENTITY( CGib, "gib" ); pChunk->Spawn( "models/gibs/hgibs.mdl" ); pChunk->SetBloodColor( DONT_BLEED ); QAngle vecSpawnAngles; vecSpawnAngles.Random( -90, 90 ); pChunk->SetAbsOrigin( vecAbsPoint ); pChunk->SetAbsAngles( vecSpawnAngles ); int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 ); pChunk->Spawn( s_pChunkModelName[nGib] ); pChunk->SetOwnerEntity( this ); pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f ); pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false ); // Set the velocity if ( pPhysicsObject ) { pPhysicsObject->EnableMotion( true ); Vector vecVelocity; QAngle angles; angles.x = random->RandomFloat( -20, 20 ); angles.y = random->RandomFloat( 0, 360 ); angles.z = 0.0f; AngleVectors( angles, &vecVelocity ); vecVelocity *= random->RandomFloat( 300, 900 ); vecVelocity += GetAbsVelocity(); AngularImpulse angImpulse; angImpulse = RandomAngularImpulse( -180, 180 ); pChunk->SetAbsVelocity( vecVelocity ); pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse ); } CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false ); if ( pFlame != NULL ) { pFlame->SetLifetime( pChunk->m_lifeTime ); } } UTIL_ScreenShake( vecAbsPoint, 25.0, 150.0, 1.0, 750.0f, SHAKE_START ); if( hl2_episodic.GetBool() ) { // EP1 perf hit Ignite( 6, false ); } else { Ignite( 60, false ); } m_lifeState = LIFE_DYING; // Spawn a lesser amount if the player is close m_iRocketSalvoLeft = DEATH_VOLLEY_ROCKET_COUNT; m_flRocketTime = gpGlobals->curtime; }
//------------------------------------------------------------------------------ // Purpose: Samples the player's inputs and fires outputs based on what buttons // are currently held down. //------------------------------------------------------------------------------ void CGameUI::Think( void ) { CBasePlayer *pPlayer = m_player; // If player is gone, stop thinking if (pPlayer == NULL) { SetNextThink( TICK_NEVER_THINK ); return; } // If we're forcing an update, state with a clean button state if ( m_bForceUpdate ) { m_nLastButtonState = pPlayer->m_nButtons; } // ------------------------------------------------ // Check that toucher is facing the UI within // the field of view tolerance. If not disconnect // ------------------------------------------------ if (m_flFieldOfView > -1) { Vector vPlayerFacing; pPlayer->EyeVectors( &vPlayerFacing ); Vector vPlayerToUI = GetAbsOrigin() - pPlayer->WorldSpaceCenter(); VectorNormalize(vPlayerToUI); float flDotPr = DotProduct(vPlayerFacing,vPlayerToUI); if (flDotPr < m_flFieldOfView) { Deactivate( pPlayer ); return; } } pPlayer->AddFlag( FL_ONTRAIN ); SetNextThink( gpGlobals->curtime ); // Deactivate if they jump or press +use. // FIXME: prevent the use from going through in player.cpp if ((( pPlayer->m_afButtonPressed & IN_USE ) && ( m_spawnflags & SF_GAMEUI_USE_DEACTIVATES )) || (( pPlayer->m_afButtonPressed & IN_JUMP ) && ( m_spawnflags & SF_GAMEUI_JUMP_DEACTIVATES ))) { Deactivate( pPlayer ); return; } // Determine what's different int nButtonsChanged = ( pPlayer->m_nButtons ^ m_nLastButtonState ); // // Handle all our possible input triggers // if ( nButtonsChanged & IN_MOVERIGHT ) { if ( m_nLastButtonState & IN_MOVERIGHT ) { m_unpressedMoveRight.FireOutput( pPlayer, this, 0 ); } else { m_pressedMoveRight.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_MOVELEFT ) { if ( m_nLastButtonState & IN_MOVELEFT ) { m_unpressedMoveLeft.FireOutput( pPlayer, this, 0 ); } else { m_pressedMoveLeft.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_FORWARD ) { if ( m_nLastButtonState & IN_FORWARD ) { m_unpressedForward.FireOutput( pPlayer, this, 0 ); } else { m_pressedForward.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_BACK ) { if ( m_nLastButtonState & IN_BACK ) { m_unpressedBack.FireOutput( pPlayer, this, 0 ); } else { m_pressedBack.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_ATTACK ) { if ( m_nLastButtonState & IN_ATTACK ) { m_unpressedAttack.FireOutput( pPlayer, this, 0 ); } else { m_pressedAttack.FireOutput( pPlayer, this, 0 ); } } if ( nButtonsChanged & IN_ATTACK2 ) { if ( m_nLastButtonState & IN_ATTACK2 ) { m_unpressedAttack2.FireOutput( pPlayer, this, 0 ); } else { m_pressedAttack2.FireOutput( pPlayer, this, 0 ); } } // Setup for the next frame m_nLastButtonState = pPlayer->m_nButtons; float x = 0, y = 0, attack = 0, attack2 = 0; if ( pPlayer->m_nButtons & IN_MOVERIGHT ) { x = 1; } else if ( pPlayer->m_nButtons & IN_MOVELEFT ) { x = -1; } if ( pPlayer->m_nButtons & IN_FORWARD ) { y = 1; } else if ( pPlayer->m_nButtons & IN_BACK ) { y = -1; } if ( pPlayer->m_nButtons & IN_ATTACK ) { attack = 1; } if ( pPlayer->m_nButtons & IN_ATTACK2 ) { attack2 = 1; } // // Fire the analog outputs if they changed. // if ( m_bForceUpdate || ( m_xaxis.Get() != x ) ) { m_xaxis.Set( x, pPlayer, this ); } if ( m_bForceUpdate || ( m_yaxis.Get() != y ) ) { m_yaxis.Set( y, pPlayer, this ); } if ( m_bForceUpdate || ( m_attackaxis.Get() != attack ) ) { m_attackaxis.Set( attack, pPlayer, this ); } if ( m_bForceUpdate || ( m_attack2axis.Get() != attack2 ) ) { m_attack2axis.Set( attack2, pPlayer, this ); } m_bForceUpdate = false; }
// CONSIDER: if player in water state, autoset and underwater soundscape? void CEnvSoundscape::UpdateForPlayer( ss_update_t &update ) { if ( !IsEnabled() ) { if ( update.pCurrentSoundscape == this ) { update.pCurrentSoundscape = NULL; update.currentDistance = 0; update.bInRange = false; } return; } // calc range from sound entity to player Vector target = EarPosition(); float range = (update.playerPosition - target).Length(); if ( update.pCurrentSoundscape == this ) { update.currentDistance = range; update.bInRange = false; if ( m_flRadius > range || m_flRadius == -1 ) { trace_t tr; update.traceCount++; UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1 && !tr.startsolid ) { update.bInRange = true; } } } else { if ( (!update.bInRange || range < update.currentDistance ) && (m_flRadius > range || m_flRadius == -1) ) { trace_t tr; update.traceCount++; UTIL_TraceLine( target, update.playerPosition, MASK_SOLID_BRUSHONLY|MASK_WATER, update.pPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1 && !tr.startsolid ) { audioparams_t &audio = update.pPlayer->GetAudioParams(); WriteAudioParamsTo( audio ); update.pCurrentSoundscape = this; update.bInRange = true; update.currentDistance = range; } } } if ( soundscape_debug.GetBool() ) { // draw myself NDebugOverlay::Box(GetAbsOrigin(), Vector(-10,-10,-10), Vector(10,10,10), 255, 0, 255, 64, NDEBUG_PERSIST_TILL_NEXT_SERVER ); if ( update.pPlayer ) { audioparams_t &audio = update.pPlayer->GetAudioParams(); if ( audio.entIndex != entindex() ) { if ( InRangeOfPlayer( update.pPlayer ) ) { NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 255, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } else { NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } } else { if ( InRangeOfPlayer( update.pPlayer ) ) { NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } else { NDebugOverlay::Line( GetAbsOrigin(), update.pPlayer->WorldSpaceCenter(), 255, 170, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } // also draw lines to each sound position. // we don't store the number of local sound positions, just a bitvector of which ones are on. unsigned int soundbits = audio.localBits.Get(); float periodic = 2.0f * sin((fmod(gpGlobals->curtime,2.0f) - 1.0f) * M_PI); // = -4f .. 4f for (int ii = 0 ; ii < NUM_AUDIO_LOCAL_SOUNDS ; ++ii ) { if ( soundbits & (1 << ii) ) { const Vector &soundLoc = audio.localSound.Get(ii); NDebugOverlay::Line( GetAbsOrigin(), soundLoc, 0, 32 , 255 , false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); NDebugOverlay::Cross3D( soundLoc, 16.0f + periodic, 0, 0, 255, false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } } } } NDebugOverlay::EntityTextAtPosition( GetAbsOrigin(), 0, STRING(m_soundscapeName), NDEBUG_PERSIST_TILL_NEXT_SERVER ); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) { if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) ) { // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) return; } if ( pOther->m_takedamage != DAMAGE_NO ) { trace_t tr, tr2; tr = BaseClass::GetTouchTrace(); Vector vecNormalizedVel = GetAbsVelocity(); ClearMultiDamage(); VectorNormalize( vecNormalizedVel ); #if defined(HL2_EPISODIC) //!!!HACKHACK - specific hack for ep2_outland_10 to allow crossbow bolts to pass through her bounding box when she's crouched in front of the player // (the player thinks they have clear line of sight because Alyx is crouching, but her BBOx is still full-height and blocks crossbow bolts. if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->Classify() == CLASS_PLAYER_ALLY_VITAL && FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") ) { // Change the owner to stop further collisions with Alyx. We do this by making her the owner. // The player won't get credit for this kill but at least the bolt won't magically disappear! SetOwnerEntity( pOther ); return; } #endif//HL2_EPISODIC if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() ) { CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_NEVERGIB ); dmgInfo.AdjustPlayerDamageInflictedForSkillLevel(); CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr ); CBasePlayer *pPlayer = ToBasePlayer( GetOwnerEntity() ); if ( pPlayer ) { gamestats->Event_WeaponHit( pPlayer, true, "weapon_crossbow", dmgInfo ); } } else { CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), sk_plr_dmg_crossbow.GetFloat(), DMG_BULLET | DMG_NEVERGIB ); CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr ); } ApplyMultiDamage(); //Adrian: keep going through the glass. if ( pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS ) return; /*if ( !pOther->IsAlive() ) { // We killed it! const surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps ); if ( pdata->game.material == CHAR_TEX_GLASS ) { return; } }*/ SetAbsVelocity( Vector( 0, 0, 0 ) ); // play body "thwack" sound EmitSound( "Weapon_Crossbow.BoltHitBody" ); Vector vForward; AngleVectors( GetAbsAngles(), &vForward ); VectorNormalize ( vForward ); UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_BLOCKLOS, pOther, COLLISION_GROUP_NONE, &tr2 ); if ( tr2.fraction != 1.0f ) { // NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 ); // NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 ); if ( tr2.m_pEnt == NULL || ( tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE ) ) { CEffectData data; data.m_vOrigin = tr2.endpos; data.m_vNormal = vForward; data.m_nEntIndex = tr2.fraction != 1.0f; DispatchEffect( "BoltImpact", data ); } } SetTouch( NULL ); SetThink( NULL ); if ( !g_pGameRules->IsMultiplayer() ) { UTIL_Remove( this ); } } else { trace_t tr; tr = BaseClass::GetTouchTrace(); // See if we struck the world if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) ) { EmitSound( "Weapon_Crossbow.BoltHitWorld" ); // if what we hit is static architecture, can stay around for a while. Vector vecDir = GetAbsVelocity(); float speed = VectorNormalize( vecDir ); // See if we should reflect off this surface float hitDot = DotProduct( tr.plane.normal, -vecDir ); if ( ( hitDot < 0.5f ) && ( speed > 100 ) ) { Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir; QAngle reflectAngles; VectorAngles( vReflection, reflectAngles ); SetLocalAngles( reflectAngles ); SetAbsVelocity( vReflection * speed * 0.75f ); // Start to sink faster SetGravity( 1.0f ); } else { SetThink( &CCrossbowBolt::SUB_Remove ); SetNextThink( gpGlobals->curtime + 2.0f ); //FIXME: We actually want to stick (with hierarchy) to what we've hit SetMoveType( MOVETYPE_NONE ); Vector vForward; AngleVectors( GetAbsAngles(), &vForward ); VectorNormalize ( vForward ); CEffectData data; data.m_vOrigin = tr.endpos; data.m_vNormal = vForward; data.m_nEntIndex = 0; DispatchEffect( "BoltImpact", data ); UTIL_ImpactTrace( &tr, DMG_BULLET ); AddEffects( EF_NODRAW ); SetTouch( NULL ); SetThink( &CCrossbowBolt::SUB_Remove ); SetNextThink( gpGlobals->curtime + 2.0f ); if ( m_pGlowSprite != NULL ) { m_pGlowSprite->TurnOn(); m_pGlowSprite->FadeAndDie( 3.0f ); } } // Shoot some sparks if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER) { g_pEffects->Sparks( GetAbsOrigin() ); } } else { // Put a mark unless we've hit the sky if ( ( tr.surface.flags & SURF_SKY ) == false ) { UTIL_ImpactTrace( &tr, DMG_BULLET ); } UTIL_Remove( this ); } } if ( g_pGameRules->IsMultiplayer() ) { // SetThink( &CCrossbowBolt::ExplodeThink ); // SetNextThink( gpGlobals->curtime + 0.1f ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponCrossbow::FireBolt() { if ( m_iClip1 <= 0 ) { if ( !m_bFireOnEmpty ) Reload(); else { WeaponSound( EMPTY ); m_flNextPrimaryAttack = 0.15; } return; } CBasePlayer *pOwner = ToBasePlayer(GetOwner()); if ( !pOwner ) return; #ifndef CLIENT_DLL pOwner->RumbleEffect(RUMBLE_357, 0, RUMBLE_FLAG_RESTART); Vector vecAiming = pOwner->GetAutoaimVector(0); Vector vecSrc = pOwner->Weapon_ShootPosition(); QAngle angAiming; VectorAngles( vecAiming, angAiming ); #if defined(HL2_EPISODIC) // !!!HACK - the other piece of the Alyx crossbow bolt hack for Outland_10 (see ::BoltTouch() for more detail) if( FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") ) { trace_t tr; UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 24.0f, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr ); if( tr.m_pEnt != NULL && tr.m_pEnt->Classify() == CLASS_PLAYER_ALLY_VITAL ) { // If Alyx is right in front of the player, make sure the bolt starts outside of the player's BBOX, or the bolt // will instantly collide with the player after the owner of the bolt is switched to Alyx in ::BoltTouch(). We // avoid this altogether by making it impossible for the bolt to collide with the player. vecSrc += vecAiming * 24.0f; } } #endif CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, pOwner ); if ( pOwner->GetWaterLevel() == 3 ) pBolt->SetAbsVelocity(vecAiming * BOLT_WATER_VELOCITY); else pBolt->SetAbsVelocity(vecAiming * BOLT_AIR_VELOCITY); #endif m_iClip1--; pOwner->ViewPunch(QAngle( -2, 0, 0 )); WeaponSound(SINGLE); WeaponSound(SPECIAL2); #ifndef CLIENT_DLL CSoundEnt::InsertSound(SOUND_COMBAT, GetAbsOrigin(), 200, 0.2); #endif SendWeaponAnim(ACT_VM_PRIMARYATTACK); // HEV suit - indicate out of ammo condition if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0); m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75; DoLoadEffect(); SetChargerState(CHARGER_STATE_DISCHARGE); }
//----------------------------------------------------------------------------- // Chooses an item when the player is full //----------------------------------------------------------------------------- void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBasePlayer *pPlayer, int iDebug ) { // Can we not actually spawn the item? if ( !HasSpawnFlags(SF_DYNAMICRESUPPLY_ALWAYS_SPAWN) ) return; float flRatio[NUM_AMMO_ITEMS]; int i; float flTotalProb = 0.0f; for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) { int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef ); bool bCanSpawn = pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) != NULL; if ( bCanSpawn && ( g_DynamicResupplyAmmoItems[i].flFullProbability != 0 ) && ( pMaster->m_flDesiredAmmo[i] != 0.0f ) ) { flTotalProb += g_DynamicResupplyAmmoItems[i].flFullProbability; flRatio[i] = flTotalProb; } else { flRatio[i] = -1.0f; } } if ( flTotalProb == 0.0f ) { // If we're supposed to fallback to just a health vial, do that and finish. if ( pMaster->HasSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL) ) { CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles(), this ); if ( iDebug ) { Msg("Player is full, spawning item_healthvial due to spawnflag.\n"); } return; } // Otherwise, spawn the first ammo item in the list flRatio[0] = 1.0f; flTotalProb = 1.0f; } float flChoice = random->RandomFloat( 0.0f, flTotalProb ); for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) { if ( flChoice <= flRatio[i] ) { CBaseEntity::Create( g_DynamicResupplyAmmoItems[i].sEntityName, GetAbsOrigin(), GetAbsAngles(), this ); if ( iDebug ) { Msg("Player is full, spawning %s \n", g_DynamicResupplyAmmoItems[i].sEntityName ); } return; } } if ( iDebug ) { Msg("Player is full on all health + ammo, is not spawning.\n" ); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CSprite::Spawn( void ) { SetSolid( SOLID_NONE ); SetMoveType( MOVETYPE_NONE ); m_flFrame = 0; Precache(); SetModel( STRING( GetModelName() ) ); CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE ); m_flMaxFrame = (float)modelinfo->GetModelFrameCount( GetModel() ) - 1; AddEffects( EF_NOSHADOW | EF_NORECEIVESHADOW ); #if !defined( CLIENT_DLL ) if ( m_flGlowProxySize > MAX_GLOW_PROXY_SIZE ) { // Clamp on Spawn to prevent per-frame spew DevWarning( "env_sprite at setpos %0.0f %0.0f %0.0f has invalid glow size %f - clamping to %f\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, m_flGlowProxySize.Get(), MAX_GLOW_PROXY_SIZE ); m_flGlowProxySize = MAX_GLOW_PROXY_SIZE; } if ( GetEntityName() != NULL_STRING && !(m_spawnflags & SF_SPRITE_STARTON) ) { TurnOff(); } else #endif { TurnOn(); } // Worldcraft only sets y rotation, copy to Z if ( GetLocalAngles().y != 0 && GetLocalAngles().z == 0 ) { QAngle angles = GetLocalAngles(); angles.z = angles.y; angles.y = 0; SetLocalAngles( angles ); } // Clamp our scale if necessary float scale = m_flSpriteScale; if ( scale < 0 || scale > MAX_SPRITE_SCALE ) { #if !defined( CLIENT_DLL ) DevMsg( "LEVEL DESIGN ERROR: Sprite %s with bad scale %f [0..%f]\n", GetDebugName(), m_flSpriteScale, MAX_SPRITE_SCALE ); #endif scale = clamp( m_flSpriteScale, 0, MAX_SPRITE_SCALE ); } //Set our state SetBrightness( GetRenderAlpha() ); SetScale( scale ); #if defined( CLIENT_DLL ) m_flStartScale = m_flDestScale = m_flSpriteScale; m_nStartBrightness = m_nDestBrightness = m_nBrightness; #endif }
void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther ) { float flpitch; TraceResult tr = UTIL_GetGlobalTrace(); // don't hit the guy that launched this grenade if( pev->owner && pOther->edict() == pev->owner ) return; // at least until we've bounced once pev->owner = NULL; pev->angles.x = 0; pev->angles.z = 0; // avoid bouncing too much if( m_flNextHit > gpGlobals->time ) return; // higher pitch as squeeker gets closer to detonation time flpitch = 155.0 - 60.0 * ( ( m_flDie - gpGlobals->time ) / SQUEEK_DETONATE_DELAY ); if( pOther->pev->takedamage && m_flNextAttack < gpGlobals->time ) { // attack! // make sure it's me who has touched them if( tr.pHit == pOther->edict() ) { // and it's not another squeakgrenade if( tr.pHit->v.modelindex != pev->modelindex ) { // ALERT( at_console, "hit enemy\n"); g_MultiDamage.Clear(); pOther->TraceAttack( CTakeDamageInfo( this, gSkillData.GetSnarkDmgBite(), DMG_SLASH ), gpGlobals->v_forward, &tr ); if( m_hOwner != NULL ) g_MultiDamage.ApplyMultiDamage( this, m_hOwner ); else g_MultiDamage.ApplyMultiDamage( this, this ); pev->dmg += gSkillData.GetSnarkDmgPop(); // add more explosion damage // m_flDie += 2.0; // add more life // make bite sound EMIT_SOUND_DYN( this, CHAN_WEAPON, "squeek/sqk_deploy1.wav", 1.0, ATTN_NORM, 0, ( int ) flpitch ); m_flNextAttack = gpGlobals->time + 0.5; } } else { // ALERT( at_console, "been hit\n"); } } m_flNextHit = gpGlobals->time + 0.1; m_flNextHunt = gpGlobals->time; if( g_pGameRules->IsMultiplayer() ) { // in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows. if( gpGlobals->time < m_flNextBounceSoundTime ) { // too soon! return; } } if( !( pev->flags & FL_ONGROUND ) ) { // play bounce sound float flRndSound = RANDOM_FLOAT( 0, 1 ); if( flRndSound <= 0.33 ) EMIT_SOUND_DYN( this, CHAN_VOICE, "squeek/sqk_hunt1.wav", 1, ATTN_NORM, 0, ( int ) flpitch ); else if( flRndSound <= 0.66 ) EMIT_SOUND_DYN( this, CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, ( int ) flpitch ); else EMIT_SOUND_DYN( this, CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, ( int ) flpitch ); CSoundEnt::InsertSound( bits_SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 ); } else { // skittering sound CSoundEnt::InsertSound( bits_SOUND_COMBAT, GetAbsOrigin(), 100, 0.1 ); } m_flNextBounceSoundTime = gpGlobals->time + 0.5;// half second. }
void CSqueakGrenade::HuntThink( void ) { // ALERT( at_console, "think\n" ); if( !IsInWorld() ) { SetTouch( NULL ); UTIL_Remove( this ); return; } StudioFrameAdvance(); pev->nextthink = gpGlobals->time + 0.1; // explode when ready if( gpGlobals->time >= m_flDie ) { g_vecAttackDir = pev->velocity.Normalize(); pev->health = -1; Killed( CTakeDamageInfo( this, 0, 0 ), GIB_NORMAL ); return; } // float if( GetWaterLevel() != WATERLEVEL_DRY ) { if( pev->movetype == MOVETYPE_BOUNCE ) { pev->movetype = MOVETYPE_FLY; } pev->velocity = pev->velocity * 0.9; pev->velocity.z += 8.0; } else if( pev->movetype == MOVETYPE_FLY ) { pev->movetype = MOVETYPE_BOUNCE; } // return if not time to hunt if( m_flNextHunt > gpGlobals->time ) return; m_flNextHunt = gpGlobals->time + 2.0; CBaseEntity *pOther = NULL; Vector vecDir; TraceResult tr; Vector vecFlat = pev->velocity; vecFlat.z = 0; vecFlat = vecFlat.Normalize(); UTIL_MakeVectors( pev->angles ); if( m_hEnemy == NULL || !m_hEnemy->IsAlive() ) { // find target, bounce a bit towards it. Look( 512 ); m_hEnemy = BestVisibleEnemy(); } // squeek if it's about time blow up if( ( m_flDie - gpGlobals->time <= 0.5 ) && ( m_flDie - gpGlobals->time >= 0.3 ) ) { EMIT_SOUND_DYN( this, CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + RANDOM_LONG( 0, 0x3F ) ); CSoundEnt::InsertSound( bits_SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 ); } // higher pitch as squeeker gets closer to detonation time float flpitch = 155.0 - 60.0 * ( ( m_flDie - gpGlobals->time ) / SQUEEK_DETONATE_DELAY ); if( flpitch < 80 ) flpitch = 80; if( m_hEnemy != NULL ) { if( FVisible( m_hEnemy ) ) { vecDir = m_hEnemy->EyePosition() - GetAbsOrigin(); m_vecTarget = vecDir.Normalize(); } float flVel = pev->velocity.Length(); float flAdj = 50.0 / ( flVel + 10.0 ); if( flAdj > 1.2 ) flAdj = 1.2; // ALERT( at_console, "think : enemy\n"); // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); pev->velocity = pev->velocity * flAdj + m_vecTarget * 300; } if( pev->flags & FL_ONGROUND ) { pev->avelocity = Vector( 0, 0, 0 ); } else { if( pev->avelocity == Vector( 0, 0, 0 ) ) { pev->avelocity.x = RANDOM_FLOAT( -100, 100 ); pev->avelocity.z = RANDOM_FLOAT( -100, 100 ); } } if( ( GetAbsOrigin() - m_posPrev ).Length() < 1.0 ) { pev->velocity.x = RANDOM_FLOAT( -100, 100 ); pev->velocity.y = RANDOM_FLOAT( -100, 100 ); } m_posPrev = GetAbsOrigin(); pev->angles = UTIL_VecToAngles( pev->velocity ); pev->angles.z = 0; pev->angles.x = 0; }
//------------------------------------------------------------------------------ // Purpose : //------------------------------------------------------------------------------ void CLookDoor::MoveThink(void) { // -------------------------------- // Make sure we have a looker // -------------------------------- if (m_hLooker == NULL) { m_hLooker = (CBaseEntity*)gEntList.FindEntityByName( NULL, m_target ); if (m_hLooker == NULL) { return; } } //-------------------------------------- // Calculate an orgin for the door //-------------------------------------- Vector vOrigin = WorldSpaceCenter() - GetAbsOrigin(); // If FROM_OPEN flag is set, door proximity is measured // from the open and not the closed position if (FBitSet (m_spawnflags, SF_LDOOR_FROM_OPEN)) { vOrigin += m_vecPosition2; } // ------------------------------------------------------ // First add movement based on proximity // ------------------------------------------------------ float flProxMove = 0; if (m_flProximityDistance > 0) { float flDist = (m_hLooker->GetAbsOrigin() - vOrigin).Length()-m_flProximityOffset; if (flDist < 0) flDist = 0; if (flDist < m_flProximityDistance) { if (FBitSet (m_spawnflags, SF_LDOOR_THRESHOLD)) { flProxMove = 1.0; } else { flProxMove = 1-flDist/m_flProximityDistance; } } } // ------------------------------------------------------ // Then add movement based on view angle // ------------------------------------------------------ float flViewMove = 0; if (m_flFieldOfView > 0) { // ---------------------------------------- // Check that toucher is facing the target // ---------------------------------------- Assert( dynamic_cast< CBaseCombatCharacter* >( m_hLooker.Get() ) ); CBaseCombatCharacter* pBCC = (CBaseCombatCharacter*)m_hLooker.Get(); Vector vTouchDir = pBCC->EyeDirection3D( ); Vector vTargetDir = vOrigin - pBCC->EyePosition(); VectorNormalize(vTargetDir); float flDotPr = DotProduct(vTouchDir,vTargetDir); if (flDotPr < m_flFieldOfView) { flViewMove = 0.0; } else { flViewMove = (flDotPr-m_flFieldOfView)/(1.0 - m_flFieldOfView); } } //--------------------------------------- // Summate the two moves //--------------------------------------- float flMove = flProxMove + flViewMove; if (flMove > 1.0) { flMove = 1.0; } // If behavior is inverted do the reverse if (FBitSet (m_spawnflags, SF_LDOOR_INVERT)) { flMove = 1-flMove; } // Move the door SetPosition( flMove ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CNPC_VehicleDriver::OverridePathMove( float flInterval ) { // Setup our initial path data if we've just started running a path if ( !m_pCurrentWaypoint ) { m_vecPrevPoint = GetAbsOrigin(); m_vecPrevPrevPoint = GetAbsOrigin(); m_vecDesiredPosition = GetNavigator()->GetCurWaypointPos(); CalculatePostPoints(); // Init our two waypoints m_Waypoints[0] = new CVehicleWaypoint( m_vecPrevPrevPoint, m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint ); m_Waypoints[1] = new CVehicleWaypoint( m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint, m_vecPostPostPoint ); m_pCurrentWaypoint = m_Waypoints[0]; m_pNextWaypoint = m_Waypoints[1]; m_flDistanceAlongSpline = 0.2; } // Have we reached our target? See if we've passed the current waypoint's plane. Vector vecAbsMins, vecAbsMaxs; CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs ); if ( BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pCurrentWaypoint->planeWaypoint ) == 3 ) { if ( WaypointReached() ) return true; } // Did we bypass it and reach the next one already? if ( m_pNextWaypoint && BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pNextWaypoint->planeWaypoint ) == 3 ) { if ( WaypointReached() ) return true; } // We may have just teleported, so check to make sure we have a waypoint if ( !m_pCurrentWaypoint || !m_pNextWaypoint ) return false; // Figure out which spline we're trucking along CVehicleWaypoint *pCurrentSplineBeingTraversed = m_pCurrentWaypoint; if ( m_flDistanceAlongSpline > 1 ) { pCurrentSplineBeingTraversed = m_pNextWaypoint; } // Get our current speed, and check it against the length of the spline to know how far to advance our marker AngularImpulse angVel; Vector vecVelocity; IPhysicsObject *pVehiclePhysics = m_hVehicleEntity->VPhysicsGetObject(); if( !pVehiclePhysics ) { // I think my vehicle has been destroyed. return false; } pVehiclePhysics->GetVelocity( &vecVelocity, &angVel ); float flSpeed = vecVelocity.Length(); float flIncTime = gpGlobals->curtime - GetLastThink(); float flIncrement = flIncTime * (flSpeed / pCurrentSplineBeingTraversed->GetLength()); // Now advance our point along the spline m_flDistanceAlongSpline = clamp( m_flDistanceAlongSpline + flIncrement, 0, 2); if ( m_flDistanceAlongSpline > 1 ) { // We crossed the spline boundary pCurrentSplineBeingTraversed = m_pNextWaypoint; } Vector vSplinePoint = pCurrentSplineBeingTraversed->GetPointAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline ); Vector vSplineTangent = pCurrentSplineBeingTraversed->GetTangentAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline ); // Now that we've got the target spline point & tangent, use it to decide what our desired velocity is. // If we're close to the tangent, just use the tangent. Otherwise, Lerp towards it. Vector vecToDesired = (vSplinePoint - GetAbsOrigin()); float flDistToDesired = VectorNormalize( vecToDesired ); float flTangentLength = VectorNormalize( vSplineTangent ); if ( flDistToDesired > (flTangentLength * 0.75) ) { m_vecDesiredVelocity = vecToDesired * flTangentLength; } else { VectorLerp( vSplineTangent, vecToDesired * flTangentLength, (flDistToDesired / (flTangentLength * 0.5)), m_vecDesiredVelocity ); } // Decrease speed according to the turn we're trying to make Vector vecRight; m_hVehicleEntity->GetVectors( NULL, &vecRight, NULL ); Vector vecNormVel = m_vecDesiredVelocity; VectorNormalize( vecNormVel ); float flDotRight = DotProduct( vecRight, vecNormVel ); flSpeed = (1.0 - fabs(flDotRight)); // Don't go slower than we've been told to go if ( flSpeed < m_flDriversMinSpeed ) { flSpeed = m_flDriversMinSpeed; } m_vecDesiredVelocity = vecNormVel * (flSpeed * m_flMaxSpeed); // Bunch o'debug if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH ) { NDebugOverlay::Box( m_vecPrevPrevPoint, -Vector(15,15,15), Vector(15,15,15), 192,0,0, true, 0.1); NDebugOverlay::Box( m_vecPrevPoint, -Vector(20,20,20), Vector(20,20,20), 255,0,0, true, 0.1); NDebugOverlay::Box( m_vecPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,192,0, true, 0.1); NDebugOverlay::Box( m_vecPostPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,128,0, true, 0.1); NDebugOverlay::Box( vSplinePoint, -Vector(10,10,10), Vector(10,10,10), 0,0,255, true, 0.1); NDebugOverlay::Line( vSplinePoint, vSplinePoint + (vSplineTangent * 40), 0,0,255, true, 0.1); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[0], pCurrentSplineBeingTraversed->splinePoints[1], 30, 255,255,255,0, false, 0.1f ); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[1], pCurrentSplineBeingTraversed->splinePoints[2], 20, 255,255,255,0, false, 0.1f ); //NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[2], pCurrentSplineBeingTraversed->splinePoints[3], 10, 255,255,255,0, false, 0.1f ); // Draw the plane we're checking against for waypoint passing Vector vecPlaneRight; CrossProduct( m_pCurrentWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight ); Vector vecPlane = m_pCurrentWaypoint->splinePoints[2]; NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 255,0,0, true, 0.1); // Draw the next plane too CrossProduct( m_pNextWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight ); vecPlane = m_pNextWaypoint->splinePoints[2]; NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 192,0,0, true, 0.1); } if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH_SPLINE ) { for ( int i = 0; i < 10; i++ ) { Vector vecTarget = m_pCurrentWaypoint->GetPointAt( 0.1 * i ); Vector vecTangent = m_pCurrentWaypoint->GetTangentAt( 0.1 * i ); VectorNormalize(vecTangent); NDebugOverlay::Box( vecTarget, -Vector(10,10,10), Vector(10,10,10), 255,0,0, true, 0.1 ); NDebugOverlay::Line( vecTarget, vecTarget + (vecTangent * 10), 255,255,0, true, 0.1); } } return true; }
//----------------------------------------------------------------------------- // Run all of the AI for elements within the range iStart to iEnd //----------------------------------------------------------------------------- void CNPC_Blob::DoBlobBatchedAI( int iStart, int iEnd ) { float flInterval = gpGlobals->curtime - GetLastThink(); // Local fields for sin-wave movement variance float flMySine; float flAmplitude = npc_blob_sin_amplitude.GetFloat(); float flMyAmplitude; Vector vecRight; Vector vecForward; // Local fields for attract/repel float minDistSqr = Square( m_flMinElementDist ); float flBlobSpeed = blob_element_speed.GetFloat(); float flSpeed; // Local fields for speed limiting float flMinSpeed = blob_element_speed.GetFloat() * 0.5f; float flMaxSpeed = blob_element_speed.GetFloat() * 1.5f; bool bEnforceSpeedLimit; bool bEnforceRelativePositions; bool bDoMovementVariation; bool bDoOrientation = npc_blob_use_orientation.GetBool(); float flIdleSpeedFactor = npc_blob_idle_speed_factor.GetFloat(); // Group cohesion float flBlobRadiusSqr = Square( blob_radius.GetFloat() + 48.0f ); // Four feet of fudge // Build a right-hand vector along which we'll add some sine wave data to give each // element a unique insect-like undulation along an axis perpendicular to their path, // which makes the entire group look far less orderly if( GetEnemy() != NULL ) { // If I have an enemy, the right-hand vector is perpendicular to a straight line // from the group's centroid to the enemy's origin. vecForward = GetEnemy()->GetAbsOrigin() - m_vecCentroid; VectorNormalize( vecForward ); vecRight.x = vecForward.y; vecRight.y = -vecForward.x; } else { // If there is no enemy, wobble along the axis from the centroid to me. vecForward = GetAbsOrigin() - m_vecCentroid; VectorNormalize( vecForward ); vecRight.x = vecForward.y; vecRight.y = -vecForward.x; } //-- // MAIN LOOP - Run all of the elements in the set iStart to iEnd //-- for( int i = iStart ; i < iEnd ; i++ ) { CBlobElement *pThisElement = m_Elements[ i ]; //-- // Initial movement //-- // Start out with bEnforceSpeedLimit set to false. This is because an element // can't overspeed if it's moving undisturbed towards its target entity or // target location. An element can only under or overspeed when it is repelled // by multiple other elements in the group. See "Relative Positions" below. // // Initialize some 'defaults' that may be changed for each iteration of this loop bEnforceSpeedLimit = false; bEnforceRelativePositions = true; bDoMovementVariation = true; flSpeed = flBlobSpeed; switch( pThisElement->GetActiveMovementRule() ) { case BLOB_MOVE_DONT_MOVE: { pThisElement->SetElementVelocity( vec3_origin, true ); trace_t tr; Vector vecOrigin = pThisElement->GetAbsOrigin(); UTIL_TraceLine( vecOrigin, vecOrigin - Vector( 0, 0, 16), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if( tr.fraction < 1.0f ) { QAngle angles; VectorAngles( tr.plane.normal, angles ); float flSwap = angles.x; angles.x = -angles.y; angles.y = flSwap; pThisElement->SetAbsAngles( angles ); } } continue; break; case BLOB_MOVE_TO_TARGET_LOCATION: { Vector vecDiff = pThisElement->GetAbsOrigin() - pThisElement->m_vecTargetLocation; if( vecDiff.Length2DSqr() <= Square(80.0f) ) { // Don't shove this guy around any more, let him get to his goal position. flSpeed *= 0.5f; bEnforceRelativePositions = false; bDoMovementVariation = false; } pThisElement->MoveTowardsTargetLocation( flSpeed ); } break; case BLOB_MOVE_TO_TARGET_ENTITY: { if( !IsMoving() && GetEnemy() == NULL ) { if( pThisElement->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) <= flBlobRadiusSqr ) { flSpeed = (flSpeed * flIdleSpeedFactor) * pThisElement->m_flRandomEightyPercent; } } pThisElement->MoveTowardsTargetEntity( flSpeed ); } break; default: Msg("ERROR: Blob Element with unspecified Movement Rule\n"); break; } //--- // Relative positions //-- // Check this element against ALL other elements. If the two elements are closer // than the allowed minimum distance, repel this element away. (The other element // will repel when its AI runs). A single element can be repelled by many other // elements. This is why bEnforceSpeedLimit is set to true if any of the repelling // code runs for this element. Multiple attempts to repel an element in the same // direction will cause overspeed. Conflicting attempts to repel an element in opposite // directions will cause underspeed. Vector vecDir = Vector( 0, 0, 0 ); Vector vecThisElementOrigin = pThisElement->GetAbsOrigin(); if( bEnforceRelativePositions ) { for( int j = 0 ; j < m_Elements.Count() ; j++ ) { // This is the innermost loop! We should optimize here, if anywhere. // If this element is on the wall, then don't be repelled by anyone. Repelling // elements that are trying to climb a wall usually make them look like they // fall off the wall a few times while climbing. if( pThisElement->m_bOnWall ) continue; CBlobElement *pThatElement = m_Elements[ j ]; if( i != j ) { Vector vecThatElementOrigin = pThatElement->GetAbsOrigin(); float distSqr = vecThisElementOrigin.DistToSqr( vecThatElementOrigin ); if( distSqr < minDistSqr ) { // Too close to the other element. Move away. float flRepelSpeed; Vector vecRepelDir = ( vecThisElementOrigin - vecThatElementOrigin ); vecRepelDir.NormalizeInPlace(); flRepelSpeed = (flSpeed * ( 1.0f - ( distSqr / minDistSqr ) ) ) * pThatElement->GetSinePhase(); pThisElement->AddElementVelocity( vecRepelDir * flRepelSpeed, true ); // Since we altered this element's velocity after it was initially set, there's a chance // that the sums of multiple vectors will cause the element to over or underspeed, so // mark it for speed limit enforcement bEnforceSpeedLimit = true; } } } } //-- // Movement variation //-- if( bDoMovementVariation ) { flMySine = sin( gpGlobals->curtime * pThisElement->GetSineFrequency() ); flMyAmplitude = flAmplitude * pThisElement->GetSineAmplitude(); pThisElement->AddElementVelocity( vecRight * (flMySine * flMyAmplitude), true ); } // Avoidance for( int a = 0 ; a < m_iNumAvoidOrigins ; a++ ) { Vector vecAvoidDir = pThisElement->GetAbsOrigin() - m_vecAvoidOrigin[ a ]; if( vecAvoidDir.LengthSqr() <= (m_flAvoidRadiusSqr * pThisElement->m_flRandomEightyPercent) ) { VectorNormalize( vecAvoidDir ); pThisElement->AddElementVelocity( vecAvoidDir * (flSpeed * 2.0f), true ); break; } } //-- // Speed limits //--- if( bEnforceSpeedLimit == true ) { pThisElement->EnforceSpeedLimits( flMinSpeed, flMaxSpeed ); } //-- // Wall crawling //-- pThisElement->ModifyVelocityForSurface( flInterval, flSpeed ); // For identifying stuck elements. pThisElement->m_vecPrevOrigin = pThisElement->GetAbsOrigin(); pThisElement->m_flDistFromCentroidSqr = pThisElement->m_vecPrevOrigin.DistToSqr( m_vecCentroid ); // Orientation if( bDoOrientation ) { QAngle angles; VectorAngles( pThisElement->GetAbsVelocity(), angles ); pThisElement->SetAbsAngles( angles ); } /* //-- // Stragglers/Group integrity // if( pThisElement->m_flDistFromCentroidSqr > flStragglerDistSqr ) { NDebugOverlay::Line( pThisElement->GetAbsOrigin(), m_vecCentroid, 255, 0, 0, false, 0.025f ); } */ } }
//----------------------------------------------------------------------------- // Purpose: This takes the current place the NPC's trying to get to, figures out // what keys to press to get the vehicle to go there, and then sends // them to the vehicle. //----------------------------------------------------------------------------- void CNPC_VehicleDriver::DriveVehicle( void ) { AngularImpulse angVel; Vector vecVelocity; IPhysicsObject *pVehiclePhysics = m_hVehicleEntity->VPhysicsGetObject(); if ( !pVehiclePhysics ) return; pVehiclePhysics->GetVelocity( &vecVelocity, &angVel ); float flSpeed = VectorNormalize( vecVelocity ); // If we have no target position to drive to, brake to a halt if ( !m_flMaxSpeed || m_vecDesiredPosition == vec3_origin ) { if ( flSpeed > 1 ) { m_pVehicleInterface->NPC_Brake(); } return; } if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH ) { NDebugOverlay::Box(m_vecDesiredPosition, -Vector(20,20,20), Vector(20,20,20), 0,255,0, true, 0.1); NDebugOverlay::Line(GetAbsOrigin(), GetAbsOrigin() + m_vecDesiredVelocity, 0,255,0, true, 0.1); } m_flGoalSpeed = VectorNormalize(m_vecDesiredVelocity); // Is our target in front or behind us? Vector vecForward, vecRight; m_hVehicleEntity->GetVectors( &vecForward, &vecRight, NULL ); float flDot = DotProduct( vecForward, m_vecDesiredVelocity ); bool bBehind = ( flDot < 0 ); float flVelDot = DotProduct( vecVelocity, m_vecDesiredVelocity ); bool bGoingWrongWay = ( flVelDot < 0 ); // Figure out whether we should accelerate / decelerate if ( bGoingWrongWay || (flSpeed < m_flGoalSpeed) ) { // If it's behind us, go backwards not forwards if ( bBehind ) { m_pVehicleInterface->NPC_ThrottleReverse(); } else { m_pVehicleInterface->NPC_ThrottleForward(); } } else { // Brake if we're go significantly too fast if ( (flSpeed - 200) > m_flGoalSpeed ) { m_pVehicleInterface->NPC_Brake(); } else { m_pVehicleInterface->NPC_ThrottleCenter(); } } // Do we need to turn? float flDotRight = DotProduct( vecRight, m_vecDesiredVelocity ); if ( bBehind ) { // If we're driving backwards, flip our turning flDotRight *= -1; } // Map it to the vehicle's steering flDotRight *= (m_flSteering / 90); if ( flDotRight < 0 ) { // Turn left m_pVehicleInterface->NPC_TurnLeft( -flDotRight ); } else if ( flDotRight > 0 ) { // Turn right m_pVehicleInterface->NPC_TurnRight( flDotRight ); } else { m_pVehicleInterface->NPC_TurnCenter(); } }
void CNPC_Roller::Unstick( void ) { CBaseEntity *pList[ 16 ]; IPhysicsObject *pPhysObj; int i; Vector vecDirToEnemy; Vector vecDirToObject; m_flWaitFinished = gpGlobals->curtime; int count = UTIL_EntitiesInBox( pList, 16, GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, 0 ); m_vecUnstickDirection = vec3_origin; for( i = 0 ; i < count ; i++ ) { pPhysObj = pList[ i ]->VPhysicsGetObject(); if( !pPhysObj || pList[ i ]->m_iClassname == m_iClassname ) { // Only consider physics objects. Exclude rollers. continue; } if( pPhysObj->GetMass() <= ROLLER_MAX_PUSH_MASS ) { // Try to bash this physics object. trace_t tr; AI_TraceLine( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if( tr.fraction == 1.0 || tr.m_pEnt == pList[ i ] ) { // Roll towards this item if the trace hits nothing, or // the trace hits the object. Vector vecBashDir; vecBashDir = pList[ i ]->GetAbsOrigin() - GetAbsOrigin(); VectorNormalize( vecBashDir ); vecBashDir.z = 0.0; //NDebugOverlay::Line( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), 0,255,0, true, 2 ); m_vecUnstickDirection = vecBashDir * 80; return; } } } // No physics objects. Just pick a direction with some clearance and go there. #define ROLLER_UNSTICK_DIST 80 Vector vecDirections[ 4 ] = { Vector( 0, ROLLER_UNSTICK_DIST, 0 ), Vector( ROLLER_UNSTICK_DIST, 0, 0 ), Vector( 0, -ROLLER_UNSTICK_DIST, 0 ), Vector( -ROLLER_UNSTICK_DIST, 0, 0 ) }; trace_t tr; for( i = 0 ; i < 4 ; i++ ) { AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecDirections[ i ], MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if( tr.fraction == 1.0 ) { m_vecUnstickDirection = vecDirections[ i ]; //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + m_vecUnstickDirection, 255,255,0, true, 2 ); // Roll in this direction for a couple of seconds. Msg( "unsticking!\n" ); return; } } }
void AddEntity( void ) { Vector direction = GetAbsOrigin() - m_vecLastOrigin; float flDist = VectorNormalize( direction ); int numBubbles = (int) ( flDist * BUBBLES_PER_INCH ); if ( numBubbles < 1 ) numBubbles = 1; // Make bubbles SimpleParticle *sParticle; Vector offset; for ( int i = 0; i < numBubbles; i++ ) { offset = m_vecLastOrigin + ( direction * ( flDist / numBubbles ) * i ) + RandomVector( -2.5f, 2.5f ); sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), m_pEmitter->GetPMaterial( "effects/bubble" ), offset ); if ( sParticle ) { sParticle->m_flLifetime = 0.0f; sParticle->m_flDieTime = random->RandomFloat( 0.75f, 1.25f ); sParticle->m_flRoll = 0; sParticle->m_flRollDelta = 0; unsigned char color = random->RandomInt( 128, 255 ); sParticle->m_uchColor[0] = color; sParticle->m_uchColor[1] = color; sParticle->m_uchColor[2] = color; sParticle->m_uchStartAlpha = 255; sParticle->m_uchEndAlpha = 0; sParticle->m_uchStartSize = random->RandomInt( 1, 2 ); sParticle->m_uchEndSize = sParticle->m_uchStartSize; sParticle->m_vecVelocity = ( direction * 64.0f ) + Vector( 0, 0, 32 ); } sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), m_pEmitter->GetPMaterial( "effects/splash2" ), offset ); if ( sParticle ) { sParticle->m_flLifetime = 0.0f; sParticle->m_flDieTime = 0.2f; sParticle->m_flRoll = random->RandomInt( 0, 360 ); sParticle->m_flRollDelta = random->RandomInt( -4, 4 );; unsigned char color = random->RandomInt( 200, 255 ); sParticle->m_uchColor[0] = color; sParticle->m_uchColor[1] = color; sParticle->m_uchColor[2] = color; sParticle->m_uchStartAlpha = 128; sParticle->m_uchEndAlpha = 0; sParticle->m_uchStartSize = 2; sParticle->m_uchEndSize = sParticle->m_uchStartSize * 4; sParticle->m_vecVelocity = ( direction * 64.0f ) + Vector( 0, 0, 32 ); } } // Save our last position m_vecLastOrigin = GetAbsOrigin(); BaseClass::AddEntity(); }
//----------------------------------------------------------------------------- // Purpose: Stick to an entity (using hierarchy if we can) // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWeaponStriderBuster::StickToEntity( CBaseEntity *pOther ) { // Make sure the object is travelling fast enough to stick if ( m_flCollisionSpeedSqr > 50 && !m_bNoseDiving ) { // See if this is a valid strider bit if ( ShouldStickToEntity( pOther ) ) { // Attempt to constraint to it if ( CreateConstraintToObject( pOther ) ) { // Only works for striders, at the moment CBaseEntity *pFollowParent = pOther->GetOwnerEntity(); if ( pFollowParent == NULL ) return false; // Allows us to identify our constrained object later SetOwnerEntity( pFollowParent ); // Make a sound EmitSound( "Weapon_StriderBuster.StickToEntity" ); DispatchParticleEffect( "striderbuster_attach", GetAbsOrigin(), GetAbsAngles(), NULL ); if( striderbuster_use_particle_flare.GetBool() ) { // We don't have to save any pointers or handles to this because it's parented to the buster. // So it will die when the buster dies. Yay. CParticleSystem *pFlare = (CParticleSystem *) CreateEntityByName( "info_particle_system" ); if ( pFlare != NULL ) { pFlare->KeyValue( "start_active", "1" ); pFlare->KeyValue( "effect_name", "striderbuster_attached_pulse" ); pFlare->SetParent( this ); pFlare->SetLocalOrigin( vec3_origin ); DispatchSpawn( pFlare ); pFlare->Activate(); } } else { // Create a glow sprite m_hGlowSprite = CSprite::SpriteCreate( "sprites/orangeflare1.vmt", GetLocalOrigin(), false ); Assert( m_hGlowSprite ); if ( m_hGlowSprite != NULL ) { m_hGlowSprite->TurnOn(); m_hGlowSprite->SetTransparency( kRenderWorldGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); m_hGlowSprite->SetAbsOrigin( GetAbsOrigin() ); m_hGlowSprite->SetScale( 5.0f ); m_hGlowSprite->m_nRenderFX = kRenderFxStrobeFaster; m_hGlowSprite->SetGlowProxySize( 16.0f ); m_hGlowSprite->SetParent( this ); } } // Stop touching things SetTouch( NULL ); // Must be a strider CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent); if ( pStrider == NULL ) return false; // Notify the strider we're attaching to him pStrider->StriderBusterAttached( this ); m_OnAttachToStrider.FireOutput( this, this ); // Start the ping sound. SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext ); // Don't autodelete this one! WeaponManager_RemoveManaged( this ); return true; } return false; } } return false; }
// CONSIDER: if player in water state, autoset and underwater soundscape? void CEnvSoundscape::Update() { bool bUpdated = UpdatePlayersInPVS(); if ( !IsEnabled() ) return; // Only update soundscapes in multiplayer when the PVS gets updated if ( g_pGameRules->IsMultiplayer() && !bUpdated && !soundscape_debug.GetBool() ) return; bool bDebugThis = soundscape_debug.GetInt() == 1; for ( int i=0; i < m_hPlayersInPVS.Count(); i++ ) { CBasePlayer *pPlayer = m_hPlayersInPVS[i]; if ( !pPlayer ) continue; if ( !InRangeOfPlayer( pPlayer ) ) continue; // check to see if this is the sound entity that is // currently affecting this player audioparams_t &audio = pPlayer->GetAudioParams(); // if we got this far, we're looking at an entity that is contending // for current player sound. the closest entity to player wins. CEnvSoundscape *pCurrent = (CEnvSoundscape *)audio.ent.Get(); if ( !pCurrent || !pCurrent->IsEnabled() || !pCurrent->InRangeOfPlayer( pPlayer ) ) { // The old one is obscured or out of range.. take over. WriteAudioParamsTo( audio ); } else if ( pCurrent && EarPosition().DistTo( pPlayer->EarPosition() ) < pCurrent->EarPosition().DistTo( pPlayer->EarPosition() ) ) { // new entity is closer to player, so it wins. WriteAudioParamsTo( audio ); } if ( !bDebugThis ) { bDebugThis = soundscape_debug.GetInt() == 2; } } if ( bDebugThis ) { // draw myself NDebugOverlay::Box(GetAbsOrigin(), Vector(-10,-10,-10), Vector(10,10,10), 255, 0, 255, 64, NDEBUG_PERSIST_TILL_NEXT_SERVER ); // Don't use GetLocalPlayer(), because that prevents multiplayer games using this for testing with a single client in the game CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { audioparams_t &audio = pPlayer->GetAudioParams(); if ( audio.ent.Get() != this ) { if ( InRangeOfPlayer( pPlayer ) ) { NDebugOverlay::Line( GetAbsOrigin(), pPlayer->WorldSpaceCenter(), 255, 255, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } else { NDebugOverlay::Line( GetAbsOrigin(), pPlayer->WorldSpaceCenter(), 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } } else { if ( InRangeOfPlayer( pPlayer ) ) { NDebugOverlay::Line( GetAbsOrigin(), pPlayer->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } else { NDebugOverlay::Line( GetAbsOrigin(), pPlayer->WorldSpaceCenter(), 255, 170, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } // also draw lines to each sound position. // we don't store the number of local sound positions, just a bitvector of which ones are on. unsigned int soundbits = audio.localBits.Get(); float periodic = 2.0f * sin((fmod(gpGlobals->curtime,2.0f) - 1.0f) * M_PI); // = -4f .. 4f for (int ii = 0 ; ii < NUM_AUDIO_LOCAL_SOUNDS ; ++ii ) { if ( soundbits & (1 << ii) ) { const Vector &soundLoc = audio.localSound.Get(ii); NDebugOverlay::Line( GetAbsOrigin(), soundLoc, 0, 32 , 255 , false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); NDebugOverlay::Cross3D( soundLoc, 16.0f + periodic, 0, 0, 255, false, NDEBUG_PERSIST_TILL_NEXT_SERVER ); } } } } NDebugOverlay::EntityTextAtPosition( GetAbsOrigin(), 0, STRING(m_soundscapeName), NDEBUG_PERSIST_TILL_NEXT_SERVER ); } }
//----------------------------------------------------------------------------- // Purpose: Intercept damage and decide whether or not we want to trigger // Input : &info - //----------------------------------------------------------------------------- int CWeaponStriderBuster::OnTakeDamage( const CTakeDamageInfo &info ) { // If we're attached, any damage from the player makes us trigger CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pAttacker = info.GetAttacker(); bool bInflictorIsPlayer = ( pInflictor != NULL && pInflictor->IsPlayer() ); bool bAttackerIsPlayer = ( pAttacker != NULL && pAttacker->IsPlayer() ); if ( GetParent() && GetParent()->ClassMatches( g_iszVehicle ) ) { return 0; } // Only take damage from a player, for the moment if ( striderbuster_allow_all_damage.GetBool() || ( IsAttachedToStrider() && ( bAttackerIsPlayer || bInflictorIsPlayer ) ) ) { Detonate(); return 0; } if ( pAttacker && ( pAttacker->Classify() == CLASS_COMBINE || pAttacker->Classify() == CLASS_COMBINE_HUNTER ) ) { if ( VPhysicsGetObject() && !VPhysicsGetObject()->IsMoveable() ) { return 0; } } // Hunters are able to destroy strider busters if ( hunter_hate_held_striderbusters.GetBool() || hunter_hate_thrown_striderbusters.GetBool() || hunter_hate_attached_striderbusters.GetBool() ) { if ( ( GetHealth() > 0 ) && ( pInflictor != NULL ) && FClassnameIs( pInflictor, "hunter_flechette" ) ) { // // Flechette impacts don't hurt the striderbuster unless it's attached to a strider, // but the explosions always do. This is so that held or thrown striderbusters fly // awry because of the flechette, but attached striderbusters break instantly to make // the hunters more effective at defending the strider. // if ( IsAttachedToStrider() || !( info.GetDamageType() & DMG_NEVERGIB ) ) { if( striderbuster_die_detach.GetBool() && IsAttachedToStrider() ) { // Make the buster fall off and break. m_takedamage = DAMAGE_NO; CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(GetOwnerEntity()); Assert( pStrider != NULL ); pStrider->StriderBusterDetached( this ); DestroyConstraint(); // Amplify some lateral force. Vector vecForce = info.GetDamageForce(); vecForce.z = 0.0f; VPhysicsGetObject()->ApplyForceCenter( vecForce * 5.0f ); SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext ); SetThink( &CWeaponStriderBuster::BusterDetachThink ); SetNextThink( gpGlobals->curtime ); m_iBusterFlags |= STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER; return 0; } else { // Destroy the buster in place // Make sure they know it blew up prematurely. EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() ); SetHealth( 0 ); Shatter( info.GetAttacker() ); return 0; } } if ( info.GetDamage() < 5 ) { bool bFirst = ( m_CarryAngles.x == 45 && m_CarryAngles.y == 0 && m_CarryAngles.z == 0); float sinTime = sin( gpGlobals->curtime ); bool bSubtractX = ( bFirst ) ? ( sinTime < 0 ) : ( m_CarryAngles.x < 45 ); m_CarryAngles.x += ( 10.0 + 10.0 * fabsf( sinTime ) + random->RandomFloat( -2.5, 2.5 ) + random->RandomFloat( -2.5, 2.5 ) ) * ( ( bSubtractX ) ? -1.0 : 1.0 ); m_CarryAngles.y = 15 * ( sin( gpGlobals->curtime ) + cos( gpGlobals->curtime * 0.5 ) ) * .5 + random->RandomFloat( -15, 15 ); m_CarryAngles.z = 7.5 * ( sin( gpGlobals->curtime ) + sin( gpGlobals->curtime * 2.0 ) ) * .5 + random->RandomFloat( -7.5, 7.5 ); } return 1; } } // Allow crushing damage if ( info.GetDamageType() & DMG_CRUSH ) return BaseClass::OnTakeDamage( info ); return 0; }
virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ) { return GetAbsOrigin(); }
//----------------------------------------------------------------------------- // Purpose: Give the buster a slight attraction to striders. // Ported back from the magnade. //----------------------------------------------------------------------------- void CWeaponStriderBuster::BusterFlyThink() { if (IsAttachedToStrider()) return; // early out. Think no more. // If we're nosediving, forget about magnetism. if ( m_bNoseDiving ) { if ( VPhysicsGetObject() ) VPhysicsGetObject()->ApplyForceCenter( Vector( 0, 0, striderbuster_dive_force.GetFloat() ) ); SetNextThink(gpGlobals->curtime + 0.01f); return; } // seek? const float magradius = 38.0 * sk_striderbuster_magnet_multiplier.GetFloat(); // radius of strider hull times multiplier if (magradius > 0 && GetMoveType() == MOVETYPE_VPHYSICS && VPhysicsGetObject() ) { // find the nearest enemy. CBaseEntity *pList[16]; Vector origin = GetAbsOrigin(); // do a find in box ( a little faster than sphere ) int count; { Vector mins,maxs; mins = origin; mins -= magradius; maxs = origin; maxs += magradius; count = UTIL_EntitiesInBox(pList, 16, mins, maxs, FL_NPC); } float magradiusSq = Square( magradius ); float nearestDistSq = magradiusSq + 1; int bestFit = -1; Vector toTarget; // will be garbage unless something good is found CNPC_Strider *pBestStrider = NULL; for ( int ii = 0 ; ii < count ; ++ii ) { CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pList[ii]); if ( pStrider && !pStrider->CarriedByDropship() ) // ShouldStickToEntity() doesn't work because the strider NPC isn't what we glue to { // get distance squared VectorSubtract( pStrider->GetAdjustedOrigin(), GetAbsOrigin(), toTarget ); //NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + toTarget, 128, 0, 128, false, 0.1 ); float dSq = toTarget.LengthSqr(); if (dSq < nearestDistSq) { bestFit = ii; nearestDistSq = dSq; pBestStrider = pStrider; } } } if (bestFit >= 0) // we found something and should attract towards it. (hysterisis later?) { if ( striderbuster_debugseek.GetBool() ) { NDebugOverlay::Circle( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, 255, true, .1 ); NDebugOverlay::Cross3D( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, true, .1 ); } // force magnitude. float magnitude = GetMass() * striderbuster_magnetic_force_strider.GetFloat(); int falloff = striderbuster_falloff_power.GetInt(); switch (falloff) { case 1: VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / nearestDistSq) ); // dividing through by distance squared normalizes toTarget and gives a linear falloff break; case 2: VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * sqrtf(nearestDistSq))) ); // dividing through by distance cubed normalizes toTarget and gives a quadratic falloff break; case 3: VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * nearestDistSq)) ); // dividing through by distance fourth normalizes toTarget and gives a cubic falloff break; case 4: { Vector toTarget; pBestStrider->GetAttachment( "buster_target", toTarget ); if ( striderbuster_debugseek.GetBool() ) { NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 ); NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 ); } toTarget -= GetAbsOrigin(); toTarget.NormalizeInPlace(); VPhysicsGetObject()->ApplyForceCenter( toTarget * magnitude ); } break; default: // arbitrary powers VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude * powf(nearestDistSq,(falloff+1.0f)/2)) ); // square root for distance instead of squared, add one to normalize toTarget break; } } SetNextThink(gpGlobals->curtime + 0.01f); } }
//----------------------------------------------------------------------------- // Purpose: Draws the object //----------------------------------------------------------------------------- int C_NPC_Barnacle::InternalDrawModel( int flags ) { if ( !GetModel() ) return 0; // Make sure hdr is valid for drawing if ( !GetModelPtr() ) return 0; // UNDONE: With a bit of work on the model->world transform, we can probably // move the frustum culling into the client DLL entirely. Then TestVisibility() // can just return true/false and only be called when frustumcull is set. if ( flags & STUDIO_FRUSTUMCULL ) { switch ( TestVisibility() ) { // not visible, don't draw case VIS_NOT_VISIBLE: return 0; // definitely visible, disable engine check case VIS_IS_VISIBLE: flags &= ~STUDIO_FRUSTUMCULL; break; default: case VIS_USE_ENGINE: break; } } Vector vecMins, vecMaxs; GetRenderBounds( vecMins, vecMaxs ); int drawn = modelrender->DrawModel( flags, this, GetModelInstance(), index, GetModel(), GetAbsOrigin(), GetAbsAngles(), GetSequence(), m_nSkin, m_nBody, m_nHitboxSet, &GetAbsMins(), &GetAbsMaxs() ); if ( vcollide_wireframe.GetBool() ) { if ( IsRagdoll() ) { m_pRagdoll->DrawWireframe(); } else { vcollide_t *pCollide = modelinfo->GetVCollide( GetModelIndex() ); if ( pCollide && pCollide->solidCount == 1 ) { static color32 debugColor = {0,255,255,0}; matrix3x4_t matrix; AngleMatrix( GetAbsAngles(), GetAbsOrigin(), matrix ); engine->DebugDrawPhysCollide( pCollide->solids[0], NULL, matrix, debugColor ); } } } return drawn; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CNPC_VehicleDriver::RunTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_RANGE_ATTACK1: { // Vehicle driver has no animations, so fire a burst at the target CBaseEntity *pEnemy = GetEnemy(); if ( pEnemy ) { // TODO: Get a bodytarget from the firing point of the gun in the vehicle Vector vecTarget = GetEnemy()->BodyTarget( GetAbsOrigin(), false ); m_pVehicleInterface->NPC_AimPrimaryWeapon( vecTarget ); m_pVehicleInterface->NPC_PrimaryFire(); TaskComplete(); } else { TaskFail(FAIL_NO_ENEMY); return; } } break; case TASK_RANGE_ATTACK2: { // Vehicle driver has no animations, so fire a burst at the target CBaseEntity *pEnemy = GetEnemy(); if ( pEnemy ) { // TODO: Get a bodytarget from the firing point of the gun in the vehicle Vector vecTarget = GetEnemy()->BodyTarget( GetAbsOrigin(), false ); m_pVehicleInterface->NPC_AimSecondaryWeapon( vecTarget ); m_pVehicleInterface->NPC_SecondaryFire(); TaskComplete(); } else { TaskFail(FAIL_NO_ENEMY); return; } } break; case TASK_WAIT_FOR_MOVEMENT: { BaseClass::RunTask( pTask ); if ( HasCondition(COND_SEE_ENEMY) ) { // we can see the enemy if ( HasCondition(COND_CAN_RANGE_ATTACK2) ) { ChainRunTask( TASK_RANGE_ATTACK2, pTask->flTaskData ); } if ( HasCondition(COND_CAN_RANGE_ATTACK1) ) { ChainRunTask( TASK_RANGE_ATTACK1, pTask->flTaskData ); } } } break; default: BaseClass::RunTask( pTask ); break; } }
int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist ) { // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); CBaseEntity *pEnemy = pNPC->GetEnemy(); if (!pEnemy) return COND_NONE; Vector vecVelocity; AngularImpulse angVelocity; pEnemy->GetVelocity( &vecVelocity, &angVelocity ); // Project where the enemy will be in a little while, add some randomness so he doesn't always hit float dt = sk_crowbar_lead_time.GetFloat(); dt += random->RandomFloat( -0.3f, 0.2f ); if ( dt < 0.0f ) dt = 0.0f; Vector vecExtrapolatedPos; VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos ); Vector vecDelta; VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta ); if ( fabs( vecDelta.z ) > 70 ) { return COND_TOO_FAR_TO_ATTACK; } Vector vecForward = pNPC->BodyDirection2D( ); vecDelta.z = 0.0f; float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() ); if ((flDot < 0.7) && (flExtrapolatedDot < 0.7)) { return COND_NOT_FACING_ATTACK; } float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() ); if( pEnemy->IsPlayer() ) { //Vector vecDir = pEnemy->GetSmoothedVelocity(); //float flSpeed = VectorNormalize( vecDir ); // If player will be in front of me in one-half second, clock his arse. Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35); Vector vecProjectMe = GetAbsOrigin(); if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f ) { return COND_CAN_MELEE_ATTACK1; } } /* if( metropolice_move_and_melee.GetBool() ) { if( pNPC->IsMoving() ) { flTargetDist *= 1.5f; } } */ float flTargetDist = 48.0f; if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist)) { return COND_TOO_FAR_TO_ATTACK; } return COND_CAN_MELEE_ATTACK1; }
//----------------------------------------------------------------------------- // Purpose: We've hit a waypoint. Handle it, and return true if this is the // end of the path. //----------------------------------------------------------------------------- bool CNPC_VehicleDriver::WaypointReached( void ) { // We reached our current waypoint. m_vecPrevPrevPoint = m_vecPrevPoint; m_vecPrevPoint = GetAbsOrigin(); // If we've got to our goal, we're done here. if ( GetNavigator()->CurWaypointIsGoal() ) { // Necessary for InPass outputs to be fired, is a no-op otherwise GetNavigator()->AdvancePath(); // Stop pathing ClearWaypoints(); TaskComplete(); SetGoalEnt( NULL ); return true; } AI_Waypoint_t *pCurWaypoint = GetNavigator()->GetPath()->GetCurWaypoint(); if ( !pCurWaypoint ) return false; // Check to see if the waypoint wants us to change speed if ( pCurWaypoint->Flags() & bits_WP_TO_PATHCORNER ) { CBaseEntity *pEntity = pCurWaypoint->hPathCorner; if ( pEntity ) { if ( pEntity->m_flSpeed > 0 ) { if ( pEntity->m_flSpeed <= 1.0 ) { m_flDriversMaxSpeed = pEntity->m_flSpeed; RecalculateSpeeds(); } else { Warning("path_track %s tried to tell the npc_vehicledriver to set speed to %.3f. npc_vehicledriver only accepts values between 0 and 1.\n", STRING(pEntity->GetEntityName()), pEntity->m_flSpeed ); } } } } // Get the waypoints for the next part of the path GetNavigator()->AdvancePath(); if ( !GetNavigator()->GetPath()->GetCurWaypoint() ) { ClearWaypoints(); TaskComplete(); SetGoalEnt( NULL ); return true; } m_vecDesiredPosition = GetNavigator()->GetCurWaypointPos(); CalculatePostPoints(); // Move to the next waypoint delete m_pCurrentWaypoint; m_pCurrentWaypoint = m_pNextWaypoint; m_Waypoints[1] = new CVehicleWaypoint( m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint, m_vecPostPostPoint ); m_pNextWaypoint = m_Waypoints[1]; // Drop the spline marker back m_flDistanceAlongSpline = MAX( 0, m_flDistanceAlongSpline - 1.0 ); CheckForTeleport(); return false; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_FireSmoke::AddFlames( void ) { #if PARTICLE_FIRE if ( ( gpGlobals->frametime != 0.0f ) && ( m_flScaleRegister > 0.0f ) ) { Vector offset; float scalar; scalar = 32.0f*m_flScaleRegister; offset[0] = Helper_RandomFloat( -scalar, scalar ); offset[1] = Helper_RandomFloat( -scalar, scalar ); offset[2] = 0.0f; CSmartPtr<CSimpleEmitter> pEmitter = CSimpleEmitter::Create( "C_FireSmoke" ); pEmitter->SetSortOrigin( GetAbsOrigin()+offset ); SimpleParticle *sParticle; //for ( int i = 0; i < 1; i++ ) { scalar = 32.0f*m_flScaleRegister; offset[0] = Helper_RandomFloat( -scalar, scalar ); offset[1] = Helper_RandomFloat( -scalar, scalar ); offset[2] = 12.0f*m_flScaleRegister; sParticle = (SimpleParticle *) pEmitter->AddParticle( sizeof(SimpleParticle), pEmitter->GetPMaterial( VarArgs("sprites/flamelet%d", Helper_RandomInt( 1, 5 ) ) ), GetAbsOrigin()+offset ); if ( sParticle ) { sParticle->m_flLifetime = 0.0f; sParticle->m_flDieTime = 0.25f; sParticle->m_flRoll = Helper_RandomInt( 0, 360 ); sParticle->m_flRollDelta = Helper_RandomFloat( -4.0f, 4.0f ); float alpha = Helper_RandomInt( 128, 255 ); sParticle->m_uchColor[0] = alpha; sParticle->m_uchColor[1] = alpha; sParticle->m_uchColor[2] = alpha; sParticle->m_uchStartAlpha = 255; sParticle->m_uchEndAlpha = 0; sParticle->m_uchStartSize = 64.0f*m_flScaleRegister; sParticle->m_uchEndSize = 0; float speedScale = ((GetAbsOrigin()+offset)-GetAbsOrigin()).Length2D() / (32.0f*m_flScaleRegister); sParticle->m_vecVelocity = Vector( Helper_RandomFloat( -32.0f, 32.0f ), Helper_RandomFloat( -32.0f, 32.0f ), Helper_RandomFloat( 32.0f, 128.0f )*speedScale ); } } pEmitter->Release(); } #endif //#if !PARTICLE_FIRE float alpha = 1.0f; //Update the child flame alpha for ( int i = 0; i < NUM_CHILD_FLAMES; i++ ) { if ( m_entFlames[i].GetScale() > 1e-3f ) { m_entFlames[i].SetRenderColor( ( 255.0f * alpha ), ( 255.0f * alpha ), ( 255.0f * alpha ) ); m_entFlames[i].SetBrightness( 255.0f * alpha ); Assert( m_entFlames[i].GetRenderHandle() != INVALID_CLIENT_RENDER_HANDLE ); m_entFlames[i].AddToLeafSystem(); } } if ( m_nFlags & bitsFIRESMOKE_VISIBLE_FROM_ABOVE ) { for ( int i = 0; i < NUM_CHILD_FLAMES; i++ ) { if ( m_entFlamesFromAbove[i].GetScale() > 1e-3f ) { m_entFlamesFromAbove[i].SetRenderColor( ( 255.0f * alpha ), ( 255.0f * alpha ), ( 255.0f * alpha ) ); m_entFlamesFromAbove[i].SetBrightness( 255.0f * alpha ); Assert( m_entFlamesFromAbove[i].GetRenderHandle() != INVALID_CLIENT_RENDER_HANDLE ); m_entFlamesFromAbove[i].AddToLeafSystem(); } } } //#endif }
void CBreakable::Precache( void ) { const char *pGibName = "WoodChunks"; switch (m_Material) { case matWood: pGibName = "WoodChunks"; break; case matUnbreakableGlass: case matGlass: pGibName = "GlassChunks"; break; case matMetal: pGibName = "MetalChunks"; break; case matRocks: pGibName = "ConcreteChunks"; break; #ifdef HL1_DLL case matComputer: pGibName = "ComputerGibs"; break; case matCeilingTile: pGibName = "CeilingTile"; break; case matFlesh: pGibName = "FleshGibs"; break; case matCinderBlock: pGibName = "CinderBlocks"; break; case matWeb: pGibName = "WebGibs"; break; #else case matCinderBlock: pGibName = "ConcreteChunks"; break; #endif default: Warning("%s (%s) at (%.3f %.3f %.3f) using obsolete or unknown material type.\n", GetClassname(), GetDebugName(), GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); pGibName = "WoodChunks"; break; } if ( m_iszGibModel != NULL_STRING ) { pGibName = STRING(m_iszGibModel); #ifdef HL1_DLL PrecacheModel( pGibName ); #endif } m_iszModelName = MAKE_STRING( pGibName ); // Precache the spawn item's data if ( !CommandLine()->CheckParm("-makereslists")) { if ( m_iszSpawnObject != NULL_STRING ) { UTIL_PrecacheOther( STRING( m_iszSpawnObject ) ); } } else { // Actually, precache all possible objects... for ( size_t i = 0; i < ARRAYSIZE(pSpawnObjects) ; ++i ) { if ( !pSpawnObjects[ i ] ) continue; if ( !Q_strnicmp( pSpawnObjects[ i ], "unused", Q_strlen( "unused" ) ) ) continue; UTIL_PrecacheOther( pSpawnObjects[ i ] ); } } PrecacheScriptSound( "Breakable.MatGlass" ); PrecacheScriptSound( "Breakable.MatWood" ); PrecacheScriptSound( "Breakable.MatMetal" ); PrecacheScriptSound( "Breakable.MatFlesh" ); PrecacheScriptSound( "Breakable.MatConcrete" ); PrecacheScriptSound( "Breakable.Computer" ); PrecacheScriptSound( "Breakable.Crate" ); PrecacheScriptSound( "Breakable.Glass" ); PrecacheScriptSound( "Breakable.Metal" ); PrecacheScriptSound( "Breakable.Flesh" ); PrecacheScriptSound( "Breakable.Concrete" ); PrecacheScriptSound( "Breakable.Ceiling" ); }