g_nav.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // leave this line at the top for all g_xxxx.cpp files...
  2. #include "g_headers.h"
  3. #include "b_local.h"
  4. #include "g_nav.h"
  5. #include "g_navigator.h"
  6. //Global navigator
  7. //CNavigator navigator;
  8. extern qboolean G_EntIsUnlockedDoor( int entityNum );
  9. extern qboolean G_EntIsDoor( int entityNum );
  10. extern qboolean G_EntIsRemovableUsable( int entNum );
  11. extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result );
  12. extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
  13. //For debug graphics
  14. extern void CG_Line( vec3_t start, vec3_t end, vec3_t color, float alpha );
  15. extern void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha );
  16. extern void CG_CubeOutline( vec3_t mins, vec3_t maxs, int time, unsigned int color, float alpha );
  17. extern qboolean FlyingCreature( gentity_t *ent );
  18. extern vec3_t NPCDEBUG_RED;
  19. /*
  20. -------------------------
  21. NPC_SetMoveGoal
  22. -------------------------
  23. */
  24. void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGoal, int combatPoint, gentity_t *targetEnt )
  25. {
  26. //Must be an NPC
  27. if ( ent->NPC == NULL )
  28. {
  29. return;
  30. }
  31. if ( ent->NPC->tempGoal == NULL )
  32. {//must still have a goal
  33. return;
  34. }
  35. //Copy the origin
  36. //VectorCopy( point, ent->NPC->goalPoint ); //FIXME: Make it use this, and this alone!
  37. VectorCopy( point, ent->NPC->tempGoal->currentOrigin );
  38. //Copy the mins and maxs to the tempGoal
  39. VectorCopy( ent->mins, ent->NPC->tempGoal->mins );
  40. VectorCopy( ent->mins, ent->NPC->tempGoal->maxs );
  41. //FIXME: TESTING let's try making sure the tempGoal isn't stuck in the ground?
  42. if ( 0 )
  43. {
  44. trace_t trace;
  45. vec3_t bottom = {ent->NPC->tempGoal->currentOrigin[0],ent->NPC->tempGoal->currentOrigin[1],ent->NPC->tempGoal->currentOrigin[2]+ent->NPC->tempGoal->mins[2]};
  46. gi.trace( &trace, ent->NPC->tempGoal->currentOrigin, vec3_origin, vec3_origin, bottom, ent->s.number, ent->clipmask );
  47. if ( trace.fraction < 1.0f )
  48. {//in the ground, raise it up
  49. ent->NPC->tempGoal->currentOrigin[2] -= ent->NPC->tempGoal->mins[2]*(1.0f-trace.fraction)-0.125f;
  50. }
  51. }
  52. ent->NPC->tempGoal->target = NULL;
  53. ent->NPC->tempGoal->clipmask = ent->clipmask;
  54. ent->NPC->tempGoal->svFlags &= ~SVF_NAVGOAL;
  55. if ( targetEnt && targetEnt->waypoint >= 0 )
  56. {
  57. ent->NPC->tempGoal->waypoint = targetEnt->waypoint;
  58. }
  59. else
  60. {
  61. ent->NPC->tempGoal->waypoint = WAYPOINT_NONE;
  62. }
  63. ent->NPC->tempGoal->noWaypointTime = 0;
  64. if ( isNavGoal )
  65. {
  66. assert(ent->NPC->tempGoal->owner);
  67. ent->NPC->tempGoal->svFlags |= SVF_NAVGOAL;
  68. }
  69. ent->NPC->tempGoal->combatPoint = combatPoint;
  70. ent->NPC->tempGoal->enemy = targetEnt;
  71. ent->NPC->goalEntity = ent->NPC->tempGoal;
  72. ent->NPC->goalRadius = radius;
  73. ent->NPC->aiFlags &= ~NPCAI_STOP_AT_LOS;
  74. gi.linkentity( ent->NPC->goalEntity );
  75. }
  76. /*
  77. -------------------------
  78. waypoint_testDirection
  79. -------------------------
  80. */
  81. static float waypoint_testDirection( vec3_t origin, float yaw, float minDist )
  82. {
  83. vec3_t trace_dir, test_pos;
  84. vec3_t maxs, mins;
  85. trace_t tr;
  86. //Setup the mins and max
  87. VectorSet( maxs, DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2 );
  88. VectorSet( mins, DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2 + STEPSIZE );
  89. //Get our test direction
  90. vec3_t angles = { 0, yaw, 0 };
  91. AngleVectors( angles, trace_dir, NULL, NULL );
  92. //Move ahead
  93. VectorMA( origin, minDist, trace_dir, test_pos );
  94. gi.trace( &tr, origin, mins, maxs, test_pos, ENTITYNUM_NONE, ( CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ) );
  95. return ( minDist * tr.fraction ); //return actual dist completed
  96. }
  97. /*
  98. -------------------------
  99. waypoint_getRadius
  100. -------------------------
  101. */
  102. static float waypoint_getRadius( gentity_t *ent )
  103. {
  104. float minDist = MAX_RADIUS_CHECK + 1; // (unsigned int) -1;
  105. float dist;
  106. for ( int i = 0; i < YAW_ITERATIONS; i++ )
  107. {
  108. dist = waypoint_testDirection( ent->currentOrigin, ((360.0f/YAW_ITERATIONS) * i), minDist );
  109. if ( dist < minDist )
  110. minDist = dist;
  111. }
  112. return minDist + DEFAULT_MAXS_0;
  113. }
  114. /*QUAKED waypoint (0.7 0.7 0) (-20 -20 -24) (20 20 45) SOLID_OK DROP_TO_FLOOR
  115. a place to go.
  116. SOLID_OK - only use if placing inside solid is unavoidable in map, but may be clear in-game (ie: at the bottom of a tall, solid lift that starts at the top position)
  117. DROP_TO_FLOOR - will cause the point to auto drop to the floor
  118. radius is automatically calculated in-world.
  119. "targetJump" is a special edge that only guys who can jump will cross (so basically Jedi)
  120. */
  121. extern int delayedShutDown;
  122. void SP_waypoint ( gentity_t *ent )
  123. {
  124. VectorSet(ent->mins, DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2);
  125. VectorSet(ent->maxs, DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2);
  126. ent->contents = CONTENTS_TRIGGER;
  127. ent->clipmask = MASK_DEADSOLID;
  128. gi.linkentity( ent );
  129. ent->count = -1;
  130. ent->classname = "waypoint";
  131. if (ent->spawnflags&2)
  132. {
  133. ent->currentOrigin[2] += 128.0f;
  134. }
  135. if( !(ent->spawnflags&1) && G_CheckInSolid (ent, qtrue))
  136. {//if not SOLID_OK, and in solid
  137. ent->maxs[2] = CROUCH_MAXS_2;
  138. if(G_CheckInSolid (ent, qtrue))
  139. {
  140. gi.Printf(S_COLOR_RED"ERROR: Waypoint %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  141. assert(0 && "Waypoint in solid!");
  142. // if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region
  143. // G_Error("Waypoint %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  144. // }
  145. delayedShutDown = level.time + 100;
  146. G_FreeEntity(ent);
  147. return;
  148. }
  149. }
  150. //G_SpawnString("targetJump", "", &ent->targetJump);
  151. ent->radius = waypoint_getRadius( ent );
  152. NAV::SpawnedPoint(ent);
  153. G_FreeEntity(ent);
  154. return;
  155. }
  156. /*QUAKED waypoint_small (0.7 0.7 0) (-2 -2 -24) (2 2 32) SOLID_OK
  157. SOLID_OK - only use if placing inside solid is unavoidable in map, but may be clear in-game (ie: at the bottom of a tall, solid lift that starts at the top position)
  158. DROP_TO_FLOOR - will cause the point to auto drop to the floor
  159. */
  160. void SP_waypoint_small (gentity_t *ent)
  161. {
  162. VectorSet(ent->mins, -2, -2, DEFAULT_MINS_2);
  163. VectorSet(ent->maxs, 2, 2, DEFAULT_MAXS_2);
  164. ent->contents = CONTENTS_TRIGGER;
  165. ent->clipmask = MASK_DEADSOLID;
  166. gi.linkentity( ent );
  167. ent->count = -1;
  168. ent->classname = "waypoint";
  169. if ( !(ent->spawnflags&1) && G_CheckInSolid( ent, qtrue ) )
  170. {
  171. ent->maxs[2] = CROUCH_MAXS_2;
  172. if ( G_CheckInSolid( ent, qtrue ) )
  173. {
  174. gi.Printf(S_COLOR_RED"ERROR: Waypoint_small %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  175. assert(0);
  176. #ifndef FINAL_BUILD
  177. if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region
  178. G_Error("Waypoint_small %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  179. }
  180. #endif
  181. G_FreeEntity(ent);
  182. return;
  183. }
  184. }
  185. ent->radius = 2; // radius
  186. NAV::SpawnedPoint(ent);
  187. G_FreeEntity(ent);
  188. return;
  189. }
  190. /*QUAKED waypoint_navgoal (0.3 1 0.3) (-20 -20 -24) (20 20 40) SOLID_OK DROP_TO_FLOOR NO_AUTO_CONNECT
  191. A waypoint for script navgoals
  192. Not included in navigation data
  193. DROP_TO_FLOOR - will cause the point to auto drop to the floor
  194. NO_AUTO_CONNECT - will not automatically connect to any other points, you must then connect it by hand
  195. SOLID_OK - only use if placing inside solid is unavoidable in map, but may be clear in-game (ie: at the bottom of a tall, solid lift that starts at the top position)
  196. targetname - name you would use in script when setting a navgoal (like so:)
  197. For example: if you give this waypoint a targetname of "console", make an NPC go to it in a script like so:
  198. set ("navgoal", "console");
  199. radius - how far from the navgoal an ent can be before it thinks it reached it - default is "0" which means no radius check, just have to touch it
  200. */
  201. void SP_waypoint_navgoal( gentity_t *ent )
  202. {
  203. int radius = ( ent->radius ) ? (ent->radius) : 12;
  204. VectorSet( ent->mins, -16, -16, -24 );
  205. VectorSet( ent->maxs, 16, 16, 32 );
  206. ent->s.origin[2] += 0.125;
  207. if ( !(ent->spawnflags&1) && G_CheckInSolid( ent, qfalse ) )
  208. {
  209. gi.Printf(S_COLOR_RED"ERROR: Waypoint_navgoal %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  210. assert(0);
  211. #ifndef FINAL_BUILD
  212. if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region
  213. G_Error("Waypoint_navgoal %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
  214. }
  215. #endif
  216. }
  217. TAG_Add( ent->targetname, NULL, ent->s.origin, ent->s.angles, radius, RTF_NAVGOAL );
  218. ent->classname = "navgoal";
  219. NAV::SpawnedPoint(ent, NAV::PT_GOALNODE);
  220. G_FreeEntity( ent );//can't do this, they need to be found later by some functions, though those could be fixed, maybe?
  221. }
  222. /*
  223. -------------------------
  224. Svcmd_Nav_f
  225. -------------------------
  226. */
  227. void Svcmd_Nav_f( void )
  228. {
  229. char *cmd = gi.argv( 1 );
  230. if ( Q_stricmp( cmd, "show" ) == 0 )
  231. {
  232. cmd = gi.argv( 2 );
  233. if ( Q_stricmp( cmd, "all" ) == 0 )
  234. {
  235. NAVDEBUG_showNodes = !NAVDEBUG_showNodes;
  236. //NOTENOTE: This causes the two states to sync up if they aren't already
  237. NAVDEBUG_showCollision = NAVDEBUG_showNavGoals =
  238. NAVDEBUG_showCombatPoints = NAVDEBUG_showEnemyPath =
  239. NAVDEBUG_showEdges = NAVDEBUG_showNearest = NAVDEBUG_showRadius = NAVDEBUG_showNodes;
  240. }
  241. else if ( Q_stricmp( cmd, "nodes" ) == 0 )
  242. {
  243. NAVDEBUG_showNodes = !NAVDEBUG_showNodes;
  244. }
  245. else if ( Q_stricmp( cmd, "radius" ) == 0 )
  246. {
  247. NAVDEBUG_showRadius = !NAVDEBUG_showRadius;
  248. }
  249. else if ( Q_stricmp( cmd, "edges" ) == 0 )
  250. {
  251. NAVDEBUG_showEdges = !NAVDEBUG_showEdges;
  252. }
  253. else if ( Q_stricmp( cmd, "testpath" ) == 0 )
  254. {
  255. NAVDEBUG_showTestPath = !NAVDEBUG_showTestPath;
  256. }
  257. else if ( Q_stricmp( cmd, "enemypath" ) == 0 )
  258. {
  259. NAVDEBUG_showEnemyPath = !NAVDEBUG_showEnemyPath;
  260. }
  261. else if ( Q_stricmp( cmd, "combatpoints" ) == 0 )
  262. {
  263. NAVDEBUG_showCombatPoints = !NAVDEBUG_showCombatPoints;
  264. }
  265. else if ( Q_stricmp( cmd, "navgoals" ) == 0 )
  266. {
  267. NAVDEBUG_showNavGoals = !NAVDEBUG_showNavGoals;
  268. }
  269. else if ( Q_stricmp( cmd, "collision" ) == 0 )
  270. {
  271. NAVDEBUG_showCollision = !NAVDEBUG_showCollision;
  272. }
  273. else if ( Q_stricmp( cmd, "grid" ) == 0 )
  274. {
  275. NAVDEBUG_showGrid = !NAVDEBUG_showGrid;
  276. }
  277. else if ( Q_stricmp( cmd, "nearest" ) == 0 )
  278. {
  279. NAVDEBUG_showNearest = !NAVDEBUG_showNearest;
  280. }
  281. else if ( Q_stricmp( cmd, "lines" ) == 0 )
  282. {
  283. NAVDEBUG_showPointLines = !NAVDEBUG_showPointLines;
  284. }
  285. }
  286. else if ( Q_stricmp( cmd, "set" ) == 0 )
  287. {
  288. cmd = gi.argv( 2 );
  289. if ( Q_stricmp( cmd, "testgoal" ) == 0 )
  290. {
  291. // NAVDEBUG_curGoal = navigator.GetNearestNode( &g_entities[0], g_entities[0].waypoint, NF_CLEAR_PATH, WAYPOINT_NONE );
  292. }
  293. }
  294. else if ( Q_stricmp( cmd, "goto" ) == 0 )
  295. {
  296. cmd = gi.argv( 2 );
  297. NAV::TeleportTo(&(g_entities[0]), cmd);
  298. }
  299. else if ( Q_stricmp( cmd, "gotonum" ) == 0 )
  300. {
  301. cmd = gi.argv( 2 );
  302. NAV::TeleportTo(&(g_entities[0]), atoi(cmd));
  303. }
  304. else if ( Q_stricmp( cmd, "totals" ) == 0 )
  305. {
  306. NAV::ShowStats();
  307. }
  308. else
  309. {
  310. //Print the available commands
  311. Com_Printf("nav - valid commands\n---\n" );
  312. Com_Printf("show\n - nodes\n - edges\n - testpath\n - enemypath\n - combatpoints\n - navgoals\n---\n");
  313. Com_Printf("goto\n ---\n" );
  314. Com_Printf("gotonum\n ---\n" );
  315. Com_Printf("totals\n ---\n" );
  316. Com_Printf("set\n - testgoal\n---\n" );
  317. }
  318. }
  319. //
  320. //JWEIER ADDITIONS START
  321. bool navCalculatePaths = false;
  322. bool NAVDEBUG_showNodes = false;
  323. bool NAVDEBUG_showRadius = false;
  324. bool NAVDEBUG_showEdges = false;
  325. bool NAVDEBUG_showTestPath = false;
  326. bool NAVDEBUG_showEnemyPath = false;
  327. bool NAVDEBUG_showCombatPoints = false;
  328. bool NAVDEBUG_showNavGoals = false;
  329. bool NAVDEBUG_showCollision = false;
  330. int NAVDEBUG_curGoal = 0;
  331. bool NAVDEBUG_showGrid = false;
  332. bool NAVDEBUG_showNearest = false;
  333. bool NAVDEBUG_showPointLines = false;
  334. //
  335. //JWEIER ADDITIONS END