quakeforge/nq/source/sv_cl_phys.c
2012-07-18 14:51:53 +09:00

373 lines
9 KiB
C

/*
sv_phys.c
@description@
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/cvar.h"
#include "QF/sys.h"
#include "host.h"
#include "server.h"
#include "sv_progs.h"
#include "world.h"
#define sv_frametime host_frametime
cvar_t *sv_nostep;
// CLIENT MOVEMENT ============================================================
/*
SV_CheckStuck
This is a big hack to try and fix the rare case of getting stuck in the world
clipping hull.
*/
static void
SV_CheckStuck (edict_t *ent)
{
int i, j, z;
vec3_t org;
if (!SV_TestEntityPosition (ent)) {
VectorCopy (SVvector (ent, origin), SVvector (ent, oldorigin));
return;
}
VectorCopy (SVvector (ent, origin), org);
VectorCopy (SVvector (ent, oldorigin), SVvector (ent, origin));
if (!SV_TestEntityPosition (ent)) {
Sys_MaskPrintf (SYS_DEV, "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++) {
SVvector (ent, origin)[0] = org[0] + i;
SVvector (ent, origin)[1] = org[1] + j;
SVvector (ent, origin)[2] = org[2] + z;
if (!SV_TestEntityPosition (ent)) {
Sys_MaskPrintf (SYS_DEV, "Unstuck.\n");
SV_LinkEdict (ent, true);
return;
}
}
VectorCopy (org, SVvector (ent, origin));
Sys_MaskPrintf (SYS_DEV, "player is stuck.\n");
}
static qboolean
SV_CheckWater (edict_t *ent)
{
int cont;
vec3_t point;
point[0] = SVvector (ent, origin)[0];
point[1] = SVvector (ent, origin)[1];
point[2] = SVvector (ent, origin)[2] + SVvector (ent, mins)[2] + 1;
SVfloat (ent, waterlevel) = 0;
SVfloat (ent, watertype) = CONTENTS_EMPTY;
cont = SV_PointContents (point);
if (cont <= CONTENTS_WATER) {
SVfloat (ent, watertype) = cont;
SVfloat (ent, waterlevel) = 1;
point[2] = SVvector (ent, origin)[2] + (SVvector (ent, mins)[2] +
SVvector (ent, maxs)[2]) * 0.5;
cont = SV_PointContents (point);
if (cont <= CONTENTS_WATER) {
SVfloat (ent, waterlevel) = 2;
point[2] = SVvector (ent, origin)[2] + SVvector (ent, view_ofs)[2];
cont = SV_PointContents (point);
if (cont <= CONTENTS_WATER)
SVfloat (ent, waterlevel) = 3;
}
}
return SVfloat (ent, waterlevel) > 1;
}
static void
SV_WallFriction (edict_t *ent, trace_t *trace)
{
float d, i;
vec3_t forward, right, up, into, side;
AngleVectors (SVvector (ent, 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, SVvector (ent, velocity));
VectorScale (trace->plane.normal, i, into);
VectorSubtract (SVvector (ent, velocity), into, side);
SVvector (ent, velocity)[0] = side[0] * (1 + d);
SVvector (ent, 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...
*/
static int
SV_TryUnstick (edict_t *ent, vec3_t oldvel)
{
int i, clip;
vec3_t dir, oldorg;
trace_t steptrace;
VectorCopy (SVvector (ent, origin), oldorg);
VectorZero (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
SVvector (ent, velocity)[0] = oldvel[0];
SVvector (ent, velocity)[1] = oldvel[1];
SVvector (ent, velocity)[2] = 0;
clip = SV_FlyMove (ent, 0.1, &steptrace);
if (fabs (oldorg[1] - SVvector (ent, origin)[1]) > 4
|| fabs (oldorg[0] - SVvector (ent, origin)[0]) > 4) {
// Sys_MaskPrintf (SYS_DEV, "unstuck!\n");
return clip;
}
// go back to the original pos and try again
VectorCopy (oldorg, SVvector (ent, origin));
}
VectorZero (SVvector (ent, velocity));
return 7; // still not moving
}
#define STEPSIZE 18
/*
SV_WalkMove
Used only by players
*/
static void
SV_WalkMove (edict_t *ent)
{
int clip, oldonground;
vec3_t upmove, downmove, nosteporg, nostepvel, oldorg, oldvel;
trace_t steptrace, downtrace;
// do a regular slide move unless it looks like you ran into a step
oldonground = (int) SVfloat (ent, flags) & FL_ONGROUND;
SVfloat (ent, flags) = (int) SVfloat (ent, flags) & ~FL_ONGROUND;
VectorCopy (SVvector (ent, origin), oldorg);
VectorCopy (SVvector (ent, velocity), oldvel);
clip = SV_FlyMove (ent, sv_frametime, &steptrace);
if (!(clip & 2))
return; // move didn't block on a step
if (!oldonground && SVfloat (ent, waterlevel) == 0)
return; // don't stair up while jumping
if (SVfloat (ent, movetype) != MOVETYPE_WALK)
return; // gibbed by a trigger
if (sv_nostep->int_val)
return;
if ((int) SVfloat (sv_player, flags) & FL_WATERJUMP)
return;
VectorCopy (SVvector (ent, origin), nosteporg);
VectorCopy (SVvector (ent, velocity), nostepvel);
// try moving up and forward to go up a step
VectorCopy (oldorg, SVvector (ent, origin)); // back to start pos
VectorZero (upmove);
VectorZero (downmove);
upmove[2] = STEPSIZE;
downmove[2] = -STEPSIZE + oldvel[2] * sv_frametime;
// move up
SV_PushEntity (ent, upmove); // FIXME: don't link?
// move forward
SVvector (ent, velocity)[0] = oldvel[0];
SVvector (ent, velocity)[1] = oldvel[1];
SVvector (ent, velocity)[2] = 0;
clip = SV_FlyMove (ent, sv_frametime, &steptrace);
// check for stuckness, possibly due to the limited precision of floats
// in the clipping hulls
if (clip) {
if (fabs (oldorg[1] - SVvector (ent, origin)[1]) < 0.03125
&& fabs (oldorg[0] - SVvector (ent, 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 (SV_EntCanSupportJump (ent)) {
SVfloat (ent, flags) = (int) SVfloat (ent, flags) | FL_ONGROUND;
SVfloat (ent, groundentity) = EDICT_TO_PROG (&sv_pr_state,
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, SVvector (ent, origin));
VectorCopy (nostepvel, SVvector (ent, velocity));
}
}
/*
SV_Physics_Client
Player character actions
*/
void
SV_Physics_Client (edict_t *ent, int num)
{
if (!svs.clients[num - 1].active)
return; // unconnected slot
// call standard client pre-think
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, ent);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.PlayerPreThink);
SVdata (ent)->add_grav = false;
// do a move
SV_CheckVelocity (ent);
// decide which move function to call
switch ((int) SVfloat (ent, movetype)) {
case MOVETYPE_NONE:
if (!SV_RunThink (ent))
return;
break;
case MOVETYPE_WALK:
if (!SV_RunThink (ent))
return;
if (!SV_CheckWater (ent)
&& !((int) SVfloat (ent, flags) & FL_WATERJUMP))
SV_AddGravity (ent);
SV_CheckStuck (ent);
SV_WalkMove (ent);
break;
case MOVETYPE_TOSS:
case MOVETYPE_BOUNCE:
SV_Physics_Toss (ent);
break;
case MOVETYPE_FLY:
if (!SV_RunThink (ent))
return;
SV_FlyMove (ent, sv_frametime, NULL);
break;
case MOVETYPE_NOCLIP:
if (!SV_RunThink (ent))
return;
VectorMultAdd (SVvector (ent, origin), sv_frametime,
SVvector (ent, velocity), SVvector (ent, origin));
break;
default:
Sys_Error ("SV_Physics_client: bad movetype %i",
(int) SVfloat (ent, movetype));
}
SV_LinkEdict (ent, SVfloat (ent, movetype) != MOVETYPE_NOCLIP);
// call standard player post-think
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, ent);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.PlayerPostThink);
}