int CLocalNav::PathTraversable(Vector &vecSource, Vector &vecDest, BOOL fNoMonsters) { TraceResult tr; Vector vecSrcTmp; Vector vecDestTmp; Vector vecDir; float flTotal; int retval; retval = TRAVERSABLE_NO; vecSrcTmp = vecSource; vecDestTmp = vecDest - vecSource; vecDir = vecDestTmp.Normalize(); vecDir.z = 0; flTotal = vecDestTmp.Length2D(); while (flTotal > 1) { if (flTotal >= s_flStepSize) vecDestTmp = vecSrcTmp + (vecDir * s_flStepSize); else vecDestTmp = vecDest; m_fTargetEntHit = FALSE; if (PathClear(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { vecDestTmp = tr.vecEndPos; if (retval == TRAVERSABLE_NO) retval = TRAVERSABLE_SLOPE; } else { if (tr.fStartSolid) return TRAVERSABLE_NO; if (tr.pHit && !fNoMonsters && !FStringNull(tr.pHit->v.classname) && FClassnameIs(tr.pHit, "hostage_entity")) return TRAVERSABLE_NO; vecSrcTmp = tr.vecEndPos; if (tr.vecPlaneNormal.z > 0.7) { if (SlopeTraversable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { if (retval == TRAVERSABLE_NO) retval = TRAVERSABLE_SLOPE; } else return TRAVERSABLE_NO; } else { if (StepTraversable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { if (retval == TRAVERSABLE_NO) retval = TRAVERSABLE_STEP; } else if (StepJumpable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { if (retval == TRAVERSABLE_NO) retval = TRAVERSABLE_STEPJUMPABLE; } else return TRAVERSABLE_NO; } } if (PathClear(vecDestTmp, vecDestTmp - Vector(0, 0, 300), fNoMonsters, tr)) return TRAVERSABLE_NO; if (!tr.fStartSolid) vecDestTmp = tr.vecEndPos; vecSrcTmp = vecDestTmp; if (!m_fTargetEntHit) flTotal = (vecDest - vecDestTmp).Length2D(); else flTotal = 0; } vecDest = vecDestTmp; return retval; }
//----------------------------------------------------------------------------- // Client-side obstacle avoidance //----------------------------------------------------------------------------- void C_BaseHLPlayer::PerformClientSideObstacleAvoidance( float flFrameTime, CUserCmd *pCmd ) { // Don't avoid if noclipping or in movetype none switch ( GetMoveType() ) { case MOVETYPE_NOCLIP: case MOVETYPE_NONE: case MOVETYPE_OBSERVER: return; default: break; } // Try to steer away from any objects/players we might interpenetrate Vector size = WorldAlignSize(); float radius = 0.7f * sqrt( size.x * size.x + size.y * size.y ); float curspeed = GetLocalVelocity().Length2D(); //int slot = 1; //engine->Con_NPrintf( slot++, "speed %f\n", curspeed ); //engine->Con_NPrintf( slot++, "radius %f\n", radius ); // If running, use a larger radius float factor = 1.0f; if ( curspeed > 150.0f ) { curspeed = MIN( 2048.0f, curspeed ); factor = ( 1.0f + ( curspeed - 150.0f ) / 150.0f ); //engine->Con_NPrintf( slot++, "scaleup (%f) to radius %f\n", factor, radius * factor ); radius = radius * factor; } Vector currentdir; Vector rightdir; QAngle vAngles = pCmd->viewangles; vAngles.x = 0; AngleVectors( vAngles, ¤tdir, &rightdir, NULL ); bool istryingtomove = false; bool ismovingforward = false; if ( fabs( pCmd->forwardmove ) > 0.0f || fabs( pCmd->sidemove ) > 0.0f ) { istryingtomove = true; if ( pCmd->forwardmove > 1.0f ) { ismovingforward = true; } } if ( istryingtomove == true ) radius *= 1.3f; CPlayerAndObjectEnumerator avoid( radius ); partition->EnumerateElementsInSphere( PARTITION_CLIENT_SOLID_EDICTS, GetAbsOrigin(), radius, false, &avoid ); // Okay, decide how to avoid if there's anything close by int c = avoid.GetObjectCount(); if ( c <= 0 ) return; //engine->Con_NPrintf( slot++, "moving %s forward %s\n", istryingtomove ? "true" : "false", ismovingforward ? "true" : "false" ); float adjustforwardmove = 0.0f; float adjustsidemove = 0.0f; for ( int i = 0; i < c; i++ ) { C_AI_BaseNPC *obj = dynamic_cast< C_AI_BaseNPC *>(avoid.GetObject( i )); if( !obj ) continue; Vector vecToObject = obj->GetAbsOrigin() - GetAbsOrigin(); float flDist = vecToObject.Length2D(); // Figure out a 2D radius for the object Vector vecWorldMins, vecWorldMaxs; obj->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs ); Vector objSize = vecWorldMaxs - vecWorldMins; float objectradius = 0.5f * sqrt( objSize.x * objSize.x + objSize.y * objSize.y ); //Don't run this code if the NPC is not moving UNLESS we are in stuck inside of them. if ( !obj->IsMoving() && flDist > objectradius ) continue; if ( flDist > objectradius && obj->IsEffectActive( EF_NODRAW ) ) { obj->RemoveEffects( EF_NODRAW ); } Vector vecNPCVelocity; obj->EstimateAbsVelocity( vecNPCVelocity ); float flNPCSpeed = VectorNormalize( vecNPCVelocity ); Vector vPlayerVel = GetAbsVelocity(); VectorNormalize( vPlayerVel ); float flHit1, flHit2; Vector vRayDir = vecToObject; VectorNormalize( vRayDir ); float flVelProduct = DotProduct( vecNPCVelocity, vPlayerVel ); float flDirProduct = DotProduct( vRayDir, vPlayerVel ); if ( !IntersectInfiniteRayWithSphere( GetAbsOrigin(), vRayDir, obj->GetAbsOrigin(), radius, &flHit1, &flHit2 ) ) continue; Vector dirToObject = -vecToObject; VectorNormalize( dirToObject ); float fwd = 0; float rt = 0; float sidescale = 2.0f; float forwardscale = 1.0f; bool foundResult = false; Vector vMoveDir = vecNPCVelocity; if ( flNPCSpeed > 0.001f ) { // This NPC is moving. First try deflecting the player left or right relative to the NPC's velocity. // Start with whatever side they're on relative to the NPC's velocity. Vector vecNPCTrajectoryRight = CrossProduct( vecNPCVelocity, Vector( 0, 0, 1) ); int iDirection = ( vecNPCTrajectoryRight.Dot( dirToObject ) > 0 ) ? 1 : -1; for ( int nTries = 0; nTries < 2; nTries++ ) { Vector vecTryMove = vecNPCTrajectoryRight * iDirection; VectorNormalize( vecTryMove ); Vector vTestPosition = GetAbsOrigin() + vecTryMove * radius * 2; if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) ) { fwd = currentdir.Dot( vecTryMove ); rt = rightdir.Dot( vecTryMove ); //Msg( "PUSH DEFLECT fwd=%f, rt=%f\n", fwd, rt ); foundResult = true; break; } else { // Try the other direction. iDirection *= -1; } } } else { // the object isn't moving, so try moving opposite the way it's facing Vector vecNPCForward; obj->GetVectors( &vecNPCForward, NULL, NULL ); Vector vTestPosition = GetAbsOrigin() - vecNPCForward * radius * 2; if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) ) { fwd = currentdir.Dot( -vecNPCForward ); rt = rightdir.Dot( -vecNPCForward ); if ( flDist < objectradius ) { obj->AddEffects( EF_NODRAW ); } //Msg( "PUSH AWAY FACE fwd=%f, rt=%f\n", fwd, rt ); foundResult = true; } } if ( !foundResult ) { // test if we can move in the direction the object is moving Vector vTestPosition = GetAbsOrigin() + vMoveDir * radius * 2; if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) ) { fwd = currentdir.Dot( vMoveDir ); rt = rightdir.Dot( vMoveDir ); if ( flDist < objectradius ) { obj->AddEffects( EF_NODRAW ); } //Msg( "PUSH ALONG fwd=%f, rt=%f\n", fwd, rt ); foundResult = true; } else { // try moving directly away from the object Vector vTestPosition = GetAbsOrigin() - dirToObject * radius * 2; if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) ) { fwd = currentdir.Dot( -dirToObject ); rt = rightdir.Dot( -dirToObject ); foundResult = true; //Msg( "PUSH AWAY fwd=%f, rt=%f\n", fwd, rt ); } } } if ( !foundResult ) { // test if we can move through the object Vector vTestPosition = GetAbsOrigin() - vMoveDir * radius * 2; fwd = currentdir.Dot( -vMoveDir ); rt = rightdir.Dot( -vMoveDir ); if ( flDist < objectradius ) { obj->AddEffects( EF_NODRAW ); } //Msg( "PUSH THROUGH fwd=%f, rt=%f\n", fwd, rt ); foundResult = true; } // If running, then do a lot more sideways veer since we're not going to do anything to // forward velocity if ( istryingtomove ) { sidescale = 6.0f; } if ( flVelProduct > 0.0f && flDirProduct > 0.0f ) { sidescale = 0.1f; } float force = 1.0f; float forward = forwardscale * fwd * force * AVOID_SPEED; float side = sidescale * rt * force * AVOID_SPEED; adjustforwardmove += forward; adjustsidemove += side; } pCmd->forwardmove += adjustforwardmove; pCmd->sidemove += adjustsidemove; // Clamp the move to within legal limits, preserving direction. This is a little // complicated because we have different limits for forward, back, and side //Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove ); float flForwardScale = 1.0f; if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) ) { flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove; } else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) ) { flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove ); } float flSideScale = 1.0f; if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) ) { flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove ); } float flScale = MIN( flForwardScale, flSideScale ); pCmd->forwardmove *= flScale; pCmd->sidemove *= flScale; //Msg( "POSTCLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove ); }
/** * Compute a point a fixed distance ahead along our path. * Returns path index just after point. */ int CNavPathFollower::FindPathPoint( float aheadRange, Vector *point, int *prevIndex ) { // find path index just past aheadRange int afterIndex; // finds the closest point on local area of path, and returns the path index just prior to it Vector close; int startIndex = FindOurPositionOnPath( &close, true ); if (prevIndex) *prevIndex = startIndex; if (startIndex <= 0) { // went off the end of the path // or next point in path is unwalkable (ie: jump-down) // keep same point return m_segmentIndex; } // if we are crouching, just follow the path exactly if (m_improv->IsCrouching()) { // we want to move to the immediately next point along the path from where we are now int index = startIndex+1; if (index >= m_path->GetSegmentCount()) index = m_path->GetSegmentCount()-1; *point = (*m_path)[ index ]->pos; // if we are very close to the next point in the path, skip ahead to the next one to avoid wiggling // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc const float closeEpsilon = 20.0f; // 10 while ((*point - close).Make2D().IsLengthLessThan( closeEpsilon )) { ++index; if (index >= m_path->GetSegmentCount()) { index = m_path->GetSegmentCount()-1; break; } *point = (*m_path)[ index ]->pos; } return index; } // make sure we use a node a minimum distance ahead of us, to avoid wiggling while (startIndex < m_path->GetSegmentCount()-1) { Vector pos = (*m_path)[ startIndex+1 ]->pos; // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc const float closeEpsilon = 20.0f; if ((pos - close).Make2D().IsLengthLessThan( closeEpsilon )) { ++startIndex; } else { break; } } // if we hit a ladder or jump area, must stop (dont use ladder behind us) if (startIndex > m_segmentIndex && startIndex < m_path->GetSegmentCount() && ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_JUMP)) { *point = (*m_path)[ startIndex ]->pos; return startIndex; } // we need the point just *ahead* of us ++startIndex; if (startIndex >= m_path->GetSegmentCount()) startIndex = m_path->GetSegmentCount()-1; // if we hit a ladder or jump area, must stop if (startIndex < m_path->GetSegmentCount() && ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_JUMP)) { *point = (*m_path)[ startIndex ]->pos; return startIndex; } // note direction of path segment we are standing on Vector initDir = (*m_path)[ startIndex ]->pos - (*m_path)[ startIndex-1 ]->pos; initDir.NormalizeInPlace(); Vector feet = m_improv->GetFeet(); Vector eyes = m_improv->GetEyes(); float rangeSoFar = 0; // this flag is true if our ahead point is visible bool visible = true; Vector prevDir = initDir; // step along the path until we pass aheadRange bool isCorner = false; int i; for( i=startIndex; i<m_path->GetSegmentCount(); ++i ) { Vector pos = (*m_path)[i]->pos; Vector to = pos - (*m_path)[i-1]->pos; Vector dir = to.Normalize(); // don't allow path to double-back from our starting direction (going upstairs, down curved passages, etc) if (DotProduct( dir, initDir ) < 0.0f) // -0.25f { --i; break; } // if the path turns a corner, we want to move towards the corner, not into the wall/stairs/etc if (DotProduct( dir, prevDir ) < 0.5f) { isCorner = true; --i; break; } prevDir = dir; // don't use points we cant see Vector probe = pos + Vector( 0, 0, HalfHumanHeight ); if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES )) { // presumably, the previous point is visible, so we will interpolate visible = false; break; } // if we encounter a ladder or jump area, we must stop if (i < m_path->GetSegmentCount() && ((*m_path)[ i ]->ladder || (*m_path)[ i ]->area->GetAttributes() & NAV_JUMP)) break; // Check straight-line path from our current position to this position // Test for un-jumpable height change, or unrecoverable fall //if (!IsStraightLinePathWalkable( &pos )) //{ // --i; // break; //} Vector along = (i == startIndex) ? (pos - feet) : (pos - (*m_path)[i-1]->pos); rangeSoFar += along.Length2D(); // stop if we have gone farther than aheadRange if (rangeSoFar >= aheadRange) break; } if (i < startIndex) afterIndex = startIndex; else if (i < m_path->GetSegmentCount()) afterIndex = i; else afterIndex = m_path->GetSegmentCount()-1; // compute point on the path at aheadRange if (afterIndex == 0) { *point = (*m_path)[0]->pos; } else { // interpolate point along path segment const Vector *afterPoint = &(*m_path)[ afterIndex ]->pos; const Vector *beforePoint = &(*m_path)[ afterIndex-1 ]->pos; Vector to = *afterPoint - *beforePoint; float length = to.Length2D(); float t = 1.0f - ((rangeSoFar - aheadRange) / length); if (t < 0.0f) t = 0.0f; else if (t > 1.0f) t = 1.0f; *point = *beforePoint + t * to; // if afterPoint wasn't visible, slide point backwards towards beforePoint until it is if (!visible) { const float sightStepSize = 25.0f; float dt = sightStepSize / length; Vector probe = *point + Vector( 0, 0, HalfHumanHeight ); while( t > 0.0f && !IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ) ) { t -= dt; *point = *beforePoint + t * to; } if (t <= 0.0f) *point = *beforePoint; } } // if position found is too close to us, or behind us, force it farther down the path so we don't stop and wiggle if (!isCorner) { const float epsilon = 50.0f; Vector2D toPoint; Vector2D centroid( m_improv->GetCentroid().x, m_improv->GetCentroid().y ); toPoint.x = point->x - centroid.x; toPoint.y = point->y - centroid.y; if (DotProduct( toPoint, initDir.Make2D() ) < 0.0f || toPoint.IsLengthLessThan( epsilon )) { int i; for( i=startIndex; i<m_path->GetSegmentCount(); ++i ) { toPoint.x = (*m_path)[i]->pos.x - centroid.x; toPoint.y = (*m_path)[i]->pos.y - centroid.y; if ((*m_path)[i]->ladder || (*m_path)[i]->area->GetAttributes() & NAV_JUMP || toPoint.IsLengthGreaterThan( epsilon )) { *point = (*m_path)[i]->pos; startIndex = i; break; } } if (i == m_path->GetSegmentCount()) { *point = m_path->GetEndpoint(); startIndex = m_path->GetSegmentCount()-1; } } } // m_pathIndex should always be the next point on the path, even if we're not moving directly towards it if (startIndex < m_path->GetSegmentCount()) return startIndex; return m_path->GetSegmentCount()-1; }
void CAI_BlendedMotor::BuildVelocityScript( const AILocalMoveGoal_t &move ) { int i; float a; float idealVelocity = GetIdealSpeed(); if (idealVelocity == 0) { idealVelocity = 50; } float idealAccel = GetIdealAccel(); if (idealAccel == 0) { idealAccel = 100; } AI_Movementscript_t script; // set current location as start of script script.vecLocation = GetAbsOrigin(); script.flMaxVelocity = GetCurSpeed(); m_scriptMove.AddToTail( script ); //------------------------- extern ConVar *npc_height_adjust; if (npc_height_adjust->GetBool() && move.bHasTraced && move.directTrace.flTotalDist != move.thinkTrace.flTotalDist) { float flDist = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D(); float flHeight = move.directTrace.vEndPosition.z - m_scriptMove[0].vecLocation.z; float flDelta; if (flDist > 0) { flDelta = flHeight / flDist; } else { flDelta = 0; } m_flPredictiveSpeedAdjust = 1.1 - fabs( flDelta ); m_flPredictiveSpeedAdjust = clamp( m_flPredictiveSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 ); /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { Msg("m_flPredictiveSpeedAdjust %.3f %.1f %.1f\n", m_flPredictiveSpeedAdjust, flHeight, flDist ); NDebugOverlay::Box( move.directTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0,255,255, 0, 0.12 ); } */ } if (npc_height_adjust->GetBool()) { float flDist = (move.thinkTrace.vEndPosition - m_vecPrevOrigin2).Length2D(); float flHeight = move.thinkTrace.vEndPosition.z - m_vecPrevOrigin2.z; float flDelta; if (flDist > 0) { flDelta = flHeight / flDist; } else { flDelta = 0; } float newSpeedAdjust = 1.1 - fabs( flDelta ); newSpeedAdjust = clamp( newSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 ); // debounce speed adjust if (newSpeedAdjust < m_flReactiveSpeedAdjust) { m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.2 + newSpeedAdjust * 0.8; } else { m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.5 + newSpeedAdjust * 0.5; } // filter through origins m_vecPrevOrigin2 = m_vecPrevOrigin1; m_vecPrevOrigin1 = GetAbsOrigin(); /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { NDebugOverlay::Box( m_vecPrevOrigin2, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 ); NDebugOverlay::Box( move.thinkTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 ); Msg("m_flReactiveSpeedAdjust %.3f %.1f %.1f\n", m_flReactiveSpeedAdjust, flHeight, flDist ); } */ } idealVelocity = idealVelocity * min( m_flReactiveSpeedAdjust, m_flPredictiveSpeedAdjust ); //------------------------- bool bAddedExpected = false; // add all waypoint locations and velocities AI_Waypoint_t *pCurWaypoint = GetNavigator()->GetPath()->GetCurWaypoint(); // there has to be at least one waypoint Assert( pCurWaypoint ); while (pCurWaypoint && (pCurWaypoint->NavType() == NAV_GROUND || pCurWaypoint->NavType() == NAV_FLY) /*&& flTotalDist / idealVelocity < 3.0*/) // limit lookahead to 3 seconds { script.Init(); AI_Waypoint_t *pNext = pCurWaypoint->GetNext(); if (ai_path_adjust_speed_on_immediate_turns->GetBool() && !bAddedExpected) { // hack in next expected immediate location for move script.vecLocation = GetAbsOrigin() + move.dir * move.curExpectedDist; bAddedExpected = true; pNext = pCurWaypoint; } else { script.vecLocation = pCurWaypoint->vecLocation; script.pWaypoint = pCurWaypoint; } //DevMsg("waypoint %.1f %.1f %.1f\n", script.vecLocation.x, script.vecLocation.y, script.vecLocation.z ); if (pNext) { switch( pNext->NavType()) { case NAV_GROUND: case NAV_FLY: { Vector d1 = pNext->vecLocation - script.vecLocation; Vector d2 = script.vecLocation - m_scriptMove[m_scriptMove.Count()-1].vecLocation; // remove very short, non terminal ground links // FIXME: is this safe? Maybe just check for co-located ground points? if (d1.Length2D() < 1.0) { /* if (m_scriptMove.Count() > 1) { int i = m_scriptMove.Count() - 1; m_scriptMove[i].vecLocation = pCurWaypoint->vecLocation; m_scriptMove[i].pWaypoint = pCurWaypoint; } */ pCurWaypoint = pNext; continue; } d1.z = 0; VectorNormalize( d1 ); d2.z = 0; VectorNormalize( d2 ); // figure velocity float dot = (DotProduct( d1, d2 ) + 0.2); if (dot > 0) { dot = clamp( dot, 0.0f, 1.0f ); script.flMaxVelocity = idealVelocity * dot; } else { script.flMaxVelocity = 0; } } break; case NAV_JUMP: // FIXME: information about what the jump should look like isn't stored in the waypoints // this'll need to call // GetMoveProbe()->MoveLimit( NAV_JUMP, GetLocalOrigin(), GetPath()->CurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace ); // to get how far/fast the jump will be, but this is also stateless, so it'd call it per frame. // So far it's not clear that the moveprobe doesn't also call this..... { float minJumpHeight = 0; float maxHorzVel = max( GetCurSpeed(), 100 ); float gravity = sv_gravity->GetFloat() * GetOuter()->GetGravity(); Vector vecApex; Vector rawJumpVel = GetMoveProbe()->CalcJumpLaunchVelocity(script.vecLocation, pNext->vecLocation, gravity, &minJumpHeight, maxHorzVel, &vecApex ); script.flMaxVelocity = rawJumpVel.Length2D(); // Msg("%.1f\n", script.flMaxVelocity ); } break; case NAV_CLIMB: { /* CAI_Node *pClimbNode = GetNavigator()->GetNetwork()->GetNode(pNext->iNodeID); check: pClimbNode->m_eNodeInfo bits_NODE_CLIMB_BOTTOM, bits_NODE_CLIMB_ON, bits_NODE_CLIMB_OFF_FORWARD, bits_NODE_CLIMB_OFF_LEFT, bits_NODE_CLIMB_OFF_RIGHT */ script.flMaxVelocity = 0; } break; /* case NAV_FLY: // FIXME: can there be a NAV_GROUND -> NAV_FLY transition? script.flMaxVelocity = 0; break; */ default: break; } } else { script.flMaxVelocity = GetNavigator()->GetArrivalSpeed(); // Assert( script.flMaxVelocity == 0 ); } m_scriptMove.AddToTail( script ); pCurWaypoint = pNext; } //------------------------- // update distances float flTotalDist = 0; for (i = 0; i < m_scriptMove.Count() - 1; i++ ) { flTotalDist += m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); } //------------------------- if ( !m_bDeceleratingToGoal && m_scriptMove.Count() && flTotalDist > 0 ) { float flNeededAccel = DeltaV( m_scriptMove[0].flMaxVelocity, m_scriptMove[m_scriptMove.Count() - 1].flMaxVelocity, flTotalDist ); m_bDeceleratingToGoal = (flNeededAccel < -idealAccel); //Assert( flNeededAccel != idealAccel); } //------------------------- // insert slowdown points due to blocking if (ai_path_insert_pause_at_obstruction->GetBool() && move.directTrace.pObstruction) { float distToObstruction = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D(); // HACK move obstruction out "stepsize" to account for it being based on stand position and not a trace distToObstruction = distToObstruction + 16; InsertSlowdown( distToObstruction, idealAccel, false ); } if (ai_path_insert_pause_at_est_end->GetBool() && GetNavigator()->GetArrivalDistance() > 0.0) { InsertSlowdown( flTotalDist - GetNavigator()->GetArrivalDistance(), idealAccel, true ); } // calc initial velocity based on immediate direction changes if ( ai_path_adjust_speed_on_immediate_turns->GetBool() && m_scriptMove.Count() > 1) { /* if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT)) { Vector tmp = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation; VectorNormalize( tmp ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 255,255,255, true, 0.1 ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[1].vecLocation + Vector( 0, 0, 10 ), 255,0,0, true, 0.1 ); tmp = GetCurVel(); VectorNormalize( tmp ); NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 0,0,255, true, 0.1 ); } */ Vector d1 = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation; d1.z = 0; VectorNormalize( d1 ); Vector d2 = GetCurVel(); d2.z = 0; VectorNormalize( d2 ); float dot = (DotProduct( d1, d2 ) + MIN_STEER_DOT); dot = clamp( dot, 0.0f, 1.0f ); m_scriptMove[0].flMaxVelocity = m_scriptMove[0].flMaxVelocity * dot; } // clamp forward velocities for (i = 0; i < m_scriptMove.Count() - 1; i++ ) { // find needed acceleration float dv = m_scriptMove[i+1].flMaxVelocity - m_scriptMove[i].flMaxVelocity; if (dv > 0.0) { // find time, distance to accel to next max vel float t1 = dv / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // is there enough distance if (d1 > m_scriptMove[i].flDist) { float r1, r2; // clamp the next velocity to the possible accel in the given distance if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i].flDist, r1, r2 )) { m_scriptMove[i+1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1; } } } } // clamp decel velocities for (i = m_scriptMove.Count() - 1; i > 0; i-- ) { // find needed deceleration float dv = m_scriptMove[i].flMaxVelocity - m_scriptMove[i-1].flMaxVelocity; if (dv < 0.0) { // find time, distance to decal to next max vel float t1 = -dv / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // is there enough distance if (d1 > m_scriptMove[i-1].flDist) { float r1, r2; // clamp the next velocity to the possible decal in the given distance if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i-1].flDist, r1, r2 )) { m_scriptMove[i-1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1; } } } } /* for (i = 0; i < m_scriptMove.Count(); i++) { NDebugOverlay::Text( m_scriptMove[i].vecLocation, (const char *)CFmtStr( "%.2f ", m_scriptMove[i].flMaxVelocity ), false, 0.1 ); // DevMsg("%.2f ", m_scriptMove[i].flMaxVelocity ); } // DevMsg("\n"); */ // insert intermediate ideal velocities for (i = 0; i < m_scriptMove.Count() - 1;) { // accel to ideal float t1 = (idealVelocity - m_scriptMove[i].flMaxVelocity) / idealAccel; float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1; // decel from ideal float t2 = (idealVelocity - m_scriptMove[i+1].flMaxVelocity) / idealAccel; float d2 = m_scriptMove[i+1].flMaxVelocity * t2 + 0.5 * (idealAccel) * t2 * t2; m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); // is it possible to accel and decal to idealVelocity between next two nodes if (d1 + d2 < m_scriptMove[i].flDist) { Vector start = m_scriptMove[i].vecLocation; Vector end = m_scriptMove[i+1].vecLocation; float dist = m_scriptMove[i].flDist; // insert the two points needed to end accel and start decel if (d1 > 1.0 && t1 > 0.1) { a = d1 / dist; script.Init(); script.vecLocation = end * a + start * (1 - a); script.flMaxVelocity = idealVelocity; m_scriptMove.InsertAfter( i, script ); i++; } if (dist - d2 > 1.0 && t2 > 0.1) { // DevMsg("%.2f : ", a ); a = (dist - d2) / dist; script.Init(); script.vecLocation = end * a + start * (1 - a); script.flMaxVelocity = idealVelocity; m_scriptMove.InsertAfter( i, script ); i++; } i++; } else { // check to see if the amount of change needed to reach target is less than the ideal acceleration float flNeededAccel = fabs( DeltaV( m_scriptMove[i].flMaxVelocity, m_scriptMove[i+1].flMaxVelocity, m_scriptMove[i].flDist ) ); if (flNeededAccel < idealAccel) { // if so, they it's possible to get a bit towards the ideal velocity float v1 = m_scriptMove[i].flMaxVelocity; float v2 = m_scriptMove[i+1].flMaxVelocity; float dist = m_scriptMove[i].flDist; // based on solving: // v1+A*t1-v2-A*t2=0 // v1*t1+0.5*A*t1*t1+v2*t2+0.5*A*t2*t2-D=0 float tmp = idealAccel*dist+0.5*v1*v1+0.5*v2*v2; Assert( tmp >= 0 ); t1 = (-v1+sqrt( tmp )) / idealAccel; t2 = (v1+idealAccel*t1-v2)/idealAccel; // if this assert hits, write down the v1, v2, dist, and idealAccel numbers and send them to me (Ken). // go ahead the comment it out, it's safe, but I'd like to know a test case where it's happening //Assert( t1 > 0 && t2 > 0 ); // check to make sure it's really worth it if (t1 > 0.0 && t2 > 0.0) { d1 = v1 * t1 + 0.5 * idealAccel * t1 * t1; /* d2 = v2 * t2 + 0.5 * idealAccel * t2 * t2; Assert( fabs( d1 + d2 - dist ) < 0.001 ); */ float a = d1 / m_scriptMove[i].flDist; script.Init(); script.vecLocation = m_scriptMove[i+1].vecLocation * a + m_scriptMove[i].vecLocation * (1 - a); script.flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * t1; if (script.flMaxVelocity < idealVelocity) { // DevMsg("insert %.2f %.2f %.2f\n", m_scriptMove[i].flMaxVelocity, script.flMaxVelocity, m_scriptMove[i+1].flMaxVelocity ); m_scriptMove.InsertAfter( i, script ); i += 1; } } } i += 1; } } // clamp min velocities for (i = 0; i < m_scriptMove.Count(); i++) { m_scriptMove[i].flMaxVelocity = max( m_scriptMove[i].flMaxVelocity, MIN_VELOCITY ); } // rebuild fields m_scriptMove[0].flElapsedTime = 0; for (i = 0; i < m_scriptMove.Count() - 1; ) { m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D(); if (m_scriptMove[i].flMaxVelocity == 0 && m_scriptMove[i+1].flMaxVelocity == 0) { // force a minimum velocity //CE_assert //Assert( 0 ); m_scriptMove[i+1].flMaxVelocity = 1.0; } float t = m_scriptMove[i].flDist / (0.5 * (m_scriptMove[i].flMaxVelocity + m_scriptMove[i+1].flMaxVelocity)); m_scriptMove[i].flTime = t; /* if (m_scriptMove[i].flDist < 0.01) { // Assert( m_scriptMove[i+1].pWaypoint == NULL ); m_scriptMove.Remove( i + 1 ); continue; } */ m_scriptMove[i+1].flElapsedTime = m_scriptMove[i].flElapsedTime + m_scriptMove[i].flTime; i++; } /* for (i = 0; i < m_scriptMove.Count(); i++) { DevMsg("(%.2f : %.2f : %.2f)", m_scriptMove[i].flMaxVelocity, m_scriptMove[i].flDist, m_scriptMove[i].flTime ); // DevMsg("(%.2f:%.2f)", m_scriptMove[i].flTime, m_scriptMove[i].flElapsedTime ); } DevMsg("\n"); */ }
// 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 CSupplier::UpdateTendrils() { if (IsConstructing()) return; TStubbed("Tendrils"); #if 0 if (!GetPlayerOwner()) { if (m_iTendrilsCallList) glDeleteLists((GLuint)m_iTendrilsCallList, 1); m_iTendrilsCallList = 0; DigitanksGame()->GetTerrain()->DirtyChunkTexturesWithinDistance(GetGlobalOrigin(), GetDataFlowRadius() + GetBoundingRadius()); return; } if (GameServer()->IsLoading()) return; bool bUpdateTerrain = false; size_t iRadius = (size_t)GetDataFlowRadius(); while (m_aTendrils.size() < iRadius) { m_aTendrils.push_back(CTendril()); CTendril* pTendril = &m_aTendrils[m_aTendrils.size()-1]; pTendril->m_flLength = (float)m_aTendrils.size() + GetBoundingRadius(); pTendril->m_vecEndPoint = DigitanksGame()->GetTerrain()->GetPointHeight(GetGlobalOrigin() + AngleVector(EAngle(0, RandomFloat(0, 360), 0)) * pTendril->m_flLength); pTendril->m_flScale = RandomFloat(3, 7); pTendril->m_flOffset = RandomFloat(0, 1); pTendril->m_flSpeed = RandomFloat(0.5f, 2); bUpdateTerrain = true; } if (bUpdateTerrain) DigitanksGame()->GetTerrain()->DirtyChunkTexturesWithinDistance(GetGlobalOrigin(), GetDataFlowRadius() + GetBoundingRadius()); if (m_iTendrilsCallList) glDeleteLists((GLuint)m_iTendrilsCallList, 1); m_iTendrilsCallList = glGenLists(1); Color clrTeam = GetPlayerOwner()->GetColor(); clrTeam = (Vector(clrTeam) + Vector(1,1,1))/2; glNewList((GLuint)m_iTendrilsCallList, GL_COMPILE); for (size_t i = 0; i < m_aTendrils.size(); i++) { // Only show the longest tendrils, for perf reasons. if (i < iRadius - 15) continue; CTendril* pTendril = &m_aTendrils[i]; Vector vecDestination = pTendril->m_vecEndPoint; Vector vecPath = vecDestination - GetGlobalOrigin(); vecPath.y = 0; float flDistance = vecPath.Length2D(); Vector vecDirection = vecPath.Normalized(); size_t iSegments = (size_t)(flDistance/3); GLuint iScrollingTextureProgram = (GLuint)CShaderLibrary::GetScrollingTextureProgram(); GLuint flSpeed = glGetUniformLocation(iScrollingTextureProgram, "flSpeed"); glUniform1f(flSpeed, pTendril->m_flSpeed); clrTeam.SetAlpha(105); CRopeRenderer oRope(GameServer()->GetRenderer(), s_iTendrilBeam, DigitanksGame()->GetTerrain()->GetPointHeight(GetGlobalOrigin()) + Vector(0, 0, 1), 1.0f); oRope.SetColor(clrTeam); oRope.SetTextureScale(pTendril->m_flScale); oRope.SetTextureOffset(pTendril->m_flOffset); oRope.SetForward(Vector(0, 0, -1)); for (size_t i = 1; i < iSegments; i++) { clrTeam.SetAlpha((int)RemapVal((float)i, 1, (float)iSegments, 100, 30)); oRope.SetColor(clrTeam); float flCurrentDistance = ((float)i*flDistance)/iSegments; oRope.AddLink(DigitanksGame()->GetTerrain()->GetPointHeight(GetGlobalOrigin() + vecDirection*flCurrentDistance) + Vector(0, 0, 1)); } oRope.Finish(DigitanksGame()->GetTerrain()->GetPointHeight(vecDestination) + Vector(0, 0, 1)); } glEndList(); #endif }
void CSupplier::PostRender() const { BaseClass::PostRender(); if (!GameServer()->GetRenderer()->IsRenderingTransparent()) return; float flGrowthTime = (float)(GameServer()->GetGameTime() - m_flTendrilGrowthStartTime); if (flGrowthTime < 0) return; if (m_flTendrilGrowthStartTime == 0) { DigitanksGame()->GetDigitanksRenderer()->AddTendrilBatch(this); return; } COverheadCamera* pCamera = DigitanksGame()->GetOverheadCamera(); Vector vecCamera = pCamera->GetGlobalOrigin(); float flDistanceSqr = GetGlobalOrigin().DistanceSqr(vecCamera); float flFadeDistance = tendril_fade_distance.GetFloat(); float flFadeAlpha = RemapValClamped(flDistanceSqr, flFadeDistance*flFadeDistance, (flFadeDistance+20)*(flFadeDistance+20), 1.0f, 0.0f); if (flFadeAlpha <= 0) return; float flTreeAlpha = 1.0f; if (DigitanksGame()->GetTerrain()->GetBit(CTerrain::WorldToArraySpace(GetGlobalOrigin().x), CTerrain::WorldToArraySpace(GetGlobalOrigin().y), TB_TREE)) flTreeAlpha = 0.3f; CRenderingContext r(GameServer()->GetRenderer(), true); if (DigitanksGame()->ShouldRenderFogOfWar()) r.UseFrameBuffer(DigitanksGame()->GetDigitanksRenderer()->GetVisibilityMaskedBuffer()); r.SetDepthMask(false); r.UseMaterial(s_hTendrilBeam); r.SetUniform("flTime", (float)GameServer()->GetGameTime()); r.SetUniform("iTexture", 0); r.SetUniform("flAlpha", flFadeAlpha * flTreeAlpha); Color clrTeam = GetPlayerOwner()?GetPlayerOwner()->GetColor():Color(255,255,255,255); clrTeam = (Vector(clrTeam) + Vector(1,1,1))/2; if (m_flTendrilGrowthStartTime > 0) { float flTotalSize = (float)m_aTendrils.size() + GetBoundingRadius(); for (size_t i = 0; i < m_aTendrils.size(); i++) { if (i < m_aTendrils.size() - 15) continue; const CTendril* pTendril = &m_aTendrils[i]; float flGrowthLength = RemapVal(flGrowthTime, 0, GROWTH_TIME, pTendril->m_flLength-flTotalSize, pTendril->m_flLength); if (flGrowthLength < 0) continue; Vector vecDestination = GetGlobalOrigin() + (pTendril->m_vecEndPoint - GetGlobalOrigin()).Normalized() * flGrowthLength; Vector vecPath = vecDestination - GetGlobalOrigin(); vecPath.y = 0; float flDistance = vecPath.Length2D(); Vector vecDirection = vecPath.Normalized(); size_t iSegments = (size_t)(flDistance/3); r.SetUniform("flSpeed", pTendril->m_flSpeed); clrTeam.SetAlpha(105); CRopeRenderer oRope(GameServer()->GetRenderer(), s_hTendrilBeam, DigitanksGame()->GetTerrain()->GetPointHeight(GetGlobalOrigin()) + Vector(0, 0, 1), 1.0f); oRope.SetColor(clrTeam); oRope.SetTextureScale(pTendril->m_flScale); oRope.SetTextureOffset(pTendril->m_flOffset); oRope.SetForward(Vector(0, 0, -1)); for (size_t i = 1; i < iSegments; i++) { clrTeam.SetAlpha((int)RemapVal((float)i, 1, (float)iSegments, 100, 30)); oRope.SetColor(clrTeam); float flCurrentDistance = ((float)i*flDistance)/iSegments; oRope.AddLink(DigitanksGame()->GetTerrain()->GetPointHeight(GetGlobalOrigin() + vecDirection*flCurrentDistance) + Vector(0, 0, 1)); } oRope.Finish(DigitanksGame()->GetTerrain()->GetPointHeight(vecDestination) + Vector(0, 0, 1)); } } }
/* <485ecf> ../cstrike/dlls/hostage/hostage_localnav.cpp:472 */ int CLocalNav::PathTraversable(Vector &vecSource, Vector &vecDest, int fNoMonsters) { TraceResult tr; Vector vecSrcTmp; Vector vecDestTmp; Vector vecDir; float_precision flTotal; int retval = PATH_TRAVERSABLE_EMPTY; vecSrcTmp = vecSource; vecDestTmp = vecDest - vecSource; vecDir = vecDestTmp.NormalizePrecision(); vecDir.z = 0; flTotal = vecDestTmp.Length2D(); while (flTotal > 1.0) { if (flTotal >= s_flStepSize) { #ifndef PLAY_GAMEDLL vecDestTmp = vecSrcTmp + (vecDir * s_flStepSize); #else // TODO: fix test demo vecDestTmp[0] = vecSrcTmp[0] + (vecDir[0] * s_flStepSize); vecDestTmp[1] = vecSrcTmp[1] + (float)(vecDir[1] * s_flStepSize); vecDestTmp[2] = vecSrcTmp[2] + (vecDir[2] * s_flStepSize); #endif // PLAY_GAMEDLL } else vecDestTmp = vecDest; m_fTargetEntHit = FALSE; if (PathClear(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { vecDestTmp = tr.vecEndPos; if (retval == PATH_TRAVERSABLE_EMPTY) { retval = PATH_TRAVERSABLE_SLOPE; } } else { if (tr.fStartSolid) { return PATH_TRAVERSABLE_EMPTY; } if (tr.pHit && !fNoMonsters && tr.pHit->v.classname) { if (FClassnameIs(tr.pHit, "hostage_entity")) { return PATH_TRAVERSABLE_EMPTY; } } vecSrcTmp = tr.vecEndPos; if (tr.vecPlaneNormal.z <= 0.7) { if (StepTraversable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { if (retval == PATH_TRAVERSABLE_EMPTY) { retval = PATH_TRAVERSABLE_STEP; } } else { if (!StepJumpable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { return PATH_TRAVERSABLE_EMPTY; } if (retval == PATH_TRAVERSABLE_EMPTY) { retval = PATH_TRAVERSABLE_STEPJUMPABLE; } } } else { if (!SlopeTraversable(vecSrcTmp, vecDestTmp, fNoMonsters, tr)) { return PATH_TRAVERSABLE_EMPTY; } if (retval == PATH_TRAVERSABLE_EMPTY) { retval = PATH_TRAVERSABLE_SLOPE; } } } Vector vecDropDest = vecDestTmp - Vector(0, 0, 300); if (PathClear(vecDestTmp, vecDropDest, fNoMonsters, tr)) { return PATH_TRAVERSABLE_EMPTY; } if (!tr.fStartSolid) vecDestTmp = tr.vecEndPos; vecSrcTmp = vecDestTmp; BOOL fProgressThisTime = m_fTargetEntHit; Vector vecSrcThisTime = vecDest - vecDestTmp; if (fProgressThisTime) break; flTotal = vecSrcThisTime.Length2D(); } vecDest = vecDestTmp; return retval; }
float CSDKPlayerAnimState::GetOuterXYSpeed() { Vector vel; GetOuterAbsVelocity( vel ); return vel.Length2D(); }
void CSDKPlayer::CheckBallShield(const Vector &oldPos, Vector &newPos, const Vector &oldVel, Vector &newVel, const QAngle &oldAng, QAngle &newAng) { bool stopPlayer = false; const float border = (GetFlags() & FL_SHIELD_KEEP_IN) ? -mp_shield_border.GetInt() : mp_shield_border.GetInt(); int forward; #ifdef CLIENT_DLL forward = GetPlayersTeam(this)->m_nForward; #else forward = GetTeam()->m_nForward; #endif if (SDKGameRules()->m_nShieldType != SHIELD_NONE) { if (SDKGameRules()->m_nShieldType == SHIELD_GOALKICK || SDKGameRules()->m_nShieldType == SHIELD_PENALTY || SDKGameRules()->m_nShieldType == SHIELD_FREEKICK || (SDKGameRules()->m_nShieldType == SHIELD_KICKOFF && !SDKGameRules()->IsIntermissionState()) || SDKGameRules()->m_nShieldType == SHIELD_CORNER) { const float radius = mp_shield_ball_radius.GetFloat(); Vector dir = newPos - SDKGameRules()->m_vShieldPos; if (dir.Length2DSqr() < pow(radius, 2)) { dir.z = 0; dir.NormalizeInPlace(); newPos = SDKGameRules()->m_vShieldPos + dir * radius; stopPlayer = true; } } if (SDKGameRules()->m_nShieldType == SHIELD_GOALKICK || SDKGameRules()->m_nShieldType == SHIELD_PENALTY || SDKGameRules()->m_nShieldType == SHIELD_KEEPERHANDS) { int side = (SDKGameRules()->m_nShieldType == SHIELD_PENALTY ? GetGlobalTeam(SDKGameRules()->m_nShieldTeam)->GetOppTeamNumber() : SDKGameRules()->m_nShieldTeam); Vector min = GetGlobalTeam(side)->m_vPenBoxMin - border; Vector max = GetGlobalTeam(side)->m_vPenBoxMax + border; if (GetFlags() & FL_SHIELD_KEEP_OUT || SDKGameRules()->m_nShieldType == SHIELD_PENALTY) { if (SDKGameRules()->m_vKickOff.GetY() > min.y) min.y -= 500; else max.y += 500; } bool isInsideBox = newPos.x > min.x && newPos.y > min.y && newPos.x < max.x && newPos.y < max.y; Vector boxCenter = (min + max) / 2; if (GetFlags() & FL_SHIELD_KEEP_OUT && isInsideBox) { bool oldPosInBox = true; if (newPos.x > min.x && oldPos.x <= min.x && newPos.x < boxCenter.x) { newPos.x = min.x; oldPosInBox = false; } else if (newPos.x < max.x && oldPos.x >= max.x && newPos.x > boxCenter.x) { newPos.x = max.x; oldPosInBox = false; } if (newPos.y > min.y && oldPos.y <= min.y && newPos.y < boxCenter.y) { newPos.y = min.y; oldPosInBox = false; } else if (newPos.y < max.y && oldPos.y >= max.y && newPos.y > boxCenter.y) { newPos.y = max.y; oldPosInBox = false; } stopPlayer = true; if (SDKGameRules()->m_nShieldType == SHIELD_KEEPERHANDS && oldPosInBox) { Vector goalCenter = GetGlobalTeam(SDKGameRules()->m_nShieldTeam)->m_vGoalCenter; goalCenter.y -= GetGlobalTeam(SDKGameRules()->m_nShieldTeam)->m_nForward * 500; if ((goalCenter - newPos).Length2DSqr() < (goalCenter - oldPos).Length2DSqr()) { newPos.x = oldPos.x; newPos.y = oldPos.y; stopPlayer = true; } else stopPlayer = false; } } else if (GetFlags() & FL_SHIELD_KEEP_IN && !isInsideBox) { if (newPos.x < min.x) newPos.x = min.x; else if (newPos.x > max.x) newPos.x = max.x; if (newPos.y < min.y) newPos.y = min.y; else if (newPos.y > max.y) newPos.y = max.y; stopPlayer = true; } } if (SDKGameRules()->m_nShieldType == SHIELD_THROWIN && GetFlags() & FL_SHIELD_KEEP_OUT || SDKGameRules()->m_nShieldType == SHIELD_FREEKICK || SDKGameRules()->m_nShieldType == SHIELD_CORNER || SDKGameRules()->m_nShieldType == SHIELD_KICKOFF || SDKGameRules()->m_nShieldType == SHIELD_PENALTY && (GetFlags() & FL_SHIELD_KEEP_OUT)) { float radius = SDKGameRules()->GetShieldRadius(GetTeamNumber(), GetFlags() & FL_SHIELD_KEEP_IN) + border; Vector dir = newPos - SDKGameRules()->m_vShieldPos; if (!SDKGameRules()->IsIntermissionState() && ((GetFlags() & FL_SHIELD_KEEP_OUT) && dir.Length2D() < radius || (GetFlags() & FL_SHIELD_KEEP_IN) && dir.Length2D() > radius)) { dir.z = 0; dir.NormalizeInPlace(); newPos = SDKGameRules()->m_vShieldPos + dir * radius; stopPlayer = true; } if (SDKGameRules()->m_nShieldType == SHIELD_KICKOFF && (GetFlags() & FL_SHIELD_KEEP_OUT) && (!SDKGameRules()->IsIntermissionState() || SDKGameRules()->State_Get() == MATCH_PERIOD_WARMUP && mp_shield_block_opponent_half.GetBool())) { int forward; #ifdef CLIENT_DLL forward = GetPlayersTeam(this)->m_nForward; #else forward = GetTeam()->m_nForward; #endif float yBorder = SDKGameRules()->m_vKickOff.GetY() - abs(border) * forward; if (ZeroSign(newPos.y - yBorder) == forward) { newPos.y = yBorder; stopPlayer = true; } } if (SDKGameRules()->m_nShieldType == SHIELD_FREEKICK && mp_shield_block_sixyardbox.GetBool()) { int teamPosType; #ifdef CLIENT_DLL teamPosType = GameResources()->GetTeamPosType(entindex()); #else teamPosType = GetTeamPosType(); #endif if (teamPosType != POS_GK || GetTeamNumber() != GetGlobalTeam(SDKGameRules()->m_nShieldTeam)->GetOppTeamNumber()) { int side = GetGlobalTeam(SDKGameRules()->m_nShieldTeam)->GetOppTeamNumber(); Vector min = GetGlobalTeam(side)->m_vSixYardBoxMin - border; Vector max = GetGlobalTeam(side)->m_vSixYardBoxMax + border; if (GetGlobalTeam(side)->m_nForward == 1) min.y -= 500; else max.y += 500; bool isInsideBox = newPos.x > min.x && newPos.y > min.y && newPos.x < max.x && newPos.y < max.y; Vector boxCenter = (min + max) / 2; if (isInsideBox) { if (newPos.x > min.x && oldPos.x <= min.x && newPos.x < boxCenter.x) newPos.x = min.x; else if (newPos.x < max.x && oldPos.x >= max.x && newPos.x > boxCenter.x) newPos.x = max.x; if (newPos.y > min.y && oldPos.y <= min.y && newPos.y < boxCenter.y) newPos.y = min.y; else if (newPos.y < max.y && oldPos.y >= max.y && newPos.y > boxCenter.y) newPos.y = max.y; stopPlayer = true; } } } } if (SDKGameRules()->m_nShieldType == SHIELD_THROWIN && GetFlags() & FL_SHIELD_KEEP_IN) { const int xLength = mp_shield_throwin_movement_x.GetInt(); const int yLength = mp_shield_throwin_movement_y.GetInt(); Vector xSign = SDKGameRules()->m_vShieldPos.GetX() > SDKGameRules()->m_vKickOff.GetX() ? 1 : -1; Vector min = SDKGameRules()->m_vShieldPos + Vector(xSign == -1 ? -xLength : 0, -yLength / 2, 0); Vector max = SDKGameRules()->m_vShieldPos + Vector(xSign == -1 ? 0 : xLength, yLength / 2, 0); bool isInsideBox = newPos.x > min.x && newPos.y > min.y && newPos.x < max.x && newPos.y < max.y; if (!isInsideBox) { if (newPos.x < min.x) newPos.x = min.x; else if (newPos.x > max.x) newPos.x = max.x; if (newPos.y < min.y) newPos.y = min.y; else if (newPos.y > max.y) newPos.y = max.y; stopPlayer = true; } } } if (!SDKGameRules()->IsIntermissionState() && mp_field_padding_enabled.GetBool()) { float border = mp_field_padding.GetInt(); Vector min = SDKGameRules()->m_vFieldMin - border; Vector max = SDKGameRules()->m_vFieldMax + border; if (newPos.x < min.x || newPos.y < min.y || newPos.x > max.x || newPos.y > max.y) { if (newPos.x < min.x) newPos.x = min.x; else if (newPos.x > max.x) newPos.x = max.x; if (newPos.y < min.y) newPos.y = min.y; else if (newPos.y > max.y) newPos.y = max.y; stopPlayer = true; } } if (stopPlayer) { //newVel = oldVel; trace_t trace; UTIL_TraceHull(newPos, newPos, GetPlayerMins(), GetPlayerMaxs(), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &trace); if (trace.startsolid) { // Stay at the old pos since the new pos is taken newPos = oldPos; } Vector dir = newPos - oldPos; dir.z = 0; float speed = dir.NormalizeInPlace(); newVel = dir * min(speed * 100, mp_runspeed.GetInt()); //newVel.x = (newPos - oldPos).x * 100; //newVel.y = (newPos - oldPos).y * 100; //newPos = pos; } }
void CKeeperBot::BotAdjustPos() { float modifier = KEEPER_MID_COEFF; QAngle ang = m_oldcmd.viewangles; Vector target = GetTeam()->m_vGoalCenter; if (m_vBallVel.Length2D() > 750 && m_flAngToBallVel < 60) { float yDist = GetTeam()->m_vGoalCenter.GetY() - m_vBallPos.y; float vAng = acos(Sign(yDist) * m_vBallDir2D.y); float xDist = Sign(m_vBallDir2D.x) * abs(yDist) * tan(vAng); target.x = clamp(m_vBallPos.x + xDist, GetTeam()->m_vGoalCenter.GetX() - 150, GetTeam()->m_vGoalCenter.GetX() + 150); } if (m_pBall->State_Get() == BALL_STATE_KEEPERHANDS && m_pBall->GetCurrentPlayer() == this) { if (ShotButtonsReleased()) { modifier = KEEPER_CLOSE_COEFF; //m_cmd.buttons |= (IN_ATTACK2 | IN_ATTACK); m_cmd.buttons |= IN_ALT1; CSDKPlayer *pPl = FindClosestPlayerToSelf(true, true); if (!pPl && SDKGameRules()->IsIntermissionState()) pPl = FindClosestPlayerToSelf(false, true); if (pPl) { VectorAngles(pPl->GetLocalOrigin() - GetLocalOrigin(), ang); ang[PITCH] = g_IOSRand.RandomFloat(-40, 0); //m_flBotNextShot = gpGlobals->curtime + 1; } else { VectorAngles(Vector(0, GetTeam()->m_nForward, 0), ang); ang[YAW] += g_IOSRand.RandomFloat(-45, 45); ang[PITCH] = g_IOSRand.RandomFloat(-40, 0); //m_flBotNextShot = gpGlobals->curtime + 1; } } } else// if (gpGlobals->curtime >= m_flBotNextShot) { VectorAngles(m_vDirToBall, ang); float ballDistToGoal = (m_vBallPos - GetTeam()->m_vGoalCenter).Length2D(); CSDKPlayer *pClosest = FindClosestPlayerToBall(); m_cmd.buttons |= IN_ATTACK; if (ballDistToGoal < 750 && m_vDirToBall.Length2D() < 200 && m_vDirToBall.z < 200 && (m_vDirToBall.z < 80 || m_vBallVel.z <= 0)) { modifier = KEEPER_CLOSE_COEFF;// max(0.15f, 1 - ballDistToGoal / 750); VectorAngles(Vector(0, GetTeam()->m_nForward, 0), ang); bool diving = false; if (m_flAngToBallVel < 60 && m_flAngToBallVel > 15) { if (abs(m_vDirToBall.x) > 50 && abs(m_vDirToBall.x) < 200 && m_vDirToBall.z < 150 && abs(m_vDirToBall.x) < 150 && m_vBallVel.Length() > 200) { m_cmd.buttons |= IN_JUMP; m_cmd.buttons |= Sign(m_vDirToBall.x) == GetTeam()->m_nRight ? IN_MOVERIGHT : IN_MOVELEFT; //m_cmd.buttons |= (IN_ATTACK2 | IN_ATTACK); diving = true; } else if (m_vDirToBall.z > 100 && m_vDirToBall.z < 150 && m_vDirToBall.Length2D() < 100) { m_cmd.buttons |= IN_JUMP; //m_cmd.buttons |= (IN_ATTACK2 | IN_ATTACK); diving = true; } else if (abs(m_vDirToBall.y) > 50 && abs(m_vDirToBall.y) < 200 && m_vDirToBall.z < 100 && abs(m_vDirToBall.x) < 50 && m_vBallVel.Length() < 200 && pClosest != this) { m_cmd.buttons |= IN_JUMP; m_cmd.buttons |= Sign(m_vLocalDirToBall.x) == GetTeam()->m_nForward ? IN_FORWARD : IN_BACK; //m_cmd.buttons |= (IN_ATTACK2 | IN_ATTACK); diving = true; } } if (!diving) { if (m_vDirToBall.Length2D() < 50) { modifier = KEEPER_CLOSE_COEFF; //m_cmd.buttons |= (IN_ATTACK2 | IN_ATTACK); CSDKPlayer *pPl = FindClosestPlayerToSelf(true, true); if (!pPl && SDKGameRules()->IsIntermissionState()) pPl = FindClosestPlayerToSelf(false, true); if (pPl) { VectorAngles(pPl->GetLocalOrigin() - GetLocalOrigin(), ang); ang[PITCH] = g_IOSRand.RandomFloat(-40, 0); //m_flBotNextShot = gpGlobals->curtime + 1; } else { VectorAngles(Vector(0, GetTeam()->m_nForward, 0), ang); ang[YAW] += g_IOSRand.RandomFloat(-45, 45); ang[PITCH] = g_IOSRand.RandomFloat(-40, 0); //m_flBotNextShot = gpGlobals->curtime + 1; } } else modifier = KEEPER_CLOSE_COEFF; } } else if (ballDistToGoal < 1250 && m_flAngToBallVel < 60 && m_vBallVel.Length2D() > 300 && (m_vBallVel.z > 100 || m_vDirToBall.z > 100)) { modifier = KEEPER_FAR_COEFF; } else if (ballDistToGoal < 1000 && m_vDirToBall.z < 80 && m_vBallVel.Length2D() < 300 && m_vBallVel.z < 100) { if (pClosest == this) modifier = KEEPER_CLOSE_COEFF; else modifier = max(KEEPER_FAR_COEFF, 1 - pow(min(1, ballDistToGoal / 750.0f), 2)); } else { modifier = KEEPER_MID_COEFF; } m_cmd.viewangles = ang; m_LastAngles = m_cmd.viewangles; SetLocalAngles(m_cmd.viewangles); SnapEyeAngles(ang); Vector targetPosDir = target + modifier * (m_vBallPos - target) - GetLocalOrigin(); targetPosDir.z = 0; float dist = targetPosDir.Length2D(); VectorNormalizeFast(targetPosDir); Vector localDir; VectorIRotate(targetPosDir, EntityToWorldTransform(), localDir); //float speed; //if (dist < 10) // speed = 0; //else if (dist < 100) // speed = mp_runspeed.GetInt(); //else // speed = mp_sprintspeed.GetInt(); //float speed = clamp(dist - 10, 0, mp_runspeed.GetInt()); float speed = 0; if (dist > 30) speed = clamp(5 * dist, 0, mp_sprintspeed.GetInt() * (mp_botkeeperskill.GetInt() / 100.0f)); if (speed > mp_runspeed.GetInt()) m_cmd.buttons |= IN_SPEED; m_cmd.forwardmove = localDir.x * speed; m_cmd.sidemove = -localDir.y * speed; if (m_cmd.forwardmove > 0) m_cmd.buttons |= IN_FORWARD; else if (m_cmd.forwardmove < 0) m_cmd.buttons |= IN_BACK; if (m_cmd.sidemove > 0) m_cmd.buttons |= IN_RIGHT; else if (m_cmd.sidemove < 0) m_cmd.buttons |= IN_MOVELEFT; } m_cmd.viewangles = ang; }
void CKeeperBot::BotCenter() { Vector dir = GetTeam()->m_vGoalCenter - GetLocalOrigin(); m_cmd.sidemove = Sign(dir.x) * max(0, dir.Length2D() - 10); m_cmd.forwardmove = Sign(dir.y) * max(0, dir.Length2D() - 10); }