//----------------------------------------------------------------------------- // Fling the buster with the physcannon either via punt or launch. //----------------------------------------------------------------------------- void CWeaponStriderBuster::Launch( CBasePlayer *pPhysGunUser ) { if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) { WeaponManager_RemoveManaged( this ); } m_bLaunched = true; // Notify all nearby hunters that we were launched. Hunter_StriderBusterLaunched( this ); // Start up the eye glow m_hMainGlow = CSprite::SpriteCreate( "sprites/blueglow1.vmt", GetLocalOrigin(), false ); if ( m_hMainGlow != NULL ) { m_hMainGlow->FollowEntity( this ); m_hMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 140, kRenderFxNoDissipation ); m_hMainGlow->SetScale( 2.0f ); m_hMainGlow->SetGlowProxySize( 8.0f ); } if ( !m_bNoseDiving ) { DispatchParticleEffect( "striderbuster_trail", PATTACH_ABSORIGIN_FOLLOW, this ); } else { DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this ); } // We get our touch function from the physics system SetTouch ( &CWeaponStriderBuster::BusterTouch ); SetThink( &CWeaponStriderBuster::BusterFlyThink ); SetNextThink( gpGlobals->curtime ); gamestats->Event_WeaponFired( pPhysGunUser, true, GetClassname() ); }
void CDHLProjectile::ReceiveMessage( int classID, bf_read &msg ) { if ( classID != GetClientClass()->m_ClassID ) { // message is for subclass BaseClass::ReceiveMessage( classID, msg ); return; } int iType = msg.ReadByte(); //Server is letting us know that we're about to be deleted if ( iType == MSG_NOTIFY_REMOVAL ) { if ( !m_bCollided ) { Vector vecDir = vec3_origin; if ( GetAbsVelocity() != vec3_origin ) vecDir = GetAbsVelocity(); else vecDir = m_vecProjectileVelocity; VectorNormalize( vecDir ); Vector vecStartPos = GetMoveType() == MOVETYPE_CUSTOM ? GetLocalOrigin() : m_vecProjectileOrigin; //Try to plant a decal trace_t decaltr; UTIL_TraceLine( vecStartPos, vecStartPos + (vecDir * 120.0), MASK_SHOT, this, //Pretty long distance, but seems necessary in practice COLLISION_GROUP_NONE, &decaltr ); //DebugDrawLine( decaltr.startpos, decaltr.endpos, 255, 0, 0, false, 3.0f ); if ( decaltr.DidHit() ) { OnTouch( decaltr, true ); } m_bCollided = true; } } }
void CASW_Parasite::Leap( const Vector &vecVel ) { SetTouch( &CASW_Parasite::LeapTouch ); SetCondition( COND_FLOATING_OFF_GROUND ); SetGroundEntity( NULL ); m_flIgnoreWorldCollisionTime = gpGlobals->curtime + PARASITE_IGNORE_WORLD_COLLISION_TIME; if( HasHeadroom() ) { // Take him off ground so engine doesn't instantly reset FL_ONGROUND. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0, 0, 1 ) ); } SetAbsVelocity( vecVel ); // Think every frame so the player sees the headcrab where he actually is... m_bMidJump = true; SetThink( &CASW_Parasite::LeapThink ); SetNextThink( gpGlobals->curtime ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CASW_Shotgun_Pellet_Predicted::Spawn( void ) { Precache(); SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM ); SetSolid( SOLID_BBOX ); //m_flGravity = 1.0; SetFriction( 0.75 ); SetModel( PELLET_MODEL ); SetSize( -Vector(1,1,1), Vector(1,1,1) ); SetSolid( SOLID_BBOX ); SetGravity( 0.05f ); SetCollisionGroup( ASW_COLLISION_GROUP_SHOTGUN_PELLET ); SetTouch( &CASW_Shotgun_Pellet_Predicted::PelletTouch ); SetThink( &CASW_Shotgun_Pellet_Predicted::SUB_Remove ); SetNextThink( gpGlobals->curtime + 1.0f ); m_flDamage = 10; m_takedamage = DAMAGE_NO; // Create a white light CBasePlayer *player = ToBasePlayer( GetOwnerEntity() ); if ( player ) { m_hLiveSprite = SPRITE_CREATE_PREDICTABLE( "sprites/chargeball2.vmt", GetLocalOrigin() + Vector(0,0,1), false ); if ( m_hLiveSprite ) { m_hLiveSprite->SetOwnerEntity( player ); m_hLiveSprite->SetPlayerSimulated( player ); m_hLiveSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation ); m_hLiveSprite->SetBrightness( 255 ); m_hLiveSprite->SetScale( 0.15, 5.0f ); m_hLiveSprite->SetAttachment( this, 0 ); } } }
//----------------------------------------------------------------------------- // Purpose: Explode // Input : // Output : //----------------------------------------------------------------------------- void CPlayer_Missile::LoseMissileControl(void) { // Create a missile to take the place of this one CGrenadeHomer *pGrenade = (CGrenadeHomer*)CreateEntityByName( "grenade_homer" ); if ( pGrenade ) { pGrenade->Spawn(); pGrenade->SetLocalOrigin( GetLocalOrigin() ); pGrenade->SetLocalAngles( GetLocalAngles() ); pGrenade->SetModel( PMISSILE_MISSILE_MODEL ); pGrenade->SetDamage(m_flDamage); pGrenade->SetDamageRadius(m_flDamageRadius); pGrenade->Launch(this,NULL,GetAbsVelocity(),0,0,HOMER_SMOKE_TRAIL_OFF); pGrenade->m_hRocketTrail[0] = m_hSmokeTrail; ((SmokeTrail*)(CBaseEntity*)m_hSmokeTrail)->FollowEntity(ENTINDEX(pGrenade->pev)); } m_takedamage = DAMAGE_NO; m_lifeState = LIFE_DEAD; StopSound( "Player_Manhack.Fly" ); ControlDeactivate(); }
//----------------------------------------------------------------------------- // Purpose: Aim the offset barrel at a position in parent space // Input : parentTarget - the position of the target in parent space // Output : Vector - angles in local space //----------------------------------------------------------------------------- QAngle CAPCController::AimBarrelAt( const Vector &parentTarget ) { Vector target = parentTarget - GetLocalOrigin(); float quadTarget = target.LengthSqr(); float quadTargetXY = target.x*target.x + target.y*target.y; // We're trying to aim the offset barrel at an arbitrary point. // To calculate this, I think of the target as being on a sphere with // it's center at the origin of the gun. // The rotation we need is the opposite of the rotation that moves the target // along the surface of that sphere to intersect with the gun's shooting direction // To calculate that rotation, we simply calculate the intersection of the ray // coming out of the barrel with the target sphere (that's the new target position) // and use atan2() to get angles // angles from target pos to center float targetToCenterYaw = atan2( target.y, target.x ); float centerToGunYaw = atan2( m_barrelPos.y, sqrt( quadTarget - (m_barrelPos.y*m_barrelPos.y) ) ); float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) ); float centerToGunPitch = atan2( -m_barrelPos.z, sqrt( quadTarget - (m_barrelPos.z*m_barrelPos.z) ) ); return QAngle( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 ); }
// Assumes this is ALWAYS enabled CPathTrack *CPathTrack::Nearest( const Vector &origin ) { int deadCount; float minDist, dist; Vector delta; CPathTrack *ppath, *pnearest; delta = origin - GetLocalOrigin(); delta.z = 0; minDist = delta.Length(); pnearest = this; ppath = GetNext(); // Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :) deadCount = 0; while ( ppath && ppath != this ) { deadCount++; if ( deadCount > 9999 ) { Warning( "Bad sequence of path_tracks from %s\n", GetDebugName() ); Assert(0); return NULL; } delta = origin - ppath->GetLocalOrigin(); delta.z = 0; dist = delta.Length(); if ( dist < minDist ) { minDist = dist; pnearest = ppath; } ppath = ppath->GetNext(); } return pnearest; }
//------------------------------------------------------------------------------ // Purpose: routine called every frame when a task is running // Input : pTask - the task structure //------------------------------------------------------------------------------ void CAI_ASW_MeleeBehavior::RunTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_MELEE_FLIP_AROUND: { CBaseEntity *pTarget = GetEnemy(); if ( pTarget ) { GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetLocalOrigin(), AI_KEEP_YAW_SPEED ); } if ( GetOuter()->IsActivityFinished() ) { TaskComplete(); } } break; default: BaseClass::RunTask( pTask ); break; } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CShieldGrenade::Spawn( void ) { BaseClass::Spawn(); m_LastCollision.Init( 0, 0, 0 ); SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); SetSolid( SOLID_BBOX ); SetGravity( 1.0 ); SetFriction( 0.9 ); SetModel( "models/weapons/w_grenade.mdl"); UTIL_SetSize(this, Vector( -4, -4, -4), Vector(4, 4, 4)); m_IsEMPed = false; m_IsDeployed = false; m_flEMPDamageEndTime = 0.0f; SetTouch( StickyTouch ); SetCollisionGroup( TFCOLLISION_GROUP_GRENADE ); // Create a green light m_pLiveSprite = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin() + Vector(0,0,1), false ); m_pLiveSprite->SetTransparency( kRenderGlow, 0, 0, 255, 128, kRenderFxNoDissipation ); m_pLiveSprite->SetScale( 1 ); m_pLiveSprite->SetAttachment( this, 0 ); }
//----------------------------------------------------------------------------- // Little hack to avoid game crash when changing bodygroup (DmitRex) //----------------------------------------------------------------------------- void CZombie::SetHeadlessModel( void ) { SetModel(""); CreateRagGib( "models/zombie/classic.mdl", GetLocalOrigin(), GetLocalAngles(), GetLocalVelocity(), 0, ShouldIgniteZombieGib() ); }
void CFuncTank::TrackTarget( void ) { trace_t tr; bool updateTime = FALSE, lineOfSight; QAngle angles; Vector barrelEnd; CBaseEntity *pTarget = NULL; barrelEnd.Init(); // Get a position to aim for if (m_pController) { // Tanks attempt to mirror the player's angles angles = m_pController->EyeAngles(); SetNextThink( gpGlobals->curtime + 0.05 ); } else { if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 0.1f ); } else { return; } // ----------------------------------- // Get world target position // ----------------------------------- barrelEnd = WorldBarrelPosition(); Vector worldTargetPosition; if (m_spawnflags & SF_TANK_AIM_AT_POS) { worldTargetPosition = m_vTargetPosition; } else { CBaseEntity *pEntity = (CBaseEntity *)m_hTarget; if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) ) { if ( m_targetEntityName != NULL_STRING ) // New HL2 behavior { m_hTarget = FindTarget( m_targetEntityName, NULL ); } else // HL1 style { m_hTarget = ToBasePlayer( GetContainingEntity( UTIL_FindClientInPVS( edict() ) ) ); } if ( m_hTarget != NULL ) { SetNextThink( gpGlobals->curtime ); // Think again immediately } else { if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 2 ); // Wait 2 secs } if ( m_fireLast !=0 ) { m_OnLoseTarget.FireOutput(this, this); m_fireLast = 0; } } return; } pTarget = pEntity; // Calculate angle needed to aim at target worldTargetPosition = pEntity->EyePosition(); } float range = (worldTargetPosition - barrelEnd).Length(); if ( !InRange( range ) ) { m_fireLast = 0; return; } UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if (m_spawnflags & SF_TANK_AIM_AT_POS) { updateTime = TRUE; m_sightOrigin = m_vTargetPosition; } else { lineOfSight = FALSE; // No line of sight, don't track if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget ) { lineOfSight = TRUE; CBaseEntity *pInstance = pTarget; if ( InRange( range ) && pInstance && pInstance->IsAlive() ) { updateTime = TRUE; // Sight position is BodyTarget with no noise (so gun doesn't bob up and down) m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false ); } } } // Convert targetPosition to parent angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) ); } // Force the angles to be relative to the center position float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter ); float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter ); angles.y = m_yawCenter + offsetY; angles.x = m_pitchCenter + offsetX; // Limit against range in y // MDB - don't check pitch! If two func_tanks are meant to align, // and one can pitch and the other cannot, this can lead to them getting // different values for angles.y. Nothing is lost by not updating yaw // because the target is not in pitch range. bool bOutsideYawRange = ( fabs( offsetY ) > m_yawRange + m_yawTolerance ); bool bOutsidePitchRange = ( fabs( offsetX ) > m_pitchRange + m_pitchTolerance ); Vector vecToTarget = m_sightOrigin - GetLocalOrigin(); // if target is outside yaw range if ( bOutsideYawRange ) { if ( angles.y > m_yawCenter + m_yawRange ) { angles.y = m_yawCenter + m_yawRange; } else if ( angles.y < (m_yawCenter - m_yawRange) ) { angles.y = (m_yawCenter - m_yawRange); } } if ( bOutsidePitchRange || bOutsideYawRange || ( vecToTarget.Length() < ( barrelEnd - GetAbsOrigin() ).Length() ) ) { // Don't update if you saw the player, but out of range updateTime = false; } if ( updateTime ) { m_lastSightTime = gpGlobals->curtime; m_persist2burst = 0; } // Move toward target at rate or less float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y ); QAngle vecAngVel = GetLocalAngularVelocity(); vecAngVel.y = distY * 10; vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate ); // Limit against range in x angles.x = clamp( angles.x, m_pitchCenter - m_pitchRange, m_pitchCenter + m_pitchRange ); // Move toward target at rate or less float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x ); vecAngVel.x = distX * 10; vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate ); SetLocalAngularVelocity( vecAngVel ); SetMoveDoneTime( 0.1 ); if ( m_pController ) return; if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (m_spawnflags & SF_TANK_LINEOFSIGHT) ) ) { bool fire = FALSE; Vector forward; AngleVectors( GetLocalAngles(), &forward ); forward = m_parentMatrix.ApplyRotation( forward ); if ( m_spawnflags & SF_TANK_LINEOFSIGHT ) { float length = (m_maxRange > 0) ? m_maxRange : MAX_TRACE_LENGTH; UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if ( tr.m_pEnt == pTarget ) fire = TRUE; } else fire = TRUE; if ( fire ) { if (m_fireLast == 0) { m_OnAquireTarget.FireOutput(this, this); } FiringSequence( barrelEnd, forward, this ); } else { if (m_fireLast !=0) { m_OnLoseTarget.FireOutput(this, this); } m_fireLast = 0; } } else { if (m_fireLast !=0) { m_OnLoseTarget.FireOutput(this, this); } m_fireLast = 0; } }
void CPhysMotor::Spawn( void ) { m_motor.m_axis -= GetLocalOrigin(); VectorNormalize(m_motor.m_axis); UTIL_SnapDirectionToAxis( m_motor.m_axis ); }
void CBaseDoor::Spawn( void ) { if( pev->skin == CONTENTS_NONE ) { // normal door if( FBitSet( pev->spawnflags, SF_DOOR_PASSABLE )) pev->solid = SOLID_NOT; else pev->solid = SOLID_BSP; } else { SetBits( pev->spawnflags, SF_DOOR_SILENT ); pev->solid = SOLID_NOT; // special contents } Precache(); pev->movetype = MOVETYPE_PUSH; SET_MODEL( edict(), GetModel() ); // NOTE: original Half-Life was contain a bug in LinearMove function // while m_flWait was equal 0 then object has stopped forever. See code from quake: /* void LinearMove( Vector vecDest, float flSpeed ) { ... ... ... if( flTravelTime < 0.1f ) { pev->velocity = g_vecZero; pev->nextthink = pev->ltime + 0.1f; return; } } */ // this block was removed from Half-Life and there no difference // between wait = 0 and wait = -1. But in Xash this bug was fixed // and level-designer errors is now actual. I'm set m_flWait to -1 for compatibility if( m_flWait == 0.0f ) m_flWait = -1; if( pev->speed == 0 ) pev->speed = 100; if( pev->movedir == g_vecZero ) pev->movedir = Vector( 1.0f, 0.0f, 0.0f ); m_vecPosition1 = GetLocalOrigin(); // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big Vector vecSize = pev->size - Vector( 2, 2, 2 ); m_vecPosition2 = m_vecPosition1 + (pev->movedir * (DotProductAbs( pev->movedir, vecSize ) - m_flLip)); ASSERTSZ( m_vecPosition1 != m_vecPosition2, "door start/end positions are equal" ); if( FBitSet( pev->spawnflags, SF_DOOR_START_OPEN )) { UTIL_SetOrigin( this, m_vecPosition2 ); m_vecPosition2 = m_vecPosition1; m_vecPosition1 = GetLocalOrigin(); } else { UTIL_SetOrigin( this, m_vecPosition1 ); } // another hack: PhysX 2.8.4.0 crashed while trying to created kinematic body from this brush-model if ( FStrEq( STRING( gpGlobals->mapname ), "c2a5e" ) && FStrEq( STRING( pev->model ), "*103" )); else m_pUserData = WorldPhysic->CreateKinematicBodyFromEntity( this ); m_iState = STATE_OFF; // if the door is flagged for USE button activation only, use NULL touch function if( FBitSet( pev->spawnflags, SF_DOOR_USE_ONLY )) { SetTouch( NULL ); } else { // touchable button SetTouch( DoorTouch ); } }
//----------------------------------------------------------------------------- // 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 CAPCController::TrackTarget( void ) { trace_t tr; bool updateTime = FALSE, lineOfSight; QAngle angles; Vector barrelEnd; CBaseEntity *pTarget = NULL; barrelEnd.Init(); if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 0.1f ); } else { return; } // ----------------------------------- // Get world target position // ----------------------------------- barrelEnd = WorldBarrelPosition(); Vector worldTargetPosition; CBaseEntity *pEntity = (CBaseEntity *)m_hTarget; if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) ) { m_hTarget = FindTarget( m_targetEntityName, NULL ); if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 2 ); // Wait 2 sec s } return; } pTarget = pEntity; // Calculate angle needed to aim at target worldTargetPosition = pEntity->EyePosition(); float range = (worldTargetPosition - barrelEnd).Length(); if ( !InRange( range ) ) { m_bFireDelayed = false; return; } UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr ); lineOfSight = FALSE; // No line of sight, don't track if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget ) { lineOfSight = TRUE; CBaseEntity *pInstance = pTarget; if ( InRange( range ) && pInstance && pInstance->IsAlive() ) { updateTime = TRUE; // Sight position is BodyTarget with no noise (so gun doesn't bob up and down) m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false ); } } // Convert targetPosition to parent angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) ); // Force the angles to be relative to the center position float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter ); float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter ); angles.y = m_yawCenter + offsetY; angles.x = m_pitchCenter + offsetX; // Move toward target at rate or less float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y ); QAngle vecAngVel = GetLocalAngularVelocity(); vecAngVel.y = distY * 10; vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate ); // Move toward target at rate or less float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x ); vecAngVel.x = distX * 10; vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate ); SetLocalAngularVelocity( vecAngVel ); SetMoveDoneTime( 0.1 ); Vector forward; AngleVectors( GetLocalAngles(), &forward ); forward = m_parentMatrix.ApplyRotation( forward ); AngleVectors(angles, &forward); if ( lineOfSight == TRUE ) { // FIXME: This will ultimately have to deal with NPCs being in the vehicle as well // See if the target is in a vehicle. If so, check its relationship CBasePlayer *pPlayer = ToBasePlayer( pTarget ); if ( pPlayer && pPlayer->IsInAVehicle() ) { IServerVehicle *pVehicle = pPlayer->GetVehicle(); if ( pVehicle->ClassifyPassenger( pPlayer, CLASS_PLAYER ) == CLASS_PLAYER) { if ( !m_bFireDelayed ) { m_bFireDelayed = true; m_flFiringDelay = gpGlobals->curtime + 1.5; // setup delay time before we start firing return; } if ( gpGlobals->curtime > m_flFiringDelay ) { m_OnFireAtTarget.Set(forward, this, this); // tell apc to fire rockets, and what direction } } } } else { m_bFireDelayed = false; // reset flag since we can no longer see target } }
//----------------------------------------------------------------------------- // Purpose: // Input : pTask - //----------------------------------------------------------------------------- void CNPC_Crow::StartTask( const Task_t *pTask ) { switch ( pTask->iTask ) { // // This task enables us to build a path that requires flight. // // case TASK_CROW_PREPARE_TO_FLY: // { // SetFlyingState( FlyState_Flying ); // TaskComplete(); // break; // } case TASK_CROW_TAKEOFF: { if ( random->RandomInt( 1, 4 ) == 1 ) { AlertSound(); } FlapSound(); SetIdealActivity( ( Activity )ACT_CROW_TAKEOFF ); break; } case TASK_CROW_PICK_EVADE_GOAL: { if ( GetEnemy() != NULL ) { // // Get our enemy's position in x/y. // Vector vecEnemyOrigin = GetEnemy()->GetAbsOrigin(); vecEnemyOrigin.z = GetAbsOrigin().z; // // Pick a hop goal a random distance along a vector away from our enemy. // m_vSavePosition = GetAbsOrigin() - vecEnemyOrigin; VectorNormalize( m_vSavePosition ); m_vSavePosition = GetAbsOrigin() + m_vSavePosition * ( 32 + random->RandomInt( 0, 32 ) ); GetMotor()->SetIdealYawToTarget( m_vSavePosition ); TaskComplete(); } else { TaskFail( "No enemy" ); } break; } case TASK_CROW_FALL_TO_GROUND: { SetFlyingState( FlyState_Falling ); break; } case TASK_FIND_HINTNODE: { if ( GetGoalEnt() ) { TaskComplete(); return; } // Overloaded because we search over a greater distance. if ( !GetHintNode() ) { SetHintNode(CAI_HintManager::FindHint( this, HINT_CROW_FLYTO_POINT, bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP, 10000 )); } if ( GetHintNode() ) { TaskComplete(); } else { TaskFail( FAIL_NO_HINT_NODE ); } break; } case TASK_GET_PATH_TO_HINTNODE: { //How did this happen?! if ( GetGoalEnt() == this ) { SetGoalEnt( NULL ); } if ( GetGoalEnt() ) { SetFlyingState( FlyState_Flying ); StartTargetHandling( GetGoalEnt() ); m_bReachedMoveGoal = false; TaskComplete(); SetHintNode( NULL ); return; } if ( GetHintNode() ) { Vector vHintPos; GetHintNode()->GetPosition(this, &vHintPos); SetNavType( NAV_FLY ); CapabilitiesAdd( bits_CAP_MOVE_FLY ); if ( !GetNavigator()->SetGoal( vHintPos ) ) SetHintNode(NULL); CapabilitiesRemove( bits_CAP_MOVE_FLY ); } if ( GetHintNode() ) { m_bReachedMoveGoal = false; TaskComplete(); } else { TaskFail( FAIL_NO_ROUTE ); } break; } // // We have failed to fly normally. Pick a random "up" direction and fly that way. // case TASK_CROW_FLY: { float flYaw = UTIL_AngleMod( random->RandomInt( -180, 180 ) ); Vector vecNewVelocity( cos( DEG2RAD( flYaw ) ), sin( DEG2RAD( flYaw ) ), random->RandomFloat( 0.1f, 0.5f ) ); vecNewVelocity *= CROW_AIRSPEED; SetAbsVelocity( vecNewVelocity ); SetIdealActivity( ACT_FLY ); m_bSoar = false; m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 ); break; } case TASK_CROW_PICK_RANDOM_GOAL: { m_vSavePosition = GetLocalOrigin() + Vector( random->RandomFloat( -48.0f, 48.0f ), random->RandomFloat( -48.0f, 48.0f ), 0 ); TaskComplete(); break; } case TASK_CROW_HOP: { SetIdealActivity( ACT_HOP ); m_flHopStartZ = GetLocalOrigin().z; break; } case TASK_CROW_WAIT_FOR_BARNACLE_KILL: { break; } default: { BaseClass::StartTask( pTask ); } } }
//----------------------------------------------------------------------------- // Purpose: Catches the monster-specific messages that occur when tagged // animation frames are played. // Input : pEvent - //----------------------------------------------------------------------------- void CNPC_Crow::HandleAnimEvent( animevent_t *pEvent ) { if ( pEvent->event == AE_CROW_TAKEOFF ) { if ( GetNavigator()->GetPath()->GetCurWaypoint() ) { Takeoff( GetNavigator()->GetCurWaypointPos() ); } return; } if( pEvent->event == AE_CROW_HOP ) { SetGroundEntity( NULL ); // // Take him off ground so engine doesn't instantly reset FL_ONGROUND. // UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 )); // // How fast does the crow need to travel to reach the hop goal given gravity? // float flHopDistance = ( m_vSavePosition - GetLocalOrigin() ).Length(); float gravity = sv_gravity.GetFloat(); if ( gravity <= 1 ) { gravity = 1; } float height = 0.25 * flHopDistance; float speed = sqrt( 2 * gravity * height ); float time = speed / gravity; // // Scale the sideways velocity to get there at the right time // Vector vecJumpDir = m_vSavePosition - GetLocalOrigin(); vecJumpDir = vecJumpDir / time; // // Speed to offset gravity at the desired height. // vecJumpDir.z = speed; // // Don't jump too far/fast. // float distance = vecJumpDir.Length(); if ( distance > 650 ) { vecJumpDir = vecJumpDir * ( 650.0 / distance ); } m_nMorale -= random->RandomInt( 1, 6 ); if ( m_nMorale <= 0 ) { m_nMorale = 0; } // Play a hop flap sound. EmitSound( "NPC_Crow.Hop" ); SetAbsVelocity( vecJumpDir ); return; } if( pEvent->event == AE_CROW_FLY ) { // // Start flying. // SetActivity( ACT_FLY ); m_bSoar = false; m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 ); return; } CAI_BaseNPC::HandleAnimEvent( pEvent ); }
//----------------------------------------------------------------------------- // 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; }
//========================================================= // HandleAnimEvent - catches the monster-specific messages // that occur when tagged animation frames are played. //========================================================= void CNPC_Houndeye::HandleAnimEvent( animevent_t *pEvent ) { switch ( pEvent->event ) { case HOUND_AE_WARN: // do stuff for this event. WarnSound(); break; case HOUND_AE_STARTATTACK: WarmUpSound(); break; case HOUND_AE_HOPBACK: { float flGravity = sv_gravity.GetFloat(); SetGroundEntity( NULL ); Vector forward; AngleVectors( GetLocalAngles(), &forward ); Vector vecNewVelocity = forward * -200; //jump up 36 inches vecNewVelocity.z += sqrt( 2 * flGravity * 36 ); SetAbsVelocity( vecNewVelocity ); break; } case HOUND_AE_THUMP: // emit the shockwaves SonicAttack(); m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 5.0, 8.0 ); break; case HOUND_AE_ANGERSOUND1: { EmitSound( "NPC_Houndeye.Anger1" ); } break; case HOUND_AE_ANGERSOUND2: { EmitSound( "NPC_Houndeye.Anger2" ); } break; case HOUND_AE_CLOSE_EYE: if ( !m_fDontBlink ) { //<<TEMP>> pev->skin = HOUNDEYE_EYE_FRAMES - 1; } break; case HOUND_AE_LEAP_HIT: { //<<TEMP>>return;//<<TEMP>> SetGroundEntity( NULL ); // // Take him off ground so engine doesn't instantly reset FL_ONGROUND. // UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 )); Vector vecJumpDir; if ( GetEnemy() != NULL ) { Vector vecEnemyEyePos = GetEnemy()->EyePosition(); float gravity = sv_gravity.GetFloat(); if ( gravity <= 1 ) { gravity = 1; } // // How fast does the houndeye need to travel to reach my enemy's eyes given gravity? // float height = ( vecEnemyEyePos.z - GetAbsOrigin().z ); if ( height < 16 ) { height = 16; } else if ( height > 120 ) { height = 120; } float speed = sqrt( 2 * gravity * height ); float time = speed / gravity; // // Scale the sideways velocity to get there at the right time // vecJumpDir = vecEnemyEyePos - GetAbsOrigin(); vecJumpDir = vecJumpDir / time; // // Speed to offset gravity at the desired height. // vecJumpDir.z = speed; // // Don't jump too far/fast. // float distance = vecJumpDir.Length(); if ( distance > 650 ) { vecJumpDir = vecJumpDir * ( 650.0 / distance ); } } else { Vector forward, up; AngleVectors( GetLocalAngles(), &forward, NULL, &up ); // // Jump hop, don't care where. // vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350; } SetAbsVelocity( vecJumpDir ); m_flNextAttack = gpGlobals->curtime + 2; break; } default: BaseClass::HandleAnimEvent( pEvent ); break; } }
void CAI_LeadBehavior::RunTask( const Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_LEAD_SUCCEED: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_DONE ); } break; } case TASK_LEAD_ARRIVE: { if ( !IsSpeaking() ) { TaskComplete(); NotifyEvent( LBE_ARRIVAL_DONE ); } break; } case TASK_LEAD_MOVE_TO_RANGE: { // If we haven't spoken our start speech, move closer if ( !m_hasspokenstart) { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_leaddistance - 24 ); } else { ChainRunTask( TASK_MOVE_TO_GOAL_RANGE, m_retrievedistance ); if ( !TaskIsComplete() ) { // Transition to a walk when we get near the player // Check Z first, and only check 2d if we're within that Vector vecGoalPos = GetNavigator()->GetGoalPos(); float distance = fabs(vecGoalPos.z - GetLocalOrigin().z); bool bWithinZ = false; if ( distance < m_retrievedistance ) { distance = ( vecGoalPos - GetLocalOrigin() ).Length2D(); bWithinZ = true; } if ( distance > m_retrievedistance ) { Activity followActivity = ACT_WALK; if ( GetOuter()->GetState() == NPC_STATE_COMBAT || ( (!bWithinZ || distance < (m_retrievedistance*4)) && GetOuter()->GetState() != NPC_STATE_COMBAT ) ) { followActivity = ACT_RUN; } // Don't confuse move and shoot by resetting the activity every think Activity curActivity = GetNavigator()->GetMovementActivity(); switch( curActivity ) { case ACT_WALK_AIM: curActivity = ACT_WALK; break; case ACT_RUN_AIM: curActivity = ACT_RUN; break; } if ( curActivity != followActivity ) { GetNavigator()->SetMovementActivity(followActivity); } GetNavigator()->SetArrivalDirection( GetOuter()->GetTarget() ); } } } break; } case TASK_LEAD_RETRIEVE_WAIT: { ChainRunTask( TASK_WAIT_INDEFINITE ); break; } case TASK_LEAD_WALK_PATH: { // If we're leading, and we're supposed to run, run instead of walking if ( m_run && ( IsCurSchedule( SCHED_LEAD_WAITFORPLAYER, false ) || IsCurSchedule( SCHED_LEAD_PLAYER, false ) || IsCurSchedule( SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER, false )|| IsCurSchedule( SCHED_LEAD_RETRIEVE, false ) ) ) { ChainRunTask( TASK_RUN_PATH ); } else { ChainRunTask( TASK_WALK_PATH ); } // While we're walking if ( TaskIsRunning() && IsCurSchedule( SCHED_LEAD_PLAYER, false ) ) { // If we're not speaking, and we haven't tried for a while, try to speak lead idle if ( m_flNextLeadIdle < gpGlobals->curtime && !IsSpeaking() ) { m_flNextLeadIdle = gpGlobals->curtime + RandomFloat( 10,15 ); if ( !m_args.iRetrievePlayer && HasCondition( COND_LEAD_FOLLOWER_LOST ) && HasCondition(COND_SEE_PLAYER) ) { Speak( TLK_LEAD_COMINGBACK ); } else { Speak( TLK_LEAD_IDLE ); } } } break; } default: BaseClass::RunTask( pTask); } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_LeadBehavior::GatherConditions( void ) { BaseClass::GatherConditions(); if ( HasGoal() ) { // Fix for bad transition case (to investigate) if ( ( WorldSpaceCenter() - m_goal ).LengthSqr() > (64*64) && IsCurSchedule( SCHED_LEAD_AWAIT_SUCCESS, false) ) { GetOuter()->ClearSchedule( "Lead behavior - bad transition?" ); } // We have to collect data about the person we're leading around. CBaseEntity *pFollower = AI_GetSinglePlayer(); if( pFollower ) { ClearCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); ClearCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); // Check distance to the follower float flFollowerDist = ( WorldSpaceCenter() - pFollower->WorldSpaceCenter() ).Length(); bool bLagging = flFollowerDist > (m_leaddistance*4); if ( bLagging ) { if ( PlayerIsAheadOfMe() ) { bLagging = false; } } // Player heading towards me? // Only factor this in if you're not too far from them if ( flFollowerDist < (m_leaddistance*4) ) { Vector vecVelocity = pFollower->GetSmoothedVelocity(); if ( VectorNormalize(vecVelocity) > 50 ) { Vector vecToPlayer = (GetAbsOrigin() - pFollower->GetAbsOrigin()); VectorNormalize( vecToPlayer ); if ( DotProduct( vecVelocity, vecToPlayer ) > 0.5 ) { SetCondition( COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME ); bLagging = false; } } } // If he's outside our lag range, consider him lagging if ( bLagging ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } else { ClearCondition( COND_LEAD_FOLLOWER_LAGGING ); SetCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); // If he's really close, note that if ( flFollowerDist < m_leaddistance ) { SetCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ); } } // To be considered not lagging, the follower must be visible, and within the lead distance if ( GetOuter()->FVisible( pFollower ) && GetOuter()->GetSenses()->ShouldSeeEntity( pFollower ) ) { SetCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); m_LostLOSTimer.Stop(); } else { ClearCondition( COND_LEAD_HAVE_FOLLOWER_LOS ); // We don't have a LOS. But if we did have LOS, don't clear it until the timer is up. if ( m_LostLOSTimer.IsRunning() ) { if ( m_LostLOSTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LAGGING ); ClearCondition( COND_LEAD_FOLLOWER_NOT_LAGGING ); } } else { m_LostLOSTimer.Start(); } } // Now we want to see if the follower is lost. Being lost means being (far away || out of LOS ) // && some time has passed. Also, lagging players are considered lost if the NPC's never delivered // the start speech, because it means the NPC should run to the player to start the lead. if( HasCondition( COND_LEAD_FOLLOWER_LAGGING ) ) { if ( !m_hasspokenstart ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } else { if ( m_args.bStopScenesWhenPlayerLost ) { // Try and stop me speaking my monolog, if I am if ( !m_hasPausedScenes && IsRunningScriptedScene( GetOuter() ) ) { //Msg("Stopping scenes.\n"); PauseActorsScriptedScenes( GetOuter(), false ); m_hasPausedScenes = true; } } if( m_LostTimer.IsRunning() ) { if( m_LostTimer.Expired() ) { SetCondition( COND_LEAD_FOLLOWER_LOST ); } } else { m_LostTimer.Start(); } } } else { // If I was speaking a monolog, resume it if ( m_args.bStopScenesWhenPlayerLost && m_hasPausedScenes ) { if ( IsRunningScriptedScene( GetOuter() ) ) { //Msg("Resuming scenes.\n"); ResumeActorsScriptedScenes( GetOuter(), false ); } m_hasPausedScenes = false; } m_LostTimer.Stop(); ClearCondition( COND_LEAD_FOLLOWER_LOST ); } // Evaluate for success // Success right now means being stationary, close to the goal, and having the player close by if ( !( m_args.flags & AILF_NO_DEF_SUCCESS ) ) { ClearCondition( COND_LEAD_SUCCESS ); // Check Z first, and only check 2d if we're within that bool bWithinZ = fabs(GetLocalOrigin().z - m_goal.z) < 64; if ( bWithinZ && (GetLocalOrigin() - m_goal).Length2D() <= 64 ) { if ( HasCondition( COND_LEAD_FOLLOWER_VERY_CLOSE ) ) { SetCondition( COND_LEAD_SUCCESS ); } else if ( m_successdistance ) { float flDistSqr = (pFollower->GetAbsOrigin() - GetLocalOrigin()).Length2DSqr(); if ( flDistSqr < (m_successdistance*m_successdistance) ) { SetCondition( COND_LEAD_SUCCESS ); } } } } if ( m_MoveMonitor.IsMarkSet() && m_MoveMonitor.TargetMoved( pFollower ) ) SetCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); else ClearCondition( COND_LEAD_FOLLOWER_MOVED_FROM_MARK ); } } if( m_args.bLeadDuringCombat ) { ClearCondition( COND_LIGHT_DAMAGE ); ClearCondition( COND_HEAVY_DAMAGE ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : flInterval - // &m_LastMoveTarget - // eMoveType - //----------------------------------------------------------------------------- void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType ) { // dvs: something is setting this bit, causing us to stop moving and get stuck that way Forget( bits_MEMORY_TURNING ); Vector Steer, SteerAvoid, SteerRel; Vector forward, right, up; //Get our orientation vectors. GetVectors( &forward, &right, &up); if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) ) { SteerSeek( Steer, GetEnemy()->GetAbsOrigin() ); } else { //If we are approaching our goal, use an arrival steering mechanism. if ( eMoveType == ICH_MOVETYPE_ARRIVE ) { SteerArrive( Steer, MoveTarget ); } else { //Otherwise use a seek steering mechanism. SteerSeek( Steer, MoveTarget ); } } #if FEELER_COLLISION Vector f, u, l, r, d; float probeLength = GetAbsVelocity().Length(); if ( probeLength < 150 ) probeLength = 150; if ( probeLength > 500 ) probeLength = 500; f = DoProbe( GetLocalOrigin() + (probeLength * forward) ); r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) ); l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) ); u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) ); d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) ); SteerAvoid = f+r+l+u+d; //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f ); if ( SteerAvoid.LengthSqr() ) { Steer = (SteerAvoid*0.5f); } m_vecVelocity = m_vecVelocity + (Steer*0.5f); VectorNormalize( m_vecVelocity ); SteerRel.x = forward.Dot( m_vecVelocity ); SteerRel.y = right.Dot( m_vecVelocity ); SteerRel.z = up.Dot( m_vecVelocity ); m_vecVelocity *= m_flGroundSpeed; #else //See if we need to avoid any obstacles. if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) ) { //Take the avoidance vector Steer = SteerAvoid; } //Clamp our ideal steering vector to within our physical limitations. ClampSteer( Steer, SteerRel, forward, right, up ); ApplyAbsVelocityImpulse( Steer * flInterval ); #endif Vector vecNewVelocity = GetAbsVelocity(); float flLength = vecNewVelocity.Length(); //Clamp our final speed if ( flLength > m_flGroundSpeed ) { vecNewVelocity *= ( m_flGroundSpeed / flLength ); flLength = m_flGroundSpeed; } Vector workVelocity = vecNewVelocity; AddSwimNoise( &workVelocity ); // Pose the fish properly SetPoses( SteerRel, flLength ); //Drag our victim before moving if ( m_pVictim != NULL ) { DragVictim( (workVelocity*flInterval).Length() ); } //Move along the current velocity vector if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false ) { //Attempt a half-step if ( WalkMove( (workVelocity*0.5f) * flInterval, MASK_NPCSOLID) == false ) { //Restart the velocity //VectorNormalize( m_vecVelocity ); vecNewVelocity *= 0.5f; } else { //Cut our velocity in half vecNewVelocity *= 0.5f; } } SetAbsVelocity( vecNewVelocity ); }
Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe ) { trace_t tr; float fraction = 1.0f; bool collided = false; Vector normal = Vector( 0, 0, -1 ); float waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 ); waterLevel -= GetAbsOrigin().z; waterLevel /= 150; if ( waterLevel < 1.0f ) { collided = true; fraction = waterLevel; } AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if ( ( collided == false ) || ( tr.fraction < fraction ) ) { fraction = tr.fraction; normal = tr.plane.normal; } if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) ) { #if FEELER_COLLISION_VISUALIZE NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f ); #endif Vector probeDir = probe - GetLocalOrigin(); Vector normalToProbeAndWallNormal = probeDir.Cross( normal ); Vector steeringVector = normalToProbeAndWallNormal.Cross( probeDir ); Vector velDir = GetAbsVelocity(); VectorNormalize( velDir ); float steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir ); if ( steeringForce < 0.0f ) { steeringForce = -steeringForce; } velDir = steeringVector; VectorNormalize( velDir ); steeringVector = steeringForce * velDir; return steeringVector; } #if FEELER_COLLISION_VISUALIZE NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f ); #endif return Vector( 0.0f, 0.0f, 0.0f ); }
//----------------------------------------------------------------------------- // Purpose: Called when spawning, after keyvalues have been set. //----------------------------------------------------------------------------- void CFuncRotating::Spawn( ) { #ifdef TF_DLL AddSpawnFlags( SF_BRUSH_ROTATE_CLIENTSIDE ); #endif // // Maintain compatibility with previous maps. // if (m_flVolume == 0.0) { m_flVolume = 1.0; } // // If the designer didn't set a sound attenuation, default to one. // if ( HasSpawnFlags(SF_BRUSH_ROTATE_SMALLRADIUS) ) { m_flAttenuation = ATTN_IDLE; } else if ( HasSpawnFlags(SF_BRUSH_ROTATE_MEDIUMRADIUS) ) { m_flAttenuation = ATTN_STATIC; } else if ( HasSpawnFlags(SF_BRUSH_ROTATE_LARGERADIUS) ) { m_flAttenuation = ATTN_NORM; } else { m_flAttenuation = ATTN_NORM; } // // Prevent divide by zero if level designer forgets friction! // if ( m_flFanFriction == 0 ) { m_flFanFriction = 1; } // // Build the axis of rotation based on spawnflags. // if ( HasSpawnFlags(SF_BRUSH_ROTATE_Z_AXIS) ) { m_vecMoveAng = QAngle(0,0,1); } else if ( HasSpawnFlags(SF_BRUSH_ROTATE_X_AXIS) ) { m_vecMoveAng = QAngle(1,0,0); } else { m_vecMoveAng = QAngle(0,1,0); // y-axis } // // Check for reverse rotation. // if ( HasSpawnFlags(SF_BRUSH_ROTATE_BACKWARDS) ) { m_vecMoveAng = m_vecMoveAng * -1; } SetSolid( SOLID_VPHYSICS ); // // Some rotating objects like fake volumetric lights will not be solid. // if ( HasSpawnFlags(SF_ROTATING_NOT_SOLID) ) { AddSolidFlags( FSOLID_NOT_SOLID ); SetMoveType( MOVETYPE_PUSH ); } else { RemoveSolidFlags( FSOLID_NOT_SOLID ); SetMoveType( MOVETYPE_PUSH ); } SetModel( STRING( GetModelName() ) ); SetUse( &CFuncRotating::RotatingUse ); // // Did level designer forget to assign a maximum speed? Prevent a divide by // zero in RampPitchVol as well as allowing the rotator to work. // m_flMaxSpeed = fabs( m_flMaxSpeed ); if (m_flMaxSpeed == 0) { m_flMaxSpeed = 100; } // // If the brush should be initially rotating, use it in a little while. // if ( HasSpawnFlags(SF_BRUSH_ROTATE_START_ON) ) { SetThink( &CFuncRotating::SUB_CallUseToggle ); SetNextThink( gpGlobals->curtime + .2 ); // leave a magic delay for client to start up } // // Can this brush inflict pain? // if ( HasSpawnFlags(SF_BRUSH_HURT) ) { SetTouch( &CFuncRotating::HurtTouch ); } // // Set speed to 0 in case there's an old "speed" key lying around. // m_flSpeed = 0; Precache( ); CreateVPhysics(); m_angStart = GetLocalAngles(); // Slam the object back to solid - if we really want it to be solid. if ( m_bSolidBsp ) { SetSolid( SOLID_BSP ); } if ( HasSpawnFlags(SF_BRUSH_ROTATE_CLIENTSIDE) ) { m_vecClientOrigin = GetLocalOrigin(); m_vecClientAngles = GetLocalAngles(); } }
void CMomentaryDoor :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { m_hActivator = pActivator; if( IsLockedByMaster( pActivator )) return; if( useType == USE_RESET ) { // stop moving immediately MoveDone(); return; } float speed = 0.0f; // allow on-off mode that simulate standard door if( FBitSet( pev->spawnflags, SF_DOOR_ONOFF_MODE )) { if( useType == USE_ON ) { useType = USE_SET; value = 1.0f; } else if( useType == USE_OFF ) { useType = USE_SET; value = 0.0f; } } // momentary buttons will pass down a float in here if( useType != USE_SET ) return; value = bound( 0.0f, value, 1.0f ); Vector move = m_vecPosition1 + (value * ( m_vecPosition2 - m_vecPosition1 )); // NOTE: historically momentary_door is completely ignore pev->speed // and get local speed that based on distance between new and current position // with interval 0.1 secs. This flag is allow constant speed like for func_door if( FBitSet( pev->spawnflags, SF_DOOR_CONSTANT_SPEED )) { speed = pev->speed; } else { // classic Valve method to determine speed Vector delta = move - GetLocalOrigin(); speed = delta.Length() * 10; } if( move != g_vecZero ) { // This entity only thinks when it moves, so if it's thinking, it's in the process of moving // play the sound when it starts moving if( m_iState == STATE_OFF ) EMIT_SOUND( edict(), CHAN_STATIC, STRING( pev->noise ), 1, ATTN_NORM ); m_iState = STATE_ON; // we are "in-moving" // clear think (that stops sounds) SetThink( NULL ); LinearMove( move, speed ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : flInterval - // - // *pTraceResult - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CAI_BaseNPC::AutoMovement( float flInterval, CBaseEntity *pTarget, AIMoveTrace_t *pTraceResult ) { bool ignored; Vector newPos; QAngle newAngles; if (flInterval <= 0.0) return true; m_ScheduleState.bTaskRanAutomovement = true; if (GetIntervalMovement( flInterval, ignored, newPos, newAngles )) { // DevMsg( "%.2f : (%.1f) %.1f %.1f %.1f\n", gpGlobals->curtime, (newPos - GetLocalOrigin()).Length(), newPos.x, newPos.y, newAngles.y ); if ( m_hCine ) { m_hCine->ModifyScriptedAutoMovement( &newPos ); } if (GetMoveType() == MOVETYPE_STEP) { if (!(GetFlags() & FL_FLY)) { if ( !pTarget ) { pTarget = GetNavTargetEntity(); } // allow NPCs to adjust the automatic movement if ( ModifyAutoMovement( newPos ) ) { // Set our motor's speed here Vector vecOriginalPosition = GetAbsOrigin(); bool bResult = false; if (!TaskIsComplete()) { bResult = ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS ); } Vector change = GetAbsOrigin() - vecOriginalPosition; if (flInterval != 0) { change /= flInterval; } GetMotor()->SetMoveVel(change); return bResult; } return ( GetMotor()->MoveGroundStep( newPos, pTarget, newAngles.y, false, true, pTraceResult ) == AIM_SUCCESS ); } else { // FIXME: here's no direct interface to a fly motor, plus this needs to support a state where going through the world is okay. // FIXME: add callbacks into the script system for validation // FIXME: add function on scripts to force only legal movements // FIXME: GetIntervalMovement deals in Local space, nor global. Currently now way to communicate that through these interfaces. SetLocalOrigin( newPos ); SetLocalAngles( newAngles ); return true; } } else if (GetMoveType() == MOVETYPE_FLY) { Vector dist = newPos - GetLocalOrigin(); VectorScale( dist, 1.0 / flInterval, dist ); SetLocalVelocity( dist ); return true; } } return false; }
void CBaseDoor::Blocked( CBaseEntity *pOther ) { // hurt the blocker a little. if( pev->dmg ) pOther->TakeDamage( pev, pev, pev->dmg, DMG_CRUSH ); // if a door has a negative wait, it would never come back if blocked, // so let it just squash the object to death real fast if( m_flWait >= 0 ) { if( !FBitSet( pev->spawnflags, SF_DOOR_SILENT )) STOP_SOUND( edict(), CHAN_STATIC, STRING( pev->noise1 )); if( m_iState == STATE_TURN_OFF ) { DoorGoUp(); } else { DoorGoDown(); } } // block all door pieces with the same targetname here. if( !FStringNull( pev->targetname )) { CBaseDoor *pDoorList[64]; int doorCount = GetDoorMovementGroup( pDoorList, ARRAYSIZE( pDoorList )); for( int i = 0; i < doorCount; i++ ) { CBaseDoor *pDoor = pDoorList[i]; if( pDoor->m_flWait >= 0) { if( m_bDoorGroup && pDoor->pev->movedir == pev->movedir && pDoor->GetAbsVelocity() == GetAbsVelocity() && pDoor->GetLocalAvelocity() == GetLocalAvelocity( )) { pDoor->m_iPhysicsFrame = g_ulFrameCount; // don't run physics this frame if you haven't run yet // this is the most hacked, evil, bastardized thing I've ever seen. kjb if( !pDoor->IsRotatingDoor( )) { // set origin to realign normal doors pDoor->SetLocalOrigin( GetLocalOrigin( )); pDoor->SetAbsVelocity( g_vecZero ); // stop! } else { // set angles to realign rotating doors pDoor->SetLocalAngles( GetLocalAngles( )); pDoor->SetLocalAvelocity( g_vecZero ); } } if( pDoor->m_iState == STATE_TURN_OFF ) pDoor->DoorGoUp(); else pDoor->DoorGoDown(); } } } }
void CAI_PlaneSolver::GenerateSuggestionFromTrace( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &moveTrace, float probeDist, float arcCenter, float arcSpan, int probeOffset ) { AI_MoveSuggestion_t suggestion; AI_MoveSuggType_t type; switch ( moveTrace.fStatus ) { case AIMR_BLOCKED_ENTITY: type = AIMST_AVOID_OBJECT; break; case AIMR_BLOCKED_WORLD: type = AIMST_AVOID_WORLD; break; case AIMR_BLOCKED_NPC: type = AIMST_AVOID_NPC; break; case AIMR_ILLEGAL: type = AIMST_AVOID_DANGER; break; default: type = AIMST_NO_KNOWLEDGE; AssertMsg( 0, "Unexpected mode status" ); break; } if ( goal.pMoveTarget != NULL && goal.pMoveTarget == moveTrace.pObstruction ) { suggestion.Set( type, 0, arcCenter, arcSpan, moveTrace.pObstruction ); m_Solver.AddRegulation( suggestion ); return; } float clearDist = probeDist - moveTrace.flDistObstructed; float pctBlocked = 1.0 - ( clearDist / probeDist ); float weight = CalculateRegulationWeight( moveTrace, pctBlocked ); if ( weight < 0.001 ) return; if ( pctBlocked < 0.5 ) { arcSpan *= pctBlocked * 2.0; } Vector vecToEnd = moveTrace.vEndPosition - GetLocalOrigin(); Vector crossProduct; bool favorLeft = false, favorRight = false; if ( moveTrace.fStatus == AIMR_BLOCKED_NPC ) { Vector vecToOther = moveTrace.pObstruction->GetLocalOrigin() - GetLocalOrigin(); CrossProduct(vecToEnd, vecToOther, crossProduct); favorLeft = ( crossProduct.z < 0 ); favorRight = ( crossProduct.z > 0 ); } else if ( moveTrace.vHitNormal != vec3_origin ) { CrossProduct(vecToEnd, moveTrace.vHitNormal, crossProduct); favorLeft = ( crossProduct.z > 0 ); favorRight = ( crossProduct.z < 0 ); } float thirdSpan = arcSpan / 3.0; float favoredWeight = weight * pctBlocked; suggestion.Set( type, weight, arcCenter, thirdSpan, moveTrace.pObstruction ); m_Solver.AddRegulation( suggestion ); suggestion.Set( type, ( favorRight ) ? favoredWeight : weight, arcCenter - thirdSpan, thirdSpan, moveTrace.pObstruction ); m_Solver.AddRegulation( suggestion ); suggestion.Set( type, ( favorLeft ) ? favoredWeight : weight, arcCenter + thirdSpan, thirdSpan, moveTrace.pObstruction ); m_Solver.AddRegulation( suggestion ); }
void CPhysMotor::Activate( void ) { BaseClass::Activate(); // This gets called after all objects spawn and after all objects restore if ( m_attachedObject == NULL ) { CBaseEntity *pAttach = gEntList.FindEntityByName( NULL, m_nameAttach, NULL ); if ( pAttach && pAttach->GetMoveType() == MOVETYPE_VPHYSICS ) { m_attachedObject = pAttach; IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject(); CalculateAcceleration(); matrix3x4_t matrix; pPhys->GetPositionMatrix( matrix ); Vector motorAxis_ls; VectorIRotate( m_motor.m_axis, matrix, motorAxis_ls ); float inertia = DotProductAbs( pPhys->GetInertia(), motorAxis_ls ); m_motor.m_maxTorque = inertia * m_motor.m_inertiaFactor * (m_angularAcceleration + m_additionalAcceleration); m_motor.m_restistanceDamping = 1.0f; } } if ( m_attachedObject ) { IPhysicsObject *pPhys = m_attachedObject->VPhysicsGetObject(); // create a hinge constraint for this object? if ( m_spawnflags & SF_MOTOR_HINGE ) { // UNDONE: Don't do this on restore? if ( !m_pHinge ) { constraint_hingeparams_t hingeParams; hingeParams.Defaults(); hingeParams.worldAxisDirection = m_motor.m_axis; hingeParams.worldPosition = GetLocalOrigin(); m_pHinge = physenv->CreateHingeConstraint( g_PhysWorldObject, pPhys, NULL, hingeParams ); m_pHinge->SetGameData( (void *)this ); } if ( m_spawnflags & SF_MOTOR_NOCOLLIDE ) { physenv->DisableCollisions( pPhys, g_PhysWorldObject ); } } else { m_pHinge = NULL; } // NOTE: On restore, this path isn't run because m_pController will not be NULL if ( !m_pController ) { m_pController = physenv->CreateMotionController( &m_motor ); m_pController->AttachObject( m_attachedObject->VPhysicsGetObject() ); if ( m_spawnflags & SF_MOTOR_START_ON ) { TurnOn(); } } } // Need to do this on restore since there's no good way to save this if ( m_pController ) { m_pController->SetEventHandler( &m_motor ); } }
bool CAI_PlaneSolver::RunMoveSolver( const AILocalMoveGoal_t &goal, const AIMoveTrace_t &directTrace, float degreesPositiveArc, bool fDeterOscillation, Vector *pResult ) { PLANESOLVER_PROFILE_SCOPE( CAI_PlaneSolver_RunMoveSolver ); AI_MoveSolution_t solution; if ( m_Solver.HaveRegulations() ) { // @TODO (toml 07-19-02): add a movement threshhold here (the target may be the same, // but the ai is nowhere near where the last solution was derived) bool fNewTarget = ( !m_fSolvedPrev || m_PrevTarget != goal.target ); // For debugging, visualize our regulations VisualizeRegulations(); AI_MoveSuggestion_t moveSuggestions[2]; int nSuggestions = 1; moveSuggestions[0].Set( AIMST_MOVE, 1, UTIL_VecToYaw( goal.dir ), degreesPositiveArc ); moveSuggestions[0].flags |= ComputeTurnBiasFlags( goal, directTrace ); if ( fDeterOscillation && !fNewTarget ) { #ifndef TESTING_SUGGESTIONS moveSuggestions[nSuggestions++].Set( AIMST_OSCILLATION_DETERRANCE, 1, m_PrevSolution - 180, 180 ); #endif } if ( m_Solver.Solve( moveSuggestions, nSuggestions, &solution ) ) { *pResult = UTIL_YawToVector( solution.dir ); if (goal.navType == NAV_FLY) { // FIXME: Does the z component have to occur during the goal // setting because it's there & only there where MoveLimit // will report contact with the world if we move up? AdjustSolutionForFliers( goal, solution.dir, pResult ); } // A crude attempt at oscillation detection: if we solved last time, and this time, and the same target is // involved, and we resulted in nearly a 180, we are probably oscillating #ifndef TESTING_SUGGESTIONS if ( !fNewTarget ) { float delta = solution.dir - m_PrevSolution; if ( delta < 0 ) delta += 360; if ( delta > 165 && delta < 195 ) return false; } #endif m_PrevSolution = solution.dir; m_PrevSolutionVector = *pResult; Vector curVelocity = m_pNpc->GetSmoothedVelocity(); if ( curVelocity != vec3_origin ) { VectorNormalize( curVelocity ); if ( !fNewTarget ) { *pResult = curVelocity * 0.1 + m_PrevSolutionVector * 0.1 + *pResult * 0.8; } else { *pResult = curVelocity * 0.2 + *pResult * 0.8; } } return true; } } else { if (goal.navType != NAV_FLY) { *pResult = goal.dir; } else { VectorSubtract( goal.target, GetLocalOrigin(), *pResult ); VectorNormalize( *pResult ); } return true; } return false; }