2010-02-15 23:26:55 +00:00
/*
Copyright ( C ) 1996 - 2001 Id Software , Inc .
Copyright ( C ) 2002 - 2009 John Fitzgibbons and others
2014-09-22 08:55:46 +00:00
Copyright ( C ) 2010 - 2014 QuakeSpasm developers
2010-02-15 23:26:55 +00:00
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_phys.c
# include "quakedef.h"
/*
pushmove objects do not obey gravity , and do not interact with each other or trigger fields , but block normal movement and push normal objects when they move .
onground is set for toss objects when they come to a complete rest . it is set for steping or walking objects
doors , plats , etc are SOLID_BSP , and MOVETYPE_PUSH
bonus items are SOLID_TRIGGER touch , and MOVETYPE_TOSS
corpses are SOLID_NOT and MOVETYPE_TOSS
crates are SOLID_BBOX and MOVETYPE_TOSS
walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
flying / floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
solid_edge items only clip against bsp models .
*/
2011-12-28 22:01:33 +00:00
cvar_t sv_friction = { " sv_friction " , " 4 " , CVAR_NOTIFY | CVAR_SERVERINFO } ;
cvar_t sv_stopspeed = { " sv_stopspeed " , " 100 " , CVAR_NONE } ;
cvar_t sv_gravity = { " sv_gravity " , " 800 " , CVAR_NOTIFY | CVAR_SERVERINFO } ;
cvar_t sv_maxvelocity = { " sv_maxvelocity " , " 2000 " , CVAR_NONE } ;
cvar_t sv_nostep = { " sv_nostep " , " 0 " , CVAR_NONE } ;
cvar_t sv_freezenonclients = { " sv_freezenonclients " , " 0 " , CVAR_NONE } ;
2017-09-17 02:12:53 +00:00
cvar_t sv_gameplayfix_spawnbeforethinks = { " sv_gameplayfix_spawnbeforethinks " , " 0 " , CVAR_NONE } ;
2021-11-08 00:17:44 +00:00
cvar_t sv_gameplayfix_bouncedownslopes = { " sv_gameplayfix_bouncedownslopes " , " 0 " , CVAR_NONE } ; //fixes grenades making horrible noises on slopes.
2011-06-10 03:04:51 +00:00
2020-01-12 08:05:35 +00:00
cvar_t sv_sound_watersplash = { " sv_sound_watersplash " , " misc/h2ohit1.wav " , CVAR_NONE } ;
cvar_t sv_sound_land = { " sv_sound_land " , " demon/dland2.wav " , CVAR_NONE } ;
2010-02-15 23:26:55 +00:00
# define MOVE_EPSILON 0.01
void SV_Physics_Toss ( edict_t * ent ) ;
2020-09-04 11:22:26 +00:00
static void World_StartSound ( edict_t * entity , float * origin , int channel , const char * sample , int volume , float attenuation )
{ //small helper function
if ( qcvm = = & sv . qcvm )
SV_StartSound ( entity , origin , channel , sample , volume , attenuation ) ;
else
{
vec3_t mid ;
int i ;
if ( ! origin )
{
for ( i = 0 ; i < 3 ; i + + )
mid [ i ] = entity - > v . origin [ i ] + 0.5 * ( entity - > v . mins [ i ] + entity - > v . maxs [ i ] ) ;
origin = mid ;
}
S_StartSound ( - NUM_FOR_EDICT ( entity ) , channel , S_PrecacheSound ( sample ) , origin , volume , attenuation ) ;
}
}
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = = =
SV_CheckAllEnts
= = = = = = = = = = = = = = = =
*/
void SV_CheckAllEnts ( void )
{
int e ;
edict_t * check ;
// see if any solid entities are inside the final position
2018-05-01 00:35:14 +00:00
check = NEXT_EDICT ( qcvm - > edicts ) ;
for ( e = 1 ; e < qcvm - > num_edicts ; e + + , check = NEXT_EDICT ( check ) )
2010-02-15 23:26:55 +00:00
{
if ( check - > free )
continue ;
if ( check - > v . movetype = = MOVETYPE_PUSH
| | check - > v . movetype = = MOVETYPE_NONE
| | check - > v . movetype = = MOVETYPE_NOCLIP )
continue ;
if ( SV_TestEntityPosition ( check ) )
Con_Printf ( " entity in invalid position \n " ) ;
}
}
/*
= = = = = = = = = = = = = = = =
SV_CheckVelocity
= = = = = = = = = = = = = = = =
*/
void SV_CheckVelocity ( edict_t * ent )
{
int i ;
//
// bound velocity
//
for ( i = 0 ; i < 3 ; i + + )
{
if ( IS_NAN ( ent - > v . velocity [ i ] ) )
{
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
Con_Printf ( " Got a NaN velocity on %s \n " , PR_GetString ( ent - > v . classname ) ) ;
2010-02-15 23:26:55 +00:00
ent - > v . velocity [ i ] = 0 ;
}
if ( IS_NAN ( ent - > v . origin [ i ] ) )
{
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
Con_Printf ( " Got a NaN origin on %s \n " , PR_GetString ( ent - > v . classname ) ) ;
2010-02-15 23:26:55 +00:00
ent - > v . origin [ i ] = 0 ;
}
if ( ent - > v . velocity [ i ] > sv_maxvelocity . value )
ent - > v . velocity [ i ] = sv_maxvelocity . value ;
else if ( ent - > v . velocity [ i ] < - sv_maxvelocity . value )
ent - > v . velocity [ i ] = - sv_maxvelocity . value ;
}
}
/*
= = = = = = = = = = = = =
SV_RunThink
Runs thinking code if time . There is some play in the exact time the think
function will be called , because it is called before any movement is done
in a frame . Not used for pushmove objects , because they must be exact .
Returns false if the entity removed itself .
= = = = = = = = = = = = =
*/
qboolean SV_RunThink ( edict_t * ent )
{
float thinktime ;
float oldframe ; //johnfitz
int i ; //johnfitz
thinktime = ent - > v . nextthink ;
2022-07-19 21:36:57 +00:00
if ( thinktime < = 0 | | thinktime > qcvm - > time + qcvm - > frametime )
2010-02-15 23:26:55 +00:00
return true ;
2018-05-01 00:35:14 +00:00
if ( thinktime < qcvm - > time )
thinktime = qcvm - > time ; // don't let things stay in the past.
2010-02-15 23:26:55 +00:00
// it is possible to start that way
// by a trigger with a local time.
oldframe = ent - > v . frame ; //johnfitz
ent - > v . nextthink = 0 ;
pr_global_struct - > time = thinktime ;
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
2018-05-01 00:35:14 +00:00
pr_global_struct - > other = EDICT_TO_PROG ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
PR_ExecuteProgram ( ent - > v . think ) ;
//johnfitz -- PROTOCOL_FITZQUAKE
//capture interval to nextthink here and send it to client for better
//lerp timing, but only if interval is not 0.1 (which client assumes)
ent - > sendinterval = false ;
if ( ! ent - > free & & ent - > v . nextthink & & ( ent - > v . movetype = = MOVETYPE_STEP | | ent - > v . frame ! = oldframe ) )
{
i = Q_rint ( ( ent - > v . nextthink - thinktime ) * 255 ) ;
if ( i > = 0 & & i < 256 & & i ! = 25 & & i ! = 26 ) //25 and 26 are close enough to 0.1 to not send
ent - > sendinterval = true ;
}
//johnfitz
return ! ent - > free ;
}
/*
= = = = = = = = = = = = = = = = = =
SV_Impact
Two entities have touched , so run their touch functions
= = = = = = = = = = = = = = = = = =
*/
void SV_Impact ( edict_t * e1 , edict_t * e2 )
{
int old_self , old_other ;
old_self = pr_global_struct - > self ;
old_other = pr_global_struct - > other ;
2018-05-01 00:35:14 +00:00
pr_global_struct - > time = qcvm - > time ;
2010-02-15 23:26:55 +00:00
if ( e1 - > v . touch & & e1 - > v . solid ! = SOLID_NOT )
{
pr_global_struct - > self = EDICT_TO_PROG ( e1 ) ;
pr_global_struct - > other = EDICT_TO_PROG ( e2 ) ;
PR_ExecuteProgram ( e1 - > v . touch ) ;
}
if ( e2 - > v . touch & & e2 - > v . solid ! = SOLID_NOT )
{
pr_global_struct - > self = EDICT_TO_PROG ( e2 ) ;
pr_global_struct - > other = EDICT_TO_PROG ( e1 ) ;
PR_ExecuteProgram ( e2 - > v . touch ) ;
}
pr_global_struct - > self = old_self ;
pr_global_struct - > other = old_other ;
}
/*
= = = = = = = = = = = = = = = = = =
ClipVelocity
Slide off of the impacting object
returns the blocked flags ( 1 = floor , 2 = step / wall )
= = = = = = = = = = = = = = = = = =
*/
# define STOP_EPSILON 0.1
int ClipVelocity ( vec3_t in , vec3_t normal , vec3_t out , float overbounce )
{
float backoff ;
float change ;
int i , blocked ;
blocked = 0 ;
if ( normal [ 2 ] > 0 )
blocked | = 1 ; // floor
if ( ! normal [ 2 ] )
blocked | = 2 ; // step
backoff = DotProduct ( in , normal ) * overbounce ;
for ( i = 0 ; i < 3 ; i + + )
{
change = normal [ i ] * backoff ;
out [ i ] = in [ i ] - change ;
if ( out [ i ] > - STOP_EPSILON & & out [ i ] < STOP_EPSILON )
out [ i ] = 0 ;
}
return blocked ;
}
/*
= = = = = = = = = = = =
SV_FlyMove
The basic solid body movement clip that slides along multiple planes
Returns the clipflags if the velocity was modified ( hit something solid )
1 = floor
2 = wall / step
4 = dead stop
If steptrace is not NULL , the trace of any vertical wall hit will be stored
= = = = = = = = = = = =
*/
# define MAX_CLIP_PLANES 5
int SV_FlyMove ( edict_t * ent , float time , trace_t * steptrace )
{
int bumpcount , numbumps ;
vec3_t dir ;
float d ;
int numplanes ;
vec3_t planes [ MAX_CLIP_PLANES ] ;
vec3_t primal_velocity , original_velocity , new_velocity ;
int i , j ;
trace_t trace ;
vec3_t end ;
float time_left ;
int blocked ;
numbumps = 4 ;
blocked = 0 ;
VectorCopy ( ent - > v . velocity , original_velocity ) ;
VectorCopy ( ent - > v . velocity , primal_velocity ) ;
numplanes = 0 ;
time_left = time ;
for ( bumpcount = 0 ; bumpcount < numbumps ; bumpcount + + )
{
if ( ! ent - > v . velocity [ 0 ] & & ! ent - > v . velocity [ 1 ] & & ! ent - > v . velocity [ 2 ] )
break ;
for ( i = 0 ; i < 3 ; i + + )
end [ i ] = ent - > v . origin [ i ] + time_left * ent - > v . velocity [ i ] ;
trace = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , end , false , ent ) ;
if ( trace . allsolid )
{ // entity is trapped in another solid
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
return 3 ;
}
if ( trace . fraction > 0 )
{ // actually covered some distance
VectorCopy ( trace . endpos , ent - > v . origin ) ;
VectorCopy ( ent - > v . velocity , original_velocity ) ;
numplanes = 0 ;
}
if ( trace . fraction = = 1 )
break ; // moved the entire distance
if ( ! trace . ent )
Sys_Error ( " SV_FlyMove: !trace.ent " ) ;
if ( trace . plane . normal [ 2 ] > 0.7 )
{
blocked | = 1 ; // floor
if ( trace . ent - > v . solid = = SOLID_BSP )
{
ent - > v . flags = ( int ) ent - > v . flags | FL_ONGROUND ;
ent - > v . groundentity = EDICT_TO_PROG ( trace . ent ) ;
}
}
if ( ! trace . plane . normal [ 2 ] )
{
blocked | = 2 ; // step
if ( steptrace )
* steptrace = trace ; // save for player extrafriction
}
//
// run the impact function
//
SV_Impact ( ent , trace . ent ) ;
if ( ent - > free )
break ; // removed by the impact function
time_left - = time_left * trace . fraction ;
// cliped to another plane
if ( numplanes > = MAX_CLIP_PLANES )
{ // this shouldn't really happen
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
return 3 ;
}
VectorCopy ( trace . plane . normal , planes [ numplanes ] ) ;
numplanes + + ;
//
// modify original_velocity so it parallels all of the clip planes
//
for ( i = 0 ; i < numplanes ; i + + )
{
ClipVelocity ( original_velocity , planes [ i ] , new_velocity , 1 ) ;
for ( j = 0 ; j < numplanes ; j + + )
if ( j ! = i )
{
if ( DotProduct ( new_velocity , planes [ j ] ) < 0 )
break ; // not ok
}
if ( j = = numplanes )
break ;
}
if ( i ! = numplanes )
{ // go along this plane
VectorCopy ( new_velocity , ent - > v . velocity ) ;
}
else
{ // go along the crease
if ( numplanes ! = 2 )
{
// Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
return 7 ;
}
CrossProduct ( planes [ 0 ] , planes [ 1 ] , dir ) ;
d = DotProduct ( dir , ent - > v . velocity ) ;
VectorScale ( dir , d , ent - > v . velocity ) ;
}
//
// if original velocity is against the original velocity, stop dead
// to avoid tiny occilations in sloping corners
//
if ( DotProduct ( ent - > v . velocity , primal_velocity ) < = 0 )
{
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
return blocked ;
}
}
return blocked ;
}
/*
= = = = = = = = = = = =
SV_AddGravity
= = = = = = = = = = = =
*/
void SV_AddGravity ( edict_t * ent )
{
float ent_gravity ;
eval_t * val ;
2018-05-01 00:35:14 +00:00
val = GetEdictFieldValue ( ent , qcvm - > extfields . gravity ) ;
2010-02-15 23:26:55 +00:00
if ( val & & val - > _float )
ent_gravity = val - > _float ;
else
ent_gravity = 1.0 ;
2022-07-19 21:36:57 +00:00
ent - > v . velocity [ 2 ] - = ent_gravity * sv_gravity . value * qcvm - > frametime ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
PUSHMOVE
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = =
SV_PushEntity
Does not change the entities velocity at all
= = = = = = = = = = = =
*/
trace_t SV_PushEntity ( edict_t * ent , vec3_t push )
{
trace_t trace ;
vec3_t end ;
VectorAdd ( ent - > v . origin , push , end ) ;
if ( ent - > v . movetype = = MOVETYPE_FLYMISSILE )
trace = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , end , MOVE_MISSILE , ent ) ;
2021-09-08 01:44:57 +00:00
else if ( ent - > v . solid = = SOLID_TRIGGER | | ent - > v . solid = = SOLID_EXT_BSPTRIGGER | | ent - > v . solid = = SOLID_NOT )
2010-02-15 23:26:55 +00:00
// only clip against bmodels
trace = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , end , MOVE_NOMONSTERS , ent ) ;
else
trace = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , end , MOVE_NORMAL , ent ) ;
VectorCopy ( trace . endpos , ent - > v . origin ) ;
SV_LinkEdict ( ent , true ) ;
if ( trace . ent )
SV_Impact ( ent , trace . ent ) ;
return trace ;
}
/*
= = = = = = = = = = = =
SV_PushMove
= = = = = = = = = = = =
*/
2020-04-06 23:58:07 +00:00
static qboolean SV_PushMoveAngles ( edict_t * pusher , float movetime )
{
int i , e ;
edict_t * check , * block ;
vec3_t mins , maxs ;
//float oldsolid;
vec3_t org , org2 , move2 , forward , right , up ;
vec3_t move , amove ;
struct {
edict_t * ent ;
vec3_t origin ;
vec3_t angles ;
} * pushed , * pushed_p , * p ;
for ( i = 0 ; i < 3 ; i + + )
{
move [ i ] = pusher - > v . velocity [ i ] * movetime ;
amove [ i ] = pusher - > v . avelocity [ i ] * movetime ;
mins [ i ] = pusher - > v . absmin [ i ] + move [ i ] ;
maxs [ i ] = pusher - > v . absmax [ i ] + move [ i ] ;
}
//using johnfitz's dynamic alloc strategy, consistent with SV_PushMove
pushed_p = pushed = Hunk_Alloc ( qcvm - > num_edicts * sizeof ( * pushed ) ) ;
// find the bounding box
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = pusher - > v . absmin [ i ] + move [ i ] ;
maxs [ i ] = pusher - > v . absmax [ i ] + move [ i ] ;
}
// we need this for pushing things later
VectorSubtract ( vec3_origin , amove , org ) ;
AngleVectors ( org , forward , right , up ) ;
// save the pusher's original position
pushed_p - > ent = pusher ;
VectorCopy ( pusher - > v . origin , pushed_p - > origin ) ;
VectorCopy ( pusher - > v . angles , pushed_p - > angles ) ;
pushed_p + + ;
// move the pusher to it's final position
VectorAdd ( pusher - > v . origin , move , pusher - > v . origin ) ;
VectorAdd ( pusher - > v . angles , amove , pusher - > v . angles ) ;
SV_LinkEdict ( pusher , false ) ;
// see if any solid entities are inside the final position
check = NEXT_EDICT ( qcvm - > edicts ) ;
for ( e = 1 ; e < qcvm - > num_edicts ; e + + , check = NEXT_EDICT ( check ) )
{
if ( check - > free )
continue ;
if ( check - > v . movetype = = MOVETYPE_PUSH
| | check - > v . movetype = = MOVETYPE_NONE
| | check - > v . movetype = = MOVETYPE_NOCLIP
| | check - > v . movetype = = MOVETYPE_ANGLENOCLIP )
continue ;
/*
oldsolid = pusher - > v - > solid ;
pusher - > v - > solid = SOLID_NOT ;
block = World_TestEntityPosition ( w , check ) ;
pusher - > v - > solid = oldsolid ;
if ( block )
continue ;
*/
// if the entity is standing on the pusher, it will definitely be moved
if ( ! ( ( ( int ) check - > v . flags & FL_ONGROUND )
& & PROG_TO_EDICT ( check - > v . groundentity ) = = pusher ) )
{
// see if the ent needs to be tested
if ( check - > v . absmin [ 0 ] > = maxs [ 0 ]
| | check - > v . absmin [ 1 ] > = maxs [ 1 ]
| | check - > v . absmin [ 2 ] > = maxs [ 2 ]
| | check - > v . absmax [ 0 ] < = mins [ 0 ]
| | check - > v . absmax [ 1 ] < = mins [ 1 ]
| | check - > v . absmax [ 2 ] < = mins [ 2 ] )
continue ;
// see if the ent's bbox is inside the pusher's final position
if ( ! SV_TestEntityPosition ( check ) )
continue ;
}
if ( ( pusher - > v . movetype = = MOVETYPE_PUSH ) | | ( PROG_TO_EDICT ( check - > v . groundentity ) = = pusher ) )
{
// move this entity
pushed_p - > ent = check ;
VectorCopy ( check - > v . origin , pushed_p - > origin ) ;
VectorCopy ( check - > v . angles , pushed_p - > angles ) ;
pushed_p + + ;
// try moving the contacted entity
VectorAdd ( check - > v . origin , move , check - > v . origin ) ;
VectorAdd ( check - > v . angles , amove , check - > v . angles ) ;
// figure movement due to the pusher's amove
VectorSubtract ( check - > v . origin , pusher - > v . origin , org ) ;
org2 [ 0 ] = DotProduct ( org , forward ) ;
org2 [ 1 ] = - DotProduct ( org , right ) ;
org2 [ 2 ] = DotProduct ( org , up ) ;
VectorSubtract ( org2 , org , move2 ) ;
VectorAdd ( check - > v . origin , move2 , check - > v . origin ) ;
if ( check - > v . movetype ! = MOVETYPE_WALK )
check - > v . flags = ( int ) check - > v . flags & ~ FL_ONGROUND ;
// may have pushed them off an edge
if ( PROG_TO_EDICT ( check - > v . groundentity ) ! = pusher )
check - > v . groundentity = 0 ;
block = SV_TestEntityPosition ( check ) ;
if ( ! block )
{ // pushed ok
SV_LinkEdict ( check , false ) ;
// impact?
continue ;
}
// if it is ok to leave in the old position, do it
// this is only relevent for riding entities, not pushed
// FIXME: this doesn't acount for rotation
VectorCopy ( pushed_p [ - 1 ] . origin , check - > v . origin ) ;
block = SV_TestEntityPosition ( check ) ;
if ( ! block )
{
pushed_p - - ;
continue ;
}
//okay, that didn't work, try pushing the against stuff
SV_PushEntity ( check , move ) ;
block = SV_TestEntityPosition ( check ) ;
if ( ! block )
continue ;
VectorCopy ( check - > v . origin , move ) ;
for ( i = 0 ; i < 8 & & block ; i + + )
{
//precision errors can strike when you least expect it. lets try and reduce them.
check - > v . origin [ 0 ] = move [ 0 ] + ( ( i & 1 ) ? - 1 : 1 ) / 8.0 ;
check - > v . origin [ 1 ] = move [ 1 ] + ( ( i & 2 ) ? - 1 : 1 ) / 8.0 ;
check - > v . origin [ 2 ] = move [ 2 ] + ( ( i & 4 ) ? - 1 : 1 ) / 8.0 ;
block = SV_TestEntityPosition ( check ) ;
}
if ( ! block )
{
SV_LinkEdict ( check , false ) ;
continue ;
}
}
// if it is sitting on top. Do not block.
if ( check - > v . mins [ 0 ] = = check - > v . maxs [ 0 ] )
{
SV_LinkEdict ( check , false ) ;
continue ;
}
//some pushers are contents brushes, and are not solid. water cannot crush. the player just enters the water.
//but, the player will be moved along with the water if possible.
if ( pusher - > v . skin < 0 )
continue ;
2021-09-08 01:44:57 +00:00
if ( check - > v . solid = = SOLID_NOT | | check - > v . solid = = SOLID_TRIGGER | | check - > v . solid = = SOLID_EXT_BSPTRIGGER )
2020-04-06 23:58:07 +00:00
{ // corpse
check - > v . mins [ 0 ] = check - > v . mins [ 1 ] = 0 ;
VectorCopy ( check - > v . mins , check - > v . maxs ) ;
SV_LinkEdict ( check , false ) ;
continue ;
}
// Con_Printf("Pusher hit %s\n", PR_GetString(w->progs, check->v->classname));
if ( pusher - > v . blocked )
{
pr_global_struct - > self = EDICT_TO_PROG ( pusher ) ;
pr_global_struct - > other = EDICT_TO_PROG ( check ) ;
PR_ExecuteProgram ( pusher - > v . blocked ) ;
}
// move back any entities we already moved
// go backwards, so if the same entity was pushed
// twice, it goes back to the original position
for ( p = pushed_p - 1 ; p > = pushed ; p - - )
{
VectorCopy ( p - > origin , p - > ent - > v . origin ) ;
VectorCopy ( p - > angles , p - > ent - > v . angles ) ;
SV_LinkEdict ( p - > ent , false ) ;
}
return false ;
}
//FIXME: is there a better way to handle this?
// see if anything we moved has touched a trigger
for ( p = pushed_p - 1 ; p > = pushed ; p - - )
SV_LinkEdict ( p - > ent , true ) ;
return true ;
}
2010-02-15 23:26:55 +00:00
void SV_PushMove ( edict_t * pusher , float movetime )
{
int i , e ;
edict_t * check , * block ;
vec3_t mins , maxs , move ;
vec3_t entorig , pushorig ;
int num_moved ;
edict_t * * moved_edict ; //johnfitz -- dynamically allocate
vec3_t * moved_from ; //johnfitz -- dynamically allocate
int mark ; //johnfitz
2017-09-17 02:12:53 +00:00
float solid_backup ;
2010-02-15 23:26:55 +00:00
2022-08-23 09:10:17 +00:00
if ( ( pusher - > v . avelocity [ 0 ] | | pusher - > v . avelocity [ 1 ] | | pusher - > v . avelocity [ 2 ] ) )
{
if ( qcvm - > rotatingbmodel )
{ //spike -- added this block for proper rotations
mark = Hunk_LowMark ( ) ;
if ( SV_PushMoveAngles ( pusher , movetime ) )
pusher - > v . ltime + = movetime ;
Hunk_FreeToLowMark ( mark ) ;
return ;
}
if ( ! qcvm - > warned_rotatingbmodel )
{
Con_Warning ( " MOVETYPE_PUSH( \" %s \" ) has avelocity, but DP_SV_ROTATINGBMODEL is not enabled \n " , PR_GetString ( pusher - > v . classname ) ) ;
qcvm - > warned_rotatingbmodel = true ;
}
2020-04-06 23:58:07 +00:00
}
2010-02-15 23:26:55 +00:00
if ( ! pusher - > v . velocity [ 0 ] & & ! pusher - > v . velocity [ 1 ] & & ! pusher - > v . velocity [ 2 ] )
{
pusher - > v . ltime + = movetime ;
return ;
}
for ( i = 0 ; i < 3 ; i + + )
{
move [ i ] = pusher - > v . velocity [ i ] * movetime ;
mins [ i ] = pusher - > v . absmin [ i ] + move [ i ] ;
maxs [ i ] = pusher - > v . absmax [ i ] + move [ i ] ;
}
VectorCopy ( pusher - > v . origin , pushorig ) ;
// move the pusher to it's final position
VectorAdd ( pusher - > v . origin , move , pusher - > v . origin ) ;
pusher - > v . ltime + = movetime ;
SV_LinkEdict ( pusher , false ) ;
//johnfitz -- dynamically allocate
mark = Hunk_LowMark ( ) ;
2018-05-01 00:35:14 +00:00
moved_edict = ( edict_t * * ) Hunk_Alloc ( qcvm - > num_edicts * sizeof ( edict_t * ) ) ;
moved_from = ( vec3_t * ) Hunk_Alloc ( qcvm - > num_edicts * sizeof ( vec3_t ) ) ;
2010-02-15 23:26:55 +00:00
//johnfitz
// see if any solid entities are inside the final position
num_moved = 0 ;
2018-05-01 00:35:14 +00:00
check = NEXT_EDICT ( qcvm - > edicts ) ;
for ( e = 1 ; e < qcvm - > num_edicts ; e + + , check = NEXT_EDICT ( check ) )
2010-02-15 23:26:55 +00:00
{
if ( check - > free )
continue ;
if ( check - > v . movetype = = MOVETYPE_PUSH
| | check - > v . movetype = = MOVETYPE_NONE
| | check - > v . movetype = = MOVETYPE_NOCLIP )
continue ;
// if the entity is standing on the pusher, it will definately be moved
if ( ! ( ( ( int ) check - > v . flags & FL_ONGROUND )
& & PROG_TO_EDICT ( check - > v . groundentity ) = = pusher ) )
{
if ( check - > v . absmin [ 0 ] > = maxs [ 0 ]
| | check - > v . absmin [ 1 ] > = maxs [ 1 ]
| | check - > v . absmin [ 2 ] > = maxs [ 2 ]
| | check - > v . absmax [ 0 ] < = mins [ 0 ]
| | check - > v . absmax [ 1 ] < = mins [ 1 ]
| | check - > v . absmax [ 2 ] < = mins [ 2 ] )
continue ;
// see if the ent's bbox is inside the pusher's final position
2019-09-13 17:51:16 +00:00
if ( pusher - > v . skin < 0 )
{ //a more precise check...
2021-03-06 04:51:40 +00:00
if ( ! SV_ClipMoveToEntity ( pusher , check - > v . origin , check - > v . mins , check - > v . maxs , check - > v . origin , CONTENTMASK_ANYSOLID ) . startsolid )
2019-09-13 17:51:16 +00:00
continue ;
}
else
{
if ( ! SV_TestEntityPosition ( check ) )
continue ;
}
2010-02-15 23:26:55 +00:00
}
// remove the onground flag for non-players
if ( check - > v . movetype ! = MOVETYPE_WALK )
2021-11-08 00:17:44 +00:00
if ( ! pr_checkextension . value | | PROG_TO_EDICT ( check - > v . groundentity ) ! = pusher ) //unless they're already riding us (prevents grenade sound spam)
check - > v . flags = ( int ) check - > v . flags & ~ FL_ONGROUND ;
2010-02-15 23:26:55 +00:00
VectorCopy ( check - > v . origin , entorig ) ;
VectorCopy ( check - > v . origin , moved_from [ num_moved ] ) ;
moved_edict [ num_moved ] = check ;
num_moved + + ;
2017-09-17 02:12:53 +00:00
//QIP fix for end.bsp
solid_backup = pusher - > v . solid ;
if ( solid_backup = = SOLID_BSP // everything that blocks: bsp models = map brushes = doors, plats, etc.
| | solid_backup = = SOLID_BBOX // normally boxes
| | solid_backup = = SOLID_SLIDEBOX ) // normally monsters
{
// try moving the contacted entity
pusher - > v . solid = SOLID_NOT ;
SV_PushEntity ( check , move ) ;
// if it is still inside the pusher, block
2019-09-13 17:51:16 +00:00
if ( pusher - > v . skin < 0 )
{ //if it has forced contents then do things in a slightly different order, so water can push properly.
block = SV_TestEntityPosition ( check ) ;
pusher - > v . solid = solid_backup ;
}
else
{
pusher - > v . solid = solid_backup ;
block = SV_TestEntityPosition ( check ) ;
}
2017-09-17 02:12:53 +00:00
}
else
block = NULL ;
2010-02-15 23:26:55 +00:00
if ( block )
{ // fail the move
if ( check - > v . mins [ 0 ] = = check - > v . maxs [ 0 ] )
continue ;
2021-09-08 01:44:57 +00:00
if ( check - > v . solid = = SOLID_NOT | | check - > v . solid = = SOLID_TRIGGER | | check - > v . solid = = SOLID_EXT_BSPTRIGGER )
2010-02-15 23:26:55 +00:00
{ // corpse
check - > v . mins [ 0 ] = check - > v . mins [ 1 ] = 0 ;
VectorCopy ( check - > v . mins , check - > v . maxs ) ;
continue ;
}
VectorCopy ( entorig , check - > v . origin ) ;
SV_LinkEdict ( check , true ) ;
VectorCopy ( pushorig , pusher - > v . origin ) ;
SV_LinkEdict ( pusher , false ) ;
pusher - > v . ltime - = movetime ;
// if the pusher has a "blocked" function, call it
// otherwise, just stay in place until the obstacle is gone
if ( pusher - > v . blocked )
{
pr_global_struct - > self = EDICT_TO_PROG ( pusher ) ;
pr_global_struct - > other = EDICT_TO_PROG ( check ) ;
PR_ExecuteProgram ( pusher - > v . blocked ) ;
}
// move back any entities we already moved
for ( i = 0 ; i < num_moved ; i + + )
{
VectorCopy ( moved_from [ i ] , moved_edict [ i ] - > v . origin ) ;
SV_LinkEdict ( moved_edict [ i ] , false ) ;
}
Hunk_FreeToLowMark ( mark ) ; //johnfitz
return ;
}
}
Hunk_FreeToLowMark ( mark ) ; //johnfitz
}
/*
= = = = = = = = = = = = = = = =
SV_Physics_Pusher
= = = = = = = = = = = = = = = =
*/
void SV_Physics_Pusher ( edict_t * ent )
{
float thinktime ;
float oldltime ;
float movetime ;
oldltime = ent - > v . ltime ;
thinktime = ent - > v . nextthink ;
2022-07-19 21:36:57 +00:00
if ( thinktime < ent - > v . ltime + qcvm - > frametime )
2010-02-15 23:26:55 +00:00
{
movetime = thinktime - ent - > v . ltime ;
if ( movetime < 0 )
movetime = 0 ;
}
else
2022-07-19 21:36:57 +00:00
movetime = qcvm - > frametime ;
2010-02-15 23:26:55 +00:00
if ( movetime )
{
SV_PushMove ( ent , movetime ) ; // advances ent->v.ltime if not blocked
}
if ( thinktime > oldltime & & thinktime < = ent - > v . ltime )
{
ent - > v . nextthink = 0 ;
2018-05-01 00:35:14 +00:00
pr_global_struct - > time = qcvm - > time ;
2010-02-15 23:26:55 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
2018-05-01 00:35:14 +00:00
pr_global_struct - > other = EDICT_TO_PROG ( qcvm - > edicts ) ;
2010-02-15 23:26:55 +00:00
PR_ExecuteProgram ( ent - > v . think ) ;
if ( ent - > free )
return ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
CLIENT MOVEMENT
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = =
SV_CheckStuck
This is a big hack to try and fix the rare case of getting stuck in the world
clipping hull .
= = = = = = = = = = = = =
*/
void SV_CheckStuck ( edict_t * ent )
{
int i , j ;
int z ;
vec3_t org ;
if ( ! SV_TestEntityPosition ( ent ) )
{
VectorCopy ( ent - > v . origin , ent - > v . oldorigin ) ;
return ;
}
VectorCopy ( ent - > v . origin , org ) ;
VectorCopy ( ent - > v . oldorigin , ent - > v . origin ) ;
if ( ! SV_TestEntityPosition ( ent ) )
{
Con_DPrintf ( " Unstuck. \n " ) ;
SV_LinkEdict ( ent , true ) ;
return ;
}
for ( z = 0 ; z < 18 ; z + + )
for ( i = - 1 ; i < = 1 ; i + + )
for ( j = - 1 ; j < = 1 ; j + + )
{
ent - > v . origin [ 0 ] = org [ 0 ] + i ;
ent - > v . origin [ 1 ] = org [ 1 ] + j ;
ent - > v . origin [ 2 ] = org [ 2 ] + z ;
if ( ! SV_TestEntityPosition ( ent ) )
{
Con_DPrintf ( " Unstuck. \n " ) ;
SV_LinkEdict ( ent , true ) ;
return ;
}
}
VectorCopy ( org , ent - > v . origin ) ;
Con_DPrintf ( " player is stuck. \n " ) ;
}
/*
= = = = = = = = = = = = =
SV_CheckWater
= = = = = = = = = = = = =
*/
qboolean SV_CheckWater ( edict_t * ent )
{
vec3_t point ;
int cont ;
2019-09-13 17:51:16 +00:00
trace_t tr ;
//Spike -- FTE_ENT_SKIN_CONTENTS -- check if we're on a ladder, and if so fire a trace forwards to ensure its a valid ladder instead of a random volume
tr = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , ent - > v . origin , MOVE_HITALLCONTENTS , ent ) ;
if ( tr . contents = = CONTENTS_LADDER )
{
vec3_t flatforward ;
flatforward [ 0 ] = cos ( ( M_PI / 180 ) * ent - > v . angles [ 1 ] ) ;
flatforward [ 1 ] = sin ( ( M_PI / 180 ) * ent - > v . angles [ 1 ] ) ;
flatforward [ 2 ] = 0 ;
VectorMA ( ent - > v . origin , 24 , flatforward , point ) ;
tr = SV_Move ( ent - > v . origin , ent - > v . mins , ent - > v . maxs , point , 0 , ent ) ;
if ( tr . fraction < 1 )
2020-09-04 10:47:12 +00:00
ent - > onladder = true ;
2019-09-13 17:51:16 +00:00
else
2020-09-04 10:47:12 +00:00
ent - > onladder = false ;
2019-09-13 17:51:16 +00:00
}
else
2020-09-04 10:47:12 +00:00
ent - > onladder = false ;
2010-02-15 23:26:55 +00:00
point [ 0 ] = ent - > v . origin [ 0 ] ;
point [ 1 ] = ent - > v . origin [ 1 ] ;
point [ 2 ] = ent - > v . origin [ 2 ] + ent - > v . mins [ 2 ] + 1 ;
ent - > v . waterlevel = 0 ;
ent - > v . watertype = CONTENTS_EMPTY ;
2019-09-13 17:51:16 +00:00
//Spike -- FTE_ENT_SKIN_CONTENTS -- check submodels too, because we can.
cont = SV_PointContentsAllBsps ( point , ent ) ;
2010-02-15 23:26:55 +00:00
if ( cont < = CONTENTS_WATER )
{
ent - > v . watertype = cont ;
ent - > v . waterlevel = 1 ;
point [ 2 ] = ent - > v . origin [ 2 ] + ( ent - > v . mins [ 2 ] + ent - > v . maxs [ 2 ] ) * 0.5 ;
2019-09-13 17:51:16 +00:00
cont = SV_PointContentsAllBsps ( point , ent ) ;
2010-02-15 23:26:55 +00:00
if ( cont < = CONTENTS_WATER )
{
ent - > v . waterlevel = 2 ;
point [ 2 ] = ent - > v . origin [ 2 ] + ent - > v . view_ofs [ 2 ] ;
2019-09-13 17:51:16 +00:00
cont = SV_PointContentsAllBsps ( point , ent ) ;
2010-02-15 23:26:55 +00:00
if ( cont < = CONTENTS_WATER )
ent - > v . waterlevel = 3 ;
}
}
return ent - > v . waterlevel > 1 ;
}
/*
= = = = = = = = = = = =
SV_WallFriction
= = = = = = = = = = = =
*/
void SV_WallFriction ( edict_t * ent , trace_t * trace )
{
vec3_t forward , right , up ;
float d , i ;
vec3_t into , side ;
AngleVectors ( ent - > v . v_angle , forward , right , up ) ;
d = DotProduct ( trace - > plane . normal , forward ) ;
d + = 0.5 ;
if ( d > = 0 )
return ;
// cut the tangential velocity
i = DotProduct ( trace - > plane . normal , ent - > v . velocity ) ;
VectorScale ( trace - > plane . normal , i , into ) ;
VectorSubtract ( ent - > v . velocity , into , side ) ;
ent - > v . velocity [ 0 ] = side [ 0 ] * ( 1 + d ) ;
ent - > v . velocity [ 1 ] = side [ 1 ] * ( 1 + d ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
SV_TryUnstick
Player has come to a dead stop , possibly due to the problem with limited
float precision at some angle joins in the BSP hull .
Try fixing by pushing one pixel in each direction .
This is a hack , but in the interest of good gameplay . . .
= = = = = = = = = = = = = = = = = = = = = =
*/
int SV_TryUnstick ( edict_t * ent , vec3_t oldvel )
{
int i ;
vec3_t oldorg ;
vec3_t dir ;
int clip ;
trace_t steptrace ;
VectorCopy ( ent - > v . origin , oldorg ) ;
VectorCopy ( vec3_origin , dir ) ;
for ( i = 0 ; i < 8 ; i + + )
{
// try pushing a little in an axial direction
switch ( i )
{
case 0 : dir [ 0 ] = 2 ; dir [ 1 ] = 0 ; break ;
case 1 : dir [ 0 ] = 0 ; dir [ 1 ] = 2 ; break ;
case 2 : dir [ 0 ] = - 2 ; dir [ 1 ] = 0 ; break ;
case 3 : dir [ 0 ] = 0 ; dir [ 1 ] = - 2 ; break ;
case 4 : dir [ 0 ] = 2 ; dir [ 1 ] = 2 ; break ;
case 5 : dir [ 0 ] = - 2 ; dir [ 1 ] = 2 ; break ;
case 6 : dir [ 0 ] = 2 ; dir [ 1 ] = - 2 ; break ;
case 7 : dir [ 0 ] = - 2 ; dir [ 1 ] = - 2 ; break ;
}
SV_PushEntity ( ent , dir ) ;
// retry the original move
ent - > v . velocity [ 0 ] = oldvel [ 0 ] ;
ent - > v . velocity [ 1 ] = oldvel [ 1 ] ;
ent - > v . velocity [ 2 ] = 0 ;
clip = SV_FlyMove ( ent , 0.1 , & steptrace ) ;
if ( fabs ( oldorg [ 1 ] - ent - > v . origin [ 1 ] ) > 4
2022-04-22 14:50:02 +00:00
| | fabs ( oldorg [ 0 ] - ent - > v . origin [ 0 ] ) > 4 )
2010-02-15 23:26:55 +00:00
{
2022-04-22 14:50:02 +00:00
// Con_DPrintf ("unstuck!\n");
2010-02-15 23:26:55 +00:00
return clip ;
}
// go back to the original pos and try again
VectorCopy ( oldorg , ent - > v . origin ) ;
}
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
return 7 ; // still not moving
}
/*
= = = = = = = = = = = = = = = = = = = = =
SV_WalkMove
Only used by players
= = = = = = = = = = = = = = = = = = = = = =
*/
# define STEPSIZE 18
void SV_WalkMove ( edict_t * ent )
{
vec3_t upmove , downmove ;
vec3_t oldorg , oldvel ;
vec3_t nosteporg , nostepvel ;
int clip ;
int oldonground ;
trace_t steptrace , downtrace ;
//
// do a regular slide move unless it looks like you ran into a step
//
oldonground = ( int ) ent - > v . flags & FL_ONGROUND ;
ent - > v . flags = ( int ) ent - > v . flags & ~ FL_ONGROUND ;
VectorCopy ( ent - > v . origin , oldorg ) ;
VectorCopy ( ent - > v . velocity , oldvel ) ;
2022-07-19 21:36:57 +00:00
clip = SV_FlyMove ( ent , qcvm - > frametime , & steptrace ) ;
2010-02-15 23:26:55 +00:00
if ( ! ( clip & 2 ) )
return ; // move didn't block on a step
if ( ! oldonground & & ent - > v . waterlevel = = 0 )
return ; // don't stair up while jumping
if ( ent - > v . movetype ! = MOVETYPE_WALK )
return ; // gibbed by a trigger
if ( sv_nostep . value )
return ;
2020-09-04 10:47:12 +00:00
if ( ( int ) ent - > v . flags & FL_WATERJUMP )
2010-02-15 23:26:55 +00:00
return ;
VectorCopy ( ent - > v . origin , nosteporg ) ;
VectorCopy ( ent - > v . velocity , nostepvel ) ;
//
// try moving up and forward to go up a step
//
VectorCopy ( oldorg , ent - > v . origin ) ; // back to start pos
VectorCopy ( vec3_origin , upmove ) ;
VectorCopy ( vec3_origin , downmove ) ;
upmove [ 2 ] = STEPSIZE ;
2022-07-19 21:36:57 +00:00
downmove [ 2 ] = - STEPSIZE + oldvel [ 2 ] * qcvm - > frametime ;
2010-02-15 23:26:55 +00:00
// move up
SV_PushEntity ( ent , upmove ) ; // FIXME: don't link?
// move forward
ent - > v . velocity [ 0 ] = oldvel [ 0 ] ;
ent - > v . velocity [ 1 ] = oldvel [ 1 ] ;
ent - > v . velocity [ 2 ] = 0 ;
2022-07-19 21:36:57 +00:00
clip = SV_FlyMove ( ent , qcvm - > frametime , & steptrace ) ;
2010-02-15 23:26:55 +00:00
// check for stuckness, possibly due to the limited precision of floats
// in the clipping hulls
if ( clip )
{
if ( fabs ( oldorg [ 1 ] - ent - > v . origin [ 1 ] ) < 0.03125
& & fabs ( oldorg [ 0 ] - ent - > v . origin [ 0 ] ) < 0.03125 )
{ // stepping up didn't make any progress
clip = SV_TryUnstick ( ent , oldvel ) ;
}
}
// extra friction based on view angle
if ( clip & 2 )
SV_WallFriction ( ent , & steptrace ) ;
// move down
downtrace = SV_PushEntity ( ent , downmove ) ; // FIXME: don't link?
if ( downtrace . plane . normal [ 2 ] > 0.7 )
{
if ( ent - > v . solid = = SOLID_BSP )
{
ent - > v . flags = ( int ) ent - > v . flags | FL_ONGROUND ;
ent - > v . groundentity = EDICT_TO_PROG ( downtrace . ent ) ;
}
}
else
{
// if the push down didn't end up on good ground, use the move without
// the step up. This happens near wall / slope combinations, and can
// cause the player to hop up higher on a slope too steep to climb
VectorCopy ( nosteporg , ent - > v . origin ) ;
VectorCopy ( nostepvel , ent - > v . velocity ) ;
}
}
/*
= = = = = = = = = = = = = = = =
SV_Physics_Client
Player character actions
= = = = = = = = = = = = = = = =
*/
void SV_Physics_Client ( edict_t * ent , int num )
{
2020-09-04 11:22:26 +00:00
eval_t * val ;
2010-02-15 23:26:55 +00:00
if ( ! svs . clients [ num - 1 ] . active )
return ; // unconnected slot
2023-07-25 12:37:52 +00:00
if ( svs . clients [ num - 1 ] . usingpmove )
2020-09-04 11:36:56 +00:00
return ; //we're doing independant player physics with this mod, so clientside prediction can do its thing.
2017-09-17 02:12:53 +00:00
if ( ! svs . clients [ num - 1 ] . knowntoqc & & sv_gameplayfix_spawnbeforethinks . value )
return ; //don't spam prethinks before we called putclientinserver.
2010-02-15 23:26:55 +00:00
//
// call standard client pre-think
//
2018-05-01 00:35:14 +00:00
pr_global_struct - > time = qcvm - > time ;
2010-02-15 23:26:55 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
PR_ExecuteProgram ( pr_global_struct - > PlayerPreThink ) ;
//
// do a move
//
SV_CheckVelocity ( ent ) ;
//
// decide which move function to call
//
2020-09-04 11:22:26 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . customphysics ) ) & & val - > function )
{
pr_global_struct - > time = qcvm - > time ;
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
PR_ExecuteProgram ( val - > function ) ;
}
else switch ( ( int ) ent - > v . movetype )
2010-02-15 23:26:55 +00:00
{
case MOVETYPE_NONE :
if ( ! SV_RunThink ( ent ) )
return ;
break ;
case MOVETYPE_WALK :
if ( ! SV_RunThink ( ent ) )
return ;
if ( ! SV_CheckWater ( ent ) & & ! ( ( int ) ent - > v . flags & FL_WATERJUMP ) )
2019-09-13 17:51:16 +00:00
if ( ! sv_player - > onladder )
SV_AddGravity ( ent ) ;
2010-02-15 23:26:55 +00:00
SV_CheckStuck ( ent ) ;
SV_WalkMove ( ent ) ;
break ;
case MOVETYPE_TOSS :
case MOVETYPE_BOUNCE :
2021-08-31 05:43:13 +00:00
case MOVETYPE_EXT_BOUNCEMISSILE :
2010-02-15 23:26:55 +00:00
SV_Physics_Toss ( ent ) ;
break ;
case MOVETYPE_FLY :
if ( ! SV_RunThink ( ent ) )
return ;
2022-07-19 21:36:57 +00:00
SV_FlyMove ( ent , qcvm - > frametime , NULL ) ;
2010-02-15 23:26:55 +00:00
break ;
case MOVETYPE_NOCLIP :
if ( ! SV_RunThink ( ent ) )
return ;
2022-07-19 21:36:57 +00:00
VectorMA ( ent - > v . origin , qcvm - > frametime , ent - > v . velocity , ent - > v . origin ) ;
2010-02-15 23:26:55 +00:00
break ;
default :
2017-09-17 02:12:53 +00:00
Host_EndGame ( " SV_Physics_client: bad movetype %i " , ( int ) ent - > v . movetype ) ;
2010-02-15 23:26:55 +00:00
}
//
// call standard player post-think
//
SV_LinkEdict ( ent , true ) ;
2018-05-01 00:35:14 +00:00
pr_global_struct - > time = qcvm - > time ;
2010-02-15 23:26:55 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
PR_ExecuteProgram ( pr_global_struct - > PlayerPostThink ) ;
}
//============================================================================
/*
= = = = = = = = = = = = =
SV_Physics_None
Non moving objects can only think
= = = = = = = = = = = = =
*/
void SV_Physics_None ( edict_t * ent )
{
// regular thinking
SV_RunThink ( ent ) ;
}
/*
= = = = = = = = = = = = =
SV_Physics_Noclip
A moving object that doesn ' t obey physics
= = = = = = = = = = = = =
*/
void SV_Physics_Noclip ( edict_t * ent )
{
// regular thinking
if ( ! SV_RunThink ( ent ) )
return ;
2022-07-19 21:36:57 +00:00
VectorMA ( ent - > v . angles , qcvm - > frametime , ent - > v . avelocity , ent - > v . angles ) ;
VectorMA ( ent - > v . origin , qcvm - > frametime , ent - > v . velocity , ent - > v . origin ) ;
2010-02-15 23:26:55 +00:00
SV_LinkEdict ( ent , false ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TOSS / BOUNCE
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = =
SV_CheckWaterTransition
= = = = = = = = = = = = =
*/
void SV_CheckWaterTransition ( edict_t * ent )
{
int cont ;
cont = SV_PointContents ( ent - > v . origin ) ;
if ( ! ent - > v . watertype )
{ // just spawned here
ent - > v . watertype = cont ;
ent - > v . waterlevel = 1 ;
return ;
}
if ( cont < = CONTENTS_WATER )
{
2020-01-12 08:05:35 +00:00
if ( ent - > v . watertype = = CONTENTS_EMPTY & & * sv_sound_watersplash . string )
2010-02-15 23:26:55 +00:00
{ // just crossed into water
2020-09-04 11:22:26 +00:00
World_StartSound ( ent , NULL , 0 , sv_sound_watersplash . string , 255 , 1 ) ;
2010-02-15 23:26:55 +00:00
}
ent - > v . watertype = cont ;
ent - > v . waterlevel = 1 ;
}
else
{
2020-01-12 08:05:35 +00:00
if ( ent - > v . watertype ! = CONTENTS_EMPTY & & * sv_sound_watersplash . string )
2010-02-15 23:26:55 +00:00
{ // just crossed into water
2020-09-04 11:22:26 +00:00
World_StartSound ( ent , NULL , 0 , sv_sound_watersplash . string , 255 , 1 ) ;
2010-02-15 23:26:55 +00:00
}
ent - > v . watertype = CONTENTS_EMPTY ;
ent - > v . waterlevel = cont ;
}
}
/*
= = = = = = = = = = = = =
SV_Physics_Toss
Toss , bounce , and fly movement . When onground , do nothing .
= = = = = = = = = = = = =
*/
void SV_Physics_Toss ( edict_t * ent )
{
trace_t trace ;
vec3_t move ;
float backoff ;
2021-08-31 05:43:13 +00:00
int movetype ;
2010-02-15 23:26:55 +00:00
// regular thinking
if ( ! SV_RunThink ( ent ) )
return ;
// if onground, return without moving
if ( ( ( int ) ent - > v . flags & FL_ONGROUND ) )
return ;
SV_CheckVelocity ( ent ) ;
// add gravity
2021-08-31 05:43:13 +00:00
movetype = ent - > v . movetype ; if ( movetype = = MOVETYPE_EXT_BOUNCEMISSILE & & qcvm - > brokenbouncemissile ) movetype = MOVETYPE_BOUNCE ;
2010-02-15 23:26:55 +00:00
if ( ent - > v . movetype ! = MOVETYPE_FLY
& & ent - > v . movetype ! = MOVETYPE_FLYMISSILE )
SV_AddGravity ( ent ) ;
// move angles
2022-07-19 21:36:57 +00:00
VectorMA ( ent - > v . angles , qcvm - > frametime , ent - > v . avelocity , ent - > v . angles ) ;
2010-02-15 23:26:55 +00:00
// move origin
2022-07-19 21:36:57 +00:00
VectorScale ( ent - > v . velocity , qcvm - > frametime , move ) ;
2010-02-15 23:26:55 +00:00
trace = SV_PushEntity ( ent , move ) ;
if ( trace . fraction = = 1 )
return ;
if ( ent - > free )
return ;
2021-08-31 05:43:13 +00:00
movetype = ent - > v . movetype ; if ( movetype = = MOVETYPE_EXT_BOUNCEMISSILE & & qcvm - > brokenbouncemissile ) movetype = MOVETYPE_BOUNCE ;
if ( movetype = = MOVETYPE_BOUNCE )
2010-02-15 23:26:55 +00:00
backoff = 1.5 ;
2021-08-31 05:43:13 +00:00
else if ( movetype = = MOVETYPE_EXT_BOUNCEMISSILE )
backoff = 2 ; //bouncemissile.
2010-02-15 23:26:55 +00:00
else
backoff = 1 ;
ClipVelocity ( ent - > v . velocity , trace . plane . normal , ent - > v . velocity , backoff ) ;
// stop if on ground
if ( trace . plane . normal [ 2 ] > 0.7 )
{
2021-11-08 00:17:44 +00:00
if ( ent - > v . movetype ! = MOVETYPE_BOUNCE | | ( sv_gameplayfix_bouncedownslopes . value ? DotProduct ( trace . plane . normal , ent - > v . velocity ) : ent - > v . velocity [ 2 ] ) < 60 )
2010-02-15 23:26:55 +00:00
{
ent - > v . flags = ( int ) ent - > v . flags | FL_ONGROUND ;
ent - > v . groundentity = EDICT_TO_PROG ( trace . ent ) ;
VectorCopy ( vec3_origin , ent - > v . velocity ) ;
VectorCopy ( vec3_origin , ent - > v . avelocity ) ;
}
}
// check for in water
SV_CheckWaterTransition ( ent ) ;
}
2017-09-17 02:12:53 +00:00
/*
= = = = = = = = = = = = =
SV_Physics_Follow
Entities that are " stuck " to another entity
= = = = = = = = = = = = =
*/
static void SV_Physics_Follow ( edict_t * ent )
{
vec3_t vf , vr , vu , angles , v ;
edict_t * e ;
// regular thinking
if ( ! SV_RunThink ( ent ) )
return ;
// LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
e = PROG_TO_EDICT ( ent - > v . aiment ) ;
if ( e - > v . angles [ 0 ] = = ent - > v . punchangle [ 0 ] & & e - > v . angles [ 1 ] = = ent - > v . punchangle [ 1 ] & & e - > v . angles [ 2 ] = = ent - > v . punchangle [ 2 ] )
{
// quick case for no rotation
VectorAdd ( e - > v . origin , ent - > v . view_ofs , ent - > v . origin ) ;
}
else
{
angles [ 0 ] = - ent - > v . punchangle [ 0 ] ;
angles [ 1 ] = ent - > v . punchangle [ 1 ] ;
angles [ 2 ] = ent - > v . punchangle [ 2 ] ;
AngleVectors ( angles , vf , vr , vu ) ;
v [ 0 ] = ent - > v . view_ofs [ 0 ] * vf [ 0 ] + ent - > v . view_ofs [ 1 ] * vr [ 0 ] + ent - > v . view_ofs [ 2 ] * vu [ 0 ] ;
v [ 1 ] = ent - > v . view_ofs [ 0 ] * vf [ 1 ] + ent - > v . view_ofs [ 1 ] * vr [ 1 ] + ent - > v . view_ofs [ 2 ] * vu [ 1 ] ;
v [ 2 ] = ent - > v . view_ofs [ 0 ] * vf [ 2 ] + ent - > v . view_ofs [ 1 ] * vr [ 2 ] + ent - > v . view_ofs [ 2 ] * vu [ 2 ] ;
angles [ 0 ] = - e - > v . angles [ 0 ] ;
angles [ 1 ] = e - > v . angles [ 1 ] ;
angles [ 2 ] = e - > v . angles [ 2 ] ;
AngleVectors ( angles , vf , vr , vu ) ;
ent - > v . origin [ 0 ] = v [ 0 ] * vf [ 0 ] + v [ 1 ] * vf [ 1 ] + v [ 2 ] * vf [ 2 ] + e - > v . origin [ 0 ] ;
ent - > v . origin [ 1 ] = v [ 0 ] * vr [ 0 ] + v [ 1 ] * vr [ 1 ] + v [ 2 ] * vr [ 2 ] + e - > v . origin [ 1 ] ;
ent - > v . origin [ 2 ] = v [ 0 ] * vu [ 0 ] + v [ 1 ] * vu [ 1 ] + v [ 2 ] * vu [ 2 ] + e - > v . origin [ 2 ] ;
}
VectorAdd ( e - > v . angles , ent - > v . v_angle , ent - > v . angles ) ;
SV_LinkEdict ( ent , true ) ;
}
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
STEPPING MOVEMENT
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = =
SV_Physics_Step
Monsters freefall when they don ' t have a ground entity , otherwise
all movement is done with discrete steps .
This is also used for objects that have become still on the ground , but
will fall if the floor is pulled out from under them .
= = = = = = = = = = = = =
*/
void SV_Physics_Step ( edict_t * ent )
{
qboolean hitsound ;
// freefall if not onground
if ( ! ( ( int ) ent - > v . flags & ( FL_ONGROUND | FL_FLY | FL_SWIM ) ) )
{
if ( ent - > v . velocity [ 2 ] < sv_gravity . value * - 0.1 )
hitsound = true ;
else
hitsound = false ;
SV_AddGravity ( ent ) ;
SV_CheckVelocity ( ent ) ;
2022-07-19 21:36:57 +00:00
SV_FlyMove ( ent , qcvm - > frametime , NULL ) ;
2010-02-15 23:26:55 +00:00
SV_LinkEdict ( ent , true ) ;
if ( ( int ) ent - > v . flags & FL_ONGROUND ) // just hit ground
{
2020-01-12 08:05:35 +00:00
if ( hitsound & & * sv_sound_land . string )
2020-09-04 11:22:26 +00:00
World_StartSound ( ent , NULL , 0 , sv_sound_land . string , 255 , 1 ) ;
2010-02-15 23:26:55 +00:00
}
}
// regular thinking
SV_RunThink ( ent ) ;
SV_CheckWaterTransition ( ent ) ;
}
//============================================================================
/*
= = = = = = = = = = = = = = = =
SV_Physics
= = = = = = = = = = = = = = = =
*/
2022-07-19 21:36:57 +00:00
void SV_Physics ( double frametime )
2010-02-15 23:26:55 +00:00
{
2011-06-10 03:04:51 +00:00
int i ;
int entity_cap ; // For sv_freezenonclients
2010-02-15 23:26:55 +00:00
edict_t * ent ;
2020-09-04 11:22:26 +00:00
eval_t * val ;
int physics_mode ;
if ( qcvm - > extglobals . physics_mode )
physics_mode = * qcvm - > extglobals . physics_mode ;
else
physics_mode = ( qcvm = = & cl . qcvm ) ? 0 : 2 ; //csqc doesn't run thinks by default. it was meant to simplify implementations, but we just force fields to match ssqc so its not that large a burden.
2022-08-23 10:58:15 +00:00
if ( frametime < 0 )
frametime = 0 ; //no, just no. stoopid float precision.
2022-07-19 21:36:57 +00:00
pr_global_struct - > time = qcvm - > time ;
pr_global_struct - > frametime = qcvm - > frametime = frametime ;
2020-09-04 11:22:26 +00:00
if ( ! physics_mode )
{
2022-07-19 21:36:57 +00:00
qcvm - > time + = frametime ;
2020-09-04 11:22:26 +00:00
return ;
}
else if ( physics_mode = = 1 )
{ //for dp compat. note that this violates MOVETYPE_PUSH.
for ( i = 0 , ent = qcvm - > edicts ; i < qcvm - > num_edicts ; i + + , ent = NEXT_EDICT ( ent ) )
{
if ( ent - > free )
continue ;
SV_RunThink ( ent ) ;
}
2022-07-19 21:36:57 +00:00
qcvm - > time + = frametime ;
2020-09-04 11:22:26 +00:00
return ;
}
// else if (physics_mode==2) standard quake physics
2010-02-15 23:26:55 +00:00
// let the progs know that a new frame has started
2020-09-04 11:22:26 +00:00
if ( pr_global_struct - > StartFrame )
{
pr_global_struct - > self = EDICT_TO_PROG ( qcvm - > edicts ) ;
pr_global_struct - > other = EDICT_TO_PROG ( qcvm - > edicts ) ;
pr_global_struct - > time = qcvm - > time ;
PR_ExecuteProgram ( pr_global_struct - > StartFrame ) ;
}
2010-02-15 23:26:55 +00:00
//SV_CheckAllEnts ();
//
// treat each object in turn
//
2018-05-01 00:35:14 +00:00
ent = qcvm - > edicts ;
2011-06-10 03:04:51 +00:00
2020-09-04 11:22:26 +00:00
if ( sv_freezenonclients . value & & qcvm = = & sv . qcvm )
2017-09-17 02:12:53 +00:00
entity_cap = svs . maxclients + 1 ; // Only run physics on clients and the world
2011-06-10 03:04:51 +00:00
else
2022-04-27 21:32:40 +00:00
entity_cap = qcvm - > num_edicts ;
2011-06-10 03:04:51 +00:00
//for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
for ( i = 0 ; i < entity_cap ; i + + , ent = NEXT_EDICT ( ent ) )
2010-02-15 23:26:55 +00:00
{
if ( ent - > free )
continue ;
if ( pr_global_struct - > force_retouch )
{
SV_LinkEdict ( ent , true ) ; // force retouch even for stationary
}
2020-09-04 11:22:26 +00:00
if ( i > 0 & & i < = svs . maxclients & & qcvm = = & sv . qcvm )
2010-02-15 23:26:55 +00:00
SV_Physics_Client ( ent , i ) ;
2020-09-04 11:22:26 +00:00
else if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . customphysics ) ) & & val - > function )
{
pr_global_struct - > time = qcvm - > time ;
pr_global_struct - > self = EDICT_TO_PROG ( ent ) ;
PR_ExecuteProgram ( val - > function ) ;
}
2010-02-15 23:26:55 +00:00
else if ( ent - > v . movetype = = MOVETYPE_PUSH )
SV_Physics_Pusher ( ent ) ;
else if ( ent - > v . movetype = = MOVETYPE_NONE )
SV_Physics_None ( ent ) ;
else if ( ent - > v . movetype = = MOVETYPE_NOCLIP )
SV_Physics_Noclip ( ent ) ;
else if ( ent - > v . movetype = = MOVETYPE_STEP )
SV_Physics_Step ( ent ) ;
else if ( ent - > v . movetype = = MOVETYPE_TOSS
2021-08-31 05:43:13 +00:00
| | ent - > v . movetype = = MOVETYPE_EXT_BOUNCEMISSILE
2010-02-15 23:26:55 +00:00
| | ent - > v . movetype = = MOVETYPE_BOUNCE
| | ent - > v . movetype = = MOVETYPE_FLY
| | ent - > v . movetype = = MOVETYPE_FLYMISSILE )
SV_Physics_Toss ( ent ) ;
2017-09-17 02:12:53 +00:00
else if ( ent - > v . movetype = = MOVETYPE_EXT_FOLLOW )
SV_Physics_Follow ( ent ) ;
else if ( ent - > v . movetype = = MOVETYPE_WALK )
{
if ( SV_RunThink ( ent ) )
{
if ( ! SV_CheckWater ( ent ) & & ! ( ( int ) ent - > v . flags & FL_WATERJUMP ) )
SV_AddGravity ( ent ) ;
SV_CheckStuck ( ent ) ;
SV_WalkMove ( ent ) ;
}
}
2010-02-15 23:26:55 +00:00
else
2017-09-17 02:12:53 +00:00
Host_EndGame ( " SV_Physics: bad movetype %i " , ( int ) ent - > v . movetype ) ;
2010-02-15 23:26:55 +00:00
}
if ( pr_global_struct - > force_retouch )
pr_global_struct - > force_retouch - - ;
2017-09-17 02:12:53 +00:00
2018-05-01 00:35:14 +00:00
if ( qcvm - > extfuncs . EndFrame )
2017-09-17 02:12:53 +00:00
{
2018-05-01 00:35:14 +00:00
pr_global_struct - > self = EDICT_TO_PROG ( qcvm - > edicts ) ;
pr_global_struct - > other = EDICT_TO_PROG ( qcvm - > edicts ) ;
pr_global_struct - > time = qcvm - > time ;
PR_ExecuteProgram ( qcvm - > extfuncs . EndFrame ) ;
2017-09-17 02:12:53 +00:00
}
2023-07-22 16:40:33 +00:00
if ( ! ( sv_freezenonclients . value & & qcvm = = & sv . qcvm ) ) //FIXME: this breaks input_timelength
2022-07-19 21:36:57 +00:00
qcvm - > time + = frametime ;
2010-02-15 23:26:55 +00:00
}