Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
10 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_movesolver.h"
  9. #include "ndebugoverlay.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. #if !defined(PS3) && (!defined(_MSC_VER) || _MSC_VER < 1800)
  14. // This C99 function exists in VS 2013's math.h and for PS3 but are not currently available elsewhere.
  15. inline float round( float f )
  16. {
  17. return (float)( (int)( f + 0.5 ) );
  18. }
  19. #endif
  20. //-----------------------------------------------------------------------------
  21. // CAI_MoveSolver
  22. //-----------------------------------------------------------------------------
  23. // The epsilon used by the solver
  24. const float AIMS_EPS = 0.01;
  25. //-----------------------------------------------------------------------------
  26. // Visualization
  27. //-----------------------------------------------------------------------------
  28. void CAI_MoveSolver::VisualizeRegulations( const Vector& origin )
  29. {
  30. if ( m_Regulations.Count() )
  31. {
  32. CAI_MoveSuggestions regulations;
  33. regulations.AddVectorToTail( m_Regulations );
  34. NormalizeSuggestions( &regulations[0], (&regulations[0]) + regulations.Count() );
  35. Vector side1, mid, side2;
  36. for (int i = regulations.Count(); --i >= 0; )
  37. {
  38. // Compute the positions of the angles...
  39. float flMinAngle = regulations[i].arc.center - regulations[i].arc.span * 0.5f;
  40. float flMaxAngle = regulations[i].arc.center + regulations[i].arc.span * 0.5f;
  41. side1 = UTIL_YawToVector( flMinAngle );
  42. side2 = UTIL_YawToVector( flMaxAngle );
  43. mid = UTIL_YawToVector( regulations[i].arc.center );
  44. // Stronger weighted ones are bigger
  45. if ( regulations[i].weight < 0 )
  46. {
  47. float flLength = 10 + 40 * ( regulations[i].weight * -1.0);
  48. side1 *= flLength;
  49. side2 *= flLength;
  50. mid *= flLength;
  51. side1 += origin;
  52. side2 += origin;
  53. mid += origin;
  54. NDebugOverlay::Triangle(origin, mid, side1, 255, 0, 0, 48, true, 0.1f );
  55. NDebugOverlay::Triangle(origin, side2, mid, 255, 0, 0, 48, true, 0.1f );
  56. }
  57. }
  58. }
  59. }
  60. //-------------------------------------
  61. // Purpose: The actual solver function. Reweights according to type and sums
  62. // all the suggestions, identifying the best.
  63. //-------------------------------------
  64. bool CAI_MoveSolver::Solve( const AI_MoveSuggestion_t *pSuggestions, int nSuggestions, AI_MoveSolution_t *pResult)
  65. {
  66. //---------------------------------
  67. //
  68. // Quick out
  69. //
  70. if ( !nSuggestions )
  71. return false;
  72. if ( nSuggestions == 1 && m_Regulations.Count() == 0 && pSuggestions->type == AIMST_MOVE )
  73. {
  74. pResult->dir = pSuggestions->arc.center;
  75. return true;
  76. }
  77. //---------------------------------
  78. //
  79. // Setup
  80. //
  81. CAI_MoveSuggestions suggestions;
  82. suggestions.EnsureCapacity( m_Regulations.Count() + nSuggestions );
  83. suggestions.CopyArray( pSuggestions, nSuggestions);
  84. suggestions.AddVectorToTail( m_Regulations );
  85. // Initialize the solver
  86. const int NUM_SOLUTIONS = 120;
  87. const int SOLUTION_ANG = 360 / NUM_SOLUTIONS;
  88. COMPILE_TIME_ASSERT( ( 360 % NUM_SOLUTIONS ) == 0 );
  89. struct Solution_t
  90. {
  91. // The sum bias
  92. float bias;
  93. float highBias;
  94. AI_MoveSuggestion_t *pHighSuggestion;
  95. };
  96. Solution_t solutions[NUM_SOLUTIONS] = { 0 };
  97. //---------------------------------
  98. // The first thing we do is reweight and normalize the weights into a range of [-1..1], where
  99. // a negative weight is a repulsion. This becomes a bias for the solver.
  100. // @TODO (toml 06-18-02): this can be made sligtly more optimal by precalculating regulation adjusted weights
  101. Assert( suggestions.Count() >= 1 );
  102. NormalizeSuggestions( &suggestions[0], (&suggestions[0]) + suggestions.Count() );
  103. //
  104. // Add the biased suggestions to the solutions
  105. //
  106. for ( int iSuggestion = 0; iSuggestion < suggestions.Count(); ++iSuggestion )
  107. {
  108. AI_MoveSuggestion_t &current = suggestions[iSuggestion];
  109. // Convert arc values to solution indices relative to right post. Right is angle down, left is angle up.
  110. float halfSpan = current.arc.span * 0.5;
  111. int center = round( ( halfSpan * NUM_SOLUTIONS ) / 360 );
  112. int left = ( current.arc.span * NUM_SOLUTIONS ) / 360;
  113. float angRight = current.arc.center - halfSpan;
  114. if (angRight < 0.0)
  115. angRight += 360;
  116. int base = ( angRight * NUM_SOLUTIONS ) / 360;
  117. // Sweep from left to right, summing the bias. For positive suggestions,
  118. // the bias is further weighted to favor the center of the arc.
  119. const float positiveDegradePer180 = 0.05; // i.e., lose 5% of weight by the time hit 180 degrees off center
  120. const float positiveDegrade = ( positiveDegradePer180 / ( NUM_SOLUTIONS * 0.5 ) );
  121. for ( int i = 0; i < left + 1; ++i )
  122. {
  123. float bias = 0.0;
  124. if ( current.weight > 0)
  125. {
  126. int iOffset = center - i;
  127. float degrade = abs( iOffset ) * positiveDegrade;
  128. if ( ( (current.flags & AIMS_FAVOR_LEFT ) && i > center ) ||
  129. ( (current.flags & AIMS_FAVOR_RIGHT) && i < center ) )
  130. {
  131. degrade *= 0.9;
  132. }
  133. bias = current.weight - ( current.weight * degrade );
  134. }
  135. else
  136. bias = current.weight;
  137. int iCurSolution = (base + i) % NUM_SOLUTIONS;
  138. solutions[iCurSolution].bias += bias;
  139. if ( bias > solutions[iCurSolution].highBias )
  140. {
  141. solutions[iCurSolution].highBias = bias;
  142. solutions[iCurSolution].pHighSuggestion = &current;
  143. }
  144. }
  145. }
  146. //
  147. // Find the best solution
  148. //
  149. int best = -1;
  150. float biasBest = 0;
  151. for ( int i = 0; i < NUM_SOLUTIONS; ++i )
  152. {
  153. if ( solutions[i].bias > biasBest )
  154. {
  155. best = i;
  156. biasBest = solutions[i].bias;
  157. }
  158. }
  159. if ( best == -1 )
  160. return false; // no solution
  161. //
  162. // Construct the results
  163. //
  164. float result = best * SOLUTION_ANG;
  165. // If the matching suggestion is within the solution, use that as the result,
  166. // as it is valid and more precise.
  167. const float suggestionCenter = solutions[best].pHighSuggestion->arc.center;
  168. if ( suggestionCenter > result && suggestionCenter <= result + SOLUTION_ANG )
  169. result = suggestionCenter;
  170. pResult->dir = result;
  171. return true;
  172. }
  173. //-------------------------------------
  174. // Purpose: Adjusts the suggestion weights according to the type of the suggestion,
  175. // apply the appropriate sign, ensure values are in expected ranges
  176. //-------------------------------------
  177. struct AI_MoveSuggWeights
  178. {
  179. float min;
  180. float max;
  181. };
  182. static AI_MoveSuggWeights g_AI_MoveSuggWeights[] = // @TODO (toml 06-18-02): these numbers need tuning
  183. {
  184. { 0.20, 1.00 }, // AIMST_MOVE
  185. { -0.00, -0.25 }, // AIMST_AVOID_DANGER
  186. { -0.00, -0.25 }, // AIMST_AVOID_OBJECT
  187. { -0.00, -0.25 }, // AIMST_AVOID_NPC
  188. { -0.00, -0.25 }, // AIMST_AVOID_WORLD
  189. { -1.00, -1.00 }, // AIMST_NO_KNOWLEDGE
  190. { -0.60, -0.60 }, // AIMST_OSCILLATION_DETERRANCE
  191. { 0.00, 0.00 }, // AIMST_INVALID
  192. };
  193. void CAI_MoveSolver::NormalizeSuggestions( AI_MoveSuggestion_t *pBegin, AI_MoveSuggestion_t *pEnd )
  194. {
  195. while ( pBegin != pEnd )
  196. {
  197. const float min = g_AI_MoveSuggWeights[pBegin->type].min;
  198. const float max = g_AI_MoveSuggWeights[pBegin->type].max;
  199. Assert( pBegin->weight >= -AIMS_EPS && pBegin->weight <= 1.0 + AIMS_EPS );
  200. if ( pBegin->weight < AIMS_EPS ) // zero normalizes to zero
  201. pBegin->weight = 0.0;
  202. else
  203. pBegin->weight = ( ( max - min ) * pBegin->weight ) + min;
  204. while (pBegin->arc.center < 0)
  205. pBegin->arc.center += 360;
  206. while (pBegin->arc.center >= 360)
  207. pBegin->arc.center -= 360;
  208. ++pBegin;
  209. }
  210. }
  211. //-------------------------------------
  212. bool CAI_MoveSolver::HaveRegulationForObstacle( CBaseEntity *pEntity)
  213. {
  214. for ( int i = 0; i < m_Regulations.Count(); ++i )
  215. {
  216. if ( m_Regulations[i].hObstacleEntity != NULL &&
  217. pEntity == m_Regulations[i].hObstacleEntity.Get() )
  218. {
  219. return true;
  220. }
  221. }
  222. return false;
  223. }
  224. //-----------------------------------------------------------------------------
  225. //
  226. // Commands and tests
  227. //
  228. #ifdef DEBUG
  229. CON_COMMAND(ai_test_move_solver, "Tests the AI move solver system")
  230. {
  231. #ifdef DEBUG
  232. const float EPS = 0.001;
  233. #endif
  234. DevMsg( "Beginning move solver tests...\n" );
  235. CAI_MoveSolver solver;
  236. AI_MoveSolution_t solution;
  237. int i;
  238. //
  239. // Value in, no regulations, should yield value out
  240. //
  241. {
  242. DevMsg( "Simple... " );
  243. for (i = 0; i < 360; ++i)
  244. {
  245. Assert( solver.Solve( AI_MoveSuggestion_t( AIMST_MOVE, 1, i, 180 ), &solution ) );
  246. Assert( solution.dir == (float)i );
  247. }
  248. DevMsg( "pass.\n" );
  249. solver.ClearRegulations();
  250. }
  251. //
  252. // Two values in, should yield the first
  253. //
  254. {
  255. DevMsg( "Two positive... " );
  256. AI_MoveSuggestion_t suggestions[2];
  257. suggestions[0].Set( AIMST_MOVE, 1.0, 180, 100 );
  258. suggestions[1].Set( AIMST_MOVE, 0.5, 0, 100 );
  259. Assert( solver.Solve( suggestions, 2, &solution ) );
  260. Assert( solution.dir == (float)suggestions[0].arc.center );
  261. DevMsg( "pass.\n" );
  262. solver.ClearRegulations();
  263. }
  264. //
  265. // Two values in, first regulated, should yield the second
  266. //
  267. {
  268. DevMsg( "Avoid one of two... " );
  269. AI_MoveSuggestion_t suggestions[2];
  270. solver.AddRegulation(AI_MoveSuggestion_t( AIMST_AVOID_OBJECT, 1, 260, 60 ) );
  271. suggestions[0].Set( AIMST_MOVE, 1.0, 270, 45 );
  272. suggestions[1].Set( AIMST_MOVE, 1.0, 0, 45 );
  273. Assert( solver.Solve( suggestions, 2, &solution ) );
  274. Assert( solution.dir == (float)suggestions[1].arc.center );
  275. DevMsg( "pass.\n" );
  276. solver.ClearRegulations();
  277. }
  278. //
  279. // No solution
  280. //
  281. {
  282. DevMsg( "No solution... " );
  283. AI_MoveSuggestion_t suggestions[2];
  284. suggestions[0].Set( AIMST_MOVE, 1.0, 270, 90 );
  285. suggestions[1].Set( AIMST_AVOID_OBJECT, 1.0, 260, 180 );
  286. Assert( !solver.Solve( suggestions, 2, &solution ) );
  287. DevMsg( "pass.\n" );
  288. solver.ClearRegulations();
  289. }
  290. //
  291. // Nearest solution, in tolerance
  292. //
  293. {
  294. DevMsg( "Nearest solution, in tolerance... " );
  295. AI_MoveSuggestion_t suggestions[2];
  296. suggestions[0].Set( AIMST_MOVE, 1.0, 278, 90 );
  297. suggestions[1].Set( AIMST_AVOID_OBJECT, 1.0, 260, 24 );
  298. Assert( solver.Solve( suggestions, 2, &solution ) );
  299. Assert( solution.dir == (float)suggestions[0].arc.center );
  300. DevMsg( "pass.\n" );
  301. solver.ClearRegulations();
  302. }
  303. //
  304. // Nearest solution
  305. //
  306. {
  307. DevMsg( "Nearest solution... " );
  308. AI_MoveSuggestion_t suggestions[2];
  309. suggestions[0].Set( AIMST_MOVE, 1.0, 270, 90 );
  310. suggestions[1].Set( AIMST_AVOID_OBJECT, 1.0, 260, 40 );
  311. Assert( solver.Solve( suggestions, 2, &solution ) );
  312. Assert( solution.dir - 282 < EPS ); // given 60 solutions
  313. DevMsg( "pass.\n" );
  314. solver.ClearRegulations();
  315. }
  316. }
  317. #endif
  318. //=============================================================================