BOOL KBall::PassTo(KHero* pPasser, KHero* pReceiver) { BOOL bResult = false; BOOL bRetCode = false; KPOSITION cSrcPos; KPOSITION cDstPos; KFix2PParabola cPath(GetCurrentGravity()); int nZMax = 0; KGLOG_PROCESS_ERROR(pPasser && pReceiver); KGLOG_PROCESS_ERROR(pPasser->m_dwID != pReceiver->m_dwID); bRetCode = IsTokenBy(pPasser); KG_PROCESS_ERROR(bRetCode); cSrcPos = GetPassSrcPos(pPasser, pReceiver); cDstPos = GetPassDstPos(pPasser, pReceiver); nZMax = GetPassZMax(cSrcPos, cDstPos); cPath.SetCtrlParams(cSrcPos, cDstPos, nZMax); cPath.Start(g_pSO3World->m_nGameLoop); DoTrackMove(&cPath); pPasser->ForbitAutoTakeBall(GAME_FPS); bResult = true; Exit0: return bResult; }
void PhysicsLevelInit( void ) { physenv = physics->CreateEnvironment(); assert( physenv ); #ifdef PORTAL physenv_main = physenv; #endif { MEM_ALLOC_CREDIT(); g_EntityCollisionHash = physics->CreateObjectPairHash(); } // TODO: need to get the right factory function here //physenv->SetDebugOverlay( appSystemFactory ); physenv->SetGravity( Vector(0, 0, -GetCurrentGravity() ) ); // 15 ms per tick // NOTE: Always run client physics at this rate - helps keep ragdolls stable physenv->SetSimulationTimestep( IsXbox() ? DEFAULT_XBOX_CLIENT_VPHYSICS_TICK : DEFAULT_TICK_INTERVAL ); physenv->SetCollisionEventHandler( &g_Collisions ); physenv->SetCollisionSolver( &g_Collisions ); g_PhysWorldObject = PhysCreateWorld_Shared( GetClientWorldEntity(), modelinfo->GetVCollide(1), g_PhysDefaultObjectParams ); staticpropmgr->CreateVPhysicsRepresentations( physenv, &g_SolidSetup, NULL ); }
void KMovableObject::AfterProcessMoveZ() { if (IsInAir()) { // 悬空状态向下加速 m_nVelocityZ -= GetCurrentGravity(); } }
IMotionEvent::simresult_e CPhysicsNPCSolver::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) { if ( IsIntersecting() ) { const float PUSH_SPEED = 150.0f; if ( pObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if ( pPlayer ) { pPlayer->ForceDropOfCarriedPhysObjects( m_hEntity ); } } ResetCancelTime(); angular.Init(); linear.Init(); // Don't push on vehicles because they won't move if ( pObject->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY ) { if ( m_hEntity->GetServerVehicle() ) return SIM_NOTHING; } Vector origin, vel; pObject->GetPosition( &origin, NULL ); pObject->GetVelocity( &vel, NULL ); Vector dir = origin - m_hNPC->GetAbsOrigin(); dir.z = dir.z > 0 ? 0.1f : -0.1f; VectorNormalize(dir); AngularImpulse angVel; angVel.Init(); // NOTE: Iterate this object's contact points // if it can't move in this direction, try sliding along the plane/crease Vector pushImpulse; PhysComputeSlideDirection( pObject, dir * PUSH_SPEED, angVel, &pushImpulse, NULL, 0 ); dir = pushImpulse; VectorNormalize(dir); if ( DotProduct( vel, dir ) < PUSH_SPEED * 0.5f ) { linear = pushImpulse; if ( pObject->GetContactPoint(NULL,NULL) ) { linear.z += GetCurrentGravity(); } } return SIM_GLOBAL_ACCELERATION; } return SIM_NOTHING; }
BOOL KBall::BeShoot(KHero* pShooter, KBasketSocket* pTargetSocket, int nInterference) { BOOL bResult = false; BOOL bRetCode = false; int nNowHitRate = 0; int nNowAttenuationTimes = 0; KFix2PParabola cPath(GetCurrentGravity()); int nZmax = 0; KPOSITION cStart; KPOSITION cEnd; KGLOG_PROCESS_ERROR(pShooter && pTargetSocket); bRetCode = CanBeShootBy(pShooter); KGLOG_PROCESS_ERROR(bRetCode); cStart = GetShootStartPos(pShooter); cEnd = GetShootEndPos(pTargetSocket); pShooter->CalcHitRateParams(cStart, cEnd, nNowAttenuationTimes, nNowHitRate, nInterference); m_bCanHit = pShooter->RandomHitted(nNowHitRate); LogInfo( "Canhit:%d, hitrate:%d, virtualframe:%d, nInterference:%d", m_bCanHit, nNowHitRate, pShooter->m_nVirtualFrame, nInterference ); if (!m_bCanHit) { int nFlag = 1; if (nNowAttenuationTimes % 2 == 0) nFlag = -1; cEnd.nX += nFlag * pShooter->m_nMaxHitOffset * (HUNDRED_NUM - nNowHitRate) / HUNDRED_NUM; cEnd.nY += nFlag * pShooter->m_nMaxHitOffset * (HUNDRED_NUM - nNowHitRate) / HUNDRED_NUM; } nZmax = CalcProcessTrackMoveZmax(pShooter->m_nVirtualFrame, cStart, cEnd); cPath.SetCtrlParams(cStart, cEnd, nZmax); cPath.Start(g_pSO3World->m_nGameLoop); DoTrackMove(&cPath); m_nTotalFrame = cPath.GetTotalFrame(); m_pTargetSocket = pTargetSocket; m_dwShooterID = pShooter->m_dwID; m_dwThrowerID = ERROR_ID; bResult = true; Exit0: return bResult; }
void KMovableObject::ProcessXYRebound() { m_nVelocityX = (int)(m_fXYReboundCoeX * m_nVelocityX); m_nVelocityY = (int)(m_fXYReboundCoeY * m_nVelocityY); m_nVelocityZ += GetCurrentGravity(); m_nVelocityZ = -(int)(m_fXYReboundCoeZ * m_nVelocityZ); if (m_nVelocityZ < 0) m_nVelocityZ = 0; m_bXYRebound = false; //LogInfo("XY面碰撞后, V(%d, %d, %d), loop:%d", m_nVelocityX, m_nVelocityY, m_nVelocityZ, g_pSO3World->m_nGameLoop); }
BOOL KBall::SkillShoot(KHero* pShooter, KBasketSocket* pSocket) { BOOL bResult = false; BOOL bRetCode = false; int nVelocity = CELL_LENGTH; KSkillShootPath cPath((GetCurrentGravity())); KPOSITION cSocketPos; KPOSITION cSrc; KPOSITION cDst; KPOSITION cCtrl; KGLOG_PROCESS_ERROR(pShooter && pSocket); bRetCode = CanBeShootBy(pShooter); KG_PROCESS_ERROR(bRetCode); cSocketPos = pSocket->GetPosition(); cSrc = GetShootStartPos(pShooter); cDst = cSocketPos; cDst.nZ += CELL_LENGTH * 3; cCtrl.nX = (pSocket->m_eDir == csdLeft) ? cSocketPos.nX - (CELL_LENGTH * 12) : cSocketPos.nX + (CELL_LENGTH * 12); cCtrl.nY = cSocketPos.nY; cCtrl.nZ = CELL_LENGTH * 4; cPath.SetCtrlParams(cSrc, cDst, cCtrl, nVelocity); cPath.Start(g_pSO3World->m_nGameLoop); DoTrackMove(&cPath); m_bAttackState = true; m_dwShooterID = pShooter->m_dwID; m_dwThrowerID = ERROR_ID; m_pTargetSocket = pSocket; m_bRestAfterTrackMove = true; bResult = true; Exit0: return bResult; }
//------------------------------------------------------------------------------ // Apply the forces to the entity //------------------------------------------------------------------------------ IMotionEvent::simresult_e C_EntityDissolve::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular ) { linear.Init(); angular.Init(); // Make it zero g linear.z -= -1.02 * GetCurrentGravity(); Vector vel; AngularImpulse angVel; pObject->GetVelocity( &vel, &angVel ); vel += linear * deltaTime; // account for gravity scale Vector unitVel = vel; Vector unitAngVel = angVel; float speed = VectorNormalize( unitVel ); // float angSpeed = VectorNormalize( unitAngVel ); // float speedScale = 0.0; // float angSpeedScale = 0.0; float flLinearLimit = 50; float flLinearLimitDelta = 40; if ( speed > flLinearLimit ) { float flDeltaVel = (flLinearLimit - speed) / deltaTime; if ( flLinearLimitDelta != 0.0f ) { float flMaxDeltaVel = -flLinearLimitDelta / deltaTime; if ( flDeltaVel < flMaxDeltaVel ) { flDeltaVel = flMaxDeltaVel; } } VectorMA( linear, flDeltaVel, unitVel, linear ); } return SIM_GLOBAL_ACCELERATION; }
BOOL KBall::SkillSlamEx(KHero* pSlamer, KSlamBallSkill* pSkill, KBasketSocket* pTargetSocket) { BOOL bResult = false; BOOL bRetCode = false; int nNowHitRate = 0; int nNowAttenuationTimes = 0; int nZmax = 0; KFix2PParabola cPath(GetCurrentGravity()); KPOSITION cSrc; KPOSITION cDst; int nTotalFrame = 0; KG_PROCESS_ERROR(pSlamer && pTargetSocket); bRetCode = CanSkillSlam(pSlamer, pTargetSocket); KGLOG_PROCESS_ERROR(bRetCode); cSrc = GetShootStartPos(pSlamer); cDst = GetSkillSlamDstPos(pSlamer, pTargetSocket); nZmax = CalcProcessTrackMoveZmax(pSlamer->m_nVirtualFrame, cSrc, cDst); cPath.SetCtrlParams(cSrc, cDst, nZmax); cPath.Start(pSlamer->m_nVirtualFrame); pSlamer->DoTrackMove(&cPath); nTotalFrame = cPath.GetTotalFrame(); pSlamer->LoseControlByCounter(nTotalFrame); m_bCanHit = true; m_nTotalFrame = cPath.GetTotalFrame(); m_pTargetSocket = pTargetSocket; m_dwShooterID = pSlamer->m_dwID; m_dwThrowerID = ERROR_ID; bResult = true; Exit0: return bResult; }
//----------------------------------------------------------------------------- // Purpose: Hop off the ground to start deployment //----------------------------------------------------------------------------- void CGrenadeHopwire::Detonate( void ) { SetModel( GRENADE_MODEL_OPEN ); AngularImpulse hopAngle = RandomAngularImpulse( -300, 300 ); //Find out how tall the ceiling is and always try to hop halfway trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, MAX_HOP_HEIGHT*2 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); // Jump half the height to the found ceiling float hopHeight = MIN( MAX_HOP_HEIGHT, (MAX_HOP_HEIGHT*tr.fraction) ); //Add upwards velocity for the "hop" Vector hopVel( 0.0f, 0.0f, hopHeight ); SetVelocity( hopVel, hopAngle ); // Get the time until the apex of the hop float apexTime = sqrt( hopHeight / GetCurrentGravity() ); // Explode at the apex SetThink( &CGrenadeHopwire::CombatThink ); SetNextThink( gpGlobals->curtime + apexTime); }
void CWeaponBugBait::DrawArc( ) { int idxImpact = NUM_ARC_POINTS-1; float curtime = gpGlobals->curtime; CBaseCombatCharacter *pOwner = GetOwner(); if ( pOwner == NULL ) return; CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); if ( pPlayer == NULL ) return; Vector throwVelocity, shootDirection; Vector origin = pPlayer->EyePosition() + pPlayer->EyeToWeaponOffset(); pPlayer->EyeVectors(&shootDirection); pPlayer->GetVelocity( &throwVelocity, NULL ); throwVelocity += shootDirection * 1000; throwVelocity *= ARC_TIME_UNIT; Vector vecGravity = Vector(0,0,-GetCurrentGravity() * ARC_TIME_UNIT * ARC_TIME_UNIT); Vector last = origin; bool impacted = false; // Reset the indicator pulse time float pulseSpeedScale = 1; if ( curtime > m_pulseStart + NUM_ARC_POINTS*ARC_TIME_UNIT*pulseSpeedScale*2.5) m_pulseStart = curtime; for ( int i = 0; i < NUM_ARC_POINTS - 1; i ++ ) { if ( impacted ) { m_hArcPoints[i]->TurnOff(); continue; } // Position at a specific point in time: // p(n) = orig + n*vel + ((n^2+n)*accel) / 2 int t = i+1; Vector position = origin + throwVelocity*t + ((t*t+t)*vecGravity) / 2; m_hArcPoints[i]->TurnOn(); m_hArcPoints[i]->SetAbsOrigin(position); float pct = i/(float) NUM_ARC_POINTS; m_hArcPoints[i]->SetBrightness( 80 - 80*pct*pct ); // show a segment brighter if it fits within the pulse window float elapsed = curtime-m_pulseStart; int pulseSegment = floor(elapsed / (ARC_TIME_UNIT*pulseSpeedScale)); if ( i == pulseSegment ) m_hArcPoints[i]->SetScale(ARC_SPRITE_SCALE*2, .2); else m_hArcPoints[i]->SetScale(ARC_SPRITE_SCALE, .2); trace_t tr; UTIL_TraceLine(last, position, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction < 1 ) // segment impacted, place impact sprite { impacted = true; m_hArcPoints[i]->TurnOff(); m_hArcPoints[idxImpact]->SetAbsOrigin(tr.endpos); m_hArcPoints[idxImpact]->TurnOn(); } last = position; } if ( !impacted ) { m_hArcPoints[idxImpact]->TurnOff(); } }
//========================================================= // 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 = GetCurrentGravity(); 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 = GetCurrentGravity(); 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; } }
//----------------------------------------------------------------------------- // Purpose: Override to check throw // Input : // Output : //----------------------------------------------------------------------------- int CWeaponMolotov::WeaponRangeAttack1Condition( float flDot, float flDist ) { // If things haven't changed too much since last time // just return that previously calculated value if (gpGlobals->curtime < m_fNextThrowCheck ) { return m_iThrowBits; } if ( flDist < m_fMinRange1) { m_iThrowBits = COND_TOO_CLOSE_TO_ATTACK; } else if (flDist > m_fMaxRange1) { m_iThrowBits = COND_TOO_FAR_TO_ATTACK; } else if (flDot < 0.5) { m_iThrowBits = COND_NOT_FACING_ATTACK; } // If moving, can't throw. else if ( m_flGroundSpeed != 0 ) { m_iThrowBits = COND_NONE; } else { // Ok we should check again as some time has passed // This function is only used by NPC's so we can cast to a Base Monster CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); CBaseEntity *pEnemy = pNPC->GetEnemy(); if (!pEnemy) { m_iThrowBits = COND_NONE; } // Get Enemy Position Vector vecTarget; pEnemy->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecTarget ); // Get Toss Vector Vector throwStart = pNPC->Weapon_ShootPosition(); Vector vecToss; CBaseEntity* pBlocker = NULL; float throwDist = (throwStart - vecTarget).Length(); float fGravity = GetCurrentGravity(); float throwLimit = pNPC->ThrowLimit(throwStart, vecTarget, fGravity, 35, WorldAlignMins(), WorldAlignMaxs(), pEnemy, &vecToss, &pBlocker); // If I can make the throw (or most of the throw) if (!throwLimit || (throwLimit != throwDist && throwLimit > 0.8*throwDist)) { m_vecTossVelocity = vecToss; m_iThrowBits = COND_CAN_RANGE_ATTACK1; } else { m_iThrowBits = COND_NONE; } } // don't check again for a while. m_fNextThrowCheck = gpGlobals->curtime + 0.33; // 1/3 second. return m_iThrowBits; }
//------------------------------------------------------------------------------ // Purpose : Move toward targetmap // Input : // Output : //------------------------------------------------------------------------------ void CGrenadeHomer::AimThink( void ) { // Blow up the missile if we have an explicit detonate time that // has been reached if (m_flDetonateTime != 0 && gpGlobals->curtime > m_flDetonateTime) { Detonate(); return; } PlayFlySound(); Vector vTargetPos = vec3_origin; Vector vTargetDir; float flCurHomingStrength = 0; // ------------------------------------------------ // If I'm homing // ------------------------------------------------ if (m_hTarget != NULL) { vTargetPos = m_hTarget->EyePosition(); vTargetDir = vTargetPos - GetAbsOrigin(); VectorNormalize(vTargetDir); // -------------------------------------------------- // If my target is far away do some primitive // obstacle avoidance // -------------------------------------------------- if ((vTargetPos - GetAbsOrigin()).Length() > 200) { Vector vTravelDir = GetAbsVelocity(); VectorNormalize(vTravelDir); vTravelDir *= 50; trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTravelDir, MASK_SHOT, m_hTarget, COLLISION_GROUP_NONE, &tr ); if (tr.fraction != 1.0) { // Head off in normal float dotPr = DotProduct(vTravelDir,tr.plane.normal); Vector vBounce = -dotPr * tr.plane.normal; vBounce.z = 0; VectorNormalize(vBounce); vTargetDir += vBounce; VectorNormalize(vTargetDir); // DEBUG TOOL //NDebugOverlay::Line(GetOrigin(), GetOrigin()+vTravelDir, 255,0,0, true, 20); //NDebugOverlay::Line(GetOrigin(), GetOrigin()+(12*tr.plane.normal), 0,0,255, true, 20); //NDebugOverlay::Line(GetOrigin(), GetOrigin()+(vTargetDir), 0,255,0, true, 20); } } float flTargetSpeed = GetAbsVelocity().Length(); float flHomingRampUpStartTime = m_flHomingLaunchTime + m_flHomingDelay; float flHomingSustainStartTime = flHomingRampUpStartTime + m_flHomingRampUp; float flHomingRampDownStartTime = flHomingSustainStartTime + m_flHomingDuration; float flHomingEndHomingTime = flHomingRampDownStartTime + m_flHomingRampDown; // --------- // Delay // --------- if (gpGlobals->curtime < flHomingRampUpStartTime) { flCurHomingStrength = 0; flTargetSpeed = 0; } // ---------- // Ramp Up // ---------- else if (gpGlobals->curtime < flHomingSustainStartTime) { float flAge = gpGlobals->curtime - flHomingRampUpStartTime; flCurHomingStrength = m_flHomingStrength * (flAge/m_flHomingRampUp); flTargetSpeed = flCurHomingStrength * m_flHomingSpeed; } // ---------- // Sustain // ---------- else if (gpGlobals->curtime < flHomingRampDownStartTime) { flCurHomingStrength = m_flHomingStrength; flTargetSpeed = m_flHomingSpeed; } // ----------- // Ramp Down // ----------- else if (gpGlobals->curtime < flHomingEndHomingTime) { float flAge = gpGlobals->curtime - flHomingRampDownStartTime; flCurHomingStrength = m_flHomingStrength * (1-(flAge/m_flHomingRampDown)); flTargetSpeed = m_flHomingSpeed; } // --------------- // Set Homing // --------------- if (flCurHomingStrength > 0) { // ------------- // Smoke trail. // ------------- if (m_nRocketTrailType == HOMER_SMOKE_TRAIL_ON_HOMING) { UpdateRocketTrail(flCurHomingStrength); } // Extract speed and direction Vector vCurDir = GetAbsVelocity(); float flCurSpeed = VectorNormalize(vCurDir); flTargetSpeed = MAX(flTargetSpeed, flCurSpeed); // Add in homing direction Vector vecNewVelocity = GetAbsVelocity(); float flTimeToUse = gpGlobals->frametime; while (flTimeToUse > 0) { vecNewVelocity = (flCurHomingStrength * vTargetDir) + ((1 - flCurHomingStrength) * vCurDir); flTimeToUse = -0.1; } VectorNormalize(vecNewVelocity); vecNewVelocity *= flTargetSpeed; SetAbsVelocity( vecNewVelocity ); } } // ---------------------------------------------------------------------------------------- // Add time-coherent noise to the current velocity // ---------------------------------------------------------------------------------------- Vector vecImpulse( 0, 0, 0 ); if (m_flSpinMagnitude > 0) { vecImpulse.x += m_flSpinMagnitude*sin(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset); vecImpulse.y += m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset); vecImpulse.z -= m_flSpinMagnitude*cos(m_flSpinSpeed * gpGlobals->curtime + m_flSpinOffset); } // Add in gravity vecImpulse.z -= GetGravity() * GetCurrentGravity() * gpGlobals->frametime; ApplyAbsVelocityImpulse( vecImpulse ); QAngle angles; VectorAngles( GetAbsVelocity(), angles ); SetLocalAngles( angles ); #if 0 // BUBBLE if( gpGlobals->curtime > m_flNextWarnTime ) { // Make a bubble of warning sound in front of me. const float WARN_INTERVAL = 0.25f; float flSpeed = GetAbsVelocity().Length(); Vector vecWarnLocation; // warn a little bit ahead of us, please. vecWarnLocation = GetAbsOrigin() + GetAbsVelocity() * 0.75; // Make a bubble of warning ahead of the missile. CSoundEnt::InsertSound ( SOUND_DANGER, vecWarnLocation, flSpeed * WARN_INTERVAL, 0.5 ); #if 0 Vector vecRight, vecForward; AngleVectors( GetAbsAngles(), &vecForward, &vecRight, NULL ); NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10); NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecForward * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10); NDebugOverlay::Line( vecWarnLocation, vecWarnLocation + vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10); NDebugOverlay::Line( vecWarnLocation, vecWarnLocation - vecRight * flSpeed * WARN_INTERVAL * 0.5, 255,255,0, true, 10); #endif m_flNextWarnTime = gpGlobals->curtime + WARN_INTERVAL; } #endif // BUBBLE SetNextThink( gpGlobals->curtime + 0.1f ); }
//========================================================= // HandleAnimEvent - catches the monster-specific messages // that occur when tagged animation frames are played. //========================================================= void CNPC_Bullsquid::HandleAnimEvent( animevent_t *pEvent ) { switch( pEvent->event ) { case BSQUID_AE_SPIT: { Vector vSpitPos; GetAttachment("mouth", vSpitPos); vSpitPos.z += 40.0f; Vector vTarget; // If our enemy is looking at us and far enough away, lead him if (HasCondition(COND_ENEMY_FACING_ME) && UTIL_DistApprox(GetAbsOrigin(), GetEnemy()->GetAbsOrigin()) > (40 * 12)) { UTIL_PredictedPosition(GetEnemy(), 0.5f, &vTarget); vTarget.z = GetEnemy()->GetAbsOrigin().z; } else { // Otherwise he can't see us and he won't be able to dodge vTarget = GetEnemy()->BodyTarget(vSpitPos, true); } vTarget[2] += random->RandomFloat(0.0f, 32.0f); // Try and spit at our target Vector vecToss; if (GetSpitVector(vSpitPos, vTarget, &vecToss) == false) { DevMsg("GetSpitVector( vSpitPos, vTarget, &vecToss ) == false\n"); // Now try where they were if (GetSpitVector(vSpitPos, m_vSavePosition, &vecToss) == false) { DevMsg("GetSpitVector( vSpitPos, m_vSavePosition, &vecToss ) == false\n"); // Failing that, just shoot with the old velocity we calculated initially! vecToss = m_vecSaveSpitVelocity; } } // Find what our vertical theta is to estimate the time we'll impact the ground Vector vecToTarget = (vTarget - vSpitPos); VectorNormalize(vecToTarget); float flVelocity = VectorNormalize(vecToss); float flCosTheta = DotProduct(vecToTarget, vecToss); float flTime = (vSpitPos - vTarget).Length2D() / (flVelocity * flCosTheta); // Emit a sound where this is going to hit so that targets get a chance to act correctly CSoundEnt::InsertSound(SOUND_DANGER, vTarget, (15 * 12), flTime, this); // Don't fire again until this volley would have hit the ground (with some lag behind it) SetNextAttack(gpGlobals->curtime + flTime + random->RandomFloat(0.5f, 2.0f)); for (int i = 0; i < 6; i++) { CGrenadeSpit *pGrenade = (CGrenadeSpit*)CreateEntityByName("grenade_spit"); pGrenade->SetAbsOrigin(vSpitPos); pGrenade->SetAbsAngles(vec3_angle); DispatchSpawn(pGrenade); pGrenade->SetThrower(this); pGrenade->SetOwnerEntity(this); if (i == 0) { pGrenade->SetSpitSize(SPIT_LARGE); pGrenade->SetAbsVelocity(vecToss * flVelocity); } else { pGrenade->SetAbsVelocity((vecToss + RandomVector(-0.035f, 0.035f)) * flVelocity); pGrenade->SetSpitSize(random->RandomInt(SPIT_SMALL, SPIT_MEDIUM)); } // Tumble through the air pGrenade->SetLocalAngularVelocity(QAngle(random->RandomFloat(-250, -500), random->RandomFloat(-250, -500), random->RandomFloat(-250, -500))); } for (int i = 0; i < 8; i++) { DispatchParticleEffect("blood_impact_yellow_01", vSpitPos + RandomVector(-12.0f, 12.0f), RandomAngle(0, 360)); } EmitSound("NPC_Antlion.PoisonShoot"); } break; case BSQUID_AE_BITE: { // SOUND HERE! CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_bite.GetFloat(), DMG_SLASH ); if ( pHurt ) { Vector forward, up; AngleVectors( GetAbsAngles(), &forward, NULL, &up ); pHurt->ApplyAbsVelocityImpulse( 100 * (up-forward) ); pHurt->SetGroundEntity( NULL ); } } break; case BSQUID_AE_WHIP_SND: { EmitSound( "NPC_Bullsquid.TailWhip" ); break; } /* case BSQUID_AE_TAILWHIP: { CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_whip.GetFloat(), DMG_SLASH | DMG_ALWAYSGIB ); if ( pHurt ) { Vector right, up; AngleVectors( GetAbsAngles(), NULL, &right, &up ); if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) ) pHurt->ViewPunch( QAngle( 20, 0, -20 ) ); pHurt->ApplyAbsVelocityImpulse( 100 * (up+2*right) ); } } break; */ case BSQUID_AE_BLINK: { // close eye. m_nSkin = 1; } break; case BSQUID_AE_HOP: { float flGravity = GetCurrentGravity(); // throw the squid up into the air on this frame. if ( GetFlags() & FL_ONGROUND ) { SetGroundEntity( NULL ); } // jump 40 inches into the air Vector vecVel = GetAbsVelocity(); vecVel.z += sqrt( flGravity * 2.0 * 40 ); SetAbsVelocity( vecVel ); } break; case BSQUID_AE_THROW: { // squid throws its prey IF the prey is a client. CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), 0, 0 ); if ( pHurt ) { pHurt->ViewPunch( QAngle(20,0,-20) ); // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. UTIL_ScreenShake( pHurt->GetAbsOrigin(), 25.0, 1.5, 0.7, 2, SHAKE_START ); // If the player, throw him around if ( pHurt->IsPlayer()) { Vector forward, up; AngleVectors( GetLocalAngles(), &forward, NULL, &up ); pHurt->ApplyAbsVelocityImpulse( forward * 300 + up * 300 ); } // If not the player see if has bullsquid throw interatcion else { CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pHurt ); if (pVictim) { if ( pVictim->DispatchInteraction( g_interactionBullsquidThrow, NULL, this ) ) { Vector forward, up; AngleVectors( GetLocalAngles(), &forward, NULL, &up ); pVictim->ApplyAbsVelocityImpulse( forward * 300 + up * 250 ); } } } } } break; default: BaseClass::HandleAnimEvent( pEvent ); } }
Vector BullsquidVecCheckThrowTolerance(CBaseEntity *pEdict, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flTolerance) { flSpeed = MAX(1.0f, flSpeed); float flGravity = GetCurrentGravity(); Vector vecGrenadeVel = (vecSpot2 - vecSpot1); // throw at a constant time float time = vecGrenadeVel.Length() / flSpeed; vecGrenadeVel = vecGrenadeVel * (1.0 / time); // adjust upward toss to compensate for gravity loss vecGrenadeVel.z += flGravity * time * 0.5; Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5); trace_t tr; UTIL_TraceLine(vecSpot1, vecApex, MASK_SOLID, pEdict, COLLISION_GROUP_NONE, &tr); if (tr.fraction != 1.0) { // fail! if (g_debug_bullsquid.GetBool()) { NDebugOverlay::Line(vecSpot1, vecApex, 255, 0, 0, true, 5.0); } return vec3_origin; } if (g_debug_bullsquid.GetBool()) { NDebugOverlay::Line(vecSpot1, vecApex, 0, 255, 0, true, 5.0); } UTIL_TraceLine(vecApex, vecSpot2, MASK_SOLID_BRUSHONLY, pEdict, COLLISION_GROUP_NONE, &tr); if (tr.fraction != 1.0) { bool bFail = true; // Didn't make it all the way there, but check if we're within our tolerance range if (flTolerance > 0.0f) { float flNearness = (tr.endpos - vecSpot2).LengthSqr(); if (flNearness < Square(flTolerance)) { if (g_debug_bullsquid.GetBool()) { NDebugOverlay::Sphere(tr.endpos, vec3_angle, flTolerance, 0, 255, 0, 0, true, 5.0); } bFail = false; } } if (bFail) { if (g_debug_bullsquid.GetBool()) { NDebugOverlay::Line(vecApex, vecSpot2, 255, 0, 0, true, 5.0); NDebugOverlay::Sphere(tr.endpos, vec3_angle, flTolerance, 255, 0, 0, 0, true, 5.0); } return vec3_origin; } } if (g_debug_bullsquid.GetBool()) { NDebugOverlay::Line(vecApex, vecSpot2, 0, 255, 0, true, 5.0); } return vecGrenadeVel; }