1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 #include "b_local.h"
24 #include "g_nav.h"
25 #include "g_navigator.h"
26 
27 //Global navigator
28 //CNavigator		navigator;
29 
30 extern qboolean G_EntIsUnlockedDoor( int entityNum );
31 extern qboolean G_EntIsDoor( int entityNum );
32 extern qboolean G_EntIsRemovableUsable( int entNum );
33 extern qboolean G_FindClosestPointOnLineSegment( const vec3_t start, const vec3_t end, const vec3_t from, vec3_t result );
34 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
35 //For debug graphics
36 extern void CG_Line( vec3_t start, vec3_t end, vec3_t color, float alpha );
37 extern void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha );
38 extern void CG_CubeOutline( vec3_t mins, vec3_t maxs, int time, unsigned int color, float alpha );
39 extern qboolean FlyingCreature( gentity_t *ent );
40 
41 
42 extern vec3_t NPCDEBUG_RED;
43 
44 
45 /*
46 -------------------------
47 NPC_SetMoveGoal
48 -------------------------
49 */
50 
NPC_SetMoveGoal(gentity_t * ent,vec3_t point,int radius,qboolean isNavGoal,int combatPoint,gentity_t * targetEnt)51 void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGoal, int combatPoint, gentity_t *targetEnt )
52 {
53 	//Must be an NPC
54 	if ( ent->NPC == NULL )
55 	{
56 		return;
57 	}
58 
59 	if ( ent->NPC->tempGoal == NULL )
60 	{//must still have a goal
61 		return;
62 	}
63 
64 	//Copy the origin
65 	//VectorCopy( point, ent->NPC->goalPoint );	//FIXME: Make it use this, and this alone!
66 	VectorCopy( point, ent->NPC->tempGoal->currentOrigin );
67 
68 	//Copy the mins and maxs to the tempGoal
69 	VectorCopy( ent->mins, ent->NPC->tempGoal->mins );
70 	VectorCopy( ent->mins, ent->NPC->tempGoal->maxs );
71 
72 	//FIXME: TESTING let's try making sure the tempGoal isn't stuck in the ground?
73 	if ( 0 )
74 	{
75 		trace_t	trace;
76 		vec3_t	bottom = {ent->NPC->tempGoal->currentOrigin[0],ent->NPC->tempGoal->currentOrigin[1],ent->NPC->tempGoal->currentOrigin[2]+ent->NPC->tempGoal->mins[2]};
77 		gi.trace( &trace, ent->NPC->tempGoal->currentOrigin, vec3_origin, vec3_origin, bottom, ent->s.number, ent->clipmask, (EG2_Collision)0, 0 );
78 		if ( trace.fraction < 1.0f )
79 		{//in the ground, raise it up
80 			ent->NPC->tempGoal->currentOrigin[2] -= ent->NPC->tempGoal->mins[2]*(1.0f-trace.fraction)-0.125f;
81 		}
82 	}
83 
84 	ent->NPC->tempGoal->target = NULL;
85 	ent->NPC->tempGoal->clipmask = ent->clipmask;
86 	ent->NPC->tempGoal->svFlags &= ~SVF_NAVGOAL;
87 	if ( targetEnt && targetEnt->waypoint >= 0 )
88 	{
89 		ent->NPC->tempGoal->waypoint = targetEnt->waypoint;
90 	}
91 	else
92 	{
93 		ent->NPC->tempGoal->waypoint = WAYPOINT_NONE;
94 	}
95 	ent->NPC->tempGoal->noWaypointTime = 0;
96 
97 	if ( isNavGoal )
98 	{
99 		assert(ent->NPC->tempGoal->owner);
100 		ent->NPC->tempGoal->svFlags |= SVF_NAVGOAL;
101 	}
102 
103 	ent->NPC->tempGoal->combatPoint = combatPoint;
104 	ent->NPC->tempGoal->enemy = targetEnt;
105 
106 	ent->NPC->goalEntity = ent->NPC->tempGoal;
107 	ent->NPC->goalRadius = radius;
108 	ent->NPC->aiFlags	&= ~NPCAI_STOP_AT_LOS;
109 
110 	gi.linkentity( ent->NPC->goalEntity );
111 }
112 /*
113 -------------------------
114 waypoint_testDirection
115 -------------------------
116 */
117 
waypoint_testDirection(vec3_t origin,float yaw,float minDist)118 static float waypoint_testDirection( vec3_t origin, float yaw, float minDist )
119 {
120 	vec3_t	trace_dir, test_pos;
121 	vec3_t	maxs, mins;
122 	trace_t	tr;
123 
124 	//Setup the mins and max
125 	VectorSet( maxs, DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2 );
126 	VectorSet( mins, DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2 + STEPSIZE );
127 
128 	//Get our test direction
129 	vec3_t	angles = { 0, yaw, 0 };
130 	AngleVectors( angles, trace_dir, NULL, NULL );
131 
132 	//Move ahead
133 	VectorMA( origin, minDist, trace_dir, test_pos );
134 
135 	gi.trace( &tr, origin, mins, maxs, test_pos, ENTITYNUM_NONE, ( CONTENTS_SOLID | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ), (EG2_Collision)0, 0 );
136 
137 	return ( minDist * tr.fraction );	//return actual dist completed
138 }
139 
140 /*
141 -------------------------
142 waypoint_getRadius
143 -------------------------
144 */
145 
waypoint_getRadius(gentity_t * ent)146 static float waypoint_getRadius( gentity_t *ent )
147 {
148 	float	minDist = MAX_RADIUS_CHECK + 1; // (unsigned int) -1;
149 	float	dist;
150 
151 	for ( int i = 0; i < YAW_ITERATIONS; i++ )
152 	{
153 		dist = waypoint_testDirection( ent->currentOrigin, ((360.0f/YAW_ITERATIONS) * i), minDist );
154 
155 		if ( dist < minDist )
156 			minDist = dist;
157 	}
158 
159 	return minDist + DEFAULT_MAXS_0;
160 }
161 
162 /*QUAKED waypoint  (0.7 0.7 0) (-20 -20 -24) (20 20 45) SOLID_OK DROP_TO_FLOOR
163 a place to go.
164 
165 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)
166 DROP_TO_FLOOR - will cause the point to auto drop to the floor
167 
168 radius is automatically calculated in-world.
169 "targetJump" is a special edge that only guys who can jump will cross (so basically Jedi)
170 */
171 extern int delayedShutDown;
SP_waypoint(gentity_t * ent)172 void SP_waypoint ( gentity_t *ent )
173 {
174 		VectorSet(ent->mins, DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2);
175 		VectorSet(ent->maxs, DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2);
176 
177 		ent->contents = CONTENTS_TRIGGER;
178 		ent->clipmask = MASK_DEADSOLID;
179 
180 		gi.linkentity( ent );
181 
182 		ent->count = -1;
183 		ent->classname = "waypoint";
184 
185 		if (ent->spawnflags&2)
186 		{
187 			ent->currentOrigin[2] += 128.0f;
188 		}
189 
190 		if( !(ent->spawnflags&1) && G_CheckInSolid (ent, qtrue))
191 		{//if not SOLID_OK, and in solid
192 			ent->maxs[2] = CROUCH_MAXS_2;
193 			if(G_CheckInSolid (ent, qtrue))
194 			{
195 				gi.Printf(S_COLOR_RED"ERROR: Waypoint %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
196 				assert(0 && "Waypoint in solid!");
197 //				if (!g_entities[ENTITYNUM_WORLD].s.radius){	//not a region
198 //					G_Error("Waypoint %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
199 //				}
200 				delayedShutDown = level.time + 100;
201 				G_FreeEntity(ent);
202 				return;
203 			}
204 		}
205 
206 		//G_SpawnString("targetJump", "", &ent->targetJump);
207 		ent->radius = waypoint_getRadius( ent );
208 		NAV::SpawnedPoint(ent);
209 
210 		G_FreeEntity(ent);
211 		return;
212 }
213 
214 /*QUAKED waypoint_small  (0.7 0.7 0) (-2 -2 -24) (2 2 32) SOLID_OK
215 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)
216 DROP_TO_FLOOR - will cause the point to auto drop to the floor
217 */
SP_waypoint_small(gentity_t * ent)218 void SP_waypoint_small (gentity_t *ent)
219 {
220 		VectorSet(ent->mins, -2, -2, DEFAULT_MINS_2);
221 		VectorSet(ent->maxs, 2, 2, DEFAULT_MAXS_2);
222 
223 		ent->contents = CONTENTS_TRIGGER;
224 		ent->clipmask = MASK_DEADSOLID;
225 
226 		gi.linkentity( ent );
227 
228 		ent->count = -1;
229 		ent->classname = "waypoint";
230 
231 		if ( !(ent->spawnflags&1) && G_CheckInSolid( ent, qtrue ) )
232 		{
233 			ent->maxs[2] = CROUCH_MAXS_2;
234 			if ( G_CheckInSolid( ent, qtrue ) )
235 			{
236 				gi.Printf(S_COLOR_RED"ERROR: Waypoint_small %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
237 				assert(0);
238 #ifndef FINAL_BUILD
239 				if (!g_entities[ENTITYNUM_WORLD].s.radius){	//not a region
240 					G_Error("Waypoint_small %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
241 				}
242 #endif
243 				G_FreeEntity(ent);
244 				return;
245 			}
246 		}
247 
248 		ent->radius = 2;	// radius
249 		NAV::SpawnedPoint(ent);
250 
251 		G_FreeEntity(ent);
252 		return;
253 }
254 
255 
256 /*QUAKED waypoint_navgoal (0.3 1 0.3) (-20 -20 -24) (20 20 40) SOLID_OK DROP_TO_FLOOR NO_AUTO_CONNECT
257 A waypoint for script navgoals
258 Not included in navigation data
259 
260 DROP_TO_FLOOR - will cause the point to auto drop to the floor
261 NO_AUTO_CONNECT - will not automatically connect to any other points, you must then connect it by hand
262 
263 
264 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)
265 
266 targetname - name you would use in script when setting a navgoal (like so:)
267 
268   For example: if you give this waypoint a targetname of "console", make an NPC go to it in a script like so:
269 
270   set ("navgoal", "console");
271 
272 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
273 
274 */
275 
SP_waypoint_navgoal(gentity_t * ent)276 void SP_waypoint_navgoal( gentity_t *ent )
277 {
278 	int radius = ( ent->radius ) ? (ent->radius) : 12;
279 
280 	VectorSet( ent->mins, -16, -16, -24 );
281 	VectorSet( ent->maxs, 16, 16, 32 );
282 	ent->s.origin[2] += 0.125;
283 	if ( !(ent->spawnflags&1) && G_CheckInSolid( ent, qfalse ) )
284 	{
285 		gi.Printf(S_COLOR_RED"ERROR: Waypoint_navgoal %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
286 		assert(0);
287 #ifndef FINAL_BUILD
288 		if (!g_entities[ENTITYNUM_WORLD].s.radius){	//not a region
289 			G_Error("Waypoint_navgoal %s at %s in solid!\n", ent->targetname, vtos(ent->currentOrigin));
290 		}
291 #endif
292 	}
293 	TAG_Add( ent->targetname, NULL, ent->s.origin, ent->s.angles, radius, RTF_NAVGOAL );
294 
295 	ent->classname = "navgoal";
296 
297 	NAV::SpawnedPoint(ent, NAV::PT_GOALNODE);
298 
299 	G_FreeEntity( ent );//can't do this, they need to be found later by some functions, though those could be fixed, maybe?
300 }
301 
302 /*
303 -------------------------
304 Svcmd_Nav_f
305 -------------------------
306 */
307 
Svcmd_Nav_f(void)308 void Svcmd_Nav_f( void )
309 {
310 	const char	*cmd = gi.argv( 1 );
311 
312 	if ( Q_stricmp( cmd, "show" ) == 0 )
313 	{
314 		cmd = gi.argv( 2 );
315 
316 		if ( Q_stricmp( cmd, "all" ) == 0 )
317 		{
318 			NAVDEBUG_showNodes = !NAVDEBUG_showNodes;
319 
320 			//NOTENOTE: This causes the two states to sync up if they aren't already
321 			NAVDEBUG_showCollision = NAVDEBUG_showNavGoals =
322 			NAVDEBUG_showCombatPoints = NAVDEBUG_showEnemyPath =
323 			NAVDEBUG_showEdges = NAVDEBUG_showNearest = NAVDEBUG_showRadius = NAVDEBUG_showNodes;
324 		}
325 		else if ( Q_stricmp( cmd, "nodes" ) == 0 )
326 		{
327 			NAVDEBUG_showNodes = !NAVDEBUG_showNodes;
328 		}
329 		else if ( Q_stricmp( cmd, "radius" ) == 0 )
330 		{
331 			NAVDEBUG_showRadius = !NAVDEBUG_showRadius;
332 		}
333 		else if ( Q_stricmp( cmd, "edges" ) == 0 )
334 		{
335 			NAVDEBUG_showEdges = !NAVDEBUG_showEdges;
336 		}
337 		else if ( Q_stricmp( cmd, "testpath" ) == 0 )
338 		{
339 			NAVDEBUG_showTestPath = !NAVDEBUG_showTestPath;
340 		}
341 		else if ( Q_stricmp( cmd, "enemypath" ) == 0 )
342 		{
343 			NAVDEBUG_showEnemyPath = !NAVDEBUG_showEnemyPath;
344 		}
345 		else if ( Q_stricmp( cmd, "combatpoints" ) == 0 )
346 		{
347 			NAVDEBUG_showCombatPoints = !NAVDEBUG_showCombatPoints;
348 		}
349 		else if ( Q_stricmp( cmd, "navgoals" ) == 0 )
350 		{
351 			NAVDEBUG_showNavGoals = !NAVDEBUG_showNavGoals;
352 		}
353 		else if ( Q_stricmp( cmd, "collision" ) == 0 )
354 		{
355 			NAVDEBUG_showCollision = !NAVDEBUG_showCollision;
356 		}
357 		else if ( Q_stricmp( cmd, "grid" ) == 0 )
358 		{
359 			NAVDEBUG_showGrid = !NAVDEBUG_showGrid;
360 		}
361 		else if ( Q_stricmp( cmd, "nearest" ) == 0 )
362 		{
363 			NAVDEBUG_showNearest = !NAVDEBUG_showNearest;
364 		}
365 		else if ( Q_stricmp( cmd, "lines" ) == 0 )
366 		{
367 			NAVDEBUG_showPointLines = !NAVDEBUG_showPointLines;
368 		}
369 	}
370 	else if ( Q_stricmp( cmd, "set" ) == 0 )
371 	{
372 		cmd = gi.argv( 2 );
373 
374 		if ( Q_stricmp( cmd, "testgoal" ) == 0 )
375 		{
376 		//	NAVDEBUG_curGoal = navigator.GetNearestNode( &g_entities[0], g_entities[0].waypoint, NF_CLEAR_PATH, WAYPOINT_NONE );
377 		}
378 	}
379 	else if ( Q_stricmp( cmd, "goto" ) == 0 )
380 	{
381 		cmd = gi.argv( 2 );
382 		NAV::TeleportTo(&(g_entities[0]), cmd);
383 	}
384 	else if ( Q_stricmp( cmd, "gotonum" ) == 0 )
385 	{
386 		cmd = gi.argv( 2 );
387 		NAV::TeleportTo(&(g_entities[0]), atoi(cmd));
388 	}
389 	else if ( Q_stricmp( cmd, "totals" ) == 0 )
390 	{
391 		NAV::ShowStats();
392 	}
393 	else
394 	{
395 		//Print the available commands
396 		Com_Printf("nav - valid commands\n---\n" );
397 		Com_Printf("show\n - nodes\n - edges\n - testpath\n - enemypath\n - combatpoints\n - navgoals\n---\n");
398 		Com_Printf("goto\n ---\n" );
399 		Com_Printf("gotonum\n ---\n" );
400 		Com_Printf("totals\n ---\n" );
401 		Com_Printf("set\n - testgoal\n---\n" );
402 	}
403 }
404 
405 //
406 //JWEIER ADDITIONS START
407 
408 bool	navCalculatePaths	= false;
409 
410 bool	NAVDEBUG_showNodes			= false;
411 bool	NAVDEBUG_showRadius			= false;
412 bool	NAVDEBUG_showEdges			= false;
413 bool	NAVDEBUG_showTestPath		= false;
414 bool	NAVDEBUG_showEnemyPath		= false;
415 bool	NAVDEBUG_showCombatPoints	= false;
416 bool	NAVDEBUG_showNavGoals		= false;
417 bool	NAVDEBUG_showCollision		= false;
418 int		NAVDEBUG_curGoal			= 0;
419 bool	NAVDEBUG_showGrid			= false;
420 bool	NAVDEBUG_showNearest		= false;
421 bool	NAVDEBUG_showPointLines		= false;
422 
423 
424 //
425 //JWEIER ADDITIONS END
426