2004-08-23 00:15:46 +00:00
/*
Copyright ( C ) 1996 - 1997 Id Software , Inc .
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
// sv_move.c -- monster movement
2009-11-17 00:15:44 +00:00
# include "quakedef.h"
# include "pr_common.h"
2004-08-23 00:15:46 +00:00
2009-11-17 00:15:44 +00:00
# if defined(CSQC_DAT) || !defined(CLIENTONLY)
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = =
SV_CheckBottom
Returns false if any part of the bottom of the entity is off an edge that
is not a staircase .
= = = = = = = = = = = = =
*/
2018-11-19 06:37:25 +00:00
//int c_yes, c_no;
2004-08-23 00:15:46 +00:00
2014-06-21 17:58:17 +00:00
hull_t * Q1BSP_ChooseHull ( model_t * model , int hullnum , vec3_t mins , vec3_t maxs , vec3_t offset ) ;
//this function is axial. major axis determines ground. if it switches slightly, a new axis may become the ground...
qboolean World_CheckBottom ( world_t * world , wedict_t * ent , vec3_t up )
2004-08-23 00:15:46 +00:00
{
vec3_t mins , maxs , start , stop ;
trace_t trace ;
int x , y ;
2014-06-21 17:58:17 +00:00
float mid ;
int a0 , a1 , a2 ; //logical x, y, z
int sign ;
mins [ 0 ] = fabs ( up [ 0 ] ) ;
mins [ 1 ] = fabs ( up [ 1 ] ) ;
mins [ 2 ] = fabs ( up [ 2 ] ) ;
if ( mins [ 2 ] > mins [ 0 ] & & mins [ 2 ] > mins [ 1 ] )
{
a0 = 0 ;
a1 = 1 ;
a2 = 2 ;
}
else
{
a2 = mins [ 1 ] > mins [ 0 ] ;
a0 = 1 - a2 ;
a1 = 2 ;
}
sign = ( up [ a2 ] > 0 ) ? 1 : - 1 ;
2004-08-23 00:15:46 +00:00
2005-03-28 00:11:59 +00:00
VectorAdd ( ent - > v - > origin , ent - > v - > mins , mins ) ;
2017-01-13 00:39:50 +00:00
# ifdef Q1BSPS
if ( world - > worldmodel - > fromgame = = fg_quake | | world - > worldmodel - > fromgame = = fg_halflife )
2014-06-21 17:58:17 +00:00
{
//quake's hulls are weird. sizes are defined as from mins to mins+hullsize. the actual maxs is ignored other than for its size.
hull_t * hull ;
hull = Q1BSP_ChooseHull ( world - > worldmodel , ent - > xv - > hull , ent - > v - > mins , ent - > v - > maxs , start ) ;
2023-06-05 21:41:59 +00:00
//ignore the hull's offset. the minpoint is the minpoint. lets fix up the size though, just in case.
2014-06-21 17:58:17 +00:00
VectorSubtract ( mins , hull - > clip_mins , maxs ) ;
VectorAdd ( maxs , hull - > clip_maxs , maxs ) ;
}
else
2017-01-13 00:39:50 +00:00
# endif
2014-06-21 17:58:17 +00:00
VectorAdd ( ent - > v - > origin , ent - > v - > maxs , maxs ) ;
2004-08-23 00:15:46 +00:00
// if all of the points under the corners are solid world, don't bother
// with the tougher checks
// the corners must be within 16 of the midpoint
2014-06-21 17:58:17 +00:00
start [ a2 ] = ( sign < 0 ) ? maxs [ a2 ] : mins [ a2 ] - sign ;
2004-08-23 00:15:46 +00:00
for ( x = 0 ; x < = 1 ; x + + )
for ( y = 0 ; y < = 1 ; y + + )
{
2014-06-21 17:58:17 +00:00
start [ a0 ] = x ? maxs [ a0 ] : mins [ a0 ] ;
start [ a1 ] = y ? maxs [ a1 ] : mins [ a1 ] ;
2019-09-17 19:49:39 +00:00
if ( ! ( World_PointContentsWorldOnly ( world , start ) & FTECONTENTS_SOLID ) )
2004-08-23 00:15:46 +00:00
goto realcheck ;
}
2018-11-19 06:37:25 +00:00
// c_yes++;
2004-08-23 00:15:46 +00:00
return true ; // we got out easy
realcheck :
2018-11-19 06:37:25 +00:00
// c_no++;
2004-08-23 00:15:46 +00:00
//
// check it for real...
//
2014-06-21 17:58:17 +00:00
start [ a2 ] = ( sign < 0 ) ? maxs [ a2 ] : mins [ a2 ] ;
2004-08-23 00:15:46 +00:00
// the midpoint must be within 16 of the bottom
2014-06-21 17:58:17 +00:00
start [ a0 ] = stop [ a0 ] = ( mins [ a0 ] + maxs [ a0 ] ) * 0.5 ;
start [ a1 ] = stop [ a1 ] = ( mins [ a1 ] + maxs [ a1 ] ) * 0.5 ;
stop [ a2 ] = start [ a2 ] - 2 * movevars . stepheight * sign ;
2016-07-12 00:40:13 +00:00
trace = World_Move ( world , start , vec3_origin , vec3_origin , stop , true | MOVE_IGNOREHULL , ent ) ;
2004-08-23 00:15:46 +00:00
if ( trace . fraction = = 1.0 )
return false ;
2016-07-12 00:40:13 +00:00
2014-06-21 17:58:17 +00:00
mid = trace . endpos [ 2 ] ;
mid = ( mid - start [ a2 ] - ( movevars . stepheight * sign ) ) / ( stop [ a2 ] - start [ a2 ] ) ;
2004-08-23 00:15:46 +00:00
// the corners must be within 16 of the midpoint
for ( x = 0 ; x < = 1 ; x + + )
for ( y = 0 ; y < = 1 ; y + + )
{
2014-06-21 17:58:17 +00:00
start [ a0 ] = stop [ a0 ] = x ? maxs [ a0 ] : mins [ a0 ] ;
start [ a1 ] = stop [ a1 ] = y ? maxs [ a1 ] : mins [ a1 ] ;
2004-08-23 00:15:46 +00:00
2016-07-12 00:40:13 +00:00
trace = World_Move ( world , start , vec3_origin , vec3_origin , stop , true | MOVE_IGNOREHULL , ent ) ;
2014-04-12 03:31:59 +00:00
2014-06-21 17:58:17 +00:00
if ( trace . fraction = = 1.0 | | trace . fraction > mid ) //mid - trace.endpos[2] > movevars.stepheight)
2004-08-23 00:15:46 +00:00
return false ;
}
2018-11-19 06:37:25 +00:00
// c_yes++;
2004-08-23 00:15:46 +00:00
return true ;
}
/*
= = = = = = = = = = = = =
SV_movestep
Called by monster program code .
The move will be adjusted for slopes and stairs , but if the move isn ' t
possible , no move is done , false is returned , and
pr_global_struct - > trace_normal is set to the normal of the blocking wall
= = = = = = = = = = = = =
*/
2018-09-23 19:35:24 +00:00
qboolean World_movestep ( world_t * world , wedict_t * ent , vec3_t move , vec3_t axis [ 3 ] , qboolean relink , qboolean noenemy , void ( * set_move_trace ) ( pubprogfuncs_t * prinst , trace_t * trace ) )
2004-08-23 00:15:46 +00:00
{
float dz ;
vec3_t oldorg , neworg , end ;
trace_t trace ;
int i ;
2009-11-17 00:15:44 +00:00
wedict_t * enemy = world - > edicts ;
2012-01-17 07:57:46 +00:00
int eflags = ent - > v - > flags ;
2014-06-21 17:58:17 +00:00
vec3_t eaxis [ 3 ] ;
if ( ! axis )
{
//fixme?
World_GetEntGravityAxis ( ent , eaxis ) ;
axis = eaxis ;
}
2012-01-17 07:57:46 +00:00
2019-01-13 16:51:50 +00:00
// try the move
2005-03-28 00:11:59 +00:00
VectorCopy ( ent - > v - > origin , oldorg ) ;
VectorAdd ( ent - > v - > origin , move , neworg ) ;
2004-08-23 00:15:46 +00:00
// flying monsters don't step up
2019-01-13 16:51:50 +00:00
if ( ( eflags & ( FL_SWIM | FL_FLY ) )
# if defined(HEXEN2) && defined(HAVE_SERVER)
//hexen2 has some extra logic for FLH2_HUNTFACE, but its buggy and thus never used.
//it would be nice to redefine the NOZ flag to instead force noenemy here, but that's not hexen2-compatible and FLH2_NOZ is bound to conflict with some quake mod.
2019-01-13 17:56:26 +00:00
& & ( world ! = & sv . world | | progstype ! = PROG_H2 | | ! ( eflags & ( FLH2_NOZ | FLH2_HUNTFACE ) ) )
2019-01-13 16:51:50 +00:00
# endif
2019-01-13 17:56:26 +00:00
)
2004-08-23 00:15:46 +00:00
{
// try one move with vertical motion, then one without
for ( i = 0 ; i < 2 ; i + + )
{
2005-03-28 00:11:59 +00:00
VectorAdd ( ent - > v - > origin , move , neworg ) ;
2004-08-23 00:15:46 +00:00
if ( ! noenemy )
{
2009-11-17 00:15:44 +00:00
enemy = ( wedict_t * ) PROG_TO_EDICT ( world - > progs , ent - > v - > enemy ) ;
if ( i = = 0 & & enemy - > entnum )
2004-08-23 00:15:46 +00:00
{
2014-06-21 17:58:17 +00:00
VectorSubtract ( ent - > v - > origin , ( ( wedict_t * ) PROG_TO_EDICT ( world - > progs , ent - > v - > enemy ) ) - > v - > origin , end ) ;
dz = DotProduct ( end , axis [ 2 ] ) ;
2004-08-23 00:15:46 +00:00
if ( dz > 40 )
2014-06-21 17:58:17 +00:00
VectorMA ( neworg , - 8 , axis [ 2 ] , neworg ) ;
2004-08-23 00:15:46 +00:00
if ( dz < 30 )
2014-06-21 17:58:17 +00:00
VectorMA ( neworg , 8 , axis [ 2 ] , neworg ) ;
2004-08-23 00:15:46 +00:00
}
}
2009-11-17 00:15:44 +00:00
trace = World_Move ( world , ent - > v - > origin , ent - > v - > mins , ent - > v - > maxs , neworg , false , ent ) ;
2010-11-21 03:39:12 +00:00
if ( set_move_trace )
2018-09-23 19:35:24 +00:00
set_move_trace ( world - > progs , & trace ) ;
2019-01-13 16:51:50 +00:00
2004-08-23 00:15:46 +00:00
if ( trace . fraction = = 1 )
{
2019-09-17 19:49:39 +00:00
if ( ( eflags & FL_SWIM ) & & ! ( World_PointContentsWorldOnly ( world , trace . endpos ) & FTECONTENTS_FLUID ) )
2010-12-18 17:02:47 +00:00
continue ; // swim monster left water
2004-08-23 00:15:46 +00:00
2005-03-28 00:11:59 +00:00
VectorCopy ( trace . endpos , ent - > v - > origin ) ;
2004-08-23 00:15:46 +00:00
if ( relink )
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2004-08-23 00:15:46 +00:00
return true ;
}
2019-01-13 16:51:50 +00:00
2009-11-17 00:15:44 +00:00
if ( noenemy | | ! enemy - > entnum )
2004-08-23 00:15:46 +00:00
break ;
}
2019-01-13 16:51:50 +00:00
2004-08-23 00:15:46 +00:00
return false ;
}
// push down from a step height above the wished position
2014-06-21 17:58:17 +00:00
VectorMA ( neworg , movevars . stepheight , axis [ 2 ] , neworg ) ;
VectorMA ( neworg , movevars . stepheight * - 2 , axis [ 2 ] , end ) ;
2004-08-23 00:15:46 +00:00
2009-11-17 00:15:44 +00:00
trace = World_Move ( world , neworg , ent - > v - > mins , ent - > v - > maxs , end , false , ent ) ;
2010-11-21 03:39:12 +00:00
if ( set_move_trace )
2018-09-23 19:35:24 +00:00
set_move_trace ( world - > progs , & trace ) ;
2004-08-23 00:15:46 +00:00
if ( trace . allsolid )
return false ;
if ( trace . startsolid )
{
2014-06-21 17:58:17 +00:00
//move up by an extra step, if needed
VectorMA ( neworg , - movevars . stepheight , axis [ 2 ] , neworg ) ;
2009-11-17 00:15:44 +00:00
trace = World_Move ( world , neworg , ent - > v - > mins , ent - > v - > maxs , end , false , ent ) ;
2010-11-21 03:39:12 +00:00
if ( set_move_trace )
2018-09-23 19:35:24 +00:00
set_move_trace ( world - > progs , & trace ) ;
2004-08-23 00:15:46 +00:00
if ( trace . allsolid | | trace . startsolid )
return false ;
}
if ( trace . fraction = = 1 )
{
// if monster had the ground pulled out, go ahead and fall
2005-03-28 00:11:59 +00:00
if ( ( int ) ent - > v - > flags & FL_PARTIALGROUND )
2004-08-23 00:15:46 +00:00
{
2005-03-28 00:11:59 +00:00
VectorAdd ( ent - > v - > origin , move , ent - > v - > origin ) ;
2004-08-23 00:15:46 +00:00
if ( relink )
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2005-03-28 00:11:59 +00:00
ent - > v - > flags = ( int ) ent - > v - > flags & ~ FL_ONGROUND ;
2004-08-23 00:15:46 +00:00
// Con_Printf ("fall down\n");
return true ;
}
return false ; // walked off an edge
}
// check point traces down for dangling corners
2005-03-28 00:11:59 +00:00
VectorCopy ( trace . endpos , ent - > v - > origin ) ;
2004-08-23 00:15:46 +00:00
2014-06-21 17:58:17 +00:00
if ( ! World_CheckBottom ( world , ent , axis [ 2 ] ) )
2004-08-23 00:15:46 +00:00
{
2005-03-28 00:11:59 +00:00
if ( ( int ) ent - > v - > flags & FL_PARTIALGROUND )
2004-08-23 00:15:46 +00:00
{ // entity had floor mostly pulled out from underneath it
// and is trying to correct
if ( relink )
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2004-08-23 00:15:46 +00:00
return true ;
}
2005-03-28 00:11:59 +00:00
VectorCopy ( oldorg , ent - > v - > origin ) ;
2004-08-23 00:15:46 +00:00
return false ;
}
2005-03-28 00:11:59 +00:00
if ( ( int ) ent - > v - > flags & FL_PARTIALGROUND )
2004-08-23 00:15:46 +00:00
{
// Con_Printf ("back on ground\n");
2005-03-28 00:11:59 +00:00
ent - > v - > flags = ( int ) ent - > v - > flags & ~ FL_PARTIALGROUND ;
2004-08-23 00:15:46 +00:00
}
2009-11-17 00:15:44 +00:00
ent - > v - > groundentity = EDICT_TO_PROG ( world - > progs , trace . ent ) ;
2004-08-23 00:15:46 +00:00
// the move is ok
if ( relink )
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2004-08-23 00:15:46 +00:00
return true ;
}
//============================================================================
2014-06-21 17:58:17 +00:00
qboolean World_GetEntGravityAxis ( wedict_t * ent , vec3_t axis [ 3 ] )
{
if ( ent - > xv - > gravitydir [ 0 ] | | ent - > xv - > gravitydir [ 1 ] | | ent - > xv - > gravitydir [ 2 ] )
{
void PerpendicularVector ( vec3_t dst , const vec3_t src ) ;
VectorNegate ( ent - > xv - > gravitydir , axis [ 2 ] ) ;
VectorNormalize ( axis [ 2 ] ) ;
PerpendicularVector ( axis [ 0 ] , axis [ 2 ] ) ;
VectorNormalize ( axis [ 0 ] ) ;
CrossProduct ( axis [ 2 ] , axis [ 0 ] , axis [ 1 ] ) ;
VectorNormalize ( axis [ 1 ] ) ;
return true ;
}
else
{
VectorSet ( axis [ 0 ] , 1 , 0 , 0 ) ;
VectorSet ( axis [ 1 ] , 0 , 1 , 0 ) ;
VectorSet ( axis [ 2 ] , 0 , 0 , 1 ) ;
return false ;
}
}
2009-11-17 00:15:44 +00:00
/*
= = = = = = = = = = = = = =
PF_changeyaw
This was a major timewaster in progs , so it was converted to C
= = = = = = = = = = = = = =
*/
2014-06-21 17:58:17 +00:00
float World_changeyaw ( wedict_t * ent )
2009-11-17 00:15:44 +00:00
{
float ideal , current , move , speed ;
2014-06-21 17:58:17 +00:00
vec3_t surf [ 3 ] ;
if ( World_GetEntGravityAxis ( ent , surf ) )
{
//complex matrix stuff
float mat [ 16 ] ;
float surfm [ 16 ] , invsurfm [ 16 ] ;
float viewm [ 16 ] ;
vec3_t view [ 4 ] ;
vec3_t vang ;
/*calc current view matrix relative to the surface*/
2020-10-06 03:17:28 +00:00
AngleVectorsMesh ( ent - > v - > angles , view [ 0 ] , view [ 1 ] , view [ 2 ] ) ;
2014-06-21 17:58:17 +00:00
VectorNegate ( view [ 1 ] , view [ 1 ] ) ;
World_GetEntGravityAxis ( ent , surf ) ;
Matrix4x4_RM_FromVectors ( surfm , surf [ 0 ] , surf [ 1 ] , surf [ 2 ] , vec3_origin ) ;
Matrix3x4_InvertTo4x4_Simple ( surfm , invsurfm ) ;
/*calc current view matrix relative to the surface*/
Matrix4x4_RM_FromVectors ( viewm , view [ 0 ] , view [ 1 ] , view [ 2 ] , vec3_origin ) ;
Matrix4_Multiply ( viewm , invsurfm , mat ) ;
/*convert that back to angles*/
Matrix3x4_RM_ToVectors ( mat , view [ 0 ] , view [ 1 ] , view [ 2 ] , view [ 3 ] ) ;
added r_meshpitch cvar that allows for fixing the unfixable mesh pitch bug from vanilla... needs a better name... do note that this will break pretty much any mod, so this is really only for TCs designed to use it. Its likely that I missed places.
nqsv: added support for spectators with nq clients. the angles are a bit rough, but hey. need to do something about frags so nq clients know who's a spectator. use 'cmd observe' to get an nq client to spectate on an fte server (then attack/jump behave the same as in qw clients).
nqsv: rewrote EF_MUZZLEFLASH handling, so svc_muzzleflash is now translated properly to EF_MUZZLEFLASH, and vice versa. No more missing muzzleflashes!
added screenshot_cubemap, so you can actually pre-generate cubemaps with fte (which can be used for reflections or whatever).
misc fixes (server crash, a couple of other less important ones).
external files based on a model's name will now obey r_replacemodels properly, instead of needing to use foo.mdl_0.skin for foo.md3.
identify <playernum> should now use the correct masked ip, instead of abrubtly failing (reported by kt)
vid_toggle console command should now obey vid_width and vid_height when switching to fullscreen, but only if vid_fullscreen is actually set, which should make it seem better behaved (reported by kt).
qcc: cleaned up sym->symboldata[sym->ofs] to be more consistent at all stages.
qcc: typedef float vec4[4]; now works to define a float array with 4 elements (however, it will be passed by-value rather than by-reference).
qcc: cleaned up optional vs __out ordering issues.
qccgui: shift+f3 searches backwards
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5064 fc73d0e0-1445-4013-8a0c-d673dee63da5
2017-02-27 09:34:35 +00:00
VectorAngles ( view [ 0 ] , view [ 2 ] , vang , true ) ;
2014-06-21 17:58:17 +00:00
/*edit it*/
ideal = ent - > v - > ideal_yaw ;
speed = ent - > v - > yaw_speed ;
move = ideal - anglemod ( vang [ YAW ] ) ;
if ( move > 180 )
move - = 360 ;
else if ( move < - 180 )
move + = 360 ;
if ( move > 0 )
{
if ( move > speed )
move = speed ;
}
else
{
if ( move < - speed )
move = - speed ;
}
vang [ YAW ] = anglemod ( vang [ YAW ] + move ) ;
/*clamp pitch, kill roll. monsters don't pitch/roll.*/
vang [ PITCH ] = 0 ;
vang [ ROLL ] = 0 ;
move = ideal - vang [ YAW ] ;
/*turn those angles back to a matrix*/
AngleVectors ( vang , view [ 0 ] , view [ 1 ] , view [ 2 ] ) ;
VectorNegate ( view [ 1 ] , view [ 1 ] ) ;
Matrix4x4_RM_FromVectors ( mat , view [ 0 ] , view [ 1 ] , view [ 2 ] , vec3_origin ) ;
/*rotate back into world space*/
Matrix4_Multiply ( mat , surfm , viewm ) ;
/*and figure out the final result*/
Matrix3x4_RM_ToVectors ( viewm , view [ 0 ] , view [ 1 ] , view [ 2 ] , view [ 3 ] ) ;
added r_meshpitch cvar that allows for fixing the unfixable mesh pitch bug from vanilla... needs a better name... do note that this will break pretty much any mod, so this is really only for TCs designed to use it. Its likely that I missed places.
nqsv: added support for spectators with nq clients. the angles are a bit rough, but hey. need to do something about frags so nq clients know who's a spectator. use 'cmd observe' to get an nq client to spectate on an fte server (then attack/jump behave the same as in qw clients).
nqsv: rewrote EF_MUZZLEFLASH handling, so svc_muzzleflash is now translated properly to EF_MUZZLEFLASH, and vice versa. No more missing muzzleflashes!
added screenshot_cubemap, so you can actually pre-generate cubemaps with fte (which can be used for reflections or whatever).
misc fixes (server crash, a couple of other less important ones).
external files based on a model's name will now obey r_replacemodels properly, instead of needing to use foo.mdl_0.skin for foo.md3.
identify <playernum> should now use the correct masked ip, instead of abrubtly failing (reported by kt)
vid_toggle console command should now obey vid_width and vid_height when switching to fullscreen, but only if vid_fullscreen is actually set, which should make it seem better behaved (reported by kt).
qcc: cleaned up sym->symboldata[sym->ofs] to be more consistent at all stages.
qcc: typedef float vec4[4]; now works to define a float array with 4 elements (however, it will be passed by-value rather than by-reference).
qcc: cleaned up optional vs __out ordering issues.
qccgui: shift+f3 searches backwards
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5064 fc73d0e0-1445-4013-8a0c-d673dee63da5
2017-02-27 09:34:35 +00:00
VectorAngles ( view [ 0 ] , view [ 2 ] , ent - > v - > angles , true ) ;
2014-06-21 17:58:17 +00:00
//make sure everything is sane
ent - > v - > angles [ PITCH ] = anglemod ( ent - > v - > angles [ PITCH ] ) ;
ent - > v - > angles [ YAW ] = anglemod ( ent - > v - > angles [ YAW ] ) ;
ent - > v - > angles [ ROLL ] = anglemod ( ent - > v - > angles [ ROLL ] ) ;
return move ;
}
2009-11-17 00:15:44 +00:00
2014-01-13 02:42:25 +00:00
//FIXME: gravitydir. reorient the angles to change the yaw with respect to the current ground surface.
2009-11-17 00:15:44 +00:00
current = anglemod ( ent - > v - > angles [ 1 ] ) ;
ideal = ent - > v - > ideal_yaw ;
speed = ent - > v - > yaw_speed ;
if ( current = = ideal )
2014-06-21 17:58:17 +00:00
return 0 ;
2009-11-17 00:15:44 +00:00
move = ideal - current ;
if ( ideal > current )
{
if ( move > = 180 )
move = move - 360 ;
}
else
{
if ( move < = - 180 )
move = move + 360 ;
}
if ( move > 0 )
{
if ( move > speed )
move = speed ;
}
else
{
if ( move < - speed )
move = - speed ;
}
ent - > v - > angles [ 1 ] = anglemod ( current + move ) ;
2014-06-21 17:58:17 +00:00
return ideal - ent - > v - > angles [ 1 ] ;
2009-11-17 00:15:44 +00:00
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = =
SV_StepDirection
Turns to the movement direction , and walks the current distance if
facing it .
= = = = = = = = = = = = = = = = = = = = = =
*/
2014-06-21 17:58:17 +00:00
qboolean World_StepDirection ( world_t * world , wedict_t * ent , float yaw , float dist , vec3_t axis [ 3 ] )
2004-08-23 00:15:46 +00:00
{
vec3_t move , oldorigin ;
2014-06-21 17:58:17 +00:00
float delta , s ;
2004-08-23 00:15:46 +00:00
2005-03-28 00:11:59 +00:00
ent - > v - > ideal_yaw = yaw ;
2004-08-23 00:15:46 +00:00
2014-06-21 17:58:17 +00:00
delta = World_changeyaw ( ent ) ;
2004-08-23 00:15:46 +00:00
yaw = yaw * M_PI * 2 / 360 ;
2014-06-21 17:58:17 +00:00
s = cos ( yaw ) * dist ;
VectorScale ( axis [ 0 ] , s , move ) ;
s = sin ( yaw ) * dist ;
VectorMA ( move , s , axis [ 1 ] , move ) ;
2004-08-23 00:15:46 +00:00
2012-01-17 07:57:46 +00:00
//FIXME: Hexen2: ent flags & FL_SET_TRACE
2005-03-28 00:11:59 +00:00
VectorCopy ( ent - > v - > origin , oldorigin ) ;
2018-09-23 19:35:24 +00:00
if ( World_movestep ( world , ent , move , axis , false , false , NULL ) )
2004-08-23 00:15:46 +00:00
{
2014-06-21 17:58:17 +00:00
delta = anglemod ( delta ) ;
2004-08-23 00:15:46 +00:00
if ( delta > 45 & & delta < 315 )
2014-06-21 17:58:17 +00:00
{ // not turned far enough, so don't take the step
//FIXME: surely this is noticably inefficient?
2005-03-28 00:11:59 +00:00
VectorCopy ( oldorigin , ent - > v - > origin ) ;
2004-08-23 00:15:46 +00:00
}
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2004-08-23 00:15:46 +00:00
return true ;
}
2009-11-17 00:15:44 +00:00
World_LinkEdict ( world , ent , true ) ;
2004-08-23 00:15:46 +00:00
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = =
SV_FixCheckBottom
= = = = = = = = = = = = = = = = = = = = = =
*/
2009-11-17 00:15:44 +00:00
void World_FixCheckBottom ( wedict_t * ent )
2004-08-23 00:15:46 +00:00
{
// Con_Printf ("SV_FixCheckBottom\n");
2005-03-28 00:11:59 +00:00
ent - > v - > flags = ( int ) ent - > v - > flags | FL_PARTIALGROUND ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = =
SV_NewChaseDir
= = = = = = = = = = = = = = = =
*/
# define DI_NODIR -1
2014-06-21 17:58:17 +00:00
void World_NewChaseDir ( world_t * world , wedict_t * actor , wedict_t * enemy , float dist , vec3_t axis [ 3 ] )
2004-08-23 00:15:46 +00:00
{
float deltax , deltay ;
float d [ 3 ] ;
float tdir , olddir , turnaround ;
2005-03-28 00:11:59 +00:00
olddir = anglemod ( ( int ) ( actor - > v - > ideal_yaw / 45 ) * 45 ) ;
2004-08-23 00:15:46 +00:00
turnaround = anglemod ( olddir - 180 ) ;
2014-06-21 17:58:17 +00:00
VectorSubtract ( enemy - > v - > origin , actor - > v - > origin , d ) ;
deltax = DotProduct ( d , axis [ 0 ] ) ;
deltay = DotProduct ( d , axis [ 1 ] ) ;
2004-08-23 00:15:46 +00:00
if ( deltax > 10 )
d [ 1 ] = 0 ;
else if ( deltax < - 10 )
d [ 1 ] = 180 ;
else
d [ 1 ] = DI_NODIR ;
if ( deltay < - 10 )
d [ 2 ] = 270 ;
else if ( deltay > 10 )
d [ 2 ] = 90 ;
else
d [ 2 ] = DI_NODIR ;
// try direct route
if ( d [ 1 ] ! = DI_NODIR & & d [ 2 ] ! = DI_NODIR )
{
if ( d [ 1 ] = = 0 )
tdir = d [ 2 ] = = 90 ? 45 : 315 ;
else
tdir = d [ 2 ] = = 90 ? 135 : 215 ;
2014-06-21 17:58:17 +00:00
if ( tdir ! = turnaround & & World_StepDirection ( world , actor , tdir , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
}
// try other directions
2016-07-12 00:40:13 +00:00
if ( ( ( rand ( ) & 3 ) & 1 ) | | fabs ( deltay ) > fabs ( deltax ) )
2004-08-23 00:15:46 +00:00
{
tdir = d [ 1 ] ;
d [ 1 ] = d [ 2 ] ;
d [ 2 ] = tdir ;
}
if ( d [ 1 ] ! = DI_NODIR & & d [ 1 ] ! = turnaround
2014-06-21 17:58:17 +00:00
& & World_StepDirection ( world , actor , d [ 1 ] , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
if ( d [ 2 ] ! = DI_NODIR & & d [ 2 ] ! = turnaround
2014-06-21 17:58:17 +00:00
& & World_StepDirection ( world , actor , d [ 2 ] , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
/* there is no direct path to the player, so pick another direction */
2014-06-21 17:58:17 +00:00
if ( olddir ! = DI_NODIR & & World_StepDirection ( world , actor , olddir , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
if ( rand ( ) & 1 ) /*randomly determine direction of search*/
{
for ( tdir = 0 ; tdir < = 315 ; tdir + = 45 )
2014-06-21 17:58:17 +00:00
if ( tdir ! = turnaround & & World_StepDirection ( world , actor , tdir , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
}
else
{
for ( tdir = 315 ; tdir > = 0 ; tdir - = 45 )
2014-06-21 17:58:17 +00:00
if ( tdir ! = turnaround & & World_StepDirection ( world , actor , tdir , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
}
2014-06-21 17:58:17 +00:00
if ( turnaround ! = DI_NODIR & & World_StepDirection ( world , actor , turnaround , dist , axis ) )
2004-08-23 00:15:46 +00:00
return ;
2005-03-28 00:11:59 +00:00
actor - > v - > ideal_yaw = olddir ; // can't move
2004-08-23 00:15:46 +00:00
// if a bridge was pulled out from underneath a monster, it may not have
// a valid standing position at all
2014-06-21 17:58:17 +00:00
if ( ! World_CheckBottom ( world , actor , axis [ 2 ] ) )
2009-11-17 00:15:44 +00:00
World_FixCheckBottom ( actor ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = =
SV_CloseEnough
= = = = = = = = = = = = = = = = = = = = = =
*/
2009-11-17 00:15:44 +00:00
qboolean World_CloseEnough ( wedict_t * ent , wedict_t * goal , float dist )
2004-08-23 00:15:46 +00:00
{
int i ;
for ( i = 0 ; i < 3 ; i + + )
{
2005-03-28 00:11:59 +00:00
if ( goal - > v - > absmin [ i ] > ent - > v - > absmax [ i ] + dist )
2004-08-23 00:15:46 +00:00
return false ;
2005-03-28 00:11:59 +00:00
if ( goal - > v - > absmax [ i ] < ent - > v - > absmin [ i ] - dist )
2004-08-23 00:15:46 +00:00
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = =
SV_MoveToGoal
= = = = = = = = = = = = = = = = = = = = = =
*/
2009-11-17 00:15:44 +00:00
qboolean World_MoveToGoal ( world_t * world , wedict_t * ent , float dist )
2004-08-23 00:15:46 +00:00
{
2009-11-17 00:15:44 +00:00
wedict_t * goal ;
2014-06-21 17:58:17 +00:00
vec3_t axis [ 3 ] ;
2004-08-23 00:15:46 +00:00
2011-09-16 05:56:54 +00:00
ent = ( wedict_t * ) PROG_TO_EDICT ( world - > progs , * world - > g . self ) ;
2009-11-17 00:15:44 +00:00
goal = ( wedict_t * ) PROG_TO_EDICT ( world - > progs , ent - > v - > goalentity ) ;
2004-08-23 00:15:46 +00:00
2005-03-28 00:11:59 +00:00
if ( ! ( ( int ) ent - > v - > flags & ( FL_ONGROUND | FL_FLY | FL_SWIM ) ) )
2004-08-23 00:15:46 +00:00
{
2009-11-17 00:15:44 +00:00
return false ;
2004-08-23 00:15:46 +00:00
}
// if the next step hits the enemy, return immediately
2009-11-17 00:15:44 +00:00
if ( PROG_TO_EDICT ( world - > progs , ent - > v - > enemy ) ! = ( edict_t * ) world - > edicts & & World_CloseEnough ( ent , goal , dist ) )
return true ;
2004-08-23 00:15:46 +00:00
2014-06-21 17:58:17 +00:00
World_GetEntGravityAxis ( ent , axis ) ;
2004-08-23 00:15:46 +00:00
// bump around...
if ( ( rand ( ) & 3 ) = = 1 | |
2014-06-21 17:58:17 +00:00
! World_StepDirection ( world , ent , ent - > v - > ideal_yaw , dist , axis ) )
2004-08-23 00:15:46 +00:00
{
2014-06-21 17:58:17 +00:00
World_NewChaseDir ( world , ent , goal , dist , axis ) ;
2004-08-23 00:15:46 +00:00
}
2009-11-17 00:15:44 +00:00
return true ;
2004-08-23 00:15:46 +00:00
}
2018-04-27 16:40:50 +00:00
# ifdef ENGINE_ROUTING
2020-06-27 19:32:49 +00:00
# ifdef HAVE_CLIENT
2018-11-19 06:37:25 +00:00
static cvar_t route_shownodes = CVAR ( " route_shownodes " , " 0 " ) ;
2018-11-19 08:06:07 +00:00
# endif
2018-04-27 16:40:50 +00:00
# define LF_EDGE 0x00000001
# define LF_JUMP 0x00000002
# define LF_CROUCH 0x00000004
# define LF_TELEPORT 0x00000008
# define LF_USER 0x7fffff00
# define LF_DESTINATION 0x80000000 //You have reached your destination...
struct waypointnetwork_s
{
size_t refs ;
size_t numwaypoints ;
model_t * worldmodel ;
struct resultnodes_s
{
vec3_t pos ;
int linkflags ;
2021-01-02 10:13:59 +00:00
float radius ;
2018-04-27 16:40:50 +00:00
} * displaynode ;
2018-11-19 06:37:25 +00:00
size_t displaynodes ;
2018-04-27 16:40:50 +00:00
struct waypoint_s
{
vec3_t org ;
float radius ; //used for picking the closest waypoint. aka proximity weight. also relaxes routes inside the area.
struct wpneighbour_s
{
int node ;
float linkcost ; //might be much lower in the case of teleports, or expensive if someone wanted it to be a lower priority link.
int linkflags ; //LF_*
} * neighbour ;
2018-11-19 06:37:25 +00:00
size_t neighbours ;
2018-04-27 16:40:50 +00:00
} waypoints [ 1 ] ;
} ;
void WayNet_Done ( struct waypointnetwork_s * net )
{
if ( net )
if ( 0 = = - - net - > refs )
{
Z_Free ( net ) ;
}
}
static qboolean WayNet_TokenizeLine ( char * * linestart )
{
char * end = * linestart ;
if ( ! * end )
{ //clear it out...
Cmd_TokenizeString ( " " , false , false ) ;
return false ;
}
for ( ; * end ; end + + )
{
if ( * end = = ' \n ' )
{
* end + + = 0 ;
break ;
}
}
Cmd_TokenizeString ( * linestart , false , false ) ;
* linestart = end ;
return true ;
}
static struct waypointnetwork_s * WayNet_Begin ( void * * ctxptr , model_t * worldmodel )
{
struct waypointnetwork_s * net = * ctxptr ;
if ( ! net )
{
char * wf = NULL , * l , * e ;
int numwaypoints , maxlinks , numlinks ;
struct wpneighbour_s * nextlink ;
if ( ! worldmodel )
return NULL ;
if ( ! wf & & ! strncmp ( worldmodel - > name , " maps/ " , 5 ) )
{
char n [ MAX_QPATH ] ;
COM_StripExtension ( worldmodel - > name + 5 , n , sizeof ( n ) ) ;
wf = FS_MallocFile ( va ( " data/%s.way " , n ) , FS_GAME , NULL ) ;
}
if ( ! wf )
wf = FS_MallocFile ( va ( " %s.way " , worldmodel - > name ) , FS_GAME , NULL ) ;
2018-06-06 09:09:14 +00:00
if ( ! wf )
return NULL ;
2018-04-27 16:40:50 +00:00
l = wf ;
//read the number of waypoints
WayNet_TokenizeLine ( & l ) ;
numwaypoints = atoi ( Cmd_Argv ( 0 ) ) ;
//count lines and guess the link count.
for ( e = l , maxlinks = 0 ; * e ; e + + )
if ( * e = = ' \n ' )
maxlinks + + ;
maxlinks - = numwaypoints ;
net = Z_Malloc ( sizeof ( * net ) - sizeof ( net - > waypoints ) + ( numwaypoints * sizeof ( struct waypoint_s ) ) + ( maxlinks * sizeof ( struct wpneighbour_s ) ) ) ;
net - > refs = 1 ;
net - > worldmodel = worldmodel ;
* ctxptr = net ;
nextlink = ( struct wpneighbour_s * ) ( net - > waypoints + numwaypoints ) ;
while ( WayNet_TokenizeLine ( & l ) & & net - > numwaypoints < numwaypoints )
{
if ( ! Cmd_Argc ( ) )
continue ; //a comment line?
net - > waypoints [ net - > numwaypoints ] . org [ 0 ] = atof ( Cmd_Argv ( 0 ) ) ;
net - > waypoints [ net - > numwaypoints ] . org [ 1 ] = atof ( Cmd_Argv ( 1 ) ) ;
net - > waypoints [ net - > numwaypoints ] . org [ 2 ] = atof ( Cmd_Argv ( 2 ) ) ;
2020-06-27 19:32:49 +00:00
net - > waypoints [ net - > numwaypoints ] . radius = atof ( Cmd_Argv ( 3 ) ) ;
2018-04-27 16:40:50 +00:00
numlinks = bound ( 0 , atoi ( Cmd_Argv ( 4 ) ) , maxlinks ) ;
//make sure the links are valid, and clamp to avoid problems (even if we're then going to mis-parse.
net - > waypoints [ net - > numwaypoints ] . neighbour = nextlink ;
while ( numlinks - - > 0 & & WayNet_TokenizeLine ( & l ) )
{
if ( ! Cmd_Argc ( ) )
continue ; //a comment line?
nextlink [ net - > waypoints [ net - > numwaypoints ] . neighbours ] . node = atoi ( Cmd_Argv ( 0 ) ) ;
nextlink [ net - > waypoints [ net - > numwaypoints ] . neighbours ] . linkcost = atof ( Cmd_Argv ( 1 ) ) ;
nextlink [ net - > waypoints [ net - > numwaypoints ] . neighbours + + ] . linkflags = atoi ( Cmd_Argv ( 2 ) ) ;
}
maxlinks - = net - > waypoints [ net - > numwaypoints ] . neighbours ;
nextlink + = net - > waypoints [ net - > numwaypoints + + ] . neighbours ;
}
BZ_Free ( wf ) ;
}
net - > refs + + ;
return net ;
}
struct waydist_s
{
int node ;
float sdist ;
} ;
int QDECL WayNet_Prioritise ( const void * a , const void * b )
{
const struct waydist_s * w1 = a , * w2 = b ;
if ( w1 - > sdist < w2 - > sdist )
return - 1 ;
if ( w1 - > sdist = = w2 - > sdist )
return 0 ;
return 1 ;
}
int WayNet_FindNearestNode ( struct waypointnetwork_s * net , vec3_t pos )
{
if ( net & & net - > numwaypoints )
{
//we qsort the possible nodes, in an attempt to reduce traces.
struct waydist_s * sortedways = alloca ( sizeof ( * sortedways ) * net - > numwaypoints ) ;
size_t u ;
vec3_t disp ;
float sradius ;
trace_t tr ;
for ( u = 0 ; u < net - > numwaypoints ; u + + )
{
sortedways [ u ] . node = u ;
VectorSubtract ( net - > waypoints [ u ] . org , pos , disp ) ;
sortedways [ u ] . sdist = DotProduct ( disp , disp ) ;
sradius = net - > waypoints [ u ] . radius * net - > waypoints [ u ] . radius ;
if ( sortedways [ u ] . sdist < sradius )
sortedways [ u ] . sdist - = sradius ; //if we're inside the waypoint's radius, push inwards resulting in negatives, so these are always highly prioritised
}
qsort ( sortedways , net - > numwaypoints , sizeof ( struct waydist_s ) , WayNet_Prioritise ) ;
//can't trace yet...
if ( net - > worldmodel - > loadstate ! = MLS_LOADED )
return sortedways [ 0 ] . node ;
for ( u = 0 ; u < net - > numwaypoints ; u + + )
{
if ( sortedways [ u ] . sdist > 0 )
{ //if we're outside the node, we need to do a trace to make sure we can actually reach it.
net - > worldmodel - > funcs . NativeTrace ( net - > worldmodel , 0 , NULL , NULL , pos , net - > waypoints [ sortedways [ u ] . node ] . org , vec3_origin , vec3_origin , false , MASK_WORLDSOLID , & tr ) ;
if ( tr . fraction < 1 )
continue ; //this node is blocked. just move on to the next.
}
return sortedways [ u ] . node ;
}
}
return - 1 ;
}
struct routecalc_s
{
world_t * world ;
wedict_t * ed ;
2018-11-19 06:37:25 +00:00
int spawncount ; //so we don't confuse stuff if the map gets restarted.
2018-04-27 16:40:50 +00:00
// float spawnid; //so the route fails if the ent is removed.
func_t callback ;
vec3_t start ;
vec3_t end ;
int denylinkflags ;
int startn ;
int endn ;
int numresultnodes ;
struct resultnodes_s * resultnodes ;
struct waypointnetwork_s * waynet ;
} ;
//main thread
void Route_Calculated ( void * ctx , void * data , size_t a , size_t b )
{
struct routecalc_s * route = data ;
pubprogfuncs_t * prinst = route - > world - > progs ;
//let the gamecode know the results
if ( ! route - > callback )
{
BZ_Free ( route - > waynet - > displaynode ) ;
route - > waynet - > displaynode = BZ_Malloc ( sizeof ( struct resultnodes_s ) * route - > numresultnodes ) ;
route - > waynet - > displaynodes = route - > numresultnodes ;
memcpy ( route - > waynet - > displaynode , route - > resultnodes , sizeof ( struct resultnodes_s ) * route - > numresultnodes ) ;
}
else if ( route - > callback & & route - > world - > spawncount = = route - > spawncount /* && route->spawnid == route->ed->xv->uniquespawnid*/ )
{
struct globalvars_s * pr_globals = PR_globals ( prinst , PR_CURRENT ) ;
struct resultnodes_s * ptr = prinst - > AddressableAlloc ( prinst , sizeof ( struct resultnodes_s ) * route - > numresultnodes ) ;
memcpy ( ptr , route - > resultnodes , sizeof ( struct resultnodes_s ) * route - > numresultnodes ) ;
G_INT ( OFS_PARM0 ) = EDICT_TO_PROG ( prinst , route - > ed ) ;
VectorCopy ( route - > end , G_VECTOR ( OFS_PARM1 ) ) ;
G_INT ( OFS_PARM2 ) = route - > numresultnodes ;
G_INT ( OFS_PARM3 ) = ( char * ) ptr - prinst - > stringtable ;
PR_ExecuteProgram ( prinst , route - > callback ) ;
}
//and we're done. destroy everything.
WayNet_Done ( route - > waynet ) ;
Z_Free ( route - > resultnodes ) ;
Z_Free ( route ) ;
}
//#define FLOODALL
# define COST_INFINITE FLT_MAX
2020-12-29 07:22:21 +00:00
typedef struct
{
int id ;
int flags ;
} nodefrom_t ;
static qboolean Route_Completed ( struct routecalc_s * r , nodefrom_t * nodecamefrom )
2018-04-27 16:40:50 +00:00
{
size_t u ;
struct waypointnetwork_s * n = r - > waynet ;
r - > resultnodes = Z_Malloc ( sizeof ( * r - > resultnodes ) * ( n - > numwaypoints + 1 ) * 3 ) ;
r - > numresultnodes = 0 ;
//target point is first. yay.
VectorCopy ( r - > end , r - > resultnodes [ 0 ] . pos ) ;
r - > resultnodes [ 0 ] . linkflags = LF_DESTINATION ;
2021-01-02 10:13:59 +00:00
r - > resultnodes [ 0 ] . radius = 32 ;
2018-04-27 16:40:50 +00:00
r - > numresultnodes + + ;
u = r - > endn ;
for ( ; ; )
{
VectorCopy ( n - > waypoints [ u ] . org , r - > resultnodes [ r - > numresultnodes ] . pos ) ;
2020-12-29 07:22:21 +00:00
r - > resultnodes [ r - > numresultnodes ] . linkflags = nodecamefrom [ u ] . flags ;
2021-01-02 10:13:59 +00:00
r - > resultnodes [ r - > numresultnodes ] . radius = n - > waypoints [ u ] . radius ;
2018-04-27 16:40:50 +00:00
r - > numresultnodes + + ;
if ( u = = r - > startn )
break ;
2020-12-29 07:22:21 +00:00
u = nodecamefrom [ u ] . id ;
2018-04-27 16:40:50 +00:00
}
//and include the start point, because we can
VectorCopy ( r - > start , r - > resultnodes [ r - > numresultnodes ] . pos ) ;
r - > resultnodes [ r - > numresultnodes ] . linkflags = 0 ;
2021-01-02 10:13:59 +00:00
r - > resultnodes [ r - > numresultnodes ] . radius = 32 ;
2018-04-27 16:40:50 +00:00
r - > numresultnodes + + ;
return true ;
}
# if 1
static float Route_GuessCost ( struct routecalc_s * r , float * fromorg )
{ //if we want to guarentee the shortest route, then we MUST always return a value <= to the actual cost here.
//unfortunately we don't know how many teleporters are between the two points.
//on the plus side, a little randomness here means we'll find alternative (longer) routes some times, which will reduce flash points and help flag carriers...
vec3_t disp ;
VectorSubtract ( r - > end , fromorg , disp ) ;
return sqrt ( DotProduct ( disp , disp ) ) ;
}
static qboolean Route_Process ( struct routecalc_s * r )
{
struct waypointnetwork_s * n = r - > waynet ;
int opennodes = 0 ;
int u , j ;
float guesscost ;
struct opennode_s {
int node ;
float cost ;
} * open = alloca ( sizeof ( * open ) * n - > numwaypoints ) ;
float * nodecost = alloca ( sizeof ( * nodecost ) * n - > numwaypoints ) ;
2020-12-29 07:22:21 +00:00
nodefrom_t * nodecamefrom = alloca ( sizeof ( * nodecamefrom ) * n - > numwaypoints ) ;
2018-04-27 16:40:50 +00:00
for ( u = 0 ; u < n - > numwaypoints ; u + + )
nodecost [ u ] = COST_INFINITE ;
if ( r - > startn > = 0 )
{
nodecost [ r - > startn ] = 0 ;
open [ 0 ] . node = r - > startn ;
open [ 0 ] . cost = 0 ;
opennodes + + ;
}
while ( opennodes )
{
int nodeidx = open [ - - opennodes ] . node ;
struct waypoint_s * wp = & n - > waypoints [ nodeidx ] ;
# ifdef _DEBUG
if ( nodeidx < 0 | | nodeidx > = n - > numwaypoints )
{
Con_Printf ( " Bad node index in open list \n " ) ;
return false ;
}
# endif
if ( nodeidx = = r - > endn )
{ //we found the end!
return Route_Completed ( r , nodecamefrom ) ;
}
for ( u = 0 ; u < wp - > neighbours ; u + + )
{
struct wpneighbour_s * l = & wp - > neighbour [ u ] ;
int linkidx = l - > node ;
float realcost = nodecost [ nodeidx ] + l - > linkcost ;
2024-03-08 19:45:50 +00:00
if ( l - > linkflags & r - > denylinkflags )
continue ;
2018-04-27 16:40:50 +00:00
# ifdef _DEBUG
if ( linkidx < 0 | | linkidx > = n - > numwaypoints )
{
Con_Printf ( " Bad node link index in routing table \n " ) ;
return false ;
}
# endif
if ( realcost > = nodecost [ linkidx ] )
continue ;
2020-12-29 07:22:21 +00:00
nodecamefrom [ linkidx ] . id = nodeidx ;
nodecamefrom [ linkidx ] . flags = l - > linkflags ;
2018-04-27 16:40:50 +00:00
nodecost [ linkidx ] = realcost ;
for ( j = opennodes - 1 ; j > = 0 ; j - - )
{
if ( open [ j ] . node = = linkidx )
break ;
}
guesscost = realcost + Route_GuessCost ( r , n - > waypoints [ linkidx ] . org ) ;
if ( j < 0 )
{ //not already in the list
//tbh, we should probably just directly bubble in this loop instead of doing the memcpy (with its internal second loop).
for ( j = opennodes - 1 ; j > = 0 ; j - - )
if ( guesscost < = open [ j ] . cost )
break ;
j + + ;
//move them up
memmove ( & open [ j + 1 ] , & open [ j ] , sizeof ( * open ) * ( opennodes - j ) ) ;
open [ j ] . node = linkidx ;
open [ j ] . cost = guesscost ;
opennodes + + ;
}
else if ( guesscost < open [ j ] . cost )
{ //if it got cheaper, be prepared to move the node towards the higher addresses (these will be checked first).
for ( ; j + 1 < opennodes & & open [ j + 1 ] . cost > guesscost ; j + + )
open [ j ] = open [ j + 1 ] ;
//okay, so we can't keep going... this is our new slot!
open [ j ] . node = linkidx ;
open [ j ] . cost = guesscost ;
}
//otherwise it got more expensive, and we don't care about that
}
}
return false ;
}
# else
static qboolean Route_Process ( struct routecalc_s * r )
{
struct waypointnetwork_s * n = r - > waynet ;
int opennodes = 0 ;
int u , j ;
//we use an open list in a desperate attempt to avoid recursing the entire network
int * open = alloca ( sizeof ( * open ) * n - > numwaypoints ) ;
float * nodecost = alloca ( sizeof ( * nodecost ) * n - > numwaypoints ) ;
2020-12-29 07:22:21 +00:00
nodefrom_t * nodecamefrom = alloca ( sizeof ( * nodecamefrom ) * n - > numwaypoints ) ;
2018-04-27 16:40:50 +00:00
for ( u = 0 ; u < n - > numwaypoints ; u + + )
nodecost [ u ] = COST_INFINITE ;
nodecost [ r - > startn ] = 0 ;
open [ opennodes + + ] = r - > startn ;
while ( opennodes )
{
int nodeidx = open [ - - opennodes ] ;
struct waypoint_s * wp = & n - > waypoints [ nodeidx ] ;
// if (nodeidx < 0 || nodeidx >= n->numwaypoints)
// return false;
for ( u = 0 ; u < wp - > neighbours ; u + + )
{
struct wpneighbour_s * l = & wp - > neighbour [ u ] ;
int linkidx = l - > node ;
float realcost = nodecost [ nodeidx ] + l - > linkcost ;
// if (linkidx < 0 || linkidx >= n->numwaypoints)
// return false;
if ( realcost > = nodecost [ linkidx ] )
continue ;
2020-12-29 07:22:21 +00:00
nodecamefrom [ linkidx ] . id = nodeidx ;
nodecamefrom [ linkidx ] . flags = l - > linkflags ;
2018-04-27 16:40:50 +00:00
nodecost [ linkidx ] = realcost ;
for ( j = 0 ; j < opennodes ; j + + )
{
if ( open [ j ] = = linkidx )
break ;
}
if ( j = = opennodes ) //not already queued
open [ opennodes + + ] = linkidx ;
}
}
if ( r - > endn > = 0 & & nodecost [ r - > endn ] < COST_INFINITE )
{ //we found the end! we can build the route from end to start.
return Route_Completed ( r , nodecamefrom ) ;
}
return false ;
}
# endif
//worker thread
void Route_Calculate ( void * ctx , void * data , size_t a , size_t b )
{
struct routecalc_s * route = data ;
//first thing is to find the start+end nodes.
if ( route - > waynet & & route - > startn > = 0 & & route - > endn > = 0 & & Route_Process ( route ) )
;
else
{
route - > numresultnodes = 0 ;
route - > resultnodes = Z_Malloc ( sizeof ( * route - > resultnodes ) * 2 ) ;
VectorCopy ( route - > end , route - > resultnodes [ 0 ] . pos ) ;
route - > resultnodes [ 0 ] . linkflags = LF_DESTINATION ;
route - > numresultnodes + + ;
VectorCopy ( route - > start , route - > resultnodes [ route - > numresultnodes ] . pos ) ;
route - > resultnodes [ route - > numresultnodes ] . linkflags = 0 ;
route - > numresultnodes + + ;
}
COM_AddWork ( WG_MAIN , Route_Calculated , NULL , route , 0 , 0 ) ;
}
2018-07-22 11:49:37 +00:00
//void route_linkitem(entity item, int ittype) //-1 to unlink
//void route_choosedest(entity ent, int numitemtypes, float *itemweights)
2018-04-27 16:40:50 +00:00
/*
= = = = = = = = = = = = =
PF_route_calculate
engine reads + caches the nodes from a file .
the route ' s nodes must be memfreed on completion .
the first node in the nodelist is the destination .
typedef struct {
vector dest ;
int linkflags ;
2018-07-22 11:49:37 +00:00
//float anglehint;
2018-04-27 16:40:50 +00:00
} nodeslist_t ;
void ( entity ent , vector dest , int denylinkflags , void ( entity ent , vector dest , int numnodes , nodeslist_t * nodelist ) callback ) route_calculate = # 0 ;
= = = = = = = = = = = = =
*/
void QCBUILTIN PF_route_calculate ( pubprogfuncs_t * prinst , struct globalvars_s * pr_globals )
{
struct routecalc_s * route = Z_Malloc ( sizeof ( * route ) ) ;
float * end ;
route - > world = prinst - > parms - > user ;
route - > spawncount = route - > world - > spawncount ;
route - > ed = G_WEDICT ( prinst , OFS_PARM0 ) ;
// route->spawnid = route->ed->xv->uniquespawnid;
end = G_VECTOR ( OFS_PARM1 ) ;
route - > denylinkflags = G_INT ( OFS_PARM2 ) ;
route - > callback = G_INT ( OFS_PARM3 ) ;
VectorCopy ( route - > ed - > v - > origin , route - > start ) ;
VectorCopy ( end , route - > end ) ;
route - > waynet = WayNet_Begin ( & route - > world - > waypoints , route - > world - > worldmodel ) ;
//tracelines use some sequence info to avoid retracing the same brush multiple times.
// this means that we can't reliably trace on worker threads (would break the main thread occasionally).
// so we have to do this here.
//FIXME: find a safe way to NOT do this here.
route - > startn = WayNet_FindNearestNode ( route - > waynet , route - > start ) ;
route - > endn = WayNet_FindNearestNode ( route - > waynet , route - > end ) ;
COM_AddWork ( WG_LOADER , Route_Calculate , NULL , route , 0 , 0 ) ;
}
2020-06-27 19:32:49 +00:00
# ifdef HAVE_CLIENT
2018-04-27 16:40:50 +00:00
static void Route_Visualise_f ( void )
{
extern world_t csqc_world ;
vec3_t targ = { atof ( Cmd_Argv ( 1 ) ) , atof ( Cmd_Argv ( 2 ) ) , atof ( Cmd_Argv ( 3 ) ) } ;
struct routecalc_s * route = Z_Malloc ( sizeof ( * route ) ) ;
route - > world = & csqc_world ;
route - > spawncount = route - > world - > spawncount ;
route - > ed = route - > world - > edicts ;
// route->spawnid = route->ed->xv->uniquespawnid;
VectorCopy ( r_refdef . vieworg , route - > start ) ;
VectorCopy ( targ , route - > end ) ;
route - > waynet = WayNet_Begin ( & route - > world - > waypoints , route - > world - > worldmodel ) ;
//tracelines use some sequence info to avoid retracing the same brush multiple times.
// this means that we can't reliably trace on worker threads (would break the main thread occasionally).
// so we have to do this here.
//FIXME: find a safe way to NOT do this here.
route - > startn = WayNet_FindNearestNode ( route - > waynet , route - > start ) ;
route - > endn = WayNet_FindNearestNode ( route - > waynet , route - > end ) ;
COM_AddWork ( WG_LOADER , Route_Calculate , NULL , route , 0 , 0 ) ;
}
# include "shader.h"
void PR_Route_Visualise ( void )
{
extern world_t csqc_world ;
world_t * w = & csqc_world ;
struct waypointnetwork_s * wn ;
size_t u ;
wn = ( w & & ( w - > waypoints | | route_shownodes . ival ) ) ? WayNet_Begin ( & w - > waypoints , w - > worldmodel ) : NULL ;
if ( wn )
{
if ( route_shownodes . ival )
{
float mat [ 12 ] = { 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 } ;
shader_t * shader_out = R_RegisterShader ( " waypointvolume_out " , SUF_NONE ,
" { \n "
" polygonoffset \n "
" nodepth \n "
" { \n "
" map $whiteimage \n "
" blendfunc add \n "
" rgbgen vertex \n "
" alphagen vertex \n "
" } \n "
" } \n " ) ;
shader_t * shader_in = R_RegisterShader ( " waypointvolume_in " , SUF_NONE ,
" { \n "
" polygonoffset \n "
" cull disable \n "
" nodepth \n "
" { \n "
" map $whiteimage \n "
" blendfunc add \n "
" rgbgen vertex \n "
" alphagen vertex \n "
" } \n "
" } \n " ) ;
float radius ;
vec3_t dir ;
//should probably use a different colour for the node you're inside.
for ( u = 0 ; u < wn - > numwaypoints ; u + + )
{
mat [ 3 ] = wn - > waypoints [ u ] . org [ 0 ] ;
mat [ 7 ] = wn - > waypoints [ u ] . org [ 1 ] ;
mat [ 11 ] = wn - > waypoints [ u ] . org [ 2 ] ;
radius = wn - > waypoints [ u ] . radius ;
if ( radius < = 0 )
radius = 1 ; //waypoints shouldn't really have a radius of 0, but if they do we'll still want to draw something.
VectorSubtract ( wn - > waypoints [ u ] . org , r_refdef . vieworg , dir ) ;
if ( DotProduct ( dir , dir ) < radius * radius )
CLQ1_AddOrientedSphere ( shader_in , radius , mat , 0.0 , 0.1 , 0 , 1 ) ;
else
CLQ1_AddOrientedSphere ( shader_out , radius , mat , 0.2 , 0.0 , 0 , 1 ) ;
}
for ( u = 0 ; u < wn - > numwaypoints ; u + + )
{
size_t n ;
for ( n = 0 ; n < wn - > waypoints [ u ] . neighbours ; n + + )
{
struct waypoint_s * r = wn - > waypoints + wn - > waypoints [ u ] . neighbour [ n ] . node ;
CLQ1_DrawLine ( shader_out , wn - > waypoints [ u ] . org , r - > org , 0 , 0 , 1 , 1 ) ;
}
}
}
if ( wn - > displaynodes )
{ //FIXME: we should probably use beams here
shader_t * shader_route = R_RegisterShader ( " waypointroute " , SUF_NONE ,
" { \n "
" polygonoffset \n "
" nodepth \n "
" { \n "
" map $whiteimage \n "
" blendfunc add \n "
" rgbgen vertex \n "
" alphagen vertex \n "
" } \n "
" } \n " ) ;
for ( u = wn - > displaynodes - 1 ; u > 0 ; u - - )
{
vec_t * start = wn - > displaynode [ u ] . pos ;
vec_t * end = wn - > displaynode [ u - 1 ] . pos ;
CLQ1_DrawLine ( shader_route , start , end , 0.5 , 0.5 , 0.5 , 1 ) ;
}
}
}
WayNet_Done ( wn ) ;
}
# endif
//destroys the routing waypoint cache.
void PR_Route_Shutdown ( world_t * world )
{
WayNet_Done ( world - > waypoints ) ;
world - > waypoints = NULL ;
}
static void Route_Reload_f ( void )
{
2020-06-27 19:32:49 +00:00
# if defined(HAVE_CLIENT) && defined(CSQC_DAT)
2018-04-27 16:40:50 +00:00
extern world_t csqc_world ;
PR_Route_Shutdown ( & csqc_world ) ;
# endif
2020-06-27 19:32:49 +00:00
# ifdef HAVE_SERVER
2018-04-27 16:40:50 +00:00
PR_Route_Shutdown ( & sv . world ) ;
# endif
}
void PR_Route_Init ( void )
{
2020-06-27 19:32:49 +00:00
# if defined(HAVE_CLIENT) && defined(CSQC_DAT)
2018-04-27 16:40:50 +00:00
Cvar_Register ( & route_shownodes , NULL ) ;
Cmd_AddCommand ( " route_visualise " , Route_Visualise_f ) ;
# endif
Cmd_AddCommand ( " route_reload " , Route_Reload_f ) ;
}
//route_force
//COM_WorkerPartialSync
# endif
2004-11-29 01:21:00 +00:00
# endif