void NPC::MoveAction(void) { if ((g_waypoint->g_waypointPointFlag[m_currentWaypointIndex] & WAYPOINT_LADDER && GetDistance2D(pev->origin, g_waypoint->g_waypointPointOrigin[m_currentWaypointIndex]) <= 10.0f) || (m_oldNavIndex != -1 && g_waypoint->g_waypointPointFlag[m_oldNavIndex] & WAYPOINT_LADDER && GetDistance2D(pev->origin, g_waypoint->g_waypointPointOrigin[m_oldNavIndex]) <= 10.0f)) pev->movetype = MOVETYPE_FLY; else pev->movetype = MOVETYPE_PUSHSTEP; float oldSpeed = pev->speed; pev->speed = m_moveSpeed; if (m_moveSpeed == 0.0f || !IsAlive (GetEntity ())) { if (!IsOnLadder(GetEntity()) && pev->solid != SOLID_NOT) DROP_TO_FLOOR(GetEntity()); return; } if (IsOnLadder(GetEntity()) || pev->solid == SOLID_NOT) { pev->velocity = GetSpeedVector(pev->origin, m_destOrigin, pev->speed); if (pev->solid == SOLID_NOT) goto lastly; } else { Vector vecMove = m_destOrigin - pev->origin; Vector vecFwd, vecAng; VEC_TO_ANGLES(vecMove, vecAng); vecAng = Vector(0.0f, vecAng.y, 0.0f); UTIL_MakeVectorsPrivate(vecAng, vecFwd, null, null); pev->velocity.x = vecFwd.x * pev->speed; pev->velocity.y = vecFwd.y * pev->speed; } if (m_jumpAction) { pev->velocity.z = (270.0f * pev->gravity) + 32.0f; // client gravity 1 = 270.0f , and jump+duck + 32.0f m_jumpAction = false; } CheckStuck(oldSpeed); lastly: float speed = GetDistance2D(pev->velocity); if (speed > 10.0f || speed < -10.0f) g_npcAS |= ASC_MOVE; MakeVectors(pev->angles); }
void CCSBot::MoveTowardsPosition(const Vector *pos) { // Jump up on ledges // Because we may not be able to get to our goal position and enter the next // area because our extent collides with a nearby vertical ledge, make sure // we look far enough ahead to avoid this situation. // Can't look too far ahead, or bots will try to jump up slopes. // NOTE: We need to do this frequently to catch edges at the right time // TODO: Look ahead *along path* instead of straight line if ((m_lastKnownArea == NULL || !(m_lastKnownArea->GetAttributes() & NAV_NO_JUMP)) && !IsOnLadder() && !m_isJumpCrouching) { float ground; Vector aheadRay(pos->x - pev->origin.x, pos->y - pev->origin.y, 0); aheadRay.NormalizeInPlace(); // look far ahead to allow us to smoothly jump over gaps, ledges, etc // only jump if ground is flat at lookahead spot to avoid jumping up slopes bool jumped = false; if (IsRunning()) { const float farLookAheadRange = 80.0f; Vector normal; Vector stepAhead = pev->origin + farLookAheadRange * aheadRay; stepAhead.z += HalfHumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground, &normal)) { if (normal.z > 0.9f) jumped = DiscontinuityJump(ground, ONLY_JUMP_DOWN); } } if (!jumped) { // close up jumping // cant be less or will miss jumps over low walls const float lookAheadRange = 30.0f; Vector stepAhead = pev->origin + lookAheadRange * aheadRay; stepAhead.z += HalfHumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground)) { jumped = DiscontinuityJump(ground); } } if (!jumped) { // about to fall gap-jumping const float lookAheadRange = 10.0f; Vector stepAhead = pev->origin + lookAheadRange * aheadRay; stepAhead.z += HalfHumanHeight; if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground)) { jumped = DiscontinuityJump(ground, ONLY_JUMP_DOWN, MUST_JUMP); } } } // compute our current forward and lateral vectors float angle = pev->v_angle.y; Vector2D dir(BotCOS(angle), BotSIN(angle)); Vector2D lat(-dir.y, dir.x); // compute unit vector to goal position Vector2D to(pos->x - pev->origin.x, pos->y - pev->origin.y); to.NormalizeInPlace(); // move towards the position independant of our view direction float toProj = to.x * dir.x + to.y * dir.y; float latProj = to.x * lat.x + to.y * lat.y; const float c = 0.25f; if (toProj > c) MoveForward(); else if (toProj < -c) MoveBackward(); // if we are avoiding someone via strafing, don't override if (m_avoid != NULL) return; if (latProj >= c) StrafeLeft(); else if (latProj <= -c) StrafeRight(); }
void Bot::CombatFight (void) { // no enemy? no need to do strafing if (FNullEnt (m_enemy)) return; Vector enemyOrigin = m_lookAt; if (m_currentWeapon == WEAPON_KNIFE) m_destOrigin = m_enemy->v.origin; enemyOrigin = (enemyOrigin - EyePosition ()).SkipZ (); // ignore z component (up & down) float distance = enemyOrigin.GetLength (); // how far away is the enemy scum? if (m_timeWaypointMove + m_frameInterval < engine->GetTime ()) { if (m_currentWeapon == WEAPON_KNIFE) return; int approach; if ((m_states & STATE_SUSPECTENEMY) && !(m_states & STATE_SEEINGENEMY)) // if suspecting enemy stand still approach = 49; else if (m_isReloading || m_isVIP) // if reloading or vip back off approach = 29; else if (m_currentWeapon == WEAPON_KNIFE) // knife? approach = 100; else { approach = static_cast <int> (pev->health * m_agressionLevel); if (UsesSniper () && (approach > 49)) approach = 49; } // only take cover when bomb is not planted and enemy can see the bot or the bot is VIP if (approach < 30 && !g_bombPlanted && (::IsInViewCone (pev->origin, m_enemy) || m_isVIP)) { m_moveSpeed = -pev->maxspeed; GetCurrentTask ()->taskID = TASK_SEEKCOVER; GetCurrentTask ()->canContinue = true; GetCurrentTask ()->desire = TASKPRI_FIGHTENEMY + 1.0f; } else if (approach < 50) m_moveSpeed = 0.0f; else m_moveSpeed = pev->maxspeed; if (distance < 96 && m_currentWeapon != WEAPON_KNIFE) m_moveSpeed = -pev->maxspeed; if (UsesSniper ()) { m_fightStyle = 1; m_lastFightStyleCheck = engine->GetTime (); } else if (UsesRifle () || UsesSubmachineGun ()) { if (m_lastFightStyleCheck + 3.0f < engine->GetTime ()) { int rand = engine->RandomInt (1, 100); if (distance < 450) m_fightStyle = 0; else if (distance < 1024) { if (rand < (UsesSubmachineGun () ? 50 : 30)) m_fightStyle = 0; else m_fightStyle = 1; } else { if (rand < (UsesSubmachineGun () ? 80 : 93)) m_fightStyle = 1; else m_fightStyle = 0; } m_lastFightStyleCheck = engine->GetTime (); } } else { if (m_lastFightStyleCheck + 3.0f < engine->GetTime ()) { if (engine->RandomInt (0, 100) < 65) m_fightStyle = 1; else m_fightStyle = 0; m_lastFightStyleCheck = engine->GetTime (); } } if ((m_skill > 50 && m_fightStyle == 0) || ((pev->button & IN_RELOAD) || m_isReloading) || (UsesPistol () && distance < 500.0f)) { if (m_strafeSetTime < engine->GetTime ()) { // to start strafing, we have to first figure out if the target is on the left side or right side MakeVectors (m_enemy->v.v_angle); Vector dirToPoint = (pev->origin - m_enemy->v.origin).Normalize2D (); Vector rightSide = g_pGlobals->v_right.Normalize2D (); if ((dirToPoint | rightSide) < 0) m_combatStrafeDir = 1; else m_combatStrafeDir = 0; if (engine->RandomInt (1, 100) < 30) m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime () + engine->RandomFloat (0.5, 2.5); } if (m_combatStrafeDir == 0) { if (!CheckWallOnLeft ()) m_strafeSpeed = -160.0f; else { m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime () + 0.7f; } } else { if (!CheckWallOnRight ()) m_strafeSpeed = 160.0f; else { m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime () + 1.0f; } } if (m_skill > 80 && (m_jumpTime + 5.0f < engine->GetTime () && IsOnFloor () && engine->RandomInt (0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.GetLength2D () > 150.0f)) pev->button |= IN_JUMP; if (m_moveSpeed != 0.0f && distance > 150.0f) m_moveSpeed = 0.0f; } else if (m_fightStyle == 1) { bool shouldDuck = true; // should duck // check the enemy height float enemyHalfHeight = ((m_enemy->v.flags & FL_DUCKING) == FL_DUCKING ? 36.0f : 72.0f) / 2; // check center/feet if (!IsVisible (m_enemy->v.origin, GetEntity ()) && !IsVisible (m_enemy->v.origin + Vector (0, 0, -enemyHalfHeight), GetEntity ())) shouldDuck = false; int nearestToEnemyPoint = g_waypoint->FindNearest (m_enemy->v.origin); if (shouldDuck && GetCurrentTask ()->taskID != TASK_SEEKCOVER && GetCurrentTask ()->taskID != TASK_HUNTENEMY && (m_visibility & VISIBILITY_BODY) && !(m_visibility & VISIBILITY_OTHER) && g_waypoint->IsDuckVisible (m_currentWaypointIndex, nearestToEnemyPoint)) m_duckTime = engine->GetTime () + m_frameInterval * 3.5f; m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; m_navTimeset = engine->GetTime (); } } if (m_duckTime > engine->GetTime ()) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; } if (m_moveSpeed != 0.0f) m_moveSpeed = GetWalkSpeed (); if (m_isReloading) { m_moveSpeed = -pev->maxspeed; m_duckTime = engine->GetTime () - (m_frameInterval * 4.0f); } if (!IsInWater () && !IsOnLadder () && (m_moveSpeed != 0 || m_strafeSpeed != 0)) { MakeVectors (pev->v_angle); if (IsDeadlyDrop (pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * m_frameInterval))) { m_strafeSpeed = -m_strafeSpeed; m_moveSpeed = -m_moveSpeed; pev->button &= ~IN_JUMP; } } }
// Navigate our current ladder. Return true if we are doing ladder navigation. // TODO: Need Push() and Pop() for run/walk context to keep ladder speed contained. bool CCSBot::UpdateLadderMovement() { if (!m_pathLadder) return false; bool giveUp = false; // check for timeout const float ladderTimeoutDuration = 10.0f; if (gpGlobals->time - m_pathLadderTimestamp > ladderTimeoutDuration) { PrintIfWatched("Ladder timeout!\n"); giveUp = true; } else if (m_pathLadderState == APPROACH_ASCENDING_LADDER || m_pathLadderState == APPROACH_DESCENDING_LADDER || m_pathLadderState == ASCEND_LADDER || m_pathLadderState == DESCEND_LADDER || m_pathLadderState == DISMOUNT_ASCENDING_LADDER || m_pathLadderState == MOVE_TO_DESTINATION) { if (m_isStuck) { PrintIfWatched("Giving up ladder - stuck\n"); giveUp = true; } } if (giveUp) { // jump off ladder and give up Jump(MUST_JUMP); Wiggle(); ResetStuckMonitor(); DestroyPath(); Run(); return false; } ResetStuckMonitor(); // check if somehow we totally missed the ladder switch (m_pathLadderState) { case MOUNT_ASCENDING_LADDER: case MOUNT_DESCENDING_LADDER: case ASCEND_LADDER: case DESCEND_LADDER: { const float farAway = 200.0f; Vector2D d = (m_pathLadder->m_top - pev->origin).Make2D(); if (d.IsLengthGreaterThan(farAway)) { PrintIfWatched("Missed ladder\n"); Jump(MUST_JUMP); DestroyPath(); Run(); return false; } break; } } m_areaEnteredTimestamp = gpGlobals->time; const float tolerance = 10.0f; const float closeToGoal = 25.0f; switch (m_pathLadderState) { case APPROACH_ASCENDING_LADDER: { bool approached = false; Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y < 0.0f) { Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x); #ifdef REGAMEDLL_FIXES if (Q_abs(d.x * perp.x + d.y * perp.y) < tolerance && d.Length() < closeToGoal) #else if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal) #endif approached = true; } // small radius will just slow them down a little for more accuracy in hitting their spot const float walkRange = 50.0f; if (d.IsLengthLessThan(walkRange)) { Walk(); StandUp(); } // TODO: Check that we are on the ladder we think we are if (IsOnLadder()) { m_pathLadderState = ASCEND_LADDER; PrintIfWatched("ASCEND_LADDER\n"); // find actual top in case m_pathLadder penetrates the ceiling ComputeLadderEndpoint(true); } else if (approached) { // face the m_pathLadder m_pathLadderState = FACE_ASCENDING_LADDER; PrintIfWatched("FACE_ASCENDING_LADDER\n"); } else { // move toward ladder mount point MoveTowardsPosition(&m_goalPosition); } break; } case APPROACH_DESCENDING_LADDER: { // fall check if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) { PrintIfWatched("Fell from ladder.\n"); m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } else { bool approached = false; Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y > 0.0f) { Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x); if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal) approached = true; } // if approaching ladder from the side or "ahead", walk if (m_pathLadder->m_topBehindArea != m_lastKnownArea) { const float walkRange = 150.0f; if (!IsCrouching() && d.IsLengthLessThan(walkRange)) Walk(); } // TODO: Check that we are on the ladder we think we are if (IsOnLadder()) { // we slipped onto the ladder - climb it m_pathLadderState = DESCEND_LADDER; Run(); PrintIfWatched("DESCEND_LADDER\n"); // find actual bottom in case m_pathLadder penetrates the floor ComputeLadderEndpoint(false); } else if (approached) { // face the ladder m_pathLadderState = FACE_DESCENDING_LADDER; PrintIfWatched("FACE_DESCENDING_LADDER\n"); } else { // move toward ladder mount point MoveTowardsPosition(&m_goalPosition); } } break; } case FACE_ASCENDING_LADDER: { // find yaw to directly aim at ladder Vector to = m_pathLadder->m_bottom - pev->origin; Vector idealAngle = UTIL_VecToAngles(to); const float angleTolerance = 5.0f; if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance)) { // move toward ladder until we become "on" it Run(); ResetStuckMonitor(); m_pathLadderState = MOUNT_ASCENDING_LADDER; PrintIfWatched("MOUNT_ASCENDING_LADDER\n"); } break; } case FACE_DESCENDING_LADDER: { // find yaw to directly aim at ladder Vector to = m_pathLadder->m_top - pev->origin; Vector idealAngle = UTIL_VecToAngles(to); const float angleTolerance = 5.0f; if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance)) { // move toward ladder until we become "on" it m_pathLadderState = MOUNT_DESCENDING_LADDER; ResetStuckMonitor(); PrintIfWatched("MOUNT_DESCENDING_LADDER\n"); } break; } case MOUNT_ASCENDING_LADDER: { if (IsOnLadder()) { m_pathLadderState = ASCEND_LADDER; PrintIfWatched("ASCEND_LADDER\n"); // find actual top in case m_pathLadder penetrates the ceiling ComputeLadderEndpoint(true); } MoveForward(); break; } case MOUNT_DESCENDING_LADDER: { // fall check if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) { PrintIfWatched("Fell from ladder.\n"); m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } else { if (IsOnLadder()) { m_pathLadderState = DESCEND_LADDER; PrintIfWatched("DESCEND_LADDER\n"); // find actual bottom in case m_pathLadder penetrates the floor ComputeLadderEndpoint(false); } // move toward ladder mount point MoveForward(); } break; } case ASCEND_LADDER: { // run, so we can make our dismount jump to the side, if necessary Run(); // if our destination area requires us to crouch, do it if (m_path[m_pathIndex].area->GetAttributes() & NAV_CROUCH) Crouch(); // did we reach the top? if (GetFeetZ() >= m_pathLadderEnd) { // we reached the top - dismount m_pathLadderState = DISMOUNT_ASCENDING_LADDER; PrintIfWatched("DISMOUNT_ASCENDING_LADDER\n"); if (m_path[m_pathIndex].area == m_pathLadder->m_topForwardArea) m_pathLadderDismountDir = FORWARD; else if (m_path[m_pathIndex].area == m_pathLadder->m_topLeftArea) m_pathLadderDismountDir = LEFT; else if (m_path[m_pathIndex].area == m_pathLadder->m_topRightArea) m_pathLadderDismountDir = RIGHT; m_pathLadderDismountTimestamp = gpGlobals->time; } else if (!IsOnLadder()) { // we fall off the ladder, repath DestroyPath(); return false; } // move up ladder MoveForward(); break; } case DESCEND_LADDER: { Run(); float destHeight = m_pathLadderEnd + HalfHumanHeight; if (!IsOnLadder() || GetFeetZ() <= destHeight) { // we reached the bottom, or we fell off - dismount m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); PrintIfWatched("MOVE_TO_DESTINATION\n"); } // Move down ladder MoveForward(); break; } case DISMOUNT_ASCENDING_LADDER: { if (gpGlobals->time - m_pathLadderDismountTimestamp >= 0.4f) { m_pathLadderState = MOVE_TO_DESTINATION; m_path[m_pathIndex].area->GetClosestPointOnArea(&pev->origin, &m_goalPosition); PrintIfWatched("MOVE_TO_DESTINATION\n"); } // We should already be facing the dismount point if (m_pathLadderFaceIn) { switch (m_pathLadderDismountDir) { case LEFT: StrafeLeft(); break; case RIGHT: StrafeRight(); break; case FORWARD: MoveForward(); break; } } else { switch (m_pathLadderDismountDir) { case LEFT: StrafeRight(); break; case RIGHT: StrafeLeft(); break; case FORWARD: MoveBackward(); break; } } break; } case MOVE_TO_DESTINATION: { if (m_path[m_pathIndex].area->Contains(&pev->origin)) { // successfully traversed ladder and reached destination area // exit ladder state machine PrintIfWatched("Ladder traversed.\n"); m_pathLadder = nullptr; // incrememnt path index to next step beyond this ladder SetPathIndex(m_pathIndex + 1); return false; } MoveTowardsPosition(&m_goalPosition); break; } } return true; }
// 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; }
void CHostage::DoFollow(void) { if (m_hTargetEnt == NULL) return; CBaseEntity *pFollowing; Vector vecDest; float flRadius; int nindexPath; pFollowing = GetClassPtr((CBaseEntity *)m_hTargetEnt->pev); m_LocalNav->SetTargetEnt(pFollowing); vecDest = pFollowing->pev->origin; vecDest.z += pFollowing->pev->mins.z; flRadius = (vecDest - pev->origin).Length(); if (flRadius < 80) { if (m_fHasPath) return; if (m_LocalNav->PathTraversable(pev->origin, vecDest, TRUE) != TRAVERSABLE_NO) return; } if (FBitSet(pev->flags, FL_ONGROUND)) { if (gpGlobals->time > m_flLastPathCheck + m_flPathCheckInterval) { if (!m_fHasPath || pFollowing->pev->velocity.Length2D() > 1) { m_flLastPathCheck = gpGlobals->time; m_LocalNav->RequestNav(this); } } } if (m_fHasPath) { nTargetNode = nindexPath = m_LocalNav->GetFurthestTraversableNode(pev->origin, vecNodes, m_nPathNodes, TRUE); if (!nindexPath) { if ((vecNodes[nTargetNode] - pev->origin).Length2D() < HOSTAGE_STEPSIZE) nTargetNode = -1; } if (nTargetNode == -1) { m_fHasPath = FALSE; m_flPathCheckInterval = 0.1; } } if (gpGlobals->time < m_flFlinchTime) return; if (nTargetNode != -1) { if (FBitSet(pev->flags, FL_ONGROUND)) PointAt(vecNodes[nTargetNode]); if (IsOnLadder()) pev->v_angle.x = -60; MoveToward(vecNodes[nTargetNode]); m_bStuck = FALSE; } if (pev->takedamage == DAMAGE_YES) { if (m_improv) { } if (m_hTargetEnt != NULL && m_State == FOLLOW) { if (!m_bStuck) { if (flRadius > 200) { m_bStuck = TRUE; m_flStuckTime = gpGlobals->time; } } } } if (FBitSet(pev->flags, FL_ONGROUND)) { if (m_flPathAcquired != -1 && m_flPathAcquired + 2 > gpGlobals->time) { if (pev->velocity.Length2D() < 1 || nTargetNode == -1) Wiggle(); } } }
void NPC::CheckStuck(float oldSpeed) { if (!IsOnFloor(GetEntity()) || IsOnLadder (GetEntity ())) return; if (WalkMove()) return; if (m_checkStuckTime > gpGlobals->time) return; m_checkStuckTime = gpGlobals->time + 0.5f; bool isStuck = false; float moveDistance = GetDistance(pev->origin, m_prevOrigin); if (oldSpeed >= 10.0f && pev->speed >= 10.0f) { if (moveDistance <= 2.0f) isStuck = true; } if (isStuck) { MakeVectors(pev->angles); TraceResult tr; Vector dest = pev->origin; dest.z += 32; Vector src = pev->origin + gpGlobals->v_forward * 32; UTIL_TraceHull(dest, src, dont_ignore_monsters, head_hull, GetEntity(), &tr); if (tr.flFraction > 0.0f && tr.flFraction != 1.0f) { float newOriginZ = pev->origin.z + (tr.vecEndPos.z - GetBottomOrigin(GetEntity ()).z) - 32; if (newOriginZ > pev->origin.z && (newOriginZ - pev->origin.z) <= 32) { pev->velocity.z = (270.0f * pev->gravity) + 32.0f; m_jumpAction = false; goto end; } } for (int i = 0; i <= 18; i++) { UTIL_TraceHull(pev->origin, pev->origin, dont_ignore_monsters, human_hull, GetEntity(), &tr); if (!tr.fStartSolid && !tr.fAllSolid && tr.fInOpen) break; if (i == 17) { int block = MF_ExecuteForward(g_callStuck_Pre, (cell)ENTINDEX(GetEntity())); if (!block) { LogToFile("The NPC is stuck, remove now"); m_needRemove = true; } break; } pev->origin.z += 1; } m_goalWaypoint = -1; m_currentWaypointIndex = -1; DeleteSearchNodes(); } end: m_prevOrigin = pev->origin; }
// SyPB Pro P.30 - Attack Ai void Bot::CombatFight(void) { if (FNullEnt(m_enemy)) return; //if (m_currentWeapon == WEAPON_KNIFE) // SyPB Pro P.34 - Base Ai m_destOrigin = GetEntityOrigin(m_enemy); // SyPB Pro P.30 - Zombie Mod if (GetGameMod() == 2 || GetGameMod () == 4) // SyPB Pro P.37 - small change { m_currentWaypointIndex = -1; m_prevGoalIndex = -1; m_moveToGoal = false; m_navTimeset = engine->GetTime(); if (IsZombieBot(GetEntity())) { m_moveSpeed = pev->maxspeed; return; } } Vector enemyOrigin = GetEntityOrigin(m_enemy); float distance = (pev->origin - enemyOrigin).GetLength(); if (m_timeWaypointMove + m_frameInterval < engine->GetTime()) { if (GetGameMod() == 2) { float baseDistance = 600.0f; if (::IsInViewCone(pev->origin, m_enemy) && GetNearbyEnemiesNearPosition (GetEntityOrigin (m_enemy), 300) < 3) // SyPB Pro P.37 - Zombie Mode Attack Ai { if (m_currentWeapon == WEAPON_KNIFE) baseDistance = 450.0f; else baseDistance = 400.0f; } else { if (m_currentWeapon == WEAPON_KNIFE) baseDistance = -1.0f; else baseDistance = 300.0f; } if (baseDistance != -1.0f) { int fdPlayer = GetNearbyFriendsNearPosition(pev->origin, (baseDistance > 0.0f) ? int(baseDistance) / 2 : 300); int enPlayer = GetNearbyEnemiesNearPosition(enemyOrigin, (baseDistance > 0.0f) ? int(baseDistance) : 400); baseDistance -= (fdPlayer * 10.0f); baseDistance += (enPlayer * 20.0f); if (baseDistance <= 0.0f) baseDistance = 50.0f; } if (baseDistance < 0.0f) m_moveSpeed = pev->maxspeed; else { if (distance <= baseDistance) m_moveSpeed = -pev->maxspeed; else if (distance >= (baseDistance + 100.0f)) m_moveSpeed = 0.0f; } if (m_moveSpeed > 0.0f) { if (baseDistance != -1.0f) { if (distance <= 100) m_moveSpeed = -pev->maxspeed; else m_moveSpeed = 0.0f; } } } else if (GetGameMod() == 1) { if (m_currentWeapon == WEAPON_KNIFE) m_moveSpeed = pev->maxspeed; else m_moveSpeed = 0.0f; } else { int approach; if ((m_states & STATE_SUSPECTENEMY) && !(m_states & STATE_SEEINGENEMY)) // if suspecting enemy stand still approach = 49; else if (m_isReloading || m_isVIP) // if reloading or vip back off approach = 29; else if (m_currentWeapon == WEAPON_KNIFE) // knife? approach = 100; else { approach = static_cast <int> (pev->health * m_agressionLevel); if (UsesSniper() && (approach > 49)) approach = 49; // SyPB Pro P.35 - Base mode Weapon Ai Improve if (UsesSubmachineGun()) approach += 20; } // only take cover when bomb is not planted and enemy can see the bot or the bot is VIP if (approach < 30 && !g_bombPlanted && (::IsInViewCone(pev->origin, m_enemy) && !UsesSniper () || m_isVIP)) { m_moveSpeed = -pev->maxspeed; GetCurrentTask()->taskID = TASK_SEEKCOVER; GetCurrentTask()->canContinue = true; GetCurrentTask()->desire = TASKPRI_FIGHTENEMY + 1.0f; } else if (approach < 50) m_moveSpeed = 0.0f; else m_moveSpeed = pev->maxspeed; // SyPB Pro P.35 - Base mode Weapon Ai Improve if (distance < 96 && m_currentWeapon != WEAPON_KNIFE) { pev->button |= IN_DUCK; m_moveSpeed = -pev->maxspeed; } } if (UsesSniper()) { m_fightStyle = 1; m_lastFightStyleCheck = engine->GetTime(); } else if (UsesRifle() || UsesSubmachineGun()) { if (m_lastFightStyleCheck + 3.0f < engine->GetTime()) { int rand = engine->RandomInt(1, 100); if (distance < 450) m_fightStyle = 0; else if (distance < 1024) { if (rand < (UsesSubmachineGun() ? 50 : 30)) m_fightStyle = 0; else m_fightStyle = 1; } else { if (rand < (UsesSubmachineGun() ? 80 : 93)) m_fightStyle = 1; else m_fightStyle = 0; } m_lastFightStyleCheck = engine->GetTime(); } } else { if (m_lastFightStyleCheck + 3.0f < engine->GetTime()) { if (engine->RandomInt(0, 100) < 65) m_fightStyle = 1; else m_fightStyle = 0; m_lastFightStyleCheck = engine->GetTime(); } } if (((pev->button & IN_RELOAD) || (m_isReloading) || (m_skill >= 70 && m_fightStyle && distance < 800.0f)) && GetGameMod () != 2 && GetGameMod () != 4) { if (m_strafeSetTime < engine->GetTime()) { // to start strafing, we have to first figure out if the target is on the left side or right side MakeVectors(m_enemy->v.v_angle); Vector dirToPoint = (pev->origin - GetEntityOrigin(m_enemy)).Normalize2D(); Vector rightSide = g_pGlobals->v_right.Normalize2D(); if ((dirToPoint | rightSide) < 0) m_combatStrafeDir = 1; else m_combatStrafeDir = 0; if (engine->RandomInt(1, 100) < 30) m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime() + engine->RandomFloat(0.5, 2.5); } if (m_combatStrafeDir == 0) { if (!CheckWallOnLeft()) m_strafeSpeed = -160.0f; else { m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime() + 0.7f; } } else { if (!CheckWallOnRight()) m_strafeSpeed = 160.0f; else { m_combatStrafeDir ^= 1; m_strafeSetTime = engine->GetTime() + 1.0f; } } if (m_skill > 80 && (m_jumpTime + 5.0f < engine->GetTime() && IsOnFloor() && engine->RandomInt(0, 1000) < (m_isReloading ? 8 : 2) && pev->velocity.GetLength2D() > 150.0f)) pev->button |= IN_JUMP; } else if ((GetGameMod() != 2 && m_fightStyle) || (m_fightStyle && m_moveSpeed == 0.0f)) { bool shouldDuck = true; // should duck // check the enemy height float enemyHalfHeight = ((m_enemy->v.flags & FL_DUCKING) == FL_DUCKING ? 36.0f : 72.0f) / 2; // check center/feet if (!IsVisible(GetEntityOrigin(m_enemy), GetEntity()) && !IsVisible(GetEntityOrigin(m_enemy) + Vector(0, 0, -enemyHalfHeight), GetEntity())) shouldDuck = false; int nearestToEnemyPoint = g_waypoint->FindNearest(GetEntityOrigin(m_enemy)); if (shouldDuck && GetCurrentTask()->taskID != TASK_SEEKCOVER && GetCurrentTask()->taskID != TASK_HUNTENEMY && (m_visibility & VISIBILITY_BODY) && !(m_visibility & VISIBILITY_OTHER) && g_waypoint->IsDuckVisible(m_currentWaypointIndex, nearestToEnemyPoint)) m_duckTime = engine->GetTime() + m_frameInterval * 3.5f; m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; m_navTimeset = engine->GetTime(); } else m_strafeSpeed = 0.0f; } if (m_duckTime > engine->GetTime()) { m_moveSpeed = 0.0f; m_strafeSpeed = 0.0f; } if (GetGameMod() == 2) return; if (m_moveSpeed != 0.0f) m_moveSpeed = GetWalkSpeed(); if (m_isReloading) { m_moveSpeed = -pev->maxspeed; m_duckTime = engine->GetTime() - (m_frameInterval * 4.0f); } if (!IsInWater() && !IsOnLadder()) { if (m_moveSpeed != 0 || m_strafeSpeed != 0) { if (IsDeadlyDrop(pev->origin + (g_pGlobals->v_forward * m_moveSpeed * 0.2f) + (g_pGlobals->v_right * m_strafeSpeed * 0.2f) + (pev->velocity * m_frameInterval))) { m_strafeSpeed = -m_strafeSpeed; m_moveSpeed = -m_moveSpeed; pev->button &= ~IN_JUMP; } } } }