void CHostageImprov::__MAKE_VHOOK(OnReset)() { m_moveFlags = 0; m_moveType = Stopped; m_moveLimit = Running; m_vel.x = 0; m_vel.y = 0; m_actualVel = Vector(0, 0, 0); m_checkNearbyTerroristTimer.Invalidate(); m_lastKnownArea = NULL; m_hasKnownGoodPos = false; m_hasPriorKnownGoodPos = false; m_isTerroristNearby = false; m_isCrouching = false; SetMoveAngle(m_hostage->pev->angles.y); m_moveGoal = m_hostage->m_vStart; ClearLookAt(); ClearFaceTo(); m_blinkTimer.Invalidate(); m_blinkCounter = 0; m_hostage->SetBoneController(2, 0); m_hostage->SetBoneController(3, 0); m_hostage->SetBoneController(4, 0); m_path.Invalidate(); m_chatterTimer.Invalidate(); m_visiblePlayerCount = 0; m_isDelayedChatterPending = 0; m_animateState.Reset(); m_didFidget = 0; m_lastSawCT.Start(); m_lastSawT.Start(); m_scaredTimer.Invalidate(); m_jumpTimer.Invalidate(); m_hasJumped = false; m_isFriendInTheWay = false; m_aggression = RANDOM_FLOAT(0, 1); StandUp(); m_behavior.Reset(this); Idle(); }
// Update the "looking around" behavior. void CCSBot::UpdateLookAround(bool updateNow) { // check if looking around has been inhibited // Moved inhibit to allow high priority enemy lookats to still occur if (gpGlobals->time < m_inhibitLookAroundTimestamp) return; const float recentThreatTime = 0.25f; // 1.0f; // Unless we can hear them moving, in which case look towards the noise if (!IsEnemyVisible()) { const float noiseStartleRange = 1000.0f; if (CanHearNearbyEnemyGunfire(noiseStartleRange)) { Vector spot = m_noisePosition; spot.z += HalfHumanHeight; SetLookAt("Check dangerous noise", &spot, PRIORITY_HIGH, recentThreatTime); InhibitLookAround(RANDOM_FLOAT(2.0f, 4.0f)); return; } } // If we recently saw an enemy, look towards where we last saw them if (!IsLookingAtSpot(PRIORITY_MEDIUM) && gpGlobals->time - m_lastSawEnemyTimestamp < recentThreatTime) { ClearLookAt(); Vector spot = m_lastEnemyPosition; // find enemy position on the ground if (GetSimpleGroundHeight(&m_lastEnemyPosition, &spot.z)) { spot.z += HalfHumanHeight; SetLookAt("Last Enemy Position", &spot, PRIORITY_MEDIUM, RANDOM_FLOAT(2.0f, 3.0f), true); return; } } // Look at nearby enemy noises if (UpdateLookAtNoise()) return; if (IsNotMoving()) { // if we're sniping, zoom in to watch our approach points if (IsUsingSniperRifle()) { // low skill bots don't pre-zoom if (GetProfile()->GetSkill() > 0.4f) { if (!IsViewMoving()) { float range = ComputeWeaponSightRange(); AdjustZoom(range); } else { // zoom out if (GetZoomLevel() != NO_ZOOM) SecondaryAttack(); } } } if (m_lastKnownArea == NULL) return; if (gpGlobals->time < m_lookAroundStateTimestamp) return; // if we're sniping, switch look-at spots less often if (IsUsingSniperRifle()) m_lookAroundStateTimestamp = gpGlobals->time + RANDOM_FLOAT(5.0f, 10.0f); else m_lookAroundStateTimestamp = gpGlobals->time + RANDOM_FLOAT(1.0f, 2.0f); if (m_approachPointCount == 0) { ClearLookAt(); return; } int which = RANDOM_LONG(0, m_approachPointCount - 1); Vector spot = m_approachPoint[ which ]; // don't look at the floor, look roughly at chest level // TODO: If this approach point is very near, this will cause us to aim up in the air if were crouching spot.z += HalfHumanHeight; SetLookAt("Approach Point (Hiding)", &spot, PRIORITY_LOW); return; } // Glance at "encouter spots" as we move past them if (m_spotEncounter) { // Check encounter spots if (!IsSafe() && !IsLookingAtSpot(PRIORITY_LOW)) { // allow a short time to look where we're going if (gpGlobals->time < m_spotCheckTimestamp) return; // TODO: Use skill parameter instead of accuracy // lower skills have exponentially longer delays float_precision asleep = (1.0f - GetProfile()->GetSkill()); asleep *= asleep; asleep *= asleep; m_spotCheckTimestamp = gpGlobals->time + asleep * RANDOM_FLOAT(10.0f, 30.0f); // figure out how far along the path segment we are Vector delta = m_spotEncounter->path.to - m_spotEncounter->path.from; float_precision length = delta.Length(); float adx = float(Q_abs(int64(delta.x))); float ady = float(Q_abs(int64(delta.y))); float_precision t; if (adx > ady) t = (pev->origin.x - m_spotEncounter->path.from.x) / delta.x; else t = (pev->origin.y - m_spotEncounter->path.from.y) / delta.y; // advance parameter a bit so we "lead" our checks const float leadCheckRange = 50.0f; t += leadCheckRange / length; if (t < 0.0f) t = 0.0f; else if (t > 1.0f) t = 1.0f; // collect the unchecked spots so far const int MAX_DANGER_SPOTS = 8; HidingSpot *dangerSpot[MAX_DANGER_SPOTS]; int dangerSpotCount = 0; int dangerIndex = 0; const float checkTime = 10.0f; const SpotOrder *spotOrder; for (SpotOrderList::iterator iter = m_spotEncounter->spotList.begin(); iter != m_spotEncounter->spotList.end(); ++iter) { spotOrder = &(*iter); // if we have seen this spot recently, we don't need to look at it if (gpGlobals->time - GetHidingSpotCheckTimestamp(spotOrder->spot) <= checkTime) continue; if (spotOrder->t > t) break; dangerSpot[ dangerIndex++ ] = spotOrder->spot; if (dangerIndex >= MAX_DANGER_SPOTS) dangerIndex = 0; if (dangerSpotCount < MAX_DANGER_SPOTS) ++dangerSpotCount; } if (dangerSpotCount) { // pick one of the spots at random int which = RANDOM_LONG(0, dangerSpotCount - 1); const Vector *checkSpot = dangerSpot[ which ]->GetPosition(); Vector pos = *checkSpot; pos.z += HalfHumanHeight; // glance at the spot for minimum time SetLookAt("Encounter Spot", &pos, PRIORITY_LOW, 0, true, 10.0f); // immediately mark it as "checked", so we don't check it again // if we get distracted before we check it - that's the way it goes SetHidingSpotCheckTimestamp(dangerSpot[which]); } } } }
void CHostageImprov::__MAKE_VHOOK(OnUpdate)(float deltaT) { if (!IsAlive() || cv_hostage_stop.value > 0.0f) return; if (m_blinkTimer.IsElapsed()) { if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) { m_blinkTimer.Start(RANDOM_FLOAT(3, 10)); m_blinkCounter = RANDOM_LONG(2, 4); } else { m_blinkTimer.Start(RANDOM_FLOAT(0.5f, 2.0f)); m_blinkCounter = RANDOM_LONG(1, 2); } } if (m_blinkCounter) { m_hostage->pev->body = 1; --m_blinkCounter; } else { m_hostage->pev->body = 0; } UpdateGrenadeReactions(); UpdateDelayedChatter(); UpdateVision(); m_behavior.Update(); m_actualVel.x = m_hostage->pev->origin.x - m_lastPosition.x; m_actualVel.y = m_hostage->pev->origin.y - m_lastPosition.y; const float runSpeed = 289.0f; const float walkSpeed = 9.0f; const float fallVelocity = -1000.0f; const float safeTime = 0.4f; if (IsOnGround()) { if (IsCrouching()) { if (m_actualVel.LengthSquared() > 9.0f) { if (m_animateState.GetPerformance() != HostageAnimateState::CrouchWalk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::CrouchWalk); ClearLookAt(); if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) m_animateState.AddSequence(this, ACT_CROUCH_WALK, 99.9, 2.0); else m_animateState.AddSequence(this, ACT_CROUCH_WALK_SCARED, 99.9, 2.0); } } else if (m_animateState.GetPerformance() != HostageAnimateState::Crouch) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Crouch); if (m_scaredTimer.IsElapsed()) m_animateState.AddSequence(this, ACT_CROUCH_IDLE, 99.9); else m_animateState.AddSequence(this, ACT_CROUCH_IDLE_SCARED); } } else { UTIL_MakeVectors(m_hostage->pev->angles); float dot = DotProduct2D(gpGlobals->v_forward, m_actualVel); if (dot < -3.0f) { if (m_animateState.GetPerformance() != HostageAnimateState::Walk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Walk); ClearLookAt(); float speed; if (m_actualVel.LengthSquared() > runSpeed) speed = 2.0f; else speed = 1.0f; m_animateState.AddSequence(this, ACT_WALK_BACK, 99.9, speed); } } else { if (m_actualVel.LengthSquared() > runSpeed) { if (m_animateState.GetPerformance() != HostageAnimateState::Run) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Run); ClearLookAt(); if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid && !m_behavior.IsState(&m_escapeState)) m_animateState.AddSequence(this, ACT_RUN, 99.9, 2.0); else m_animateState.AddSequence(this, ACT_RUN_SCARED, 99.9, 2.0); } } else if (m_actualVel.LengthSquared() > walkSpeed) { if (m_animateState.GetPerformance() != HostageAnimateState::Walk) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Walk); ClearLookAt(); if (m_behavior.IsState(&m_escapeState)) { m_animateState.AddSequence(this, ACT_WALK_SNEAKY, 99.9, 1.5); } else if (m_scaredTimer.IsElapsed() && m_animateState.GetPerformance() != HostageAnimateState::Afraid) { m_animateState.AddSequence(this, ACT_WALK, 99.9, 1.5); } else m_animateState.AddSequence(this, ACT_WALK_SCARED, 99.9, 1.5); } } else { if (m_animateState.GetPerformance() == HostageAnimateState::Walk || m_animateState.GetPerformance() == HostageAnimateState::Run) m_animateState.Reset(); UpdateStationaryAnimation(); } } } } else if (m_hostage->pev->velocity.z < fallVelocity && m_animateState.GetPerformance() != HostageAnimateState::Fall) { m_animateState.Reset(); m_animateState.SetPerformance(HostageAnimateState::Fall); m_animateState.AddSequence(this, ACT_FALL, 99.9); } if (!m_collisionTimer.HasStarted() || m_collisionTimer.IsGreaterThen(safeTime)) SetKnownGoodPosition(m_lastPosition); m_lastPosition = m_hostage->pev->origin; m_animateState.OnUpdate(this); }
// Move along the path. Return false if end of path reached. CCSBot::PathResult CCSBot::UpdatePathMovement(bool allowSpeedChange) { if (m_pathLength == 0) return PATH_FAILURE; if (cv_bot_walk.value != 0.0f) Walk(); // If we are navigating a ladder, it overrides all other path movement until complete if (UpdateLadderMovement()) return PROGRESSING; // ladder failure can destroy the path if (m_pathLength == 0) return PATH_FAILURE; // we are not supposed to be on a ladder - if we are, jump off if (IsOnLadder()) Jump(MUST_JUMP); assert(m_pathIndex < m_pathLength); // Check if reached the end of the path bool nearEndOfPath = false; if (m_pathIndex >= m_pathLength - 1) { Vector toEnd(pev->origin.x, pev->origin.y, GetFeetZ()); Vector d = GetPathEndpoint() - toEnd; // can't use 2D because path end may be below us (jump down) const float walkRange = 200.0f; // walk as we get close to the goal position to ensure we hit it if (d.IsLengthLessThan(walkRange)) { // don't walk if crouching - too slow if (allowSpeedChange && !IsCrouching()) Walk(); // note if we are near the end of the path const float nearEndRange = 50.0f; if (d.IsLengthLessThan(nearEndRange)) nearEndOfPath = true; const float closeEpsilon = 20.0f; if (d.IsLengthLessThan(closeEpsilon)) { // reached goal position - path complete DestroyPath(); // TODO: We should push and pop walk state here, in case we want to continue walking after reaching goal if (allowSpeedChange) Run(); return END_OF_PATH; } } } // To keep us moving smoothly, we will move towards // a point farther ahead of us down our path. int prevIndex = 0; // closest index on path just prior to where we are now const float aheadRange = 300.0f; int newIndex = FindPathPoint(aheadRange, &m_goalPosition, &prevIndex); // BOTPORT: Why is prevIndex sometimes -1? if (prevIndex < 0) prevIndex = 0; // if goal position is near to us, we must be about to go around a corner - so look ahead! const float nearCornerRange = 100.0f; if (m_pathIndex < m_pathLength - 1 && (m_goalPosition - pev->origin).IsLengthLessThan(nearCornerRange)) { ClearLookAt(); InhibitLookAround(0.5f); } // if we moved to a new node on the path, setup movement if (newIndex > m_pathIndex) { SetPathIndex(newIndex); } if (!IsUsingLadder()) { // Crouching // if we are approaching a crouch area, crouch // if there are no crouch areas coming up, stand const float crouchRange = 50.0f; bool didCrouch = false; for (int i = prevIndex; i < m_pathLength; i++) { const CNavArea *to = m_path[i].area; // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump // unless we are already higher than the jump area - we must've jumped already but not moved into next area if ((to->GetAttributes() & NAV_JUMP)/* && to->GetCenter()->z > GetFeetZ()*/) break; Vector close; to->GetClosestPointOnArea(&pev->origin, &close); if ((close - pev->origin).Make2D().IsLengthGreaterThan(crouchRange)) break; if (to->GetAttributes() & NAV_CROUCH) { Crouch(); didCrouch = true; break; } } if (!didCrouch && !IsJumping()) { // no crouch areas coming up StandUp(); } // end crouching logic } // compute our forward facing angle m_forwardAngle = UTIL_VecToYaw(m_goalPosition - pev->origin); // Look farther down the path to "lead" our view around corners Vector toGoal; if (m_pathIndex == 0) { toGoal = m_path[1].pos; } else if (m_pathIndex < m_pathLength) { toGoal = m_path[m_pathIndex].pos - pev->origin; // actually aim our view farther down the path const float lookAheadRange = 500.0f; if (!m_path[m_pathIndex].ladder && !IsNearJump() && toGoal.Make2D().IsLengthLessThan(lookAheadRange)) { float along = toGoal.Length2D(); int i; for (i = m_pathIndex + 1; i < m_pathLength; i++) { Vector delta = m_path[i].pos - m_path[i - 1].pos; float segmentLength = delta.Length2D(); if (along + segmentLength >= lookAheadRange) { // interpolate between points to keep look ahead point at fixed distance float t = (lookAheadRange - along) / (segmentLength + along); Vector target; if (t <= 0.0f) target = m_path[i - 1].pos; else if (t >= 1.0f) target = m_path[i].pos; else target = m_path[i - 1].pos + t * delta; toGoal = target - pev->origin; break; } // if we are coming up to a ladder or a jump, look at it if (m_path[i].ladder || (m_path[i].area->GetAttributes() & NAV_JUMP)) { toGoal = m_path[i].pos - pev->origin; break; } along += segmentLength; } if (i == m_pathLength) { toGoal = GetPathEndpoint() - pev->origin; } } } else { toGoal = GetPathEndpoint() - pev->origin; } m_lookAheadAngle = UTIL_VecToYaw(toGoal); // initialize "adjusted" goal to current goal Vector adjustedGoal = m_goalPosition; // Use short "feelers" to veer away from close-range obstacles // Feelers come from our ankles, just above StepHeight, so we avoid short walls, too // Don't use feelers if very near the end of the path, or about to jump // TODO: Consider having feelers at several heights to deal with overhangs, etc. if (!nearEndOfPath && !IsNearJump() && !IsJumping()) { FeelerReflexAdjustment(&adjustedGoal); } // draw debug visualization if ((cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 10.0f) { DrawPath(); const Vector *pos = &m_path[m_pathIndex].pos; UTIL_DrawBeamPoints(*pos, *pos + Vector(0, 0, 50), 1, 255, 255, 0); UTIL_DrawBeamPoints(adjustedGoal, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); UTIL_DrawBeamPoints(pev->origin, adjustedGoal + Vector(0, 0, 50), 1, 255, 0, 255); } // dont use adjustedGoal, as it can vary wildly from the feeler adjustment if (!IsAttacking() && IsFriendInTheWay(&m_goalPosition)) { if (!m_isWaitingBehindFriend) { m_isWaitingBehindFriend = true; const float politeDuration = 5.0f - 3.0f * GetProfile()->GetAggression(); m_politeTimer.Start(politeDuration); } else if (m_politeTimer.IsElapsed()) { // we have run out of patience m_isWaitingBehindFriend = false; ResetStuckMonitor(); // repath to avoid clump of friends in the way DestroyPath(); } } else if (m_isWaitingBehindFriend) { // we're done waiting for our friend to move m_isWaitingBehindFriend = false; ResetStuckMonitor(); } // Move along our path if there are no friends blocking our way, // or we have run out of patience if (!m_isWaitingBehindFriend || m_politeTimer.IsElapsed()) { // Move along path MoveTowardsPosition(&adjustedGoal); // Stuck check if (m_isStuck && !IsJumping()) { Wiggle(); } } // if our goal is high above us, we must have fallen bool didFall = false; if (m_goalPosition.z - GetFeetZ() > JumpCrouchHeight) { const float closeRange = 75.0f; Vector2D to(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (to.IsLengthLessThan(closeRange)) { // we can't reach the goal position // check if we can reach the next node, in case this was a "jump down" situation if (m_pathIndex < m_pathLength - 1) { if (m_path[m_pathIndex + 1].pos.z - GetFeetZ() > JumpCrouchHeight) { // the next node is too high, too - we really did fall of the path didFall = true; } } else { // fell trying to get to the last node in the path didFall = true; } } } // This timeout check is needed if the bot somehow slips way off // of its path and cannot progress, but also moves around // enough that it never becomes "stuck" const float giveUpDuration = 5.0f; // 4.0f if (didFall || gpGlobals->time - m_areaEnteredTimestamp > giveUpDuration) { if (didFall) { PrintIfWatched("I fell off!\n"); } // if we havent made any progress in a long time, give up if (m_pathIndex < m_pathLength - 1) { PrintIfWatched("Giving up trying to get to area #%d\n", m_path[m_pathIndex].area->GetID()); } else { PrintIfWatched("Giving up trying to get to end of path\n"); } Run(); StandUp(); DestroyPath(); return PATH_FAILURE; } return PROGRESSING; }